* [PATCH bpf-next v3 6/9] selftests/bpf: Test creating dynptr from dynptr data and slice
From: Amery Hung @ 2026-04-21 22:10 UTC (permalink / raw)
To: bpf
Cc: netdev, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
martin.lau, mykyta.yatsenko5, ameryhung, kernel-team
In-Reply-To: <20260421221016.2967924-1-ameryhung@gmail.com>
The verifier currently does not allow creating dynptr from dynptr data
or slice. Add a selftest to test this explicitly.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
.../testing/selftests/bpf/progs/dynptr_fail.c | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index b5fbc9b5c484..43beb70f50ee 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -705,6 +705,48 @@ int dynptr_from_mem_invalid_api(void *ctx)
return 0;
}
+/* Cannot create dynptr from dynptr data */
+SEC("?raw_tp")
+__failure __msg("Unsupported reg type mem for bpf_dynptr_from_mem data")
+int dynptr_from_dynptr_data(void *ctx)
+{
+ struct bpf_dynptr ptr, ptr2;
+ __u8 *data;
+
+ if (get_map_val_dynptr(&ptr))
+ return 0;
+
+ data = bpf_dynptr_data(&ptr, 0, sizeof(__u32));
+ if (!data)
+ return 0;
+
+ /* this should fail */
+ bpf_dynptr_from_mem(data, sizeof(__u32), 0, &ptr2);
+
+ return 0;
+}
+
+/* Cannot create dynptr from dynptr slice */
+SEC("?tc")
+__failure __msg("Unsupported reg type mem for bpf_dynptr_from_mem data")
+int dynptr_from_dynptr_slice(struct __sk_buff *skb)
+{
+ struct bpf_dynptr ptr, ptr2;
+ struct ethhdr *hdr;
+ char buffer[sizeof(*hdr)] = {};
+
+ bpf_dynptr_from_skb(skb, 0, &ptr);
+
+ hdr = bpf_dynptr_slice_rdwr(&ptr, 0, buffer, sizeof(buffer));
+ if (!hdr)
+ return SK_DROP;
+
+ /* this should fail */
+ bpf_dynptr_from_mem(hdr, sizeof(*hdr), 0, &ptr2);
+
+ return SK_PASS;
+}
+
SEC("?tc")
__failure __msg("cannot overwrite referenced dynptr") __log_level(2)
int dynptr_pruning_overwrite(struct __sk_buff *ctx)
--
2.52.0
^ permalink raw reply related
* [PATCH bpf-next v3 5/9] bpf: Remove redundant dynptr arg check for helper
From: Amery Hung @ 2026-04-21 22:10 UTC (permalink / raw)
To: bpf
Cc: netdev, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
martin.lau, mykyta.yatsenko5, ameryhung, kernel-team
In-Reply-To: <20260421221016.2967924-1-ameryhung@gmail.com>
unmark_stack_slots_dynptr() already makes sure that CONST_PTR_TO_DYNPTR
cannot be released. process_dynptr_func() also prevents passing
uninitialized dynptr to helpers expecting initialized dynptr. Now that
unmark_stack_slots_dynptr() also error returned from
release_reference(), there should be no reason to keep these redundant
checks.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 21 +------------------
.../testing/selftests/bpf/progs/dynptr_fail.c | 6 +++---
.../selftests/bpf/progs/user_ringbuf_fail.c | 4 ++--
3 files changed, 6 insertions(+), 25 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 908a3af0e7c4..3ab9bc2fe0e3 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8405,26 +8405,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
skip_type_check:
if (arg_type_is_release(arg_type)) {
- if (arg_type_is_dynptr(arg_type)) {
- struct bpf_func_state *state = bpf_func(env, reg);
- int spi;
-
- /* Only dynptr created on stack can be released, thus
- * the get_spi and stack state checks for spilled_ptr
- * should only be done before process_dynptr_func for
- * PTR_TO_STACK.
- */
- if (reg->type == PTR_TO_STACK) {
- spi = dynptr_get_spi(env, reg);
- if (spi < 0 || !state->stack[spi].spilled_ptr.id) {
- verbose(env, "arg %d is an unacquired reference\n", regno);
- return -EINVAL;
- }
- } else {
- verbose(env, "cannot release unowned const bpf_dynptr\n");
- return -EINVAL;
- }
- } else if (!reg->ref_obj_id && !bpf_register_is_null(reg)) {
+ if (!arg_type_is_dynptr(arg_type) && !reg->ref_obj_id && !bpf_register_is_null(reg)) {
verbose(env, "R%d must be referenced when passed to release function\n",
regno);
return -EINVAL;
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index b62773ce5219..b5fbc9b5c484 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -136,7 +136,7 @@ int ringbuf_missing_release_callback(void *ctx)
/* Can't call bpf_ringbuf_submit/discard_dynptr on a non-initialized dynptr */
SEC("?raw_tp")
-__failure __msg("arg 1 is an unacquired reference")
+__failure __msg("Expected an initialized dynptr as arg #0")
int ringbuf_release_uninit_dynptr(void *ctx)
{
struct bpf_dynptr ptr;
@@ -650,7 +650,7 @@ int invalid_offset(void *ctx)
/* Can't release a dynptr twice */
SEC("?raw_tp")
-__failure __msg("arg 1 is an unacquired reference")
+__failure __msg("Expected an initialized dynptr as arg #0")
int release_twice(void *ctx)
{
struct bpf_dynptr ptr;
@@ -677,7 +677,7 @@ static int release_twice_callback_fn(__u32 index, void *data)
* within a callback function, fails
*/
SEC("?raw_tp")
-__failure __msg("arg 1 is an unacquired reference")
+__failure __msg("Expected an initialized dynptr as arg #0")
int release_twice_callback(void *ctx)
{
struct bpf_dynptr ptr;
diff --git a/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c b/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c
index 54de0389f878..c0d0422b8030 100644
--- a/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c
+++ b/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c
@@ -146,7 +146,7 @@ try_discard_dynptr(struct bpf_dynptr *dynptr, void *context)
* not be able to read past the end of the pointer.
*/
SEC("?raw_tp")
-__failure __msg("cannot release unowned const bpf_dynptr")
+__failure __msg("CONST_PTR_TO_DYNPTR cannot be released")
int user_ringbuf_callback_discard_dynptr(void *ctx)
{
bpf_user_ringbuf_drain(&user_ringbuf, try_discard_dynptr, NULL, 0);
@@ -166,7 +166,7 @@ try_submit_dynptr(struct bpf_dynptr *dynptr, void *context)
* not be able to read past the end of the pointer.
*/
SEC("?raw_tp")
-__failure __msg("cannot release unowned const bpf_dynptr")
+__failure __msg("CONST_PTR_TO_DYNPTR cannot be released")
int user_ringbuf_callback_submit_dynptr(void *ctx)
{
bpf_user_ringbuf_drain(&user_ringbuf, try_submit_dynptr, NULL, 0);
--
2.52.0
^ permalink raw reply related
* [PATCH bpf-next v3 4/9] bpf: Refactor object relationship tracking and fix dynptr UAF bug
From: Amery Hung @ 2026-04-21 22:10 UTC (permalink / raw)
To: bpf
Cc: netdev, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
martin.lau, mykyta.yatsenko5, ameryhung, kernel-team
In-Reply-To: <20260421221016.2967924-1-ameryhung@gmail.com>
Refactor object relationship tracking in the verifier and fix a dynptr
use-after-free bug where file/skb dynptrs are not invalidated when the
parent referenced object is freed.
Add parent_id to bpf_reg_state to precisely track child-parent
relationships. A child object's parent_id points to the parent object's
id. This replaces the PTR_TO_MEM-specific dynptr_id and does not
increase the size of bpf_reg_state on 64-bit machines as there is
existing padding.
When calling dynptr constructors (i.e., process_dynptr_func() with
MEM_UNINIT argument), track the parent's id if the parent is a
referenced object. This only applies to file dynptr and skb dynptr,
so only pass parent reg->id to kfunc constructors.
For release_reference(), invalidating an object now also invalidates
all descendants by traversing the object tree. This is done using
stack-based DFS to avoid recursive call chains of release_reference() ->
unmark_stack_slots_dynptr() -> release_reference(). Referenced objects
encountered during tree traversal cannot be indirectly released. They
require an explicit helper/kfunc call to release the acquired resources.
While the new design changes how object relationships are tracked in
the verifier, it does not change the verifier's behavior. Here is the
implication for dynptr, pointer casting, and owning/non-owning
references:
Dynptr:
When initializing a dynptr, referenced dynptrs acquire a reference for
ref_obj_id. If the dynptr has a referenced parent, parent_id tracks the
parent's id. When cloning, ref_obj_id and parent_id are copied from the
original. Releasing a referenced dynptr via release_reference(ref_obj_id)
invalidates all clones and derived slices. For non-referenced dynptrs,
only the specific dynptr and its children are invalidated.
Pointer casting:
Referenced socket pointers and their casted counterparts share the same
lifetime but have different nullness — they have different id but the
same ref_obj_id.
Owning to non-owning reference conversion:
After converting owning to non-owning by clearing ref_obj_id (e.g.,
object(id=1, ref_obj_id=1) -> object(id=1, ref_obj_id=0)), the
verifier only needs to release the reference state, so it calls
release_reference_nomark() instead of release_reference().
Note that the error message "reference has not been acquired before" in
the helper and kfunc release paths is removed. This message was already
unreachable. The verifier only calls release_reference() after
confirming meta.ref_obj_id is valid, so the condition could never
trigger in practice (no selftest exercises it either). With the
refactor, release_reference() can now be called with non-acquired ids
and have different error conditions. Report directly in
release_reference() instead.
Fixes: 870c28588afa ("bpf: net_sched: Add basic bpf qdisc kfuncs")
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf_verifier.h | 22 ++-
kernel/bpf/log.c | 4 +-
kernel/bpf/states.c | 9 +-
kernel/bpf/verifier.c | 264 +++++++++++++++++------------------
4 files changed, 152 insertions(+), 147 deletions(-)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index dc0cff59246d..1314299c3763 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -65,7 +65,6 @@ struct bpf_reg_state {
struct { /* for PTR_TO_MEM | PTR_TO_MEM_OR_NULL */
u32 mem_size;
- u32 dynptr_id; /* for dynptr slices */
};
/* For dynptr stack slots */
@@ -193,6 +192,13 @@ struct bpf_reg_state {
* allowed and has the same effect as bpf_sk_release(sk).
*/
u32 ref_obj_id;
+ /* Tracks the parent object this register was derived from.
+ * Used for cascading invalidation: when the parent object is
+ * released or invalidated, all registers with matching parent_id
+ * are also invalidated. For example, a slice from bpf_dynptr_data()
+ * gets parent_id set to the dynptr's id.
+ */
+ u32 parent_id;
/* Inside the callee two registers can be both PTR_TO_STACK like
* R1=fp-8 and R2=fp-8, but one of them points to this function stack
* while another to the caller's stack. To differentiate them 'frameno'
@@ -508,7 +514,7 @@ struct bpf_verifier_state {
iter < frame->allocated_stack / BPF_REG_SIZE; \
iter++, reg = bpf_get_spilled_reg(iter, frame, mask))
-#define bpf_for_each_reg_in_vstate_mask(__vst, __state, __reg, __mask, __expr) \
+#define bpf_for_each_reg_in_vstate_mask(__vst, __state, __reg, __stack, __mask, __expr) \
({ \
struct bpf_verifier_state *___vstate = __vst; \
int ___i, ___j; \
@@ -516,6 +522,7 @@ struct bpf_verifier_state {
struct bpf_reg_state *___regs; \
__state = ___vstate->frame[___i]; \
___regs = __state->regs; \
+ __stack = NULL; \
for (___j = 0; ___j < MAX_BPF_REG; ___j++) { \
__reg = &___regs[___j]; \
(void)(__expr); \
@@ -523,14 +530,19 @@ struct bpf_verifier_state {
bpf_for_each_spilled_reg(___j, __state, __reg, __mask) { \
if (!__reg) \
continue; \
+ __stack = &__state->stack[___j]; \
(void)(__expr); \
} \
} \
})
/* Invoke __expr over regsiters in __vst, setting __state and __reg */
-#define bpf_for_each_reg_in_vstate(__vst, __state, __reg, __expr) \
- bpf_for_each_reg_in_vstate_mask(__vst, __state, __reg, 1 << STACK_SPILL, __expr)
+#define bpf_for_each_reg_in_vstate(__vst, __state, __reg, __expr) \
+ ({ \
+ struct bpf_stack_state * ___stack; \
+ bpf_for_each_reg_in_vstate_mask(__vst, __state, __reg, ___stack,\
+ 1 << STACK_SPILL, __expr); \
+ })
/* linked list of verifier states used to prune search */
struct bpf_verifier_state_list {
@@ -1323,6 +1335,7 @@ struct bpf_dynptr_desc {
enum bpf_dynptr_type type;
u32 id;
u32 ref_obj_id;
+ u32 parent_id;
};
struct bpf_kfunc_call_arg_meta {
@@ -1334,6 +1347,7 @@ struct bpf_kfunc_call_arg_meta {
const char *func_name;
/* Out parameters */
u32 ref_obj_id;
+ u32 id;
u8 release_regno;
bool r0_rdonly;
u32 ret_btf_id;
diff --git a/kernel/bpf/log.c b/kernel/bpf/log.c
index 011e4ec25acd..d8dd372e45cd 100644
--- a/kernel/bpf/log.c
+++ b/kernel/bpf/log.c
@@ -667,6 +667,8 @@ static void print_reg_state(struct bpf_verifier_env *env,
verbose(env, "%+d", reg->delta);
if (reg->ref_obj_id)
verbose_a("ref_obj_id=%d", reg->ref_obj_id);
+ if (reg->parent_id)
+ verbose_a("parent_id=%d", reg->parent_id);
if (type_is_non_owning_ref(reg->type))
verbose_a("%s", "non_own_ref");
if (type_is_map_ptr(t)) {
@@ -770,8 +772,6 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifie
verbose_a("id=%d", reg->id);
if (reg->ref_obj_id)
verbose_a("ref_id=%d", reg->ref_obj_id);
- if (reg->dynptr_id)
- verbose_a("dynptr_id=%d", reg->dynptr_id);
verbose(env, ")");
break;
case STACK_ITER:
diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c
index 8478d2c6ed5b..72bd3bcda5fb 100644
--- a/kernel/bpf/states.c
+++ b/kernel/bpf/states.c
@@ -494,7 +494,8 @@ static bool regs_exact(const struct bpf_reg_state *rold,
{
return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 &&
check_ids(rold->id, rcur->id, idmap) &&
- check_ids(rold->ref_obj_id, rcur->ref_obj_id, idmap);
+ check_ids(rold->ref_obj_id, rcur->ref_obj_id, idmap) &&
+ check_ids(rold->parent_id, rcur->parent_id, idmap);
}
enum exact_level {
@@ -619,7 +620,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
range_within(rold, rcur) &&
tnum_in(rold->var_off, rcur->var_off) &&
check_ids(rold->id, rcur->id, idmap) &&
- check_ids(rold->ref_obj_id, rcur->ref_obj_id, idmap);
+ check_ids(rold->ref_obj_id, rcur->ref_obj_id, idmap) &&
+ check_ids(rold->parent_id, rcur->parent_id, idmap);
case PTR_TO_PACKET_META:
case PTR_TO_PACKET:
/* We must have at least as much range as the old ptr
@@ -799,7 +801,8 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,
cur_reg = &cur->stack[spi].spilled_ptr;
if (old_reg->dynptr.type != cur_reg->dynptr.type ||
old_reg->dynptr.first_slot != cur_reg->dynptr.first_slot ||
- !check_ids(old_reg->ref_obj_id, cur_reg->ref_obj_id, idmap))
+ !check_ids(old_reg->ref_obj_id, cur_reg->ref_obj_id, idmap) ||
+ !check_ids(old_reg->parent_id, cur_reg->parent_id, idmap))
return false;
break;
case STACK_ITER:
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 0313b7d5f6c9..908a3af0e7c4 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -201,7 +201,7 @@ struct bpf_verifier_stack_elem {
static int acquire_reference(struct bpf_verifier_env *env, int insn_idx);
static int release_reference_nomark(struct bpf_verifier_state *state, int ref_obj_id);
-static int release_reference(struct bpf_verifier_env *env, int ref_obj_id);
+static int release_reference(struct bpf_verifier_env *env, int id);
static void invalidate_non_owning_refs(struct bpf_verifier_env *env);
static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env);
static int ref_set_non_owning(struct bpf_verifier_env *env,
@@ -241,6 +241,7 @@ struct bpf_call_arg_meta {
int mem_size;
u64 msize_max_value;
int ref_obj_id;
+ u32 id;
int func_id;
struct btf *btf;
u32 btf_id;
@@ -603,14 +604,14 @@ static enum bpf_type_flag get_dynptr_type_flag(enum bpf_dynptr_type type)
}
}
-static bool dynptr_type_refcounted(enum bpf_dynptr_type type)
+static bool dynptr_type_referenced(enum bpf_dynptr_type type)
{
return type == BPF_DYNPTR_TYPE_RINGBUF || type == BPF_DYNPTR_TYPE_FILE;
}
static void __mark_dynptr_reg(struct bpf_reg_state *reg,
enum bpf_dynptr_type type,
- bool first_slot, int dynptr_id);
+ bool first_slot, int id);
static void mark_dynptr_stack_regs(struct bpf_verifier_env *env,
@@ -635,11 +636,12 @@ static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
struct bpf_func_state *state, int spi);
static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
- enum bpf_arg_type arg_type, int insn_idx, int clone_ref_obj_id)
+ enum bpf_arg_type arg_type, int insn_idx, int parent_id,
+ struct bpf_dynptr_desc *dynptr)
{
struct bpf_func_state *state = bpf_func(env, reg);
+ int spi, i, err, ref_obj_id = 0;
enum bpf_dynptr_type type;
- int spi, i, err;
spi = dynptr_get_spi(env, reg);
if (spi < 0)
@@ -673,82 +675,56 @@ static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_
mark_dynptr_stack_regs(env, &state->stack[spi].spilled_ptr,
&state->stack[spi - 1].spilled_ptr, type);
- if (dynptr_type_refcounted(type)) {
- /* The id is used to track proper releasing */
- int id;
-
- if (clone_ref_obj_id)
- id = clone_ref_obj_id;
- else
- id = acquire_reference(env, insn_idx);
-
- if (id < 0)
- return id;
-
- state->stack[spi].spilled_ptr.ref_obj_id = id;
- state->stack[spi - 1].spilled_ptr.ref_obj_id = id;
+ if (dynptr->type == BPF_DYNPTR_TYPE_INVALID) { /* dynptr constructors */
+ if (dynptr_type_referenced(type)) {
+ ref_obj_id = acquire_reference(env, insn_idx);
+ if (ref_obj_id < 0)
+ return ref_obj_id;
+ }
+ } else { /* bpf_dynptr_clone() */
+ ref_obj_id = dynptr->ref_obj_id;
+ parent_id = dynptr->parent_id;
}
+ state->stack[spi].spilled_ptr.ref_obj_id = ref_obj_id;
+ state->stack[spi - 1].spilled_ptr.ref_obj_id = ref_obj_id;
+ state->stack[spi].spilled_ptr.parent_id = parent_id;
+ state->stack[spi - 1].spilled_ptr.parent_id = parent_id;
+
return 0;
}
-static void invalidate_dynptr(struct bpf_verifier_env *env, struct bpf_func_state *state, int spi)
+static void invalidate_dynptr(struct bpf_verifier_env *env, struct bpf_func_state *state,
+ struct bpf_stack_state *stack)
{
int i;
for (i = 0; i < BPF_REG_SIZE; i++) {
- state->stack[spi].slot_type[i] = STACK_INVALID;
- state->stack[spi - 1].slot_type[i] = STACK_INVALID;
+ stack[0].slot_type[i] = STACK_INVALID;
+ stack[1].slot_type[i] = STACK_INVALID;
}
- bpf_mark_reg_not_init(env, &state->stack[spi].spilled_ptr);
- bpf_mark_reg_not_init(env, &state->stack[spi - 1].spilled_ptr);
+ bpf_mark_reg_not_init(env, &stack[0].spilled_ptr);
+ bpf_mark_reg_not_init(env, &stack[1].spilled_ptr);
}
static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
{
struct bpf_func_state *state = bpf_func(env, reg);
- int spi, ref_obj_id, i;
+ int spi;
spi = dynptr_get_spi(env, reg);
if (spi < 0)
return spi;
- if (!dynptr_type_refcounted(state->stack[spi].spilled_ptr.dynptr.type)) {
- invalidate_dynptr(env, state, spi);
- return 0;
- }
-
- ref_obj_id = state->stack[spi].spilled_ptr.ref_obj_id;
-
- /* If the dynptr has a ref_obj_id, then we need to invalidate
- * two things:
- *
- * 1) Any dynptrs with a matching ref_obj_id (clones)
- * 2) Any slices derived from this dynptr.
+ /*
+ * For referenced dynptr, the clones share the same ref_obj_id and will be
+ * invalidated too. For non-referenced dynptr, only the dynptr and slices
+ * derived from it will be invalidated.
*/
-
- /* Invalidate any slices associated with this dynptr */
- WARN_ON_ONCE(release_reference(env, ref_obj_id));
-
- /* Invalidate any dynptr clones */
- for (i = 1; i < state->allocated_stack / BPF_REG_SIZE; i++) {
- if (state->stack[i].spilled_ptr.ref_obj_id != ref_obj_id)
- continue;
-
- /* it should always be the case that if the ref obj id
- * matches then the stack slot also belongs to a
- * dynptr
- */
- if (state->stack[i].slot_type[0] != STACK_DYNPTR) {
- verifier_bug(env, "misconfigured ref_obj_id");
- return -EFAULT;
- }
- if (state->stack[i].spilled_ptr.dynptr.first_slot)
- invalidate_dynptr(env, state, i);
- }
-
- return 0;
+ reg = &state->stack[spi].spilled_ptr;
+ return release_reference(env, dynptr_type_referenced(reg->dynptr.type) ?
+ reg->ref_obj_id : reg->id);
}
static void __mark_reg_unknown(const struct bpf_verifier_env *env,
@@ -765,10 +741,6 @@ static void mark_reg_invalid(const struct bpf_verifier_env *env, struct bpf_reg_
static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
struct bpf_func_state *state, int spi)
{
- struct bpf_func_state *fstate;
- struct bpf_reg_state *dreg;
- int i, dynptr_id;
-
/* We always ensure that STACK_DYNPTR is never set partially,
* hence just checking for slot_type[0] is enough. This is
* different for STACK_SPILL, where it may be only set for
@@ -781,9 +753,9 @@ static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
if (!state->stack[spi].spilled_ptr.dynptr.first_slot)
spi = spi + 1;
- if (dynptr_type_refcounted(state->stack[spi].spilled_ptr.dynptr.type)) {
+ if (dynptr_type_referenced(state->stack[spi].spilled_ptr.dynptr.type)) {
int ref_obj_id = state->stack[spi].spilled_ptr.ref_obj_id;
- int ref_cnt = 0;
+ int i, ref_cnt = 0;
/*
* A referenced dynptr can be overwritten only if there is at
@@ -808,29 +780,8 @@ static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
mark_stack_slot_scratched(env, spi);
mark_stack_slot_scratched(env, spi - 1);
- /* Writing partially to one dynptr stack slot destroys both. */
- for (i = 0; i < BPF_REG_SIZE; i++) {
- state->stack[spi].slot_type[i] = STACK_INVALID;
- state->stack[spi - 1].slot_type[i] = STACK_INVALID;
- }
-
- dynptr_id = state->stack[spi].spilled_ptr.id;
- /* Invalidate any slices associated with this dynptr */
- bpf_for_each_reg_in_vstate(env->cur_state, fstate, dreg, ({
- /* Dynptr slices are only PTR_TO_MEM_OR_NULL and PTR_TO_MEM */
- if (dreg->type != (PTR_TO_MEM | PTR_MAYBE_NULL) && dreg->type != PTR_TO_MEM)
- continue;
- if (dreg->dynptr_id == dynptr_id)
- mark_reg_invalid(env, dreg);
- }));
-
- /* Do not release reference state, we are destroying dynptr on stack,
- * not using some helper to release it. Just reset register.
- */
- bpf_mark_reg_not_init(env, &state->stack[spi].spilled_ptr);
- bpf_mark_reg_not_init(env, &state->stack[spi - 1].spilled_ptr);
-
- return 0;
+ /* Invalidate the dynptr and any derived slices */
+ return release_reference(env, state->stack[spi].spilled_ptr.id);
}
static bool is_dynptr_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
@@ -1449,15 +1400,15 @@ static void release_reference_state(struct bpf_verifier_state *state, int idx)
return;
}
-static bool find_reference_state(struct bpf_verifier_state *state, int ptr_id)
+static struct bpf_reference_state *find_reference_state(struct bpf_verifier_state *state, int ptr_id)
{
int i;
for (i = 0; i < state->acquired_refs; i++)
if (state->refs[i].id == ptr_id)
- return true;
+ return &state->refs[i];
- return false;
+ return NULL;
}
static int release_lock_state(struct bpf_verifier_state *state, int type, int id, void *ptr)
@@ -1764,6 +1715,7 @@ static void __mark_reg_known(struct bpf_reg_state *reg, u64 imm)
offsetof(struct bpf_reg_state, var_off) - sizeof(reg->type));
reg->id = 0;
reg->ref_obj_id = 0;
+ reg->parent_id = 0;
___mark_reg_known(reg, imm);
}
@@ -1801,7 +1753,7 @@ static void mark_reg_known_zero(struct bpf_verifier_env *env,
}
static void __mark_dynptr_reg(struct bpf_reg_state *reg, enum bpf_dynptr_type type,
- bool first_slot, int dynptr_id)
+ bool first_slot, int id)
{
/* reg->type has no meaning for STACK_DYNPTR, but when we set reg for
* callback arguments, it does need to be CONST_PTR_TO_DYNPTR, so simply
@@ -1810,7 +1762,7 @@ static void __mark_dynptr_reg(struct bpf_reg_state *reg, enum bpf_dynptr_type ty
__mark_reg_known_zero(reg);
reg->type = CONST_PTR_TO_DYNPTR;
/* Give each dynptr a unique id to uniquely associate slices to it. */
- reg->id = dynptr_id;
+ reg->id = id;
reg->dynptr.type = type;
reg->dynptr.first_slot = first_slot;
}
@@ -2451,6 +2403,7 @@ void bpf_mark_reg_unknown_imprecise(struct bpf_reg_state *reg)
reg->type = SCALAR_VALUE;
reg->id = 0;
reg->ref_obj_id = 0;
+ reg->parent_id = 0;
reg->var_off = tnum_unknown;
reg->frameno = 0;
reg->precise = false;
@@ -7427,7 +7380,7 @@ static int process_kptr_func(struct bpf_verifier_env *env, int regno,
* and checked dynamically during runtime.
*/
static int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn_idx,
- enum bpf_arg_type arg_type, int clone_ref_obj_id,
+ enum bpf_arg_type arg_type, int parent_id,
struct bpf_dynptr_desc *dynptr)
{
struct bpf_reg_state *reg = reg_state(env, regno);
@@ -7470,7 +7423,8 @@ static int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn
return err;
}
- err = mark_stack_slots_dynptr(env, reg, arg_type, insn_idx, clone_ref_obj_id);
+ err = mark_stack_slots_dynptr(env, reg, arg_type, insn_idx, parent_id,
+ dynptr);
} else /* OBJ_RELEASE and None case from above */ {
/* For the reg->type == PTR_TO_STACK case, bpf_dynptr is never const */
if (reg->type == CONST_PTR_TO_DYNPTR && (arg_type & OBJ_RELEASE)) {
@@ -7507,6 +7461,7 @@ static int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn
dynptr->id = reg->id;
dynptr->type = reg->dynptr.type;
dynptr->ref_obj_id = reg->ref_obj_id;
+ dynptr->parent_id = reg->parent_id;
}
}
return err;
@@ -8461,7 +8416,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
*/
if (reg->type == PTR_TO_STACK) {
spi = dynptr_get_spi(env, reg);
- if (spi < 0 || !state->stack[spi].spilled_ptr.ref_obj_id) {
+ if (spi < 0 || !state->stack[spi].spilled_ptr.id) {
verbose(env, "arg %d is an unacquired reference\n", regno);
return -EINVAL;
}
@@ -8489,6 +8444,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return -EACCES;
}
meta->ref_obj_id = reg->ref_obj_id;
+ meta->id = reg->id;
}
switch (base_type(arg_type)) {
@@ -9111,26 +9067,75 @@ static int release_reference_nomark(struct bpf_verifier_state *state, int ref_ob
return -EINVAL;
}
-/* The pointer with the specified id has released its reference to kernel
- * resources. Identify all copies of the same pointer and clear the reference.
- *
- * This is the release function corresponding to acquire_reference(). Idempotent.
- */
-static int release_reference(struct bpf_verifier_env *env, int ref_obj_id)
+static int idstack_push(struct bpf_idmap *idmap, u32 id)
+{
+ int i;
+
+ if (!id)
+ return 0;
+
+ for (i = 0; i < idmap->cnt; i++)
+ if (idmap->map[i].old == id)
+ return 0;
+
+ if (WARN_ON_ONCE(idmap->cnt >= BPF_ID_MAP_SIZE))
+ return -EFAULT;
+
+ idmap->map[idmap->cnt++].old = id;
+ return 0;
+}
+
+static int idstack_pop(struct bpf_idmap *idmap)
{
+ if (!idmap->cnt)
+ return 0;
+
+ return idmap->map[--idmap->cnt].old;
+}
+
+/* Release id and objects referencing the id iteratively in a DFS manner */
+static int release_reference(struct bpf_verifier_env *env, int id)
+{
+ u32 mask = (1 << STACK_SPILL) | (1 << STACK_DYNPTR);
struct bpf_verifier_state *vstate = env->cur_state;
+ struct bpf_idmap *idstack = &env->idmap_scratch;
+ struct bpf_stack_state *stack;
struct bpf_func_state *state;
struct bpf_reg_state *reg;
- int err;
+ int root_id = id, err;
- err = release_reference_nomark(vstate, ref_obj_id);
- if (err)
- return err;
+ idstack->cnt = 0;
+ idstack_push(idstack, id);
- bpf_for_each_reg_in_vstate(vstate, state, reg, ({
- if (reg->ref_obj_id == ref_obj_id)
- mark_reg_invalid(env, reg);
- }));
+ if (find_reference_state(vstate, id))
+ WARN_ON_ONCE(release_reference_nomark(vstate, id));
+
+ while ((id = idstack_pop(idstack))) {
+ bpf_for_each_reg_in_vstate_mask(vstate, state, reg, stack, mask, ({
+ if (reg->id != id && reg->parent_id != id && reg->ref_obj_id != id)
+ continue;
+
+ if (reg->ref_obj_id && id != root_id) {
+ struct bpf_reference_state *ref_state;
+
+ ref_state = find_reference_state(env->cur_state, reg->ref_obj_id);
+ verbose(env, "Unreleased reference id=%d alloc_insn=%d when releasing id=%d\n",
+ ref_state->id, ref_state->insn_idx, root_id);
+ return -EINVAL;
+ }
+
+ if (reg->id != id) {
+ err = idstack_push(idstack, reg->id);
+ if (err)
+ return err;
+ }
+
+ if (!stack || stack->slot_type[BPF_REG_SIZE - 1] == STACK_SPILL)
+ mark_reg_invalid(env, reg);
+ else if (stack->slot_type[BPF_REG_SIZE - 1] == STACK_DYNPTR)
+ invalidate_dynptr(env, state, stack);
+ }));
+ }
return 0;
}
@@ -10298,11 +10303,8 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
*/
err = 0;
}
- if (err) {
- verbose(env, "func %s#%d reference has not been acquired before\n",
- func_id_name(func_id), func_id);
+ if (err)
return err;
- }
}
switch (func_id) {
@@ -10580,10 +10582,8 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
regs[BPF_REG_0].ref_obj_id = id;
}
- if (func_id == BPF_FUNC_dynptr_data) {
- regs[BPF_REG_0].dynptr_id = meta.dynptr.id;
- regs[BPF_REG_0].ref_obj_id = meta.dynptr.ref_obj_id;
- }
+ if (func_id == BPF_FUNC_dynptr_data)
+ regs[BPF_REG_0].parent_id = meta.dynptr.id;
err = do_refine_retval_range(env, regs, fn->ret_type, func_id, &meta);
if (err)
@@ -12009,6 +12009,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
return -EFAULT;
}
meta->ref_obj_id = reg->ref_obj_id;
+ meta->id = reg->id;
if (is_kfunc_release(meta))
meta->release_regno = regno;
}
@@ -12145,7 +12146,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
case KF_ARG_PTR_TO_DYNPTR:
{
enum bpf_arg_type dynptr_arg_type = ARG_PTR_TO_DYNPTR;
- int clone_ref_obj_id = 0;
if (is_kfunc_arg_uninit(btf, &args[i]))
dynptr_arg_type |= MEM_UNINIT;
@@ -12171,15 +12171,10 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
}
dynptr_arg_type |= (unsigned int)get_dynptr_type_flag(parent_type);
- clone_ref_obj_id = meta->dynptr.ref_obj_id;
- if (dynptr_type_refcounted(parent_type) && !clone_ref_obj_id) {
- verifier_bug(env, "missing ref obj id for parent of clone");
- return -EFAULT;
- }
}
- ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type, clone_ref_obj_id,
- &meta->dynptr);
+ ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type,
+ meta->ref_obj_id ? meta->id : 0, &meta->dynptr);
if (ret < 0)
return ret;
break;
@@ -12813,12 +12808,7 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_ca
verifier_bug(env, "no dynptr id");
return -EFAULT;
}
- regs[BPF_REG_0].dynptr_id = meta->dynptr.id;
-
- /* we don't need to set BPF_REG_0's ref obj id
- * because packet slices are not refcounted (see
- * dynptr_type_refcounted)
- */
+ regs[BPF_REG_0].parent_id = meta->dynptr.id;
} else {
return 0;
}
@@ -12953,6 +12943,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
if (rcu_lock) {
env->cur_state->active_rcu_locks++;
} else if (rcu_unlock) {
+ struct bpf_stack_state *stack;
struct bpf_func_state *state;
struct bpf_reg_state *reg;
u32 clear_mask = (1 << STACK_SPILL) | (1 << STACK_ITER);
@@ -12962,7 +12953,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
return -EINVAL;
}
if (--env->cur_state->active_rcu_locks == 0) {
- bpf_for_each_reg_in_vstate_mask(env->cur_state, state, reg, clear_mask, ({
+ bpf_for_each_reg_in_vstate_mask(env->cur_state, state, reg, stack, clear_mask, ({
if (reg->type & MEM_RCU) {
reg->type &= ~(MEM_RCU | PTR_MAYBE_NULL);
reg->type |= PTR_UNTRUSTED;
@@ -13005,9 +12996,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
err = unmark_stack_slots_dynptr(env, reg);
} else {
err = release_reference(env, reg->ref_obj_id);
- if (err)
- verbose(env, "kfunc %s#%d reference has not been acquired before\n",
- func_name, meta.func_id);
}
if (err)
return err;
@@ -13024,7 +13012,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
return err;
}
- err = release_reference(env, release_ref_obj_id);
+ err = release_reference_nomark(env->cur_state, release_ref_obj_id);
if (err) {
verbose(env, "kfunc %s#%d reference has not been acquired before\n",
func_name, meta.func_id);
@@ -13114,7 +13102,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
/* Ensures we don't access the memory after a release_reference() */
if (meta.ref_obj_id)
- regs[BPF_REG_0].ref_obj_id = meta.ref_obj_id;
+ regs[BPF_REG_0].parent_id = meta.ref_obj_id;
if (is_kfunc_rcu_protected(&meta))
regs[BPF_REG_0].type |= MEM_RCU;
--
2.52.0
^ permalink raw reply related
* [PATCH bpf-next v3 3/9] bpf: Preserve reg->id of pointer objects after null-check
From: Amery Hung @ 2026-04-21 22:10 UTC (permalink / raw)
To: bpf
Cc: netdev, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
martin.lau, mykyta.yatsenko5, ameryhung, kernel-team
In-Reply-To: <20260421221016.2967924-1-ameryhung@gmail.com>
Preserve reg->id of pointer objects after null-checking the register so
that children objects derived from it can still refer to it in the new
object relationship tracking mechanism introduced in a later patch. This
change incurs a slight increase in the number of states in one selftest
bpf object, rbtree_search.bpf.o. For Meta bpf objects, the increase of
states is also negligible.
Selftest BPF objects with insns_diff > 0
Program Insns (A) Insns (B) Insns (DIFF) States (A) States (B) States (DIFF)
------------------------ --------- --------- -------------- ---------- ---------- -------------
rbtree_search 6820 7326 +506 (+7.42%) 379 398 +19 (+5.01%)
Meta BPF objects with insns_diff > 0
Program Insns (A) Insns (B) Insns (DIFF) States (A) States (B) States (DIFF)
------------------------ --------- --------- -------------- ---------- ---------- -------------
ned_imex_be_tclass 52 57 +5 (+9.62%) 5 6 +1 (+20.00%)
ned_imex_be_tclass 52 57 +5 (+9.62%) 5 6 +1 (+20.00%)
ned_skop_auto_flowlabel 523 526 +3 (+0.57%) 39 40 +1 (+2.56%)
ned_skop_mss 289 292 +3 (+1.04%) 20 20 +0 (+0.00%)
ned_skopt_bet_classifier 78 82 +4 (+5.13%) 8 8 +0 (+0.00%)
dctcp_update_alpha 252 320 +68 (+26.98%) 21 27 +6 (+28.57%)
dctcp_update_alpha 252 320 +68 (+26.98%) 21 27 +6 (+28.57%)
ned_ts_func 119 126 +7 (+5.88%) 6 7 +1 (+16.67%)
tw_egress 1119 1128 +9 (+0.80%) 95 96 +1 (+1.05%)
tw_ingress 1128 1137 +9 (+0.80%) 95 96 +1 (+1.05%)
tw_tproxy_router 4380 4465 +85 (+1.94%) 114 118 +4 (+3.51%)
tw_tproxy_router4 3093 3170 +77 (+2.49%) 83 88 +5 (+6.02%)
ttls_tc_ingress 34656 35717 +1061 (+3.06%) 936 970 +34 (+3.63%)
tw_twfw_egress 222327 222338 +11 (+0.00%) 10563 10564 +1 (+0.01%)
tw_twfw_ingress 78295 78299 +4 (+0.01%) 3825 3826 +1 (+0.03%)
tw_twfw_tc_eg 222839 222859 +20 (+0.01%) 10584 10585 +1 (+0.01%)
tw_twfw_tc_in 78295 78299 +4 (+0.01%) 3825 3826 +1 (+0.03%)
tw_twfw_egress 8080 8085 +5 (+0.06%) 456 456 +0 (+0.00%)
tw_twfw_ingress 8053 8056 +3 (+0.04%) 454 454 +0 (+0.00%)
tw_twfw_tc_eg 8154 8174 +20 (+0.25%) 456 457 +1 (+0.22%)
tw_twfw_tc_in 8060 8063 +3 (+0.04%) 455 455 +0 (+0.00%)
tw_twfw_egress 222327 222338 +11 (+0.00%) 10563 10564 +1 (+0.01%)
tw_twfw_ingress 78295 78299 +4 (+0.01%) 3825 3826 +1 (+0.03%)
tw_twfw_tc_eg 222839 222859 +20 (+0.01%) 10584 10585 +1 (+0.01%)
tw_twfw_tc_in 78295 78299 +4 (+0.01%) 3825 3826 +1 (+0.03%)
tw_twfw_egress 8080 8085 +5 (+0.06%) 456 456 +0 (+0.00%)
tw_twfw_ingress 8053 8056 +3 (+0.04%) 454 454 +0 (+0.00%)
tw_twfw_tc_eg 8154 8174 +20 (+0.25%) 456 457 +1 (+0.22%)
tw_twfw_tc_in 8060 8063 +3 (+0.04%) 455 455 +0 (+0.00%)
Looking into rbtree_search, the reason for such increase is that the
verifier has to explore the main loop shown below for one more iteration
until state pruning decides the current state is safe.
long rbtree_search(void *ctx)
{
...
bpf_spin_lock(&glock0);
rb_n = bpf_rbtree_root(&groot0);
while (can_loop) {
if (!rb_n) {
bpf_spin_unlock(&glock0);
return __LINE__;
}
n = rb_entry(rb_n, struct node_data, r0);
if (lookup_key == n->key0)
break;
if (nr_gc < NR_NODES)
gc_ns[nr_gc++] = rb_n;
if (lookup_key < n->key0)
rb_n = bpf_rbtree_left(&groot0, rb_n);
else
rb_n = bpf_rbtree_right(&groot0, rb_n);
}
...
}
Below is what the verifier sees at the start of each iteration
(65: may_goto) after preserving id of rb_n. Without id of rb_n, the
verifier stops exploring the loop at iter 16.
rb_n gc_ns[15]
iter 15 257 257
iter 16 290 257 rb_n: idmap add 257->290
gc_ns[15]: check 257 != 290 --> state not equal
iter 17 325 257 rb_n: idmap add 290->325
gc_ns[15]: idmap add 257->257 --> state safe
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 93003a2a96b0..0313b7d5f6c9 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -15886,15 +15886,10 @@ static void mark_ptr_or_null_reg(struct bpf_func_state *state,
mark_ptr_not_null_reg(reg);
- if (!reg_may_point_to_spin_lock(reg)) {
- /* For not-NULL ptr, reg->ref_obj_id will be reset
- * in release_reference().
- *
- * reg->id is still used by spin_lock ptr. Other
- * than spin_lock ptr type, reg->id can be reset.
- */
- reg->id = 0;
- }
+ /*
+ * reg->id is preserved for object relationship tracking
+ * and spin_lock lock state tracking
+ */
}
}
--
2.52.0
^ permalink raw reply related
* [PATCH bpf-next v3 2/9] bpf: Assign reg->id when getting referenced kptr from ctx
From: Amery Hung @ 2026-04-21 22:10 UTC (permalink / raw)
To: bpf
Cc: netdev, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
martin.lau, mykyta.yatsenko5, ameryhung, kernel-team
In-Reply-To: <20260421221016.2967924-1-ameryhung@gmail.com>
Assign reg->id when getting referenced kptr from read program context
to be consistent with R0 of KF_ACQUIRE kfunc. skb dynptr will track the
referenced skb in qdisc programs using a new field reg->parent_id in
a later patch.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 41e4ea41c72e..93003a2a96b0 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6448,8 +6448,6 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
} else {
mark_reg_known_zero(env, regs,
value_regno);
- if (type_may_be_null(info.reg_type))
- regs[value_regno].id = ++env->id_gen;
/* A load of ctx field could have different
* actual load size with the one encoded in the
* insn. When the dst is PTR, it is for sure not
@@ -6459,8 +6457,11 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
if (base_type(info.reg_type) == PTR_TO_BTF_ID) {
regs[value_regno].btf = info.btf;
regs[value_regno].btf_id = info.btf_id;
+ regs[value_regno].id = info.ref_obj_id;
regs[value_regno].ref_obj_id = info.ref_obj_id;
}
+ if (type_may_be_null(info.reg_type) && !regs[value_regno].id)
+ regs[value_regno].id = ++env->id_gen;
}
regs[value_regno].type = info.reg_type;
}
--
2.52.0
^ permalink raw reply related
* [PATCH bpf-next v3 1/9] bpf: Unify dynptr handling in the verifier
From: Amery Hung @ 2026-04-21 22:10 UTC (permalink / raw)
To: bpf
Cc: netdev, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
martin.lau, mykyta.yatsenko5, ameryhung, kernel-team
In-Reply-To: <20260421221016.2967924-1-ameryhung@gmail.com>
Simplify dynptr checking for helper and kfunc by unifying it. Remember
the initialized dynptr (i.e.,g !(arg_type |= MEM_UNINIT)) pass to a
dynptr kfunc during process_dynptr_func() so that we can easily
retrieve the information for verification later. By saving it in
meta->dynptr, there is no need to call dynptr helpers such as
dynptr_id(), dynptr_ref_obj_id() and dynptr_type() in check_func_arg().
Remove and open code the helpers in process_dynptr_func() when
saving id, ref_obj_id, and type. It is okay to drop spi < 0 check as
is_dynptr_reg_valid_init() has made sure the dynptr is valid.
Besides, since dynptr ref_obj_id information is now pass around in
meta->bpf_dynptr_desc, drop the check in helper_multiple_ref_obj_use.
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf_verifier.h | 12 ++-
kernel/bpf/verifier.c | 178 +++++++----------------------------
2 files changed, 41 insertions(+), 149 deletions(-)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index b148f816f25b..dc0cff59246d 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -1319,6 +1319,12 @@ struct bpf_map_desc {
int uid;
};
+struct bpf_dynptr_desc {
+ enum bpf_dynptr_type type;
+ u32 id;
+ u32 ref_obj_id;
+};
+
struct bpf_kfunc_call_arg_meta {
/* In parameters */
struct btf *btf;
@@ -1359,16 +1365,12 @@ struct bpf_kfunc_call_arg_meta {
struct {
struct btf_field *field;
} arg_rbtree_root;
- struct {
- enum bpf_dynptr_type type;
- u32 id;
- u32 ref_obj_id;
- } initialized_dynptr;
struct {
u8 spi;
u8 frameno;
} iter;
struct bpf_map_desc map;
+ struct bpf_dynptr_desc dynptr;
u64 mem_size;
};
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 185210b73385..41e4ea41c72e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -232,6 +232,7 @@ static void bpf_map_key_store(struct bpf_insn_aux_data *aux, u64 state)
struct bpf_call_arg_meta {
struct bpf_map_desc map;
+ struct bpf_dynptr_desc dynptr;
bool raw_mode;
bool pkt_access;
u8 release_regno;
@@ -240,7 +241,6 @@ struct bpf_call_arg_meta {
int mem_size;
u64 msize_max_value;
int ref_obj_id;
- int dynptr_id;
int func_id;
struct btf *btf;
u32 btf_id;
@@ -434,11 +434,6 @@ static bool is_ptr_cast_function(enum bpf_func_id func_id)
func_id == BPF_FUNC_skc_to_tcp_request_sock;
}
-static bool is_dynptr_ref_function(enum bpf_func_id func_id)
-{
- return func_id == BPF_FUNC_dynptr_data;
-}
-
static bool is_sync_callback_calling_kfunc(u32 btf_id);
static bool is_async_callback_calling_kfunc(u32 btf_id);
static bool is_callback_calling_kfunc(u32 btf_id);
@@ -507,8 +502,6 @@ static bool helper_multiple_ref_obj_use(enum bpf_func_id func_id,
ref_obj_uses++;
if (is_acquire_function(func_id, map))
ref_obj_uses++;
- if (is_dynptr_ref_function(func_id))
- ref_obj_uses++;
return ref_obj_uses > 1;
}
@@ -7433,7 +7426,8 @@ static int process_kptr_func(struct bpf_verifier_env *env, int regno,
* and checked dynamically during runtime.
*/
static int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn_idx,
- enum bpf_arg_type arg_type, int clone_ref_obj_id)
+ enum bpf_arg_type arg_type, int clone_ref_obj_id,
+ struct bpf_dynptr_desc *dynptr)
{
struct bpf_reg_state *reg = reg_state(env, regno);
int err;
@@ -7499,6 +7493,20 @@ static int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn
}
err = mark_dynptr_read(env, reg);
+
+ if (dynptr) {
+ struct bpf_func_state *state = bpf_func(env, reg);
+ int spi;
+
+ if (reg->type != CONST_PTR_TO_DYNPTR) {
+ spi = dynptr_get_spi(env, reg);
+ reg = &state->stack[spi].spilled_ptr;
+ }
+
+ dynptr->id = reg->id;
+ dynptr->type = reg->dynptr.type;
+ dynptr->ref_obj_id = reg->ref_obj_id;
+ }
}
return err;
}
@@ -8263,72 +8271,6 @@ static int check_func_arg_reg_off(struct bpf_verifier_env *env,
}
}
-static struct bpf_reg_state *get_dynptr_arg_reg(struct bpf_verifier_env *env,
- const struct bpf_func_proto *fn,
- struct bpf_reg_state *regs)
-{
- struct bpf_reg_state *state = NULL;
- int i;
-
- for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++)
- if (arg_type_is_dynptr(fn->arg_type[i])) {
- if (state) {
- verbose(env, "verifier internal error: multiple dynptr args\n");
- return NULL;
- }
- state = ®s[BPF_REG_1 + i];
- }
-
- if (!state)
- verbose(env, "verifier internal error: no dynptr arg found\n");
-
- return state;
-}
-
-static int dynptr_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
-{
- struct bpf_func_state *state = bpf_func(env, reg);
- int spi;
-
- if (reg->type == CONST_PTR_TO_DYNPTR)
- return reg->id;
- spi = dynptr_get_spi(env, reg);
- if (spi < 0)
- return spi;
- return state->stack[spi].spilled_ptr.id;
-}
-
-static int dynptr_ref_obj_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
-{
- struct bpf_func_state *state = bpf_func(env, reg);
- int spi;
-
- if (reg->type == CONST_PTR_TO_DYNPTR)
- return reg->ref_obj_id;
- spi = dynptr_get_spi(env, reg);
- if (spi < 0)
- return spi;
- return state->stack[spi].spilled_ptr.ref_obj_id;
-}
-
-static enum bpf_dynptr_type dynptr_get_type(struct bpf_verifier_env *env,
- struct bpf_reg_state *reg)
-{
- struct bpf_func_state *state = bpf_func(env, reg);
- int spi;
-
- if (reg->type == CONST_PTR_TO_DYNPTR)
- return reg->dynptr.type;
-
- spi = bpf_get_spi(reg->var_off.value);
- if (spi < 0) {
- verbose(env, "verifier internal error: invalid spi when querying dynptr type\n");
- return BPF_DYNPTR_TYPE_INVALID;
- }
-
- return state->stack[spi].spilled_ptr.dynptr.type;
-}
-
static int check_reg_const_str(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, u32 regno)
{
@@ -8683,7 +8625,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
true, meta);
break;
case ARG_PTR_TO_DYNPTR:
- err = process_dynptr_func(env, regno, insn_idx, arg_type, 0);
+ err = process_dynptr_func(env, regno, insn_idx, arg_type, 0, &meta->dynptr);
if (err)
return err;
break;
@@ -9342,7 +9284,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
if (ret)
return ret;
- ret = process_dynptr_func(env, regno, -1, arg->arg_type, 0);
+ ret = process_dynptr_func(env, regno, -1, arg->arg_type, 0, NULL);
if (ret)
return ret;
} else if (base_type(arg->arg_type) == ARG_PTR_TO_BTF_ID) {
@@ -10429,52 +10371,10 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
}
}
break;
- case BPF_FUNC_dynptr_data:
- {
- struct bpf_reg_state *reg;
- int id, ref_obj_id;
-
- reg = get_dynptr_arg_reg(env, fn, regs);
- if (!reg)
- return -EFAULT;
-
-
- if (meta.dynptr_id) {
- verifier_bug(env, "meta.dynptr_id already set");
- return -EFAULT;
- }
- if (meta.ref_obj_id) {
- verifier_bug(env, "meta.ref_obj_id already set");
- return -EFAULT;
- }
-
- id = dynptr_id(env, reg);
- if (id < 0) {
- verifier_bug(env, "failed to obtain dynptr id");
- return id;
- }
-
- ref_obj_id = dynptr_ref_obj_id(env, reg);
- if (ref_obj_id < 0) {
- verifier_bug(env, "failed to obtain dynptr ref_obj_id");
- return ref_obj_id;
- }
-
- meta.dynptr_id = id;
- meta.ref_obj_id = ref_obj_id;
-
- break;
- }
case BPF_FUNC_dynptr_write:
{
- enum bpf_dynptr_type dynptr_type;
- struct bpf_reg_state *reg;
+ enum bpf_dynptr_type dynptr_type = meta.dynptr.type;
- reg = get_dynptr_arg_reg(env, fn, regs);
- if (!reg)
- return -EFAULT;
-
- dynptr_type = dynptr_get_type(env, reg);
if (dynptr_type == BPF_DYNPTR_TYPE_INVALID)
return -EFAULT;
@@ -10665,10 +10565,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
return -EFAULT;
}
- if (is_dynptr_ref_function(func_id))
- regs[BPF_REG_0].dynptr_id = meta.dynptr_id;
-
- if (is_ptr_cast_function(func_id) || is_dynptr_ref_function(func_id)) {
+ if (is_ptr_cast_function(func_id)) {
/* For release_reference() */
regs[BPF_REG_0].ref_obj_id = meta.ref_obj_id;
} else if (is_acquire_function(func_id, meta.map.ptr)) {
@@ -10682,6 +10579,11 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
regs[BPF_REG_0].ref_obj_id = id;
}
+ if (func_id == BPF_FUNC_dynptr_data) {
+ regs[BPF_REG_0].dynptr_id = meta.dynptr.id;
+ regs[BPF_REG_0].ref_obj_id = meta.dynptr.ref_obj_id;
+ }
+
err = do_refine_retval_range(env, regs, fn->ret_type, func_id, &meta);
if (err)
return err;
@@ -12260,7 +12162,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
meta->release_regno = regno;
} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_clone] &&
(dynptr_arg_type & MEM_UNINIT)) {
- enum bpf_dynptr_type parent_type = meta->initialized_dynptr.type;
+ enum bpf_dynptr_type parent_type = meta->dynptr.type;
if (parent_type == BPF_DYNPTR_TYPE_INVALID) {
verifier_bug(env, "no dynptr type for parent of clone");
@@ -12268,29 +12170,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
}
dynptr_arg_type |= (unsigned int)get_dynptr_type_flag(parent_type);
- clone_ref_obj_id = meta->initialized_dynptr.ref_obj_id;
+ clone_ref_obj_id = meta->dynptr.ref_obj_id;
if (dynptr_type_refcounted(parent_type) && !clone_ref_obj_id) {
verifier_bug(env, "missing ref obj id for parent of clone");
return -EFAULT;
}
}
- ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type, clone_ref_obj_id);
+ ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type, clone_ref_obj_id,
+ &meta->dynptr);
if (ret < 0)
return ret;
-
- if (!(dynptr_arg_type & MEM_UNINIT)) {
- int id = dynptr_id(env, reg);
-
- if (id < 0) {
- verifier_bug(env, "failed to obtain dynptr id");
- return id;
- }
- meta->initialized_dynptr.id = id;
- meta->initialized_dynptr.type = dynptr_get_type(env, reg);
- meta->initialized_dynptr.ref_obj_id = dynptr_ref_obj_id(env, reg);
- }
-
break;
}
case KF_ARG_PTR_TO_ITER:
@@ -12894,7 +12784,7 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_ca
}
} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice] ||
meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice_rdwr]) {
- enum bpf_type_flag type_flag = get_dynptr_type_flag(meta->initialized_dynptr.type);
+ enum bpf_type_flag type_flag = get_dynptr_type_flag(meta->dynptr.type);
mark_reg_known_zero(env, regs, BPF_REG_0);
@@ -12918,11 +12808,11 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_ca
}
}
- if (!meta->initialized_dynptr.id) {
+ if (!meta->dynptr.id) {
verifier_bug(env, "no dynptr id");
return -EFAULT;
}
- regs[BPF_REG_0].dynptr_id = meta->initialized_dynptr.id;
+ regs[BPF_REG_0].dynptr_id = meta->dynptr.id;
/* we don't need to set BPF_REG_0's ref obj id
* because packet slices are not refcounted (see
@@ -13110,7 +13000,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
if (meta.release_regno) {
struct bpf_reg_state *reg = ®s[meta.release_regno];
- if (meta.initialized_dynptr.ref_obj_id) {
+ if (meta.dynptr.ref_obj_id) {
err = unmark_stack_slots_dynptr(env, reg);
} else {
err = release_reference(env, reg->ref_obj_id);
--
2.52.0
^ permalink raw reply related
* [PATCH bpf-next v3 0/9] Refactor verifier object relationship tracking
From: Amery Hung @ 2026-04-21 22:10 UTC (permalink / raw)
To: bpf
Cc: netdev, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
martin.lau, mykyta.yatsenko5, ameryhung, kernel-team
Hi all,
This patchset cleans up dynptr handling, refactors object relationship
tracking in the verifier by introducing parent_id, and fixes dynptr
use-after-free bugs where file/skb dynptrs are not invalidated when
the parent referenced object is freed.
* Motivation *
In BPF qdisc programs, an skb can be freed through kfuncs. However,
since dynptr does not track the parent referenced object (e.g., skb),
the verifier does not invalidate the dynptr after the skb is freed,
resulting in use-after-free. The same issue also affects file dynptr.
The figure below shows the current state of object tracking. The
verifier tracks objects using three fields: id for nullness tracking,
ref_obj_id for lifetime tracking, and dynptr_id for tracking the parent
dynptr of a slice (PTR_TO_MEM only). While dynptr_id links slices to
their parent dynptr, there is no field that links a dynptr back to its
parent skb. When the skb is freed via release_reference(ref_obj_id=1),
only objects with ref_obj_id=1 are invalidated. Since skb dynptr is
non-referenced (ref_obj_id=0), the dynptr and its derived slices remain
accessible.
Current: object (id, ref_obj_id, dynptr_id)
id = unique id of the object (for nullness tracking)
ref_obj_id = id of the referenced object (for lifetime tracking)
dynptr_id = id of the parent dynptr (only for PTR_TO_MEM slices)
skb (0,1,0)
^
! No link from dynptr to skb
+-------------------------------+
| bpf_dynptr_clone |
dynptr A (2,0,0) dynptr C (4,0,0)
^ ^
bpf_dynptr_slice | |
| |
slice B (3,0,2) slice D (5,0,4)
* Why not simply use ref_obj_id to track the parent? *
A natural first approach is to link dynptr to its parent by sharing
the parent's ref_obj_id and propagating it to slices. Now, releasing
the skb via release_reference(ref_obj_id=1) correctly invalidates all
derived objects.
Attempted fix: share parent's ref_obj_id
skb (0,1,0)
^
+-------------------------------+
| bpf_dynptr_clone |
dynptr A (2,1,0) dynptr C (4,1,0)
^ ^
bpf_dynptr_slice | |
| |
slice B (3,1,2) slice D (5,1,4)
However, this approach does not generalize to all dynptr types.
Referenced dynptrs such as file dynptr acquire their own ref_obj_id to
track the dynptr's lifetime. Since ref_obj_id is already
used for the dynptr's own reference, it cannot also be used to point to
the parent file object. While it is possible to add specialized handling
for individual dynptr types [0], it adds complexity and does not
generalize.
An alternative approach is to avoid introducing a new field and instead
repurpose ref_obj_id as parent_id by folding lifetime tracking into id
[1]. In this design, each object is represented as (id, ref_obj_id)
where id is used for both nullness and lifetime tracking, and ref_obj_id
tracks the parent object's id.
Attempted: object (id, ref_obj_id)
id = id of the object (for nullness and lifetime tracking)
ref_obj_id = id of the parent object
' = id is referenced
skb (1',0)
^
bpf_dynptr_from_skb +-------------------------------+
| bpf_dynptr_clone(A, C) |
dynptr A (2,1') dynptr C (4,1')
^ ^
bpf_dynptr_slice | |
| |
slice B (3,2) slice D (5,4)
However, this design cannot express the relationship between referenced
socket pointers and their casted counterparts. After pointer casting,
the original and casted pointers need the same lifetime (same ref_obj_id
in the current design) but different nullness (different id). The casted
pointer may be NULL even if the original is valid. With id serving as
the only field for both nullness and lifetime, and ref_obj_id repurposed
as parent, there is no way to express "different identity, same
lifetime."
Referenced socket pointer (expressed using current design):
C = ptr_casting_function(A)
ptr A (1,1,0) ptr C (2,1,0)
^ ^
| |
ptr C may be NULL even if ptr A is valid
but they have the same lifetime
* New Design: parent_id *
To track precise object relationships, u32 parent_id is added to
bpf_reg_state. A child object's parent_id points to the parent
object's id. This replaces the PTR_TO_MEM-specific dynptr_id, and
does not increase the size of bpf_reg_state on 64-bit machines as
there is existing padding.
After: object (id, ref_obj_id, parent_id)
id = unique id of the object (for nullness tracking)
ref_obj_id = id of the referenced object; objects with the same
ref_obj_id share the same lifetime
parent_id = id of the parent object; points to parent's id
(for object relationship tracking)
skb (1,1,0)
^
bpf_dynptr_from_skb +-------------------------------+
| bpf_dynptr_clone(A, C) |
dynptr A (2,0,1) dynptr C (4,0,1)
^ ^
bpf_dynptr_slice | |
| |
slice B (3,0,2) slice D (5,0,4)
^
bpf_dynptr_from_mem |
(NOT allowed yet) |
dynptr E (6,0,3)
With parent_id, the verifier can precisely track object trees. When the
skb is freed, the verifier traverses the tree rooted at skb (id=1) and
invalidates all descendants — dynptr A, dynptr C, and their slices.
When dynptr A is destroyed by overwriting the stack slot, only dynptr A
and its children (slice B, dynptr E) are invalidated; skb, dynptr C,
and slice D remain valid.
For referenced dynptr (e.g., file dynptr), the original and its clones
share the same ref_obj_id so they are all invalidated together when any
one of them is released. For non-referenced dynptr (e.g., skb dynptr),
clones live independently since they have ref_obj_id=0.
To avoid recursive call chains when releasing objects (e.g.,
release_reference() -> unmark_stack_slots_dynptr() ->
release_reference()), release_reference() now uses stack-based DFS to
find and invalidate all registers and stack slots with matching id or
ref_obj_id and all descendants whose parent_id matches. Currently, it
skips id == 0, which could be a valid id (e.g., pkt pointer by reading
ctx). Future work may start assigning > 0 id to them. This does not
affect the current use cases where skb and file parents are both given
id > 0.
* Preserving reg->id after null-check *
For parent_id tracking to work, child objects need to refer to the
parent's id. This requires two preparatory changes: assigning reg->id
when reading referenced kptrs from program context (patch 2), and
preserving reg->id of pointer objects after null-check (patch 3).
Previously, null-check would clear reg->id, making it impossible for
children to reference the parent afterward. The latter causes a slight
increase in verified states for some programs. One selftest object
sees +19 states (+5.01%). For Meta BPF objects, the increase is
also minor, with the largest being +34 states (+3.63%).
* Object relationship in different scenarios (for reference) *
The figures below show how the new design handles all four combinations
of referenced/non-referenced dynptr with referenced/non-referenced
parent. The relationship between slices and dynptrs is omitted as it
is the same across all cases. The main difference is how cloned dynptrs
are represented. Since bpf_dynptr_clone() does not initialize a new
reference, clones of referenced dynptrs share the same ref_obj_id and
must be invalidated together. For non-referenced dynptrs, the original
and clones live independently.
(1) Non-referenced dynptr with referenced parent (e.g., skb in Qdisc):
skb (1,1,0)
^
bpf_dynptr_from_skb +-------------------------------+
| bpf_dynptr_clone(A, C) |
dynptr A (2,0,1) dynptr C (4,0,1)
(2) Non-referenced dynptr with non-referenced parent (e.g., skb in TC,
always valid):
bpf_dynptr_from_skb
bpf_dynptr_clone(A, C)
dynptr A (1,0,0) dynptr C (2,0,0)
dynptr A and C live independently
(3) Referenced dynptr with referenced parent:
file (1,1,0)
^ ^
bpf_dynptr_from_file | +-------------------------------+
| bpf_dynptr_clone(A, C) |
dynptr A (2,3,1) dynptr C (4,3,1)
^ ^
| |
dynptr A and C have the same lifetime
(4) Referenced dynptr with non-referenced parent:
bpf_ringbuf_reserve_dynptr
bpf_dynptr_clone(A, C)
dynptr A (1,1,0) dynptr C (2,1,0)
^ ^
| |
dynptr A and C have the same lifetime
[0] https://lore.kernel.org/bpf/20250414161443.1146103-2-memxor@gmail.com/
[1] https://github.com/ameryhung/bpf/commits/obj_relationship_v2_no_parent_id/
Changelog:
v2 -> v3
- Rebase to bpf-next/master
- Update veristat numbers
- Update commit msg to explain multiple dropped checks (Mykyta, Andrii)
- Reuse idmap as idstack in release_reference() and check for
duplicate id (Mykyta, Andrii)
- Change to use RUN_TEST for qdisc dynptr selftest (Eduard)
Link: https://lore.kernel.org/bpf/20260307064439.3247440-1-ameryhung@gmail.com/
v1 -> v2
- Redesign: Use object (id, ref_obj_id, parent_id) instead of
(id, ref_obj_id) as it cannot express ptr casting without
introducing specialized code to handle the case
- Use stack-based DFS to release objects to avoid recursion (Andrii)
- Keep reg->id after null check
- Add dynptr cleanup
- Fix dynptr kfunc arg type determination
- Add a file dynptr UAF selftest
Link: https://lore.kernel.org/bpf/20260202214817.2853236-1-ameryhung@gmail.com/
---
Amery Hung (9):
bpf: Unify dynptr handling in the verifier
bpf: Assign reg->id when getting referenced kptr from ctx
bpf: Preserve reg->id of pointer objects after null-check
bpf: Refactor object relationship tracking and fix dynptr UAF bug
bpf: Remove redundant dynptr arg check for helper
selftests/bpf: Test creating dynptr from dynptr data and slice
selftests/bpf: Test using dynptr after freeing the underlying object
selftests/bpf: Test using slice after invalidating dynptr clone
selftests/bpf: Test using file dynptr after the reference on file is
dropped
include/linux/bpf_verifier.h | 34 +-
kernel/bpf/log.c | 4 +-
kernel/bpf/states.c | 9 +-
kernel/bpf/verifier.c | 461 ++++++------------
.../selftests/bpf/prog_tests/bpf_qdisc.c | 8 +
..._qdisc_dynptr_use_after_invalidate_clone.c | 75 +++
.../progs/bpf_qdisc_fail__invalid_dynptr.c | 68 +++
...f_qdisc_fail__invalid_dynptr_cross_frame.c | 74 +++
.../bpf_qdisc_fail__invalid_dynptr_slice.c | 70 +++
.../testing/selftests/bpf/progs/dynptr_fail.c | 48 +-
.../selftests/bpf/progs/file_reader_fail.c | 60 +++
.../selftests/bpf/progs/user_ringbuf_fail.c | 4 +-
12 files changed, 593 insertions(+), 322 deletions(-)
create mode 100644 tools/testing/selftests/bpf/progs/bpf_qdisc_dynptr_use_after_invalidate_clone.c
create mode 100644 tools/testing/selftests/bpf/progs/bpf_qdisc_fail__invalid_dynptr.c
create mode 100644 tools/testing/selftests/bpf/progs/bpf_qdisc_fail__invalid_dynptr_cross_frame.c
create mode 100644 tools/testing/selftests/bpf/progs/bpf_qdisc_fail__invalid_dynptr_slice.c
--
2.52.0
^ permalink raw reply
* Re: [PATCH net 00/18] Remove a number of ISA and PCMCIA Ethernet drivers
From: Daniel Palmer @ 2026-04-21 22:03 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
linux-kernel, netdev, linux-doc
In-Reply-To: <20260421-v7-0-0-net-next-driver-removal-v1-v1-0-69517c689d1f@lunn.ch>
Hi Andrew,
On Wed, 22 Apr 2026 at 04:32, Andrew Lunn <andrew@lunn.ch> wrote:
>
> These old drivers have not been much of a Maintenance burden until
> recently. Now there are more newbies using AI and fuzzers finding
> issues, resulting in more work for Maintainers. Fixing these old
> drivers make little sense, if it is not clear they have users.
>
> These are all ISA and PCMCIA Ethernet devices, mostly from the last
> century, a couple from 2001 or 2002. It seems unlikely they are still
> used. However, remove them one patch at a time so they can be brought
> back if somebody still has the hardware, runs modern kernels and wants
> to take up the roll of driver Maintainer.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
I replied to the single patches for the ones I think I have off the
top of my head but maybe I have a few other these.
I think on x86 people running machines that have real ISA slots and a
modern kernel are going to be fairly rare but I think there is non-x86
hardware that has some of these and people are still using them.
I think the pcnet one is used in some PC emulators too?
This is just my opinion but I think it'd be good to give people a bit
more time before they get removed. I thought I was the only person
using the 68000 (a cpu from 1979) code in the m68k tree for years
until out of the blue a month or so ago someone popped up and asked if
I could fix a bug in it since I'd fixed a different bug in it
recently.
Maybe we could add a special thing in the maintainers for "this is
code only crazy people use" and have a rule to ignore untested AI
generated patches for it? :)
Thanks,
Daniel
^ permalink raw reply
* Re: [PATCH net 06/18] drivers: net: amd: Remove hplance and mvme147
From: Daniel Palmer @ 2026-04-21 21:38 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
linux-kernel, netdev, linux-doc
In-Reply-To: <20260421-v7-0-0-net-next-driver-removal-v1-v1-6-69517c689d1f@lunn.ch>
Hi Andrew,
On Wed, 22 Apr 2026 at 04:39, Andrew Lunn <andrew@lunn.ch> wrote:
>
> These drivers use the 7990 core with wrappers for the HP300 and
> Motorola MVME147 SBC circa 1998. It is unlikely they are used with a
> modern kernel.
I have an MVME147 blinking away running mainline using the mvme147 driver.
I think some of these need to be CC'd to the specific arch lists so
the few people using them get a chance to pipe up.
I think I'm the last person to have touched the mvme147, I don't mind
being a maintainer for it.
Cheers,
Daniel
^ permalink raw reply
* Re: [PATCH net 12/18] drivers: net: cirrus: mac89x0: Remove this driver
From: Daniel Palmer @ 2026-04-21 21:34 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
linux-kernel, netdev, linux-doc
In-Reply-To: <20260421-v7-0-0-net-next-driver-removal-v1-v1-12-69517c689d1f@lunn.ch>
Hi Andrew,
On Wed, 22 Apr 2026 at 05:01, Andrew Lunn <andrew@lunn.ch> wrote:
>
> The mac89x0 was written by Russell Nelson in 1996. It is an MAC
> device, so unlikely to be used with modern kernels.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
I think I might be using this with a mac and I'm running mainline.
FWIW the m68k mac stuff still works perfectly fine with modern kernels
and there are people using it.
^ permalink raw reply
* Re: [PATCH] dt-bindings: Fix phandle-array constraints, again
From: Rob Herring (Arm) @ 2026-04-21 21:33 UTC (permalink / raw)
To: Rob Herring (Arm)
Cc: Sylwester Nawrocki, ath11k, devicetree, linux-pci,
Mathieu Poirier, Conor Dooley, Xu Yang, Lorenzo Pieralisi,
Andrew Lunn, linux-spi, Patrice Chotard, Rao Mandadapu,
Mark Brown, linux-sound, Maarten Lankhorst, Bjorn Andersson,
linux-remoteproc, Maxime Coquelin, Thomas Zimmermann, Sibi Sankar,
linux-wireless, Johannes Berg, Yang Xiwen, Chaitanya Chundru,
Alex Elder, Manivannan Sadhasivam, Ulf Hansson, Stephan Gerhold,
linux-kernel, Eric Dumazet, Paolo Abeni, Jakub Kicinski,
linux-arm-msm, linux-usb, Bjorn Helgaas, Jeff Johnson, linux-mmc,
Krzysztof Wilczyński, Krzysztof Kozlowski, Peng Fan,
David S. Miller, Greg Kroah-Hartman, ath10k, netdev,
Maxime Ripard
In-Reply-To: <20260421195836.1547469-1-robh@kernel.org>
On Tue, 21 Apr 2026 14:55:25 -0500, Rob Herring (Arm) wrote:
> The unfortunately named 'phandle-array' property type is really a matrix
> with phandle and fixed arg cells entries. A matrix property should have 2
> levels of items constraints.
>
> Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
> ---
> Can someone from QCom provide some descriptions for 'qcom,smem-states'
> properties.
> ---
> .../display/rockchip/rockchip,rk3399-cdn-dp.yaml | 2 ++
> .../bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml | 7 ++++---
> Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml | 6 ++++++
> Documentation/devicetree/bindings/net/qcom,ipa.yaml | 6 ++++++
> .../devicetree/bindings/net/wireless/qcom,ath10k.yaml | 5 ++++-
> .../devicetree/bindings/net/wireless/qcom,ath11k.yaml | 5 ++++-
> .../bindings/net/wireless/qcom,ipq5332-wifi.yaml | 9 +++++++++
> .../devicetree/bindings/pci/toshiba,tc9563.yaml | 5 +++--
> .../bindings/remoteproc/qcom,msm8916-mss-pil.yaml | 3 +++
> .../bindings/remoteproc/qcom,msm8996-mss-pil.yaml | 3 +++
> .../devicetree/bindings/remoteproc/qcom,pas-common.yaml | 4 ++++
> .../bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml | 4 ++++
> .../bindings/remoteproc/qcom,sc7180-mss-pil.yaml | 3 +++
> .../bindings/remoteproc/qcom,sc7280-adsp-pil.yaml | 3 +++
> .../bindings/remoteproc/qcom,sc7280-mss-pil.yaml | 3 +++
> .../bindings/remoteproc/qcom,sc7280-wpss-pil.yaml | 3 +++
> .../bindings/remoteproc/qcom,sdm845-adsp-pil.yaml | 3 +++
> .../devicetree/bindings/remoteproc/qcom,wcnss-pil.yaml | 3 +++
> Documentation/devicetree/bindings/sound/samsung,tm2.yaml | 2 ++
> .../devicetree/bindings/spi/st,stm32mp25-ospi.yaml | 5 +++--
> .../devicetree/bindings/usb/chipidea,usb2-common.yaml | 2 ++
> Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml | 7 ++++---
> 22 files changed, 81 insertions(+), 12 deletions(-)
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
./Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml:99:1: [warning] too many blank lines (2 > 1) (empty-lines)
./Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml:67:1: [warning] too many blank lines (2 > 1) (empty-lines)
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/i2c/i2c-demux-pinctrl.example.dtb: i2c-mux3 (i2c-demux-pinctrl): i2c-parent:0: [2, 3, 4] is too long
from schema $id: http://devicetree.org/schemas/i2c/i2c-demux-pinctrl.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/sound/samsung,tm2.example.dtb: sound (samsung,tm2-audio): i2s-controller: [[4294967295], [0], [4294967295], [0]] is too long
from schema $id: http://devicetree.org/schemas/sound/samsung,tm2.yaml
doc reference errors (make refcheckdocs):
See https://patchwork.kernel.org/project/devicetree/patch/20260421195836.1547469-1-robh@kernel.org
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply
* Re: [PATCH net 2/4] rxrpc: Fix conn-level packet handling to unshare RESPONSE packets
From: David Howells @ 2026-04-21 20:58 UTC (permalink / raw)
To: Simon Horman
Cc: dhowells, netdev, marc.dionne, kuba, davem, edumazet, pabeni,
linux-afs, linux-kernel, jaltman, stable
In-Reply-To: <20260421203833.745240-1-horms@kernel.org>
Simon Horman <horms@kernel.org> wrote:
> This isn't a new bug introduced by this patch, but since we are modifying
> the unshare path here: when rxrpc_unshare_skb() fails, it sets *_skb = NULL
> and returns just_discard.
Is there a way to avoid having skb_unshare() eat your ref to the source skbuff
if it fails?
David
^ permalink raw reply
* Re: [syzbot] [kvm?] [net?] [virt?] BUG: sleeping function called from invalid context in vhost_get_avail_idx
From: Michael S. Tsirkin @ 2026-04-21 20:54 UTC (permalink / raw)
To: Kohei Enju; +Cc: syzbot, jasowang, linux-kernel, netdev, syzkaller-bugs
In-Reply-To: <aeerTzpq8B-WTKeC@x1>
On Wed, Apr 22, 2026 at 02:11:01AM +0900, Kohei Enju wrote:
> On 04/20 15:09, syzbot wrote:
> > Hello,
> >
> > syzbot found the following issue on:
> >
> > HEAD commit: 8541d8f725c6 Merge tag 'mtd/for-7.1' of git://git.kernel.o..
> > git tree: upstream
> > console output: https://syzkaller.appspot.com/x/log.txt?x=136454ce580000
> > kernel config: https://syzkaller.appspot.com/x/.config?x=7e54da1916e8d11f
> > dashboard link: https://syzkaller.appspot.com/bug?extid=6985cb8e543ea90ba8ee
> > compiler: gcc (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for Debian) 2.44
> > syz repro: https://syzkaller.appspot.com/x/repro.syz?x=15d264ce580000
> > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=143ec1ba580000
> >
> > Downloadable assets:
> > disk image (non-bootable): https://storage.googleapis.com/syzbot-assets/d900f083ada3/non_bootable_disk-8541d8f7.raw.xz
> > vmlinux: https://storage.googleapis.com/syzbot-assets/22dfea2c37c2/vmlinux-8541d8f7.xz
> > kernel image: https://storage.googleapis.com/syzbot-assets/e2f93ad68fe3/bzImage-8541d8f7.xz
> >
> > IMPORTANT: if you fix the issue, please add the following tag to the commit:
> > Reported-by: syzbot+6985cb8e543ea90ba8ee@syzkaller.appspotmail.com
> >
> > BUG: sleeping function called from invalid context at drivers/vhost/vhost.c:1527
> > in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 6110, name: vhost-6109
> > preempt_count: 1, expected: 0
> > RCU nest depth: 0, expected: 0
> > 2 locks held by vhost-6109/6110:
> > #0: ffff888055624cb0 (&vq->mutex/1){+.+.}-{4:4}, at: handle_tx+0x2d/0x160 drivers/vhost/net.c:971
> > #1: ffff888055620248 (&vq->mutex){+.+.}-{4:4}, at: vhost_net_busy_poll+0x9c/0x730 drivers/vhost/net.c:554
> > Preemption disabled at:
> > [<ffffffff88f1a006>] vhost_net_busy_poll+0x1c6/0x730 drivers/vhost/net.c:563
>
> I think the blamed commit may be commit 030881372460 ("vhost_net: basic
> polling support"), since it introduced preempt_{disable,enable}() around
> the busy-poll loop, which calls a sleepable function inside the loop.
>
> Also, from the changelog of the series,
>
> https://lore.kernel.org/netdev/1448435489-5949-4-git-send-email-jasowang@redhat.com/T/#u
>
> Changes from RFC V1:
> ...
> - Disable preemption during busy looping to make sure local_clock() was
> correctly used.
>
> So my understanding is that preempt_disable() was introduced to keep
> local_clock() based timeout accounting on a single CPU, rather than as a
> requirement of busy polling itself.
>
> If my understanding is correct, migrate_disable() is sufficient here
> instead of preempt_disable(), avoiding sleepable accesses from a
> preempt-disabled context.
>
> #syz test
>
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index 80965181920c..c6536cad9c4f 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -560,7 +560,7 @@ static void vhost_net_busy_poll(struct vhost_net *net,
> busyloop_timeout = poll_rx ? rvq->busyloop_timeout:
> tvq->busyloop_timeout;
>
> - preempt_disable();
> + migrate_disable();
> endtime = busy_clock() + busyloop_timeout;
>
> while (vhost_can_busy_poll(endtime)) {
> @@ -577,7 +577,7 @@ static void vhost_net_busy_poll(struct vhost_net *net,
> cpu_relax();
> }
>
> - preempt_enable();
> + migrate_enable();
>
> if (poll_rx || sock_has_rx_data(sock))
> vhost_net_busy_poll_try_queue(net, vq);
Makes sense but this stipped up the bot. Try again?
>
> > CPU: 0 UID: 0 PID: 6110 Comm: vhost-6109 Not tainted syzkaller #0 PREEMPT(full)
> > Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
> > Call Trace:
> > <TASK>
> > __dump_stack lib/dump_stack.c:94 [inline]
> > dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
> > __might_resched.cold+0x1ec/0x232 kernel/sched/core.c:9162
> > __might_fault+0x8b/0x140 mm/memory.c:7322
> > vhost_get_avail_idx+0x31c/0x4f0 drivers/vhost/vhost.c:1527
> > vhost_vq_avail_empty drivers/vhost/vhost.c:3206 [inline]
> > vhost_vq_avail_empty+0xa9/0xe0 drivers/vhost/vhost.c:3199
> > vhost_net_busy_poll+0x297/0x730 drivers/vhost/net.c:574
> > vhost_net_tx_get_vq_desc drivers/vhost/net.c:610 [inline]
> > get_tx_bufs.constprop.0+0x338/0x600 drivers/vhost/net.c:650
> > handle_tx_copy+0x28c/0x12e0 drivers/vhost/net.c:778
> > handle_tx+0x139/0x160 drivers/vhost/net.c:985
> > vhost_run_work_list+0x183/0x220 drivers/vhost/vhost.c:454
> > vhost_task_fn+0x156/0x430 kernel/vhost_task.c:49
> > ret_from_fork+0x72b/0xd50 arch/x86/kernel/process.c:158
> > ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
> > </TASK>
> >
> >
> > ---
> > This report is generated by a bot. It may contain errors.
> > See https://goo.gl/tpsmEJ for more information about syzbot.
> > syzbot engineers can be reached at syzkaller@googlegroups.com.
> >
> > syzbot will keep track of this issue. See:
> > https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
> >
> > If the report is already addressed, let syzbot know by replying with:
> > #syz fix: exact-commit-title
> >
> > If you want syzbot to run the reproducer, reply with:
> > #syz test: git://repo/address.git branch-or-commit-hash
> > If you attach or paste a git patch, syzbot will apply it before testing.
> >
> > If you want to overwrite report's subsystems, reply with:
> > #syz set subsystems: new-subsystem
> > (See the list of subsystem names on the web dashboard)
> >
> > If the report is a duplicate of another one, reply with:
> > #syz dup: exact-subject-of-another-report
> >
> > If you want to undo deduplication, reply with:
> > #syz undup
^ permalink raw reply
* Re: [PATCH net 00/18] Remove a number of ISA and PCMCIA Ethernet drivers
From: Byron Stanoszek @ 2026-04-21 20:44 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
linux-kernel, netdev, linux-doc
In-Reply-To: <20260421-v7-0-0-net-next-driver-removal-v1-v1-0-69517c689d1f@lunn.ch>
On Tue, 21 Apr 2026, Andrew Lunn wrote:
> These old drivers have not been much of a Maintenance burden until
> recently. Now there are more newbies using AI and fuzzers finding
> issues, resulting in more work for Maintainers. Fixing these old
> drivers make little sense, if it is not clear they have users.
>
> drivers: net: 3com: 3c59x: Remove this driver
Hi Andrew,
I happen to still use this driver on several hundred industrial PC
installations that were outfitted with 3com 3C905-B & CX cards 15+ years ago.
The old hardware still runs, therefore those cards haven't needed to be
replaced. I keep these systems up to date with the latest Linux kernel roughly
once per year.
I understand the maintenance burden, but I would be delighted to continue
receiving bug fixes for this driver via the mainline Linux kernel if you are
still willing to continue to support it.
Thanks and best regards,
-Byron
^ permalink raw reply
* Re: [PATCH net 2/4] rxrpc: Fix conn-level packet handling to unshare RESPONSE packets
From: Simon Horman @ 2026-04-21 20:38 UTC (permalink / raw)
To: dhowells
Cc: 'Simon Horman', netdev, marc.dionne, kuba, davem,
edumazet, pabeni, linux-afs, linux-kernel, jaltman, stable
In-Reply-To: <20260420145900.1223732-3-dhowells@redhat.com>
From: 'Simon Horman' <horms@kernel.org>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
Subject: rxrpc: Fix conn-level packet handling to unshare RESPONSE packets
This commit modifies the RxRPC connection-level packet processor to unshare
RESPONSE packets before decryption. It introduces a new rxrpc_unshare_skb()
helper to safely unshare skbuffs passed via a double pointer and replaces
direct calls to skb_unshare().
Closes: https://sashiko.dev/#/patchset/20260408121252.2249051-1-dhowells%40redhat.com
> diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c
> index 9a41ec708aeb9..3d56a5d233697 100644
> --- a/net/rxrpc/conn_event.c
> +++ b/net/rxrpc/conn_event.c
[ ... ]
> @@ -371,7 +379,7 @@ static void rxrpc_do_process_connection(struct rxrpc_connection *conn)
> * connection that each one has when we've finished with it */
> while ((skb = skb_dequeue(&conn->rx_queue))) {
> rxrpc_see_skb(skb, rxrpc_skb_see_conn_work);
> - ret = rxrpc_process_event(conn, skb);
> + ret = rxrpc_process_event(conn, &skb);
> switch (ret) {
> case -ENOMEM:
> case -EAGAIN:
Looking at the code immediately following this hunk in
rxrpc_do_process_connection():
switch (ret) {
case -ENOMEM:
case -EAGAIN:
skb_queue_head(&conn->rx_queue, skb);
rxrpc_queue_conn(conn, rxrpc_conn_queue_retry_work);
break;
If rxrpc_process_event() fails with -ENOMEM because rxrpc_unshare_skb()
failed, rxrpc_unshare_skb() will have set *_skb = NULL.
Does this mean skb is NULL when we reach skb_queue_head()?
Since skb_queue_head() dereferences the skb parameter, this appears to
result in a NULL pointer dereference. Furthermore, since skb_unshare()
frees the original skb on allocation failure, the packet is already gone
and requeuing it wouldn't be possible anyway.
> diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
> index 6979569319252..0592ce644fc3d 100644
> --- a/net/rxrpc/io_thread.c
> +++ b/net/rxrpc/io_thread.c
[ ... ]
> @@ -249,19 +249,10 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
> * decryption.
> */
> if (sp->hdr.securityIndex != 0) {
> - skb = skb_unshare(skb, GFP_ATOMIC);
> - if (!skb) {
> - rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
> - *_skb = NULL;
> + skb = rxrpc_unshare_skb(_skb, GFP_ATOMIC);
> + if (!skb)
> return just_discard;
> - }
This isn't a new bug introduced by this patch, but since we are modifying
the unshare path here: when rxrpc_unshare_skb() fails, it sets *_skb = NULL
and returns just_discard.
Back in rxrpc_io_thread(), the packet is processed like this:
net/rxrpc/io_thread.c:rxrpc_io_thread() {
...
while ((skb = __skb_dequeue(&rx_queue))) {
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
switch (skb->mark) {
case RXRPC_SKB_MARK_PACKET:
skb->priority = 0;
if (!rxrpc_input_packet(local, &skb))
rxrpc_reject_packet(local, skb);
trace_rxrpc_rx_done(skb->mark, skb->priority);
...
If rxrpc_input_packet() sets skb to NULL and returns false (just_discard),
does this cause a NULL pointer dereference when trace_rxrpc_rx_done() tries
to access skb->mark?
^ permalink raw reply
* Re: [PATCH net 1/4] rxrpc: Fix memory leaks in rxkad_verify_response()
From: Simon Horman @ 2026-04-21 20:32 UTC (permalink / raw)
To: David Howells
Cc: netdev, Marc Dionne, Jakub Kicinski, David S. Miller,
Eric Dumazet, Paolo Abeni, linux-afs, linux-kernel,
Jeffrey Altman, stable
In-Reply-To: <20260420145900.1223732-2-dhowells@redhat.com>
On Mon, Apr 20, 2026 at 03:58:54PM +0100, David Howells wrote:
> Fix rxkad_verify_response() to free ticket by using a __free() construct
> rather than explicitly freeing it.
>
> Also fix rxkad_verify_response() to free the server key by using a __free()
> construct.
>
> Fixes: 57af281e5389 ("rxrpc: Tidy up abort generation infrastructure")
> Fixes: ec832bd06d6f ("rxrpc: Don't retain the server key in the connection")
> Closes: https://sashiko.dev/#/patchset/20260408121252.2249051-1-dhowells%40redhat.com
> Signed-off-by: David Howells <dhowells@redhat.com>
...
> index eb7f2769d2b1..0acdc46f42c2 100644
> --- a/net/rxrpc/rxkad.c
> +++ b/net/rxrpc/rxkad.c
...
> @@ -1160,16 +1159,15 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
> }
>
> ret = -ENOMEM;
> - response = kzalloc_obj(struct rxkad_response, GFP_NOFS);
> + struct rxkad_response *response __free(kfree) =
> + kzalloc_obj(struct rxkad_response, GFP_NOFS);
> if (!response)
> goto temporary_error;
>
Hi David,
This goto, combined with the use of __free in the declaration
of ticket below results in a compile error for x86_64 allmodconfig
with clang 21.1.8.
net/rxrpc/rxkad.c:1165:3: error: cannot jump from this goto statement to its label
1165 | goto temporary_error;
| ^
net/rxrpc/rxkad.c:1192:8: note: jump bypasses initialization of variable with __attribute__((cleanup))
1192 | void *ticket __free(kfree) = kmalloc(ticket_len, GFP_NOFS);
| ^
Moreover, the use of this construct is discouraged in Networking code:
1.7.3. Using device-managed and cleanup.h constructs¶
Netdev remains skeptical about promises of all “auto-cleanup” APIs,
including even devm_ helpers, historically. They are not the preferred
style of implementation, merely an acceptable one.
Use of guard() is discouraged within any function longer than 20 lines,
scoped_guard() is considered more readable. Using normal lock/unlock is
still (weakly) preferred.
Low level cleanup constructs (such as __free()) can be used when building
APIs and helpers, especially scoped iterators. However, direct use of
__free() within networking core and drivers is discouraged. Similar
guidance applies to declaring variables mid-function.
https://docs.kernel.org/process/maintainer-netdev.html#using-device-managed-and-cleanup-h-constructs
And to round things out, Sashiko also points out problems with
the use of __free() in this patch.
...
>
> /* extract the kerberos ticket and decrypt and decode it */
> ret = -ENOMEM;
> - ticket = kmalloc(ticket_len, GFP_NOFS);
> + void *ticket __free(kfree) = kmalloc(ticket_len, GFP_NOFS);
> if (!ticket)
> - goto temporary_error_free_resp;
> + goto temporary_error;
...
> temporary_error:
> /* Ignore the response packet if we got a temporary error such as
> * ENOMEM. We just want to send the challenge again. Note that we
> * also come out this way if the ticket decryption fails.
> */
> - key_put(server_key);
> return ret;
> }
>
>
--
pw-bot: changes-requested
^ permalink raw reply
* [PATCH net v3] ipv6: Cap TLV scan in ip6_tnl_parse_tlv_enc_lim
From: Daniel Borkmann @ 2026-04-21 20:24 UTC (permalink / raw)
To: kuba
Cc: edumazet, dsahern, tom, willemdebruijn.kernel, idosch,
justin.iurman, pabeni, netdev
Commit 47d3d7ac656a ("ipv6: Implement limits on Hop-by-Hop and
Destination options") added net.ipv6.max_{hbh,dst}_opts_{cnt,len}
and applied them in ip6_parse_tlv(), the generic TLV walker
invoked from ipv6_destopt_rcv() and ipv6_parse_hopopts().
ip6_tnl_parse_tlv_enc_lim() does not go through ip6_parse_tlv();
it has its own hand-rolled TLV scanner inside its NEXTHDR_DEST
branch which looks for IPV6_TLV_TNL_ENCAP_LIMIT. That inner
loop is bounded only by optlen, which can be up to 2048 bytes.
Stuffing the Destination Options header with 2046 Pad1 (type=0)
entries advances the scanner a single byte at a time, yielding
~2000 TLV iterations per extension header.
Reusing max_dst_opts_cnt to bound the TLV iterations, matching
the semantics from 47d3d7ac656a, would require duplicating
ip6_parse_tlv() to also validate Pad1/PadN payload. It would
also mandate enforcing max_dst_opts_len, since otherwise an
attacker shifts the axis to few options with a giant PadN and
recovers the original DoS. Allowing up to 8 options before the
tunnel encapsulation limit TLV is liberal enough; in practice
encap limit is the first TLV. Thus, go with a hard-coded limit
IP6_TUNNEL_MAX_DEST_TLVS (8).
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
v2->v3:
- Hard code limit of 8 instead of max_dst_opts_cnt (Ido)
v1->v2:
- Use abs() given max_dst_opts_cnt's negative meaning (Justin)
- Remove unlikely (Justin)
net/ipv6/ip6_tunnel.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 907c6a2af331..4546a60942ab 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -62,6 +62,8 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_RTNL_LINK("ip6tnl");
MODULE_ALIAS_NETDEV("ip6tnl0");
+#define IP6_TUNNEL_MAX_DEST_TLVS 8
+
#define IP6_TUNNEL_HASH_SIZE_SHIFT 5
#define IP6_TUNNEL_HASH_SIZE (1 << IP6_TUNNEL_HASH_SIZE_SHIFT)
@@ -430,11 +432,15 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw)
break;
}
if (nexthdr == NEXTHDR_DEST) {
+ int tlv_cnt = 0;
u16 i = 2;
while (1) {
struct ipv6_tlv_tnl_enc_lim *tel;
+ if (unlikely(tlv_cnt++ >= IP6_TUNNEL_MAX_DEST_TLVS))
+ break;
+
/* No more room for encapsulation limit */
if (i + sizeof(*tel) > optlen)
break;
--
2.43.0
^ permalink raw reply related
* [PATCH iproute2] lib: add input validation for time, rate, and size parsing functions
From: Stephen Hemminger @ 2026-04-21 20:23 UTC (permalink / raw)
To: netdev; +Cc: Stephen Hemminger
The parsing functions get_time(), get_time64(), get_rate(), get_rate64(),
and get_size64() use strtod() to convert user input but don't validate
the parsed values. This allows negative numbers and overflow values to
be passed through, which can cause unexpected behavior or security issues
when these values reach the kernel as unsigned integers.
Add validation to reject:
- Negative values (which make no sense for time, rate, or size)
- Overflow conditions (when strtod() returns HUGE_VAL with ERANGE)
- Empty strings (already checked, but now with explicit comments)
This prevents potential issues and provides clearer error reporting.
Fixing it in iproute2 does not mean that the kernel is off the hook
for handling negative values. Checks are still needed.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/utils.c | 16 ++++++++++++++--
lib/utils_math.c | 25 ++++++++++++++++++++++---
2 files changed, 36 insertions(+), 5 deletions(-)
diff --git a/lib/utils.c b/lib/utils.c
index 1215fe31..50602e59 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -1647,7 +1647,13 @@ int get_time(unsigned int *time, const char *str)
t = strtod(str, &p);
if (p == str)
- return -1;
+ return -1; /* empty string */
+
+ if (t < 0)
+ return -1; /* negative value */
+
+ if (t == HUGE_VAL && errno == ERANGE)
+ return -1; /* out of range */
if (*p) {
if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec") == 0 ||
@@ -1693,7 +1699,13 @@ int get_time64(__s64 *time, const char *str)
nsec = strtod(str, &p);
if (p == str)
- return -1;
+ return -1; /* empty string */
+
+ if (nsec < 0)
+ return -1; /* negative value */
+
+ if (nsec == HUGE_VAL && errno == ERANGE)
+ return -1; /* out of range */
if (*p) {
if (strcasecmp(p, "s") == 0 ||
diff --git a/lib/utils_math.c b/lib/utils_math.c
index fd2ddc7c..4b0ac266 100644
--- a/lib/utils_math.c
+++ b/lib/utils_math.c
@@ -5,6 +5,7 @@
#include <string.h>
#include <math.h>
#include <limits.h>
+#include <errno.h>
#include <asm/types.h>
#include "utils.h"
@@ -42,7 +43,13 @@ int get_rate(unsigned int *rate, const char *str)
const struct rate_suffix *s;
if (p == str)
- return -1;
+ return -1; /* empty string */
+
+ if (bps < 0)
+ return -1; /* negative */
+
+ if (bps == HUGE_VAL && errno == ERANGE)
+ return -1; /* out of range */
for (s = suffixes; s->name; ++s) {
if (strcasecmp(s->name, p) == 0) {
@@ -70,7 +77,13 @@ int get_rate64(__u64 *rate, const char *str)
const struct rate_suffix *s;
if (p == str)
- return -1;
+ return -1; /* empty string */
+
+ if (bps < 0)
+ return -1; /* negative */
+
+ if (bps == HUGE_VAL && errno == ERANGE)
+ return -1; /* out of range */
for (s = suffixes; s->name; ++s) {
if (strcasecmp(s->name, p) == 0) {
@@ -95,7 +108,13 @@ int get_size64(__u64 *size, const char *str)
sz = strtod(str, &p);
if (p == str)
- return -1;
+ return -1; /* empty string */
+
+ if (sz < 0)
+ return -1; /* negative */
+
+ if (sz == HUGE_VAL && errno == ERANGE)
+ return -1; /* out of range */
if (*p) {
if (strcasecmp(p, "kb") == 0 || strcasecmp(p, "k") == 0)
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v11 2/4] arm64: dts: s32: set Ethernet channel irqs
From: Jared Kangas @ 2026-04-21 20:07 UTC (permalink / raw)
To: jan.petrous
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Maxime Coquelin, Alexandre Torgue, Chester Lin,
Matthias Brugger, Ghennadi Procopciuc, NXP S32 Linux Team,
Shawn Guo, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li, netdev,
linux-stm32, linux-arm-kernel, linux-kernel, imx, devicetree,
rmk+kernel, vladimir.oltean, boon.khai.ng
In-Reply-To: <20260312-dwmac_multi_irq-v11-2-09621ccb040b@oss.nxp.com>
On Thu, Mar 12, 2026 at 09:55:28AM +0100, Jan Petrous via B4 Relay wrote:
> From: "Jan Petrous (OSS)" <jan.petrous@oss.nxp.com>
>
> The GMAC Ethernet controller found on S32G2/S32G3 and S32R45
> contains up to 5 RX and 5 TX channels.
> It can operate in two interrupt modes:
>
> 1) Sharing IRQ mode: only MAC IRQ line is used
> for all channels.
>
> 2) Multiple IRQ mode: every channel uses two IRQ lines,
> one for RX and second for TX.
>
> Specify all IRQ twins for all channels.
>
> Reviewed-by: Matthias Brugger <mbrugger@suse.com>
> Signed-off-by: Jan Petrous (OSS) <jan.petrous@oss.nxp.com>
> ---
Tested the new channels on an S32G-VNP-RDB3 while testing patch 4/4.
Tested-by: Jared Kangas <jkangas@redhat.com>
^ permalink raw reply
* Re: [PATCH] net: usb: rtl8150: fix use-after-free in rtl8150_start_xmit()
From: Michal Pecio @ 2026-04-21 20:05 UTC (permalink / raw)
To: Morduan Zang
Cc: petkan, davem, edumazet, kuba, pabeni, andrew+netdev, linux-usb,
netdev, linux-kernel, syzkaller-bugs, Zhan Jun,
syzbot+3f46c095ac0ca048cb71
In-Reply-To: <73ACB7391A6DE033+20260421110412.14795-1-zhangdandan@uniontech.com>
On Tue, 21 Apr 2026 19:04:12 +0800, Morduan Zang wrote:
> From: Zhan Jun <zhanjun@uniontech.com>
>
> syzbot reported a KASAN slab-use-after-free read in rtl8150_start_xmit()
> when accessing skb->len for tx statistics after usb_submit_urb() has
> been called:
>
> BUG: KASAN: slab-use-after-free in rtl8150_start_xmit+0x71f/0x760
> drivers/net/usb/rtl8150.c:712
> Read of size 4 at addr ffff88810eb7a930 by task kworker/0:4/5226
>
> The URB completion handler write_bulk_callback() frees the skb via
> dev_kfree_skb_irq(dev->tx_skb). The URB may complete on another CPU
> in softirq context before usb_submit_urb() returns in the submitter,
> so by the time the submitter reads skb->len the skb has already been
> queued to the per-CPU completion_queue and freed by net_tx_action():
>
> CPU A (xmit) CPU B (USB completion softirq)
> ------------ ------------------------------
> dev->tx_skb = skb;
> usb_submit_urb() --+
> |-------> write_bulk_callback()
> | dev_kfree_skb_irq(dev->tx_skb)
> | net_tx_action()
> | napi_skb_cache_put() <-- free
> netdev->stats.tx_bytes |
> += skb->len; <-- UAF read
>
> Fix it by caching skb->len before submitting the URB and using the
> cached value when updating the tx_bytes counter.
Question:
Is it correct that ETH_ZLEN padding isn't counted in tx_bytes?
> This mirrors the fix pattern used by other USB network drivers.
Which ones? I looked at a few and they either:
- appear to have the same bug (kaweth)
- update stats on URB completion, right before freeing skb
- copy data out of skb, update stats, free skb before URB completion
Regards,
Michal
^ permalink raw reply
* Re: [PATCH v11 4/4] stmmac: s32: enable support for Multi-IRQ mode
From: Jared Kangas @ 2026-04-21 20:02 UTC (permalink / raw)
To: jan.petrous
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Maxime Coquelin, Alexandre Torgue, Chester Lin,
Matthias Brugger, Ghennadi Procopciuc, NXP S32 Linux Team,
Shawn Guo, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li, netdev,
linux-stm32, linux-arm-kernel, linux-kernel, imx, devicetree,
rmk+kernel, vladimir.oltean, boon.khai.ng
In-Reply-To: <20260312-dwmac_multi_irq-v11-4-09621ccb040b@oss.nxp.com>
Hi Jan,
On Thu, Mar 12, 2026 at 09:55:30AM +0100, Jan Petrous via B4 Relay wrote:
> From: "Jan Petrous (OSS)" <jan.petrous@oss.nxp.com>
>
> Based on previous changes in platform driver, the vendor
> glue driver can enable Multi-IRQ mode, if needed.
>
> [...]
>
> If those prerequisites are met, the driver switches to Multi-IRQ mode,
> using per-queue IRQs for rx/tx data pathr:
>
> [ 1.387045] s32-dwmac 4033c000.ethernet: Multi-IRQ mode (per queue IRQs) selected
>
> Now the driver owns all queues IRQs:
>
> root@s32g399aevb3:~# grep eth /proc/interrupts
> 29: 0 0 0 0 0 0 0 0 GICv3 89 Level eth0:mac
> 30: 0 0 0 0 0 0 0 0 GICv3 91 Level eth0:rx-0
> 31: 0 0 0 0 0 0 0 0 GICv3 93 Level eth0:rx-1
> 32: 0 0 0 0 0 0 0 0 GICv3 95 Level eth0:rx-2
> 33: 0 0 0 0 0 0 0 0 GICv3 97 Level eth0:rx-3
> 34: 0 0 0 0 0 0 0 0 GICv3 99 Level eth0:rx-4
> 35: 0 0 0 0 0 0 0 0 GICv3 90 Level eth0:tx-0
> 36: 0 0 0 0 0 0 0 0 GICv3 92 Level eth0:tx-1
> 37: 0 0 0 0 0 0 0 0 GICv3 94 Level eth0:tx-2
> 38: 0 0 0 0 0 0 0 0 GICv3 96 Level eth0:tx-3
> 39: 0 0 0 0 0 0 0 0 GICv3 98 Level eth0:tx-4
I ran this series' changes on an NXP S32G-VNP-RDB3 (dwmac-s32) and
confirmed multichannel TX by doing a basic iperf3 throughput test:
# dmesg | grep Multi-IRQ
[ 37.463467] s32-dwmac 4033c000.ethernet: Multi-IRQ mode (per queue IRQs) selected
# iperf3 -s
[connection logs snipped]
# grep end0 /proc/interrupts | column -t
29: 0 0 0 0 0 0 0 0 GICv3 89 Level end0:mac
30: 968 0 0 0 0 0 0 0 GICv3 90 Level end0:tx-0
31: 0 3 0 0 0 0 0 0 GICv3 92 Level end0:tx-1
32: 0 0 3 0 0 0 0 0 GICv3 94 Level end0:tx-2
33: 0 0 0 3 0 0 0 0 GICv3 96 Level end0:tx-3
34: 0 0 0 0 3 0 0 0 GICv3 98 Level end0:tx-4
35: 67302 0 0 0 0 0 0 0 GICv3 91 Level end0:rx-0
36: 0 0 0 0 0 0 0 0 GICv3 93 Level end0:rx-1
37: 0 0 0 0 0 0 0 0 GICv3 95 Level end0:rx-2
38: 0 0 0 0 0 0 0 0 GICv3 97 Level end0:rx-3
39: 0 0 0 0 0 0 0 0 GICv3 99 Level end0:rx-4
Also tried out multichannel RX by adding 'snps,route-multi-broad' to
rx-queues-config/queue2 in the devicetree, which showed activity on
the corresponding rx-2 entry:
# grep end0 /proc/interrupts | column -t
29: 0 0 0 0 0 0 0 0 GICv3 89 Level end0:mac
30: 4 0 0 0 0 0 0 0 GICv3 90 Level end0:tx-0
31: 0 1 0 0 0 0 0 0 GICv3 92 Level end0:tx-1
32: 0 0 1 0 0 0 0 0 GICv3 94 Level end0:tx-2
33: 0 0 0 0 0 0 0 0 GICv3 96 Level end0:tx-3
34: 0 0 0 0 1 0 0 0 GICv3 98 Level end0:tx-4
35: 68 0 0 0 0 0 0 0 GICv3 91 Level end0:rx-0
36: 0 0 0 0 0 0 0 0 GICv3 93 Level end0:rx-1
37: 0 0 91 0 0 0 0 0 GICv3 95 Level end0:rx-2
38: 0 0 0 0 0 0 0 0 GICv3 97 Level end0:rx-3
39: 0 0 0 0 0 0 0 0 GICv3 99 Level end0:rx-4
I didn't see any regressions with light network usage, and both TX/RX
appear to function as expected.
Tested-by: Jared Kangas <jkangas@redhat.com>
^ permalink raw reply
* Re: [PATCH] dt-bindings: Fix phandle-array constraints, again
From: Mark Brown @ 2026-04-21 20:00 UTC (permalink / raw)
To: Rob Herring (Arm)
Cc: Maarten Lankhorst, Maxime Ripard, Krzysztof Kozlowski,
Conor Dooley, Ulf Hansson, Stephan Gerhold, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Johannes Berg, Jeff Johnson, Bjorn Helgaas, Lorenzo Pieralisi,
Krzysztof Wilczyński, Manivannan Sadhasivam, Bjorn Andersson,
Mathieu Poirier, Sylwester Nawrocki, Maxime Coquelin,
Greg Kroah-Hartman, Yang Xiwen, Alex Elder, Chaitanya Chundru,
Sibi Sankar, Rao Mandadapu, Patrice Chotard, Xu Yang, Peng Fan,
Thomas Zimmermann, devicetree, linux-kernel, linux-mmc,
linux-arm-msm, netdev, linux-wireless, ath10k, ath11k, linux-pci,
linux-remoteproc, linux-sound, linux-spi, linux-usb
In-Reply-To: <20260421195836.1547469-1-robh@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 292 bytes --]
On Tue, Apr 21, 2026 at 02:55:25PM -0500, Rob Herring (Arm) wrote:
> The unfortunately named 'phandle-array' property type is really a matrix
> with phandle and fixed arg cells entries. A matrix property should have 2
> levels of items constraints.
Acked-by: Mark Brown <broonie@kernel.org>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH net 16/18] drivers: net: 8390: pcnet: Remove this driver
From: Andrew Lunn @ 2026-04-21 19:31 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260421-v7-0-0-net-next-driver-removal-v1-v1-0-69517c689d1f@lunn.ch>
The pcnet was written by David A. Hindsh in 1999. It is an PCMCIA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/8390/Kconfig | 11 -
drivers/net/ethernet/8390/Makefile | 1 -
drivers/net/ethernet/8390/pcnet_cs.c | 1717 ----------------------------------
3 files changed, 1729 deletions(-)
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
index 3dea042cc2eb..3e56806471a3 100644
--- a/drivers/net/ethernet/8390/Kconfig
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -132,17 +132,6 @@ config APNE
To compile this driver as a module, choose M here: the module
will be called apne.
-config PCMCIA_PCNET
- tristate "NE2000 compatible PCMCIA support"
- depends on PCMCIA && HAS_IOPORT
- select CRC32
- help
- Say Y here if you intend to attach an NE2000 compatible PCMCIA
- (PC-card) Ethernet or Fast Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called pcnet_cs. If unsure, say N.
-
config STNIC
tristate "National DP83902AV support"
depends on SUPERH
diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile
index 60220484b382..b215136a603b 100644
--- a/drivers/net/ethernet/8390/Makefile
+++ b/drivers/net/ethernet/8390/Makefile
@@ -11,7 +11,6 @@ obj-$(CONFIG_HYDRA) += hydra.o
obj-$(CONFIG_MCF8390) += mcf8390.o
obj-$(CONFIG_NE2000) += ne.o 8390p.o
obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
-obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o
obj-$(CONFIG_STNIC) += stnic.o 8390.o
obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o
obj-$(CONFIG_WD80x3) += wd.o 8390.o
diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c
deleted file mode 100644
index 19f9c5db3f3b..000000000000
--- a/drivers/net/ethernet/8390/pcnet_cs.c
+++ /dev/null
@@ -1,1717 +0,0 @@
-// SPDX-License-Identifier: GPL-1.0+
-/*======================================================================
-
- A PCMCIA ethernet driver for NS8390-based cards
-
- This driver supports the D-Link DE-650 and Linksys EthernetCard
- cards, the newer D-Link and Linksys combo cards, Accton EN2212
- cards, the RPTI EP400, and the PreMax PE-200 in non-shared-memory
- mode, and the IBM Credit Card Adapter, the NE4100, the Thomas
- Conrad ethernet card, and the Kingston KNE-PCM/x in shared-memory
- mode. It will also handle the Socket EA card in either mode.
-
- Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
-
- pcnet_cs.c 1.153 2003/11/09 18:53:09
-
- The network driver code is based on Donald Becker's NE2000 code:
-
- Written 1992,1993 by Donald Becker.
- Copyright 1993 United States Government as represented by the
- Director, National Security Agency.
- Donald Becker may be reached at becker@scyld.com
-
- Based also on Keith Moore's changes to Don Becker's code, for IBM
- CCAE support. Drivers merged back together, and shared-memory
- Socket EA support added, by Ken Raeburn, September 1995.
-
-======================================================================*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/ptrace.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/log2.h>
-#include <linux/etherdevice.h>
-#include <linux/mii.h>
-#include "8390.h"
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/cisreg.h>
-
-#include <asm/io.h>
-#include <asm/byteorder.h>
-#include <linux/uaccess.h>
-
-#define PCNET_CMD 0x00
-#define PCNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */
-#define PCNET_RESET 0x1f /* Issue a read to reset, a write to clear. */
-#define PCNET_MISC 0x18 /* For IBM CCAE and Socket EA cards */
-
-#define PCNET_START_PG 0x40 /* First page of TX buffer */
-#define PCNET_STOP_PG 0x80 /* Last page +1 of RX ring */
-
-/* Socket EA cards have a larger packet buffer */
-#define SOCKET_START_PG 0x01
-#define SOCKET_STOP_PG 0xff
-
-#define PCNET_RDC_TIMEOUT (2*HZ/100) /* Max wait in jiffies for Tx RDC */
-
-static const char *if_names[] = { "auto", "10baseT", "10base2"};
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
-MODULE_DESCRIPTION("NE2000 compatible PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-INT_MODULE_PARM(if_port, 1); /* Transceiver type */
-INT_MODULE_PARM(use_big_buf, 1); /* use 64K packet buffer? */
-INT_MODULE_PARM(mem_speed, 0); /* shared mem speed, in ns */
-INT_MODULE_PARM(delay_output, 0); /* pause after xmit? */
-INT_MODULE_PARM(delay_time, 4); /* in usec */
-INT_MODULE_PARM(use_shmem, -1); /* use shared memory? */
-INT_MODULE_PARM(full_duplex, 0); /* full duplex? */
-
-/* Ugh! Let the user hardwire the hardware address for queer cards */
-static int hw_addr[6] = { 0, /* ... */ };
-module_param_array(hw_addr, int, NULL, 0);
-
-/*====================================================================*/
-
-static void mii_phy_probe(struct net_device *dev);
-static int pcnet_config(struct pcmcia_device *link);
-static void pcnet_release(struct pcmcia_device *link);
-static int pcnet_open(struct net_device *dev);
-static int pcnet_close(struct net_device *dev);
-static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
-static void ei_watchdog(struct timer_list *t);
-static void pcnet_reset_8390(struct net_device *dev);
-static int set_config(struct net_device *dev, struct ifmap *map);
-static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
- int stop_pg, int cm_offset);
-static int setup_dma_config(struct pcmcia_device *link, int start_pg,
- int stop_pg);
-
-static void pcnet_detach(struct pcmcia_device *p_dev);
-
-/*====================================================================*/
-
-struct hw_info {
- u_int offset;
- u_char a0, a1, a2;
- u_int flags;
-};
-
-#define DELAY_OUTPUT 0x01
-#define HAS_MISC_REG 0x02
-#define USE_BIG_BUF 0x04
-#define HAS_IBM_MISC 0x08
-#define IS_DL10019 0x10
-#define IS_DL10022 0x20
-#define HAS_MII 0x40
-#define USE_SHMEM 0x80 /* autodetected */
-
-#define AM79C9XX_HOME_PHY 0x00006B90 /* HomePNA PHY */
-#define AM79C9XX_ETH_PHY 0x00006B70 /* 10baseT PHY */
-#define MII_PHYID_REV_MASK 0xfffffff0
-#define MII_PHYID_REG1 0x02
-#define MII_PHYID_REG2 0x03
-
-static struct hw_info hw_info[] = {
- { /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT },
- { /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 },
- { /* APEX MultiCard */ 0x03f4, 0x00, 0x20, 0xe5, 0 },
- { /* ASANTE FriendlyNet */ 0x4910, 0x00, 0x00, 0x94,
- DELAY_OUTPUT | HAS_IBM_MISC },
- { /* Danpex EN-6200P2 */ 0x0110, 0x00, 0x40, 0xc7, 0 },
- { /* DataTrek NetCard */ 0x0ff0, 0x00, 0x20, 0xe8, 0 },
- { /* Dayna CommuniCard E */ 0x0110, 0x00, 0x80, 0x19, 0 },
- { /* D-Link DE-650 */ 0x0040, 0x00, 0x80, 0xc8, 0 },
- { /* EP-210 Ethernet */ 0x0110, 0x00, 0x40, 0x33, 0 },
- { /* EP4000 Ethernet */ 0x01c0, 0x00, 0x00, 0xb4, 0 },
- { /* Epson EEN10B */ 0x0ff0, 0x00, 0x00, 0x48,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* ELECOM Laneed LD-CDWA */ 0xb8, 0x08, 0x00, 0x42, 0 },
- { /* Hypertec Ethernet */ 0x01c0, 0x00, 0x40, 0x4c, 0 },
- { /* IBM CCAE */ 0x0ff0, 0x08, 0x00, 0x5a,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* IBM CCAE */ 0x0ff0, 0x00, 0x04, 0xac,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* IBM CCAE */ 0x0ff0, 0x00, 0x06, 0x29,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* IBM FME */ 0x0374, 0x08, 0x00, 0x5a,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* IBM FME */ 0x0374, 0x00, 0x04, 0xac,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* Kansai KLA-PCM/T */ 0x0ff0, 0x00, 0x60, 0x87,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* NSC DP83903 */ 0x0374, 0x08, 0x00, 0x17,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* NSC DP83903 */ 0x0374, 0x00, 0xc0, 0xa8,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* NSC DP83903 */ 0x0374, 0x00, 0xa0, 0xb0,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* NSC DP83903 */ 0x0198, 0x00, 0x20, 0xe0,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* I-O DATA PCLA/T */ 0x0ff0, 0x00, 0xa0, 0xb0, 0 },
- { /* Katron PE-520 */ 0x0110, 0x00, 0x40, 0xf6, 0 },
- { /* Kingston KNE-PCM/x */ 0x0ff0, 0x00, 0xc0, 0xf0,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* Kingston KNE-PCM/x */ 0x0ff0, 0xe2, 0x0c, 0x0f,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* Kingston KNE-PC2 */ 0x0180, 0x00, 0xc0, 0xf0, 0 },
- { /* Maxtech PCN2000 */ 0x5000, 0x00, 0x00, 0xe8, 0 },
- { /* NDC Instant-Link */ 0x003a, 0x00, 0x80, 0xc6, 0 },
- { /* NE2000 Compatible */ 0x0ff0, 0x00, 0xa0, 0x0c, 0 },
- { /* Network General Sniffer */ 0x0ff0, 0x00, 0x00, 0x65,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* Panasonic VEL211 */ 0x0ff0, 0x00, 0x80, 0x45,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* PreMax PE-200 */ 0x07f0, 0x00, 0x20, 0xe0, 0 },
- { /* RPTI EP400 */ 0x0110, 0x00, 0x40, 0x95, 0 },
- { /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 },
- { /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b,
- DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF },
- { /* Socket LP-E CF+ */ 0x01c0, 0x00, 0xc0, 0x1b, 0 },
- { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 },
- { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 },
- { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 },
- { /* PCMCIA Technology OEM */ 0x01c8, 0x00, 0xa0, 0x0c, 0 }
-};
-
-#define NR_INFO ARRAY_SIZE(hw_info)
-
-static struct hw_info default_info = { 0, 0, 0, 0, 0 };
-static struct hw_info dl10019_info = { 0, 0, 0, 0, IS_DL10019|HAS_MII };
-static struct hw_info dl10022_info = { 0, 0, 0, 0, IS_DL10022|HAS_MII };
-
-struct pcnet_dev {
- struct pcmcia_device *p_dev;
- u_int flags;
- void __iomem *base;
- struct timer_list watchdog;
- int stale, fast_poll;
- u_char phy_id;
- u_char eth_phy, pna_phy;
- u_short link_status;
- u_long mii_reset;
-};
-
-static inline struct pcnet_dev *PRIV(struct net_device *dev)
-{
- char *p = netdev_priv(dev);
- return (struct pcnet_dev *)(p + sizeof(struct ei_device));
-}
-
-static const struct net_device_ops pcnet_netdev_ops = {
- .ndo_open = pcnet_open,
- .ndo_stop = pcnet_close,
- .ndo_set_config = set_config,
- .ndo_start_xmit = ei_start_xmit,
- .ndo_get_stats = ei_get_stats,
- .ndo_eth_ioctl = ei_ioctl,
- .ndo_set_rx_mode = ei_set_multicast_list,
- .ndo_tx_timeout = ei_tx_timeout,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = ei_poll,
-#endif
-};
-
-static int pcnet_probe(struct pcmcia_device *link)
-{
- struct pcnet_dev *info;
- struct net_device *dev;
-
- dev_dbg(&link->dev, "pcnet_attach()\n");
-
- /* Create new ethernet device */
- dev = __alloc_ei_netdev(sizeof(struct pcnet_dev));
- if (!dev) return -ENOMEM;
- info = PRIV(dev);
- info->p_dev = link;
- link->priv = dev;
-
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
-
- dev->netdev_ops = &pcnet_netdev_ops;
-
- return pcnet_config(link);
-} /* pcnet_attach */
-
-static void pcnet_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "pcnet_detach\n");
-
- unregister_netdev(dev);
-
- pcnet_release(link);
-
- free_netdev(dev);
-} /* pcnet_detach */
-
-/*======================================================================
-
- This probes for a card's hardware address, for card types that
- encode this information in their CIS.
-
-======================================================================*/
-
-static struct hw_info *get_hwinfo(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- u_char __iomem *base, *virt;
- u8 addr[ETH_ALEN];
- int i, j;
-
- /* Allocate a small memory window */
- link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
- link->resource[2]->start = 0; link->resource[2]->end = 0;
- i = pcmcia_request_window(link, link->resource[2], 0);
- if (i != 0)
- return NULL;
-
- virt = ioremap(link->resource[2]->start,
- resource_size(link->resource[2]));
- if (unlikely(!virt)) {
- pcmcia_release_window(link, link->resource[2]);
- return NULL;
- }
-
- for (i = 0; i < NR_INFO; i++) {
- pcmcia_map_mem_page(link, link->resource[2],
- hw_info[i].offset & ~(resource_size(link->resource[2])-1));
- base = &virt[hw_info[i].offset & (resource_size(link->resource[2])-1)];
- if ((readb(base+0) == hw_info[i].a0) &&
- (readb(base+2) == hw_info[i].a1) &&
- (readb(base+4) == hw_info[i].a2)) {
- for (j = 0; j < 6; j++)
- addr[j] = readb(base + (j<<1));
- eth_hw_addr_set(dev, addr);
- break;
- }
- }
-
- iounmap(virt);
- j = pcmcia_release_window(link, link->resource[2]);
- return (i < NR_INFO) ? hw_info+i : NULL;
-} /* get_hwinfo */
-
-/*======================================================================
-
- This probes for a card's hardware address by reading the PROM.
- It checks the address against a list of known types, then falls
- back to a simple NE2000 clone signature check.
-
-======================================================================*/
-
-static struct hw_info *get_prom(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- u8 addr[ETH_ALEN];
- u_char prom[32];
- int i, j;
-
- /* This is lifted straight from drivers/net/ethernet/8390/ne.c */
- struct {
- u_char value, offset;
- } program_seq[] = {
- {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
- {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
- {0x00, EN0_RCNTLO}, /* Clear the count regs. */
- {0x00, EN0_RCNTHI},
- {0x00, EN0_IMR}, /* Mask completion irq. */
- {0xFF, EN0_ISR},
- {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
- {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
- {32, EN0_RCNTLO},
- {0x00, EN0_RCNTHI},
- {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
- {0x00, EN0_RSARHI},
- {E8390_RREAD+E8390_START, E8390_CMD},
- };
-
- pcnet_reset_8390(dev);
- mdelay(10);
-
- for (i = 0; i < ARRAY_SIZE(program_seq); i++)
- outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
-
- for (i = 0; i < 32; i++)
- prom[i] = inb(ioaddr + PCNET_DATAPORT);
- for (i = 0; i < NR_INFO; i++) {
- if ((prom[0] == hw_info[i].a0) &&
- (prom[2] == hw_info[i].a1) &&
- (prom[4] == hw_info[i].a2))
- break;
- }
- if ((i < NR_INFO) || ((prom[28] == 0x57) && (prom[30] == 0x57))) {
- for (j = 0; j < 6; j++)
- addr[j] = prom[j<<1];
- eth_hw_addr_set(dev, addr);
- return (i < NR_INFO) ? hw_info+i : &default_info;
- }
- return NULL;
-} /* get_prom */
-
-/*======================================================================
-
- For DL10019 based cards, like the Linksys EtherFast
-
-======================================================================*/
-
-static struct hw_info *get_dl10019(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- u8 addr[ETH_ALEN];
- int i;
- u_char sum;
-
- for (sum = 0, i = 0x14; i < 0x1c; i++)
- sum += inb_p(dev->base_addr + i);
- if (sum != 0xff)
- return NULL;
- for (i = 0; i < 6; i++)
- addr[i] = inb_p(dev->base_addr + 0x14 + i);
- eth_hw_addr_set(dev, addr);
- i = inb(dev->base_addr + 0x1f);
- return ((i == 0x91)||(i == 0x99)) ? &dl10022_info : &dl10019_info;
-}
-
-/*======================================================================
-
- For Asix AX88190 based cards
-
-======================================================================*/
-
-static struct hw_info *get_ax88190(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- u8 addr[ETH_ALEN];
- int i, j;
-
- /* Not much of a test, but the alternatives are messy */
- if (link->config_base != 0x03c0)
- return NULL;
-
- outb_p(0x01, ioaddr + EN0_DCFG); /* Set word-wide access. */
- outb_p(0x00, ioaddr + EN0_RSARLO); /* DMA starting at 0x0400. */
- outb_p(0x04, ioaddr + EN0_RSARHI);
- outb_p(E8390_RREAD+E8390_START, ioaddr + E8390_CMD);
-
- for (i = 0; i < 6; i += 2) {
- j = inw(ioaddr + PCNET_DATAPORT);
- addr[i] = j & 0xff;
- addr[i+1] = j >> 8;
- }
- eth_hw_addr_set(dev, addr);
- return NULL;
-}
-
-/*======================================================================
-
- This should be totally unnecessary... but when we can't figure
- out the hardware address any other way, we'll let the user hard
- wire it when the module is initialized.
-
-======================================================================*/
-
-static struct hw_info *get_hwired(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- u8 addr[ETH_ALEN];
- int i;
-
- for (i = 0; i < 6; i++)
- if (hw_addr[i] != 0) break;
- if (i == 6)
- return NULL;
-
- for (i = 0; i < 6; i++)
- addr[i] = hw_addr[i];
- eth_hw_addr_set(dev, addr);
-
- return &default_info;
-} /* get_hwired */
-
-static int try_io_port(struct pcmcia_device *link)
-{
- int j, ret;
- link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
- if (link->resource[0]->end == 32) {
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
- if (link->resource[1]->end > 0) {
- /* for master/slave multifunction cards */
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- }
- } else {
- /* This should be two 16-port windows */
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
- }
- if (link->resource[0]->start == 0) {
- for (j = 0; j < 0x400; j += 0x20) {
- link->resource[0]->start = j ^ 0x300;
- link->resource[1]->start = (j ^ 0x300) + 0x10;
- link->io_lines = 16;
- ret = pcmcia_request_io(link);
- if (ret == 0)
- return ret;
- }
- return ret;
- } else {
- return pcmcia_request_io(link);
- }
-}
-
-static int pcnet_confcheck(struct pcmcia_device *p_dev, void *priv_data)
-{
- int *priv = priv_data;
- int try = (*priv & 0x1);
-
- *priv &= (p_dev->resource[2]->end >= 0x4000) ? 0x10 : ~0x10;
-
- if (p_dev->config_index == 0)
- return -EINVAL;
-
- if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32)
- return -EINVAL;
-
- if (try)
- p_dev->io_lines = 16;
- return try_io_port(p_dev);
-}
-
-static struct hw_info *pcnet_try_config(struct pcmcia_device *link,
- int *has_shmem, int try)
-{
- struct net_device *dev = link->priv;
- struct hw_info *local_hw_info;
- struct pcnet_dev *info = PRIV(dev);
- int priv = try;
- int ret;
-
- ret = pcmcia_loop_config(link, pcnet_confcheck, &priv);
- if (ret) {
- dev_warn(&link->dev, "no useable port range found\n");
- return NULL;
- }
- *has_shmem = (priv & 0x10);
-
- if (!link->irq)
- return NULL;
-
- if (resource_size(link->resource[1]) == 8)
- link->config_flags |= CONF_ENABLE_SPKR;
-
- if ((link->manf_id == MANFID_IBM) &&
- (link->card_id == PRODID_IBM_HOME_AND_AWAY))
- link->config_index |= 0x10;
-
- ret = pcmcia_enable_device(link);
- if (ret)
- return NULL;
-
- dev->irq = link->irq;
- dev->base_addr = link->resource[0]->start;
-
- if (info->flags & HAS_MISC_REG) {
- if ((if_port == 1) || (if_port == 2))
- dev->if_port = if_port;
- else
- dev_notice(&link->dev, "invalid if_port requested\n");
- } else
- dev->if_port = 0;
-
- if ((link->config_base == 0x03c0) &&
- (link->manf_id == 0x149) && (link->card_id == 0xc1ab)) {
- dev_info(&link->dev,
- "this is an AX88190 card - use axnet_cs instead.\n");
- return NULL;
- }
-
- local_hw_info = get_hwinfo(link);
- if (!local_hw_info)
- local_hw_info = get_prom(link);
- if (!local_hw_info)
- local_hw_info = get_dl10019(link);
- if (!local_hw_info)
- local_hw_info = get_ax88190(link);
- if (!local_hw_info)
- local_hw_info = get_hwired(link);
-
- return local_hw_info;
-}
-
-static int pcnet_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct pcnet_dev *info = PRIV(dev);
- int start_pg, stop_pg, cm_offset;
- int has_shmem = 0;
- struct hw_info *local_hw_info;
-
- dev_dbg(&link->dev, "pcnet_config\n");
-
- local_hw_info = pcnet_try_config(link, &has_shmem, 0);
- if (!local_hw_info) {
- /* check whether forcing io_lines to 16 helps... */
- pcmcia_disable_device(link);
- local_hw_info = pcnet_try_config(link, &has_shmem, 1);
- if (local_hw_info == NULL) {
- dev_notice(&link->dev, "unable to read hardware net"
- " address for io base %#3lx\n", dev->base_addr);
- goto failed;
- }
- }
-
- info->flags = local_hw_info->flags;
- /* Check for user overrides */
- info->flags |= (delay_output) ? DELAY_OUTPUT : 0;
- if ((link->manf_id == MANFID_SOCKET) &&
- ((link->card_id == PRODID_SOCKET_LPE) ||
- (link->card_id == PRODID_SOCKET_LPE_CF) ||
- (link->card_id == PRODID_SOCKET_EIO)))
- info->flags &= ~USE_BIG_BUF;
- if (!use_big_buf)
- info->flags &= ~USE_BIG_BUF;
-
- if (info->flags & USE_BIG_BUF) {
- start_pg = SOCKET_START_PG;
- stop_pg = SOCKET_STOP_PG;
- cm_offset = 0x10000;
- } else {
- start_pg = PCNET_START_PG;
- stop_pg = PCNET_STOP_PG;
- cm_offset = 0;
- }
-
- /* has_shmem is ignored if use_shmem != -1 */
- if ((use_shmem == 0) || (!has_shmem && (use_shmem == -1)) ||
- (setup_shmem_window(link, start_pg, stop_pg, cm_offset) != 0))
- setup_dma_config(link, start_pg, stop_pg);
-
- ei_status.name = "NE2000";
- ei_status.word16 = 1;
- ei_status.reset_8390 = pcnet_reset_8390;
-
- if (info->flags & (IS_DL10019|IS_DL10022))
- mii_phy_probe(dev);
-
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- pr_notice("register_netdev() failed\n");
- goto failed;
- }
-
- if (info->flags & (IS_DL10019|IS_DL10022)) {
- u_char id = inb(dev->base_addr + 0x1a);
- netdev_info(dev, "NE2000 (DL100%d rev %02x): ",
- (info->flags & IS_DL10022) ? 22 : 19, id);
- if (info->pna_phy)
- pr_cont("PNA, ");
- } else {
- netdev_info(dev, "NE2000 Compatible: ");
- }
- pr_cont("io %#3lx, irq %d,", dev->base_addr, dev->irq);
- if (info->flags & USE_SHMEM)
- pr_cont(" mem %#5lx,", dev->mem_start);
- if (info->flags & HAS_MISC_REG)
- pr_cont(" %s xcvr,", if_names[dev->if_port]);
- pr_cont(" hw_addr %pM\n", dev->dev_addr);
- return 0;
-
-failed:
- pcnet_release(link);
- return -ENODEV;
-} /* pcnet_config */
-
-static void pcnet_release(struct pcmcia_device *link)
-{
- struct pcnet_dev *info = PRIV(link->priv);
-
- dev_dbg(&link->dev, "pcnet_release\n");
-
- if (info->flags & USE_SHMEM)
- iounmap(info->base);
-
- pcmcia_disable_device(link);
-}
-
-static int pcnet_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int pcnet_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open) {
- pcnet_reset_8390(dev);
- NS8390_init(dev, 1);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-
-/*======================================================================
-
- MII interface support for DL10019 and DL10022 based cards
-
- On the DL10019, the MII IO direction bit is 0x10; on the DL10022
- it is 0x20. Setting both bits seems to work on both card types.
-
-======================================================================*/
-
-#define DLINK_GPIO 0x1c
-#define DLINK_DIAG 0x1d
-#define DLINK_EEPROM 0x1e
-
-#define MDIO_SHIFT_CLK 0x80
-#define MDIO_DATA_OUT 0x40
-#define MDIO_DIR_WRITE 0x30
-#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)
-#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)
-#define MDIO_DATA_READ 0x10
-#define MDIO_MASK 0x0f
-
-static void mdio_sync(unsigned int addr)
-{
- int bits, mask = inb(addr) & MDIO_MASK;
- for (bits = 0; bits < 32; bits++) {
- outb(mask | MDIO_DATA_WRITE1, addr);
- outb(mask | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
- }
-}
-
-static int mdio_read(unsigned int addr, int phy_id, int loc)
-{
- u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
- int i, retval = 0, mask = inb(addr) & MDIO_MASK;
-
- mdio_sync(addr);
- for (i = 13; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb(mask | dat, addr);
- outb(mask | dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 19; i > 0; i--) {
- outb(mask, addr);
- retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
- outb(mask | MDIO_SHIFT_CLK, addr);
- }
- return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(unsigned int addr, int phy_id, int loc, int value)
-{
- u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
- int i, mask = inb(addr) & MDIO_MASK;
-
- mdio_sync(addr);
- for (i = 31; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb(mask | dat, addr);
- outb(mask | dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 1; i >= 0; i--) {
- outb(mask, addr);
- outb(mask | MDIO_SHIFT_CLK, addr);
- }
-}
-
-/*======================================================================
-
- EEPROM access routines for DL10019 and DL10022 based cards
-
-======================================================================*/
-
-#define EE_EEP 0x40
-#define EE_ASIC 0x10
-#define EE_CS 0x08
-#define EE_CK 0x04
-#define EE_DO 0x02
-#define EE_DI 0x01
-#define EE_ADOT 0x01 /* DataOut for ASIC */
-#define EE_READ_CMD 0x06
-
-#define DL19FDUPLX 0x0400 /* DL10019 Full duplex mode */
-
-static int read_eeprom(unsigned int ioaddr, int location)
-{
- int i, retval = 0;
- unsigned int ee_addr = ioaddr + DLINK_EEPROM;
- int read_cmd = location | (EE_READ_CMD << 8);
-
- outb(0, ee_addr);
- outb(EE_EEP|EE_CS, ee_addr);
-
- /* Shift the read command bits out. */
- for (i = 10; i >= 0; i--) {
- short dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
- outb_p(EE_EEP|EE_CS|dataval, ee_addr);
- outb_p(EE_EEP|EE_CS|dataval|EE_CK, ee_addr);
- }
- outb(EE_EEP|EE_CS, ee_addr);
-
- for (i = 16; i > 0; i--) {
- outb_p(EE_EEP|EE_CS | EE_CK, ee_addr);
- retval = (retval << 1) | ((inb(ee_addr) & EE_DI) ? 1 : 0);
- outb_p(EE_EEP|EE_CS, ee_addr);
- }
-
- /* Terminate the EEPROM access. */
- outb(0, ee_addr);
- return retval;
-}
-
-/*
- The internal ASIC registers can be changed by EEPROM READ access
- with EE_ASIC bit set.
- In ASIC mode, EE_ADOT is used to output the data to the ASIC.
-*/
-
-static void write_asic(unsigned int ioaddr, int location, short asic_data)
-{
- int i;
- unsigned int ee_addr = ioaddr + DLINK_EEPROM;
- short dataval;
- int read_cmd = location | (EE_READ_CMD << 8);
-
- asic_data |= read_eeprom(ioaddr, location);
-
- outb(0, ee_addr);
- outb(EE_ASIC|EE_CS|EE_DI, ee_addr);
-
- read_cmd = read_cmd >> 1;
-
- /* Shift the read command bits out. */
- for (i = 9; i >= 0; i--) {
- dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
- outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
- outb_p(EE_ASIC|EE_CS|EE_DI|dataval|EE_CK, ee_addr);
- outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
- }
- // sync
- outb(EE_ASIC|EE_CS, ee_addr);
- outb(EE_ASIC|EE_CS|EE_CK, ee_addr);
- outb(EE_ASIC|EE_CS, ee_addr);
-
- for (i = 15; i >= 0; i--) {
- dataval = (asic_data & (1 << i)) ? EE_ADOT : 0;
- outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
- outb_p(EE_ASIC|EE_CS|dataval|EE_CK, ee_addr);
- outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
- }
-
- /* Terminate the ASIC access. */
- outb(EE_ASIC|EE_DI, ee_addr);
- outb(EE_ASIC|EE_DI| EE_CK, ee_addr);
- outb(EE_ASIC|EE_DI, ee_addr);
-
- outb(0, ee_addr);
-}
-
-/*====================================================================*/
-
-static void set_misc_reg(struct net_device *dev)
-{
- unsigned int nic_base = dev->base_addr;
- struct pcnet_dev *info = PRIV(dev);
- u_char tmp;
-
- if (info->flags & HAS_MISC_REG) {
- tmp = inb_p(nic_base + PCNET_MISC) & ~3;
- if (dev->if_port == 2)
- tmp |= 1;
- if (info->flags & USE_BIG_BUF)
- tmp |= 2;
- if (info->flags & HAS_IBM_MISC)
- tmp |= 8;
- outb_p(tmp, nic_base + PCNET_MISC);
- }
- if (info->flags & IS_DL10022) {
- if (info->flags & HAS_MII) {
- /* Advertise 100F, 100H, 10F, 10H */
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1);
- /* Restart MII autonegotiation */
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
- info->mii_reset = jiffies;
- } else {
- outb(full_duplex ? 4 : 0, nic_base + DLINK_DIAG);
- }
- } else if (info->flags & IS_DL10019) {
- /* Advertise 100F, 100H, 10F, 10H */
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1);
- /* Restart MII autonegotiation */
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
- }
-}
-
-/*====================================================================*/
-
-static void mii_phy_probe(struct net_device *dev)
-{
- struct pcnet_dev *info = PRIV(dev);
- unsigned int mii_addr = dev->base_addr + DLINK_GPIO;
- int i;
- u_int tmp, phyid;
-
- for (i = 31; i >= 0; i--) {
- tmp = mdio_read(mii_addr, i, 1);
- if ((tmp == 0) || (tmp == 0xffff))
- continue;
- tmp = mdio_read(mii_addr, i, MII_PHYID_REG1);
- phyid = tmp << 16;
- phyid |= mdio_read(mii_addr, i, MII_PHYID_REG2);
- phyid &= MII_PHYID_REV_MASK;
- netdev_dbg(dev, "MII at %d is 0x%08x\n", i, phyid);
- if (phyid == AM79C9XX_HOME_PHY) {
- info->pna_phy = i;
- } else if (phyid != AM79C9XX_ETH_PHY) {
- info->eth_phy = i;
- }
- }
-}
-
-static int pcnet_open(struct net_device *dev)
-{
- int ret;
- struct pcnet_dev *info = PRIV(dev);
- struct pcmcia_device *link = info->p_dev;
- unsigned int nic_base = dev->base_addr;
-
- dev_dbg(&link->dev, "pcnet_open('%s')\n", dev->name);
-
- if (!pcmcia_dev_present(link))
- return -ENODEV;
-
- set_misc_reg(dev);
-
- outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */
- ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, dev->name, dev);
- if (ret)
- return ret;
-
- link->open++;
-
- info->phy_id = info->eth_phy;
- info->link_status = 0x00;
- timer_setup(&info->watchdog, ei_watchdog, 0);
- mod_timer(&info->watchdog, jiffies + HZ);
-
- return ei_open(dev);
-} /* pcnet_open */
-
-/*====================================================================*/
-
-static int pcnet_close(struct net_device *dev)
-{
- struct pcnet_dev *info = PRIV(dev);
- struct pcmcia_device *link = info->p_dev;
-
- dev_dbg(&link->dev, "pcnet_close('%s')\n", dev->name);
-
- ei_close(dev);
- free_irq(dev->irq, dev);
-
- link->open--;
- netif_stop_queue(dev);
- timer_delete_sync(&info->watchdog);
-
- return 0;
-} /* pcnet_close */
-
-/*======================================================================
-
- Hard reset the card. This used to pause for the same period that
- a 8390 reset command required, but that shouldn't be necessary.
-
-======================================================================*/
-
-static void pcnet_reset_8390(struct net_device *dev)
-{
- unsigned int nic_base = dev->base_addr;
- int i;
-
- ei_status.txing = ei_status.dmaing = 0;
-
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD);
-
- outb(inb(nic_base + PCNET_RESET), nic_base + PCNET_RESET);
-
- for (i = 0; i < 100; i++) {
- if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
- break;
- udelay(100);
- }
- outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */
-
- if (i == 100)
- netdev_err(dev, "pcnet_reset_8390() did not complete.\n");
-
- set_misc_reg(dev);
-
-} /* pcnet_reset_8390 */
-
-/*====================================================================*/
-
-static int set_config(struct net_device *dev, struct ifmap *map)
-{
- struct pcnet_dev *info = PRIV(dev);
- if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
- if (!(info->flags & HAS_MISC_REG))
- return -EOPNOTSUPP;
- else if ((map->port < 1) || (map->port > 2))
- return -EINVAL;
- WRITE_ONCE(dev->if_port, map->port);
- netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
- NS8390_init(dev, 1);
- }
- return 0;
-}
-
-/*====================================================================*/
-
-static irqreturn_t ei_irq_wrapper(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct pcnet_dev *info;
- irqreturn_t ret = ei_interrupt(irq, dev_id);
-
- if (ret == IRQ_HANDLED) {
- info = PRIV(dev);
- info->stale = 0;
- }
- return ret;
-}
-
-static void ei_watchdog(struct timer_list *t)
-{
- struct pcnet_dev *info = timer_container_of(info, t, watchdog);
- struct net_device *dev = info->p_dev->priv;
- unsigned int nic_base = dev->base_addr;
- unsigned int mii_addr = nic_base + DLINK_GPIO;
- u_short link;
-
- if (!netif_device_present(dev)) goto reschedule;
-
- /* Check for pending interrupt with expired latency timer: with
- this, we can limp along even if the interrupt is blocked */
- if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) {
- if (!info->fast_poll)
- netdev_info(dev, "interrupt(s) dropped!\n");
- ei_irq_wrapper(dev->irq, dev);
- info->fast_poll = HZ;
- }
- if (info->fast_poll) {
- info->fast_poll--;
- info->watchdog.expires = jiffies + 1;
- add_timer(&info->watchdog);
- return;
- }
-
- if (!(info->flags & HAS_MII))
- goto reschedule;
-
- mdio_read(mii_addr, info->phy_id, 1);
- link = mdio_read(mii_addr, info->phy_id, 1);
- if (!link || (link == 0xffff)) {
- if (info->eth_phy) {
- info->phy_id = info->eth_phy = 0;
- } else {
- netdev_info(dev, "MII is missing!\n");
- info->flags &= ~HAS_MII;
- }
- goto reschedule;
- }
-
- link &= 0x0004;
- if (link != info->link_status) {
- u_short p = mdio_read(mii_addr, info->phy_id, 5);
- netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
- if (link && (info->flags & IS_DL10022)) {
- /* Disable collision detection on full duplex links */
- outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG);
- } else if (link && (info->flags & IS_DL10019)) {
- /* Disable collision detection on full duplex links */
- write_asic(dev->base_addr, 4, (p & 0x140) ? DL19FDUPLX : 0);
- }
- if (link) {
- if (info->phy_id == info->eth_phy) {
- if (p)
- netdev_info(dev, "autonegotiation complete: "
- "%sbaseT-%cD selected\n",
- ((p & 0x0180) ? "100" : "10"),
- ((p & 0x0140) ? 'F' : 'H'));
- else
- netdev_info(dev, "link partner did not autonegotiate\n");
- }
- NS8390_init(dev, 1);
- }
- info->link_status = link;
- }
- if (info->pna_phy && time_after(jiffies, info->mii_reset + 6*HZ)) {
- link = mdio_read(mii_addr, info->eth_phy, 1) & 0x0004;
- if (((info->phy_id == info->pna_phy) && link) ||
- ((info->phy_id != info->pna_phy) && !link)) {
- /* isolate this MII and try flipping to the other one */
- mdio_write(mii_addr, info->phy_id, 0, 0x0400);
- info->phy_id ^= info->pna_phy ^ info->eth_phy;
- netdev_info(dev, "switched to %s transceiver\n",
- (info->phy_id == info->eth_phy) ? "ethernet" : "PNA");
- mdio_write(mii_addr, info->phy_id, 0,
- (info->phy_id == info->eth_phy) ? 0x1000 : 0);
- info->link_status = 0;
- info->mii_reset = jiffies;
- }
- }
-
-reschedule:
- info->watchdog.expires = jiffies + HZ;
- add_timer(&info->watchdog);
-}
-
-/*====================================================================*/
-
-
-static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct pcnet_dev *info = PRIV(dev);
- struct mii_ioctl_data *data = if_mii(rq);
- unsigned int mii_addr = dev->base_addr + DLINK_GPIO;
-
- if (!(info->flags & (IS_DL10019|IS_DL10022)))
- return -EINVAL;
-
- switch (cmd) {
- case SIOCGMIIPHY:
- data->phy_id = info->phy_id;
- fallthrough;
- case SIOCGMIIREG: /* Read MII PHY register. */
- data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f);
- return 0;
- case SIOCSMIIREG: /* Write MII PHY register. */
- mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in);
- return 0;
- }
- return -EOPNOTSUPP;
-}
-
-/*====================================================================*/
-
-static void dma_get_8390_hdr(struct net_device *dev,
- struct e8390_pkt_hdr *hdr,
- int ring_page)
-{
- unsigned int nic_base = dev->base_addr;
-
- if (ei_status.dmaing) {
- netdev_err(dev, "DMAing conflict in dma_block_input."
- "[DMAstat:%1x][irqlock:%1x]\n",
- ei_status.dmaing, ei_status.irqlock);
- return;
- }
-
- ei_status.dmaing |= 0x01;
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
- outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
- outb_p(0, nic_base + EN0_RCNTHI);
- outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
- outb_p(ring_page, nic_base + EN0_RSARHI);
- outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD);
-
- insw(nic_base + PCNET_DATAPORT, hdr,
- sizeof(struct e8390_pkt_hdr)>>1);
- /* Fix for big endian systems */
- hdr->count = le16_to_cpu(hdr->count);
-
- outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
- ei_status.dmaing &= ~0x01;
-}
-
-/*====================================================================*/
-
-static void dma_block_input(struct net_device *dev, int count,
- struct sk_buff *skb, int ring_offset)
-{
- unsigned int nic_base = dev->base_addr;
- int xfer_count = count;
- char *buf = skb->data;
- struct ei_device *ei_local = netdev_priv(dev);
-
- if ((netif_msg_rx_status(ei_local)) && (count != 4))
- netdev_dbg(dev, "[bi=%d]\n", count+4);
- if (ei_status.dmaing) {
- netdev_err(dev, "DMAing conflict in dma_block_input."
- "[DMAstat:%1x][irqlock:%1x]\n",
- ei_status.dmaing, ei_status.irqlock);
- return;
- }
- ei_status.dmaing |= 0x01;
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
- outb_p(count & 0xff, nic_base + EN0_RCNTLO);
- outb_p(count >> 8, nic_base + EN0_RCNTHI);
- outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
- outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
- outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD);
-
- insw(nic_base + PCNET_DATAPORT,buf,count>>1);
- if (count & 0x01) {
- buf[count-1] = inb(nic_base + PCNET_DATAPORT);
- xfer_count++;
- }
-
- /* This was for the ALPHA version only, but enough people have been
- encountering problems that it is still here. */
-#ifdef PCMCIA_DEBUG
- /* DMA termination address check... */
- if (netif_msg_rx_status(ei_local)) {
- int addr, tries = 20;
- do {
- /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
- -- it's broken for Rx on some cards! */
- int high = inb_p(nic_base + EN0_RSARHI);
- int low = inb_p(nic_base + EN0_RSARLO);
- addr = (high << 8) + low;
- if (((ring_offset + xfer_count) & 0xff) == (addr & 0xff))
- break;
- } while (--tries > 0);
- if (tries <= 0)
- netdev_notice(dev, "RX transfer address mismatch,"
- "%#4.4x (expected) vs. %#4.4x (actual).\n",
- ring_offset + xfer_count, addr);
- }
-#endif
- outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
- ei_status.dmaing &= ~0x01;
-} /* dma_block_input */
-
-/*====================================================================*/
-
-static void dma_block_output(struct net_device *dev, int count,
- const u_char *buf, const int start_page)
-{
- unsigned int nic_base = dev->base_addr;
- struct pcnet_dev *info = PRIV(dev);
-#ifdef PCMCIA_DEBUG
- int retries = 0;
- struct ei_device *ei_local = netdev_priv(dev);
-#endif
- u_long dma_start;
-
-#ifdef PCMCIA_DEBUG
- netif_dbg(ei_local, tx_queued, dev, "[bo=%d]\n", count);
-#endif
-
- /* Round the count up for word writes. Do we need to do this?
- What effect will an odd byte count have on the 8390?
- I should check someday. */
- if (count & 0x01)
- count++;
- if (ei_status.dmaing) {
- netdev_err(dev, "DMAing conflict in dma_block_output."
- "[DMAstat:%1x][irqlock:%1x]\n",
- ei_status.dmaing, ei_status.irqlock);
- return;
- }
- ei_status.dmaing |= 0x01;
- /* We should already be in page 0, but to be safe... */
- outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base+PCNET_CMD);
-
-#ifdef PCMCIA_DEBUG
- retry:
-#endif
-
- outb_p(ENISR_RDC, nic_base + EN0_ISR);
-
- /* Now the normal output. */
- outb_p(count & 0xff, nic_base + EN0_RCNTLO);
- outb_p(count >> 8, nic_base + EN0_RCNTHI);
- outb_p(0x00, nic_base + EN0_RSARLO);
- outb_p(start_page, nic_base + EN0_RSARHI);
-
- outb_p(E8390_RWRITE+E8390_START, nic_base + PCNET_CMD);
- outsw(nic_base + PCNET_DATAPORT, buf, count>>1);
-
- dma_start = jiffies;
-
-#ifdef PCMCIA_DEBUG
- /* This was for the ALPHA version only, but enough people have been
- encountering problems that it is still here. */
- /* DMA termination address check... */
- if (netif_msg_tx_queued(ei_local)) {
- int addr, tries = 20;
- do {
- int high = inb_p(nic_base + EN0_RSARHI);
- int low = inb_p(nic_base + EN0_RSARLO);
- addr = (high << 8) + low;
- if ((start_page << 8) + count == addr)
- break;
- } while (--tries > 0);
- if (tries <= 0) {
- netdev_notice(dev, "Tx packet transfer address mismatch,"
- "%#4.4x (expected) vs. %#4.4x (actual).\n",
- (start_page << 8) + count, addr);
- if (retries++ == 0)
- goto retry;
- }
- }
-#endif
-
- while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
- if (time_after(jiffies, dma_start + PCNET_RDC_TIMEOUT)) {
- netdev_warn(dev, "timeout waiting for Tx RDC.\n");
- pcnet_reset_8390(dev);
- NS8390_init(dev, 1);
- break;
- }
-
- outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
- if (info->flags & DELAY_OUTPUT)
- udelay((long)delay_time);
- ei_status.dmaing &= ~0x01;
-}
-
-/*====================================================================*/
-
-static int setup_dma_config(struct pcmcia_device *link, int start_pg,
- int stop_pg)
-{
- struct net_device *dev = link->priv;
-
- ei_status.tx_start_page = start_pg;
- ei_status.rx_start_page = start_pg + TX_PAGES;
- ei_status.stop_page = stop_pg;
-
- /* set up block i/o functions */
- ei_status.get_8390_hdr = dma_get_8390_hdr;
- ei_status.block_input = dma_block_input;
- ei_status.block_output = dma_block_output;
-
- return 0;
-}
-
-/*====================================================================*/
-
-static void copyin(void *dest, void __iomem *src, int c)
-{
- u_short *d = dest;
- u_short __iomem *s = src;
- int odd;
-
- if (c <= 0)
- return;
- odd = (c & 1); c >>= 1;
-
- if (c) {
- do { *d++ = __raw_readw(s++); } while (--c);
- }
- /* get last byte by fetching a word and masking */
- if (odd)
- *((u_char *)d) = readw(s) & 0xff;
-}
-
-static void copyout(void __iomem *dest, const void *src, int c)
-{
- u_short __iomem *d = dest;
- const u_short *s = src;
- int odd;
-
- if (c <= 0)
- return;
- odd = (c & 1); c >>= 1;
-
- if (c) {
- do { __raw_writew(*s++, d++); } while (--c);
- }
- /* copy last byte doing a read-modify-write */
- if (odd)
- writew((readw(d) & 0xff00) | *(u_char *)s, d);
-}
-
-/*====================================================================*/
-
-static void shmem_get_8390_hdr(struct net_device *dev,
- struct e8390_pkt_hdr *hdr,
- int ring_page)
-{
- void __iomem *xfer_start = ei_status.mem + (TX_PAGES<<8)
- + (ring_page << 8)
- - (ei_status.rx_start_page << 8);
-
- copyin(hdr, xfer_start, sizeof(struct e8390_pkt_hdr));
- /* Fix for big endian systems */
- hdr->count = le16_to_cpu(hdr->count);
-}
-
-/*====================================================================*/
-
-static void shmem_block_input(struct net_device *dev, int count,
- struct sk_buff *skb, int ring_offset)
-{
- void __iomem *base = ei_status.mem;
- unsigned long offset = (TX_PAGES<<8) + ring_offset
- - (ei_status.rx_start_page << 8);
- char *buf = skb->data;
-
- if (offset + count > ei_status.priv) {
- /* We must wrap the input move. */
- int semi_count = ei_status.priv - offset;
- copyin(buf, base + offset, semi_count);
- buf += semi_count;
- offset = TX_PAGES<<8;
- count -= semi_count;
- }
- copyin(buf, base + offset, count);
-}
-
-/*====================================================================*/
-
-static void shmem_block_output(struct net_device *dev, int count,
- const u_char *buf, const int start_page)
-{
- void __iomem *shmem = ei_status.mem + (start_page << 8);
- shmem -= ei_status.tx_start_page << 8;
- copyout(shmem, buf, count);
-}
-
-/*====================================================================*/
-
-static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
- int stop_pg, int cm_offset)
-{
- struct net_device *dev = link->priv;
- struct pcnet_dev *info = PRIV(dev);
- int i, window_size, offset, ret;
-
- window_size = (stop_pg - start_pg) << 8;
- if (window_size > 32 * 1024)
- window_size = 32 * 1024;
-
- /* Make sure it's a power of two. */
- window_size = roundup_pow_of_two(window_size);
-
- /* Allocate a memory window */
- link->resource[3]->flags |= WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
- link->resource[3]->flags |= WIN_USE_WAIT;
- link->resource[3]->start = 0; link->resource[3]->end = window_size;
- ret = pcmcia_request_window(link, link->resource[3], mem_speed);
- if (ret)
- goto failed;
-
- offset = (start_pg << 8) + cm_offset;
- offset -= offset % window_size;
- ret = pcmcia_map_mem_page(link, link->resource[3], offset);
- if (ret)
- goto failed;
-
- /* Try scribbling on the buffer */
- info->base = ioremap(link->resource[3]->start,
- resource_size(link->resource[3]));
- if (unlikely(!info->base)) {
- ret = -ENOMEM;
- goto failed;
- }
-
- for (i = 0; i < (TX_PAGES<<8); i += 2)
- __raw_writew((i>>1), info->base+offset+i);
- udelay(100);
- for (i = 0; i < (TX_PAGES<<8); i += 2)
- if (__raw_readw(info->base+offset+i) != (i>>1)) break;
- pcnet_reset_8390(dev);
- if (i != (TX_PAGES<<8)) {
- iounmap(info->base);
- pcmcia_release_window(link, link->resource[3]);
- info->base = NULL;
- goto failed;
- }
-
- ei_status.mem = info->base + offset;
- ei_status.priv = resource_size(link->resource[3]);
- dev->mem_start = (u_long)ei_status.mem;
- dev->mem_end = dev->mem_start + resource_size(link->resource[3]);
-
- ei_status.tx_start_page = start_pg;
- ei_status.rx_start_page = start_pg + TX_PAGES;
- ei_status.stop_page = start_pg + (
- (resource_size(link->resource[3]) - offset) >> 8);
-
- /* set up block i/o functions */
- ei_status.get_8390_hdr = shmem_get_8390_hdr;
- ei_status.block_input = shmem_block_input;
- ei_status.block_output = shmem_block_output;
-
- info->flags |= USE_SHMEM;
- return 0;
-
-failed:
- return 1;
-}
-
-/*====================================================================*/
-
-static const struct pcmcia_device_id pcnet_ids[] = {
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0057, 0x0021),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0104, 0x000a),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0xea15),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0x3341),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0xc0ab),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x021b, 0x0101),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x08a1, 0xc0ab),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f),
- PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c),
- PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3),
- PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15),
- PCMCIA_MFC_DEVICE_PROD_ID123(0, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f),
- PCMCIA_MFC_DEVICE_PROD_ID2(0, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302),
- PCMCIA_DEVICE_MANF_CARD(0x0057, 0x1004),
- PCMCIA_DEVICE_MANF_CARD(0x0104, 0x000d),
- PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0075),
- PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0145),
- PCMCIA_DEVICE_MANF_CARD(0x0149, 0x0230),
- PCMCIA_DEVICE_MANF_CARD(0x0149, 0x4530),
- PCMCIA_DEVICE_MANF_CARD(0x0149, 0xc1ab),
- PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0110),
- PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x8041),
- PCMCIA_DEVICE_MANF_CARD(0x0213, 0x2452),
- PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0300),
- PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0307),
- PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030a),
- PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1103),
- PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1121),
- PCMCIA_DEVICE_MANF_CARD(0xc001, 0x0009),
- PCMCIA_DEVICE_PROD_ID12("2408LAN", "Ethernet", 0x352fff7f, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID1234("Socket", "CF 10/100 Ethernet Card", "Revision B", "05/11/06", 0xb38bcc2e, 0x4de88352, 0xeaca6c8d, 0x7e57c22e),
- PCMCIA_DEVICE_PROD_ID123("Cardwell", "PCMCIA", "ETHERNET", 0x9533672e, 0x281f1c5d, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID123("CNet ", "CN30BC", "ETHERNET", 0x9fe55d3d, 0x85601198, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID123("Digital", "Ethernet", "Adapter", 0x9999ab35, 0x00b2e941, 0x4b0d829e),
- PCMCIA_DEVICE_PROD_ID123("Edimax Technology Inc.", "PCMCIA", "Ethernet Card", 0x738a0019, 0x281f1c5d, 0x5e9d92c0),
- PCMCIA_DEVICE_PROD_ID123("EFA ", "EFA207", "ETHERNET", 0x3d294be4, 0xeb9aab6c, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID123("I-O DATA", "PCLA", "ETHERNET", 0x1d55d7ec, 0xe4c64d34, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID123("IO DATA", "PCLATE", "ETHERNET", 0x547e66dc, 0x6b260753, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID123("KingMax Technology Inc.", "EN10-T2", "PCMCIA Ethernet Card", 0x932b7189, 0x699e4436, 0x6f6652e0),
- PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2216", 0x281f1c5d, 0xd4cd2f20, 0xb87add82),
- PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2620", 0x281f1c5d, 0xd4cd2f20, 0x7d3d83a8),
- PCMCIA_DEVICE_PROD_ID1("2412LAN", 0x67f236ab),
- PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2212", 0xdfc6b5b2, 0xcb112a11),
- PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2216-PCMCIA-ETHERNET", 0xdfc6b5b2, 0x5542bfff),
- PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA100-PCM-T V2 100/10M LAN PC Card", 0xbb7fbdd7, 0xcd91cc68),
- PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA100-PCM V2", 0x36634a66, 0xc6d05997),
- PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA-PCM_V2", 0xbb7fBdd7, 0x28e299f8),
- PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA-PCM V3", 0x36634a66, 0x62241d96),
- PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8010", 0x5070a7f9, 0x82f96e96),
- PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8610", 0x5070a7f9, 0x86741224),
- PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002", 0x93b15570, 0x75ec3efb),
- PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002T", 0x93b15570, 0x461c5247),
- PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8010", 0x93b15570, 0x82f96e96),
- PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet", 0x578ba6e7, 0x0a9888c1),
- PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet 10/100", 0x578ba6e7, 0x939fedbd),
- PCMCIA_DEVICE_PROD_ID12("AROWANA", "PCMCIA Ethernet LAN Card", 0x313adbc8, 0x08d9f190),
- PCMCIA_DEVICE_PROD_ID12("ASANTE", "FriendlyNet PC Card", 0x3a7ade0f, 0x41c64504),
- PCMCIA_DEVICE_PROD_ID12("Billionton", "LNT-10TB", 0x552ab682, 0xeeb1ba6a),
- PCMCIA_DEVICE_PROD_ID12("CF", "10Base-Ethernet", 0x44ebf863, 0x93ae4d79),
- PCMCIA_DEVICE_PROD_ID12("CNet", "CN40BC Ethernet", 0xbc477dde, 0xfba775a7),
- PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "BASEline PCMCIA 10 MBit Ethernetadapter", 0xfa2e424d, 0xe9190d8a),
- PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "FASTline PCMCIA 10/100 Fast-Ethernet", 0xfa2e424d, 0x3953d9b9),
- PCMCIA_DEVICE_PROD_ID12("CONTEC", "C-NET(PC)C-10L", 0x21cab552, 0xf6f90722),
- PCMCIA_DEVICE_PROD_ID12("corega", "FEther PCC-TXF", 0x0a21501a, 0xa51564a2),
- PCMCIA_DEVICE_PROD_ID12("corega", "Ether CF-TD", 0x0a21501a, 0x6589340a),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether CF-TD LAN Card", 0x5261440f, 0x8797663b),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-T", 0x5261440f, 0xfa9d85bd),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-TD", 0x5261440f, 0xc49bd73d),
- PCMCIA_DEVICE_PROD_ID12("Corega K.K.", "corega EtherII PCC-TD", 0xd4fdcbd8, 0xc49bd73d),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-T", 0x5261440f, 0x6705fcaa),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-TD", 0x5261440f, 0x47d5ca83),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FastEther PCC-TX", 0x5261440f, 0x485e85d9),
- PCMCIA_DEVICE_PROD_ID12("Corega,K.K.", "Ethernet LAN Card", 0x110d26d9, 0x9fd2f0a2),
- PCMCIA_DEVICE_PROD_ID12("corega,K.K.", "Ethernet LAN Card", 0x9791a90e, 0x9fd2f0a2),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "(CG-LAPCCTXD)", 0x5261440f, 0x73ec0d88),
- PCMCIA_DEVICE_PROD_ID12("CouplerlessPCMCIA", "100BASE", 0xee5af0ad, 0x7c2add04),
- PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-010", 0x77008979, 0x9d8d445d),
- PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-110E 10/100M LAN Card", 0x77008979, 0xfd184814),
- PCMCIA_DEVICE_PROD_ID12("DataTrek.", "NetCard ", 0x5cd66d9d, 0x84697ce0),
- PCMCIA_DEVICE_PROD_ID12("Dayna Communications, Inc.", "CommuniCard E", 0x0c629325, 0xb4e7dbaf),
- PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100", 0x697403d8, 0xe160b995),
- PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100 Dongless", 0x697403d8, 0xa6d3b233),
- PCMCIA_DEVICE_PROD_ID12("DIGITAL", "DEPCM-XX", 0x69616cb3, 0xe600e76e),
- PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-650", 0x1a424a1c, 0xf28c8398),
- PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-660", 0x1a424a1c, 0xd9a1d05b),
- PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-660+", 0x1a424a1c, 0x50dcd0ec),
- PCMCIA_DEVICE_PROD_ID12("D-Link", "DFE-650", 0x1a424a1c, 0x0f0073f9),
- PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 PC Card", 0x725b842d, 0xf1efee84),
- PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 Port Attached PC Card", 0x725b842d, 0x2db1f8e9),
- PCMCIA_DEVICE_PROD_ID12("Dynalink", "L10BC", 0x55632fd5, 0xdc65f2b1),
- PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10BC", 0x6a26d1cf, 0xdc65f2b1),
- PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10C", 0x6a26d1cf, 0xc4f84efb),
- PCMCIA_DEVICE_PROD_ID12("E-CARD", "E-CARD", 0x6701da11, 0x6701da11),
- PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet 10BaseT card", 0x53c864c6, 0xedd059f6),
- PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet Combo card", 0x53c864c6, 0x929c486c),
- PCMCIA_DEVICE_PROD_ID12("Ethernet", "Adapter", 0x00b2e941, 0x4b0d829e),
- PCMCIA_DEVICE_PROD_ID12("Ethernet Adapter", "E2000 PCMCIA Ethernet", 0x96767301, 0x71fbbc61),
- PCMCIA_DEVICE_PROD_ID12("Ethernet PCMCIA adapter", "EP-210", 0x8dd86181, 0xf2b52517),
- PCMCIA_DEVICE_PROD_ID12("Fast Ethernet", "Adapter", 0xb4be14e3, 0x4b0d829e),
- PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2000", 0x2a151fac, 0xf00555cb),
- PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2220", 0x2a151fac, 0xc1b7e327),
- PCMCIA_DEVICE_PROD_ID12("GVC", "NIC-2000p", 0x76e171bd, 0x6eb1c947),
- PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "Ethernet", 0xe3736c88, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("IC-CARD", "IC-CARD", 0x60cb09a6, 0x60cb09a6),
- PCMCIA_DEVICE_PROD_ID12("IC-CARD+", "IC-CARD+", 0x93693494, 0x93693494),
- PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCETTX", 0x547e66dc, 0x6fc5459b),
- PCMCIA_DEVICE_PROD_ID12("iPort", "10/100 Ethernet Card", 0x56c538d2, 0x11b0ffc0),
- PCMCIA_DEVICE_PROD_ID12("KANSAI ELECTRIC CO.,LTD", "KLA-PCM/T", 0xb18dc3b4, 0xcc51a956),
- PCMCIA_DEVICE_PROD_ID12("KENTRONICS", "KEP-230", 0xaf8144c9, 0x868f6616),
- PCMCIA_DEVICE_PROD_ID12("KCI", "PE520 PCMCIA Ethernet Adapter", 0xa89b87d3, 0x1eb88e64),
- PCMCIA_DEVICE_PROD_ID12("KINGMAX", "EN10T2T", 0x7bcb459a, 0xa5c81fa5),
- PCMCIA_DEVICE_PROD_ID12("Kingston", "KNE-PC2", 0x1128e633, 0xce2a89b3),
- PCMCIA_DEVICE_PROD_ID12("Kingston Technology Corp.", "EtheRx PC Card Ethernet Adapter", 0x313c7be3, 0x0afb54a2),
- PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-10/100CD", 0x1b7827b2, 0xcda71d1c),
- PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDF", 0x1b7827b2, 0xfec71e40),
- PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDL/T", 0x1b7827b2, 0x79fba4f7),
- PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDS", 0x1b7827b2, 0x931afaab),
- PCMCIA_DEVICE_PROD_ID12("LEMEL", "LM-N89TX PRO", 0xbbefb52f, 0xd2897a97),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "Combo PCMCIA EthernetCard (EC2T)", 0x0733cc81, 0x32ee8c78),
- PCMCIA_DEVICE_PROD_ID12("LINKSYS", "E-CARD", 0xf7cb0b07, 0x6701da11),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 Integrated PC Card (PCM100)", 0x0733cc81, 0x453c3f9d),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100)", 0x0733cc81, 0x66c5a389),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V2)", 0x0733cc81, 0x3a3b28e9),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "HomeLink Phoneline + 10/100 Network PC Card (PCM100H1)", 0x733cc81, 0x7a3e5c3a),
- PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN100TX", 0x88fcdeda, 0x6d772737),
- PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN100TE", 0x88fcdeda, 0x0e714bee),
- PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN20T", 0x88fcdeda, 0x81090922),
- PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN10TE", 0x88fcdeda, 0xc1e2521c),
- PCMCIA_DEVICE_PROD_ID12("LONGSHINE", "PCMCIA Ethernet Card", 0xf866b0b0, 0x6f6652e0),
- PCMCIA_DEVICE_PROD_ID12("MACNICA", "ME1-JEIDA", 0x20841b68, 0xaf8a3578),
- PCMCIA_DEVICE_PROD_ID12("Macsense", "MPC-10", 0xd830297f, 0xd265c307),
- PCMCIA_DEVICE_PROD_ID12("Matsushita Electric Industrial Co.,LTD.", "CF-VEL211", 0x44445376, 0x8ded41d4),
- PCMCIA_DEVICE_PROD_ID12("MAXTECH", "PCN2000", 0x78d64bc0, 0xca0ca4b8),
- PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-T", 0x481e0094, 0xa2eb0cf3),
- PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-TX", 0x481e0094, 0x41a6916c),
- PCMCIA_DEVICE_PROD_ID12("Microcom C.E.", "Travel Card LAN 10/100", 0x4b91cec7, 0xe70220d6),
- PCMCIA_DEVICE_PROD_ID12("Microdyne", "NE4200", 0x2e6da59b, 0x0478e472),
- PCMCIA_DEVICE_PROD_ID12("MIDORI ELEC.", "LT-PCMT", 0x648d55c1, 0xbde526c7),
- PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover 4100", 0x36e1191f, 0x60c229b9),
- PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover NE4100", 0x36e1191f, 0xa6617ec8),
- PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J12", 0x18df0ba0, 0xbc912d76),
- PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA410TX", 0x9aa79dc3, 0x60e5bc0e),
- PCMCIA_DEVICE_PROD_ID12("Network Everywhere", "Fast Ethernet 10/100 PC Card", 0x820a67b6, 0x31ed1a5f),
- PCMCIA_DEVICE_PROD_ID12("NextCom K.K.", "Next Hawk", 0xaedaec74, 0xad050ef1),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100Mbps Ethernet Card", 0x281f1c5d, 0x6e41773b),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet", 0x281f1c5d, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET", 0x281f1c5d, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet 10BaseT Card", 0x281f1c5d, 0x4de2f6c8),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Card", 0x281f1c5d, 0x5e9d92c0),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Combo card", 0x281f1c5d, 0x929c486c),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET V1.0", 0x281f1c5d, 0x4d8817c8),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEthernet", 0x281f1c5d, 0xfe871eeb),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast-Ethernet", 0x281f1c5d, 0x45f1f3b4),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FAST ETHERNET CARD", 0x281f1c5d, 0xec5dbca7),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA LAN", "Ethernet", 0x7500e246, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "LNT-10TN", 0x281f1c5d, 0xe707f641),
- PCMCIA_DEVICE_PROD_ID12("PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "UE2212", 0x281f1c5d, 0xbf17199b),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", " Ethernet NE2000 Compatible", 0x281f1c5d, 0x42d5d7e1),
- PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10baseT 3.3V", 0xebf91155, 0x30074c80),
- PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10BaseT 3.3V", 0xebf91155, 0x7f5a4f50),
- PCMCIA_DEVICE_PROD_ID12("Psion Dacom", "Gold Card Ethernet", 0xf5f025c2, 0x3a30e110),
- PCMCIA_DEVICE_PROD_ID12("=RELIA==", "Ethernet", 0xcdd0644a, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("RIOS Systems Co.", "PC CARD3 ETHERNET", 0x7dd33481, 0x10b41826),
- PCMCIA_DEVICE_PROD_ID12("RP", "1625B Ethernet NE2000 Compatible", 0xe3e66e22, 0xb96150df),
- PCMCIA_DEVICE_PROD_ID12("RPTI", "EP400 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4a7e2ae0),
- PCMCIA_DEVICE_PROD_ID12("RPTI", "EP401 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4bcbd7fd),
- PCMCIA_DEVICE_PROD_ID12("RPTI LTD.", "EP400", 0xc53ac515, 0x81e39388),
- PCMCIA_DEVICE_PROD_ID12("SCM", "Ethernet Combo card", 0xbdc3b102, 0x929c486c),
- PCMCIA_DEVICE_PROD_ID12("Seiko Epson Corp.", "Ethernet", 0x09928730, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("SMC", "EZCard-10-PCMCIA", 0xc4f8b18b, 0xfb21d265),
- PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision D", 0xc70a4760, 0x2ade483e),
- PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision E", 0xc70a4760, 0x5dd978a8),
- PCMCIA_DEVICE_PROD_ID12("TDK", "LAK-CD031 for PCMCIA", 0x1eae9475, 0x0ed386fa),
- PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE450T", 0x466b05f0, 0x8b74bc4f),
- PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE550T", 0x466b05f0, 0x33c8db2a),
- PCMCIA_DEVICE_PROD_ID13("Hypertec", "EP401", 0x8787bec7, 0xf6e4a31e),
- PCMCIA_DEVICE_PROD_ID13("KingMax Technology Inc.", "Ethernet Card", 0x932b7189, 0x5e9d92c0),
- PCMCIA_DEVICE_PROD_ID13("LONGSHINE", "EP401", 0xf866b0b0, 0xf6e4a31e),
- PCMCIA_DEVICE_PROD_ID13("Xircom", "CFE-10", 0x2e3ee845, 0x22a49f89),
- PCMCIA_DEVICE_PROD_ID1("CyQ've 10 Base-T LAN CARD", 0x94faf360),
- PCMCIA_DEVICE_PROD_ID1("EP-210 PCMCIA LAN CARD.", 0x8850b4de),
- PCMCIA_DEVICE_PROD_ID1("ETHER-C16", 0x06a8514f),
- PCMCIA_DEVICE_PROD_ID1("NE2000 Compatible", 0x75b8ad5a),
- PCMCIA_DEVICE_PROD_ID2("EN-6200P2", 0xa996d078),
- /* too generic! */
- /* PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100 Ethernet Card", 0x281f1c5d, 0x11b0ffc0), */
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"),
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"),
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"),
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"),
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"),
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"),
- PCMCIA_MFC_DEVICE_CIS_PROD_ID12(0, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"),
- PCMCIA_MFC_DEVICE_CIS_PROD_ID4(0, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"),
- PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0175, 0x0000, "cis/DP83903.cis"),
- PCMCIA_DEVICE_CIS_PROD_ID12("Allied Telesis,K.K", "Ethernet LAN Card", 0x2ad62f3c, 0x9fd2f0a2, "cis/LA-PCM.cis"),
- PCMCIA_DEVICE_CIS_PROD_ID12("KTI", "PE520 PLUS", 0xad180345, 0x9d58d392, "cis/PE520.cis"),
- PCMCIA_DEVICE_CIS_PROD_ID12("NDC", "Ethernet", 0x01c43ae1, 0x00b2e941, "cis/NE2K.cis"),
- PCMCIA_DEVICE_CIS_PROD_ID12("PMX ", "PE-200", 0x34f3f1c8, 0x10b59f8c, "cis/PE-200.cis"),
- PCMCIA_DEVICE_CIS_PROD_ID12("TAMARACK", "Ethernet", 0xcf434fba, 0x00b2e941, "cis/tamarack.cis"),
- PCMCIA_DEVICE_PROD_ID12("Ethernet", "CF Size PC Card", 0x00b2e941, 0x43ac239b),
- PCMCIA_DEVICE_PROD_ID123("Fast Ethernet", "CF Size PC Card", "1.0",
- 0xb4be14e3, 0x43ac239b, 0x0877b627),
- PCMCIA_DEVICE_NULL
-};
-MODULE_DEVICE_TABLE(pcmcia, pcnet_ids);
-MODULE_FIRMWARE("cis/PCMLM28.cis");
-MODULE_FIRMWARE("cis/DP83903.cis");
-MODULE_FIRMWARE("cis/LA-PCM.cis");
-MODULE_FIRMWARE("cis/PE520.cis");
-MODULE_FIRMWARE("cis/NE2K.cis");
-MODULE_FIRMWARE("cis/PE-200.cis");
-MODULE_FIRMWARE("cis/tamarack.cis");
-
-static struct pcmcia_driver pcnet_driver = {
- .name = "pcnet_cs",
- .probe = pcnet_probe,
- .remove = pcnet_detach,
- .owner = THIS_MODULE,
- .id_table = pcnet_ids,
- .suspend = pcnet_suspend,
- .resume = pcnet_resume,
-};
-module_pcmcia_driver(pcnet_driver);
--
2.53.0
^ permalink raw reply related
* [PATCH net 10/18] drivers: net: smsc: smc91c92: Remove this driver
From: Andrew Lunn @ 2026-04-21 19:31 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260421-v7-0-0-net-next-driver-removal-v1-v1-0-69517c689d1f@lunn.ch>
The smc91c92 was written by David A Hinds in 1999. It is an PCMCIA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/smsc/Kconfig | 12 -
drivers/net/ethernet/smsc/Makefile | 1 -
drivers/net/ethernet/smsc/smc91c92_cs.c | 2059 -------------------------------
3 files changed, 2072 deletions(-)
diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig
index d25bbcc98854..66bca803b19c 100644
--- a/drivers/net/ethernet/smsc/Kconfig
+++ b/drivers/net/ethernet/smsc/Kconfig
@@ -37,18 +37,6 @@ config SMC91X
The module will be called smc91x. If you want to compile it as a
module, say M here and read <file:Documentation/kbuild/modules.rst>.
-config PCMCIA_SMC91C92
- tristate "SMC 91Cxx PCMCIA support"
- depends on PCMCIA && HAS_IOPORT
- select CRC32
- select MII
- help
- Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
- (PC-card) Ethernet or Fast Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called smc91c92_cs. If unsure, say N.
-
config EPIC100
tristate "SMC EtherPower II"
depends on PCI
diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile
index afea0b94c2a4..ab6f03f7ba17 100644
--- a/drivers/net/ethernet/smsc/Makefile
+++ b/drivers/net/ethernet/smsc/Makefile
@@ -4,7 +4,6 @@
#
obj-$(CONFIG_SMC91X) += smc91x.o
-obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o
obj-$(CONFIG_EPIC100) += epic100.o
obj-$(CONFIG_SMSC9420) += smsc9420.o
obj-$(CONFIG_SMSC911X) += smsc911x.o
diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c
deleted file mode 100644
index cc0c75694351..000000000000
--- a/drivers/net/ethernet/smsc/smc91c92_cs.c
+++ /dev/null
@@ -1,2059 +0,0 @@
-/*======================================================================
-
- A PCMCIA ethernet driver for SMC91c92-based cards.
-
- This driver supports Megahertz PCMCIA ethernet cards; and
- Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem
- multifunction cards.
-
- Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
-
- smc91c92_cs.c 1.122 2002/10/25 06:26:39
-
- This driver contains code written by Donald Becker
- (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au),
- David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman
- (erik@vt.edu). Donald wrote the SMC 91c92 code using parts of
- Erik's SMC 91c94 driver. Rowan wrote a similar driver, and I've
- incorporated some parts of his driver here. I (Dave) wrote most
- of the PCMCIA glue code, and the Ositech support code. Kelly
- Stephens (kstephen@holli.com) added support for the Motorola
- Mariner, with help from Allen Brost.
-
- This software may be used and distributed according to the terms of
- the GNU General Public License, incorporated herein by reference.
-
-======================================================================*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/crc32.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/ethtool.h>
-#include <linux/mii.h>
-#include <linux/jiffies.h>
-#include <linux/firmware.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/ss.h>
-
-#include <asm/io.h>
-#include <linux/uaccess.h>
-
-/*====================================================================*/
-
-static const char *if_names[] = { "auto", "10baseT", "10base2"};
-
-/* Firmware name */
-#define FIRMWARE_NAME "ositech/Xilinx7OD.bin"
-
-/* Module parameters */
-
-MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(FIRMWARE_NAME);
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/*
- Transceiver/media type.
- 0 = auto
- 1 = 10baseT (and autoselect if #define AUTOSELECT),
- 2 = AUI/10base2,
-*/
-INT_MODULE_PARM(if_port, 0);
-
-
-#define DRV_NAME "smc91c92_cs"
-#define DRV_VERSION "1.123"
-
-/*====================================================================*/
-
-/* Operational parameter that usually are not changed. */
-
-/* Time in jiffies before concluding Tx hung */
-#define TX_TIMEOUT ((400*HZ)/1000)
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-#define INTR_WORK 4
-
-/* Times to check the check the chip before concluding that it doesn't
- currently have room for another Tx packet. */
-#define MEMORY_WAIT_TIME 8
-
-struct smc_private {
- struct pcmcia_device *p_dev;
- spinlock_t lock;
- u_short manfid;
- u_short cardid;
-
- struct sk_buff *saved_skb;
- int packets_waiting;
- void __iomem *base;
- u_short cfg;
- struct timer_list media;
- int watchdog, tx_err;
- u_short media_status;
- u_short fast_poll;
- u_short link_status;
- struct mii_if_info mii_if;
- int duplex;
- int rx_ovrn;
- unsigned long last_rx;
-};
-
-/* Special definitions for Megahertz multifunction cards */
-#define MEGAHERTZ_ISR 0x0380
-
-/* Special function registers for Motorola Mariner */
-#define MOT_LAN 0x0000
-#define MOT_UART 0x0020
-#define MOT_EEPROM 0x20
-
-#define MOT_NORMAL \
-(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA)
-
-/* Special function registers for Ositech cards */
-#define OSITECH_AUI_CTL 0x0c
-#define OSITECH_PWRDOWN 0x0d
-#define OSITECH_RESET 0x0e
-#define OSITECH_ISR 0x0f
-#define OSITECH_AUI_PWR 0x0c
-#define OSITECH_RESET_ISR 0x0e
-
-#define OSI_AUI_PWR 0x40
-#define OSI_LAN_PWRDOWN 0x02
-#define OSI_MODEM_PWRDOWN 0x01
-#define OSI_LAN_RESET 0x02
-#define OSI_MODEM_RESET 0x01
-
-/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */
-#define BANK_SELECT 14 /* Window select register. */
-#define SMC_SELECT_BANK(x) { outw(x, ioaddr + BANK_SELECT); }
-
-/* Bank 0 registers. */
-#define TCR 0 /* transmit control register */
-#define TCR_CLEAR 0 /* do NOTHING */
-#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */
-#define TCR_PAD_EN 0x0080 /* pads short packets to 64 bytes */
-#define TCR_MONCSN 0x0400 /* Monitor Carrier. */
-#define TCR_FDUPLX 0x0800 /* Full duplex mode. */
-#define TCR_NORMAL TCR_ENABLE | TCR_PAD_EN
-
-#define EPH 2 /* Ethernet Protocol Handler report. */
-#define EPH_TX_SUC 0x0001
-#define EPH_SNGLCOL 0x0002
-#define EPH_MULCOL 0x0004
-#define EPH_LTX_MULT 0x0008
-#define EPH_16COL 0x0010
-#define EPH_SQET 0x0020
-#define EPH_LTX_BRD 0x0040
-#define EPH_TX_DEFR 0x0080
-#define EPH_LAT_COL 0x0200
-#define EPH_LOST_CAR 0x0400
-#define EPH_EXC_DEF 0x0800
-#define EPH_CTR_ROL 0x1000
-#define EPH_RX_OVRN 0x2000
-#define EPH_LINK_OK 0x4000
-#define EPH_TX_UNRN 0x8000
-#define MEMINFO 8 /* Memory Information Register */
-#define MEMCFG 10 /* Memory Configuration Register */
-
-/* Bank 1 registers. */
-#define CONFIG 0
-#define CFG_MII_SELECT 0x8000 /* 91C100 only */
-#define CFG_NO_WAIT 0x1000
-#define CFG_FULL_STEP 0x0400
-#define CFG_SET_SQLCH 0x0200
-#define CFG_AUI_SELECT 0x0100
-#define CFG_16BIT 0x0080
-#define CFG_DIS_LINK 0x0040
-#define CFG_STATIC 0x0030
-#define CFG_IRQ_SEL_1 0x0004
-#define CFG_IRQ_SEL_0 0x0002
-#define BASE_ADDR 2
-#define ADDR0 4
-#define GENERAL 10
-#define CONTROL 12
-#define CTL_STORE 0x0001
-#define CTL_RELOAD 0x0002
-#define CTL_EE_SELECT 0x0004
-#define CTL_TE_ENABLE 0x0020
-#define CTL_CR_ENABLE 0x0040
-#define CTL_LE_ENABLE 0x0080
-#define CTL_AUTO_RELEASE 0x0800
-#define CTL_POWERDOWN 0x2000
-
-/* Bank 2 registers. */
-#define MMU_CMD 0
-#define MC_ALLOC 0x20 /* or with number of 256 byte packets */
-#define MC_RESET 0x40
-#define MC_RELEASE 0x80 /* remove and release the current rx packet */
-#define MC_FREEPKT 0xA0 /* Release packet in PNR register */
-#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */
-#define PNR_ARR 2
-#define FIFO_PORTS 4
-#define FP_RXEMPTY 0x8000
-#define POINTER 6
-#define PTR_AUTO_INC 0x0040
-#define PTR_READ 0x2000
-#define PTR_AUTOINC 0x4000
-#define PTR_RCV 0x8000
-#define DATA_1 8
-#define INTERRUPT 12
-#define IM_RCV_INT 0x1
-#define IM_TX_INT 0x2
-#define IM_TX_EMPTY_INT 0x4
-#define IM_ALLOC_INT 0x8
-#define IM_RX_OVRN_INT 0x10
-#define IM_EPH_INT 0x20
-
-#define RCR 4
-enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002,
- RxEnable = 0x0100, RxStripCRC = 0x0200};
-#define RCR_SOFTRESET 0x8000 /* resets the chip */
-#define RCR_STRIP_CRC 0x200 /* strips CRC */
-#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */
-#define RCR_ALMUL 0x4 /* receive all multicast packets */
-#define RCR_PROMISC 0x2 /* enable promiscuous mode */
-
-/* the normal settings for the RCR register : */
-#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE)
-#define RCR_CLEAR 0x0 /* set it to a base state */
-#define COUNTER 6
-
-/* BANK 3 -- not the same values as in smc9194! */
-#define MULTICAST0 0
-#define MULTICAST2 2
-#define MULTICAST4 4
-#define MULTICAST6 6
-#define MGMT 8
-#define REVISION 0x0a
-
-/* Transmit status bits. */
-#define TS_SUCCESS 0x0001
-#define TS_16COL 0x0010
-#define TS_LATCOL 0x0200
-#define TS_LOSTCAR 0x0400
-
-/* Receive status bits. */
-#define RS_ALGNERR 0x8000
-#define RS_BADCRC 0x2000
-#define RS_ODDFRAME 0x1000
-#define RS_TOOLONG 0x0800
-#define RS_TOOSHORT 0x0400
-#define RS_MULTICAST 0x0001
-#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
-
-#define set_bits(v, p) outw(inw(p)|(v), (p))
-#define mask_bits(v, p) outw(inw(p)&(v), (p))
-
-/*====================================================================*/
-
-static void smc91c92_detach(struct pcmcia_device *p_dev);
-static int smc91c92_config(struct pcmcia_device *link);
-static void smc91c92_release(struct pcmcia_device *link);
-
-static int smc_open(struct net_device *dev);
-static int smc_close(struct net_device *dev);
-static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static irqreturn_t smc_interrupt(int irq, void *dev_id);
-static void smc_rx(struct net_device *dev);
-static void set_rx_mode(struct net_device *dev);
-static int s9k_config(struct net_device *dev, struct ifmap *map);
-static void smc_set_xcvr(struct net_device *dev, int if_port);
-static void smc_reset(struct net_device *dev);
-static void media_check(struct timer_list *t);
-static void mdio_sync(unsigned int addr);
-static int mdio_read(struct net_device *dev, int phy_id, int loc);
-static void mdio_write(struct net_device *dev, int phy_id, int loc, int value);
-static int smc_link_ok(struct net_device *dev);
-static const struct ethtool_ops ethtool_ops;
-
-static const struct net_device_ops smc_netdev_ops = {
- .ndo_open = smc_open,
- .ndo_stop = smc_close,
- .ndo_start_xmit = smc_start_xmit,
- .ndo_tx_timeout = smc_tx_timeout,
- .ndo_set_config = s9k_config,
- .ndo_set_rx_mode = set_rx_mode,
- .ndo_eth_ioctl = smc_ioctl,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int smc91c92_probe(struct pcmcia_device *link)
-{
- struct smc_private *smc;
- struct net_device *dev;
-
- dev_dbg(&link->dev, "smc91c92_attach()\n");
-
- /* Create new ethernet device */
- dev = alloc_etherdev(sizeof(struct smc_private));
- if (!dev)
- return -ENOMEM;
- smc = netdev_priv(dev);
- smc->p_dev = link;
- link->priv = dev;
-
- spin_lock_init(&smc->lock);
-
- /* The SMC91c92-specific entries in the device structure. */
- dev->netdev_ops = &smc_netdev_ops;
- dev->ethtool_ops = ðtool_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- smc->mii_if.dev = dev;
- smc->mii_if.mdio_read = mdio_read;
- smc->mii_if.mdio_write = mdio_write;
- smc->mii_if.phy_id_mask = 0x1f;
- smc->mii_if.reg_num_mask = 0x1f;
-
- return smc91c92_config(link);
-} /* smc91c92_attach */
-
-static void smc91c92_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "smc91c92_detach\n");
-
- unregister_netdev(dev);
-
- smc91c92_release(link);
-
- free_netdev(dev);
-} /* smc91c92_detach */
-
-/*====================================================================*/
-
-static int cvt_ascii_address(struct net_device *dev, char *s)
-{
- u8 mac[ETH_ALEN];
- int i, j, da, c;
-
- if (strlen(s) != 12)
- return -1;
- for (i = 0; i < 6; i++) {
- da = 0;
- for (j = 0; j < 2; j++) {
- c = *s++;
- da <<= 4;
- da += ((c >= '0') && (c <= '9')) ?
- (c - '0') : ((c & 0x0f) + 9);
- }
- mac[i] = da;
- }
- eth_hw_addr_set(dev, mac);
- return 0;
-}
-
-/*====================================================================
-
- Configuration stuff for Megahertz cards
-
- mhz_3288_power() is used to power up a 3288's ethernet chip.
- mhz_mfc_config() handles socket setup for multifunction (1144
- and 3288) cards. mhz_setup() gets a card's hardware ethernet
- address.
-
-======================================================================*/
-
-static int mhz_3288_power(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- u_char tmp;
-
- /* Read the ISR twice... */
- readb(smc->base+MEGAHERTZ_ISR);
- udelay(5);
- readb(smc->base+MEGAHERTZ_ISR);
-
- /* Pause 200ms... */
- mdelay(200);
-
- /* Now read and write the COR... */
- tmp = readb(smc->base + link->config_base + CISREG_COR);
- udelay(5);
- writeb(tmp, smc->base + link->config_base + CISREG_COR);
-
- return 0;
-}
-
-static int mhz_mfc_config_check(struct pcmcia_device *p_dev, void *priv_data)
-{
- int k;
- p_dev->io_lines = 16;
- p_dev->resource[1]->start = p_dev->resource[0]->start;
- p_dev->resource[1]->end = 8;
- p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- p_dev->resource[0]->end = 16;
- p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
- for (k = 0; k < 0x400; k += 0x10) {
- if (k & 0x80)
- continue;
- p_dev->resource[0]->start = k ^ 0x300;
- if (!pcmcia_request_io(p_dev))
- return 0;
- }
- return -ENODEV;
-}
-
-static int mhz_mfc_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- unsigned int offset;
- int i;
-
- link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ |
- CONF_AUTO_SET_IO;
-
- /* The Megahertz combo cards have modem-like CIS entries, so
- we have to explicitly try a bunch of port combinations. */
- if (pcmcia_loop_config(link, mhz_mfc_config_check, NULL))
- return -ENODEV;
-
- dev->base_addr = link->resource[0]->start;
-
- /* Allocate a memory window, for accessing the ISR */
- link->resource[2]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
- link->resource[2]->start = link->resource[2]->end = 0;
- i = pcmcia_request_window(link, link->resource[2], 0);
- if (i != 0)
- return -ENODEV;
-
- smc->base = ioremap(link->resource[2]->start,
- resource_size(link->resource[2]));
- offset = (smc->manfid == MANFID_MOTOROLA) ? link->config_base : 0;
- i = pcmcia_map_mem_page(link, link->resource[2], offset);
- if ((i == 0) &&
- (smc->manfid == MANFID_MEGAHERTZ) &&
- (smc->cardid == PRODID_MEGAHERTZ_EM3288))
- mhz_3288_power(link);
-
- return 0;
-}
-
-static int pcmcia_get_versmac(struct pcmcia_device *p_dev,
- tuple_t *tuple,
- void *priv)
-{
- struct net_device *dev = priv;
- cisparse_t parse;
- u8 *buf;
-
- if (pcmcia_parse_tuple(tuple, &parse))
- return -EINVAL;
-
- buf = parse.version_1.str + parse.version_1.ofs[3];
-
- if ((parse.version_1.ns > 3) && (cvt_ascii_address(dev, buf) == 0))
- return 0;
-
- return -EINVAL;
-};
-
-static int mhz_setup(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- size_t len;
- u8 *buf;
- int rc;
-
- /* Read the station address from the CIS. It is stored as the last
- (fourth) string in the Version 1 Version/ID tuple. */
- if ((link->prod_id[3]) &&
- (cvt_ascii_address(dev, link->prod_id[3]) == 0))
- return 0;
-
- /* Workarounds for broken cards start here. */
- /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
- if (!pcmcia_loop_tuple(link, CISTPL_VERS_1, pcmcia_get_versmac, dev))
- return 0;
-
- /* Another possibility: for the EM3288, in a special tuple */
- rc = -1;
- len = pcmcia_get_tuple(link, 0x81, &buf);
- if (buf && len >= 13) {
- buf[12] = '\0';
- if (cvt_ascii_address(dev, buf) == 0)
- rc = 0;
- }
- kfree(buf);
-
- return rc;
-};
-
-/*======================================================================
-
- Configuration stuff for the Motorola Mariner
-
- mot_config() writes directly to the Mariner configuration
- registers because the CIS is just bogus.
-
-======================================================================*/
-
-static void mot_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- unsigned int iouart = link->resource[1]->start;
-
- /* Set UART base address and force map with COR bit 1 */
- writeb(iouart & 0xff, smc->base + MOT_UART + CISREG_IOBASE_0);
- writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1);
- writeb(MOT_NORMAL, smc->base + MOT_UART + CISREG_COR);
-
- /* Set SMC base address and force map with COR bit 1 */
- writeb(ioaddr & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_0);
- writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1);
- writeb(MOT_NORMAL, smc->base + MOT_LAN + CISREG_COR);
-
- /* Wait for things to settle down */
- mdelay(100);
-}
-
-static int mot_setup(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- int i, wait, loop;
- u8 mac[ETH_ALEN];
- u_int addr;
-
- /* Read Ethernet address from Serial EEPROM */
-
- for (i = 0; i < 3; i++) {
- SMC_SELECT_BANK(2);
- outw(MOT_EEPROM + i, ioaddr + POINTER);
- SMC_SELECT_BANK(1);
- outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL);
-
- for (loop = wait = 0; loop < 200; loop++) {
- udelay(10);
- wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL));
- if (wait == 0) break;
- }
-
- if (wait)
- return -1;
-
- addr = inw(ioaddr + GENERAL);
- mac[2*i] = addr & 0xff;
- mac[2*i+1] = (addr >> 8) & 0xff;
- }
- eth_hw_addr_set(dev, mac);
-
- return 0;
-}
-
-/*====================================================================*/
-
-static int smc_configcheck(struct pcmcia_device *p_dev, void *priv_data)
-{
- p_dev->resource[0]->end = 16;
- p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
-
- return pcmcia_request_io(p_dev);
-}
-
-static int smc_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- int i;
-
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
-
- i = pcmcia_loop_config(link, smc_configcheck, NULL);
- if (!i)
- dev->base_addr = link->resource[0]->start;
-
- return i;
-}
-
-
-static int smc_setup(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- /* Check for a LAN function extension tuple */
- if (!pcmcia_get_mac_from_cis(link, dev))
- return 0;
-
- /* Try the third string in the Version 1 Version/ID tuple. */
- if (link->prod_id[2]) {
- if (cvt_ascii_address(dev, link->prod_id[2]) == 0)
- return 0;
- }
- return -1;
-}
-
-/*====================================================================*/
-
-static int osi_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- static const unsigned int com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
- int i, j;
-
- link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ;
- link->resource[0]->end = 64;
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- link->resource[1]->end = 8;
-
- /* Enable Hard Decode, LAN, Modem */
- link->io_lines = 16;
- link->config_index = 0x23;
-
- for (i = j = 0; j < 4; j++) {
- link->resource[1]->start = com[j];
- i = pcmcia_request_io(link);
- if (i == 0)
- break;
- }
- if (i != 0) {
- /* Fallback: turn off hard decode */
- link->config_index = 0x03;
- link->resource[1]->end = 0;
- i = pcmcia_request_io(link);
- }
- dev->base_addr = link->resource[0]->start + 0x10;
- return i;
-}
-
-static int osi_load_firmware(struct pcmcia_device *link)
-{
- const struct firmware *fw;
- int i, err;
-
- err = request_firmware(&fw, FIRMWARE_NAME, &link->dev);
- if (err) {
- pr_err("Failed to load firmware \"%s\"\n", FIRMWARE_NAME);
- return err;
- }
-
- /* Download the Seven of Diamonds firmware */
- for (i = 0; i < fw->size; i++) {
- outb(fw->data[i], link->resource[0]->start + 2);
- udelay(50);
- }
- release_firmware(fw);
- return err;
-}
-
-static int pcmcia_osi_mac(struct pcmcia_device *p_dev,
- tuple_t *tuple,
- void *priv)
-{
- struct net_device *dev = priv;
-
- if (tuple->TupleDataLen < 8)
- return -EINVAL;
- if (tuple->TupleData[0] != 0x04)
- return -EINVAL;
-
- eth_hw_addr_set(dev, &tuple->TupleData[2]);
- return 0;
-};
-
-
-static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid)
-{
- struct net_device *dev = link->priv;
- int rc;
-
- /* Read the station address from tuple 0x90, subtuple 0x04 */
- if (pcmcia_loop_tuple(link, 0x90, pcmcia_osi_mac, dev))
- return -1;
-
- if (((manfid == MANFID_OSITECH) &&
- (cardid == PRODID_OSITECH_SEVEN)) ||
- ((manfid == MANFID_PSION) &&
- (cardid == PRODID_PSION_NET100))) {
- rc = osi_load_firmware(link);
- if (rc)
- return rc;
- } else if (manfid == MANFID_OSITECH) {
- /* Make sure both functions are powered up */
- set_bits(0x300, link->resource[0]->start + OSITECH_AUI_PWR);
- /* Now, turn on the interrupt for both card functions */
- set_bits(0x300, link->resource[0]->start + OSITECH_RESET_ISR);
- dev_dbg(&link->dev, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n",
- inw(link->resource[0]->start + OSITECH_AUI_PWR),
- inw(link->resource[0]->start + OSITECH_RESET_ISR));
- }
- return 0;
-}
-
-static int smc91c92_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int smc91c92_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- int i;
-
- if ((smc->manfid == MANFID_MEGAHERTZ) &&
- (smc->cardid == PRODID_MEGAHERTZ_EM3288))
- mhz_3288_power(link);
- if (smc->manfid == MANFID_MOTOROLA)
- mot_config(link);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN)) {
- /* Power up the card and enable interrupts */
- set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
- set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
- }
- if (((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid == PRODID_OSITECH_SEVEN)) ||
- ((smc->manfid == MANFID_PSION) &&
- (smc->cardid == PRODID_PSION_NET100))) {
- i = osi_load_firmware(link);
- if (i) {
- netdev_err(dev, "Failed to load firmware\n");
- return i;
- }
- }
- if (link->open) {
- smc_reset(dev);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-
-/*======================================================================
-
- This verifies that the chip is some SMC91cXX variant, and returns
- the revision code if successful. Otherwise, it returns -ENODEV.
-
-======================================================================*/
-
-static int check_sig(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- int width;
- u_short s;
-
- SMC_SELECT_BANK(1);
- if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) {
- /* Try powering up the chip */
- outw(0, ioaddr + CONTROL);
- mdelay(55);
- }
-
- /* Try setting bus width */
- width = (link->resource[0]->flags == IO_DATA_PATH_WIDTH_AUTO);
- s = inb(ioaddr + CONFIG);
- if (width)
- s |= CFG_16BIT;
- else
- s &= ~CFG_16BIT;
- outb(s, ioaddr + CONFIG);
-
- /* Check Base Address Register to make sure bus width is OK */
- s = inw(ioaddr + BASE_ADDR);
- if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) &&
- ((s >> 8) != (s & 0xff))) {
- SMC_SELECT_BANK(3);
- s = inw(ioaddr + REVISION);
- return s & 0xff;
- }
-
- if (width) {
- netdev_info(dev, "using 8-bit IO window\n");
-
- smc91c92_suspend(link);
- pcmcia_fixup_iowidth(link);
- smc91c92_resume(link);
- return check_sig(link);
- }
- return -ENODEV;
-}
-
-static int smc91c92_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- char *name;
- int i, rev, j = 0;
- unsigned int ioaddr;
- u_long mir;
-
- dev_dbg(&link->dev, "smc91c92_config\n");
-
- smc->manfid = link->manf_id;
- smc->cardid = link->card_id;
-
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN)) {
- i = osi_config(link);
- } else if ((smc->manfid == MANFID_MOTOROLA) ||
- ((smc->manfid == MANFID_MEGAHERTZ) &&
- ((smc->cardid == PRODID_MEGAHERTZ_VARIOUS) ||
- (smc->cardid == PRODID_MEGAHERTZ_EM3288)))) {
- i = mhz_mfc_config(link);
- } else {
- i = smc_config(link);
- }
- if (i)
- goto config_failed;
-
- i = pcmcia_request_irq(link, smc_interrupt);
- if (i)
- goto config_failed;
- i = pcmcia_enable_device(link);
- if (i)
- goto config_failed;
-
- if (smc->manfid == MANFID_MOTOROLA)
- mot_config(link);
-
- dev->irq = link->irq;
-
- if ((if_port >= 0) && (if_port <= 2))
- dev->if_port = if_port;
- else
- dev_notice(&link->dev, "invalid if_port requested\n");
-
- switch (smc->manfid) {
- case MANFID_OSITECH:
- case MANFID_PSION:
- i = osi_setup(link, smc->manfid, smc->cardid); break;
- case MANFID_SMC:
- case MANFID_NEW_MEDIA:
- i = smc_setup(link); break;
- case 0x128: /* For broken Megahertz cards */
- case MANFID_MEGAHERTZ:
- i = mhz_setup(link); break;
- case MANFID_MOTOROLA:
- default: /* get the hw address from EEPROM */
- i = mot_setup(link); break;
- }
-
- if (i != 0) {
- dev_notice(&link->dev, "Unable to find hardware address.\n");
- goto config_failed;
- }
-
- smc->duplex = 0;
- smc->rx_ovrn = 0;
-
- rev = check_sig(link);
- name = "???";
- if (rev > 0)
- switch (rev >> 4) {
- case 3: name = "92"; break;
- case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break;
- case 5: name = "95"; break;
- case 7: name = "100"; break;
- case 8: name = "100-FD"; break;
- case 9: name = "110"; break;
- }
-
- ioaddr = dev->base_addr;
- if (rev > 0) {
- u_long mcr;
- SMC_SELECT_BANK(0);
- mir = inw(ioaddr + MEMINFO) & 0xff;
- if (mir == 0xff) mir++;
- /* Get scale factor for memory size */
- mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200;
- mir *= 128 * (1<<((mcr >> 9) & 7));
- SMC_SELECT_BANK(1);
- smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT;
- smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC;
- if (smc->manfid == MANFID_OSITECH)
- smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0;
- if ((rev >> 4) >= 7)
- smc->cfg |= CFG_MII_SELECT;
- } else
- mir = 0;
-
- if (smc->cfg & CFG_MII_SELECT) {
- SMC_SELECT_BANK(3);
-
- for (i = 0; i < 32; i++) {
- j = mdio_read(dev, i, 1);
- if ((j != 0) && (j != 0xffff)) break;
- }
- smc->mii_if.phy_id = (i < 32) ? i : -1;
-
- SMC_SELECT_BANK(0);
- }
-
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- dev_err(&link->dev, "register_netdev() failed\n");
- goto config_undo;
- }
-
- netdev_info(dev, "smc91c%s rev %d: io %#3lx, irq %d, hw_addr %pM\n",
- name, (rev & 0x0f), dev->base_addr, dev->irq, dev->dev_addr);
-
- if (rev > 0) {
- if (mir & 0x3ff)
- netdev_info(dev, " %lu byte", mir);
- else
- netdev_info(dev, " %lu kb", mir>>10);
- pr_cont(" buffer, %s xcvr\n",
- (smc->cfg & CFG_MII_SELECT) ? "MII" : if_names[dev->if_port]);
- }
-
- if (smc->cfg & CFG_MII_SELECT) {
- if (smc->mii_if.phy_id != -1) {
- netdev_dbg(dev, " MII transceiver at index %d, status %x\n",
- smc->mii_if.phy_id, j);
- } else {
- netdev_notice(dev, " No MII transceivers found!\n");
- }
- }
- return 0;
-
-config_undo:
- unregister_netdev(dev);
-config_failed:
- smc91c92_release(link);
- free_netdev(dev);
- return -ENODEV;
-} /* smc91c92_config */
-
-static void smc91c92_release(struct pcmcia_device *link)
-{
- dev_dbg(&link->dev, "smc91c92_release\n");
- if (link->resource[2]->end) {
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- iounmap(smc->base);
- }
- pcmcia_disable_device(link);
-}
-
-/*======================================================================
-
- MII interface support for SMC91cXX based cards
-======================================================================*/
-
-#define MDIO_SHIFT_CLK 0x04
-#define MDIO_DATA_OUT 0x01
-#define MDIO_DIR_WRITE 0x08
-#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)
-#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)
-#define MDIO_DATA_READ 0x02
-
-static void mdio_sync(unsigned int addr)
-{
- int bits;
- for (bits = 0; bits < 32; bits++) {
- outb(MDIO_DATA_WRITE1, addr);
- outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
- }
-}
-
-static int mdio_read(struct net_device *dev, int phy_id, int loc)
-{
- unsigned int addr = dev->base_addr + MGMT;
- u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
- int i, retval = 0;
-
- mdio_sync(addr);
- for (i = 13; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb(dat, addr);
- outb(dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 19; i > 0; i--) {
- outb(0, addr);
- retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
- outb(MDIO_SHIFT_CLK, addr);
- }
- return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(struct net_device *dev, int phy_id, int loc, int value)
-{
- unsigned int addr = dev->base_addr + MGMT;
- u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
- int i;
-
- mdio_sync(addr);
- for (i = 31; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb(dat, addr);
- outb(dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 1; i >= 0; i--) {
- outb(0, addr);
- outb(MDIO_SHIFT_CLK, addr);
- }
-}
-
-/*======================================================================
-
- The driver core code, most of which should be common with a
- non-PCMCIA implementation.
-
-======================================================================*/
-
-#ifdef PCMCIA_DEBUG
-static void smc_dump(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- u_short i, w, save;
- save = inw(ioaddr + BANK_SELECT);
- for (w = 0; w < 4; w++) {
- SMC_SELECT_BANK(w);
- netdev_dbg(dev, "bank %d: ", w);
- for (i = 0; i < 14; i += 2)
- pr_cont(" %04x", inw(ioaddr + i));
- pr_cont("\n");
- }
- outw(save, ioaddr + BANK_SELECT);
-}
-#endif
-
-static int smc_open(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct pcmcia_device *link = smc->p_dev;
-
- dev_dbg(&link->dev, "%s: smc_open(%p), ID/Window %4.4x.\n",
- dev->name, dev, inw(dev->base_addr + BANK_SELECT));
-#ifdef PCMCIA_DEBUG
- smc_dump(dev);
-#endif
-
- /* Check that the PCMCIA card is still here. */
- if (!pcmcia_dev_present(link))
- return -ENODEV;
- /* Physical device present signature. */
- if (check_sig(link) < 0) {
- netdev_info(dev, "Yikes! Bad chip signature!\n");
- return -ENODEV;
- }
- link->open++;
-
- netif_start_queue(dev);
- smc->saved_skb = NULL;
- smc->packets_waiting = 0;
-
- smc_reset(dev);
- timer_setup(&smc->media, media_check, 0);
- mod_timer(&smc->media, jiffies + HZ);
-
- return 0;
-} /* smc_open */
-
-/*====================================================================*/
-
-static int smc_close(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct pcmcia_device *link = smc->p_dev;
- unsigned int ioaddr = dev->base_addr;
-
- dev_dbg(&link->dev, "%s: smc_close(), status %4.4x.\n",
- dev->name, inw(ioaddr + BANK_SELECT));
-
- netif_stop_queue(dev);
-
- /* Shut off all interrupts, and turn off the Tx and Rx sections.
- Don't bother to check for chip present. */
- SMC_SELECT_BANK(2); /* Nominally paranoia, but do no assume... */
- outw(0, ioaddr + INTERRUPT);
- SMC_SELECT_BANK(0);
- mask_bits(0xff00, ioaddr + RCR);
- mask_bits(0xff00, ioaddr + TCR);
-
- /* Put the chip into power-down mode. */
- SMC_SELECT_BANK(1);
- outw(CTL_POWERDOWN, ioaddr + CONTROL );
-
- link->open--;
- timer_delete_sync(&smc->media);
-
- return 0;
-} /* smc_close */
-
-/*======================================================================
-
- Transfer a packet to the hardware and trigger the packet send.
- This may be called at either from either the Tx queue code
- or the interrupt handler.
-
-======================================================================*/
-
-static void smc_hardware_send_packet(struct net_device * dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct sk_buff *skb = smc->saved_skb;
- unsigned int ioaddr = dev->base_addr;
- u_char packet_no;
-
- if (!skb) {
- netdev_err(dev, "In XMIT with no packet to send\n");
- return;
- }
-
- /* There should be a packet slot waiting. */
- packet_no = inw(ioaddr + PNR_ARR) >> 8;
- if (packet_no & 0x80) {
- /* If not, there is a hardware problem! Likely an ejected card. */
- netdev_warn(dev, "hardware Tx buffer allocation failed, status %#2.2x\n",
- packet_no);
- dev_kfree_skb_irq(skb);
- smc->saved_skb = NULL;
- netif_start_queue(dev);
- return;
- }
-
- dev->stats.tx_bytes += skb->len;
- /* The card should use the just-allocated buffer. */
- outw(packet_no, ioaddr + PNR_ARR);
- /* point to the beginning of the packet */
- outw(PTR_AUTOINC , ioaddr + POINTER);
-
- /* Send the packet length (+6 for status, length and ctl byte)
- and the status word (set to zeros). */
- {
- u_char *buf = skb->data;
- u_int length = skb->len; /* The chip will pad to ethernet min. */
-
- netdev_dbg(dev, "Trying to xmit packet of length %d\n", length);
-
- /* send the packet length: +6 for status word, length, and ctl */
- outw(0, ioaddr + DATA_1);
- outw(length + 6, ioaddr + DATA_1);
- outsw(ioaddr + DATA_1, buf, length >> 1);
-
- /* The odd last byte, if there is one, goes in the control word. */
- outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1);
- }
-
- /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */
- outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) |
- (inw(ioaddr + INTERRUPT) & 0xff00),
- ioaddr + INTERRUPT);
-
- /* The chip does the rest of the work. */
- outw(MC_ENQUEUE , ioaddr + MMU_CMD);
-
- smc->saved_skb = NULL;
- dev_kfree_skb_irq(skb);
- netif_trans_update(dev);
- netif_start_queue(dev);
-}
-
-/*====================================================================*/
-
-static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
-
- netdev_notice(dev, "transmit timed out, Tx_status %2.2x status %4.4x.\n",
- inw(ioaddr)&0xff, inw(ioaddr + 2));
- dev->stats.tx_errors++;
- smc_reset(dev);
- netif_trans_update(dev); /* prevent tx timeout */
- smc->saved_skb = NULL;
- netif_wake_queue(dev);
-}
-
-static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u_short num_pages;
- short time_out, ir;
- unsigned long flags;
-
- netif_stop_queue(dev);
-
- netdev_dbg(dev, "smc_start_xmit(length = %d) called, status %04x\n",
- skb->len, inw(ioaddr + 2));
-
- if (smc->saved_skb) {
- /* THIS SHOULD NEVER HAPPEN. */
- dev->stats.tx_aborted_errors++;
- netdev_dbg(dev, "Internal error -- sent packet while busy\n");
- return NETDEV_TX_BUSY;
- }
- smc->saved_skb = skb;
-
- num_pages = skb->len >> 8;
-
- if (num_pages > 7) {
- netdev_err(dev, "Far too big packet error: %d pages\n", num_pages);
- dev_kfree_skb (skb);
- smc->saved_skb = NULL;
- dev->stats.tx_dropped++;
- return NETDEV_TX_OK; /* Do not re-queue this packet. */
- }
- /* A packet is now waiting. */
- smc->packets_waiting++;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(2); /* Paranoia, we should always be in window 2 */
-
- /* need MC_RESET to keep the memory consistent. errata? */
- if (smc->rx_ovrn) {
- outw(MC_RESET, ioaddr + MMU_CMD);
- smc->rx_ovrn = 0;
- }
-
- /* Allocate the memory; send the packet now if we win. */
- outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD);
- for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) {
- ir = inw(ioaddr+INTERRUPT);
- if (ir & IM_ALLOC_INT) {
- /* Acknowledge the interrupt, send the packet. */
- outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT);
- smc_hardware_send_packet(dev); /* Send the packet now.. */
- spin_unlock_irqrestore(&smc->lock, flags);
- return NETDEV_TX_OK;
- }
- }
-
- /* Otherwise defer until the Tx-space-allocated interrupt. */
- netdev_dbg(dev, "memory allocation deferred.\n");
- outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
- spin_unlock_irqrestore(&smc->lock, flags);
-
- return NETDEV_TX_OK;
-}
-
-/*======================================================================
-
- Handle a Tx anomalous event. Entered while in Window 2.
-
-======================================================================*/
-
-static void smc_tx_err(struct net_device * dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- int saved_packet = inw(ioaddr + PNR_ARR) & 0xff;
- int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f;
- int tx_status;
-
- /* select this as the packet to read from */
- outw(packet_no, ioaddr + PNR_ARR);
-
- /* read the first word from this packet */
- outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER);
-
- tx_status = inw(ioaddr + DATA_1);
-
- dev->stats.tx_errors++;
- if (tx_status & TS_LOSTCAR) dev->stats.tx_carrier_errors++;
- if (tx_status & TS_LATCOL) dev->stats.tx_window_errors++;
- if (tx_status & TS_16COL) {
- dev->stats.tx_aborted_errors++;
- smc->tx_err++;
- }
-
- if (tx_status & TS_SUCCESS) {
- netdev_notice(dev, "Successful packet caused error interrupt?\n");
- }
- /* re-enable transmit */
- SMC_SELECT_BANK(0);
- outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
- SMC_SELECT_BANK(2);
-
- outw(MC_FREEPKT, ioaddr + MMU_CMD); /* Free the packet memory. */
-
- /* one less packet waiting for me */
- smc->packets_waiting--;
-
- outw(saved_packet, ioaddr + PNR_ARR);
-}
-
-/*====================================================================*/
-
-static void smc_eph_irq(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u_short card_stats, ephs;
-
- SMC_SELECT_BANK(0);
- ephs = inw(ioaddr + EPH);
- netdev_dbg(dev, "Ethernet protocol handler interrupt, status %4.4x.\n",
- ephs);
- /* Could be a counter roll-over warning: update stats. */
- card_stats = inw(ioaddr + COUNTER);
- /* single collisions */
- dev->stats.collisions += card_stats & 0xF;
- card_stats >>= 4;
- /* multiple collisions */
- dev->stats.collisions += card_stats & 0xF;
-#if 0 /* These are for when linux supports these statistics */
- card_stats >>= 4; /* deferred */
- card_stats >>= 4; /* excess deferred */
-#endif
- /* If we had a transmit error we must re-enable the transmitter. */
- outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
-
- /* Clear a link error interrupt. */
- SMC_SELECT_BANK(1);
- outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL);
- outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
- ioaddr + CONTROL);
- SMC_SELECT_BANK(2);
-}
-
-/*====================================================================*/
-
-static irqreturn_t smc_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr;
- u_short saved_bank, saved_pointer, mask, status;
- unsigned int handled = 1;
- char bogus_cnt = INTR_WORK; /* Work we are willing to do. */
-
- if (!netif_device_present(dev))
- return IRQ_NONE;
-
- ioaddr = dev->base_addr;
-
- netdev_dbg(dev, "SMC91c92 interrupt %d at %#x.\n",
- irq, ioaddr);
-
- spin_lock(&smc->lock);
- smc->watchdog = 0;
- saved_bank = inw(ioaddr + BANK_SELECT);
- if ((saved_bank & 0xff00) != 0x3300) {
- /* The device does not exist -- the card could be off-line, or
- maybe it has been ejected. */
- netdev_dbg(dev, "SMC91c92 interrupt %d for non-existent/ejected device.\n",
- irq);
- handled = 0;
- goto irq_done;
- }
-
- SMC_SELECT_BANK(2);
- saved_pointer = inw(ioaddr + POINTER);
- mask = inw(ioaddr + INTERRUPT) >> 8;
- /* clear all interrupts */
- outw(0, ioaddr + INTERRUPT);
-
- do { /* read the status flag, and mask it */
- status = inw(ioaddr + INTERRUPT) & 0xff;
- netdev_dbg(dev, "Status is %#2.2x (mask %#2.2x).\n",
- status, mask);
- if ((status & mask) == 0) {
- if (bogus_cnt == INTR_WORK)
- handled = 0;
- break;
- }
- if (status & IM_RCV_INT) {
- /* Got a packet(s). */
- smc_rx(dev);
- }
- if (status & IM_TX_INT) {
- smc_tx_err(dev);
- outw(IM_TX_INT, ioaddr + INTERRUPT);
- }
- status &= mask;
- if (status & IM_TX_EMPTY_INT) {
- outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT);
- mask &= ~IM_TX_EMPTY_INT;
- dev->stats.tx_packets += smc->packets_waiting;
- smc->packets_waiting = 0;
- }
- if (status & IM_ALLOC_INT) {
- /* Clear this interrupt so it doesn't happen again */
- mask &= ~IM_ALLOC_INT;
-
- smc_hardware_send_packet(dev);
-
- /* enable xmit interrupts based on this */
- mask |= (IM_TX_EMPTY_INT | IM_TX_INT);
-
- /* and let the card send more packets to me */
- netif_wake_queue(dev);
- }
- if (status & IM_RX_OVRN_INT) {
- dev->stats.rx_errors++;
- dev->stats.rx_fifo_errors++;
- if (smc->duplex)
- smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */
- outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT);
- }
- if (status & IM_EPH_INT)
- smc_eph_irq(dev);
- } while (--bogus_cnt);
-
- netdev_dbg(dev, " Restoring saved registers mask %2.2x bank %4.4x pointer %4.4x.\n",
- mask, saved_bank, saved_pointer);
-
- /* restore state register */
- outw((mask<<8), ioaddr + INTERRUPT);
- outw(saved_pointer, ioaddr + POINTER);
- SMC_SELECT_BANK(saved_bank);
-
- netdev_dbg(dev, "Exiting interrupt IRQ%d.\n", irq);
-
-irq_done:
-
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN)) {
- /* Retrigger interrupt if needed */
- mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR);
- set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR);
- }
- if (smc->manfid == MANFID_MOTOROLA) {
- u_char cor;
- cor = readb(smc->base + MOT_UART + CISREG_COR);
- writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR);
- writeb(cor, smc->base + MOT_UART + CISREG_COR);
- cor = readb(smc->base + MOT_LAN + CISREG_COR);
- writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR);
- writeb(cor, smc->base + MOT_LAN + CISREG_COR);
- }
-
- if ((smc->base != NULL) && /* Megahertz MFC's */
- (smc->manfid == MANFID_MEGAHERTZ) &&
- (smc->cardid == PRODID_MEGAHERTZ_EM3288)) {
-
- u_char tmp;
- tmp = readb(smc->base+MEGAHERTZ_ISR);
- tmp = readb(smc->base+MEGAHERTZ_ISR);
-
- /* Retrigger interrupt if needed */
- writeb(tmp, smc->base + MEGAHERTZ_ISR);
- writeb(tmp, smc->base + MEGAHERTZ_ISR);
- }
-
- spin_unlock(&smc->lock);
- return IRQ_RETVAL(handled);
-}
-
-/*====================================================================*/
-
-static void smc_rx(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- int rx_status;
- int packet_length; /* Caution: not frame length, rather words
- to transfer from the chip. */
-
- /* Assertion: we are in Window 2. */
-
- if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) {
- netdev_err(dev, "smc_rx() with nothing on Rx FIFO\n");
- return;
- }
-
- /* Reset the read pointer, and read the status and packet length. */
- outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER);
- rx_status = inw(ioaddr + DATA_1);
- packet_length = inw(ioaddr + DATA_1) & 0x07ff;
-
- netdev_dbg(dev, "Receive status %4.4x length %d.\n",
- rx_status, packet_length);
-
- if (!(rx_status & RS_ERRORS)) {
- /* do stuff to make a new packet */
- struct sk_buff *skb;
- struct smc_private *smc = netdev_priv(dev);
-
- /* Note: packet_length adds 5 or 6 extra bytes here! */
- skb = netdev_alloc_skb(dev, packet_length+2);
-
- if (skb == NULL) {
- netdev_dbg(dev, "Low memory, packet dropped.\n");
- dev->stats.rx_dropped++;
- outw(MC_RELEASE, ioaddr + MMU_CMD);
- return;
- }
-
- packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6);
- skb_reserve(skb, 2);
- insw(ioaddr+DATA_1, skb_put(skb, packet_length),
- (packet_length+1)>>1);
- skb->protocol = eth_type_trans(skb, dev);
-
- netif_rx(skb);
- smc->last_rx = jiffies;
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += packet_length;
- if (rx_status & RS_MULTICAST)
- dev->stats.multicast++;
- } else {
- /* error ... */
- dev->stats.rx_errors++;
-
- if (rx_status & RS_ALGNERR) dev->stats.rx_frame_errors++;
- if (rx_status & (RS_TOOSHORT | RS_TOOLONG))
- dev->stats.rx_length_errors++;
- if (rx_status & RS_BADCRC) dev->stats.rx_crc_errors++;
- }
- /* Let the MMU free the memory of this packet. */
- outw(MC_RELEASE, ioaddr + MMU_CMD);
-}
-
-/*======================================================================
-
- Set the receive mode.
-
- This routine is used by both the protocol level to notify us of
- promiscuous/multicast mode changes, and by the open/reset code to
- initialize the Rx registers. We always set the multicast list and
- leave the receiver running.
-
-======================================================================*/
-
-static void set_rx_mode(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct smc_private *smc = netdev_priv(dev);
- unsigned char multicast_table[8];
- unsigned long flags;
- u_short rx_cfg_setting;
- int i;
-
- memset(multicast_table, 0, sizeof(multicast_table));
-
- if (dev->flags & IFF_PROMISC) {
- rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti;
- } else if (dev->flags & IFF_ALLMULTI)
- rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti;
- else {
- if (!netdev_mc_empty(dev)) {
- struct netdev_hw_addr *ha;
-
- netdev_for_each_mc_addr(ha, dev) {
- u_int position = ether_crc(6, ha->addr);
- multicast_table[position >> 29] |= 1 << ((position >> 26) & 7);
- }
- }
- rx_cfg_setting = RxStripCRC | RxEnable;
- }
-
- /* Load MC table and Rx setting into the chip without interrupts. */
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- for (i = 0; i < 8; i++)
- outb(multicast_table[i], ioaddr + MULTICAST0 + i);
- SMC_SELECT_BANK(0);
- outw(rx_cfg_setting, ioaddr + RCR);
- SMC_SELECT_BANK(2);
- spin_unlock_irqrestore(&smc->lock, flags);
-}
-
-/*======================================================================
-
- Senses when a card's config changes. Here, it's coax or TP.
-
-======================================================================*/
-
-static int s9k_config(struct net_device *dev, struct ifmap *map)
-{
- struct smc_private *smc = netdev_priv(dev);
- if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
- if (smc->cfg & CFG_MII_SELECT)
- return -EOPNOTSUPP;
- else if (map->port > 2)
- return -EINVAL;
- WRITE_ONCE(dev->if_port, map->port);
- netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
- smc_reset(dev);
- }
- return 0;
-}
-
-/*======================================================================
-
- Reset the chip, reloading every register that might be corrupted.
-
-======================================================================*/
-
-/*
- Set transceiver type, perhaps to something other than what the user
- specified in dev->if_port.
-*/
-static void smc_set_xcvr(struct net_device *dev, int if_port)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u_short saved_bank;
-
- saved_bank = inw(ioaddr + BANK_SELECT);
- SMC_SELECT_BANK(1);
- if (if_port == 2) {
- outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN))
- set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
- smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002);
- } else {
- outw(smc->cfg, ioaddr + CONFIG);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN))
- mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
- smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001);
- }
- SMC_SELECT_BANK(saved_bank);
-}
-
-static void smc_reset(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct smc_private *smc = netdev_priv(dev);
- int i;
-
- netdev_dbg(dev, "smc91c92 reset called.\n");
-
- /* The first interaction must be a write to bring the chip out
- of sleep mode. */
- SMC_SELECT_BANK(0);
- /* Reset the chip. */
- outw(RCR_SOFTRESET, ioaddr + RCR);
- udelay(10);
-
- /* Clear the transmit and receive configuration registers. */
- outw(RCR_CLEAR, ioaddr + RCR);
- outw(TCR_CLEAR, ioaddr + TCR);
-
- /* Set the Window 1 control, configuration and station addr registers.
- No point in writing the I/O base register ;-> */
- SMC_SELECT_BANK(1);
- /* Automatically release successfully transmitted packets,
- Accept link errors, counter and Tx error interrupts. */
- outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
- ioaddr + CONTROL);
- smc_set_xcvr(dev, dev->if_port);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN))
- outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) |
- (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00),
- ioaddr - 0x10 + OSITECH_AUI_PWR);
-
- /* Fill in the physical address. The databook is wrong about the order! */
- for (i = 0; i < 6; i += 2)
- outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i],
- ioaddr + ADDR0 + i);
-
- /* Reset the MMU */
- SMC_SELECT_BANK(2);
- outw(MC_RESET, ioaddr + MMU_CMD);
- outw(0, ioaddr + INTERRUPT);
-
- /* Re-enable the chip. */
- SMC_SELECT_BANK(0);
- outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) |
- TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR);
- set_rx_mode(dev);
-
- if (smc->cfg & CFG_MII_SELECT) {
- SMC_SELECT_BANK(3);
-
- /* Reset MII */
- mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000);
-
- /* Advertise 100F, 100H, 10F, 10H */
- mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1);
-
- /* Restart MII autonegotiation */
- mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000);
- mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200);
- }
-
- /* Enable interrupts. */
- SMC_SELECT_BANK(2);
- outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,
- ioaddr + INTERRUPT);
-}
-
-/*======================================================================
-
- Media selection timer routine
-
-======================================================================*/
-
-static void media_check(struct timer_list *t)
-{
- struct smc_private *smc = timer_container_of(smc, t, media);
- struct net_device *dev = smc->mii_if.dev;
- unsigned int ioaddr = dev->base_addr;
- u_short i, media, saved_bank;
- u_short link;
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
-
- saved_bank = inw(ioaddr + BANK_SELECT);
-
- if (!netif_device_present(dev))
- goto reschedule;
-
- SMC_SELECT_BANK(2);
-
- /* need MC_RESET to keep the memory consistent. errata? */
- if (smc->rx_ovrn) {
- outw(MC_RESET, ioaddr + MMU_CMD);
- smc->rx_ovrn = 0;
- }
- i = inw(ioaddr + INTERRUPT);
- SMC_SELECT_BANK(0);
- media = inw(ioaddr + EPH) & EPH_LINK_OK;
- SMC_SELECT_BANK(1);
- media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;
-
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
-
- /* Check for pending interrupt with watchdog flag set: with
- this, we can limp along even if the interrupt is blocked */
- if (smc->watchdog++ && ((i>>8) & i)) {
- if (!smc->fast_poll)
- netdev_info(dev, "interrupt(s) dropped!\n");
- local_irq_save(flags);
- smc_interrupt(dev->irq, dev);
- local_irq_restore(flags);
- smc->fast_poll = HZ;
- }
- if (smc->fast_poll) {
- smc->fast_poll--;
- smc->media.expires = jiffies + HZ/100;
- add_timer(&smc->media);
- return;
- }
-
- spin_lock_irqsave(&smc->lock, flags);
-
- saved_bank = inw(ioaddr + BANK_SELECT);
-
- if (smc->cfg & CFG_MII_SELECT) {
- if (smc->mii_if.phy_id < 0)
- goto reschedule;
-
- SMC_SELECT_BANK(3);
- link = mdio_read(dev, smc->mii_if.phy_id, 1);
- if (!link || (link == 0xffff)) {
- netdev_info(dev, "MII is missing!\n");
- smc->mii_if.phy_id = -1;
- goto reschedule;
- }
-
- link &= 0x0004;
- if (link != smc->link_status) {
- u_short p = mdio_read(dev, smc->mii_if.phy_id, 5);
- netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
- smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40))
- ? TCR_FDUPLX : 0);
- if (link) {
- netdev_info(dev, "autonegotiation complete: "
- "%dbaseT-%cD selected\n",
- (p & 0x0180) ? 100 : 10, smc->duplex ? 'F' : 'H');
- }
- SMC_SELECT_BANK(0);
- outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR);
- smc->link_status = link;
- }
- goto reschedule;
- }
-
- /* Ignore collisions unless we've had no rx's recently */
- if (time_after(jiffies, smc->last_rx + HZ)) {
- if (smc->tx_err || (smc->media_status & EPH_16COL))
- media |= EPH_16COL;
- }
- smc->tx_err = 0;
-
- if (media != smc->media_status) {
- if ((media & smc->media_status & 1) &&
- ((smc->media_status ^ media) & EPH_LINK_OK))
- netdev_info(dev, "%s link beat\n",
- smc->media_status & EPH_LINK_OK ? "lost" : "found");
- else if ((media & smc->media_status & 2) &&
- ((smc->media_status ^ media) & EPH_16COL))
- netdev_info(dev, "coax cable %s\n",
- media & EPH_16COL ? "problem" : "ok");
- if (dev->if_port == 0) {
- if (media & 1) {
- if (media & EPH_LINK_OK)
- netdev_info(dev, "flipped to 10baseT\n");
- else
- smc_set_xcvr(dev, 2);
- } else {
- if (media & EPH_16COL)
- smc_set_xcvr(dev, 1);
- else
- netdev_info(dev, "flipped to 10base2\n");
- }
- }
- smc->media_status = media;
- }
-
-reschedule:
- smc->media.expires = jiffies + HZ;
- add_timer(&smc->media);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
-}
-
-static int smc_link_ok(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct smc_private *smc = netdev_priv(dev);
-
- if (smc->cfg & CFG_MII_SELECT) {
- return mii_link_ok(&smc->mii_if);
- } else {
- SMC_SELECT_BANK(0);
- return inw(ioaddr + EPH) & EPH_LINK_OK;
- }
-}
-
-static void smc_netdev_get_ecmd(struct net_device *dev,
- struct ethtool_link_ksettings *ecmd)
-{
- u16 tmp;
- unsigned int ioaddr = dev->base_addr;
- u32 supported;
-
- supported = (SUPPORTED_TP | SUPPORTED_AUI |
- SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full);
-
- SMC_SELECT_BANK(1);
- tmp = inw(ioaddr + CONFIG);
- ecmd->base.port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP;
- ecmd->base.speed = SPEED_10;
- ecmd->base.phy_address = ioaddr + MGMT;
-
- SMC_SELECT_BANK(0);
- tmp = inw(ioaddr + TCR);
- ecmd->base.duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF;
-
- ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported,
- supported);
-}
-
-static int smc_netdev_set_ecmd(struct net_device *dev,
- const struct ethtool_link_ksettings *ecmd)
-{
- u16 tmp;
- unsigned int ioaddr = dev->base_addr;
-
- if (ecmd->base.speed != SPEED_10)
- return -EINVAL;
- if (ecmd->base.duplex != DUPLEX_HALF &&
- ecmd->base.duplex != DUPLEX_FULL)
- return -EINVAL;
- if (ecmd->base.port != PORT_TP && ecmd->base.port != PORT_AUI)
- return -EINVAL;
-
- if (ecmd->base.port == PORT_AUI)
- smc_set_xcvr(dev, 1);
- else
- smc_set_xcvr(dev, 0);
-
- SMC_SELECT_BANK(0);
- tmp = inw(ioaddr + TCR);
- if (ecmd->base.duplex == DUPLEX_FULL)
- tmp |= TCR_FDUPLX;
- else
- tmp &= ~TCR_FDUPLX;
- outw(tmp, ioaddr + TCR);
-
- return 0;
-}
-
-static int check_if_running(struct net_device *dev)
-{
- if (!netif_running(dev))
- return -EINVAL;
- return 0;
-}
-
-static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
- strscpy(info->driver, DRV_NAME, sizeof(info->driver));
- strscpy(info->version, DRV_VERSION, sizeof(info->version));
-}
-
-static int smc_get_link_ksettings(struct net_device *dev,
- struct ethtool_link_ksettings *ecmd)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- if (smc->cfg & CFG_MII_SELECT)
- mii_ethtool_get_link_ksettings(&smc->mii_if, ecmd);
- else
- smc_netdev_get_ecmd(dev, ecmd);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return 0;
-}
-
-static int smc_set_link_ksettings(struct net_device *dev,
- const struct ethtool_link_ksettings *ecmd)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- int ret;
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- if (smc->cfg & CFG_MII_SELECT)
- ret = mii_ethtool_set_link_ksettings(&smc->mii_if, ecmd);
- else
- ret = smc_netdev_set_ecmd(dev, ecmd);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return ret;
-}
-
-static u32 smc_get_link(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- u32 ret;
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- ret = smc_link_ok(dev);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return ret;
-}
-
-static int smc_nway_reset(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- if (smc->cfg & CFG_MII_SELECT) {
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- int res;
-
- SMC_SELECT_BANK(3);
- res = mii_nway_restart(&smc->mii_if);
- SMC_SELECT_BANK(saved_bank);
-
- return res;
- } else
- return -EOPNOTSUPP;
-}
-
-static const struct ethtool_ops ethtool_ops = {
- .begin = check_if_running,
- .get_drvinfo = smc_get_drvinfo,
- .get_link = smc_get_link,
- .nway_reset = smc_nway_reset,
- .get_link_ksettings = smc_get_link_ksettings,
- .set_link_ksettings = smc_set_link_ksettings,
-};
-
-static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct mii_ioctl_data *mii = if_mii(rq);
- int rc = 0;
- u16 saved_bank;
- unsigned int ioaddr = dev->base_addr;
- unsigned long flags;
-
- if (!netif_running(dev))
- return -EINVAL;
-
- spin_lock_irqsave(&smc->lock, flags);
- saved_bank = inw(ioaddr + BANK_SELECT);
- SMC_SELECT_BANK(3);
- rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return rc;
-}
-
-static const struct pcmcia_device_id smc91c92_ids[] = {
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed),
- PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x016c, 0x0020),
- PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0023),
- PCMCIA_DEVICE_PROD_ID123("BASICS by New Media Corporation", "Ethernet", "SMC91C94", 0x23c78a9d, 0x00b2e941, 0xcef397fb),
- PCMCIA_DEVICE_PROD_ID12("ARGOSY", "Fast Ethernet PCCard", 0x78f308dc, 0xdcea68bc),
- PCMCIA_DEVICE_PROD_ID12("dit Co., Ltd.", "PC Card-10/100BTX", 0xe59365c8, 0x6a2161d1),
- PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L100C", 0x6a26d1cf, 0xc16ce9c5),
- PCMCIA_DEVICE_PROD_ID12("Farallon", "Farallon Enet", 0x58d93fc4, 0x244734e9),
- PCMCIA_DEVICE_PROD_ID12("Megahertz", "CC10BT/2", 0x33234748, 0x3c95b953),
- PCMCIA_DEVICE_PROD_ID12("MELCO/SMC", "LPC-TX", 0xa2cd8e6d, 0x42da662a),
- PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Four of Diamonds Ethernet", 0xc2f80cd, 0xb3466314),
- PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Seven of Diamonds Ethernet", 0xc2f80cd, 0x194b650a),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast Ethernet PCCard", 0x281f1c5d, 0xdcea68bc),
- PCMCIA_DEVICE_PROD_ID12("Psion", "10Mb Ethernet", 0x4ef00b21, 0x844be9e9),
- PCMCIA_DEVICE_PROD_ID12("SMC", "EtherEZ Ethernet 8020", 0xc4f8b18b, 0x4a0eeb2d),
- /* These conflict with other cards! */
- /* PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0100), */
- /* PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), */
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, smc91c92_ids);
-
-static struct pcmcia_driver smc91c92_cs_driver = {
- .owner = THIS_MODULE,
- .name = "smc91c92_cs",
- .probe = smc91c92_probe,
- .remove = smc91c92_detach,
- .id_table = smc91c92_ids,
- .suspend = smc91c92_suspend,
- .resume = smc91c92_resume,
-};
-module_pcmcia_driver(smc91c92_cs_driver);
--
2.53.0
^ permalink raw reply related
* [PATCH] dt-bindings: Fix phandle-array constraints, again
From: Rob Herring (Arm) @ 2026-04-21 19:55 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Krzysztof Kozlowski,
Conor Dooley, Ulf Hansson, Stephan Gerhold, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Johannes Berg, Jeff Johnson, Bjorn Helgaas, Lorenzo Pieralisi,
Krzysztof Wilczyński, Manivannan Sadhasivam, Bjorn Andersson,
Mathieu Poirier, Sylwester Nawrocki, Mark Brown, Maxime Coquelin,
Greg Kroah-Hartman, Yang Xiwen, Alex Elder, Chaitanya Chundru,
Sibi Sankar, Rao Mandadapu, Patrice Chotard, Xu Yang, Peng Fan,
Thomas Zimmermann
Cc: devicetree, linux-kernel, linux-mmc, linux-arm-msm, netdev,
linux-wireless, ath10k, ath11k, linux-pci, linux-remoteproc,
linux-sound, linux-spi, linux-usb
The unfortunately named 'phandle-array' property type is really a matrix
with phandle and fixed arg cells entries. A matrix property should have 2
levels of items constraints.
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
---
Can someone from QCom provide some descriptions for 'qcom,smem-states'
properties.
---
.../display/rockchip/rockchip,rk3399-cdn-dp.yaml | 2 ++
.../bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml | 7 ++++---
Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml | 6 ++++++
Documentation/devicetree/bindings/net/qcom,ipa.yaml | 6 ++++++
.../devicetree/bindings/net/wireless/qcom,ath10k.yaml | 5 ++++-
.../devicetree/bindings/net/wireless/qcom,ath11k.yaml | 5 ++++-
.../bindings/net/wireless/qcom,ipq5332-wifi.yaml | 9 +++++++++
.../devicetree/bindings/pci/toshiba,tc9563.yaml | 5 +++--
.../bindings/remoteproc/qcom,msm8916-mss-pil.yaml | 3 +++
.../bindings/remoteproc/qcom,msm8996-mss-pil.yaml | 3 +++
.../devicetree/bindings/remoteproc/qcom,pas-common.yaml | 4 ++++
.../bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml | 4 ++++
.../bindings/remoteproc/qcom,sc7180-mss-pil.yaml | 3 +++
.../bindings/remoteproc/qcom,sc7280-adsp-pil.yaml | 3 +++
.../bindings/remoteproc/qcom,sc7280-mss-pil.yaml | 3 +++
.../bindings/remoteproc/qcom,sc7280-wpss-pil.yaml | 3 +++
.../bindings/remoteproc/qcom,sdm845-adsp-pil.yaml | 3 +++
.../devicetree/bindings/remoteproc/qcom,wcnss-pil.yaml | 3 +++
Documentation/devicetree/bindings/sound/samsung,tm2.yaml | 2 ++
.../devicetree/bindings/spi/st,stm32mp25-ospi.yaml | 5 +++--
.../devicetree/bindings/usb/chipidea,usb2-common.yaml | 2 ++
Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml | 7 ++++---
22 files changed, 81 insertions(+), 12 deletions(-)
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3399-cdn-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3399-cdn-dp.yaml
index 1a33128e77f5..195f665970bf 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3399-cdn-dp.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3399-cdn-dp.yaml
@@ -41,7 +41,9 @@ properties:
minItems: 1
items:
- description: Extcon device providing the cable state for DP PHY device 0
+ maxItems: 1
- description: Extcon device providing the cable state for DP PHY device 1
+ maxItems: 1
description:
List of phandle to the extcon device providing the cable state for the DP PHY.
diff --git a/Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml
index 41c9b22523e7..e447579e0f22 100644
--- a/Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml
+++ b/Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml
@@ -39,10 +39,11 @@ properties:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: |
DWMMC core on Hi3798MV2x SoCs has a delay-locked-loop(DLL) attached to card data input path.
- It is integrated into CRG core on the SoC and has to be controlled during tuning.
+ It is integrated into CRG core on the SoC and has to be controlled during tuning
items:
- - description: A phandle pointed to the CRG syscon node
- - description: Sample DLL register offset in CRG address space
+ - items:
+ - description: A phandle pointed to the CRG syscon node
+ - description: Sample DLL register offset in CRG address space
required:
- compatible
diff --git a/Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml b/Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml
index b30544410d09..e47e1e09300a 100644
--- a/Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml
+++ b/Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml
@@ -42,7 +42,13 @@ properties:
description: State bits used by the AP to signal the modem.
items:
- description: Power control
+ items:
+ - description: Phandle to ???
+ - description: ???
- description: Power control acknowledgment
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: Names for the state bits used by the AP to signal the modem.
diff --git a/Documentation/devicetree/bindings/net/qcom,ipa.yaml b/Documentation/devicetree/bindings/net/qcom,ipa.yaml
index c7f5f2ef7452..c53f63068b77 100644
--- a/Documentation/devicetree/bindings/net/qcom,ipa.yaml
+++ b/Documentation/devicetree/bindings/net/qcom,ipa.yaml
@@ -123,7 +123,13 @@ properties:
description: State bits used in by the AP to signal the modem.
items:
- description: Whether the "ipa-clock-enabled" state bit is valid
+ items:
+ - description: Phandle to ???
+ - description: ???
- description: Whether the IPA clock is enabled (if valid)
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output
diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
index f2440d39b7eb..5c580bc7df08 100644
--- a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
@@ -158,7 +158,10 @@ properties:
description: State bits used by the AP to signal the WLAN Q6.
items:
- description: Signal bits used to enable/disable low power mode
- on WCN in the case of WoW (Wake on Wireless).
+ on WCN in the case of WoW (Wake on Wireless).
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output.
diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml
index 0cc1dbf2beef..326c1a94a1a0 100644
--- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml
@@ -80,7 +80,10 @@ properties:
description: State bits used by the AP to signal the WLAN Q6.
items:
- description: Signal bits used to enable/disable low power mode
- on WCN6750 in the case of WoW (Wake on Wireless).
+ on WCN6750 in the case of WoW (Wake on Wireless).
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output.
diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ipq5332-wifi.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ipq5332-wifi.yaml
index 363a0ecb6ad9..ea8df6890478 100644
--- a/Documentation/devicetree/bindings/net/wireless/qcom,ipq5332-wifi.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/qcom,ipq5332-wifi.yaml
@@ -167,8 +167,17 @@ properties:
description: States used by the AP to signal the remote processor
items:
- description: Shutdown WCSS pd
+ items:
+ - description: Phandle to ???
+ - description: ???
- description: Stop WCSS pd
+ items:
+ - description: Phandle to ???
+ - description: ???
- description: Spawn WCSS pd
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description:
diff --git a/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml b/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml
index fae466064780..b3ad05d90201 100644
--- a/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml
+++ b/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml
@@ -49,8 +49,9 @@ properties:
A phandle to the parent I2C node and the slave address of the device
used to configure tc9563 to change FTS, tx amplitude etc.
items:
- - description: Phandle to the I2C controller node
- - description: I2C slave address
+ - items:
+ - description: Phandle to the I2C controller node
+ - description: I2C slave address
patternProperties:
"^pcie@[1-3],0$":
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,msm8916-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,msm8916-mss-pil.yaml
index c179b560572b..3c614cb7ce88 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,msm8916-mss-pil.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,msm8916-mss-pil.yaml
@@ -104,6 +104,9 @@ properties:
description: States used by the AP to signal the Hexagon core
items:
- description: Stop modem
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: Names of the states used by the AP to signal the Hexagon core
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml
index 4d2055f283ac..d459296df0c2 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml
@@ -101,6 +101,9 @@ properties:
description: States used by the AP to signal the Hexagon core
items:
- description: Stop modem
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: Names of the states used by the AP to signal the Hexagon core
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml
index 68c17bf18987..6260f77b7e4b 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml
@@ -60,6 +60,10 @@ properties:
description: States used by the AP to signal the Hexagon core
items:
- description: Stop the modem
+ items:
+ - description: Phandle to ???
+ - description: ???
+
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml
index bca59394aef4..3e410cbd45cf 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml
@@ -92,6 +92,10 @@ properties:
description: States used by the AP to signal the Hexagon core
items:
- description: Stop the modem
+ items:
+ - description: Phandle to ???
+ - description: ???
+
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-mss-pil.yaml
index b1402bef0ebe..f0bee69baf0d 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-mss-pil.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-mss-pil.yaml
@@ -134,6 +134,9 @@ properties:
description: States used by the AP to signal the Hexagon core
items:
- description: Stop the modem
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-adsp-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-adsp-pil.yaml
index 94ca7a0cc203..3ea83207ae32 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-adsp-pil.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-adsp-pil.yaml
@@ -91,6 +91,9 @@ properties:
description: States used by the AP to signal the Hexagon core
items:
- description: Stop the modem
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-mss-pil.yaml
index 005cb21732af..1f1d73610510 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-mss-pil.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-mss-pil.yaml
@@ -148,6 +148,9 @@ properties:
description: States used by the AP to signal the Hexagon core
items:
- description: Stop the modem
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml
index f4118b2da5f6..0111384d55d5 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml
@@ -104,6 +104,9 @@ properties:
description: States used by the AP to signal the Hexagon core
items:
- description: Stop the modem
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml
index a3c74871457f..b9325967b8a3 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml
@@ -92,6 +92,9 @@ properties:
description: States used by the AP to signal the Hexagon core
items:
- description: Stop the modem
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.yaml
index 117fb4d0c4ad..e009f8ed9e8c 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.yaml
@@ -84,6 +84,9 @@ properties:
States used by the AP to signal the WCNSS core that it should shutdown
items:
- description: Stop the modem
+ items:
+ - description: Phandle to ???
+ - description: ???
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output
diff --git a/Documentation/devicetree/bindings/sound/samsung,tm2.yaml b/Documentation/devicetree/bindings/sound/samsung,tm2.yaml
index 67586ba3e0a0..c752e4874e7f 100644
--- a/Documentation/devicetree/bindings/sound/samsung,tm2.yaml
+++ b/Documentation/devicetree/bindings/sound/samsung,tm2.yaml
@@ -46,7 +46,9 @@ properties:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- description: Phandle to I2S0.
+ maxItems: 1
- description: Phandle to I2S1.
+ maxItems: 1
mic-bias-gpios:
description: GPIO pin that enables the Main Mic bias regulator.
diff --git a/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
index 272bc308726b..b6be47f67fcb 100644
--- a/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
+++ b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
@@ -49,8 +49,9 @@ properties:
description: configure OCTOSPI delay block.
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- - description: phandle to syscfg
- - description: register offset within syscfg
+ - items:
+ - description: phandle to syscfg
+ - description: register offset within syscfg
access-controllers:
description: phandle to the rifsc device to check access right
diff --git a/Documentation/devicetree/bindings/usb/chipidea,usb2-common.yaml b/Documentation/devicetree/bindings/usb/chipidea,usb2-common.yaml
index 10020af15afc..e6a5e79df348 100644
--- a/Documentation/devicetree/bindings/usb/chipidea,usb2-common.yaml
+++ b/Documentation/devicetree/bindings/usb/chipidea,usb2-common.yaml
@@ -97,7 +97,9 @@ properties:
minItems: 1
items:
- description: vbus extcon
+ maxItems: 1
- description: id extcon
+ maxItems: 1
phy-clkgate-delay-us:
description:
diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml
index 691d6cf02c27..fec04702f530 100644
--- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml
+++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml
@@ -61,9 +61,10 @@ properties:
offset, and phy index
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- - description: phandle to TCSR node
- - description: register offset
- - description: phy index
+ - items:
+ - description: phandle to TCSR node
+ - description: register offset
+ - description: phy index
nvidia,phy:
description: phandle of usb phy that connects to the port. Use "phys" instead.
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox