From 1f662195dbc07a66241cb5fe483036e5d07fb642 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 30 Jan 2026 14:59:19 +0900 Subject: fs: add generic FS_IOC_SHUTDOWN definitions Currently, several filesystems (e.g., xfs, ext4, btrfs) implement a "shutdown" or "going down" ioctl to simulate filesystem force a shutdown. While they often use the same underlying numeric value, the definition is duplicated across filesystem headers or private definitions. Add generic definitions for FS_IOC_SHUTDOWN in uapi/linux/fs.h. This allows new filesystems (like ntfs) to implement this feature using a standard VFS definition and paves the way for existing filesystems to unify their definitions later. The flag names are standardized as FS_SHUTDOWN_* to be consistent with the ioctl name, replacing the historical GOING_DOWN naming convention. Reviewed-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Reviewed-by: Jan Kara Signed-off-by: Namjae Jeon --- include/uapi/linux/fs.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 70b2b661f42c..13f71202845e 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -657,4 +657,16 @@ struct procmap_query { __u64 build_id_addr; /* in */ }; +/* + * Shutdown the filesystem. + */ +#define FS_IOC_SHUTDOWN _IOR('X', 125, __u32) + +/* + * Flags for FS_IOC_SHUTDOWN + */ +#define FS_SHUTDOWN_FLAGS_DEFAULT 0x0 +#define FS_SHUTDOWN_FLAGS_LOGFLUSH 0x1 /* flush log but not data*/ +#define FS_SHUTDOWN_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */ + #endif /* _UAPI_LINUX_FS_H */ -- cgit v1.2.3 From 2e7af192697ef2a71c76fd57860b0fcd02754e14 Mon Sep 17 00:00:00 2001 From: Tommaso Cucinotta Date: Fri, 12 Sep 2025 07:38:29 +0200 Subject: sched/deadline: Add reporting of runtime left & abs deadline to sched_getattr() for DEADLINE tasks The SCHED_DEADLINE scheduler allows reading the statically configured run-time, deadline, and period parameters through the sched_getattr() system call. However, there is no immediate way to access, from user space, the current parameters used within the scheduler: the instantaneous runtime left in the current cycle, as well as the current absolute deadline. The `flags' sched_getattr() parameter, so far mandated to contain zero, now supports the SCHED_GETATTR_FLAG_DL_DYNAMIC=1 flag, to request retrieval of the leftover runtime and absolute deadline, converted to a CLOCK_MONOTONIC reference, instead of the statically configured parameters. This feature is useful for adaptive SCHED_DEADLINE tasks that need to modify their behavior depending on whether or not there is enough runtime left in the current period, and/or what is the current absolute deadline. Notes: - before returning the instantaneous parameters, the runtime is updated; - the abs deadline is returned shifted from rq_clock() to ktime_get_ns(), in CLOCK_MONOTONIC reference; this causes multiple invocations from the same period to return values that may differ for a few ns (showing some small drift), albeit the deadline doesn't move, in rq_clock() reference; - the abs deadline value returned to user-space, as unsigned 64-bit value, can represent nearly 585 years since boot time; - setting flags=0 provides the old behavior (retrieve static parameters). See also the notes from discussion held at OSPM 2025 on the topic "Making user space aware of current deadline-scheduler parameters". Signed-off-by: Tommaso Cucinotta Signed-off-by: Peter Zijlstra (Intel) Tested-by: Matteo Martelli Link: https://patch.msgid.link/20250912053937.31636-2-tommaso.cucinotta@santannapisa.it --- include/uapi/linux/sched.h | 3 +++ kernel/sched/deadline.c | 19 ++++++++++++++++--- kernel/sched/sched.h | 2 +- kernel/sched/syscalls.c | 16 +++++++++++----- 4 files changed, 31 insertions(+), 9 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h index 359a14cc76a4..52b69ce89368 100644 --- a/include/uapi/linux/sched.h +++ b/include/uapi/linux/sched.h @@ -146,4 +146,7 @@ struct clone_args { SCHED_FLAG_KEEP_ALL | \ SCHED_FLAG_UTIL_CLAMP) +/* Only for sched_getattr() own flag param, if task is SCHED_DEADLINE */ +#define SCHED_GETATTR_FLAG_DL_DYNAMIC 0x01 + #endif /* _UAPI_LINUX_SCHED_H */ diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 2de5727b94b4..9e253a825f39 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -3617,13 +3617,26 @@ void __setparam_dl(struct task_struct *p, const struct sched_attr *attr) dl_se->dl_density = to_ratio(dl_se->dl_deadline, dl_se->dl_runtime); } -void __getparam_dl(struct task_struct *p, struct sched_attr *attr) +void __getparam_dl(struct task_struct *p, struct sched_attr *attr, unsigned int flags) { struct sched_dl_entity *dl_se = &p->dl; + struct rq *rq = task_rq(p); + u64 adj_deadline; attr->sched_priority = p->rt_priority; - attr->sched_runtime = dl_se->dl_runtime; - attr->sched_deadline = dl_se->dl_deadline; + if (flags & SCHED_GETATTR_FLAG_DL_DYNAMIC) { + guard(raw_spinlock_irq)(&rq->__lock); + update_rq_clock(rq); + if (task_current(rq, p)) + update_curr_dl(rq); + + attr->sched_runtime = dl_se->runtime; + adj_deadline = dl_se->deadline - rq_clock(rq) + ktime_get_ns(); + attr->sched_deadline = adj_deadline; + } else { + attr->sched_runtime = dl_se->dl_runtime; + attr->sched_deadline = dl_se->dl_deadline; + } attr->sched_period = dl_se->dl_period; attr->sched_flags &= ~SCHED_DL_FLAGS; attr->sched_flags |= dl_se->flags; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 8bf2f7d524cd..fa2237e89bee 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -356,7 +356,7 @@ extern int sched_dl_global_validate(void); extern void sched_dl_do_global(void); extern int sched_dl_overflow(struct task_struct *p, int policy, const struct sched_attr *attr); extern void __setparam_dl(struct task_struct *p, const struct sched_attr *attr); -extern void __getparam_dl(struct task_struct *p, struct sched_attr *attr); +extern void __getparam_dl(struct task_struct *p, struct sched_attr *attr, unsigned int flags); extern bool __checkparam_dl(const struct sched_attr *attr); extern bool dl_param_changed(struct task_struct *p, const struct sched_attr *attr); extern int dl_cpuset_cpumask_can_shrink(const struct cpumask *cur, const struct cpumask *trial); diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c index 6f10db3646e7..a288ac0a633d 100644 --- a/kernel/sched/syscalls.c +++ b/kernel/sched/syscalls.c @@ -881,10 +881,10 @@ err_size: return -E2BIG; } -static void get_params(struct task_struct *p, struct sched_attr *attr) +static void get_params(struct task_struct *p, struct sched_attr *attr, unsigned int flags) { if (task_has_dl_policy(p)) { - __getparam_dl(p, attr); + __getparam_dl(p, attr, flags); } else if (task_has_rt_policy(p)) { attr->sched_priority = p->rt_priority; } else { @@ -950,7 +950,7 @@ SYSCALL_DEFINE3(sched_setattr, pid_t, pid, struct sched_attr __user *, uattr, return -ESRCH; if (attr.sched_flags & SCHED_FLAG_KEEP_PARAMS) - get_params(p, &attr); + get_params(p, &attr, 0); return sched_setattr(p, &attr); } @@ -1035,7 +1035,7 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, int retval; if (unlikely(!uattr || pid < 0 || usize > PAGE_SIZE || - usize < SCHED_ATTR_SIZE_VER0 || flags)) + usize < SCHED_ATTR_SIZE_VER0)) return -EINVAL; scoped_guard (rcu) { @@ -1043,6 +1043,12 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, if (!p) return -ESRCH; + if (flags) { + if (!task_has_dl_policy(p) || + flags != SCHED_GETATTR_FLAG_DL_DYNAMIC) + return -EINVAL; + } + retval = security_task_getscheduler(p); if (retval) return retval; @@ -1050,7 +1056,7 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, kattr.sched_policy = p->policy; if (p->sched_reset_on_fork) kattr.sched_flags |= SCHED_FLAG_RESET_ON_FORK; - get_params(p, &kattr); + get_params(p, &kattr, flags); kattr.sched_flags &= SCHED_FLAG_ALL; #ifdef CONFIG_UCLAMP_TASK -- cgit v1.2.3 From 7717fbb14028be5735acb911aeb7553b7c662418 Mon Sep 17 00:00:00 2001 From: Eric Woudstra Date: Tue, 24 Feb 2026 16:50:30 +0100 Subject: net: pppoe: avoid zero-length arrays in struct pppoe_hdr Jakub Kicinski reported following issue in upcoming patches: W=1 C=1 GCC build gives us: net/bridge/netfilter/nf_conntrack_bridge.c: note: in included file (through ../include/linux/if_pppox.h, ../include/uapi/linux/netfilter_bridge.h, ../include/linux/netfilter_bridge.h): include/uapi/linux/if_pppox.h: 153:29: warning: array of flexible structures sparse doesn't like that hdr has a zero-length array which overlaps proto. The kernel code doesn't currently need those arrays. PPPoE connection is functional after applying this patch. Reviewed-by: Nikolay Aleksandrov Reviewed-by: Kees Cook Signed-off-by: Eric Woudstra Link: https://patch.msgid.link/20260224155030.106918-1-ericwouds@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ppp/pppoe.c | 2 +- include/uapi/linux/if_pppox.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 4275b393a454..7900cc3212a5 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -885,7 +885,7 @@ static int pppoe_sendmsg(struct socket *sock, struct msghdr *m, skb->protocol = cpu_to_be16(ETH_P_PPP_SES); ph = skb_put(skb, total_len + sizeof(struct pppoe_hdr)); - start = (char *)&ph->tag[0]; + start = (char *)ph + sizeof(*ph); error = memcpy_from_msg(start, m, total_len); if (error < 0) { diff --git a/include/uapi/linux/if_pppox.h b/include/uapi/linux/if_pppox.h index 9abd80dcc46f..29b804aa7474 100644 --- a/include/uapi/linux/if_pppox.h +++ b/include/uapi/linux/if_pppox.h @@ -122,7 +122,9 @@ struct sockaddr_pppol2tpv3in6 { struct pppoe_tag { __be16 tag_type; __be16 tag_len; +#ifndef __KERNEL__ char tag_data[]; +#endif } __attribute__ ((packed)); /* Tag identifiers */ @@ -150,7 +152,9 @@ struct pppoe_hdr { __u8 code; __be16 sid; __be16 length; +#ifndef __KERNEL__ struct pppoe_tag tag[]; +#endif } __packed; /* Length of entire PPPoE + PPP header */ -- cgit v1.2.3 From 15c9ed1d8286dc0297f01347dc74f5a8cbc173de Mon Sep 17 00:00:00 2001 From: Qingfang Deng Date: Tue, 24 Feb 2026 09:50:52 +0800 Subject: pppoe: remove kernel-mode relay support The kernel-mode PPPoE relay feature and its two associated ioctls (PPPOEIOCSFWD and PPPOEIOCDFWD) are not used by any existing userspace PPPoE implementations. The most commonly-used package, RP-PPPoE [1], handles the relaying entirely in userspace. This legacy code has remained in the driver since its introduction in kernel 2.3.99-pre7 for over two decades, but has served no practical purpose. Remove the unused relay code. [1] https://dianne.skoll.ca/projects/rp-pppoe/ Signed-off-by: Qingfang Deng Acked-by: Arnd Bergmann Reviewed-by: Guillaume Nault Link: https://patch.msgid.link/20260224015053.42472-1-dqfext@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ppp/pppoe.c | 79 ------------------------------------------- drivers/net/ppp/pppox.c | 3 -- include/linux/if_pppox.h | 6 ---- include/uapi/linux/if_pppox.h | 10 ------ 4 files changed, 98 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 7900cc3212a5..1ac61c273b28 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -237,25 +237,6 @@ static inline struct pppox_sock *get_item(struct pppoe_net *pn, __be16 sid, return po; } -static inline struct pppox_sock *__get_item_by_addr(struct net *net, - struct sockaddr_pppox *sp) -{ - struct net_device *dev; - struct pppoe_net *pn; - struct pppox_sock *pppox_sock = NULL; - - int ifindex; - - dev = dev_get_by_name_rcu(net, sp->sa_addr.pppoe.dev); - if (dev) { - ifindex = dev->ifindex; - pn = pppoe_pernet(net); - pppox_sock = __get_item(pn, sp->sa_addr.pppoe.sid, - sp->sa_addr.pppoe.remote, ifindex); - } - return pppox_sock; -} - static inline void delete_item(struct pppoe_net *pn, __be16 sid, char *addr, int ifindex) { @@ -369,7 +350,6 @@ static struct notifier_block pppoe_notifier = { static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb) { struct pppox_sock *po = pppox_sk(sk); - struct pppox_sock *relay_po; /* Backlog receive. Semantics of backlog rcv preclude any code from * executing in lock_sock()/release_sock() bounds; meaning sk->sk_state @@ -378,17 +358,6 @@ static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb) if (sk->sk_state & PPPOX_BOUND) { ppp_input(&po->chan, skb); - } else if (sk->sk_state & PPPOX_RELAY) { - relay_po = __get_item_by_addr(sock_net(sk), - &po->pppoe_relay); - if (relay_po == NULL) - goto abort_kfree; - - if ((sk_pppox(relay_po)->sk_state & PPPOX_CONNECTED) == 0) - goto abort_kfree; - - if (!__pppoe_xmit(sk_pppox(relay_po), skb)) - goto abort_kfree; } else { if (sock_queue_rcv_skb(sk, skb)) goto abort_kfree; @@ -656,7 +625,6 @@ static int pppoe_connect(struct socket *sock, struct sockaddr_unsized *uservaddr po->pppoe_ifindex = 0; memset(&po->pppoe_pa, 0, sizeof(po->pppoe_pa)); - memset(&po->pppoe_relay, 0, sizeof(po->pppoe_relay)); memset(&po->chan, 0, sizeof(po->chan)); po->next = NULL; po->num = 0; @@ -783,53 +751,6 @@ static int pppoe_ioctl(struct socket *sock, unsigned int cmd, err = 0; break; - case PPPOEIOCSFWD: - { - struct pppox_sock *relay_po; - - err = -EBUSY; - if (sk->sk_state & (PPPOX_BOUND | PPPOX_DEAD)) - break; - - err = -ENOTCONN; - if (!(sk->sk_state & PPPOX_CONNECTED)) - break; - - /* PPPoE address from the user specifies an outbound - PPPoE address which frames are forwarded to */ - err = -EFAULT; - if (copy_from_user(&po->pppoe_relay, - (void __user *)arg, - sizeof(struct sockaddr_pppox))) - break; - - err = -EINVAL; - if (po->pppoe_relay.sa_family != AF_PPPOX || - po->pppoe_relay.sa_protocol != PX_PROTO_OE) - break; - - /* Check that the socket referenced by the address - actually exists. */ - rcu_read_lock(); - relay_po = __get_item_by_addr(sock_net(sk), &po->pppoe_relay); - rcu_read_unlock(); - if (!relay_po) - break; - - sk->sk_state |= PPPOX_RELAY; - err = 0; - break; - } - - case PPPOEIOCDFWD: - err = -EALREADY; - if (!(sk->sk_state & PPPOX_RELAY)) - break; - - sk->sk_state &= ~PPPOX_RELAY; - err = 0; - break; - default: err = -ENOTTY; } diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c index 08364f10a43f..5861a2f6ce3e 100644 --- a/drivers/net/ppp/pppox.c +++ b/drivers/net/ppp/pppox.c @@ -102,9 +102,6 @@ EXPORT_SYMBOL(pppox_ioctl); #ifdef CONFIG_COMPAT int pppox_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { - if (cmd == PPPOEIOCSFWD32) - cmd = PPPOEIOCSFWD; - return pppox_ioctl(sock, cmd, (unsigned long)compat_ptr(arg)); } diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index db45d6f1c4f4..8bbf676c2a85 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -25,8 +25,6 @@ struct pppoe_opt { struct net_device *dev; /* device associated with socket*/ int ifindex; /* ifindex of device associated with socket */ struct pppoe_addr pa; /* what this socket is bound to*/ - struct sockaddr_pppox relay; /* what socket data will be - relayed to (PPPoE relaying) */ struct work_struct padt_work;/* Work item for handling PADT */ }; @@ -53,7 +51,6 @@ struct pppox_sock { #define pppoe_dev proto.pppoe.dev #define pppoe_ifindex proto.pppoe.ifindex #define pppoe_pa proto.pppoe.pa -#define pppoe_relay proto.pppoe.relay static inline struct pppox_sock *pppox_sk(struct sock *sk) { @@ -80,14 +77,11 @@ extern void pppox_unbind_sock(struct sock *sk);/* delete ppp-channel binding */ extern int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); extern int pppox_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); -#define PPPOEIOCSFWD32 _IOW(0xB1 ,0, compat_size_t) - /* PPPoX socket states */ enum { PPPOX_NONE = 0, /* initial state */ PPPOX_CONNECTED = 1, /* connection established ==TCP_ESTABLISHED */ PPPOX_BOUND = 2, /* bound to ppp device */ - PPPOX_RELAY = 4, /* forwarding is enabled */ PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/ }; diff --git a/include/uapi/linux/if_pppox.h b/include/uapi/linux/if_pppox.h index 29b804aa7474..7ae044d71fb7 100644 --- a/include/uapi/linux/if_pppox.h +++ b/include/uapi/linux/if_pppox.h @@ -103,16 +103,6 @@ struct sockaddr_pppol2tpv3in6 { struct pppol2tpv3in6_addr pppol2tp; } __packed; -/********************************************************************* - * - * ioctl interface for defining forwarding of connections - * - ********************************************************************/ - -#define PPPOEIOCSFWD _IOW(0xB1 ,0, size_t) -#define PPPOEIOCDFWD _IO(0xB1 ,1) -/*#define PPPOEIOCGFWD _IOWR(0xB1,2, size_t)*/ - /* Codes to identify message types */ #define PADI_CODE 0x09 #define PADO_CODE 0x07 -- cgit v1.2.3 From 4916f2e2f3fc9aef289fcd07949301e5c29094c2 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Tue, 24 Feb 2026 02:02:14 +0000 Subject: bonding: print churn state via netlink Currently, the churn state is printed only in sysfs. Add netlink support so users could get the state via netlink. Signed-off-by: Hangbin Liu Link: https://patch.msgid.link/20260224020215.6012-1-liuhangbin@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/bonding/bond_netlink.c | 9 +++++++++ include/uapi/linux/if_link.h | 2 ++ 2 files changed, 11 insertions(+) (limited to 'include/uapi/linux') diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index 286f11c517f7..ea1a80e658ae 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -29,6 +29,8 @@ static size_t bond_get_slave_size(const struct net_device *bond_dev, nla_total_size(sizeof(u16)) + /* IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE */ nla_total_size(sizeof(s32)) + /* IFLA_BOND_SLAVE_PRIO */ nla_total_size(sizeof(u16)) + /* IFLA_BOND_SLAVE_ACTOR_PORT_PRIO */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_SLAVE_AD_CHURN_ACTOR_STATE */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_SLAVE_AD_CHURN_PARTNER_STATE */ 0; } @@ -77,6 +79,13 @@ static int bond_fill_slave_info(struct sk_buff *skb, IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, ad_port->partner_oper.port_state)) goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_ACTOR_STATE, + ad_port->sm_churn_actor_state)) + goto nla_put_failure; + if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_PARTNER_STATE, + ad_port->sm_churn_partner_state)) + goto nla_put_failure; } if (nla_put_u16(skb, IFLA_BOND_SLAVE_ACTOR_PORT_PRIO, diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index e9b5f79e1ee1..83a96c56b8ca 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -1568,6 +1568,8 @@ enum { IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, IFLA_BOND_SLAVE_PRIO, IFLA_BOND_SLAVE_ACTOR_PORT_PRIO, + IFLA_BOND_SLAVE_AD_CHURN_ACTOR_STATE, + IFLA_BOND_SLAVE_AD_CHURN_PARTNER_STATE, __IFLA_BOND_SLAVE_MAX, }; -- cgit v1.2.3 From 2164242c50084bd5b359b7d554d3a124e2c19074 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 26 Feb 2026 14:10:04 -0800 Subject: NFC: fix header file kernel-doc warnings Repair some of the comments: - use the correct enum names - don't use "/**" for a non-kernel-doc comment to fix these warnings: Warning: include/uapi/linux/nfc.h:127 Excess enum value '@NFC_EVENT_DEVICE_DEACTIVATED' description in 'nfc_commands' Warning: include/uapi/linux/nfc.h:204 Excess enum value '@NFC_ATTR_APDU' description in 'nfc_attrs' Warning: include/uapi/linux/nfc.h:302 expecting prototype for Pseudo(). Prototype was for NFC_RAW_HEADER_SIZE() instead Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260226221004.1037909-1-rdunlap@infradead.org Signed-off-by: Jakub Kicinski --- include/uapi/linux/nfc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 2f5b4be25261..82805eee4357 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -55,7 +55,7 @@ * (it sends %NFC_ATTR_DEVICE_INDEX) * @NFC_EVENT_TM_ACTIVATED: event emitted when the adapter is activated in * target mode. - * @NFC_EVENT_DEVICE_DEACTIVATED: event emitted when the adapter is deactivated + * @NFC_EVENT_TM_DEACTIVATED: event emitted when the adapter is deactivated * from target mode. * @NFC_CMD_LLC_GET_PARAMS: request LTO, RW, and MIUX parameters for a device * @NFC_CMD_LLC_SET_PARAMS: set one or more of LTO, RW, and MIUX parameters for @@ -156,7 +156,7 @@ enum nfc_commands { * @NFC_ATTR_SE_INDEX: Secure element index * @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED) * @NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS: Firmware download operation status - * @NFC_ATTR_APDU: Secure element APDU + * @NFC_ATTR_SE_APDU: Secure element APDU * @NFC_ATTR_TARGET_ISO15693_DSFID: ISO 15693 Data Storage Format Identifier * @NFC_ATTR_TARGET_ISO15693_UID: ISO 15693 Unique Identifier * @NFC_ATTR_SE_PARAMS: Parameters data from an evt_transaction @@ -291,7 +291,7 @@ struct sockaddr_nfc_llcp { #define NFC_HEADER_SIZE 1 -/** +/* * Pseudo-header info for raw socket packets * First byte is the adapter index * Second byte contains flags -- cgit v1.2.3 From d69cb039ab1930706428566caf5a714d0cb3ed3d Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 6 Feb 2026 18:15:49 +0100 Subject: wifi: cfg80211: set and report chandef CAC ongoing Allow to track and check CAC state from user mode by simple check phy channels eg. using iw phy1 channels command. This is done for regular CAC and background CAC. It is important for background CAC while we can start it from any app (eg. iw or hostapd). Signed-off-by: Janusz Dziedzic Link: https://patch.msgid.link/20260206171830.553879-3-janusz.dziedzic@gmail.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 +++ include/uapi/linux/nl80211.h | 6 ++++++ net/wireless/chan.c | 27 +++++++++++++++++++++++++++ net/wireless/core.h | 4 ++++ net/wireless/mlme.c | 7 +++++++ net/wireless/nl80211.c | 7 +++++++ 6 files changed, 54 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fc01de19c798..e00045c150e7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -190,6 +190,8 @@ enum ieee80211_channel_flags { * on this channel. * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered. * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels. + * @cac_start_time: timestamp (CLOCK_BOOTTIME, nanoseconds) when CAC was + * started on this channel. Zero when CAC is not in progress. * @psd: power spectral density (in dBm) */ struct ieee80211_channel { @@ -207,6 +209,7 @@ struct ieee80211_channel { enum nl80211_dfs_state dfs_state; unsigned long dfs_state_entered; unsigned int dfs_cac_ms; + u64 cac_start_time; s8 psd; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index b63f71850906..c75aa039f096 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4480,6 +4480,10 @@ enum nl80211_wmm_rule { * as a non-primary subchannel. Only applicable to S1G channels. * @NL80211_FREQUENCY_ATTR_NO_UHR: UHR operation is not allowed on this channel * in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_CAC_START_TIME: Channel Availability Check (CAC) + * start time (CLOCK_BOOTTIME, nanoseconds). Only present when CAC is + * currently in progress on this channel. + * @NL80211_FREQUENCY_ATTR_PAD: attribute used for padding for 64-bit alignment * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -4530,6 +4534,8 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_NO_16MHZ, NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY, NL80211_FREQUENCY_ATTR_NO_UHR, + NL80211_FREQUENCY_ATTR_CAC_START_TIME, + NL80211_FREQUENCY_ATTR_PAD, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 68221b1ab45e..dfe319565280 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -642,6 +642,33 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy, } } +void cfg80211_set_cac_state(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + bool cac_ongoing) +{ + struct ieee80211_channel *c; + int width; + u64 cac_time; + + if (WARN_ON(!cfg80211_chandef_valid(chandef))) + return; + + width = cfg80211_chandef_get_width(chandef); + if (width < 0) + return; + + /* Get the same timestamp for all subchannels */ + cac_time = cac_ongoing ? ktime_get_boottime_ns() : 0; + + for_each_subchan(chandef, freq, cf) { + c = ieee80211_get_channel_khz(wiphy, freq); + if (!c) + continue; + + c->cac_start_time = cac_time; + } +} + static bool cfg80211_dfs_permissive_check_wdev(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, diff --git a/net/wireless/core.h b/net/wireless/core.h index 6ac57b7b2615..6cace846d7a3 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -481,6 +481,10 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, enum nl80211_dfs_state dfs_state); +void cfg80211_set_cac_state(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + bool cac_ongoing); + void cfg80211_dfs_channels_update_work(struct work_struct *work); void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 212178d04efa..283ea4c7c61e 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -1162,9 +1162,11 @@ void cfg80211_cac_event(struct net_device *netdev, fallthrough; case NL80211_RADAR_CAC_ABORTED: wdev->links[link_id].cac_started = false; + cfg80211_set_cac_state(wiphy, chandef, false); break; case NL80211_RADAR_CAC_STARTED: wdev->links[link_id].cac_started = true; + cfg80211_set_cac_state(wiphy, chandef, true); break; default: WARN_ON(1); @@ -1192,15 +1194,18 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev, switch (event) { case NL80211_RADAR_CAC_FINISHED: cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); + cfg80211_set_cac_state(wiphy, chandef, false); memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef)); queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk); cfg80211_sched_dfs_chan_update(rdev); break; case NL80211_RADAR_CAC_ABORTED: + cfg80211_set_cac_state(wiphy, chandef, false); if (!cancel_delayed_work(&rdev->background_cac_done_wk)) return; break; case NL80211_RADAR_CAC_STARTED: + cfg80211_set_cac_state(wiphy, chandef, true); break; default: return; @@ -1307,6 +1312,8 @@ void cfg80211_stop_radar_detection(struct wireless_dev *wdev) chandef = *wdev_chandef(wdev, link_id); rdev_end_cac(rdev, wdev->netdev, link_id); + wdev->links[link_id].cac_started = false; + cfg80211_set_cac_state(wiphy, &chandef, false); nl80211_radar_notify(rdev, &chandef, NL80211_RADAR_CAC_ABORTED, wdev->netdev, GFP_KERNEL); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b94231c8441c..7e288d3ce5ae 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1333,6 +1333,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, if ((chan->flags & IEEE80211_CHAN_NO_UHR) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_UHR)) goto nla_put_failure; + if (chan->cac_start_time && + nla_put_u64_64bit(msg, + NL80211_FREQUENCY_ATTR_CAC_START_TIME, + chan->cac_start_time, + NL80211_FREQUENCY_ATTR_PAD)) + goto nla_put_failure; } if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, @@ -11353,6 +11359,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, wdev->links[link_id].cac_started = true; wdev->links[link_id].cac_start_time = jiffies; wdev->links[link_id].cac_time_ms = cac_time_ms; + cfg80211_set_cac_state(wiphy, &chandef, true); return 0; } -- cgit v1.2.3 From 6a584e336cefb230e2d981a464f4d85562eb750c Mon Sep 17 00:00:00 2001 From: Hari Chandrakanthan Date: Mon, 16 Feb 2026 08:50:26 +0530 Subject: wifi: cfg80211: add support to handle incumbent signal detected event from mac80211/driver When any incumbent signal is detected by an AP/mesh interface operating in 6 GHz band, FCC mandates the AP/mesh to vacate the channels affected by it [1]. Add a new API cfg80211_incumbent_signal_notify() that can be used by mac80211 or drivers to notify the higher layers about the signal interference event with the interference bitmap in which each bit denotes the affected 20 MHz in the operating channel. Add support for the new nl80211 event and nl80211 attribute as well to notify userspace on the details about the interference event. Userspace is expected to process it and take further action - vacate the channel, or reduce the bandwidth. [1] - https://apps.fcc.gov/kdb/GetAttachment.html?id=nXQiRC%2B4mfiA54Zha%2BrW4Q%3D%3D&desc=987594%20D02%20U-NII%206%20GHz%20EMC%20Measurement%20v03&tracking_number=277034 Signed-off-by: Hari Chandrakanthan Signed-off-by: Amith A Link: https://patch.msgid.link/20260216032027.2310956-2-amith.a@oss.qualcomm.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 23 +++++++++++++++++++++++ include/uapi/linux/nl80211.h | 19 +++++++++++++++++++ net/wireless/nl80211.c | 40 ++++++++++++++++++++++++++++++++++++++++ net/wireless/trace.h | 19 +++++++++++++++++++ 4 files changed, 101 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e00045c150e7..cea77bf90cfe 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -10475,4 +10475,27 @@ cfg80211_s1g_get_primary_sibling(struct wiphy *wiphy, return ieee80211_get_channel_khz(wiphy, sibling_1mhz_khz); } + +/** + * cfg80211_incumbent_signal_notify - Notify userspace of incumbent signal detection + * @wiphy: the wiphy to use + * @chandef: channel definition in which the interference was detected + * @signal_interference_bitmap: bitmap indicating interference across 20 MHz segments + * @gfp: allocation context for message creation and multicast; pass GFP_ATOMIC + * if called from atomic context (e.g. firmware event handler), otherwise + * GFP_KERNEL + * + * Use this function to notify userspace when an incumbent signal is detected on + * the operating channel in the 6 GHz band. The notification includes the + * current channel definition and a bitmap representing interference across + * the operating bandwidth. Each bit in the bitmap corresponds to a 20 MHz + * segment, with the lowest bit representing the lowest frequency segment. + * Punctured sub-channels are included in the bitmap structure but are always + * set to zero since interference detection is not performed on them. + */ +void cfg80211_incumbent_signal_notify(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + u32 signal_interference_bitmap, + gfp_t gfp); + #endif /* __NET_CFG80211_H */ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index c75aa039f096..fe2c8c8d6dd6 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1361,6 +1361,12 @@ * user space that the NAN new cluster has been joined. The cluster ID is * indicated by %NL80211_ATTR_MAC. * + * @NL80211_CMD_INCUMBENT_SIGNAL_DETECT: Once any incumbent signal is detected + * on the operating channel in 6 GHz band, userspace is notified with the + * signal interference bitmap using + * %NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP. The current channel + * definition is also sent. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1624,6 +1630,8 @@ enum nl80211_commands { NL80211_CMD_NAN_NEXT_DW_NOTIFICATION, NL80211_CMD_NAN_CLUSTER_JOINED, + NL80211_CMD_INCUMBENT_SIGNAL_DETECT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2984,6 +2992,15 @@ enum nl80211_commands { * this feature during association. This is a flag attribute. * Currently only supported in mac80211 drivers. * + * @NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP: u32 attribute specifying + * the signal interference bitmap detected on the operating bandwidth for + * %NL80211_CMD_INCUMBENT_SIGNAL_DETECT. Each bit represents a 20 MHz + * segment, lowest bit corresponds to the lowest 20 MHz segment, in the + * operating bandwidth where the interference is detected. Punctured + * sub-channels are included in the bitmap structure; however, since + * interference detection is not performed on these sub-channels, their + * corresponding bits are consistently set to zero. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3557,6 +3574,8 @@ enum nl80211_attrs { NL80211_ATTR_UHR_CAPABILITY, NL80211_ATTR_DISABLE_UHR, + NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b619f99c221e..0da055dad159 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -21127,6 +21127,46 @@ void cfg80211_ch_switch_notify(struct net_device *dev, } EXPORT_SYMBOL(cfg80211_ch_switch_notify); +void cfg80211_incumbent_signal_notify(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + u32 signal_interference_bitmap, + gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct sk_buff *msg; + void *hdr; + + trace_cfg80211_incumbent_signal_notify(wiphy, chandef, signal_interference_bitmap); + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_INCUMBENT_SIGNAL_DETECT); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) + goto nla_put_failure; + + if (nl80211_send_chandef(msg, chandef)) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP, + signal_interference_bitmap)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, + NL80211_MCGRP_MLME, gfp); + return; + +nla_put_failure: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_incumbent_signal_notify); + void cfg80211_ch_switch_started_notify(struct net_device *dev, struct cfg80211_chan_def *chandef, unsigned int link_id, u8 count, diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 643ccf4f0227..352a57d8b968 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -4225,6 +4225,25 @@ TRACE_EVENT(cfg80211_nan_cluster_joined, WDEV_PR_ARG, __entry->cluster_id, __entry->new_cluster ? " [new]" : "") ); + +TRACE_EVENT(cfg80211_incumbent_signal_notify, + TP_PROTO(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + u32 signal_interference_bitmap), + TP_ARGS(wiphy, chandef, signal_interference_bitmap), + TP_STRUCT__entry( + WIPHY_ENTRY + CHAN_DEF_ENTRY + __field(u32, signal_interference_bitmap) + ), + TP_fast_assign( + WIPHY_ASSIGN; + CHAN_DEF_ASSIGN(chandef); + __entry->signal_interference_bitmap = signal_interference_bitmap; + ), + TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", signal_interference_bitmap=0x%x", + WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->signal_interference_bitmap) +); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH -- cgit v1.2.3 From bd77375097357b46af00db1316ceab5e82ccbc8b Mon Sep 17 00:00:00 2001 From: Kavita Kavita Date: Fri, 27 Feb 2026 00:25:51 +0530 Subject: wifi: cfg80211: add support for IEEE 802.1X Authentication Protocol Add an extended feature flag NL80211_EXT_FEATURE_IEEE8021X_AUTH to allow a driver to indicate support for the IEEE 802.1X authentication protocol in non-AP STA mode, as defined in "IEEE P802.11bi/D4.0, 12.16.5". In case of SME in userspace, the Authentication frame body is prepared in userspace while the driver finalizes the Authentication frame once it receives the required fields and elements. The driver indicates support for IEEE 802.1X authentication using the extended feature flag so that userspace can initiate IEEE 802.1X authentication. When the feature flag is set, process IEEE 802.1X Authentication frames from userspace in non-AP STA mode. If the flag is not set, reject IEEE 802.1X Authentication frames. Define a new authentication type NL80211_AUTHTYPE_IEEE8021X for IEEE 802.1X authentication. Signed-off-by: Kavita Kavita Link: https://patch.msgid.link/20260226185553.1516290-4-kavita.kavita@oss.qualcomm.com Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + include/uapi/linux/nl80211.h | 9 +++++++++ net/wireless/nl80211.c | 14 ++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 0aa2fb8f88de..1bf806f85372 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1358,6 +1358,7 @@ struct ieee80211_tdls_data { #define WLAN_AUTH_FILS_SK 4 #define WLAN_AUTH_FILS_SK_PFS 5 #define WLAN_AUTH_FILS_PK 6 +#define WLAN_AUTH_IEEE8021X 8 #define WLAN_AUTH_EPPKE 9 #define WLAN_AUTH_LEAP 128 diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index fe2c8c8d6dd6..0b7a06c2b9f7 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -5491,6 +5491,8 @@ enum nl80211_bss_status { * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key * @NL80211_AUTHTYPE_EPPKE: Enhanced Privacy Protection Key Exchange + * @NL80211_AUTHTYPE_IEEE8021X: IEEE 802.1X authentication utilizing + * Authentication frames * @__NL80211_AUTHTYPE_NUM: internal * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by @@ -5507,6 +5509,7 @@ enum nl80211_auth_type { NL80211_AUTHTYPE_FILS_SK_PFS, NL80211_AUTHTYPE_FILS_PK, NL80211_AUTHTYPE_EPPKE, + NL80211_AUTHTYPE_IEEE8021X, /* keep last */ __NL80211_AUTHTYPE_NUM, @@ -6820,6 +6823,11 @@ enum nl80211_feature_flags { * frames in both non‑AP STA and AP mode as specified in * "IEEE P802.11bi/D3.0, 12.16.6". * + * @NL80211_EXT_FEATURE_IEEE8021X_AUTH: Driver supports IEEE 802.1X + * authentication utilizing Authentication frames with user space SME + * (NL80211_CMD_AUTHENTICATE) in non-AP STA mode, as specified in + * "IEEE P802.11bi/D4.0, 12.16.5". + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -6898,6 +6906,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_BEACON_RATE_EHT, NL80211_EXT_FEATURE_EPPKE, NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION, + NL80211_EXT_FEATURE_IEEE8021X_AUTH, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f54b3cca6975..de7956dbe0a0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6550,6 +6550,10 @@ static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev, NL80211_EXT_FEATURE_EPPKE) && auth_type == NL80211_AUTHTYPE_EPPKE) return false; + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_IEEE8021X_AUTH) && + auth_type == NL80211_AUTHTYPE_IEEE8021X) + return false; return true; case NL80211_CMD_CONNECT: if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) && @@ -6571,6 +6575,10 @@ static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev, NL80211_EXT_FEATURE_EPPKE) && auth_type == NL80211_AUTHTYPE_EPPKE) return false; + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_IEEE8021X_AUTH) && + auth_type == NL80211_AUTHTYPE_IEEE8021X) + return false; return true; case NL80211_CMD_START_AP: if (!wiphy_ext_feature_isset(&rdev->wiphy, @@ -12103,7 +12111,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) auth_type == NL80211_AUTHTYPE_FILS_SK || auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || auth_type == NL80211_AUTHTYPE_FILS_PK || - auth_type == NL80211_AUTHTYPE_EPPKE) && + auth_type == NL80211_AUTHTYPE_EPPKE || + auth_type == NL80211_AUTHTYPE_IEEE8021X) && !info->attrs[NL80211_ATTR_AUTH_DATA]) return -EINVAL; @@ -12112,7 +12121,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) auth_type != NL80211_AUTHTYPE_FILS_SK && auth_type != NL80211_AUTHTYPE_FILS_SK_PFS && auth_type != NL80211_AUTHTYPE_FILS_PK && - auth_type != NL80211_AUTHTYPE_EPPKE) + auth_type != NL80211_AUTHTYPE_EPPKE && + auth_type != NL80211_AUTHTYPE_IEEE8021X) return -EINVAL; req.auth_data = nla_data(info->attrs[NL80211_ATTR_AUTH_DATA]); req.auth_data_len = nla_len(info->attrs[NL80211_ATTR_AUTH_DATA]); -- cgit v1.2.3 From f6c2996709ca864a8226f54377b4b07da002b807 Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Thu, 26 Feb 2026 14:15:46 -0300 Subject: audit: fix whitespace alignment in include/uapi/linux/audit.h Fixed minor indentation inconsistencies in the audit macros to align with standard kernel coding style using 8-character hard tabs. Signed-off-by: Ricardo Robaina [PM: fixed a space before tab issue in the patch] Signed-off-by: Paul Moore --- include/uapi/linux/audit.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 14a1c1fe013a..71cbdc542ce9 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -350,7 +350,7 @@ enum { #define AUDIT_STATUS_ENABLED 0x0001 #define AUDIT_STATUS_FAILURE 0x0002 #define AUDIT_STATUS_PID 0x0004 -#define AUDIT_STATUS_RATE_LIMIT 0x0008 +#define AUDIT_STATUS_RATE_LIMIT 0x0008 #define AUDIT_STATUS_BACKLOG_LIMIT 0x0010 #define AUDIT_STATUS_BACKLOG_WAIT_TIME 0x0020 #define AUDIT_STATUS_LOST 0x0040 @@ -386,8 +386,8 @@ enum { * These bits disambiguate different calling conventions that share an * ELF machine type, bitness, and endianness */ -#define __AUDIT_ARCH_CONVENTION_MASK 0x30000000 -#define __AUDIT_ARCH_CONVENTION_MIPS64_N32 0x20000000 +#define __AUDIT_ARCH_CONVENTION_MASK 0x30000000 +#define __AUDIT_ARCH_CONVENTION_MIPS64_N32 0x20000000 /* distinguish syscall tables */ #define __AUDIT_ARCH_64BIT 0x80000000 -- cgit v1.2.3 From 9cc60ec453fe5d58d4faa70829814769a8af24d4 Mon Sep 17 00:00:00 2001 From: Qinxin Xia Date: Wed, 25 Feb 2026 17:37:58 +0800 Subject: dma-mapping: benchmark: modify the framework to adapt to more map modes This patch adjusts the DMA map benchmark framework to make the DMA map benchmark framework more flexible and adaptable to other mapping modes in the future. By abstracting the framework into five interfaces: prepare, unprepare, initialize_data, do_map, and do_unmap. The new map schema can be introduced more easily without major modifications to the existing code structure. Reviewed-by: Barry Song Signed-off-by: Qinxin Xia Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20260225093800.3625054-2-xiaqinxin@huawei.com --- include/uapi/linux/map_benchmark.h | 8 ++- kernel/dma/map_benchmark.c | 131 ++++++++++++++++++++++++++++++------- 2 files changed, 115 insertions(+), 24 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/map_benchmark.h b/include/uapi/linux/map_benchmark.h index c2d91088a40d..e076748f2120 100644 --- a/include/uapi/linux/map_benchmark.h +++ b/include/uapi/linux/map_benchmark.h @@ -17,6 +17,11 @@ #define DMA_MAP_TO_DEVICE 1 #define DMA_MAP_FROM_DEVICE 2 +enum { + DMA_MAP_BENCH_SINGLE_MODE, + DMA_MAP_BENCH_MODE_MAX +}; + struct map_benchmark { __u64 avg_map_100ns; /* average map latency in 100ns */ __u64 map_stddev; /* standard deviation of map latency */ @@ -29,7 +34,8 @@ struct map_benchmark { __u32 dma_dir; /* DMA data direction */ __u32 dma_trans_ns; /* time for DMA transmission in ns */ __u32 granule; /* how many PAGE_SIZE will do map/unmap once a time */ - __u8 expansion[76]; /* For future use */ + __u8 map_mode; /* the mode of dma map */ + __u8 expansion[75]; /* For future use */ }; #endif /* _UAPI_DMA_BENCHMARK_H */ diff --git a/kernel/dma/map_benchmark.c b/kernel/dma/map_benchmark.c index 0f33b3ea7daf..b80e0fb399b1 100644 --- a/kernel/dma/map_benchmark.c +++ b/kernel/dma/map_benchmark.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -31,17 +32,105 @@ struct map_benchmark_data { atomic64_t loops; }; +struct map_benchmark_ops { + void *(*prepare)(struct map_benchmark_data *map); + void (*unprepare)(void *mparam); + void (*initialize_data)(void *mparam); + int (*do_map)(void *mparam); + void (*do_unmap)(void *mparam); +}; + +struct dma_single_map_param { + struct device *dev; + dma_addr_t addr; + void *xbuf; + u32 npages; + u32 dma_dir; +}; + +static void *dma_single_map_benchmark_prepare(struct map_benchmark_data *map) +{ + struct dma_single_map_param *params __free(kfree) = kzalloc(sizeof(*params), + GFP_KERNEL); + if (!params) + return NULL; + + params->npages = map->bparam.granule; + params->dma_dir = map->bparam.dma_dir; + params->dev = map->dev; + params->xbuf = alloc_pages_exact(params->npages * PAGE_SIZE, GFP_KERNEL); + if (!params->xbuf) + return NULL; + + return_ptr(params); +} + +static void dma_single_map_benchmark_unprepare(void *mparam) +{ + struct dma_single_map_param *params = mparam; + + free_pages_exact(params->xbuf, params->npages * PAGE_SIZE); + kfree(params); +} + +static void dma_single_map_benchmark_initialize_data(void *mparam) +{ + struct dma_single_map_param *params = mparam; + + /* + * for a non-coherent device, if we don't stain them in the + * cache, this will give an underestimate of the real-world + * overhead of BIDIRECTIONAL or TO_DEVICE mappings; + * 66 means everything goes well! 66 is lucky. + */ + if (params->dma_dir != DMA_FROM_DEVICE) + memset(params->xbuf, 0x66, params->npages * PAGE_SIZE); +} + +static int dma_single_map_benchmark_do_map(void *mparam) +{ + struct dma_single_map_param *params = mparam; + + params->addr = dma_map_single(params->dev, params->xbuf, + params->npages * PAGE_SIZE, params->dma_dir); + if (unlikely(dma_mapping_error(params->dev, params->addr))) { + pr_err("dma_map_single failed on %s\n", dev_name(params->dev)); + return -ENOMEM; + } + + return 0; +} + +static void dma_single_map_benchmark_do_unmap(void *mparam) +{ + struct dma_single_map_param *params = mparam; + + dma_unmap_single(params->dev, params->addr, + params->npages * PAGE_SIZE, params->dma_dir); +} + +static struct map_benchmark_ops dma_single_map_benchmark_ops = { + .prepare = dma_single_map_benchmark_prepare, + .unprepare = dma_single_map_benchmark_unprepare, + .initialize_data = dma_single_map_benchmark_initialize_data, + .do_map = dma_single_map_benchmark_do_map, + .do_unmap = dma_single_map_benchmark_do_unmap, +}; + +static struct map_benchmark_ops *dma_map_benchmark_ops[DMA_MAP_BENCH_MODE_MAX] = { + [DMA_MAP_BENCH_SINGLE_MODE] = &dma_single_map_benchmark_ops, +}; + static int map_benchmark_thread(void *data) { - void *buf; - dma_addr_t dma_addr; struct map_benchmark_data *map = data; - int npages = map->bparam.granule; - u64 size = npages * PAGE_SIZE; + __u8 map_mode = map->bparam.map_mode; int ret = 0; - buf = alloc_pages_exact(size, GFP_KERNEL); - if (!buf) + struct map_benchmark_ops *mb_ops = dma_map_benchmark_ops[map_mode]; + void *mparam = mb_ops->prepare(map); + + if (!mparam) return -ENOMEM; while (!kthread_should_stop()) { @@ -49,23 +138,12 @@ static int map_benchmark_thread(void *data) ktime_t map_stime, map_etime, unmap_stime, unmap_etime; ktime_t map_delta, unmap_delta; - /* - * for a non-coherent device, if we don't stain them in the - * cache, this will give an underestimate of the real-world - * overhead of BIDIRECTIONAL or TO_DEVICE mappings; - * 66 means evertything goes well! 66 is lucky. - */ - if (map->dir != DMA_FROM_DEVICE) - memset(buf, 0x66, size); - + mb_ops->initialize_data(mparam); map_stime = ktime_get(); - dma_addr = dma_map_single(map->dev, buf, size, map->dir); - if (unlikely(dma_mapping_error(map->dev, dma_addr))) { - pr_err("dma_map_single failed on %s\n", - dev_name(map->dev)); - ret = -ENOMEM; + ret = mb_ops->do_map(mparam); + if (ret) goto out; - } + map_etime = ktime_get(); map_delta = ktime_sub(map_etime, map_stime); @@ -73,7 +151,8 @@ static int map_benchmark_thread(void *data) ndelay(map->bparam.dma_trans_ns); unmap_stime = ktime_get(); - dma_unmap_single(map->dev, dma_addr, size, map->dir); + mb_ops->do_unmap(mparam); + unmap_etime = ktime_get(); unmap_delta = ktime_sub(unmap_etime, unmap_stime); @@ -108,7 +187,7 @@ static int map_benchmark_thread(void *data) } out: - free_pages_exact(buf, size); + mb_ops->unprepare(mparam); return ret; } @@ -209,6 +288,12 @@ static long map_benchmark_ioctl(struct file *file, unsigned int cmd, switch (cmd) { case DMA_MAP_BENCHMARK: + if (map->bparam.map_mode < 0 || + map->bparam.map_mode >= DMA_MAP_BENCH_MODE_MAX) { + pr_err("invalid map mode\n"); + return -EINVAL; + } + if (map->bparam.threads == 0 || map->bparam.threads > DMA_MAP_MAX_THREADS) { pr_err("invalid thread number\n"); -- cgit v1.2.3 From a8d14dd6e621f47344d0eda72f7ce9203bdef4f1 Mon Sep 17 00:00:00 2001 From: Qinxin Xia Date: Wed, 25 Feb 2026 17:37:59 +0800 Subject: dma-mapping: benchmark: add support for dma_map_sg Support for dma scatter-gather mapping and is intended for testing mapping performance. It achieves by introducing the dma_sg_map_param structure and related functions, which enable the implementation of scatter-gather mapping preparation, mapping, and unmapping operations. Additionally, the dma_map_benchmark_ops array is updated to include operations for scatter-gather mapping. This commit aims to provide a wider range of mapping performance test to cater to different scenarios. Reviewed-by: Barry Song Signed-off-by: Qinxin Xia Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20260225093800.3625054-3-xiaqinxin@huawei.com --- include/uapi/linux/map_benchmark.h | 5 +- kernel/dma/map_benchmark.c | 115 +++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/map_benchmark.h b/include/uapi/linux/map_benchmark.h index e076748f2120..4b17829a9f17 100644 --- a/include/uapi/linux/map_benchmark.h +++ b/include/uapi/linux/map_benchmark.h @@ -19,6 +19,7 @@ enum { DMA_MAP_BENCH_SINGLE_MODE, + DMA_MAP_BENCH_SG_MODE, DMA_MAP_BENCH_MODE_MAX }; @@ -33,7 +34,9 @@ struct map_benchmark { __u32 dma_bits; /* DMA addressing capability */ __u32 dma_dir; /* DMA data direction */ __u32 dma_trans_ns; /* time for DMA transmission in ns */ - __u32 granule; /* how many PAGE_SIZE will do map/unmap once a time */ + __u32 granule; /* - SINGLE_MODE: number of pages mapped/unmapped per operation + * - SG_MODE: number of scatterlist entries (each maps one page) + */ __u8 map_mode; /* the mode of dma map */ __u8 expansion[75]; /* For future use */ }; diff --git a/kernel/dma/map_benchmark.c b/kernel/dma/map_benchmark.c index b80e0fb399b1..29eeb5fdf199 100644 --- a/kernel/dma/map_benchmark.c +++ b/kernel/dma/map_benchmark.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -117,8 +118,122 @@ static struct map_benchmark_ops dma_single_map_benchmark_ops = { .do_unmap = dma_single_map_benchmark_do_unmap, }; +struct dma_sg_map_param { + struct sg_table sgt; + struct device *dev; + void **buf; + u32 npages; + u32 dma_dir; +}; + +static void *dma_sg_map_benchmark_prepare(struct map_benchmark_data *map) +{ + struct scatterlist *sg; + int i; + + struct dma_sg_map_param *params = kzalloc(sizeof(*params), GFP_KERNEL); + + if (!params) + return NULL; + /* + * Set the number of scatterlist entries based on the granule. + * In SG mode, 'granule' represents the number of scatterlist entries. + * Each scatterlist entry corresponds to a single page. + */ + params->npages = map->bparam.granule; + params->dma_dir = map->bparam.dma_dir; + params->dev = map->dev; + params->buf = kmalloc_array(params->npages, sizeof(*params->buf), + GFP_KERNEL); + if (!params->buf) + goto out; + + if (sg_alloc_table(¶ms->sgt, params->npages, GFP_KERNEL)) + goto free_buf; + + for_each_sgtable_sg(¶ms->sgt, sg, i) { + params->buf[i] = (void *)__get_free_page(GFP_KERNEL); + if (!params->buf[i]) + goto free_page; + + sg_set_buf(sg, params->buf[i], PAGE_SIZE); + } + + return params; + +free_page: + while (i-- > 0) + free_page((unsigned long)params->buf[i]); + + sg_free_table(¶ms->sgt); +free_buf: + kfree(params->buf); +out: + kfree(params); + return NULL; +} + +static void dma_sg_map_benchmark_unprepare(void *mparam) +{ + struct dma_sg_map_param *params = mparam; + int i; + + for (i = 0; i < params->npages; i++) + free_page((unsigned long)params->buf[i]); + + sg_free_table(¶ms->sgt); + + kfree(params->buf); + kfree(params); +} + +static void dma_sg_map_benchmark_initialize_data(void *mparam) +{ + struct dma_sg_map_param *params = mparam; + struct scatterlist *sg; + int i = 0; + + if (params->dma_dir == DMA_FROM_DEVICE) + return; + + for_each_sgtable_sg(¶ms->sgt, sg, i) + memset(params->buf[i], 0x66, PAGE_SIZE); +} + +static int dma_sg_map_benchmark_do_map(void *mparam) +{ + struct dma_sg_map_param *params = mparam; + int ret = 0; + + int sg_mapped = dma_map_sg(params->dev, params->sgt.sgl, + params->npages, params->dma_dir); + if (!sg_mapped) { + pr_err("dma_map_sg failed on %s\n", dev_name(params->dev)); + ret = -ENOMEM; + } + + return ret; +} + +static void dma_sg_map_benchmark_do_unmap(void *mparam) +{ + struct dma_sg_map_param *params = mparam; + + dma_unmap_sg(params->dev, params->sgt.sgl, params->npages, + params->dma_dir); +} + +static struct map_benchmark_ops dma_sg_map_benchmark_ops = { + .prepare = dma_sg_map_benchmark_prepare, + .unprepare = dma_sg_map_benchmark_unprepare, + .initialize_data = dma_sg_map_benchmark_initialize_data, + .do_map = dma_sg_map_benchmark_do_map, + .do_unmap = dma_sg_map_benchmark_do_unmap, +}; + static struct map_benchmark_ops *dma_map_benchmark_ops[DMA_MAP_BENCH_MODE_MAX] = { [DMA_MAP_BENCH_SINGLE_MODE] = &dma_single_map_benchmark_ops, + [DMA_MAP_BENCH_SG_MODE] = &dma_sg_map_benchmark_ops, }; static int map_benchmark_thread(void *data) -- cgit v1.2.3 From 39ae83b0f557969c461d93c608545443a2f5c307 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 3 Mar 2026 17:24:37 -0800 Subject: net: openvswitch: clean up some kernel-doc warnings Fix some kernel-doc warnings in openvswitch.h: Mark enum placeholders that are not used as "private" so that kernel-doc comments are not needed for them. Correct names for 2 enum values: Warning: include/uapi/linux/openvswitch.h:300 Excess enum value '@OVS_VPORT_UPCALL_SUCCESS' description in 'ovs_vport_upcall_attr' Warning: include/uapi/linux/openvswitch.h:300 Excess enum value '@OVS_VPORT_UPCALL_FAIL' description in 'ovs_vport_upcall_attr' Convert one comment from "/**" kernel-doc to a plain C "/*" comment: Warning: include/uapi/linux/openvswitch.h:638 This comment starts with '/**', but isn't a kernel-doc comment. * Omit attributes for notifications. Add more kernel-doc: - add kernel-doc for kernel-only enums; - add missing kernel-doc for enum ovs_datapath_attr; - add missing kernel-doc for enum ovs_flow_attr; - add missing kernel-doc for enum ovs_sample_attr; - add kernel-doc for enum ovs_check_pkt_len_attr; - add kernel-doc for enum ovs_action_attr; - add kernel-doc for enum ovs_action_push_eth; - add kernel-doc for enum ovs_vport_attr; Signed-off-by: Randy Dunlap Acked-by: Ilya Maximets Link: https://patch.msgid.link/20260304012437.469151-1-rdunlap@infradead.org Signed-off-by: Jakub Kicinski --- include/uapi/linux/openvswitch.h | 76 +++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 8 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 3092c2c6f1d2..aa2acdbda8f8 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -70,12 +70,15 @@ enum ovs_datapath_cmd { * set on the datapath port (for OVS_ACTION_ATTR_MISS). Only valid on * %OVS_DP_CMD_NEW requests. A value of zero indicates that upcalls should * not be sent. + * @OVS_DP_ATTR_MASKS_CACHE_SIZE: Number of the entries in the flow table + * masks cache. * @OVS_DP_ATTR_PER_CPU_PIDS: Per-cpu array of PIDs for upcalls when * OVS_DP_F_DISPATCH_UPCALL_PER_CPU feature is set. * @OVS_DP_ATTR_STATS: Statistics about packets that have passed through the * datapath. Always present in notifications. * @OVS_DP_ATTR_MEGAFLOW_STATS: Statistics about mega flow masks usage for the * datapath. Always present in notifications. + * @OVS_DP_ATTR_USER_FEATURES: OVS_DP_F_* flags. * @OVS_DP_ATTR_IFINDEX: Interface index for a new datapath netdev. Only * valid for %OVS_DP_CMD_NEW requests. * @@ -83,18 +86,23 @@ enum ovs_datapath_cmd { * payload for %OVS_DP_* commands. */ enum ovs_datapath_attr { + /* private: */ OVS_DP_ATTR_UNSPEC, + /* public: */ OVS_DP_ATTR_NAME, /* name of dp_ifindex netdev */ OVS_DP_ATTR_UPCALL_PID, /* Netlink PID to receive upcalls */ OVS_DP_ATTR_STATS, /* struct ovs_dp_stats */ OVS_DP_ATTR_MEGAFLOW_STATS, /* struct ovs_dp_megaflow_stats */ OVS_DP_ATTR_USER_FEATURES, /* OVS_DP_F_* */ + /* private: */ OVS_DP_ATTR_PAD, + /* public: */ OVS_DP_ATTR_MASKS_CACHE_SIZE, OVS_DP_ATTR_PER_CPU_PIDS, /* Netlink PIDS to receive upcalls in * per-cpu dispatch mode */ OVS_DP_ATTR_IFINDEX, + /* private: */ __OVS_DP_ATTR_MAX }; @@ -181,6 +189,7 @@ enum ovs_packet_cmd { * %OVS_USERSPACE_ATTR_EGRESS_TUN_PORT attribute, which is sent only if the * output port is actually a tunnel port. Contains the output tunnel key * extracted from the packet as nested %OVS_TUNNEL_KEY_ATTR_* attributes. + * @OVS_PACKET_ATTR_PROBE: Packet operation is a feature probe. * @OVS_PACKET_ATTR_MRU: Present for an %OVS_PACKET_CMD_ACTION and * @OVS_PACKET_ATTR_LEN: Packet size before truncation. * %OVS_PACKET_ATTR_USERSPACE action specify the Maximum received fragment @@ -196,21 +205,26 @@ enum ovs_packet_cmd { * payload for %OVS_PACKET_* commands. */ enum ovs_packet_attr { + /* private: */ OVS_PACKET_ATTR_UNSPEC, + /* public: */ OVS_PACKET_ATTR_PACKET, /* Packet data. */ OVS_PACKET_ATTR_KEY, /* Nested OVS_KEY_ATTR_* attributes. */ OVS_PACKET_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_* attributes. */ OVS_PACKET_ATTR_USERDATA, /* OVS_ACTION_ATTR_USERSPACE arg. */ OVS_PACKET_ATTR_EGRESS_TUN_KEY, /* Nested OVS_TUNNEL_KEY_ATTR_* attributes. */ + /* private: */ OVS_PACKET_ATTR_UNUSED1, OVS_PACKET_ATTR_UNUSED2, + /* public: */ OVS_PACKET_ATTR_PROBE, /* Packet operation is a feature probe, error logging should be suppressed. */ OVS_PACKET_ATTR_MRU, /* Maximum received IP fragment size. */ OVS_PACKET_ATTR_LEN, /* Packet size before truncation. */ OVS_PACKET_ATTR_HASH, /* Packet hash. */ OVS_PACKET_ATTR_UPCALL_PID, /* u32 Netlink PID. */ + /* private: */ __OVS_PACKET_ATTR_MAX }; @@ -257,6 +271,11 @@ enum ovs_vport_type { * upcalls should not be sent. * @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for * packets sent or received through the vport. + * @OVS_VPORT_ATTR_IFINDEX: Provides the ifindex of a vport, or sets the desired + * ifindex while creating a new vport with type %OVS_VPORT_TYPE_INTERNAL. + * @OVS_VPORT_ATTR_NETNSID: Provides the netns id of the vport if it's not local. + * @OVS_VPORT_ATTR_UPCALL_STATS: Provides upcall statistics for a vport. + * Contains nested %OVS_VPORT_UPCALL_ATTR_* attributes. * * These attributes follow the &struct ovs_header within the Generic Netlink * payload for %OVS_VPORT_* commands. @@ -272,7 +291,9 @@ enum ovs_vport_type { * ovs_header plus %OVS_VPORT_ATTR_PORT_NO determine the vport. */ enum ovs_vport_attr { + /* private: */ OVS_VPORT_ATTR_UNSPEC, + /* public: */ OVS_VPORT_ATTR_PORT_NO, /* u32 port number within datapath */ OVS_VPORT_ATTR_TYPE, /* u32 OVS_VPORT_TYPE_* constant. */ OVS_VPORT_ATTR_NAME, /* string name, up to IFNAMSIZ bytes long */ @@ -280,23 +301,27 @@ enum ovs_vport_attr { OVS_VPORT_ATTR_UPCALL_PID, /* array of u32 Netlink socket PIDs for */ /* receiving upcalls */ OVS_VPORT_ATTR_STATS, /* struct ovs_vport_stats */ + /* private: */ OVS_VPORT_ATTR_PAD, + /* public: */ OVS_VPORT_ATTR_IFINDEX, OVS_VPORT_ATTR_NETNSID, OVS_VPORT_ATTR_UPCALL_STATS, + /* private: */ __OVS_VPORT_ATTR_MAX }; #define OVS_VPORT_ATTR_MAX (__OVS_VPORT_ATTR_MAX - 1) /** - * enum ovs_vport_upcall_attr - attributes for %OVS_VPORT_UPCALL* commands - * @OVS_VPORT_UPCALL_SUCCESS: 64-bit upcall success packets. - * @OVS_VPORT_UPCALL_FAIL: 64-bit upcall fail packets. + * enum ovs_vport_upcall_attr - attributes for %OVS_VPORT_ATTR_UPCALL_STATS + * @OVS_VPORT_UPCALL_ATTR_SUCCESS: 64-bit upcall success packets. + * @OVS_VPORT_UPCALL_ATTR_FAIL: 64-bit upcall fail packets. */ enum ovs_vport_upcall_attr { OVS_VPORT_UPCALL_ATTR_SUCCESS, OVS_VPORT_UPCALL_ATTR_FAIL, + /* private: */ __OVS_VPORT_UPCALL_ATTR_MAX }; @@ -431,6 +456,7 @@ enum ovs_frag_type { OVS_FRAG_TYPE_NONE, OVS_FRAG_TYPE_FIRST, OVS_FRAG_TYPE_LATER, + /* private: */ __OVS_FRAG_TYPE_MAX }; @@ -604,6 +630,8 @@ struct ovs_nsh_key_md1 { * a wildcarded match. Omitting attribute is treated as wildcarding all * corresponding fields. Optional for all requests. If not present, * all flow key bits are exact match bits. + * @OVS_FLOW_ATTR_PROBE: Flow operation is a feature probe, error logging + * should be suppressed. * @OVS_FLOW_ATTR_UFID: A value between 1-16 octets specifying a unique * identifier for the flow. Causes the flow to be indexed by this value rather * than the value of the %OVS_FLOW_ATTR_KEY attribute. Optional for all @@ -617,7 +645,9 @@ struct ovs_nsh_key_md1 { * payload for %OVS_FLOW_* commands. */ enum ovs_flow_attr { + /* private: */ OVS_FLOW_ATTR_UNSPEC, + /* public: */ OVS_FLOW_ATTR_KEY, /* Sequence of OVS_KEY_ATTR_* attributes. */ OVS_FLOW_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_* attributes. */ OVS_FLOW_ATTR_STATS, /* struct ovs_flow_stats. */ @@ -629,13 +659,14 @@ enum ovs_flow_attr { * logging should be suppressed. */ OVS_FLOW_ATTR_UFID, /* Variable length unique flow identifier. */ OVS_FLOW_ATTR_UFID_FLAGS,/* u32 of OVS_UFID_F_*. */ + /* private: */ OVS_FLOW_ATTR_PAD, __OVS_FLOW_ATTR_MAX }; #define OVS_FLOW_ATTR_MAX (__OVS_FLOW_ATTR_MAX - 1) -/** +/* * Omit attributes for notifications. * * If a datapath request contains an %OVS_UFID_F_OMIT_* flag, then the datapath @@ -653,17 +684,23 @@ enum ovs_flow_attr { * fractions of packets. * @OVS_SAMPLE_ATTR_ACTIONS: Set of actions to execute in sampling event. * Actions are passed as nested attributes. + * @OVS_SAMPLE_ATTR_ARG: For in-kernel use, passing &struct sample_arg + * derived from other attributes. * * Executes the specified actions with the given probability on a per-packet * basis. Nested actions will be able to access the probability value of the * parent @OVS_ACTION_ATTR_SAMPLE. */ enum ovs_sample_attr { + /* private: */ OVS_SAMPLE_ATTR_UNSPEC, + /* public: */ OVS_SAMPLE_ATTR_PROBABILITY, /* u32 number */ OVS_SAMPLE_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_* attributes. */ + /* private: */ __OVS_SAMPLE_ATTR_MAX, + /* public: */ #ifdef __KERNEL__ OVS_SAMPLE_ATTR_ARG /* struct sample_arg */ #endif @@ -693,12 +730,15 @@ struct sample_arg { * @OVS_USERSPACE_ATTR_ACTIONS: If present, send actions with upcall. */ enum ovs_userspace_attr { + /* private: */ OVS_USERSPACE_ATTR_UNSPEC, + /* public: */ OVS_USERSPACE_ATTR_PID, /* u32 Netlink PID to receive upcalls. */ OVS_USERSPACE_ATTR_USERDATA, /* Optional user-specified cookie. */ OVS_USERSPACE_ATTR_EGRESS_TUN_PORT, /* Optional, u32 output port * to get tunnel info. */ OVS_USERSPACE_ATTR_ACTIONS, /* Optional flag to get actions. */ + /* private: */ __OVS_USERSPACE_ATTR_MAX }; @@ -819,7 +859,9 @@ struct ovs_action_hash { * @OVS_CT_ATTR_TIMEOUT: Variable length string defining conntrack timeout. */ enum ovs_ct_attr { + /* private: */ OVS_CT_ATTR_UNSPEC, + /* public: */ OVS_CT_ATTR_COMMIT, /* No argument, commits connection. */ OVS_CT_ATTR_ZONE, /* u16 zone id. */ OVS_CT_ATTR_MARK, /* mark to associate with this connection. */ @@ -831,6 +873,7 @@ enum ovs_ct_attr { OVS_CT_ATTR_EVENTMASK, /* u32 mask of IPCT_* events. */ OVS_CT_ATTR_TIMEOUT, /* Associate timeout with this connection for * fine-grain timeout tuning. */ + /* private: */ __OVS_CT_ATTR_MAX }; @@ -859,7 +902,9 @@ enum ovs_ct_attr { * @OVS_NAT_ATTR_PROTO_RANDOM: Flag for fully randomized L4 port mapping */ enum ovs_nat_attr { + /* private: */ OVS_NAT_ATTR_UNSPEC, + /* public: */ OVS_NAT_ATTR_SRC, OVS_NAT_ATTR_DST, OVS_NAT_ATTR_IP_MIN, @@ -869,38 +914,44 @@ enum ovs_nat_attr { OVS_NAT_ATTR_PERSISTENT, OVS_NAT_ATTR_PROTO_HASH, OVS_NAT_ATTR_PROTO_RANDOM, + /* private: */ __OVS_NAT_ATTR_MAX, }; #define OVS_NAT_ATTR_MAX (__OVS_NAT_ATTR_MAX - 1) -/* +/** * struct ovs_action_push_eth - %OVS_ACTION_ATTR_PUSH_ETH action argument. * @addresses: Source and destination MAC addresses. - * @eth_type: Ethernet type */ struct ovs_action_push_eth { struct ovs_key_ethernet addresses; }; -/* +/** * enum ovs_check_pkt_len_attr - Attributes for %OVS_ACTION_ATTR_CHECK_PKT_LEN. * * @OVS_CHECK_PKT_LEN_ATTR_PKT_LEN: u16 Packet length to check for. * @OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER: Nested OVS_ACTION_ATTR_* * actions to apply if the packer length is greater than the specified * length in the attr - OVS_CHECK_PKT_LEN_ATTR_PKT_LEN. - * @OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL - Nested OVS_ACTION_ATTR_* + * @OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL: Nested OVS_ACTION_ATTR_* * actions to apply if the packer length is lesser or equal to the specified * length in the attr - OVS_CHECK_PKT_LEN_ATTR_PKT_LEN. + * @OVS_CHECK_PKT_LEN_ATTR_ARG: For in-kernel use, passing &struct + * check_pkt_len_arg derived from other attributes. */ enum ovs_check_pkt_len_attr { + /* private: */ OVS_CHECK_PKT_LEN_ATTR_UNSPEC, + /* public: */ OVS_CHECK_PKT_LEN_ATTR_PKT_LEN, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, + /* private: */ __OVS_CHECK_PKT_LEN_ATTR_MAX, + /* public: */ #ifdef __KERNEL__ OVS_CHECK_PKT_LEN_ATTR_ARG /* struct check_pkt_len_arg */ #endif @@ -968,6 +1019,9 @@ enum ovs_psample_attr { * from the packet. * @OVS_ACTION_ATTR_SAMPLE: Probabilitically executes actions, as specified in * the nested %OVS_SAMPLE_ATTR_* attributes. + * @OVS_ACTION_ATTR_RECIRC: Recirculate the clone of the packet through the + * datapath with the new id (u32 recirc_id). + * @OVS_ACTION_ATTR_HASH: Compute the packet hash, using &struct ovs_action_hash. * @OVS_ACTION_ATTR_PUSH_MPLS: Push a new MPLS label stack entry onto the * top of the packets MPLS label stack. Set the ethertype of the * encapsulating frame to either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC to @@ -997,6 +1051,8 @@ enum ovs_psample_attr { * start of the packet or at the start of the l3 header depending on the value * of l3 tunnel flag in the tun_flags field of OVS_ACTION_ATTR_ADD_MPLS * argument. + * @OVS_ACTION_ATTR_DEC_TTL: Decrement TTL or hop limit of the packet. Execute + * nested %OVS_DEC_TTL_ATTR_* actions if the value is less or equal to 1. * @OVS_ACTION_ATTR_DROP: Explicit drop action. * @OVS_ACTION_ATTR_PSAMPLE: Send a sample of the packet to external observers * via psample. @@ -1010,7 +1066,9 @@ enum ovs_psample_attr { */ enum ovs_action_attr { + /* private: */ OVS_ACTION_ATTR_UNSPEC, + /* public: */ OVS_ACTION_ATTR_OUTPUT, /* u32 port number. */ OVS_ACTION_ATTR_USERSPACE, /* Nested OVS_USERSPACE_ATTR_*. */ OVS_ACTION_ATTR_SET, /* One nested OVS_KEY_ATTR_*. */ @@ -1040,9 +1098,11 @@ enum ovs_action_attr { OVS_ACTION_ATTR_DROP, /* u32 error code. */ OVS_ACTION_ATTR_PSAMPLE, /* Nested OVS_PSAMPLE_ATTR_*. */ + /* private: */ __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ + /* public: */ #ifdef __KERNEL__ OVS_ACTION_ATTR_SET_TO_MASKED, /* Kernel module internal masked * set action converted from -- cgit v1.2.3 From cc39325f927850473d3a84b029ae6f9b508e9bd1 Mon Sep 17 00:00:00 2001 From: Mohsin Bashir Date: Mon, 2 Mar 2026 15:01:45 -0800 Subject: net: ethtool: Track pause storm events With TX pause enabled, if a device is unable to pass packets up to the stack (e.g., CPU is hanged), the device can cause pause storm. Given that devices can have native support to protect the neighbor from such flooding, such events need some tracking. This support is to track TX pause storm events for better observability. Reviewed-by: Oleksij Rempel Signed-off-by: Jakub Kicinski Signed-off-by: Mohsin Bashir Link: https://patch.msgid.link/20260302230149.1580195-2-mohsin.bashr@gmail.com Signed-off-by: Paolo Abeni --- Documentation/netlink/specs/ethtool.yaml | 13 +++++++++++++ include/linux/ethtool.h | 2 ++ include/uapi/linux/ethtool_netlink_generated.h | 1 + net/ethtool/pause.c | 4 +++- 4 files changed, 19 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml index 0a2d2343f79a..4707063af3b4 100644 --- a/Documentation/netlink/specs/ethtool.yaml +++ b/Documentation/netlink/specs/ethtool.yaml @@ -879,6 +879,19 @@ attribute-sets: - name: rx-frames type: u64 + - + name: tx-pause-storm-events + type: u64 + doc: >- + TX pause storm event count. Increments each time device + detects that its pause assertion condition has been true + for too long for normal operation. As a result, the device + has temporarily disabled its own Pause TX function to + protect the network from itself. + This counter should never increment under normal overload + conditions; it indicates catastrophic failure like an OS + crash. The rate of incrementing is implementation specific. + - name: pause attr-cnt-name: __ethtool-a-pause-cnt diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 798abec67a1b..83c375840835 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -512,12 +512,14 @@ struct ethtool_eth_ctrl_stats { * * Equivalent to `30.3.4.3 aPAUSEMACCtrlFramesReceived` * from the standard. + * @tx_pause_storm_events: TX pause storm event count (see ethtool.yaml). */ struct ethtool_pause_stats { enum ethtool_mac_stats_src src; struct_group(stats, u64 tx_pause_frames; u64 rx_pause_frames; + u64 tx_pause_storm_events; ); }; diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h index 556a0c834df5..114b83017297 100644 --- a/include/uapi/linux/ethtool_netlink_generated.h +++ b/include/uapi/linux/ethtool_netlink_generated.h @@ -381,6 +381,7 @@ enum { ETHTOOL_A_PAUSE_STAT_PAD, ETHTOOL_A_PAUSE_STAT_TX_FRAMES, ETHTOOL_A_PAUSE_STAT_RX_FRAMES, + ETHTOOL_A_PAUSE_STAT_TX_PAUSE_STORM_EVENTS, __ETHTOOL_A_PAUSE_STAT_CNT, ETHTOOL_A_PAUSE_STAT_MAX = (__ETHTOOL_A_PAUSE_STAT_CNT - 1) diff --git a/net/ethtool/pause.c b/net/ethtool/pause.c index 0f9af1e66548..5d28f642764c 100644 --- a/net/ethtool/pause.c +++ b/net/ethtool/pause.c @@ -130,7 +130,9 @@ static int pause_put_stats(struct sk_buff *skb, if (ethtool_put_stat(skb, pause_stats->tx_pause_frames, ETHTOOL_A_PAUSE_STAT_TX_FRAMES, pad) || ethtool_put_stat(skb, pause_stats->rx_pause_frames, - ETHTOOL_A_PAUSE_STAT_RX_FRAMES, pad)) + ETHTOOL_A_PAUSE_STAT_RX_FRAMES, pad) || + ethtool_put_stat(skb, pause_stats->tx_pause_storm_events, + ETHTOOL_A_PAUSE_STAT_TX_PAUSE_STORM_EVENTS, pad)) goto err_cancel; nla_nest_end(skb, nest); -- cgit v1.2.3 From 817de93c348a3086ecca6e03ff459138832157cc Mon Sep 17 00:00:00 2001 From: Mohsin Bashir Date: Mon, 2 Mar 2026 15:01:46 -0800 Subject: net: ethtool: Update doc for tunable ETHTOOL_PFC_PREVENTION_TOUT enables the configuration of timeout value for PFC storm prevention. This can also be used to configure storm detection timeout for global pause settings. In fact some existing drivers are already using it for the said purpose. Highlight that the knob can formally be used to configure timeout value for pause storm prevention mechanism. The update to the ethtool man page will follow afterwards. Link: https://lore.kernel.org/aa5f189a-ac62-4633-97b5-ebf939e9c535@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Mohsin Bashir Link: https://patch.msgid.link/20260302230149.1580195-3-mohsin.bashr@gmail.com Signed-off-by: Paolo Abeni --- include/uapi/linux/ethtool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index b74b80508553..1cdfb8341df2 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -225,7 +225,7 @@ enum tunable_id { ETHTOOL_ID_UNSPEC, ETHTOOL_RX_COPYBREAK, ETHTOOL_TX_COPYBREAK, - ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */ + ETHTOOL_PFC_PREVENTION_TOUT, /* both pause and pfc, see man ethtool */ ETHTOOL_TX_COPYBREAK_BUF_SIZE, /* * Add your fresh new tunable attribute above and remember to update -- cgit v1.2.3 From f3e334fb7f82cd63734faeb395419ab713b4bb5c Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Tue, 3 Mar 2026 10:35:28 -0300 Subject: audit: fix coding style issues Fix various coding style issues across the audit subsystem flagged by checkpatch.pl script to adhere to kernel coding standards. Specific changes include: - kernel/auditfilter.c: Move the open brace '{' to the previous line for the audit_ops array declaration. - lib/audit.c: Add a required space before the open parenthesis '('. - include/uapi/linux/audit.h: Enclose the complex macro value for AUDIT_UID_UNSET in parentheses. Signed-off-by: Ricardo Robaina Signed-off-by: Paul Moore --- include/uapi/linux/audit.h | 2 +- kernel/auditfilter.c | 3 +-- lib/audit.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 71cbdc542ce9..e8f5ce677df7 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -508,7 +508,7 @@ struct audit_tty_status { __u32 log_passwd; /* 1 = enabled, 0 = disabled */ }; -#define AUDIT_UID_UNSET (unsigned int)-1 +#define AUDIT_UID_UNSET ((unsigned int)-1) #define AUDIT_SID_UNSET ((unsigned int)-1) /* audit_rule_data supports filter rules with both integer and string diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 6e3abbf08e3d..093425123f6c 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -303,8 +303,7 @@ exit_err: return ERR_PTR(err); } -static u32 audit_ops[] = -{ +static u32 audit_ops[] = { [Audit_equal] = AUDIT_EQUAL, [Audit_not_equal] = AUDIT_NOT_EQUAL, [Audit_bitmask] = AUDIT_BIT_MASK, diff --git a/lib/audit.c b/lib/audit.c index 738bda22dd39..bc07fbd3a698 100644 --- a/lib/audit.c +++ b/lib/audit.c @@ -42,7 +42,7 @@ int audit_classify_syscall(int abi, unsigned syscall) if (audit_is_compat(abi)) return audit_classify_compat_syscall(abi, syscall); - switch(syscall) { + switch (syscall) { #ifdef __NR_open case __NR_open: return AUDITSC_OPEN; -- cgit v1.2.3 From c698f5cc940de5871ea3c65c94f5fd7fbc6844e3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 5 Mar 2026 11:48:29 +0000 Subject: inet_diag: report delayed ack timer information inet_sk_diag_fill() populates r->idiag_timer with the following precedence order: 1 - Retransmit timer. 4 - Probe0 timer. 2 - Keepalive timer. This patch adds a new value, last in the list, if other timers are not active. 5 - Delayed ACK timer. A corresponding iproute2 patch will follow to replace "unknown" with "delack": ESTAB 10 0 [2002:a05:6830:1f86::]:12875 [2002:a05:6830:1f85::]:50438 timer:(unknown,003ms,0) ino:152178 sk:3004 cgroup:unreachable:189 <-> skmem:(r1344,rb12780520,t0,tb262144,f2752,w0,o250,bl0,d0) ts usec_ts ... Also add the following enum in uapi/linux/inet_diag.h as suggested by David Ahern. enum { IDIAG_TIMER_OFF, IDIAG_TIMER_ON, IDIAG_TIMER_KEEPALIVE, IDIAG_TIMER_TIMEWAIT, IDIAG_TIMER_PROBE0, IDIAG_TIMER_DELACK, }; Neal Cardwell suggested to test for ICSK_ACK_TIMER: inet_csk_clear_xmit_timer() does not call sk_stop_timer() because INET_CSK_CLEAR_TIMERS is unset. Signed-off-by: Eric Dumazet Reviewed-by: David Ahern Reviewed-by: Neal Cardwell Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260305114829.2163276-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- include/uapi/linux/inet_diag.h | 9 +++++++++ net/ipv4/inet_diag.c | 13 +++++++++---- net/ipv4/tcp_diag.c | 4 ++-- 3 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/inet_diag.h b/include/uapi/linux/inet_diag.h index 86bb2e8b17c9..21f0d735fbae 100644 --- a/include/uapi/linux/inet_diag.h +++ b/include/uapi/linux/inet_diag.h @@ -129,6 +129,15 @@ struct inet_diag_msg { __u32 idiag_inode; }; +enum { + IDIAG_TIMER_OFF, + IDIAG_TIMER_ON, + IDIAG_TIMER_KEEPALIVE, + IDIAG_TIMER_TIMEWAIT, + IDIAG_TIMER_PROBE0, + IDIAG_TIMER_DELACK, +}; + /* Extensions */ enum { diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 9d215485b5c7..34b77aa87d0a 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -241,7 +241,7 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, inet_diag_msg_common_fill(r, sk); r->idiag_state = sk->sk_state; - r->idiag_timer = 0; + r->idiag_timer = IDIAG_TIMER_OFF; r->idiag_retrans = 0; r->idiag_expires = 0; @@ -284,20 +284,25 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, if (icsk_pending == ICSK_TIME_RETRANS || icsk_pending == ICSK_TIME_REO_TIMEOUT || icsk_pending == ICSK_TIME_LOSS_PROBE) { - r->idiag_timer = 1; + r->idiag_timer = IDIAG_TIMER_ON; r->idiag_retrans = READ_ONCE(icsk->icsk_retransmits); r->idiag_expires = jiffies_delta_to_msecs(tcp_timeout_expires(sk) - jiffies); } else if (icsk_pending == ICSK_TIME_PROBE0) { - r->idiag_timer = 4; + r->idiag_timer = IDIAG_TIMER_PROBE0; r->idiag_retrans = READ_ONCE(icsk->icsk_probes_out); r->idiag_expires = jiffies_delta_to_msecs(tcp_timeout_expires(sk) - jiffies); } else if (timer_pending(&icsk->icsk_keepalive_timer)) { - r->idiag_timer = 2; + r->idiag_timer = IDIAG_TIMER_KEEPALIVE; r->idiag_retrans = READ_ONCE(icsk->icsk_probes_out); r->idiag_expires = jiffies_delta_to_msecs(icsk->icsk_keepalive_timer.expires - jiffies); + } else if ((READ_ONCE(icsk->icsk_ack.pending) & ICSK_ACK_TIMER) && + timer_pending(&icsk->icsk_delack_timer)) { + r->idiag_timer = IDIAG_TIMER_DELACK; + r->idiag_expires = + jiffies_delta_to_msecs(icsk_delack_timeout(icsk) - jiffies); } if ((ext & (1 << (INET_DIAG_INFO - 1))) && handler->idiag_info_size) { diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index 7935702e394b..ba1fdbe9807f 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -212,7 +212,7 @@ static int tcp_twsk_diag_fill(struct sock *sk, r->idiag_retrans = 0; r->idiag_state = READ_ONCE(tw->tw_substate); - r->idiag_timer = 3; + r->idiag_timer = IDIAG_TIMER_TIMEWAIT; tmo = tw->tw_timer.expires - jiffies; r->idiag_expires = jiffies_delta_to_msecs(tmo); r->idiag_rqueue = 0; @@ -247,7 +247,7 @@ static int tcp_req_diag_fill(struct sock *sk, struct sk_buff *skb, r = nlmsg_data(nlh); inet_diag_msg_common_fill(r, sk); r->idiag_state = TCP_SYN_RECV; - r->idiag_timer = 1; + r->idiag_timer = IDIAG_TIMER_ON; r->idiag_retrans = READ_ONCE(reqsk->num_retrans); BUILD_BUG_ON(offsetof(struct inet_request_sock, ir_cookie) != -- cgit v1.2.3 From d9d2455e77d0f36a22b9dbaba8b6354dd1378101 Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Sun, 15 Feb 2026 23:31:20 +0000 Subject: io_uring/zcrx: move zcrx uapi into separate header Split out zcrx uapi into a separate file. It'll be easier to manage it this way, and that reduces the size of a not so small io_uring.h. Since there are users that expect that zcrx definitions come with io_uring.h, it includes the new file. Signed-off-by: Pavel Begunkov Signed-off-by: Jens Axboe --- include/uapi/linux/io_uring.h | 96 +-------------------------------- include/uapi/linux/io_uring/zcrx.h | 108 +++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 94 deletions(-) create mode 100644 include/uapi/linux/io_uring/zcrx.h (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 1ff16141c8a5..17475c2045fb 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -10,6 +10,8 @@ #include #include +#include + /* * this file is shared with liburing and that has to autodetect * if linux/time_types.h is available or not, it can @@ -1050,100 +1052,6 @@ struct io_timespec { __u64 tv_nsec; }; -/* Zero copy receive refill queue entry */ -struct io_uring_zcrx_rqe { - __u64 off; - __u32 len; - __u32 __pad; -}; - -struct io_uring_zcrx_cqe { - __u64 off; - __u64 __pad; -}; - -/* The bit from which area id is encoded into offsets */ -#define IORING_ZCRX_AREA_SHIFT 48 -#define IORING_ZCRX_AREA_MASK (~(((__u64)1 << IORING_ZCRX_AREA_SHIFT) - 1)) - -struct io_uring_zcrx_offsets { - __u32 head; - __u32 tail; - __u32 rqes; - __u32 __resv2; - __u64 __resv[2]; -}; - -enum io_uring_zcrx_area_flags { - IORING_ZCRX_AREA_DMABUF = 1, -}; - -struct io_uring_zcrx_area_reg { - __u64 addr; - __u64 len; - __u64 rq_area_token; - __u32 flags; - __u32 dmabuf_fd; - __u64 __resv2[2]; -}; - -enum zcrx_reg_flags { - ZCRX_REG_IMPORT = 1, -}; - -enum zcrx_features { - /* - * The user can ask for the desired rx page size by passing the - * value in struct io_uring_zcrx_ifq_reg::rx_buf_len. - */ - ZCRX_FEATURE_RX_PAGE_SIZE = 1 << 0, -}; - -/* - * Argument for IORING_REGISTER_ZCRX_IFQ - */ -struct io_uring_zcrx_ifq_reg { - __u32 if_idx; - __u32 if_rxq; - __u32 rq_entries; - __u32 flags; - - __u64 area_ptr; /* pointer to struct io_uring_zcrx_area_reg */ - __u64 region_ptr; /* struct io_uring_region_desc * */ - - struct io_uring_zcrx_offsets offsets; - __u32 zcrx_id; - __u32 rx_buf_len; - __u64 __resv[3]; -}; - -enum zcrx_ctrl_op { - ZCRX_CTRL_FLUSH_RQ, - ZCRX_CTRL_EXPORT, - - __ZCRX_CTRL_LAST, -}; - -struct zcrx_ctrl_flush_rq { - __u64 __resv[6]; -}; - -struct zcrx_ctrl_export { - __u32 zcrx_fd; - __u32 __resv1[11]; -}; - -struct zcrx_ctrl { - __u32 zcrx_id; - __u32 op; /* see enum zcrx_ctrl_op */ - __u64 __resv[2]; - - union { - struct zcrx_ctrl_export zc_export; - struct zcrx_ctrl_flush_rq zc_flush; - }; -}; - #ifdef __cplusplus } #endif diff --git a/include/uapi/linux/io_uring/zcrx.h b/include/uapi/linux/io_uring/zcrx.h new file mode 100644 index 000000000000..3163a4b8aeb0 --- /dev/null +++ b/include/uapi/linux/io_uring/zcrx.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR MIT */ +/* + * Header file for the io_uring zerocopy receive (zcrx) interface. + * + * Copyright (C) 2026 Pavel Begunkov + * Copyright (C) 2026 David Wei + * Copyright (C) Meta Platforms, Inc. + */ +#ifndef LINUX_IO_ZCRX_H +#define LINUX_IO_ZCRX_H + +#include + +/* Zero copy receive refill queue entry */ +struct io_uring_zcrx_rqe { + __u64 off; + __u32 len; + __u32 __pad; +}; + +struct io_uring_zcrx_cqe { + __u64 off; + __u64 __pad; +}; + +/* The bit from which area id is encoded into offsets */ +#define IORING_ZCRX_AREA_SHIFT 48 +#define IORING_ZCRX_AREA_MASK (~(((__u64)1 << IORING_ZCRX_AREA_SHIFT) - 1)) + +struct io_uring_zcrx_offsets { + __u32 head; + __u32 tail; + __u32 rqes; + __u32 __resv2; + __u64 __resv[2]; +}; + +enum io_uring_zcrx_area_flags { + IORING_ZCRX_AREA_DMABUF = 1, +}; + +struct io_uring_zcrx_area_reg { + __u64 addr; + __u64 len; + __u64 rq_area_token; + __u32 flags; + __u32 dmabuf_fd; + __u64 __resv2[2]; +}; + +enum zcrx_reg_flags { + ZCRX_REG_IMPORT = 1, +}; + +enum zcrx_features { + /* + * The user can ask for the desired rx page size by passing the + * value in struct io_uring_zcrx_ifq_reg::rx_buf_len. + */ + ZCRX_FEATURE_RX_PAGE_SIZE = 1 << 0, +}; + +/* + * Argument for IORING_REGISTER_ZCRX_IFQ + */ +struct io_uring_zcrx_ifq_reg { + __u32 if_idx; + __u32 if_rxq; + __u32 rq_entries; + __u32 flags; + + __u64 area_ptr; /* pointer to struct io_uring_zcrx_area_reg */ + __u64 region_ptr; /* struct io_uring_region_desc * */ + + struct io_uring_zcrx_offsets offsets; + __u32 zcrx_id; + __u32 rx_buf_len; + __u64 __resv[3]; +}; + +enum zcrx_ctrl_op { + ZCRX_CTRL_FLUSH_RQ, + ZCRX_CTRL_EXPORT, + + __ZCRX_CTRL_LAST, +}; + +struct zcrx_ctrl_flush_rq { + __u64 __resv[6]; +}; + +struct zcrx_ctrl_export { + __u32 zcrx_fd; + __u32 __resv1[11]; +}; + +struct zcrx_ctrl { + __u32 zcrx_id; + __u32 op; /* see enum zcrx_ctrl_op */ + __u64 __resv[2]; + + union { + struct zcrx_ctrl_export zc_export; + struct zcrx_ctrl_flush_rq zc_flush; + }; +}; + +#endif /* LINUX_IO_ZCRX_H */ -- cgit v1.2.3 From d8345a21902af5d754f2c2aadf877de989e3cac3 Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Mon, 2 Mar 2026 13:10:37 +0000 Subject: io_uring/timeout: immediate timeout arg One the things the user has always keep in mind is that any user pointers they put into an SQE is not going to be read by the kernel until submission happens, and the user has to ensure the pointee stays alive until then. For example, snippet below will lead to UAF of the on stack variable ts. Instead of passing the timeout value as a pointer allow to store it immediately in the SQE. The user has to set a new flag called IORING_TIMEOUT_IMMEDIATE_ARG, in which case sqe->addr for timeout or sqe->addr2 for timeout update requests will be interpreted as a time value in nanosecods. void prep_timeout(struct io_uring_sqe *sqe) { struct __kernel_timespec ts = {...}; prep_timeout(sqe, &ts); } void submit() { sqe = get_sqe(); prep_timeout(sqe); io_uring_submit(); } Signed-off-by: Pavel Begunkov Signed-off-by: Jens Axboe --- include/uapi/linux/io_uring.h | 5 +++++ io_uring/timeout.c | 20 +++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 17475c2045fb..17ac1b785440 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -343,6 +343,10 @@ enum io_uring_op { /* * sqe->timeout_flags + * + * IORING_TIMEOUT_IMMEDIATE_ARG: If set, sqe->addr stores the timeout + * value in nanoseconds instead of + * pointing to a timespec. */ #define IORING_TIMEOUT_ABS (1U << 0) #define IORING_TIMEOUT_UPDATE (1U << 1) @@ -351,6 +355,7 @@ enum io_uring_op { #define IORING_LINK_TIMEOUT_UPDATE (1U << 4) #define IORING_TIMEOUT_ETIME_SUCCESS (1U << 5) #define IORING_TIMEOUT_MULTISHOT (1U << 6) +#define IORING_TIMEOUT_IMMEDIATE_ARG (1U << 7) #define IORING_TIMEOUT_CLOCK_MASK (IORING_TIMEOUT_BOOTTIME | IORING_TIMEOUT_REALTIME) #define IORING_TIMEOUT_UPDATE_MASK (IORING_TIMEOUT_UPDATE | IORING_LINK_TIMEOUT_UPDATE) /* diff --git a/io_uring/timeout.c b/io_uring/timeout.c index 4b67746ea3ca..8eddf8add7a2 100644 --- a/io_uring/timeout.c +++ b/io_uring/timeout.c @@ -35,10 +35,17 @@ struct io_timeout_rem { bool ltimeout; }; -static int io_parse_user_time(ktime_t *time, u64 arg) +static int io_parse_user_time(ktime_t *time, u64 arg, unsigned flags) { struct timespec64 ts; + if (flags & IORING_TIMEOUT_IMMEDIATE_ARG) { + *time = ns_to_ktime(arg); + if (*time < 0) + return -EINVAL; + return 0; + } + if (get_timespec64(&ts, u64_to_user_ptr(arg))) return -EFAULT; if (ts.tv_sec < 0 || ts.tv_nsec < 0) @@ -475,9 +482,11 @@ int io_timeout_remove_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) return -EINVAL; if (tr->flags & IORING_LINK_TIMEOUT_UPDATE) tr->ltimeout = true; - if (tr->flags & ~(IORING_TIMEOUT_UPDATE_MASK|IORING_TIMEOUT_ABS)) + if (tr->flags & ~(IORING_TIMEOUT_UPDATE_MASK | + IORING_TIMEOUT_ABS | + IORING_TIMEOUT_IMMEDIATE_ARG)) return -EINVAL; - ret = io_parse_user_time(&tr->time, READ_ONCE(sqe->addr2)); + ret = io_parse_user_time(&tr->time, READ_ONCE(sqe->addr2), tr->flags); if (ret) return ret; } else if (tr->flags) { @@ -545,7 +554,8 @@ static int __io_timeout_prep(struct io_kiocb *req, flags = READ_ONCE(sqe->timeout_flags); if (flags & ~(IORING_TIMEOUT_ABS | IORING_TIMEOUT_CLOCK_MASK | IORING_TIMEOUT_ETIME_SUCCESS | - IORING_TIMEOUT_MULTISHOT)) + IORING_TIMEOUT_MULTISHOT | + IORING_TIMEOUT_IMMEDIATE_ARG)) return -EINVAL; /* more than one clock specified is invalid, obviously */ if (hweight32(flags & IORING_TIMEOUT_CLOCK_MASK) > 1) @@ -574,7 +584,7 @@ static int __io_timeout_prep(struct io_kiocb *req, data->req = req; data->flags = flags; - ret = io_parse_user_time(&data->time, READ_ONCE(sqe->addr)); + ret = io_parse_user_time(&data->time, READ_ONCE(sqe->addr), flags); if (ret) return ret; -- cgit v1.2.3 From 7d776a36277ff2685ffc3dc7eff32002d0333ac9 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Mon, 9 Mar 2026 16:24:47 +0000 Subject: ring-buffer: Add page statistics to the meta-page Add two fields pages_touched and pages_lost to the ring-buffer meta-page. Those fields are useful to get the number of used pages in the ring-buffer. Link: https://patch.msgid.link/20260309162516.2623589-2-vdonnefort@google.com Reviewed-by: Steven Rostedt (Google) Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt (Google) --- include/uapi/linux/trace_mmap.h | 8 ++++---- kernel/trace/ring_buffer.c | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/trace_mmap.h b/include/uapi/linux/trace_mmap.h index c102ef35d11e..e8185889a1c8 100644 --- a/include/uapi/linux/trace_mmap.h +++ b/include/uapi/linux/trace_mmap.h @@ -17,8 +17,8 @@ * @entries: Number of entries in the ring-buffer. * @overrun: Number of entries lost in the ring-buffer. * @read: Number of entries that have been read. - * @Reserved1: Internal use only. - * @Reserved2: Internal use only. + * @pages_lost: Number of pages overwritten by the writer. + * @pages_touched: Number of pages written by the writer. */ struct trace_buffer_meta { __u32 meta_page_size; @@ -39,8 +39,8 @@ struct trace_buffer_meta { __u64 overrun; __u64 read; - __u64 Reserved1; - __u64 Reserved2; + __u64 pages_lost; + __u64 pages_touched; }; #define TRACE_MMAP_IOCTL_GET_READER _IO('R', 0x20) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 17d0ea0cc3e6..82b4df579670 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -6154,6 +6154,8 @@ static void rb_update_meta_page(struct ring_buffer_per_cpu *cpu_buffer) meta->entries = local_read(&cpu_buffer->entries); meta->overrun = local_read(&cpu_buffer->overrun); meta->read = cpu_buffer->read; + meta->pages_lost = local_read(&cpu_buffer->pages_lost); + meta->pages_touched = local_read(&cpu_buffer->pages_touched); /* Some archs do not have data cache coherency between kernel and user-space */ flush_kernel_vmap_range(cpu_buffer->meta_page, PAGE_SIZE); -- cgit v1.2.3 From aca086ff27c3f67e81617e4b063d1126544a4f19 Mon Sep 17 00:00:00 2001 From: Ondrej Kozina Date: Fri, 6 Feb 2026 15:17:58 +0100 Subject: sed-opal: add IOC_OPAL_REACTIVATE_LSP. This adds the 'Reactivate' method as described in the "TCG Storage Opal SSC Feature Set: Single User Mode" document (ch. 3.1.1.1). The method enables switching an already active SED OPAL2 device, with appropriate firmware support for Single User Mode (SUM), to or from SUM. Signed-off-by: Ondrej Kozina Reviewed-and-tested-by: Milan Broz Reviewed-by: Hannes Reinecke Signed-off-by: Jens Axboe --- block/opal_proto.h | 1 + block/sed-opal.c | 99 +++++++++++++++++++++++++++++++++++++++++++ include/linux/sed-opal.h | 1 + include/uapi/linux/sed-opal.h | 14 ++++++ 4 files changed, 115 insertions(+) (limited to 'include/uapi/linux') diff --git a/block/opal_proto.h b/block/opal_proto.h index 3ccee5977c10..d138785b8198 100644 --- a/block/opal_proto.h +++ b/block/opal_proto.h @@ -155,6 +155,7 @@ enum opal_method { OPAL_AUTHENTICATE, OPAL_RANDOM, OPAL_ERASE, + OPAL_REACTIVATE, }; enum opal_token { diff --git a/block/sed-opal.c b/block/sed-opal.c index 83bee47aa29f..5d06f5f433bf 100644 --- a/block/sed-opal.c +++ b/block/sed-opal.c @@ -220,6 +220,8 @@ static const u8 opalmethod[][OPAL_METHOD_LENGTH] = { { 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x01 }, [OPAL_ERASE] = { 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x03 }, + [OPAL_REACTIVATE] = + { 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x01 }, }; static int end_opal_session_error(struct opal_dev *dev); @@ -2287,6 +2289,74 @@ static int activate_lsp(struct opal_dev *dev, void *data) return finalize_and_send(dev, parse_and_check_status); } +static int reactivate_lsp(struct opal_dev *dev, void *data) +{ + struct opal_lr_react *opal_react = data; + u8 user_lr[OPAL_UID_LENGTH]; + int err, i; + + err = cmd_start(dev, opaluid[OPAL_THISSP_UID], + opalmethod[OPAL_REACTIVATE]); + + if (err) { + pr_debug("Error building Reactivate LockingSP command.\n"); + return err; + } + + /* + * If neither 'entire_table' nor 'num_lrs' is set, the device + * gets reactivated with SUM disabled. Only Admin1PIN will change + * if set. + */ + if (opal_react->entire_table) { + /* Entire Locking table (all locking ranges) will be put in SUM. */ + add_token_u8(&err, dev, OPAL_STARTNAME); + add_token_u64(&err, dev, OPAL_SUM_SET_LIST); + add_token_bytestring(&err, dev, opaluid[OPAL_LOCKING_TABLE], OPAL_UID_LENGTH); + add_token_u8(&err, dev, OPAL_ENDNAME); + } else if (opal_react->num_lrs) { + /* Subset of Locking table (selected locking range(s)) to be put in SUM */ + err = build_locking_range(user_lr, sizeof(user_lr), + opal_react->lr[0]); + if (err) + return err; + + add_token_u8(&err, dev, OPAL_STARTNAME); + add_token_u64(&err, dev, OPAL_SUM_SET_LIST); + + add_token_u8(&err, dev, OPAL_STARTLIST); + add_token_bytestring(&err, dev, user_lr, OPAL_UID_LENGTH); + for (i = 1; i < opal_react->num_lrs; i++) { + user_lr[7] = opal_react->lr[i]; + add_token_bytestring(&err, dev, user_lr, OPAL_UID_LENGTH); + } + add_token_u8(&err, dev, OPAL_ENDLIST); + add_token_u8(&err, dev, OPAL_ENDNAME); + } + + /* Skipping the rangle policy parameter is same as setting its value to zero */ + if (opal_react->range_policy && (opal_react->num_lrs || opal_react->entire_table)) { + add_token_u8(&err, dev, OPAL_STARTNAME); + add_token_u64(&err, dev, OPAL_SUM_RANGE_POLICY); + add_token_u8(&err, dev, 1); + add_token_u8(&err, dev, OPAL_ENDNAME); + } + + /* + * Optional parameter. If set, it changes the Admin1 PIN even when SUM + * is being disabled. + */ + if (opal_react->new_admin_key.key_len) { + add_token_u8(&err, dev, OPAL_STARTNAME); + add_token_u64(&err, dev, OPAL_SUM_ADMIN1_PIN); + add_token_bytestring(&err, dev, opal_react->new_admin_key.key, + opal_react->new_admin_key.key_len); + add_token_u8(&err, dev, OPAL_ENDNAME); + } + + return finalize_and_send(dev, parse_and_check_status); +} + /* Determine if we're in the Manufactured Inactive or Active state */ static int get_lsp_lifecycle(struct opal_dev *dev, void *data) { @@ -2957,6 +3027,32 @@ static int opal_activate_lsp(struct opal_dev *dev, return ret; } +static int opal_reactivate_lsp(struct opal_dev *dev, + struct opal_lr_react *opal_lr_react) +{ + const struct opal_step active_steps[] = { + { start_admin1LSP_opal_session, &opal_lr_react->key }, + { reactivate_lsp, opal_lr_react }, + /* No end_opal_session. The controller terminates the session */ + }; + int ret; + + /* use either 'entire_table' parameter or set of locking ranges */ + if (opal_lr_react->num_lrs > OPAL_MAX_LRS || + (opal_lr_react->num_lrs && opal_lr_react->entire_table)) + return -EINVAL; + + ret = opal_get_key(dev, &opal_lr_react->key); + if (ret) + return ret; + mutex_lock(&dev->dev_lock); + setup_opal_dev(dev); + ret = execute_steps(dev, active_steps, ARRAY_SIZE(active_steps)); + mutex_unlock(&dev->dev_lock); + + return ret; +} + static int opal_setup_locking_range(struct opal_dev *dev, struct opal_user_lr_setup *opal_lrs) { @@ -3315,6 +3411,9 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg) case IOC_OPAL_SET_SID_PW: ret = opal_set_new_sid_pw(dev, p); break; + case IOC_OPAL_REACTIVATE_LSP: + ret = opal_reactivate_lsp(dev, p); + break; default: break; diff --git a/include/linux/sed-opal.h b/include/linux/sed-opal.h index 80f33a93f944..2ae5e6b0ac21 100644 --- a/include/linux/sed-opal.h +++ b/include/linux/sed-opal.h @@ -53,6 +53,7 @@ static inline bool is_sed_ioctl(unsigned int cmd) case IOC_OPAL_DISCOVERY: case IOC_OPAL_REVERT_LSP: case IOC_OPAL_SET_SID_PW: + case IOC_OPAL_REACTIVATE_LSP: return true; } return false; diff --git a/include/uapi/linux/sed-opal.h b/include/uapi/linux/sed-opal.h index 9025dd5a4f0f..d03e590b6501 100644 --- a/include/uapi/linux/sed-opal.h +++ b/include/uapi/linux/sed-opal.h @@ -74,6 +74,19 @@ struct opal_lr_act { __u8 align[2]; /* Align to 8 byte boundary */ }; +struct opal_lr_react { + struct opal_key key; + struct opal_key new_admin_key; /* Set new Admin1 PIN if key_len is > 0 */ + __u8 num_lrs; /* + * Configure selected ranges (from lr[]) in SUM. + * If num_lrs > 0 the 'entire_table' must be 0 + */ + __u8 lr[OPAL_MAX_LRS]; + __u8 range_policy; /* Set RangeStartRangeLengthPolicy parameter */ + __u8 entire_table; /* Set all locking objects in SUM */ + __u8 align[4]; /* Align to 8 byte boundary */ +}; + struct opal_session_info { __u32 sum; __u32 who; @@ -216,5 +229,6 @@ struct opal_revert_lsp { #define IOC_OPAL_DISCOVERY _IOW('p', 239, struct opal_discovery) #define IOC_OPAL_REVERT_LSP _IOW('p', 240, struct opal_revert_lsp) #define IOC_OPAL_SET_SID_PW _IOW('p', 241, struct opal_new_pw) +#define IOC_OPAL_REACTIVATE_LSP _IOW('p', 242, struct opal_lr_react) #endif /* _UAPI_SED_OPAL_H */ -- cgit v1.2.3 From 8e3d34a7ce7386b01947dd649bd24775544e4d3e Mon Sep 17 00:00:00 2001 From: Ondrej Kozina Date: Fri, 6 Feb 2026 15:18:00 +0100 Subject: sed-opal: add IOC_OPAL_LR_SET_START_LEN ioctl. This ioctl is used to set up locking range start (offset) and locking range length attributes only. In Single User Mode (SUM), if the RangeStartRangeLengthPolicy parameter is set in the 'Reactivate' method, only Admin authority maintains the locking range length and start (offset) attributes of Locking objects set up for SUM. All other attributes from struct opal_user_lr_setup (RLE - read locking enabled, WLE - write locking enabled) shall remain in possession of the User authority associated with the Locking object set for SUM. Therefore, we need a separate function for setting up locking range start and locking range length because it may require two different authorities (and sessions) if the RangeStartRangeLengthPolicy attribute is set. With the IOC_OPAL_LR_SET_START_LEN ioctl, the opal_user_lr_setup members 'RLE' and 'WLE' of the ioctl argument are ignored. Signed-off-by: Ondrej Kozina Reviewed-and-tested-by: Milan Broz Signed-off-by: Jens Axboe --- block/sed-opal.c | 28 ++++++++++++++++++++++++++++ include/linux/sed-opal.h | 1 + include/uapi/linux/sed-opal.h | 1 + 3 files changed, 30 insertions(+) (limited to 'include/uapi/linux') diff --git a/block/sed-opal.c b/block/sed-opal.c index 7be72f621952..55c8a0953d78 100644 --- a/block/sed-opal.c +++ b/block/sed-opal.c @@ -3091,6 +3091,31 @@ static int opal_setup_locking_range(struct opal_dev *dev, return ret; } +static int opal_setup_locking_range_start_length(struct opal_dev *dev, + struct opal_user_lr_setup *opal_lrs) +{ + const struct opal_step lr_steps[] = { + { start_auth_opal_session, &opal_lrs->session }, + { setup_locking_range_start_length, opal_lrs }, + { end_opal_session, } + }; + int ret; + + /* we can not set global locking range offset or length */ + if (opal_lrs->session.opal_key.lr == 0) + return -EINVAL; + + ret = opal_get_key(dev, &opal_lrs->session.opal_key); + if (ret) + return ret; + mutex_lock(&dev->dev_lock); + setup_opal_dev(dev); + ret = execute_steps(dev, lr_steps, ARRAY_SIZE(lr_steps)); + mutex_unlock(&dev->dev_lock); + + return ret; +} + static int opal_locking_range_status(struct opal_dev *dev, struct opal_lr_status *opal_lrst, void __user *data) @@ -3431,6 +3456,9 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg) case IOC_OPAL_REACTIVATE_LSP: ret = opal_reactivate_lsp(dev, p); break; + case IOC_OPAL_LR_SET_START_LEN: + ret = opal_setup_locking_range_start_length(dev, p); + break; default: break; diff --git a/include/linux/sed-opal.h b/include/linux/sed-opal.h index 2ae5e6b0ac21..a0df6819b0a9 100644 --- a/include/linux/sed-opal.h +++ b/include/linux/sed-opal.h @@ -54,6 +54,7 @@ static inline bool is_sed_ioctl(unsigned int cmd) case IOC_OPAL_REVERT_LSP: case IOC_OPAL_SET_SID_PW: case IOC_OPAL_REACTIVATE_LSP: + case IOC_OPAL_LR_SET_START_LEN: return true; } return false; diff --git a/include/uapi/linux/sed-opal.h b/include/uapi/linux/sed-opal.h index d03e590b6501..82de38f3fbeb 100644 --- a/include/uapi/linux/sed-opal.h +++ b/include/uapi/linux/sed-opal.h @@ -230,5 +230,6 @@ struct opal_revert_lsp { #define IOC_OPAL_REVERT_LSP _IOW('p', 240, struct opal_revert_lsp) #define IOC_OPAL_SET_SID_PW _IOW('p', 241, struct opal_new_pw) #define IOC_OPAL_REACTIVATE_LSP _IOW('p', 242, struct opal_lr_react) +#define IOC_OPAL_LR_SET_START_LEN _IOW('p', 243, struct opal_user_lr_setup) #endif /* _UAPI_SED_OPAL_H */ -- cgit v1.2.3 From a441a9d22433fea561de131e27fff41715c2d186 Mon Sep 17 00:00:00 2001 From: Ondrej Kozina Date: Fri, 6 Feb 2026 15:18:01 +0100 Subject: sed-opal: add IOC_OPAL_ENABLE_DISABLE_LR. This ioctl is used to set up RLE (read lock enabled) and WLE (write lock enabled) parameters of the Locking object. In Single User Mode (SUM), if the RangeStartRangeLengthPolicy parameter is set in the 'Reactivate' method, only Admin authority maintains the locking range length and start (offset) attributes of Locking objects set up for SUM. All other attributes from struct opal_user_lr_setup (RLE - read locking enabled, WLE - write locking enabled) shall remain in possession of the User authority associated with the Locking object set for SUM. With the IOC_OPAL_ENABLE_DISABLE_LR ioctl, the opal_user_lr_setup members 'range_start' and 'range_length' of the ioctl argument are ignored. Signed-off-by: Ondrej Kozina Reviewed-and-tested-by: Milan Broz Signed-off-by: Jens Axboe --- block/sed-opal.c | 24 ++++++++++++++++++++++++ include/linux/sed-opal.h | 1 + include/uapi/linux/sed-opal.h | 1 + 3 files changed, 26 insertions(+) (limited to 'include/uapi/linux') diff --git a/block/sed-opal.c b/block/sed-opal.c index 55c8a0953d78..53a73422911e 100644 --- a/block/sed-opal.c +++ b/block/sed-opal.c @@ -3116,6 +3116,27 @@ static int opal_setup_locking_range_start_length(struct opal_dev *dev, return ret; } +static int opal_enable_disable_range(struct opal_dev *dev, + struct opal_user_lr_setup *opal_lrs) +{ + const struct opal_step lr_steps[] = { + { start_auth_opal_session, &opal_lrs->session }, + { setup_enable_range, opal_lrs }, + { end_opal_session, } + }; + int ret; + + ret = opal_get_key(dev, &opal_lrs->session.opal_key); + if (ret) + return ret; + mutex_lock(&dev->dev_lock); + setup_opal_dev(dev); + ret = execute_steps(dev, lr_steps, ARRAY_SIZE(lr_steps)); + mutex_unlock(&dev->dev_lock); + + return ret; +} + static int opal_locking_range_status(struct opal_dev *dev, struct opal_lr_status *opal_lrst, void __user *data) @@ -3459,6 +3480,9 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg) case IOC_OPAL_LR_SET_START_LEN: ret = opal_setup_locking_range_start_length(dev, p); break; + case IOC_OPAL_ENABLE_DISABLE_LR: + ret = opal_enable_disable_range(dev, p); + break; default: break; diff --git a/include/linux/sed-opal.h b/include/linux/sed-opal.h index a0df6819b0a9..1d63479838cf 100644 --- a/include/linux/sed-opal.h +++ b/include/linux/sed-opal.h @@ -55,6 +55,7 @@ static inline bool is_sed_ioctl(unsigned int cmd) case IOC_OPAL_SET_SID_PW: case IOC_OPAL_REACTIVATE_LSP: case IOC_OPAL_LR_SET_START_LEN: + case IOC_OPAL_ENABLE_DISABLE_LR: return true; } return false; diff --git a/include/uapi/linux/sed-opal.h b/include/uapi/linux/sed-opal.h index 82de38f3fbeb..bde023ae2295 100644 --- a/include/uapi/linux/sed-opal.h +++ b/include/uapi/linux/sed-opal.h @@ -231,5 +231,6 @@ struct opal_revert_lsp { #define IOC_OPAL_SET_SID_PW _IOW('p', 241, struct opal_new_pw) #define IOC_OPAL_REACTIVATE_LSP _IOW('p', 242, struct opal_lr_react) #define IOC_OPAL_LR_SET_START_LEN _IOW('p', 243, struct opal_user_lr_setup) +#define IOC_OPAL_ENABLE_DISABLE_LR _IOW('p', 244, struct opal_user_lr_setup) #endif /* _UAPI_SED_OPAL_H */ -- cgit v1.2.3 From 0cc9293bccb234552b81c3ebc074f5839f019e01 Mon Sep 17 00:00:00 2001 From: Ondrej Kozina Date: Fri, 6 Feb 2026 15:18:03 +0100 Subject: sed-opal: add IOC_OPAL_GET_SUM_STATUS ioctl. This adds a function for retrieving the set of Locking objects enabled for Single User Mode (SUM) and the value of the RangeStartRangeLengthPolicy parameter. It retrieves data from the LockingInfo table, specifically the columns SingleUserModeRanges and RangeStartLengthPolicy, which were added according to the TCG Opal Feature Set: Single User Mode, as described in chapters 4.4.3.1 and 4.4.3.2. Signed-off-by: Ondrej Kozina Reviewed-and-tested-by: Milan Broz Signed-off-by: Jens Axboe --- block/sed-opal.c | 159 ++++++++++++++++++++++++++++++++++++++++++ include/linux/sed-opal.h | 1 + include/uapi/linux/sed-opal.h | 13 ++++ 3 files changed, 173 insertions(+) (limited to 'include/uapi/linux') diff --git a/block/sed-opal.c b/block/sed-opal.c index 6146a1b30421..c34d19e91201 100644 --- a/block/sed-opal.c +++ b/block/sed-opal.c @@ -1757,6 +1757,12 @@ static int start_anybodyASP_opal_session(struct opal_dev *dev, void *data) OPAL_ADMINSP_UID, NULL, 0); } +static int start_anybodyLSP_opal_session(struct opal_dev *dev, void *data) +{ + return start_generic_opal_session(dev, OPAL_ANYBODY_UID, + OPAL_LOCKINGSP_UID, NULL, 0); +} + static int start_SIDASP_opal_session(struct opal_dev *dev, void *data) { int ret; @@ -3389,6 +3395,156 @@ static int opal_get_geometry(struct opal_dev *dev, void __user *data) return 0; } +static int get_sum_ranges(struct opal_dev *dev, void *data) +{ + const char *lr_uid; + size_t lr_uid_len; + u64 val; + const struct opal_resp_tok *tok; + int err, tok_n = 2; + struct opal_sum_ranges *sranges = data; + const __u8 lr_all[OPAL_MAX_LRS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + + err = generic_get_columns(dev, opaluid[OPAL_LOCKING_INFO_TABLE], OPAL_SUM_SET_LIST, + OPAL_SUM_RANGE_POLICY); + if (err) { + pr_debug("Couldn't get locking info table columns %d to %d.\n", + OPAL_SUM_SET_LIST, OPAL_SUM_RANGE_POLICY); + return err; + } + + tok = response_get_token(&dev->parsed, tok_n); + if (IS_ERR(tok)) + return PTR_ERR(tok); + + if (!response_token_matches(tok, OPAL_STARTNAME)) { + pr_debug("Unexpected response token type %d.\n", tok_n); + return OPAL_INVAL_PARAM; + } + tok_n++; + + if (response_get_u64(&dev->parsed, tok_n) != OPAL_SUM_SET_LIST) { + pr_debug("Token %d does not match expected column %u.\n", + tok_n, OPAL_SUM_SET_LIST); + return OPAL_INVAL_PARAM; + } + tok_n++; + + tok = response_get_token(&dev->parsed, tok_n); + if (IS_ERR(tok)) + return PTR_ERR(tok); + + /* + * The OPAL_SUM_SET_LIST response contains two distinct values: + * + * - the list of individual locking ranges (UIDs) put in SUM. The list + * may also be empty signaling the SUM is disabled. + * + * - the Locking table UID if the entire Locking table is put in SUM. + */ + if (response_token_matches(tok, OPAL_STARTLIST)) { + sranges->num_lrs = 0; + + tok_n++; + tok = response_get_token(&dev->parsed, tok_n); + if (IS_ERR(tok)) + return PTR_ERR(tok); + + while (!response_token_matches(tok, OPAL_ENDLIST)) { + lr_uid_len = response_get_string(&dev->parsed, tok_n, &lr_uid); + if (lr_uid_len != OPAL_UID_LENGTH) { + pr_debug("Unexpected response token type %d.\n", tok_n); + return OPAL_INVAL_PARAM; + } + + if (memcmp(lr_uid, opaluid[OPAL_LOCKINGRANGE_GLOBAL], OPAL_UID_LENGTH)) { + if (lr_uid[5] != LOCKING_RANGE_NON_GLOBAL) { + pr_debug("Unexpected byte %d at LR UUID position 5.\n", + lr_uid[5]); + return OPAL_INVAL_PARAM; + } + sranges->lr[sranges->num_lrs++] = lr_uid[7]; + } else + sranges->lr[sranges->num_lrs++] = 0; + + tok_n++; + tok = response_get_token(&dev->parsed, tok_n); + if (IS_ERR(tok)) + return PTR_ERR(tok); + } + } else { + /* Only OPAL_LOCKING_TABLE UID is an alternative to OPAL_STARTLIST here. */ + lr_uid_len = response_get_string(&dev->parsed, tok_n, &lr_uid); + if (lr_uid_len != OPAL_UID_LENGTH) { + pr_debug("Unexpected response token type %d.\n", tok_n); + return OPAL_INVAL_PARAM; + } + + if (memcmp(lr_uid, opaluid[OPAL_LOCKING_TABLE], OPAL_UID_LENGTH)) { + pr_debug("Unexpected response UID.\n"); + return OPAL_INVAL_PARAM; + } + + /* sed-opal kernel API already provides following limit in Activate command */ + sranges->num_lrs = OPAL_MAX_LRS; + memcpy(sranges->lr, lr_all, OPAL_MAX_LRS); + } + tok_n++; + + tok = response_get_token(&dev->parsed, tok_n); + if (IS_ERR(tok)) + return PTR_ERR(tok); + + if (!response_token_matches(tok, OPAL_ENDNAME)) { + pr_debug("Unexpected response token type %d.\n", tok_n); + return OPAL_INVAL_PARAM; + } + tok_n++; + + err = response_get_column(&dev->parsed, &tok_n, OPAL_SUM_RANGE_POLICY, &val); + if (err) + return err; + + sranges->range_policy = val ? 1 : 0; + + return 0; +} + +static int opal_get_sum_ranges(struct opal_dev *dev, struct opal_sum_ranges *opal_sum_rngs, + void __user *data) +{ + const struct opal_step admin_steps[] = { + { start_admin1LSP_opal_session, &opal_sum_rngs->key }, + { get_sum_ranges, opal_sum_rngs }, + { end_opal_session, } + }, anybody_steps[] = { + { start_anybodyLSP_opal_session, NULL }, + { get_sum_ranges, opal_sum_rngs }, + { end_opal_session, } + }; + int ret; + + mutex_lock(&dev->dev_lock); + setup_opal_dev(dev); + if (opal_sum_rngs->key.key_len) + /* Use Admin1 session (authenticated by PIN) to retrieve LockingInfo columns */ + ret = execute_steps(dev, admin_steps, ARRAY_SIZE(admin_steps)); + else + /* Use Anybody session (no key) to retrieve LockingInfo columns */ + ret = execute_steps(dev, anybody_steps, ARRAY_SIZE(anybody_steps)); + mutex_unlock(&dev->dev_lock); + + /* skip session info when copying back to uspace */ + if (!ret && copy_to_user(data + offsetof(struct opal_sum_ranges, num_lrs), + (void *)opal_sum_rngs + offsetof(struct opal_sum_ranges, num_lrs), + sizeof(*opal_sum_rngs) - offsetof(struct opal_sum_ranges, num_lrs))) { + pr_debug("Error copying SUM ranges info to userspace\n"); + return -EFAULT; + } + + return ret; +} + int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg) { void *p; @@ -3483,6 +3639,9 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg) case IOC_OPAL_ENABLE_DISABLE_LR: ret = opal_enable_disable_range(dev, p); break; + case IOC_OPAL_GET_SUM_STATUS: + ret = opal_get_sum_ranges(dev, p, arg); + break; default: break; diff --git a/include/linux/sed-opal.h b/include/linux/sed-opal.h index 1d63479838cf..aa006edb612b 100644 --- a/include/linux/sed-opal.h +++ b/include/linux/sed-opal.h @@ -56,6 +56,7 @@ static inline bool is_sed_ioctl(unsigned int cmd) case IOC_OPAL_REACTIVATE_LSP: case IOC_OPAL_LR_SET_START_LEN: case IOC_OPAL_ENABLE_DISABLE_LR: + case IOC_OPAL_GET_SUM_STATUS: return true; } return false; diff --git a/include/uapi/linux/sed-opal.h b/include/uapi/linux/sed-opal.h index bde023ae2295..9830298ec51c 100644 --- a/include/uapi/linux/sed-opal.h +++ b/include/uapi/linux/sed-opal.h @@ -111,6 +111,18 @@ struct opal_lr_status { __u8 align[4]; }; +struct opal_sum_ranges { + /* + * Initiate Admin1 session if key_len > 0, + * use Anybody session otherwise. + */ + struct opal_key key; + __u8 num_lrs; + __u8 lr[OPAL_MAX_LRS]; + __u8 range_policy; + __u8 align[5]; /* Align to 8 byte boundary */ +}; + struct opal_lock_unlock { struct opal_session_info session; __u32 l_state; @@ -232,5 +244,6 @@ struct opal_revert_lsp { #define IOC_OPAL_REACTIVATE_LSP _IOW('p', 242, struct opal_lr_react) #define IOC_OPAL_LR_SET_START_LEN _IOW('p', 243, struct opal_user_lr_setup) #define IOC_OPAL_ENABLE_DISABLE_LR _IOW('p', 244, struct opal_user_lr_setup) +#define IOC_OPAL_GET_SUM_STATUS _IOW('p', 245, struct opal_sum_ranges) #endif /* _UAPI_SED_OPAL_H */ -- cgit v1.2.3 From 12ae2c81b21cfaa193db2faf035d495807edc3a7 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 26 Feb 2026 14:50:59 +0100 Subject: clone: add CLONE_AUTOREAP Add a new clone3() flag CLONE_AUTOREAP that makes a child process auto-reap on exit without ever becoming a zombie. This is a per-process property in contrast to the existing auto-reap mechanism via SA_NOCLDWAIT or SIG_IGN for SIGCHLD which applies to all children of a given parent. Currently the only way to automatically reap children is to set SA_NOCLDWAIT or SIG_IGN on SIGCHLD. This is a parent-scoped property affecting all children which makes it unsuitable for libraries or applications that need selective auto-reaping of specific children while still being able to wait() on others. CLONE_AUTOREAP stores an autoreap flag in the child's signal_struct. When the child exits do_notify_parent() checks this flag and causes exit_notify() to transition the task directly to EXIT_DEAD. Since the flag lives on the child it survives reparenting: if the original parent exits and the child is reparented to a subreaper or init the child still auto-reaps when it eventually exits. CLONE_AUTOREAP can be combined with CLONE_PIDFD to allow the parent to monitor the child's exit via poll() and retrieve exit status via PIDFD_GET_INFO. Without CLONE_PIDFD it provides a fire-and-forget pattern where the parent simply doesn't care about the child's exit status. No exit signal is delivered so exit_signal must be zero. CLONE_AUTOREAP is rejected in combination with CLONE_PARENT. If a CLONE_AUTOREAP child were to clone(CLONE_PARENT) the new grandchild would inherit exit_signal == 0 from the autoreap parent's group leader but without signal->autoreap. This grandchild would become a zombie that never sends a signal and is never autoreaped - confusing and arguably broken behavior. The flag is not inherited by the autoreap process's own children. Each child that should be autoreaped must be explicitly created with CLONE_AUTOREAP. Link: https://github.com/uapi-group/kernel-features/issues/45 Link: https://patch.msgid.link/20260226-work-pidfs-autoreap-v5-1-d148b984a989@kernel.org Reviewed-by: Oleg Nesterov Signed-off-by: Christian Brauner --- include/linux/sched/signal.h | 1 + include/uapi/linux/sched.h | 5 +++-- kernel/fork.c | 17 ++++++++++++++++- kernel/ptrace.c | 3 ++- kernel/signal.c | 4 ++++ 5 files changed, 26 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h index a22248aebcf9..f842c86b806f 100644 --- a/include/linux/sched/signal.h +++ b/include/linux/sched/signal.h @@ -132,6 +132,7 @@ struct signal_struct { */ unsigned int is_child_subreaper:1; unsigned int has_child_subreaper:1; + unsigned int autoreap:1; #ifdef CONFIG_POSIX_TIMERS diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h index 359a14cc76a4..69f7b4f9eb0c 100644 --- a/include/uapi/linux/sched.h +++ b/include/uapi/linux/sched.h @@ -34,8 +34,9 @@ #define CLONE_IO 0x80000000 /* Clone io context */ /* Flags for the clone3() syscall. */ -#define CLONE_CLEAR_SIGHAND 0x100000000ULL /* Clear any signal handler and reset to SIG_DFL. */ -#define CLONE_INTO_CGROUP 0x200000000ULL /* Clone into a specific cgroup given the right permissions. */ +#define CLONE_CLEAR_SIGHAND (1ULL << 32) /* Clear any signal handler and reset to SIG_DFL. */ +#define CLONE_INTO_CGROUP (1ULL << 33) /* Clone into a specific cgroup given the right permissions. */ +#define CLONE_AUTOREAP (1ULL << 34) /* Auto-reap child on exit. */ /* * cloning flags intersect with CSIGNAL so can be used with unshare and clone3 diff --git a/kernel/fork.c b/kernel/fork.c index e832da9d15a4..10549574fda6 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2028,6 +2028,18 @@ __latent_entropy struct task_struct *copy_process( return ERR_PTR(-EINVAL); } + if (clone_flags & CLONE_AUTOREAP) { + if (clone_flags & CLONE_THREAD) + return ERR_PTR(-EINVAL); + if (clone_flags & CLONE_PARENT) + return ERR_PTR(-EINVAL); + if (args->exit_signal) + return ERR_PTR(-EINVAL); + } + + if ((clone_flags & CLONE_PARENT) && current->signal->autoreap) + return ERR_PTR(-EINVAL); + /* * Force any signals received before this point to be delivered * before the fork happens. Collect up signals sent to multiple @@ -2435,6 +2447,8 @@ __latent_entropy struct task_struct *copy_process( */ p->signal->has_child_subreaper = p->real_parent->signal->has_child_subreaper || p->real_parent->signal->is_child_subreaper; + if (clone_flags & CLONE_AUTOREAP) + p->signal->autoreap = 1; list_add_tail(&p->sibling, &p->real_parent->children); list_add_tail_rcu(&p->tasks, &init_task.tasks); attach_pid(p, PIDTYPE_TGID); @@ -2897,7 +2911,8 @@ static bool clone3_args_valid(struct kernel_clone_args *kargs) { /* Verify that no unknown flags are passed along. */ if (kargs->flags & - ~(CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP)) + ~(CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP | + CLONE_AUTOREAP)) return false; /* diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 392ec2f75f01..68c17daef8d4 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -549,7 +549,8 @@ static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p) if (!dead && thread_group_empty(p)) { if (!same_thread_group(p->real_parent, tracer)) dead = do_notify_parent(p, p->exit_signal); - else if (ignoring_children(tracer->sighand)) { + else if (ignoring_children(tracer->sighand) || + p->signal->autoreap) { __wake_up_parent(p, tracer); dead = true; } diff --git a/kernel/signal.c b/kernel/signal.c index d65d0fe24bfb..e61f39fa8c8a 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2251,6 +2251,10 @@ bool do_notify_parent(struct task_struct *tsk, int sig) if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) sig = 0; } + if (!tsk->ptrace && tsk->signal->autoreap) { + autoreap = true; + sig = 0; + } /* * Send with __send_signal as si_pid and si_uid are in the * parent's namespaces. -- cgit v1.2.3 From 24baca56fafc33d4fb77cd9858a48c734183cb22 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 26 Feb 2026 14:51:00 +0100 Subject: clone: add CLONE_NNP Add a new clone3() flag CLONE_NNP that sets no_new_privs on the child process at clone time. This is analogous to prctl(PR_SET_NO_NEW_PRIVS) but applied at process creation rather than requiring a separate step after the child starts running. CLONE_NNP is rejected with CLONE_THREAD. It's conceptually a lot simpler if the whole thread-group is forced into NNP and not have single threads running around with NNP. Link: https://patch.msgid.link/20260226-work-pidfs-autoreap-v5-2-d148b984a989@kernel.org Reviewed-by: Oleg Nesterov Signed-off-by: Christian Brauner --- include/uapi/linux/sched.h | 1 + kernel/fork.c | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h index 69f7b4f9eb0c..386c8d7e89cb 100644 --- a/include/uapi/linux/sched.h +++ b/include/uapi/linux/sched.h @@ -37,6 +37,7 @@ #define CLONE_CLEAR_SIGHAND (1ULL << 32) /* Clear any signal handler and reset to SIG_DFL. */ #define CLONE_INTO_CGROUP (1ULL << 33) /* Clone into a specific cgroup given the right permissions. */ #define CLONE_AUTOREAP (1ULL << 34) /* Auto-reap child on exit. */ +#define CLONE_NNP (1ULL << 35) /* Set no_new_privs on child. */ /* * cloning flags intersect with CSIGNAL so can be used with unshare and clone3 diff --git a/kernel/fork.c b/kernel/fork.c index 10549574fda6..736798e4005a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2040,6 +2040,11 @@ __latent_entropy struct task_struct *copy_process( if ((clone_flags & CLONE_PARENT) && current->signal->autoreap) return ERR_PTR(-EINVAL); + if (clone_flags & CLONE_NNP) { + if (clone_flags & CLONE_THREAD) + return ERR_PTR(-EINVAL); + } + /* * Force any signals received before this point to be delivered * before the fork happens. Collect up signals sent to multiple @@ -2424,6 +2429,9 @@ __latent_entropy struct task_struct *copy_process( */ copy_seccomp(p); + if (clone_flags & CLONE_NNP) + task_set_no_new_privs(p); + init_task_pid_links(p); if (likely(p->pid)) { ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace); @@ -2912,7 +2920,7 @@ static bool clone3_args_valid(struct kernel_clone_args *kargs) /* Verify that no unknown flags are passed along. */ if (kargs->flags & ~(CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP | - CLONE_AUTOREAP)) + CLONE_AUTOREAP | CLONE_NNP)) return false; /* -- cgit v1.2.3 From c8134b5f13ae959de2b3c8cc278e2602b0857345 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 26 Feb 2026 14:51:01 +0100 Subject: pidfd: add CLONE_PIDFD_AUTOKILL Add a new clone3() flag CLONE_PIDFD_AUTOKILL that ties a child's lifetime to the pidfd returned from clone3(). When the last reference to the struct file created by clone3() is closed the kernel sends SIGKILL to the child. A pidfd obtained via pidfd_open() for the same process does not keep the child alive and does not trigger autokill - only the specific struct file from clone3() has this property. This is useful for container runtimes, service managers, and sandboxed subprocess execution - any scenario where the child must die if the parent crashes or abandons the pidfd. CLONE_PIDFD_AUTOKILL requires both CLONE_PIDFD (the whole point is tying lifetime to the pidfd file) and CLONE_AUTOREAP (a killed child with no one to reap it would become a zombie). CLONE_THREAD is rejected because autokill targets a process not a thread. The clone3 pidfd is identified by the PIDFD_AUTOKILL file flag set on the struct file at clone3() time. The pidfs .release handler checks this flag and sends SIGKILL via do_send_sig_info(SIGKILL, SEND_SIG_PRIV, ...) only when it is set. Files from pidfd_open() or open_by_handle_at() are distinct struct files that do not carry this flag. dup()/fork() share the same struct file so they extend the child's lifetime until the last reference drops. CLONE_PIDFD_AUTOKILL uses a privilege model based on CLONE_NNP: without CLONE_NNP the child could escalate privileges via setuid/setgid exec after being spawned, so the caller must have CAP_SYS_ADMIN in its user namespace. With CLONE_NNP the child can never gain new privileges so unprivileged usage is allowed. This is a deliberate departure from the pdeath_signal model which is reset during secureexec and commit_creds() rendering it useless for container runtimes that need to deprivilege themselves. Link: https://patch.msgid.link/20260226-work-pidfs-autoreap-v5-3-d148b984a989@kernel.org Reviewed-by: Oleg Nesterov Signed-off-by: Christian Brauner --- fs/pidfs.c | 38 ++++++++++++++++++++++++++++++++------ include/uapi/linux/pidfd.h | 1 + include/uapi/linux/sched.h | 1 + kernel/fork.c | 29 ++++++++++++++++++++++++++--- 4 files changed, 60 insertions(+), 9 deletions(-) (limited to 'include/uapi/linux') diff --git a/fs/pidfs.c b/fs/pidfs.c index 318253344b5c..a8d1bca0395d 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -637,7 +639,28 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return open_namespace(ns_common); } +static int pidfs_file_release(struct inode *inode, struct file *file) +{ + struct pid *pid = inode->i_private; + struct task_struct *task; + + if (!(file->f_flags & PIDFD_AUTOKILL)) + return 0; + + guard(rcu)(); + task = pid_task(pid, PIDTYPE_TGID); + if (!task) + return 0; + + /* Not available for kthreads or user workers for now. */ + if (WARN_ON_ONCE(task->flags & (PF_KTHREAD | PF_USER_WORKER))) + return 0; + do_send_sig_info(SIGKILL, SEND_SIG_PRIV, task, PIDTYPE_TGID); + return 0; +} + static const struct file_operations pidfs_file_operations = { + .release = pidfs_file_release, .poll = pidfd_poll, #ifdef CONFIG_PROC_FS .show_fdinfo = pidfd_show_fdinfo, @@ -1093,11 +1116,11 @@ struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags) int ret; /* - * Ensure that PIDFD_STALE can be passed as a flag without - * overloading other uapi pidfd flags. + * Ensure that internal pidfd flags don't overlap with each + * other or with uapi pidfd flags. */ - BUILD_BUG_ON(PIDFD_STALE == PIDFD_THREAD); - BUILD_BUG_ON(PIDFD_STALE == PIDFD_NONBLOCK); + BUILD_BUG_ON(hweight32(PIDFD_THREAD | PIDFD_NONBLOCK | + PIDFD_STALE | PIDFD_AUTOKILL) != 4); ret = path_from_stashed(&pid->stashed, pidfs_mnt, get_pid(pid), &path); if (ret < 0) @@ -1108,9 +1131,12 @@ struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags) flags &= ~PIDFD_STALE; flags |= O_RDWR; pidfd_file = dentry_open(&path, flags, current_cred()); - /* Raise PIDFD_THREAD explicitly as do_dentry_open() strips it. */ + /* + * Raise PIDFD_THREAD and PIDFD_AUTOKILL explicitly as + * do_dentry_open() strips O_EXCL and O_TRUNC. + */ if (!IS_ERR(pidfd_file)) - pidfd_file->f_flags |= (flags & PIDFD_THREAD); + pidfd_file->f_flags |= (flags & (PIDFD_THREAD | PIDFD_AUTOKILL)); return pidfd_file; } diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h index ea9a6811fc76..9281956a9f32 100644 --- a/include/uapi/linux/pidfd.h +++ b/include/uapi/linux/pidfd.h @@ -13,6 +13,7 @@ #ifdef __KERNEL__ #include #define PIDFD_STALE CLONE_PIDFD +#define PIDFD_AUTOKILL O_TRUNC #endif /* Flags for pidfd_send_signal(). */ diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h index 386c8d7e89cb..149dbc64923b 100644 --- a/include/uapi/linux/sched.h +++ b/include/uapi/linux/sched.h @@ -38,6 +38,7 @@ #define CLONE_INTO_CGROUP (1ULL << 33) /* Clone into a specific cgroup given the right permissions. */ #define CLONE_AUTOREAP (1ULL << 34) /* Auto-reap child on exit. */ #define CLONE_NNP (1ULL << 35) /* Set no_new_privs on child. */ +#define CLONE_PIDFD_AUTOKILL (1ULL << 36) /* Kill child when clone pidfd closes. */ /* * cloning flags intersect with CSIGNAL so can be used with unshare and clone3 diff --git a/kernel/fork.c b/kernel/fork.c index 736798e4005a..99a6cb4e7ab0 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2045,6 +2045,24 @@ __latent_entropy struct task_struct *copy_process( return ERR_PTR(-EINVAL); } + if (clone_flags & CLONE_PIDFD_AUTOKILL) { + if (!(clone_flags & CLONE_PIDFD)) + return ERR_PTR(-EINVAL); + if (!(clone_flags & CLONE_AUTOREAP)) + return ERR_PTR(-EINVAL); + if (clone_flags & CLONE_THREAD) + return ERR_PTR(-EINVAL); + /* + * Without CLONE_NNP the child could escalate privileges + * after being spawned, so require CAP_SYS_ADMIN. + * With CLONE_NNP the child can't gain new privileges, + * so allow unprivileged usage. + */ + if (!(clone_flags & CLONE_NNP) && + !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) + return ERR_PTR(-EPERM); + } + /* * Force any signals received before this point to be delivered * before the fork happens. Collect up signals sent to multiple @@ -2267,13 +2285,18 @@ __latent_entropy struct task_struct *copy_process( * if the fd table isn't shared). */ if (clone_flags & CLONE_PIDFD) { - int flags = (clone_flags & CLONE_THREAD) ? PIDFD_THREAD : 0; + unsigned flags = PIDFD_STALE; + + if (clone_flags & CLONE_THREAD) + flags |= PIDFD_THREAD; + if (clone_flags & CLONE_PIDFD_AUTOKILL) + flags |= PIDFD_AUTOKILL; /* * Note that no task has been attached to @pid yet indicate * that via CLONE_PIDFD. */ - retval = pidfd_prepare(pid, flags | PIDFD_STALE, &pidfile); + retval = pidfd_prepare(pid, flags, &pidfile); if (retval < 0) goto bad_fork_free_pid; pidfd = retval; @@ -2920,7 +2943,7 @@ static bool clone3_args_valid(struct kernel_clone_args *kargs) /* Verify that no unknown flags are passed along. */ if (kargs->flags & ~(CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP | - CLONE_AUTOREAP | CLONE_NNP)) + CLONE_AUTOREAP | CLONE_NNP | CLONE_PIDFD_AUTOKILL)) return false; /* -- cgit v1.2.3 From 5e8969bd192712419aae511dd5ba26855c2c78db Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 22 Jan 2026 11:48:48 +0100 Subject: mount: add FSMOUNT_NAMESPACE Add FSMOUNT_NAMESPACE flag to fsmount() that creates a new mount namespace with the newly created filesystem attached to a copy of the real rootfs. This returns a namespace file descriptor instead of an O_PATH mount fd, similar to how OPEN_TREE_NAMESPACE works for open_tree(). This allows creating a new filesystem and immediately placing it in a new mount namespace in a single operation, which is useful for container runtimes and other namespace-based isolation mechanisms. The rootfs mount is created before copying the real rootfs for the new namespace meaning that the mount namespace id for the mount of the root of the namespace is bigger than the child mounted on top of it. We've never explicitly given the guarantee for such ordering and I doubt anyone relies on it. Accepting that lets us avoid copying the mount again and also avoids having to massage may_copy_tree() to grant an exception for fsmount->mnt->mnt_ns being NULL. Link: https://patch.msgid.link/20260122-work-fsmount-namespace-v1-3-5ef0a886e646@kernel.org Signed-off-by: Christian Brauner --- fs/namespace.c | 37 ++++++++++++++++++++++++++++++------- include/uapi/linux/mount.h | 1 + 2 files changed, 31 insertions(+), 7 deletions(-) (limited to 'include/uapi/linux') diff --git a/fs/namespace.c b/fs/namespace.c index b098d1131e69..702e93243505 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3118,11 +3118,26 @@ static struct mnt_namespace *create_new_namespace(struct path *path, } /* - * We don't emulate unshare()ing a mount namespace. We stick - * to the restrictions of creating detached bind-mounts. It - * has a lot saner and simpler semantics. + * We don't emulate unshare()ing a mount namespace. We stick to + * the restrictions of creating detached bind-mounts. It has a + * lot saner and simpler semantics. */ - mnt = __do_loopback(path, recurse, copy_flags); + mnt = real_mount(path->mnt); + if (!mnt->mnt_ns) { + /* + * If we're moving into a new mount namespace via + * fsmount() swap the mount ids so the nullfs mount id + * is the lowest in the mount namespace avoiding another + * useless copy. This is fine we're not attached to any + * mount namespace so the mount ids are pure decoration + * at that point. + */ + swap(mnt->mnt_id_unique, new_ns_root->mnt_id_unique); + swap(mnt->mnt_id, new_ns_root->mnt_id); + mntget(&mnt->mnt); + } else { + mnt = __do_loopback(path, recurse, copy_flags); + } scoped_guard(mount_writer) { if (IS_ERR(mnt)) { emptied_ns = new_ns; @@ -4401,11 +4416,15 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, unsigned int mnt_flags = 0; long ret; - if (!may_mount()) + if ((flags & ~(FSMOUNT_CLOEXEC | FSMOUNT_NAMESPACE)) != 0) + return -EINVAL; + + if ((flags & FSMOUNT_NAMESPACE) && + !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) return -EPERM; - if ((flags & ~(FSMOUNT_CLOEXEC)) != 0) - return -EINVAL; + if (!(flags & FSMOUNT_NAMESPACE) && !may_mount()) + return -EPERM; if (attr_flags & ~FSMOUNT_VALID_FLAGS) return -EINVAL; @@ -4472,6 +4491,10 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, */ vfs_clean_context(fc); + if (flags & FSMOUNT_NAMESPACE) + return FD_ADD((flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0, + open_new_namespace(&new_path, 0)); + ns = alloc_mnt_ns(current->nsproxy->mnt_ns->user_ns, true); if (IS_ERR(ns)) return PTR_ERR(ns); diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h index d9d86598d100..2204708dbf7a 100644 --- a/include/uapi/linux/mount.h +++ b/include/uapi/linux/mount.h @@ -110,6 +110,7 @@ enum fsconfig_command { * fsmount() flags. */ #define FSMOUNT_CLOEXEC 0x00000001 +#define FSMOUNT_NAMESPACE 0x00000002 /* Create the mount in a new mount namespace */ /* * Mount attributes. -- cgit v1.2.3 From 9d4e752a24f740b31ca827bfab07010e4e7f34b0 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 6 Mar 2026 17:28:37 +0100 Subject: namespace: allow creating empty mount namespaces Add support for creating a mount namespace that contains only a copy of the root mount from the caller's mount namespace, with none of the child mounts. This is useful for containers and sandboxes that want to start with a minimal mount table and populate it from scratch rather than inheriting and then tearing down the full mount tree. Two new flags are introduced: - CLONE_EMPTY_MNTNS for clone3(), using the 64-bit flag space. - UNSHARE_EMPTY_MNTNS for unshare(), reusing the CLONE_PARENT_SETTID bit which has no meaning for unshare. Both flags imply CLONE_NEWNS. For the unshare path, UNSHARE_EMPTY_MNTNS is converted to CLONE_EMPTY_MNTNS in unshare_nsproxy_namespaces() before it reaches copy_mnt_ns(), so the mount namespace code only needs to handle a single flag. In copy_mnt_ns(), when CLONE_EMPTY_MNTNS is set, clone_mnt() is used instead of copy_tree() to clone only the root mount. The caller's root and working directory are both reset to the root dentry of the new mount. The cleanup variables are changed from vfsmount pointers with __free(mntput) to struct path with __free(path_put) because the empty mount namespace path needs to release both mount and dentry references when replacing the caller's root and pwd. In the normal (non-empty) path only the mount component is set, and dput(NULL) is a no-op so path_put remains correct there as well. Link: https://patch.msgid.link/20260306-work-empty-mntns-consolidated-v1-1-6eb30529bbb0@kernel.org Signed-off-by: Christian Brauner --- fs/namespace.c | 85 ++++++++++++++++++++++++++++++---------------- include/uapi/linux/sched.h | 7 ++++ kernel/fork.c | 17 ++++++++-- kernel/nsproxy.c | 21 +++++++++--- 4 files changed, 94 insertions(+), 36 deletions(-) (limited to 'include/uapi/linux') diff --git a/fs/namespace.c b/fs/namespace.c index 702e93243505..555f0a10de9a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -4233,8 +4233,8 @@ struct mnt_namespace *copy_mnt_ns(u64 flags, struct mnt_namespace *ns, struct user_namespace *user_ns, struct fs_struct *new_fs) { struct mnt_namespace *new_ns; - struct vfsmount *rootmnt __free(mntput) = NULL; - struct vfsmount *pwdmnt __free(mntput) = NULL; + struct path old_root __free(path_put) = {}; + struct path old_pwd __free(path_put) = {}; struct mount *p, *q; struct mount *old; struct mount *new; @@ -4254,11 +4254,18 @@ struct mnt_namespace *copy_mnt_ns(u64 flags, struct mnt_namespace *ns, return new_ns; guard(namespace_excl)(); - /* First pass: copy the tree topology */ - copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE; + + if (flags & CLONE_EMPTY_MNTNS) + copy_flags = 0; + else + copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE; if (user_ns != ns->user_ns) copy_flags |= CL_SLAVE; - new = copy_tree(old, old->mnt.mnt_root, copy_flags); + + if (flags & CLONE_EMPTY_MNTNS) + new = clone_mnt(old, old->mnt.mnt_root, copy_flags); + else + new = copy_tree(old, old->mnt.mnt_root, copy_flags); if (IS_ERR(new)) { emptied_ns = new_ns; return ERR_CAST(new); @@ -4269,33 +4276,53 @@ struct mnt_namespace *copy_mnt_ns(u64 flags, struct mnt_namespace *ns, } new_ns->root = new; - /* - * Second pass: switch the tsk->fs->* elements and mark new vfsmounts - * as belonging to new namespace. We have already acquired a private - * fs_struct, so tsk->fs->lock is not needed. - */ - p = old; - q = new; - while (p) { - mnt_add_to_ns(new_ns, q); - new_ns->nr_mounts++; + if (flags & CLONE_EMPTY_MNTNS) { + /* + * Empty mount namespace: only the root mount exists. + * Reset root and pwd to the cloned mount's root dentry. + */ if (new_fs) { - if (&p->mnt == new_fs->root.mnt) { - new_fs->root.mnt = mntget(&q->mnt); - rootmnt = &p->mnt; - } - if (&p->mnt == new_fs->pwd.mnt) { - new_fs->pwd.mnt = mntget(&q->mnt); - pwdmnt = &p->mnt; + old_root = new_fs->root; + old_pwd = new_fs->pwd; + + new_fs->root.mnt = mntget(&new->mnt); + new_fs->root.dentry = dget(new->mnt.mnt_root); + + new_fs->pwd.mnt = mntget(&new->mnt); + new_fs->pwd.dentry = dget(new->mnt.mnt_root); + } + mnt_add_to_ns(new_ns, new); + new_ns->nr_mounts++; + } else { + /* + * Full copy: walk old and new trees in parallel, switching + * the tsk->fs->* elements and marking new vfsmounts as + * belonging to new namespace. We have already acquired a + * private fs_struct, so tsk->fs->lock is not needed. + */ + p = old; + q = new; + while (p) { + mnt_add_to_ns(new_ns, q); + new_ns->nr_mounts++; + if (new_fs) { + if (&p->mnt == new_fs->root.mnt) { + old_root.mnt = new_fs->root.mnt; + new_fs->root.mnt = mntget(&q->mnt); + } + if (&p->mnt == new_fs->pwd.mnt) { + old_pwd.mnt = new_fs->pwd.mnt; + new_fs->pwd.mnt = mntget(&q->mnt); + } } + p = next_mnt(p, old); + q = next_mnt(q, new); + if (!q) + break; + // an mntns binding we'd skipped? + while (p->mnt.mnt_root != q->mnt.mnt_root) + p = next_mnt(skip_mnt_tree(p), old); } - p = next_mnt(p, old); - q = next_mnt(q, new); - if (!q) - break; - // an mntns binding we'd skipped? - while (p->mnt.mnt_root != q->mnt.mnt_root) - p = next_mnt(skip_mnt_tree(p), old); } ns_tree_add_raw(new_ns); return new_ns; diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h index 359a14cc76a4..4e76fce9f777 100644 --- a/include/uapi/linux/sched.h +++ b/include/uapi/linux/sched.h @@ -36,6 +36,7 @@ /* Flags for the clone3() syscall. */ #define CLONE_CLEAR_SIGHAND 0x100000000ULL /* Clear any signal handler and reset to SIG_DFL. */ #define CLONE_INTO_CGROUP 0x200000000ULL /* Clone into a specific cgroup given the right permissions. */ +#define CLONE_EMPTY_MNTNS (1ULL << 37) /* Create an empty mount namespace. */ /* * cloning flags intersect with CSIGNAL so can be used with unshare and clone3 @@ -43,6 +44,12 @@ */ #define CLONE_NEWTIME 0x00000080 /* New time namespace */ +/* + * unshare flags share the bit space with clone flags but only apply to the + * unshare syscall: + */ +#define UNSHARE_EMPTY_MNTNS 0x00100000 /* Unshare an empty mount namespace. */ + #ifndef __ASSEMBLY__ /** * struct clone_args - arguments for the clone3 syscall diff --git a/kernel/fork.c b/kernel/fork.c index 65113a304518..dea6b3454447 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2619,6 +2619,16 @@ pid_t kernel_clone(struct kernel_clone_args *args) int trace = 0; pid_t nr; + /* + * Creating an empty mount namespace implies creating a new mount + * namespace. Set this before copy_process() so that the + * CLONE_NEWNS|CLONE_FS mutual exclusion check works correctly. + */ + if (clone_flags & CLONE_EMPTY_MNTNS) { + clone_flags |= CLONE_NEWNS; + args->flags = clone_flags; + } + /* * For legacy clone() calls, CLONE_PIDFD uses the parent_tid argument * to return the pidfd. Hence, CLONE_PIDFD and CLONE_PARENT_SETTID are @@ -2897,7 +2907,8 @@ static bool clone3_args_valid(struct kernel_clone_args *kargs) { /* Verify that no unknown flags are passed along. */ if (kargs->flags & - ~(CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP)) + ~(CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND | + CLONE_INTO_CGROUP | CLONE_EMPTY_MNTNS)) return false; /* @@ -3050,7 +3061,7 @@ static int check_unshare_flags(unsigned long unshare_flags) CLONE_VM|CLONE_FILES|CLONE_SYSVSEM| CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET| CLONE_NEWUSER|CLONE_NEWPID|CLONE_NEWCGROUP| - CLONE_NEWTIME)) + CLONE_NEWTIME | UNSHARE_EMPTY_MNTNS)) return -EINVAL; /* * Not implemented, but pretend it works if there is nothing @@ -3149,6 +3160,8 @@ int ksys_unshare(unsigned long unshare_flags) /* * If unsharing namespace, must also unshare filesystem information. */ + if (unshare_flags & UNSHARE_EMPTY_MNTNS) + unshare_flags |= CLONE_NEWNS; if (unshare_flags & CLONE_NEWNS) unshare_flags |= CLONE_FS; diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index 259c4b4f1eeb..1bdc5be2dd20 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -95,7 +95,8 @@ static struct nsproxy *create_new_namespaces(u64 flags, if (!new_nsp) return ERR_PTR(-ENOMEM); - new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, user_ns, new_fs); + new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, + user_ns, new_fs); if (IS_ERR(new_nsp->mnt_ns)) { err = PTR_ERR(new_nsp->mnt_ns); goto out_ns; @@ -212,18 +213,28 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags, struct nsproxy **new_nsp, struct cred *new_cred, struct fs_struct *new_fs) { struct user_namespace *user_ns; + u64 flags = unshare_flags; int err = 0; - if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | - CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP | - CLONE_NEWTIME))) + if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | + CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP | + CLONE_NEWTIME))) return 0; user_ns = new_cred ? new_cred->user_ns : current_user_ns(); if (!ns_capable(user_ns, CAP_SYS_ADMIN)) return -EPERM; - *new_nsp = create_new_namespaces(unshare_flags, current, user_ns, + /* + * Convert the 32-bit UNSHARE_EMPTY_MNTNS (which aliases + * CLONE_PARENT_SETTID) to the unique 64-bit CLONE_EMPTY_MNTNS. + */ + if (flags & UNSHARE_EMPTY_MNTNS) { + flags &= ~(u64)UNSHARE_EMPTY_MNTNS; + flags |= CLONE_EMPTY_MNTNS; + } + + *new_nsp = create_new_namespaces(flags, current, user_ns, new_fs ? new_fs : current->fs); if (IS_ERR(*new_nsp)) { err = PTR_ERR(*new_nsp); -- cgit v1.2.3 From 5cba06c71c713a5beb4aafab7973287d8a248ddb Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 2 Feb 2026 23:52:47 -0500 Subject: vt: add KT_CSI keysym type for modifier-aware CSI sequences Add a new keysym type KT_CSI that generates CSI tilde sequences with automatic modifier encoding. The keysym value encodes the CSI parameter number, producing sequences like ESC [ ~ or ESC [ ; ~ when Shift, Alt, or Ctrl modifiers are held. This allows navigation keys (Home, End, Insert, Delete, PgUp, PgDn) and function keys to generate modifier-aware escape sequences without consuming string table entries for each modifier combination. Define key symbols for navigation keys (K_CSI_HOME, K_CSI_END, etc.) and function keys (K_CSI_F1 through K_CSI_F20) using standard xterm CSI parameter values. The modifier encoding follows the xterm convention: mod = 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0) Allowed CSI parameter values range from 0 to 99. Note: The Linux console historically uses a non-standard double-bracket format for F1-F5 (ESC [ [ A through ESC [ [ E) rather than the xterm tilde format (ESC [ 11 ~ through ESC [ 15 ~). The K_CSI_F1 through K_CSI_F5 definitions use the xterm format. Converting F1-F5 to KT_CSI would require updating the "linux" terminfo entry to match. Navigation keys and F6-F20 already use the tilde format and are fully compatible. Signed-off-by: Nicolas Pitre Link: https://patch.msgid.link/20260203045457.1049793-3-nico@fluxnic.net Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 38 +++++++++++++++++++++++++++++++++----- include/uapi/linux/keyboard.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index cb907a3b9d3d..44fd67eb723a 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -74,7 +74,7 @@ static inline int kbd_defleds(void) k_self, k_fn, k_spec, k_pad,\ k_dead, k_cons, k_cur, k_shift,\ k_meta, k_ascii, k_lock, k_lowercase,\ - k_slock, k_dead2, k_brl, k_ignore + k_slock, k_dead2, k_brl, k_csi typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value, char up_flag); @@ -127,6 +127,7 @@ static const unsigned char max_vals[] = { [ KT_SLOCK ] = NR_LOCK - 1, [ KT_DEAD2 ] = 255, [ KT_BRL ] = NR_BRL - 1, + [ KT_CSI ] = 99, }; static const int NR_TYPES = ARRAY_SIZE(max_vals); @@ -644,10 +645,6 @@ static void fn_null(struct vc_data *vc) /* * Special key handlers */ -static void k_ignore(struct vc_data *vc, unsigned char value, char up_flag) -{ -} - static void k_spec(struct vc_data *vc, unsigned char value, char up_flag) { if (up_flag) @@ -1029,6 +1026,37 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) } } +/* + * Handle KT_CSI keysym type: generate CSI tilde sequences with modifier + * support. The value encodes the CSI parameter number, producing sequences + * like ESC [ ~ or ESC [ ; ~ when modifiers are held. + */ +static void k_csi(struct vc_data *vc, unsigned char value, char up_flag) +{ + char buf[10]; + int i = 0; + int mod; + + if (up_flag) + return; + + mod = csi_modifier_param(); + + buf[i++] = 0x1b; + buf[i++] = '['; + if (value >= 10) + buf[i++] = '0' + value / 10; + buf[i++] = '0' + value % 10; + if (mod > 1) { + buf[i++] = ';'; + buf[i++] = '0' + mod; + } + buf[i++] = '~'; + buf[i] = 0x00; + + puts_queue(vc, buf); +} + #if IS_ENABLED(CONFIG_INPUT_LEDS) && IS_ENABLED(CONFIG_LEDS_TRIGGERS) struct kbd_led_trigger { diff --git a/include/uapi/linux/keyboard.h b/include/uapi/linux/keyboard.h index 36d230cedf12..48ecb0cefb45 100644 --- a/include/uapi/linux/keyboard.h +++ b/include/uapi/linux/keyboard.h @@ -41,6 +41,7 @@ #define KT_SLOCK 12 #define KT_DEAD2 13 #define KT_BRL 14 +#define KT_CSI 15 /* CSI sequences with modifier support */ #define K(t,v) (((t)<<8)|(v)) #define KTYP(x) ((x) >> 8) @@ -461,5 +462,33 @@ #define NR_BRL 11 +/* KT_CSI keys: value is the CSI parameter number for ESC [ ~ */ +#define K_CSI_HOME K(KT_CSI, 1) /* ESC [ 1 ~ */ +#define K_CSI_INSERT K(KT_CSI, 2) /* ESC [ 2 ~ */ +#define K_CSI_DELETE K(KT_CSI, 3) /* ESC [ 3 ~ */ +#define K_CSI_END K(KT_CSI, 4) /* ESC [ 4 ~ */ +#define K_CSI_PGUP K(KT_CSI, 5) /* ESC [ 5 ~ */ +#define K_CSI_PGDN K(KT_CSI, 6) /* ESC [ 6 ~ */ +#define K_CSI_F1 K(KT_CSI, 11) /* ESC [ 11 ~ */ +#define K_CSI_F2 K(KT_CSI, 12) /* ESC [ 12 ~ */ +#define K_CSI_F3 K(KT_CSI, 13) /* ESC [ 13 ~ */ +#define K_CSI_F4 K(KT_CSI, 14) /* ESC [ 14 ~ */ +#define K_CSI_F5 K(KT_CSI, 15) /* ESC [ 15 ~ */ +#define K_CSI_F6 K(KT_CSI, 17) /* ESC [ 17 ~ */ +#define K_CSI_F7 K(KT_CSI, 18) /* ESC [ 18 ~ */ +#define K_CSI_F8 K(KT_CSI, 19) /* ESC [ 19 ~ */ +#define K_CSI_F9 K(KT_CSI, 20) /* ESC [ 20 ~ */ +#define K_CSI_F10 K(KT_CSI, 21) /* ESC [ 21 ~ */ +#define K_CSI_F11 K(KT_CSI, 23) /* ESC [ 23 ~ */ +#define K_CSI_F12 K(KT_CSI, 24) /* ESC [ 24 ~ */ +#define K_CSI_F13 K(KT_CSI, 25) /* ESC [ 25 ~ */ +#define K_CSI_F14 K(KT_CSI, 26) /* ESC [ 26 ~ */ +#define K_CSI_F15 K(KT_CSI, 28) /* ESC [ 28 ~ */ +#define K_CSI_F16 K(KT_CSI, 29) /* ESC [ 29 ~ */ +#define K_CSI_F17 K(KT_CSI, 31) /* ESC [ 31 ~ */ +#define K_CSI_F18 K(KT_CSI, 32) /* ESC [ 32 ~ */ +#define K_CSI_F19 K(KT_CSI, 33) /* ESC [ 33 ~ */ +#define K_CSI_F20 K(KT_CSI, 34) /* ESC [ 34 ~ */ + #define MAX_DIACR 256 #endif /* _UAPI__LINUX_KEYBOARD_H */ -- cgit v1.2.3 From e4b993f2bca78357b430170574f8de7bc7874088 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 3 Mar 2026 22:17:09 +0100 Subject: wifi: nl80211: split out UHR operation information The beacon doesn't contain the full UHR operation, a number of fields (such as NPCA) are only partially there. Add a new attribute to contain the full information, so it's available to the driver/mac80211. Link: https://patch.msgid.link/20260303221710.866bacf82639.Iafdf37fb0f4304bdcdb824977d61e17b38c47685@changeid Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 6 ++++++ net/wireless/nl80211.c | 26 ++++++++++++++++---------- 2 files changed, 22 insertions(+), 10 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 0b7a06c2b9f7..67d764023988 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3001,6 +3001,10 @@ enum nl80211_commands { * interference detection is not performed on these sub-channels, their * corresponding bits are consistently set to zero. * + * @NL80211_ATTR_UHR_OPERATION: Full UHR Operation element, as it appears in + * association response etc., since it's abridged in the beacon. Used + * for START_AP etc. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3576,6 +3580,8 @@ enum nl80211_attrs { NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP, + NL80211_ATTR_UHR_OPERATION, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 699687a0caa9..3e867930e253 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -344,6 +344,17 @@ static int validate_uhr_capa(const struct nlattr *attr, return 0; } +static int validate_uhr_operation(const struct nlattr *attr, + struct netlink_ext_ack *extack) +{ + const u8 *data = nla_data(attr); + unsigned int len = nla_len(attr); + + if (!ieee80211_uhr_oper_size_ok(data, len, false)) + return -EINVAL; + return 0; +} + /* policy for the attributes */ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR]; @@ -949,6 +960,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_UHR_CAPABILITY] = NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_uhr_capa, 255), [NL80211_ATTR_DISABLE_UHR] = { .type = NLA_FLAG }, + [NL80211_ATTR_UHR_OPERATION] = + NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_uhr_operation), }; /* policy for the key attributes */ @@ -6501,16 +6514,6 @@ static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params) return -EINVAL; } - cap = cfg80211_find_ext_elem(WLAN_EID_EXT_UHR_OPER, ies, ies_len); - if (cap) { - if (!cap->datalen) - return -EINVAL; - params->uhr_oper = (void *)(cap->data + 1); - if (!ieee80211_uhr_oper_size_ok((const u8 *)params->uhr_oper, - cap->datalen - 1, true)) - return -EINVAL; - } - return 0; } @@ -6952,6 +6955,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (err) goto out; + if (info->attrs[NL80211_ATTR_UHR_OPERATION]) + params->uhr_oper = nla_data(info->attrs[NL80211_ATTR_UHR_OPERATION]); + err = nl80211_validate_ap_phy_operation(params); if (err) goto out; -- cgit v1.2.3 From 74f0cca1100b6d1f1ea28178435aff8078d06603 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 11 Mar 2026 05:19:56 +0000 Subject: udp: Remove UDPLITE_SEND_CSCOV and UDPLITE_RECV_CSCOV. UDP-Lite supports variable-length checksum and has two socket options, UDPLITE_SEND_CSCOV and UDPLITE_RECV_CSCOV, to control the checksum coverage. Let's remove the support. setsockopt(UDPLITE_SEND_CSCOV / UDPLITE_RECV_CSCOV) was only available for UDP-Lite and returned -ENOPROTOOPT for UDP. Now, the options are handled in ip_setsockopt() and ipv6_setsockopt(), which still return the same error. getsockopt(UDPLITE_SEND_CSCOV / UDPLITE_RECV_CSCOV) was available for UDP and always returned 0, meaning full checksum, but now -ENOPROTOOPT is returned. Given that getsockopt() is meaningless for UDP and even the options are not defined under include/uapi/, this should not be a problem. $ man 7 udplite ... BUGS Where glibc support is missing, the following definitions are needed: #define IPPROTO_UDPLITE 136 #define UDPLITE_SEND_CSCOV 10 #define UDPLITE_RECV_CSCOV 11 Signed-off-by: Kuniyuki Iwashima Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260311052020.1213705-10-kuniyu@google.com Signed-off-by: Jakub Kicinski --- include/linux/udp.h | 10 +--------- include/net/udplite.h | 15 --------------- include/uapi/linux/udp.h | 2 ++ net/ipv4/udp.c | 46 ++-------------------------------------------- net/ipv6/ip6_checksum.c | 2 +- net/ipv6/udp.c | 5 ++--- 6 files changed, 8 insertions(+), 72 deletions(-) delete mode 100644 include/net/udplite.h (limited to 'include/uapi/linux') diff --git a/include/linux/udp.h b/include/linux/udp.h index 1cbf6b4d3aab..ce56ebcee5cb 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -40,8 +40,6 @@ enum { UDP_FLAGS_ACCEPT_FRAGLIST, UDP_FLAGS_ACCEPT_L4, UDP_FLAGS_ENCAP_ENABLED, /* This socket enabled encap */ - UDP_FLAGS_UDPLITE_SEND_CC, /* set via udplite setsockopt */ - UDP_FLAGS_UDPLITE_RECV_CC, /* set via udplite setsockopt */ }; /* per NUMA structure for lockless producer usage. */ @@ -74,11 +72,7 @@ struct udp_sock { */ __u16 len; /* total length of pending frames */ __u16 gso_size; - /* - * Fields specific to UDP-Lite. - */ - __u16 pcslen; - __u16 pcrlen; + /* * For encapsulation sockets. */ @@ -236,8 +230,6 @@ static inline void udp_allow_gso(struct sock *sk) hlist_nulls_for_each_entry_rcu(__up, node, list, udp_lrpa_node) #endif -#define IS_UDPLITE(__sk) (unlikely(__sk->sk_protocol == IPPROTO_UDPLITE)) - static inline struct sock *udp_tunnel_sk(const struct net *net, bool is_ipv6) { #if IS_ENABLED(CONFIG_NET_UDP_TUNNEL) diff --git a/include/net/udplite.h b/include/net/udplite.h deleted file mode 100644 index 6bfa1d6833d1..000000000000 --- a/include/net/udplite.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Definitions for the UDP-Lite (RFC 3828) code. - */ -#ifndef _UDPLITE_H -#define _UDPLITE_H - -#include -#include - -/* UDP-Lite socket options */ -#define UDPLITE_SEND_CSCOV 10 /* sender partial coverage (as sent) */ -#define UDPLITE_RECV_CSCOV 11 /* receiver partial coverage (threshold ) */ - -#endif /* _UDPLITE_H */ diff --git a/include/uapi/linux/udp.h b/include/uapi/linux/udp.h index edca3e430305..877fb02df8fb 100644 --- a/include/uapi/linux/udp.h +++ b/include/uapi/linux/udp.h @@ -29,6 +29,8 @@ struct udphdr { /* UDP socket options */ #define UDP_CORK 1 /* Never send partially complete segments */ +/* Deprecated, reserved for UDPLITE_SEND_CSCOV 10 */ +/* Deprecated, reserved for UDPLITE_RECV_CSCOV 11 */ #define UDP_ENCAP 100 /* Set the socket to accept encapsulated packets */ #define UDP_NO_CHECK6_TX 101 /* Disable sending checksum for UDP6X */ #define UDP_NO_CHECK6_RX 102 /* Disable accepting checksum for UDP6 */ diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 9a2c8ff96e83..d47ca721ef0d 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -117,7 +117,6 @@ #include #include #include -#include #include #if IS_ENABLED(CONFIG_IPV6) #include @@ -2924,7 +2923,6 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, struct udp_sock *up = udp_sk(sk); int val, valbool; int err = 0; - int is_udplite = IS_UDPLITE(sk); if (level == SOL_SOCKET) { err = sk_setsockopt(sk, level, optname, optval, optlen); @@ -3011,36 +3009,6 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, sockopt_release_sock(sk); break; - /* - * UDP-Lite's partial checksum coverage (RFC 3828). - */ - /* The sender sets actual checksum coverage length via this option. - * The case coverage > packet length is handled by send module. */ - case UDPLITE_SEND_CSCOV: - if (!is_udplite) /* Disable the option on UDP sockets */ - return -ENOPROTOOPT; - if (val != 0 && val < 8) /* Illegal coverage: use default (8) */ - val = 8; - else if (val > USHRT_MAX) - val = USHRT_MAX; - WRITE_ONCE(up->pcslen, val); - udp_set_bit(UDPLITE_SEND_CC, sk); - break; - - /* The receiver specifies a minimum checksum coverage value. To make - * sense, this should be set to at least 8 (as done below). If zero is - * used, this again means full checksum coverage. */ - case UDPLITE_RECV_CSCOV: - if (!is_udplite) /* Disable the option on UDP sockets */ - return -ENOPROTOOPT; - if (val != 0 && val < 8) /* Avoid silly minimal values. */ - val = 8; - else if (val > USHRT_MAX) - val = USHRT_MAX; - WRITE_ONCE(up->pcrlen, val); - udp_set_bit(UDPLITE_RECV_CC, sk); - break; - default: err = -ENOPROTOOPT; break; @@ -3053,7 +3021,7 @@ EXPORT_IPV6_MOD(udp_lib_setsockopt); static int udp_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, unsigned int optlen) { - if (level == SOL_UDP || level == SOL_UDPLITE || level == SOL_SOCKET) + if (level == SOL_UDP || level == SOL_SOCKET) return udp_lib_setsockopt(sk, level, optname, optval, optlen, udp_push_pending_frames); @@ -3099,16 +3067,6 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, val = udp_test_bit(GRO_ENABLED, sk); break; - /* The following two cannot be changed on UDP sockets, the return is - * always 0 (which corresponds to the full checksum coverage of UDP). */ - case UDPLITE_SEND_CSCOV: - val = READ_ONCE(up->pcslen); - break; - - case UDPLITE_RECV_CSCOV: - val = READ_ONCE(up->pcrlen); - break; - default: return -ENOPROTOOPT; } @@ -3124,7 +3082,7 @@ EXPORT_IPV6_MOD(udp_lib_getsockopt); static int udp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { - if (level == SOL_UDP || level == SOL_UDPLITE) + if (level == SOL_UDP) return udp_lib_getsockopt(sk, level, optname, optval, optlen); return ip_getsockopt(sk, level, optname, optval, optlen); } diff --git a/net/ipv6/ip6_checksum.c b/net/ipv6/ip6_checksum.c index 8bb68a0cdfd6..e1a594873675 100644 --- a/net/ipv6/ip6_checksum.c +++ b/net/ipv6/ip6_checksum.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include -#include #include #ifndef _HAVE_ARCH_IPV6_CSUM diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 511e3f898be5..c3d8b5ede164 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -58,7 +58,6 @@ #include #include #include -#include static void udpv6_destruct_sock(struct sock *sk) { @@ -1831,7 +1830,7 @@ static void udpv6_destroy_sock(struct sock *sk) static int udpv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, unsigned int optlen) { - if (level == SOL_UDP || level == SOL_UDPLITE || level == SOL_SOCKET) + if (level == SOL_UDP || level == SOL_SOCKET) return udp_lib_setsockopt(sk, level, optname, optval, optlen, udp_v6_push_pending_frames); @@ -1841,7 +1840,7 @@ static int udpv6_setsockopt(struct sock *sk, int level, int optname, static int udpv6_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { - if (level == SOL_UDP || level == SOL_UDPLITE) + if (level == SOL_UDP) return udp_lib_getsockopt(sk, level, optname, optval, optlen); return ipv6_getsockopt(sk, level, optname, optval, optlen); } -- cgit v1.2.3 From 3ac949881396361b6462a717f6cbbd97f368af02 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 2 Mar 2026 08:02:24 -0700 Subject: include/psp-sev.h: fix structure member in comment The member is 'data', not 'opaque'. Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Tom Lendacky Signed-off-by: Herbert Xu --- include/uapi/linux/psp-sev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/psp-sev.h b/include/uapi/linux/psp-sev.h index 2b5b042eb73b..52dae70b058b 100644 --- a/include/uapi/linux/psp-sev.h +++ b/include/uapi/linux/psp-sev.h @@ -277,7 +277,7 @@ struct sev_user_data_snp_wrapped_vlek_hashstick { * struct sev_issue_cmd - SEV ioctl parameters * * @cmd: SEV commands to execute - * @opaque: pointer to the command structure + * @data: pointer to the command structure * @error: SEV FW return code on failure */ struct sev_issue_cmd { -- cgit v1.2.3 From 68deca0f0f4bba8c5278340c7c142500171d5f9b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 12 Mar 2026 11:03:55 +0100 Subject: devlink: expose devlink instance index over netlink Each devlink instance has an internally assigned index used for xarray storage. Expose it as a new DEVLINK_ATTR_INDEX uint attribute alongside the existing bus_name and dev_name handle. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260312100407.551173-2-jiri@resnulli.us Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/devlink.yaml | 5 +++++ include/uapi/linux/devlink.h | 2 ++ net/devlink/devl_internal.h | 2 ++ net/devlink/port.c | 1 + 4 files changed, 10 insertions(+) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/devlink.yaml b/Documentation/netlink/specs/devlink.yaml index 837112da6738..1bed67a0eefb 100644 --- a/Documentation/netlink/specs/devlink.yaml +++ b/Documentation/netlink/specs/devlink.yaml @@ -867,6 +867,10 @@ attribute-sets: type: flag doc: Request restoring parameter to its default value. value: 183 + - + name: index + type: uint + doc: Unique devlink instance index. - name: dl-dev-stats subset-of: devlink @@ -1311,6 +1315,7 @@ operations: attributes: - bus-name - dev-name + - index - reload-failed - dev-stats dump: diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index e7d6b6d13470..1ba3436db4ae 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -642,6 +642,8 @@ enum devlink_attr { DEVLINK_ATTR_PARAM_VALUE_DEFAULT, /* dynamic */ DEVLINK_ATTR_PARAM_RESET_DEFAULT, /* flag */ + DEVLINK_ATTR_INDEX, /* uint */ + /* Add new attributes above here, update the spec in * Documentation/netlink/specs/devlink.yaml and re-generate * net/devlink/netlink_gen.c. diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h index 1377864383bc..31fa98af418e 100644 --- a/net/devlink/devl_internal.h +++ b/net/devlink/devl_internal.h @@ -178,6 +178,8 @@ devlink_nl_put_handle(struct sk_buff *msg, struct devlink *devlink) return -EMSGSIZE; if (nla_put_string(msg, DEVLINK_ATTR_DEV_NAME, dev_name(devlink->dev))) return -EMSGSIZE; + if (nla_put_uint(msg, DEVLINK_ATTR_INDEX, devlink->index)) + return -EMSGSIZE; return 0; } diff --git a/net/devlink/port.c b/net/devlink/port.c index 93d8a25bb920..1ff609571ea4 100644 --- a/net/devlink/port.c +++ b/net/devlink/port.c @@ -222,6 +222,7 @@ size_t devlink_nl_port_handle_size(struct devlink_port *devlink_port) return nla_total_size(strlen(devlink->dev->bus->name) + 1) /* DEVLINK_ATTR_BUS_NAME */ + nla_total_size(strlen(dev_name(devlink->dev)) + 1) /* DEVLINK_ATTR_DEV_NAME */ + + nla_total_size(8) /* DEVLINK_ATTR_INDEX */ + nla_total_size(4); /* DEVLINK_ATTR_PORT_INDEX */ } -- cgit v1.2.3 From 725d5fdb7b9c01d9e7079682acf998703762475b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 12 Mar 2026 11:03:59 +0100 Subject: devlink: support index-based lookup via bus_name/dev_name handle Devlink instances without a backing device use bus_name "devlink_index" and dev_name set to the decimal index string. When user space sends this handle, detect the pattern and perform a direct xarray lookup by index instead of iterating all instances. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260312100407.551173-6-jiri@resnulli.us Signed-off-by: Jakub Kicinski --- include/uapi/linux/devlink.h | 2 ++ net/devlink/netlink.c | 9 +++++++++ 2 files changed, 11 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 1ba3436db4ae..7de2d8cc862f 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -19,6 +19,8 @@ #define DEVLINK_GENL_VERSION 0x1 #define DEVLINK_GENL_MCGRP_CONFIG_NAME "config" +#define DEVLINK_INDEX_BUS_NAME "devlink_index" + enum devlink_command { /* don't change the order or add anything between, this is ABI! */ DEVLINK_CMD_UNSPEC, diff --git a/net/devlink/netlink.c b/net/devlink/netlink.c index 9cba40285de4..fa38fca22fe4 100644 --- a/net/devlink/netlink.c +++ b/net/devlink/netlink.c @@ -203,6 +203,15 @@ devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs, busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]); devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]); + if (!strcmp(busname, DEVLINK_INDEX_BUS_NAME)) { + if (kstrtoul(devname, 10, &index)) + return ERR_PTR(-ENODEV); + devlink = devlinks_xa_lookup_get(net, index); + if (!devlink) + return ERR_PTR(-ENODEV); + goto found; + } + devlinks_xa_for_each_registered_get(net, index, devlink) { if (strcmp(devlink_bus_name(devlink), busname) == 0 && strcmp(devlink_dev_name(devlink), devname) == 0) -- cgit v1.2.3 From c841b676da98638f5ed8d3f2f449ddd02d9921aa Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Fri, 14 Nov 2025 11:39:40 +0100 Subject: ovpn: notify userspace on client float event Send a netlink notification when a client updates its remote UDP endpoint. The notification includes the new IP address, port, and scope ID (for IPv6). Cc: linux-kselftest@vger.kernel.org Cc: horms@kernel.org Cc: shuah@kernel.org Cc: donald.hunter@gmail.com Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli Reviewed-by: Sabrina Dubroca --- Documentation/netlink/specs/ovpn.yaml | 6 +++ drivers/net/ovpn/netlink.c | 82 +++++++++++++++++++++++++++++ drivers/net/ovpn/netlink.h | 2 + drivers/net/ovpn/peer.c | 2 + include/uapi/linux/ovpn.h | 1 + tools/testing/selftests/net/ovpn/ovpn-cli.c | 3 ++ 6 files changed, 96 insertions(+) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml index 1b91045cee2e..0d0c028bf96f 100644 --- a/Documentation/netlink/specs/ovpn.yaml +++ b/Documentation/netlink/specs/ovpn.yaml @@ -502,6 +502,12 @@ operations: - ifindex - keyconf + - + name: peer-float-ntf + doc: Notification about a peer floating (changing its remote UDP endpoint) + notify: peer-get + mcgrp: peers + mcast-groups: list: - diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index fed0e46b32a3..e10d7f9a28f5 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -1203,6 +1203,88 @@ err_free_msg: return ret; } +/** + * ovpn_nl_peer_float_notify - notify userspace about peer floating + * @peer: the floated peer + * @ss: sockaddr representing the new remote endpoint + * + * Return: 0 on success or a negative error code otherwise + */ +int ovpn_nl_peer_float_notify(struct ovpn_peer *peer, + const struct sockaddr_storage *ss) +{ + struct ovpn_socket *sock; + struct sockaddr_in6 *sa6; + struct sockaddr_in *sa; + struct sk_buff *msg; + struct nlattr *attr; + int ret = -EMSGSIZE; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, 0, 0, &ovpn_nl_family, 0, + OVPN_CMD_PEER_FLOAT_NTF); + if (!hdr) { + ret = -ENOBUFS; + goto err_free_msg; + } + + if (nla_put_u32(msg, OVPN_A_IFINDEX, peer->ovpn->dev->ifindex)) + goto err_cancel_msg; + + attr = nla_nest_start(msg, OVPN_A_PEER); + if (!attr) + goto err_cancel_msg; + + if (nla_put_u32(msg, OVPN_A_PEER_ID, peer->id)) + goto err_cancel_msg; + + if (ss->ss_family == AF_INET) { + sa = (struct sockaddr_in *)ss; + if (nla_put_in_addr(msg, OVPN_A_PEER_REMOTE_IPV4, + sa->sin_addr.s_addr) || + nla_put_net16(msg, OVPN_A_PEER_REMOTE_PORT, sa->sin_port)) + goto err_cancel_msg; + } else if (ss->ss_family == AF_INET6) { + sa6 = (struct sockaddr_in6 *)ss; + if (nla_put_in6_addr(msg, OVPN_A_PEER_REMOTE_IPV6, + &sa6->sin6_addr) || + nla_put_u32(msg, OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID, + sa6->sin6_scope_id) || + nla_put_net16(msg, OVPN_A_PEER_REMOTE_PORT, sa6->sin6_port)) + goto err_cancel_msg; + } else { + ret = -EAFNOSUPPORT; + goto err_cancel_msg; + } + + nla_nest_end(msg, attr); + genlmsg_end(msg, hdr); + + rcu_read_lock(); + sock = rcu_dereference(peer->sock); + if (!sock) { + ret = -EINVAL; + goto err_unlock; + } + genlmsg_multicast_netns(&ovpn_nl_family, sock_net(sock->sk), msg, + 0, OVPN_NLGRP_PEERS, GFP_ATOMIC); + rcu_read_unlock(); + + return 0; + +err_unlock: + rcu_read_unlock(); +err_cancel_msg: + genlmsg_cancel(msg, hdr); +err_free_msg: + nlmsg_free(msg); + return ret; +} + /** * ovpn_nl_key_swap_notify - notify userspace peer's key must be renewed * @peer: the peer whose key needs to be renewed diff --git a/drivers/net/ovpn/netlink.h b/drivers/net/ovpn/netlink.h index 8615dfc3c472..11ee7c681885 100644 --- a/drivers/net/ovpn/netlink.h +++ b/drivers/net/ovpn/netlink.h @@ -13,6 +13,8 @@ int ovpn_nl_register(void); void ovpn_nl_unregister(void); int ovpn_nl_peer_del_notify(struct ovpn_peer *peer); +int ovpn_nl_peer_float_notify(struct ovpn_peer *peer, + const struct sockaddr_storage *ss); int ovpn_nl_key_swap_notify(struct ovpn_peer *peer, u8 key_id); #endif /* _NET_OVPN_NETLINK_H_ */ diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 3716a1d82801..4e145b4497e6 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -287,6 +287,8 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) spin_unlock_bh(&peer->lock); + ovpn_nl_peer_float_notify(peer, &ss); + /* rehashing is required only in MP mode as P2P has one peer * only and thus there is no hashtable */ diff --git a/include/uapi/linux/ovpn.h b/include/uapi/linux/ovpn.h index 959b41def61f..0cce0d58b830 100644 --- a/include/uapi/linux/ovpn.h +++ b/include/uapi/linux/ovpn.h @@ -100,6 +100,7 @@ enum { OVPN_CMD_KEY_SWAP, OVPN_CMD_KEY_SWAP_NTF, OVPN_CMD_KEY_DEL, + OVPN_CMD_PEER_FLOAT_NTF, __OVPN_CMD_MAX, OVPN_CMD_MAX = (__OVPN_CMD_MAX - 1) diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c index 0f3babf19fd0..7178abae1b2f 100644 --- a/tools/testing/selftests/net/ovpn/ovpn-cli.c +++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c @@ -1516,6 +1516,9 @@ static int ovpn_handle_msg(struct nl_msg *msg, void *arg) case OVPN_CMD_PEER_DEL_NTF: fprintf(stdout, "received CMD_PEER_DEL_NTF\n"); break; + case OVPN_CMD_PEER_FLOAT_NTF: + fprintf(stdout, "received CMD_PEER_FLOAT_NTF\n"); + break; case OVPN_CMD_KEY_SWAP_NTF: fprintf(stdout, "received CMD_KEY_SWAP_NTF\n"); break; -- cgit v1.2.3 From 2e570a51408839b2079f3cb7e3944bf9b1184ee0 Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Wed, 9 Jul 2025 17:21:25 +0200 Subject: ovpn: add support for asymmetric peer IDs In order to support the multipeer architecture, upon connection setup each side of a tunnel advertises a unique ID that the other side must include in packets sent to them. Therefore when transmitting a packet, a peer inserts the recipient's advertised ID for that specific tunnel into the peer ID field. When receiving a packet, a peer expects to find its own unique receive ID for that specific tunnel in the peer ID field. Add support for the TX peer ID and embed it into transmitting packets. If no TX peer ID is specified, fallback to using the same peer ID both for RX and TX in order to be compatible with the non-multipeer compliant peers. Cc: horms@kernel.org Cc: donald.hunter@gmail.com Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli Reviewed-by: Sabrina Dubroca --- Documentation/netlink/specs/ovpn.yaml | 17 ++++++++++++++++- drivers/net/ovpn/crypto_aead.c | 2 +- drivers/net/ovpn/netlink-gen.c | 13 ++++++++++--- drivers/net/ovpn/netlink-gen.h | 6 +++--- drivers/net/ovpn/netlink.c | 14 ++++++++++++-- drivers/net/ovpn/peer.c | 4 ++++ drivers/net/ovpn/peer.h | 4 +++- include/uapi/linux/ovpn.h | 1 + 8 files changed, 50 insertions(+), 11 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml index 0d0c028bf96f..b0c782e59a32 100644 --- a/Documentation/netlink/specs/ovpn.yaml +++ b/Documentation/netlink/specs/ovpn.yaml @@ -43,7 +43,8 @@ attribute-sets: type: u32 doc: >- The unique ID of the peer in the device context. To be used to - identify peers during operations for a specific device + identify peers during operations for a specific device. + Also used to match packets received from this peer. checks: max: 0xFFFFFF - @@ -160,6 +161,16 @@ attribute-sets: name: link-tx-packets type: uint doc: Number of packets transmitted at the transport level + - + name: tx-id + type: u32 + doc: >- + The ID value used when transmitting packets to this peer. This + way outgoing packets can have a different ID than incoming ones. + Useful in multipeer-to-multipeer connections, where each peer + will advertise the tx-id to be used on the link. + checks: + max: 0xFFFFFF - name: peer-new-input subset-of: peer @@ -188,6 +199,8 @@ attribute-sets: name: keepalive-interval - name: keepalive-timeout + - + name: tx-id - name: peer-set-input subset-of: peer @@ -214,6 +227,8 @@ attribute-sets: name: keepalive-interval - name: keepalive-timeout + - + name: tx-id - name: peer-del-input subset-of: peer diff --git a/drivers/net/ovpn/crypto_aead.c b/drivers/net/ovpn/crypto_aead.c index 77be0942a269..59848c41b7b2 100644 --- a/drivers/net/ovpn/crypto_aead.c +++ b/drivers/net/ovpn/crypto_aead.c @@ -122,7 +122,7 @@ int ovpn_aead_encrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks, memcpy(skb->data, iv, OVPN_NONCE_WIRE_SIZE); /* add packet op as head of additional data */ - op = ovpn_opcode_compose(OVPN_DATA_V2, ks->key_id, peer->id); + op = ovpn_opcode_compose(OVPN_DATA_V2, ks->key_id, peer->tx_id); __skb_push(skb, OVPN_OPCODE_SIZE); BUILD_BUG_ON(sizeof(op) != OVPN_OPCODE_SIZE); *((__force __be32 *)skb->data) = htonl(op); diff --git a/drivers/net/ovpn/netlink-gen.c b/drivers/net/ovpn/netlink-gen.c index ecbe9dcf4f7d..2147cec7c2c5 100644 --- a/drivers/net/ovpn/netlink-gen.c +++ b/drivers/net/ovpn/netlink-gen.c @@ -16,6 +16,10 @@ static const struct netlink_range_validation ovpn_a_peer_id_range = { .max = 16777215ULL, }; +static const struct netlink_range_validation ovpn_a_peer_tx_id_range = { + .max = 16777215ULL, +}; + static const struct netlink_range_validation ovpn_a_keyconf_peer_id_range = { .max = 16777215ULL, }; @@ -51,7 +55,7 @@ const struct nla_policy ovpn_keydir_nl_policy[OVPN_A_KEYDIR_NONCE_TAIL + 1] = { [OVPN_A_KEYDIR_NONCE_TAIL] = NLA_POLICY_EXACT_LEN(OVPN_NONCE_TAIL_SIZE), }; -const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_LINK_TX_PACKETS + 1] = { +const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_TX_ID + 1] = { [OVPN_A_PEER_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_id_range), [OVPN_A_PEER_REMOTE_IPV4] = { .type = NLA_BE32, }, [OVPN_A_PEER_REMOTE_IPV6] = NLA_POLICY_EXACT_LEN(16), @@ -75,13 +79,14 @@ const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_LINK_TX_PACKETS + 1] = { [OVPN_A_PEER_LINK_TX_BYTES] = { .type = NLA_UINT, }, [OVPN_A_PEER_LINK_RX_PACKETS] = { .type = NLA_UINT, }, [OVPN_A_PEER_LINK_TX_PACKETS] = { .type = NLA_UINT, }, + [OVPN_A_PEER_TX_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_tx_id_range), }; const struct nla_policy ovpn_peer_del_input_nl_policy[OVPN_A_PEER_ID + 1] = { [OVPN_A_PEER_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_id_range), }; -const struct nla_policy ovpn_peer_new_input_nl_policy[OVPN_A_PEER_KEEPALIVE_TIMEOUT + 1] = { +const struct nla_policy ovpn_peer_new_input_nl_policy[OVPN_A_PEER_TX_ID + 1] = { [OVPN_A_PEER_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_id_range), [OVPN_A_PEER_REMOTE_IPV4] = { .type = NLA_BE32, }, [OVPN_A_PEER_REMOTE_IPV6] = NLA_POLICY_EXACT_LEN(16), @@ -94,9 +99,10 @@ const struct nla_policy ovpn_peer_new_input_nl_policy[OVPN_A_PEER_KEEPALIVE_TIME [OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16), [OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, }, [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, }, + [OVPN_A_PEER_TX_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_tx_id_range), }; -const struct nla_policy ovpn_peer_set_input_nl_policy[OVPN_A_PEER_KEEPALIVE_TIMEOUT + 1] = { +const struct nla_policy ovpn_peer_set_input_nl_policy[OVPN_A_PEER_TX_ID + 1] = { [OVPN_A_PEER_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_id_range), [OVPN_A_PEER_REMOTE_IPV4] = { .type = NLA_BE32, }, [OVPN_A_PEER_REMOTE_IPV6] = NLA_POLICY_EXACT_LEN(16), @@ -108,6 +114,7 @@ const struct nla_policy ovpn_peer_set_input_nl_policy[OVPN_A_PEER_KEEPALIVE_TIME [OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16), [OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, }, [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, }, + [OVPN_A_PEER_TX_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_tx_id_range), }; /* OVPN_CMD_PEER_NEW - do */ diff --git a/drivers/net/ovpn/netlink-gen.h b/drivers/net/ovpn/netlink-gen.h index b2301580770f..67cd85f86173 100644 --- a/drivers/net/ovpn/netlink-gen.h +++ b/drivers/net/ovpn/netlink-gen.h @@ -18,10 +18,10 @@ extern const struct nla_policy ovpn_keyconf_del_input_nl_policy[OVPN_A_KEYCONF_S extern const struct nla_policy ovpn_keyconf_get_nl_policy[OVPN_A_KEYCONF_CIPHER_ALG + 1]; extern const struct nla_policy ovpn_keyconf_swap_input_nl_policy[OVPN_A_KEYCONF_PEER_ID + 1]; extern const struct nla_policy ovpn_keydir_nl_policy[OVPN_A_KEYDIR_NONCE_TAIL + 1]; -extern const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_LINK_TX_PACKETS + 1]; +extern const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_TX_ID + 1]; extern const struct nla_policy ovpn_peer_del_input_nl_policy[OVPN_A_PEER_ID + 1]; -extern const struct nla_policy ovpn_peer_new_input_nl_policy[OVPN_A_PEER_KEEPALIVE_TIMEOUT + 1]; -extern const struct nla_policy ovpn_peer_set_input_nl_policy[OVPN_A_PEER_KEEPALIVE_TIMEOUT + 1]; +extern const struct nla_policy ovpn_peer_new_input_nl_policy[OVPN_A_PEER_TX_ID + 1]; +extern const struct nla_policy ovpn_peer_set_input_nl_policy[OVPN_A_PEER_TX_ID + 1]; int ovpn_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info); diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index e10d7f9a28f5..291e2e5bb450 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -305,6 +305,12 @@ static int ovpn_nl_peer_modify(struct ovpn_peer *peer, struct genl_info *info, dst_cache_reset(&peer->dst_cache); } + /* In a multipeer-to-multipeer setup we may have asymmetric peer IDs, + * that is peer->id might be different from peer->tx_id. + */ + if (attrs[OVPN_A_PEER_TX_ID]) + peer->tx_id = nla_get_u32(attrs[OVPN_A_PEER_TX_ID]); + if (attrs[OVPN_A_PEER_VPN_IPV4]) { rehash = true; peer->vpn_addrs.ipv4.s_addr = @@ -326,8 +332,8 @@ static int ovpn_nl_peer_modify(struct ovpn_peer *peer, struct genl_info *info, } netdev_dbg(peer->ovpn->dev, - "modify peer id=%u endpoint=%pIScp VPN-IPv4=%pI4 VPN-IPv6=%pI6c\n", - peer->id, &ss, + "modify peer id=%u tx_id=%u endpoint=%pIScp VPN-IPv4=%pI4 VPN-IPv6=%pI6c\n", + peer->id, peer->tx_id, &ss, &peer->vpn_addrs.ipv4.s_addr, &peer->vpn_addrs.ipv6); spin_unlock_bh(&peer->lock); @@ -373,6 +379,7 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info) } peer_id = nla_get_u32(attrs[OVPN_A_PEER_ID]); + peer = ovpn_peer_new(ovpn, peer_id); if (IS_ERR(peer)) { NL_SET_ERR_MSG_FMT_MOD(info->extack, @@ -572,6 +579,9 @@ static int ovpn_nl_send_peer(struct sk_buff *skb, const struct genl_info *info, if (nla_put_u32(skb, OVPN_A_PEER_ID, peer->id)) goto err; + if (nla_put_u32(skb, OVPN_A_PEER_TX_ID, peer->tx_id)) + goto err; + if (peer->vpn_addrs.ipv4.s_addr != htonl(INADDR_ANY)) if (nla_put_in_addr(skb, OVPN_A_PEER_VPN_IPV4, peer->vpn_addrs.ipv4.s_addr)) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 4e145b4497e6..26b55d813f0e 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -99,7 +99,11 @@ struct ovpn_peer *ovpn_peer_new(struct ovpn_priv *ovpn, u32 id) if (!peer) return ERR_PTR(-ENOMEM); + /* in the default case TX and RX IDs are the same. + * the user may set a different TX ID via netlink + */ peer->id = id; + peer->tx_id = id; peer->ovpn = ovpn; peer->vpn_addrs.ipv4.s_addr = htonl(INADDR_ANY); diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h index a1423f2b09e0..328401570cba 100644 --- a/drivers/net/ovpn/peer.h +++ b/drivers/net/ovpn/peer.h @@ -21,7 +21,8 @@ * struct ovpn_peer - the main remote peer object * @ovpn: main openvpn instance this peer belongs to * @dev_tracker: reference tracker for associated dev - * @id: unique identifier + * @id: unique identifier, used to match incoming packets + * @tx_id: identifier to be used in TX packets * @vpn_addrs: IP addresses assigned over the tunnel * @vpn_addrs.ipv4: IPv4 assigned to peer on the tunnel * @vpn_addrs.ipv6: IPv6 assigned to peer on the tunnel @@ -64,6 +65,7 @@ struct ovpn_peer { struct ovpn_priv *ovpn; netdevice_tracker dev_tracker; u32 id; + u32 tx_id; struct { struct in_addr ipv4; struct in6_addr ipv6; diff --git a/include/uapi/linux/ovpn.h b/include/uapi/linux/ovpn.h index 0cce0d58b830..06690090a1a9 100644 --- a/include/uapi/linux/ovpn.h +++ b/include/uapi/linux/ovpn.h @@ -55,6 +55,7 @@ enum { OVPN_A_PEER_LINK_TX_BYTES, OVPN_A_PEER_LINK_RX_PACKETS, OVPN_A_PEER_LINK_TX_PACKETS, + OVPN_A_PEER_TX_ID, __OVPN_A_PEER_MAX, OVPN_A_PEER_MAX = (__OVPN_A_PEER_MAX - 1) -- cgit v1.2.3 From a11661a58c06f7fdfef03a368ef20d05a4ea4ed0 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Tue, 17 Mar 2026 11:16:03 +0000 Subject: iommufd: Report ATS not supported status via IOMMU_GET_HW_INFO If the IOMMU driver reports that ATS is not supported for a device, set the IOMMU_HW_CAP_PCI_ATS_NOT_SUPPORTED flag in the returned hardware capabilities. This uses a negative flag for UAPI compatibility. Existing userspace assumes ATS is supported if no flag is present. This also ensures that new userspace works correctly on both old and new kernels, where a zero value implies ATS support. When this flag is set, ATS cannot be used for the device. When it is clear, ATS may be enabled when an appropriate HWPT is attached. Reviewed-by: Samiullah Khawaja Reviewed-by: Jason Gunthorpe Signed-off-by: Shameer Kolothum Signed-off-by: Joerg Roedel --- drivers/iommu/iommufd/device.c | 4 ++++ include/uapi/linux/iommufd.h | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'include/uapi/linux') diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 344d620cdecc..92c5d5ef8d00 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -1624,6 +1624,10 @@ int iommufd_get_hw_info(struct iommufd_ucmd *ucmd) if (device_iommu_capable(idev->dev, IOMMU_CAP_DIRTY_TRACKING)) cmd->out_capabilities |= IOMMU_HW_CAP_DIRTY_TRACKING; + /* Report when ATS cannot be used for this device */ + if (!device_iommu_capable(idev->dev, IOMMU_CAP_PCI_ATS_SUPPORTED)) + cmd->out_capabilities |= IOMMU_HW_CAP_PCI_ATS_NOT_SUPPORTED; + cmd->out_max_pasid_log2 = 0; /* * Currently, all iommu drivers enable PASID in the probe_device() diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index 1dafbc552d37..507ee9bcba01 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -695,11 +695,15 @@ enum iommu_hw_info_type { * @IOMMU_HW_CAP_PCI_PASID_PRIV: Privileged Mode Supported, user ignores it * when the struct * iommu_hw_info::out_max_pasid_log2 is zero. + * @IOMMU_HW_CAP_PCI_ATS_NOT_SUPPORTED: ATS is not supported or cannot be used + * on this device (absence implies ATS + * may be enabled) */ enum iommufd_hw_capabilities { IOMMU_HW_CAP_DIRTY_TRACKING = 1 << 0, IOMMU_HW_CAP_PCI_PASID_EXEC = 1 << 1, IOMMU_HW_CAP_PCI_PASID_PRIV = 1 << 2, + IOMMU_HW_CAP_PCI_ATS_NOT_SUPPORTED = 1 << 3, }; /** -- cgit v1.2.3 From de9e2b3d88af36411301c049a1b049f3e4fe0757 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 3 Mar 2026 21:24:17 +0200 Subject: uapi: Provide DIV_ROUND_CLOSEST() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently DIV_ROUND_CLOSEST() is only available for the kernel via include/linux/math.h. Expose it to userland as well by adding __KERNEL_DIV_ROUND_CLOSEST() as a common definition in uapi. Additionally, ensure it allows building ISO C applications by switching from the 'typeof' GNU extension to the ISO-friendly __typeof__. Reviewed-by: NĂ­colas F. R. A. Prado Tested-by: Diederik de Haas Acked-by: Andy Shevchenko Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260303-rk3588-bgcolor-v8-1-fee377037ad1@collabora.com Signed-off-by: Daniel Stone --- include/linux/math.h | 18 +----------------- include/uapi/linux/const.h | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 17 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/linux/math.h b/include/linux/math.h index 6dc1d1d32fbc..1e8fb3efbc8c 100644 --- a/include/linux/math.h +++ b/include/linux/math.h @@ -89,23 +89,7 @@ } \ ) -/* - * Divide positive or negative dividend by positive or negative divisor - * and round to closest integer. Result is undefined for negative - * divisors if the dividend variable type is unsigned and for negative - * dividends if the divisor variable type is unsigned. - */ -#define DIV_ROUND_CLOSEST(x, divisor)( \ -{ \ - typeof(x) __x = x; \ - typeof(divisor) __d = divisor; \ - (((typeof(x))-1) > 0 || \ - ((typeof(divisor))-1) > 0 || \ - (((__x) > 0) == ((__d) > 0))) ? \ - (((__x) + ((__d) / 2)) / (__d)) : \ - (((__x) - ((__d) / 2)) / (__d)); \ -} \ -) +#define DIV_ROUND_CLOSEST __KERNEL_DIV_ROUND_CLOSEST /* * Same as above but for u64 dividends. divisor must be a 32-bit * number. diff --git a/include/uapi/linux/const.h b/include/uapi/linux/const.h index b8f629ef135f..565f309b9df8 100644 --- a/include/uapi/linux/const.h +++ b/include/uapi/linux/const.h @@ -50,4 +50,22 @@ #define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +/* + * Divide positive or negative dividend by positive or negative divisor + * and round to closest integer. Result is undefined for negative + * divisors if the dividend variable type is unsigned and for negative + * dividends if the divisor variable type is unsigned. + */ +#define __KERNEL_DIV_ROUND_CLOSEST(x, divisor) \ +({ \ + __typeof__(x) __x = x; \ + __typeof__(divisor) __d = divisor; \ + \ + (((__typeof__(x))-1) > 0 || \ + ((__typeof__(divisor))-1) > 0 || \ + (((__x) > 0) == ((__d) > 0))) ? \ + (((__x) + ((__d) / 2)) / (__d)) : \ + (((__x) - ((__d) / 2)) / (__d)); \ +}) + #endif /* _UAPI_LINUX_CONST_H */ -- cgit v1.2.3 From dc3d720e12f602059490c1ab2bfee84a7465998f Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Tue, 17 Mar 2026 12:18:05 -0700 Subject: net: ethtool: add ethtool COALESCE_RX_CQE_FRAMES/NSECS Add two parameters for drivers supporting Rx CQE coalescing / descriptor writeback. ETHTOOL_A_COALESCE_RX_CQE_FRAMES: Maximum number of frames that can be coalesced into a CQE or writeback. ETHTOOL_A_COALESCE_RX_CQE_NSECS: Max time in nanoseconds after the first packet arrival in a coalesced CQE or writeback to be sent. Signed-off-by: Haiyang Zhang Link: https://patch.msgid.link/20260317191826.1346111-2-haiyangz@linux.microsoft.com Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/ethtool.yaml | 8 ++++++++ Documentation/networking/ethtool-netlink.rst | 11 +++++++++++ include/linux/ethtool.h | 6 +++++- include/uapi/linux/ethtool_netlink_generated.h | 2 ++ net/ethtool/coalesce.c | 14 +++++++++++++- 5 files changed, 39 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml index a05b5425b76a..5dd4d1b5d94b 100644 --- a/Documentation/netlink/specs/ethtool.yaml +++ b/Documentation/netlink/specs/ethtool.yaml @@ -857,6 +857,12 @@ attribute-sets: name: tx-profile type: nest nested-attributes: profile + - + name: rx-cqe-frames + type: u32 + - + name: rx-cqe-nsecs + type: u32 - name: pause-stat @@ -2253,6 +2259,8 @@ operations: - tx-aggr-time-usecs - rx-profile - tx-profile + - rx-cqe-frames + - rx-cqe-nsecs dump: *coalesce-get-op - name: coalesce-set diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 32179168eb73..e92abf45faf5 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -1076,6 +1076,8 @@ Kernel response contents: ``ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS`` u32 time (us), aggr, Tx ``ETHTOOL_A_COALESCE_RX_PROFILE`` nested profile of DIM, Rx ``ETHTOOL_A_COALESCE_TX_PROFILE`` nested profile of DIM, Tx + ``ETHTOOL_A_COALESCE_RX_CQE_FRAMES`` u32 max packets, Rx CQE + ``ETHTOOL_A_COALESCE_RX_CQE_NSECS`` u32 delay (ns), Rx CQE =========================================== ====== ======================= Attributes are only included in reply if their value is not zero or the @@ -1109,6 +1111,13 @@ well with frequent small-sized URBs transmissions. to DIM parameters, see `Generic Network Dynamic Interrupt Moderation (Net DIM) `_. +Rx CQE coalescing allows multiple received packets to be coalesced into a +single Completion Queue Entry (CQE) or descriptor writeback. +``ETHTOOL_A_COALESCE_RX_CQE_FRAMES`` describes the maximum number of +frames that can be coalesced into a CQE or writeback. +``ETHTOOL_A_COALESCE_RX_CQE_NSECS`` describes max time in nanoseconds after +the first packet arrival in a coalesced CQE or writeback to be sent. + COALESCE_SET ============ @@ -1147,6 +1156,8 @@ Request contents: ``ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS`` u32 time (us), aggr, Tx ``ETHTOOL_A_COALESCE_RX_PROFILE`` nested profile of DIM, Rx ``ETHTOOL_A_COALESCE_TX_PROFILE`` nested profile of DIM, Tx + ``ETHTOOL_A_COALESCE_RX_CQE_FRAMES`` u32 max packets, Rx CQE + ``ETHTOOL_A_COALESCE_RX_CQE_NSECS`` u32 delay (ns), Rx CQE =========================================== ====== ======================= Request is rejected if it attributes declared as unsupported by driver (i.e. diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 83c375840835..656d465bcd06 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -332,6 +332,8 @@ struct kernel_ethtool_coalesce { u32 tx_aggr_max_bytes; u32 tx_aggr_max_frames; u32 tx_aggr_time_usecs; + u32 rx_cqe_frames; + u32 rx_cqe_nsecs; }; /** @@ -380,7 +382,9 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, #define ETHTOOL_COALESCE_TX_AGGR_TIME_USECS BIT(26) #define ETHTOOL_COALESCE_RX_PROFILE BIT(27) #define ETHTOOL_COALESCE_TX_PROFILE BIT(28) -#define ETHTOOL_COALESCE_ALL_PARAMS GENMASK(28, 0) +#define ETHTOOL_COALESCE_RX_CQE_FRAMES BIT(29) +#define ETHTOOL_COALESCE_RX_CQE_NSECS BIT(30) +#define ETHTOOL_COALESCE_ALL_PARAMS GENMASK(30, 0) #define ETHTOOL_COALESCE_USECS \ (ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_TX_USECS) diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h index 114b83017297..8134baf7860f 100644 --- a/include/uapi/linux/ethtool_netlink_generated.h +++ b/include/uapi/linux/ethtool_netlink_generated.h @@ -371,6 +371,8 @@ enum { ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS, ETHTOOL_A_COALESCE_RX_PROFILE, ETHTOOL_A_COALESCE_TX_PROFILE, + ETHTOOL_A_COALESCE_RX_CQE_FRAMES, + ETHTOOL_A_COALESCE_RX_CQE_NSECS, __ETHTOOL_A_COALESCE_CNT, ETHTOOL_A_COALESCE_MAX = (__ETHTOOL_A_COALESCE_CNT - 1) diff --git a/net/ethtool/coalesce.c b/net/ethtool/coalesce.c index 3e18ca1ccc5e..349bb02c517a 100644 --- a/net/ethtool/coalesce.c +++ b/net/ethtool/coalesce.c @@ -118,6 +118,8 @@ static int coalesce_reply_size(const struct ethnl_req_info *req_base, nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_BYTES */ nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_FRAMES */ nla_total_size(sizeof(u32)) + /* _TX_AGGR_TIME_USECS */ + nla_total_size(sizeof(u32)) + /* _RX_CQE_FRAMES */ + nla_total_size(sizeof(u32)) + /* _RX_CQE_NSECS */ total_modersz * 2; /* _{R,T}X_PROFILE */ } @@ -269,7 +271,11 @@ static int coalesce_fill_reply(struct sk_buff *skb, coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES, kcoal->tx_aggr_max_frames, supported) || coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS, - kcoal->tx_aggr_time_usecs, supported)) + kcoal->tx_aggr_time_usecs, supported) || + coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_CQE_FRAMES, + kcoal->rx_cqe_frames, supported) || + coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_CQE_NSECS, + kcoal->rx_cqe_nsecs, supported)) return -EMSGSIZE; if (!req_base->dev || !req_base->dev->irq_moder) @@ -338,6 +344,8 @@ const struct nla_policy ethnl_coalesce_set_policy[] = { [ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES] = { .type = NLA_U32 }, [ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES] = { .type = NLA_U32 }, [ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS] = { .type = NLA_U32 }, + [ETHTOOL_A_COALESCE_RX_CQE_FRAMES] = { .type = NLA_U32 }, + [ETHTOOL_A_COALESCE_RX_CQE_NSECS] = { .type = NLA_U32 }, [ETHTOOL_A_COALESCE_RX_PROFILE] = NLA_POLICY_NESTED(coalesce_profile_policy), [ETHTOOL_A_COALESCE_TX_PROFILE] = @@ -570,6 +578,10 @@ __ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info, tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES], &mod); ethnl_update_u32(&kernel_coalesce.tx_aggr_time_usecs, tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], &mod); + ethnl_update_u32(&kernel_coalesce.rx_cqe_frames, + tb[ETHTOOL_A_COALESCE_RX_CQE_FRAMES], &mod); + ethnl_update_u32(&kernel_coalesce.rx_cqe_nsecs, + tb[ETHTOOL_A_COALESCE_RX_CQE_NSECS], &mod); if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_RX) { ret = ethnl_update_profile(dev, &dev->irq_moder->rx_profile, -- cgit v1.2.3 From 7da9261bab0a82bdbc4aafd2ad4bc3529b7cb772 Mon Sep 17 00:00:00 2001 From: Yang Xiuwei Date: Tue, 17 Mar 2026 15:22:24 +0800 Subject: bsg: add bsg_uring_cmd uapi structure Add the bsg_uring_cmd structure to the BSG UAPI header to support io_uring-based SCSI passthrough operations via IORING_OP_URING_CMD. Signed-off-by: Yang Xiuwei Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260317072226.2598233-2-yangxiuwei@kylinos.cn Signed-off-by: Jens Axboe --- include/uapi/linux/bsg.h | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/bsg.h b/include/uapi/linux/bsg.h index cd6302def5ed..6cff77f5b857 100644 --- a/include/uapi/linux/bsg.h +++ b/include/uapi/linux/bsg.h @@ -2,6 +2,9 @@ #ifndef _UAPIBSG_H #define _UAPIBSG_H +#ifdef __KERNEL__ +#include +#endif /* __KERNEL__ */ #include #define BSG_PROTOCOL_SCSI 0 @@ -63,5 +66,77 @@ struct sg_io_v4 { __u32 padding; }; +struct bsg_uring_cmd { + __u64 request; /* [i], [*i] command descriptor address */ + __u32 request_len; /* [i] command descriptor length in bytes */ + __u32 protocol; /* [i] protocol type (BSG_PROTOCOL_*) */ + __u32 subprotocol; /* [i] subprotocol type (BSG_SUB_PROTOCOL_*) */ + __u32 max_response_len; /* [i] response buffer size in bytes */ + + __u64 response; /* [i], [*o] response data address */ + __u64 dout_xferp; /* [i], [*i] */ + __u32 dout_xfer_len; /* [i] bytes to be transferred to device */ + __u32 dout_iovec_count; /* [i] 0 -> "flat" dout transfer else + * dout_xferp points to array of iovec + */ + __u64 din_xferp; /* [i], [*o] */ + __u32 din_xfer_len; /* [i] bytes to be transferred from device */ + __u32 din_iovec_count; /* [i] 0 -> "flat" din transfer */ + + __u32 timeout_ms; /* [i] timeout in milliseconds */ + __u8 reserved[12]; /* reserved for future extension */ +}; + +#ifdef __KERNEL__ +/* Must match IORING_OP_URING_CMD payload size (e.g. SQE128). */ +static_assert(sizeof(struct bsg_uring_cmd) == 80); +#endif /* __KERNEL__ */ + + +/* + * SCSI BSG io_uring completion (res2, 64-bit) + * + * When using BSG_PROTOCOL_SCSI + BSG_SUB_PROTOCOL_SCSI_CMD with + * IORING_OP_URING_CMD, the completion queue entry (CQE) contains: + * - result: errno (0 on success) + * - res2: packed SCSI status + * + * res2 bit layout: + * [0..7] device_status (SCSI status byte, e.g. CHECK_CONDITION) + * [8..15] driver_status (e.g. DRIVER_SENSE when sense data is valid) + * [16..23] host_status (e.g. DID_OK, DID_TIME_OUT) + * [24..31] sense_len_wr (bytes of sense data written to response buffer) + * [32..63] resid_len (residual transfer length) + */ +static inline __u8 bsg_scsi_res2_device_status(__u64 res2) +{ + return res2 & 0xff; +} +static inline __u8 bsg_scsi_res2_driver_status(__u64 res2) +{ + return res2 >> 8; +} +static inline __u8 bsg_scsi_res2_host_status(__u64 res2) +{ + return res2 >> 16; +} +static inline __u8 bsg_scsi_res2_sense_len(__u64 res2) +{ + return res2 >> 24; +} +static inline __u32 bsg_scsi_res2_resid_len(__u64 res2) +{ + return res2 >> 32; +} +static inline __u64 bsg_scsi_res2_build(__u8 device_status, __u8 driver_status, + __u8 host_status, __u8 sense_len_wr, + __u32 resid_len) +{ + return ((__u64)(__u32)(resid_len) << 32) | + ((__u64)sense_len_wr << 24) | + ((__u64)host_status << 16) | + ((__u64)driver_status << 8) | + (__u64)device_status; +} #endif /* _UAPIBSG_H */ -- cgit v1.2.3 From c547c51ff4d44c787330506737c5ce7808e536cc Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Thu, 19 Mar 2026 15:51:47 +0000 Subject: KVM: arm64: gic-v5: Add ARM_VGIC_V5 device to KVM headers This is the base GICv5 device which is to be used with the KVM_CREATE_DEVICE ioctl to create a GICv5-based vgic. Signed-off-by: Sascha Bischoff Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/20260319154937.3619520-9-sascha.bischoff@arm.com Signed-off-by: Marc Zyngier --- include/uapi/linux/kvm.h | 2 ++ tools/include/uapi/linux/kvm.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 80364d4dbebb..d0c0c8605976 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1224,6 +1224,8 @@ enum kvm_device_type { #define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_PCHPIC, #define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC + KVM_DEV_TYPE_ARM_VGIC_V5, +#define KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_MAX, diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index 65500f5db379..713e4360eca0 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -1220,6 +1220,8 @@ enum kvm_device_type { #define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_PCHPIC, #define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC + KVM_DEV_TYPE_ARM_VGIC_V5, +#define KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_MAX, -- cgit v1.2.3 From 02256acf1e81e42f6338a39020bf2de9807c33d7 Mon Sep 17 00:00:00 2001 From: Joseph Salisbury Date: Mon, 16 Mar 2026 14:56:17 -0400 Subject: vfio: uapi: fix comment typo The file contains a spelling error in a source comment (succes). Typos in comments reduce readability and make text searches less reliable for developers and maintainers. Replace 'succes' with 'success' in the affected comment. This is a comment-only cleanup and does not change behavior. Signed-off-by: Joseph Salisbury Link: https://lore.kernel.org/r/20260316185617.166414-1-joseph.salisbury@oracle.com Signed-off-by: Alex Williamson --- include/uapi/linux/vfio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index bb7b89330d35..63d56c1fbf6f 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -141,7 +141,7 @@ struct vfio_info_cap_header { * * Retrieve information about the group. Fills in provided * struct vfio_group_info. Caller sets argsz. - * Return: 0 on succes, -errno on failure. + * Return: 0 on success, -errno on failure. * Availability: Always */ struct vfio_group_status { -- cgit v1.2.3 From d7140b5dde459048da52cfc0494228055f7e2fb8 Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Tue, 17 Mar 2026 18:17:48 +0200 Subject: vfio: Define uAPI for re-init initial bytes during the PRE_COPY phase As currently defined, initial_bytes is monotonically decreasing and precedes dirty_bytes when reading from the saving file descriptor. The transition from initial_bytes to dirty_bytes is unidirectional and irreversible. The initial_bytes are considered as critical data that is highly recommended to be transferred to the target as part of PRE_COPY, without this data, the PRE_COPY phase would be ineffective. We come to solve the case when a new chunk of critical data is introduced during the PRE_COPY phase and the driver would like to report an entirely new value for the initial_bytes. For that, we extend the VFIO_MIG_GET_PRECOPY_INFO ioctl with an output flag named VFIO_PRECOPY_INFO_REINIT to allow drivers reporting a new initial_bytes value during the PRE_COPY phase. Currently, existing VFIO_MIG_GET_PRECOPY_INFO implementations don't assign info.flags before copy_to_user(), this effectively echoes userspace-provided flags back as output, preventing the field from being used to report new reliable data from the drivers. Reliable use of the new VFIO_PRECOPY_INFO_REINIT flag requires userspace to explicitly opt in by enabling the VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2 device feature. When the caller opts in, the driver may report an entirely new value for initial_bytes. It may be larger, it may be smaller, it may include the previous unread initial_bytes, it may discard the previous unread initial_bytes, up to the driver logic and state. The presence of the VFIO_PRECOPY_INFO_REINIT output flag set by the driver indicates that new initial data is present on the stream. Once the caller sees this flag, the initial_bytes value should be re-evaluated relative to the readiness state for transition to STOP_COPY. Signed-off-by: Yishai Hadas Link: https://lore.kernel.org/r/20260317161753.18964-2-yishaih@nvidia.com Signed-off-by: Alex Williamson --- include/uapi/linux/vfio.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 63d56c1fbf6f..5de618a3a5ee 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -1266,6 +1266,19 @@ enum vfio_device_mig_state { * The initial_bytes field indicates the amount of initial precopy * data available from the device. This field should have a non-zero initial * value and decrease as migration data is read from the device. + * The presence of the VFIO_PRECOPY_INFO_REINIT output flag indicates + * that new initial data is present on the stream. + * The new initial data may result, for example, from device reconfiguration + * during migration that requires additional initialization data. + * In that case initial_bytes may report a non-zero value irrespective of + * any previously reported values, which progresses towards zero as precopy + * data is read from the data stream. dirty_bytes is also reset + * to zero and represents the state change of the device relative to the new + * initial_bytes. + * VFIO_PRECOPY_INFO_REINIT can be reported only after userspace opts in to + * VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2. Without this opt-in, the flags field + * of struct vfio_precopy_info is reserved for bug-compatibility reasons. + * * It is recommended to leave PRE_COPY for STOP_COPY only after this field * reaches zero. Leaving PRE_COPY earlier might make things slower. * @@ -1301,6 +1314,7 @@ enum vfio_device_mig_state { struct vfio_precopy_info { __u32 argsz; __u32 flags; +#define VFIO_PRECOPY_INFO_REINIT (1 << 0) /* output - new initial data is present */ __aligned_u64 initial_bytes; __aligned_u64 dirty_bytes; }; @@ -1510,6 +1524,16 @@ struct vfio_device_feature_dma_buf { struct vfio_region_dma_range dma_ranges[] __counted_by(nr_ranges); }; +/* + * Enables the migration precopy_info_v2 behaviour. + * + * VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2. + * + * On SET, enables the v2 pre_copy_info behaviour, where the + * vfio_precopy_info.flags is a valid output field. + */ +#define VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2 12 + /* -------- API for Type1 VFIO IOMMU -------- */ /** -- cgit v1.2.3 From 1ccc861dbbc1b4c6a896e95f815ef3310775c33f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 26 Feb 2026 14:11:12 -0800 Subject: um: time-travel: clean up kernel-doc warnings Repair all kernel-doc warnings in um_timetravel.h: - add one enum description - mark "reserve" as private - use a leading '@' on current_time Warning: include/uapi/linux/um_timetravel.h:59 Enum value 'UM_TIMETRAVEL_SHARED_MAX_FDS' not described in enum 'um_timetravel_shared_mem_fds' Warning: include/uapi/linux/um_timetravel.h:245 union member 'reserve' not described in 'um_timetravel_schedshm_client' Warning: include/uapi/linux/um_timetravel.h:288 struct member 'current_time' not described in 'um_timetravel_schedshm' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260226221112.1042008-1-rdunlap@infradead.org Signed-off-by: Johannes Berg --- include/uapi/linux/um_timetravel.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/um_timetravel.h b/include/uapi/linux/um_timetravel.h index 546a690b0346..fa7c75334f2e 100644 --- a/include/uapi/linux/um_timetravel.h +++ b/include/uapi/linux/um_timetravel.h @@ -56,6 +56,9 @@ enum um_timetravel_shared_mem_fds { * in the control message */ UM_TIMETRAVEL_SHARED_LOGFD, + /** + * @UM_TIMETRAVEL_SHARED_MAX_FDS: number of fds listed here + */ UM_TIMETRAVEL_SHARED_MAX_FDS, }; @@ -242,6 +245,7 @@ union um_timetravel_schedshm_client { __u64 req_time; __u64 name; }; + /* private: */ char reserve[128]; /* reserved for future usage */ }; @@ -264,7 +268,7 @@ union um_timetravel_schedshm_client { * is made by any client. Clients also must update this value when they * insert/update an own request into the shared memory while not running * themselves, and the new request is before than the current value. - * current_time: Current time, can only be set by the client in running state + * @current_time: Current time, can only be set by the client in running state * (indicated by @running_id), though that client may only run until @free_until, * so it must remain smaller than @free_until. * @running_id: The current client in state running, set before a client is -- cgit v1.2.3 From 701f7f4fbabbf4989ba6fbf033b160dd943221d5 Mon Sep 17 00:00:00 2001 From: Emanuele Rocca Date: Mon, 23 Mar 2026 14:02:16 +0100 Subject: pidfds: add coredump_code field to pidfd_info The struct pidfd_info currently exposes in a field called coredump_signal the signal number (si_signo) that triggered the dump (for example, 11 for SIGSEGV). However, it is also valuable to understand the reason why that signal was sent. This additional context is provided by the signal code (si_code), such as 2 for SEGV_ACCERR. Add a new field to struct pidfd_info called coredump_code with the value of si_code for the benefit of sysadmins who pipe core dumps to user-space programs for later analysis. The following snippet illustrates a simplified C program that consumes coredump_signal and coredump_code, and then logs core dump signals and codes to a file: int pidfd = (int)atoi(argv[1]); struct pidfd_info info = { .mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP, }; if (ioctl(pidfd, PIDFD_GET_INFO, &info) == 0) if (info.mask & PIDFD_INFO_COREDUMP) fprintf(f, "PID=%d, si_signo: %d si_code: %d\n", info.pid, info.coredump_signal, info.coredump_code); Assuming the program is installed under /usr/local/bin/core-logger, core dump processing can be enabled by setting /proc/sys/kernel/core_pattern to '|/usr/local/bin/dumpstuff %F'. systemd-coredump(8) already uses pidfds to process core dumps, and it could be extended to include the values of coredump_code too. Signed-off-by: Emanuele Rocca Link: https://patch.msgid.link/acE52HIFivNZN3nE@NH27D9T0LF Acked-by: Oleg Nesterov Signed-off-by: Christian Brauner --- fs/pidfs.c | 12 ++++++++---- include/uapi/linux/pidfd.h | 4 ++++ 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/fs/pidfs.c b/fs/pidfs.c index a8d1bca0395d..2acf84670578 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -57,6 +57,7 @@ struct pidfs_attr { }; __u32 coredump_mask; __u32 coredump_signal; + __u32 coredump_code; }; static struct rhashtable pidfs_ino_ht; @@ -333,7 +334,8 @@ static __u32 pidfs_coredump_mask(unsigned long mm_flags) PIDFD_INFO_EXIT | \ PIDFD_INFO_COREDUMP | \ PIDFD_INFO_SUPPORTED_MASK | \ - PIDFD_INFO_COREDUMP_SIGNAL) + PIDFD_INFO_COREDUMP_SIGNAL | \ + PIDFD_INFO_COREDUMP_CODE) static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg) { @@ -347,7 +349,7 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg) const struct cred *c; __u64 mask; - BUILD_BUG_ON(sizeof(struct pidfd_info) != PIDFD_INFO_SIZE_VER2); + BUILD_BUG_ON(sizeof(struct pidfd_info) != PIDFD_INFO_SIZE_VER3); if (!uinfo) return -EINVAL; @@ -380,9 +382,10 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg) if (mask & PIDFD_INFO_COREDUMP) { if (test_bit(PIDFS_ATTR_BIT_COREDUMP, &attr->attr_mask)) { smp_rmb(); - kinfo.mask |= PIDFD_INFO_COREDUMP | PIDFD_INFO_COREDUMP_SIGNAL; + kinfo.mask |= PIDFD_INFO_COREDUMP | PIDFD_INFO_COREDUMP_SIGNAL | PIDFD_INFO_COREDUMP_CODE; kinfo.coredump_mask = attr->coredump_mask; kinfo.coredump_signal = attr->coredump_signal; + kinfo.coredump_code = attr->coredump_code; } } @@ -755,8 +758,9 @@ void pidfs_coredump(const struct coredump_params *cprm) PIDFD_COREDUMPED; /* If coredumping is set to skip we should never end up here. */ VFS_WARN_ON_ONCE(attr->coredump_mask & PIDFD_COREDUMP_SKIP); - /* Expose the signal number that caused the coredump. */ + /* Expose the signal number and code that caused the coredump. */ attr->coredump_signal = cprm->siginfo->si_signo; + attr->coredump_code = cprm->siginfo->si_code; smp_wmb(); set_bit(PIDFS_ATTR_BIT_COREDUMP, &attr->attr_mask); } diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h index 9281956a9f32..0919246a1611 100644 --- a/include/uapi/linux/pidfd.h +++ b/include/uapi/linux/pidfd.h @@ -29,10 +29,12 @@ #define PIDFD_INFO_COREDUMP (1UL << 4) /* Only returned if requested. */ #define PIDFD_INFO_SUPPORTED_MASK (1UL << 5) /* Want/got supported mask flags */ #define PIDFD_INFO_COREDUMP_SIGNAL (1UL << 6) /* Always returned if PIDFD_INFO_COREDUMP is requested. */ +#define PIDFD_INFO_COREDUMP_CODE (1UL << 7) /* Always returned if PIDFD_INFO_COREDUMP is requested. */ #define PIDFD_INFO_SIZE_VER0 64 /* sizeof first published struct */ #define PIDFD_INFO_SIZE_VER1 72 /* sizeof second published struct */ #define PIDFD_INFO_SIZE_VER2 80 /* sizeof third published struct */ +#define PIDFD_INFO_SIZE_VER3 88 /* sizeof fourth published struct */ /* * Values for @coredump_mask in pidfd_info. @@ -99,6 +101,8 @@ struct pidfd_info { struct /* coredump info */ { __u32 coredump_mask; __u32 coredump_signal; + __u32 coredump_code; + __u32 coredump_pad; /* align supported_mask to 8 bytes */ }; __u64 supported_mask; /* Mask flags that this kernel supports */ }; -- cgit v1.2.3 From 9dcef98dbee35b8ae784df04c041efffdd42a69c Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 12 Mar 2026 17:36:35 -0700 Subject: iommu/tegra241-cmdqv: Update uAPI to clarify HYP_OWN requirement >From hardware implementation perspective, a guest tegra241-cmdqv hardware is different than the host hardware: - Host HW is backed by a VINTF (HYP_OWN=1) - Guest HW is backed by a VINTF (HYP_OWN=0) The kernel driver has an implementation requirement of the HYP_OWN bit in the VM. So, VMM must follow that to allow the same copy of Linux to work. Add this requirement to the uAPI, which is currently missing. Fixes: 4dc0d12474f9 ("iommu/tegra241-cmdqv: Add user-space use support") Signed-off-by: Nicolin Chen Reviewed-by: Eric Auger Reviewed-by: Jason Gunthorpe Signed-off-by: Will Deacon --- include/uapi/linux/iommufd.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index 1dafbc552d37..f63edbe71d54 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -1052,6 +1052,11 @@ struct iommu_fault_alloc { enum iommu_viommu_type { IOMMU_VIOMMU_TYPE_DEFAULT = 0, IOMMU_VIOMMU_TYPE_ARM_SMMUV3 = 1, + /* + * TEGRA241_CMDQV requirements (otherwise, VCMDQs will not work) + * - Kernel will allocate a VINTF (HYP_OWN=0) to back this VIOMMU. So, + * VMM must wire the HYP_OWN bit to 0 in guest VINTF_CONFIG register + */ IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV = 2, }; -- cgit v1.2.3 From fd4cb4511b5904188a17af73c7533da16420d4e8 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 28 Jan 2026 17:12:12 -0800 Subject: tracing: trace_mmap.h: fix a kernel-doc warning Add a description of struct reader to resolve a kernel-doc warning: Warning: include/uapi/linux/trace_mmap.h:43 struct member 'reader' not described in 'trace_buffer_meta' Signed-off-by: Randy Dunlap Acked-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt (Google) --- include/uapi/linux/trace_mmap.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/trace_mmap.h b/include/uapi/linux/trace_mmap.h index c102ef35d11e..99653f259a7a 100644 --- a/include/uapi/linux/trace_mmap.h +++ b/include/uapi/linux/trace_mmap.h @@ -10,6 +10,7 @@ * @meta_struct_len: Size of this structure. * @subbuf_size: Size of each sub-buffer. * @nr_subbufs: Number of subbfs in the ring-buffer, including the reader. + * @reader: The reader composite info structure * @reader.lost_events: Number of events lost at the time of the reader swap. * @reader.id: subbuf ID of the current reader. ID range [0 : @nr_subbufs - 1] * @reader.read: Number of bytes read on the reader subbuf. -- cgit v1.2.3 From f9909cf0a2dcc9e99377f3fcc965ccd93e518e34 Mon Sep 17 00:00:00 2001 From: Thomas WeiĂźschuh Date: Thu, 5 Mar 2026 10:31:41 +0100 Subject: module: Move 'struct module_signature' to UAPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This structure definition is used outside the kernel proper. For example in kmod and the kernel build environment. To allow reuse, move it to a new UAPI header. While it is not a true UAPI, it is a common practice to have non-UAPI interface definitions in the kernel's UAPI headers. Signed-off-by: Thomas WeiĂźschuh Reviewed-by: Petr Pavlu Reviewed-by: Nicolas Schier Signed-off-by: Sami Tolvanen --- include/linux/module_signature.h | 28 +----------------------- include/uapi/linux/module_signature.h | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 include/uapi/linux/module_signature.h (limited to 'include/uapi/linux') diff --git a/include/linux/module_signature.h b/include/linux/module_signature.h index 915549c779dc..db335d46787f 100644 --- a/include/linux/module_signature.h +++ b/include/linux/module_signature.h @@ -10,33 +10,7 @@ #define _LINUX_MODULE_SIGNATURE_H #include - -/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */ -#define MODULE_SIGNATURE_MARKER "~Module signature appended~\n" - -enum module_signature_type { - MODULE_SIGNATURE_TYPE_PKCS7 = 2, /* Signature in PKCS#7 message */ -}; - -/* - * Module signature information block. - * - * The constituents of the signature section are, in order: - * - * - Signer's name - * - Key identifier - * - Signature data - * - Information block - */ -struct module_signature { - u8 algo; /* Public-key crypto algorithm [0] */ - u8 hash; /* Digest algorithm [0] */ - u8 id_type; /* Key identifier type [enum module_signature_type] */ - u8 signer_len; /* Length of signer's name [0] */ - u8 key_id_len; /* Length of key identifier [0] */ - u8 __pad[3]; - __be32 sig_len; /* Length of signature data */ -}; +#include int mod_check_sig(const struct module_signature *ms, size_t file_len, const char *name); diff --git a/include/uapi/linux/module_signature.h b/include/uapi/linux/module_signature.h new file mode 100644 index 000000000000..634c9f1c8fc2 --- /dev/null +++ b/include/uapi/linux/module_signature.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Module signature handling. + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifndef _UAPI_LINUX_MODULE_SIGNATURE_H +#define _UAPI_LINUX_MODULE_SIGNATURE_H + +#include + +/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */ +#define MODULE_SIGNATURE_MARKER "~Module signature appended~\n" + +enum module_signature_type { + MODULE_SIGNATURE_TYPE_PKCS7 = 2, /* Signature in PKCS#7 message */ +}; + +/* + * Module signature information block. + * + * The constituents of the signature section are, in order: + * + * - Signer's name + * - Key identifier + * - Signature data + * - Information block + */ +struct module_signature { + __u8 algo; /* Public-key crypto algorithm [0] */ + __u8 hash; /* Digest algorithm [0] */ + __u8 id_type; /* Key identifier type [enum module_signature_type] */ + __u8 signer_len; /* Length of signer's name [0] */ + __u8 key_id_len; /* Length of key identifier [0] */ + __u8 __pad[3]; + __be32 sig_len; /* Length of signature data */ +}; + +#endif /* _UAPI_LINUX_MODULE_SIGNATURE_H */ -- cgit v1.2.3 From 6e78b70c9a3d2a627229801f93e3f62869922587 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 18 Mar 2026 14:39:15 +0200 Subject: wifi: cfg80211: Add an API to configure local NAN schedule Add an nl80211 API to allow user space to configure the local NAN schedule. The local schedule consists of a list of channel definitions and a schedule map, in which each element covers a time slot and indicates on what channel the device should be in that time slot. Channels can be added to schedule even without being scheduled, for reservation purposes. A schedule can be configured either immedietally or be deferred, in case there are already connected peers. When the deferred flag is set, the command is a request from the device to perform an announced schedule update: send the updated NAN Availability - as set in this command - to the peers, and do the actual switch to the new schedule on the right time (i.e. at the end of the slot after the slot in which the update was sent to the peers). In addition, a notification will be sent to indicate a deferred update completion. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260219114327.ecca178a2de0.Ic977ab08b4ed5cf9b849e55d3a59b01ad3fbd08e@changeid Link: https://patch.msgid.link/20260318123926.206536-2-miriam.rachel.korenblit@intel.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 73 +++++++++++- include/uapi/linux/nl80211.h | 76 +++++++++++++ net/wireless/core.c | 54 ++++++++- net/wireless/core.h | 4 + net/wireless/nl80211.c | 266 +++++++++++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 16 +++ net/wireless/trace.h | 38 +++++++ 7 files changed, 525 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8cd870ece351..539dcf65c188 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4050,6 +4050,54 @@ struct cfg80211_nan_conf { u16 vendor_elems_len; }; +#define CFG80211_NAN_SCHED_NUM_TIME_SLOTS 32 + +/** + * struct cfg80211_nan_channel - NAN channel configuration + * + * This struct defines a NAN channel configuration + * + * @chandef: the channel definition + * @channel_entry: pointer to the Channel Entry blob as defined in Wi-Fi Aware + * (TM) 4.0 specification Table 100 (Channel Entry format for the NAN + * Availability attribute). + * @rx_nss: number of spatial streams supported on this channel + */ +struct cfg80211_nan_channel { + struct cfg80211_chan_def chandef; + const u8 *channel_entry; + u8 rx_nss; +}; + +/** + * struct cfg80211_nan_local_sched - NAN local schedule + * + * This struct defines NAN local schedule parameters + * + * @schedule: a mapping of time slots to chandef indexes in %nan_channels. + * An unscheduled slot will be set to %NL80211_NAN_SCHED_NOT_AVAIL_SLOT. + * @n_channels: number of channel definitions in %nan_channels. + * @nan_avail_blob: pointer to NAN Availability attribute blob. + * See %NL80211_ATTR_NAN_AVAIL_BLOB for more details. + * @nan_avail_blob_len: length of the @nan_avail_blob in bytes. + * @deferred: if true, the command containing this schedule configuration is a + * request from the device to perform an announced schedule update. This + * means that it needs to send the updated NAN availability to the peers, + * and do the actual switch on the right time (i.e. at the end of the slot + * after the slot in which the updated NAN Availability was sent). + * See %NL80211_ATTR_NAN_SCHED_DEFERRED for more details. + * If false, the schedule is applied immediately. + * @nan_channels: array of NAN channel definitions that can be scheduled. + */ +struct cfg80211_nan_local_sched { + u8 schedule[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; + u8 n_channels; + const u8 *nan_avail_blob; + u16 nan_avail_blob_len; + bool deferred; + struct cfg80211_nan_channel nan_channels[] __counted_by(n_channels); +}; + /** * enum cfg80211_nan_conf_changes - indicates changed fields in NAN * configuration @@ -4830,6 +4878,12 @@ struct mgmt_frame_regs { * @nan_change_conf: changes NAN configuration. The changed parameters must * be specified in @changes (using &enum cfg80211_nan_conf_changes); * All other parameters must be ignored. + * @nan_set_local_sched: configure the local schedule for NAN. The schedule + * consists of an array of %cfg80211_nan_channel and the schedule itself, + * in which each entry maps each time slot to the channel on which the + * radio should operate on. If the chandef of a NAN channel is not + * changed, the channel entry must also remain unchanged. It is the + * driver's responsibility to verify this. * * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS * @@ -5207,7 +5261,9 @@ struct cfg80211_ops { struct wireless_dev *wdev, struct cfg80211_nan_conf *conf, u32 changes); - + int (*nan_set_local_sched)(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_nan_local_sched *sched); int (*set_multicast_to_unicast)(struct wiphy *wiphy, struct net_device *dev, const bool enabled); @@ -6859,6 +6915,9 @@ struct wireless_dev { } ocb; struct { u8 cluster_id[ETH_ALEN] __aligned(2); + u8 n_channels; + struct cfg80211_chan_def *chandefs; + bool sched_update_pending; } nan; } u; @@ -10016,6 +10075,18 @@ void cfg80211_nan_func_terminated(struct wireless_dev *wdev, enum nl80211_nan_func_term_reason reason, u64 cookie, gfp_t gfp); +/** + * cfg80211_nan_sched_update_done - notify deferred schedule update completion + * @wdev: the wireless device reporting the event + * @success: whether or not the schedule update was successful + * @gfp: allocation flags + * + * This function notifies user space that a deferred local NAN schedule update + * (requested with %NL80211_ATTR_NAN_SCHED_DEFERRED) has been completed. + */ +void cfg80211_nan_sched_update_done(struct wireless_dev *wdev, bool success, + gfp_t gfp); + /* ethtool helper */ void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 67d764023988..484094667abc 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1367,6 +1367,20 @@ * %NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP. The current channel * definition is also sent. * + * @NL80211_CMD_NAN_SET_LOCAL_SCHED: Set the local NAN schedule. NAN must be + * operational (%NL80211_CMD_START_NAN was executed). Must contain + * %NL80211_ATTR_NAN_TIME_SLOTS and %NL80211_ATTR_NAN_AVAIL_BLOB, but + * %NL80211_ATTR_NAN_CHANNEL is optional (for example in case of a channel + * removal, that channel won't be provided). + * If %NL80211_ATTR_NAN_SCHED_DEFERRED is set, the command is a request + * from the device to perform an announced schedule update. See + * %NL80211_ATTR_NAN_SCHED_DEFERRED for more details. + * If not set, the schedule should be applied immediately. + * @NL80211_CMD_NAN_SCHED_UPDATE_DONE: Event sent to user space to notify that + * a deferred local NAN schedule update (requested with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED and %NL80211_ATTR_NAN_SCHED_DEFERRED) + * has been completed. The presence of %NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS + * indicates that the update was successful. * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1632,6 +1646,10 @@ enum nl80211_commands { NL80211_CMD_INCUMBENT_SIGNAL_DETECT, + NL80211_CMD_NAN_SET_LOCAL_SCHED, + + NL80211_CMD_NAN_SCHED_UPDATE_DONE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2991,6 +3009,54 @@ enum nl80211_commands { * @NL80211_ATTR_DISABLE_UHR: Force UHR capable interfaces to disable * this feature during association. This is a flag attribute. * Currently only supported in mac80211 drivers. + * @NL80211_ATTR_NAN_CHANNEL: This is a nested attribute. There can be multiple + * attributes of this type, each one represents a channel definition and + * consists of top-level attributes like %NL80211_ATTR_WIPHY_FREQ. Must + * contain %NL80211_ATTR_NAN_CHANNEL_ENTRY and + * %NL80211_ATTR_NAN_RX_NSS. + * This attribute is used with %NL80211_CMD_NAN_SET_LOCAL_SCHED to specify + * the channel definitions on which the radio needs to operate during + * specific time slots. All of the channel definitions should be mutually + * incompatible. The number of channels should fit the current + * configuration of channels and the possible interface combinations. + * If an existing NAN channel is changed but the chandef isn't, the + * channel entry must also remain unchanged. + * @NL80211_ATTR_NAN_CHANNEL_ENTRY: a byte array of 6 bytes. contains the + * Channel Entry as defined in Wi-Fi Aware (TM) 4.0 specification Table + * 100 (Channel Entry format for the NAN Availability attribute). + * @NL80211_ATTR_NAN_RX_NSS: (u8) RX NSS used for a NAN channel. This is + * used with %NL80211_ATTR_NAN_CHANNEL when configuring NAN channels with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED. + * @NL80211_ATTR_NAN_TIME_SLOTS: an array of u8 values and 32 cells. each value + * maps a time slot to the chandef on which the radio should operate on in + * that time. %NL80211_NAN_SCHED_NOT_AVAIL_SLOT indicates unscheduled. + * The chandef is represented using its index, where the index is the + * sequential number of the %NL80211_ATTR_NAN_CHANNEL attribute within all + * the attributes of this type. + * Each slots spans over 16TUs, hence the entire schedule spans over + * 512TUs. Other slot durations and periods are currently not supported. + * @NL80211_ATTR_NAN_AVAIL_BLOB: (Binary) The NAN Availability attribute blob, + * including the attribute header, as defined in Wi-Fi Aware (TM) 4.0 + * specification Table 93 (NAN Availability attribute format). Required with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED to provide the raw NAN Availability + * attribute. Used by the device to publish Schedule Update NAFs. + * @NL80211_ATTR_NAN_SCHED_DEFERRED: Flag attribute used with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED. When present, the command is a + * request from the device to perform an announced schedule update. This + * means that it needs to send the updated NAN availability to the peers, + * and do the actual switch on the right time (i.e. at the end of the slot + * after the slot in which the updated NAN Availability was sent). Since + * the slots management is done in the device, the update to the peers + * needs to be sent by the device, so it knows the actual switch time. + * If the flag is not set, the schedule should be applied immediately. + * When this flag is set, the total number of NAN channels from both the + * old and new schedules must not exceed the allowed number of local NAN + * channels, because with deferred scheduling the old channels cannot be + * removed before adding the new ones to free up space. + * @NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS: flag attribute used with + * %NL80211_CMD_NAN_SCHED_UPDATE_DONE to indicate that the deferred + * schedule update completed successfully. If this flag is not present, + * the update failed. * * @NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP: u32 attribute specifying * the signal interference bitmap detected on the operating bandwidth for @@ -3582,6 +3648,14 @@ enum nl80211_attrs { NL80211_ATTR_UHR_OPERATION, + NL80211_ATTR_NAN_CHANNEL, + NL80211_ATTR_NAN_CHANNEL_ENTRY, + NL80211_ATTR_NAN_TIME_SLOTS, + NL80211_ATTR_NAN_RX_NSS, + NL80211_ATTR_NAN_AVAIL_BLOB, + NL80211_ATTR_NAN_SCHED_DEFERRED, + NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -8574,4 +8648,6 @@ enum nl80211_nan_capabilities { NL80211_NAN_CAPABILITIES_MAX = __NL80211_NAN_CAPABILITIES_LAST - 1, }; +#define NL80211_NAN_SCHED_NOT_AVAIL_SLOT 0xff + #endif /* __LINUX_NL80211_H */ diff --git a/net/wireless/core.c b/net/wireless/core.c index 23afc250bc10..54c89b0db352 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -5,7 +5,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -254,6 +254,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { + struct cfg80211_nan_local_sched empty_sched = {}; + lockdep_assert_held(&rdev->wiphy.mtx); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN)) @@ -262,6 +264,15 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, if (!wdev_running(wdev)) return; + /* + * If there is a scheduled update pending, mark it as canceled, so the + * empty schedule will be accepted + */ + wdev->u.nan.sched_update_pending = false; + + /* Unschedule all */ + cfg80211_nan_set_local_schedule(rdev, wdev, &empty_sched); + rdev_stop_nan(rdev, wdev); wdev->is_running = false; @@ -270,6 +281,47 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, rdev->opencount--; } +int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_local_sched *sched) +{ + int ret; + + lockdep_assert_held(&rdev->wiphy.mtx); + + if (wdev->iftype != NL80211_IFTYPE_NAN || !wdev_running(wdev)) + return -EINVAL; + + if (wdev->u.nan.sched_update_pending) + return -EBUSY; + + ret = rdev_nan_set_local_sched(rdev, wdev, sched); + if (ret) + return ret; + + wdev->u.nan.sched_update_pending = sched->deferred; + + kfree(wdev->u.nan.chandefs); + wdev->u.nan.chandefs = NULL; + wdev->u.nan.n_channels = 0; + + if (!sched->n_channels) + return 0; + + wdev->u.nan.chandefs = kcalloc(sched->n_channels, + sizeof(*wdev->u.nan.chandefs), + GFP_KERNEL); + if (!wdev->u.nan.chandefs) + return -ENOMEM; + + for (int i = 0; i < sched->n_channels; i++) + wdev->u.nan.chandefs[i] = sched->nan_channels[i].chandef; + + wdev->u.nan.n_channels = sched->n_channels; + + return 0; +} + void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); diff --git a/net/wireless/core.h b/net/wireless/core.h index 6cace846d7a3..c7ae1f8a9bd8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -551,6 +551,10 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); +int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_local_sched *sched); + struct cfg80211_internal_bss * cfg80211_bss_update(struct cfg80211_registered_device *rdev, struct cfg80211_internal_bss *tmp, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e15cd26f3a79..de630e0d388b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -333,6 +333,40 @@ static int validate_nan_cluster_id(const struct nlattr *attr, return 0; } +static int validate_nan_avail_blob(const struct nlattr *attr, + struct netlink_ext_ack *extack) +{ + const u8 *data = nla_data(attr); + unsigned int len = nla_len(attr); + u16 attr_len; + + /* Need at least: Attr ID (1) + Length (2) */ + if (len < 3) { + NL_SET_ERR_MSG_FMT(extack, + "NAN Availability: Too short (need at least 3 bytes, have %u)", + len); + return -EINVAL; + } + + if (data[0] != 0x12) { + NL_SET_ERR_MSG_FMT(extack, + "NAN Availability: Invalid Attribute ID 0x%02x (expected 0x12)", + data[0]); + return -EINVAL; + } + + attr_len = get_unaligned_le16(&data[1]); + + if (attr_len != len - 3) { + NL_SET_ERR_MSG_FMT(extack, + "NAN Availability: Length field (%u) doesn't match data length (%u)", + attr_len, len - 3); + return -EINVAL; + } + + return 0; +} + static int validate_uhr_capa(const struct nlattr *attr, struct netlink_ext_ack *extack) { @@ -962,6 +996,14 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_DISABLE_UHR] = { .type = NLA_FLAG }, [NL80211_ATTR_UHR_OPERATION] = NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_uhr_operation), + [NL80211_ATTR_NAN_CHANNEL] = NLA_POLICY_NESTED(nl80211_policy), + [NL80211_ATTR_NAN_CHANNEL_ENTRY] = NLA_POLICY_EXACT_LEN(6), + [NL80211_ATTR_NAN_RX_NSS] = { .type = NLA_U8 }, + [NL80211_ATTR_NAN_TIME_SLOTS] = + NLA_POLICY_EXACT_LEN(CFG80211_NAN_SCHED_NUM_TIME_SLOTS), + [NL80211_ATTR_NAN_AVAIL_BLOB] = + NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_nan_avail_blob), + [NL80211_ATTR_NAN_SCHED_DEFERRED] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -16421,6 +16463,224 @@ nla_put_failure: } EXPORT_SYMBOL(cfg80211_nan_func_terminated); +void cfg80211_nan_sched_update_done(struct wireless_dev *wdev, bool success, + gfp_t gfp) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct sk_buff *msg; + void *hdr; + + trace_cfg80211_nan_sched_update_done(wiphy, wdev, success); + + /* Can happen if we stopped NAN */ + if (!wdev->u.nan.sched_update_pending) + return; + + wdev->u.nan.sched_update_pending = false; + + if (!wdev->owner_nlportid) + return; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_SCHED_UPDATE_DONE); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), + NL80211_ATTR_PAD) || + (success && + nla_put_flag(msg, NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS))) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_unicast(wiphy_net(wiphy), msg, wdev->owner_nlportid); + + return; + +nla_put_failure: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_nan_sched_update_done); + +static int nl80211_parse_nan_channel(struct cfg80211_registered_device *rdev, + struct nlattr *channel, + struct genl_info *info, + struct cfg80211_nan_local_sched *sched, + u8 index) +{ + struct nlattr **channel_parsed __free(kfree) = NULL; + struct cfg80211_chan_def chandef; + u8 n_rx_nss; + int ret; + + channel_parsed = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*channel_parsed), + GFP_KERNEL); + if (!channel_parsed) + return -ENOMEM; + + ret = nla_parse_nested(channel_parsed, NL80211_ATTR_MAX, channel, NULL, + info->extack); + if (ret) + return ret; + + ret = nl80211_parse_chandef(rdev, info->extack, channel_parsed, + &chandef); + if (ret) + return ret; + + if (chandef.chan->band == NL80211_BAND_6GHZ) { + NL_SET_ERR_MSG(info->extack, + "6 GHz band is not supported"); + return -EOPNOTSUPP; + } + + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, + NL80211_IFTYPE_NAN)) { + NL_SET_ERR_MSG_ATTR(info->extack, channel, + "Channel in NAN schedule is not allowed for NAN operation"); + return -EINVAL; + } + + for (int i = 0; i < index; i++) { + if (cfg80211_chandef_compatible(&sched->nan_channels[i].chandef, + &chandef)) { + NL_SET_ERR_MSG_ATTR(info->extack, channel, + "Channels in NAN schedule must be mutually incompatible"); + return -EINVAL; + } + } + + if (!channel_parsed[NL80211_ATTR_NAN_CHANNEL_ENTRY]) + return -EINVAL; + + sched->nan_channels[index].channel_entry = + nla_data(channel_parsed[NL80211_ATTR_NAN_CHANNEL_ENTRY]); + + if (!channel_parsed[NL80211_ATTR_NAN_RX_NSS]) + return -EINVAL; + + sched->nan_channels[index].rx_nss = + nla_get_u8(channel_parsed[NL80211_ATTR_NAN_RX_NSS]); + + n_rx_nss = u8_get_bits(rdev->wiphy.nan_capa.n_antennas, 0x03); + if (sched->nan_channels[index].rx_nss > n_rx_nss || + !sched->nan_channels[index].rx_nss) { + NL_SET_ERR_MSG_ATTR(info->extack, channel, + "Invalid RX NSS in NAN channel definition"); + return -EINVAL; + } + + sched->nan_channels[index].chandef = chandef; + + return 0; +} + +static bool nl80211_nan_is_sched_empty(struct cfg80211_nan_local_sched *sched) +{ + if (!sched->n_channels) + return true; + + for (int i = 0; i < ARRAY_SIZE(sched->schedule); i++) { + if (sched->schedule[i] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT) + return false; + } + + return true; +} + +static int nl80211_nan_set_local_sched(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct cfg80211_nan_local_sched *sched __free(kfree) = NULL; + struct wireless_dev *wdev = info->user_ptr[1]; + int rem, i = 0, n_channels = 0; + struct nlattr *channel; + bool sched_empty; + + if (wdev->iftype != NL80211_IFTYPE_NAN) + return -EOPNOTSUPP; + + if (!wdev_running(wdev)) + return -ENOTCONN; + + if (!info->attrs[NL80211_ATTR_NAN_TIME_SLOTS]) + return -EINVAL; + + /* First count how many channel attributes we got */ + nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL, + info->nlhdr, GENL_HDRLEN, rem) + n_channels++; + + sched = kzalloc(struct_size(sched, nan_channels, n_channels), + GFP_KERNEL); + if (!sched) + return -ENOMEM; + + sched->n_channels = n_channels; + + nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL, + info->nlhdr, GENL_HDRLEN, rem) { + int ret = nl80211_parse_nan_channel(rdev, channel, info, sched, + i); + + if (ret) + return ret; + i++; + } + + memcpy(sched->schedule, + nla_data(info->attrs[NL80211_ATTR_NAN_TIME_SLOTS]), + nla_len(info->attrs[NL80211_ATTR_NAN_TIME_SLOTS])); + + for (int slot = 0; slot < ARRAY_SIZE(sched->schedule); slot++) { + if (sched->schedule[slot] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT && + sched->schedule[slot] >= sched->n_channels) { + NL_SET_ERR_MSG(info->extack, + "Invalid time slot in NAN schedule"); + return -EINVAL; + } + } + + sched_empty = nl80211_nan_is_sched_empty(sched); + + sched->deferred = + nla_get_flag(info->attrs[NL80211_ATTR_NAN_SCHED_DEFERRED]); + + if (sched_empty) { + if (sched->deferred) { + NL_SET_ERR_MSG(info->extack, + "Schedule cannot be deferred if all time slots are unavailable"); + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]) { + NL_SET_ERR_MSG(info->extack, + "NAN Availability blob must be empty if all time slots are unavailable"); + return -EINVAL; + } + } else { + if (!info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]) { + NL_SET_ERR_MSG(info->extack, + "NAN Availability blob attribute is required"); + return -EINVAL; + } + + sched->nan_avail_blob = + nla_data(info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]); + sched->nan_avail_blob_len = + nla_len(info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]); + } + + return cfg80211_nan_set_local_schedule(rdev, wdev, sched); +} + static int nl80211_get_protocol_features(struct sk_buff *skb, struct genl_info *info) { @@ -19227,6 +19487,12 @@ static const struct genl_small_ops nl80211_small_ops[] = { .flags = GENL_UNS_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), }, + { + .cmd = NL80211_CMD_NAN_SET_LOCAL_SCHED, + .doit = nl80211_nan_set_local_sched, + .flags = GENL_ADMIN_PERM, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), + }, }; static struct genl_family nl80211_fam __ro_after_init = { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 2bad8b60b7c9..b886dedb25c6 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1060,6 +1060,22 @@ rdev_nan_change_conf(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_nan_set_local_sched(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_local_sched *sched) +{ + int ret; + + trace_rdev_nan_set_local_sched(&rdev->wiphy, wdev, sched); + if (rdev->ops->nan_set_local_sched) + ret = rdev->ops->nan_set_local_sched(&rdev->wiphy, wdev, sched); + else + ret = -EOPNOTSUPP; + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_acl_data *params) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index af23f4fca90a..d32b83439363 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2410,6 +2410,27 @@ TRACE_EVENT(rdev_del_nan_func, WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie) ); +TRACE_EVENT(rdev_nan_set_local_sched, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_nan_local_sched *sched), + TP_ARGS(wiphy, wdev, sched), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __array(u8, schedule, CFG80211_NAN_SCHED_NUM_TIME_SLOTS) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + memcpy(__entry->schedule, sched->schedule, + CFG80211_NAN_SCHED_NUM_TIME_SLOTS); + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", schedule: %s", + WIPHY_PR_ARG, WDEV_PR_ARG, + __print_array(__entry->schedule, + CFG80211_NAN_SCHED_NUM_TIME_SLOTS, 1)) +); + TRACE_EVENT(rdev_set_mac_acl, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_acl_data *params), @@ -4276,6 +4297,23 @@ TRACE_EVENT(cfg80211_incumbent_signal_notify, TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", signal_interference_bitmap=0x%x", WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->signal_interference_bitmap) ); + +TRACE_EVENT(cfg80211_nan_sched_update_done, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, bool success), + TP_ARGS(wiphy, wdev, success), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __field(bool, success) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + __entry->success = success; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT " success=%d", + WIPHY_PR_ARG, WDEV_PR_ARG, __entry->success) +); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH -- cgit v1.2.3 From 0e8ec738a71ee4e8da7c56d21dd7bb54f954c38b Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 18 Mar 2026 14:39:17 +0200 Subject: wifi: cfg80211: add support for NAN data interface This new interface type represents a NAN data interface (NDI). It is used for data communication with NAN peers. Note that the existing NL80211_IFTYPE_NAN interface, which is the NAN Management Interface (NMI), is used for management communication. An NDI interface is started when a new NAN data path is about to be established, and is stopped after the NAN data path is terminated. - An NDI interface can only be started if the NMI is running, and NAN is started. - Before the NMI is stopped, the NDI interfaces will be stopped. Add the new interface type, handle add/remove operations for it, and makes sure of the conditions above. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260219114327.0d681335c2e2.I92973483e927820ae2297853c141842fdb262747@changeid Link: https://patch.msgid.link/20260318123926.206536-4-miriam.rachel.korenblit@intel.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 21 +++++++++++++ include/uapi/linux/nl80211.h | 4 +++ net/mac80211/cfg.c | 1 + net/mac80211/chan.c | 2 ++ net/mac80211/iface.c | 3 ++ net/mac80211/rx.c | 2 ++ net/mac80211/util.c | 1 + net/wireless/chan.c | 2 ++ net/wireless/core.c | 72 ++++++++++++++++++++++++++++++++++++++------ net/wireless/core.h | 6 ++++ net/wireless/nl80211.c | 14 ++++++++- net/wireless/reg.c | 12 ++++++-- net/wireless/sysfs.c | 27 +++++++++-------- net/wireless/util.c | 21 +++++++++++-- 14 files changed, 159 insertions(+), 29 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 539dcf65c188..1797ece50295 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3980,6 +3980,27 @@ struct cfg80211_qos_map { struct cfg80211_dscp_range up[8]; }; +/** + * DOC: Neighbor Awareness Networking (NAN) + * + * NAN uses two interface types: + * + * - %NL80211_IFTYPE_NAN: a non-netdev interface. This has two roles: (1) holds + * the configuration of all NAN activities (DE parameters, synchronisation + * parameters, local schedule, etc.), and (2) uses as the NAN Management + * Interface (NMI), which is used for NAN management communication. + * + * - %NL80211_IFTYPE_NAN_DATA: The NAN Data Interface (NDI), used for data + * communication with NAN peers. + * + * An NDI interface can only be started (IFF_UP) if the NMI one is running and + * NAN is started. Before NAN is stopped, all associated NDI interfaces + * must be stopped first. + * + * The local schedule specifies which channels the device is available on and + * when. Must be cancelled before NAN is stopped. + */ + /** * struct cfg80211_nan_band_config - NAN band specific configuration * diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 484094667abc..3984c176f9e7 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3749,6 +3749,9 @@ enum nl80211_attrs { * @NL80211_IFTYPE_OCB: Outside Context of a BSS * This mode corresponds to the MIB variable dot11OCBActivated=true * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev) + * @NL80211_IFTYPE_NAN_DATA: NAN data interface type (netdev); NAN data + * interfaces can only be brought up (IFF_UP) when a NAN interface + * already exists and NAN has been started (using %NL80211_CMD_START_NAN). * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * @@ -3770,6 +3773,7 @@ enum nl80211_iftype { NL80211_IFTYPE_P2P_DEVICE, NL80211_IFTYPE_OCB, NL80211_IFTYPE_NAN, + NL80211_IFTYPE_NAN_DATA, /* keep last */ NUM_NL80211_IFTYPES, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9aa4ae0621be..13132215afb4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -718,6 +718,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev, case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_OCB: + case NL80211_IFTYPE_NAN_DATA: /* shouldn't happen */ WARN_ON_ONCE(1); break; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index bc396d6c64c5..1e4bfcd25697 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -495,6 +495,7 @@ ieee80211_get_width_of_link(struct ieee80211_link_data *link) case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_NAN_DATA: WARN_ON_ONCE(1); break; } @@ -1458,6 +1459,7 @@ ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link) case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: case NUM_NL80211_IFTYPES: WARN_ON(1); break; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 234de4762be5..125897717a4c 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1368,6 +1368,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: /* no special treatment */ break; case NL80211_IFTYPE_UNSPECIFIED: @@ -1945,6 +1946,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_P2P_DEVICE: sdata->vif.bss_conf.bssid = sdata->vif.addr; break; + case NL80211_IFTYPE_NAN_DATA: + break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_WDS: case NUM_NL80211_IFTYPES: diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 19c33f7a8193..d9a654ef082d 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -4607,6 +4607,8 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) (ieee80211_is_public_action(hdr, skb->len) || (ieee80211_is_auth(hdr->frame_control) && ether_addr_equal(sdata->vif.addr, hdr->addr1))); + case NL80211_IFTYPE_NAN_DATA: + return false; default: break; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 55054de62508..8987a4504520 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2118,6 +2118,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) return res; } break; + case NL80211_IFTYPE_NAN_DATA: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_P2P_DEVICE: diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 2dcf18f5655e..8b94c0de80ad 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -816,6 +816,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_NAN_DATA: break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_UNSPECIFIED: @@ -939,6 +940,7 @@ bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev) case NL80211_IFTYPE_P2P_DEVICE: /* Can NAN type be considered as beaconing interface? */ case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_WDS: diff --git a/net/wireless/core.c b/net/wireless/core.c index 54c89b0db352..200b97f912eb 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -329,16 +329,21 @@ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) ASSERT_RTNL(); + /* + * Some netdev interfaces need to be closed before some non-netdev + * ones, i.e. NAN_DATA interfaces need to be closed before the NAN + * interface + */ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { if (wdev->netdev) { dev_close(wdev->netdev); continue; } + } - /* otherwise, check iftype */ - - guard(wiphy)(wiphy); + guard(wiphy)(wiphy); + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { switch (wdev->iftype) { case NL80211_IFTYPE_P2P_DEVICE: cfg80211_stop_p2p_device(rdev, wdev); @@ -396,6 +401,8 @@ void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) { if (wdev->nl_owner_dead) { + cfg80211_close_dependents(rdev, wdev); + if (wdev->netdev) dev_close(wdev->netdev); @@ -406,6 +413,21 @@ void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) } } +void cfg80211_close_dependents(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + ASSERT_RTNL(); + + if (wdev->iftype != NL80211_IFTYPE_NAN) + return; + + /* Close all NAN DATA interfaces */ + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + if (wdev->iftype == NL80211_IFTYPE_NAN_DATA) + dev_close(wdev->netdev); + } +} + static void cfg80211_destroy_iface_wk(struct work_struct *work) { struct cfg80211_registered_device *rdev; @@ -1419,9 +1441,8 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, rdev->num_running_monitor_ifaces += num; } -void cfg80211_leave(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - int link_id) +void cfg80211_leave_locked(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int link_id) { struct net_device *dev = wdev->netdev; struct cfg80211_sched_scan_request *pos, *tmp; @@ -1472,6 +1493,7 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev, break; case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_NAN_DATA: /* nothing to do */ break; case NL80211_IFTYPE_UNSPECIFIED: @@ -1482,6 +1504,19 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev, } } +void cfg80211_leave(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int link_id) +{ + ASSERT_RTNL(); + + /* NAN_DATA interfaces must be closed before stopping NAN */ + cfg80211_close_dependents(rdev, wdev); + + guard(wiphy)(&rdev->wiphy); + + cfg80211_leave_locked(rdev, wdev, link_id); +} + void cfg80211_stop_link(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id, gfp_t gfp) { @@ -1497,6 +1532,9 @@ void cfg80211_stop_link(struct wiphy *wiphy, struct wireless_dev *wdev, trace_cfg80211_stop_link(wiphy, wdev, link_id); + if (wdev->iftype == NL80211_IFTYPE_NAN) + return; + ev = kzalloc_obj(*ev, gfp); if (!ev) return; @@ -1647,10 +1685,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, } break; case NETDEV_GOING_DOWN: - scoped_guard(wiphy, &rdev->wiphy) { - cfg80211_leave(rdev, wdev, -1); + cfg80211_leave(rdev, wdev, -1); + scoped_guard(wiphy, &rdev->wiphy) cfg80211_remove_links(wdev); - } /* since we just did cfg80211_leave() nothing to do there */ cancel_work_sync(&wdev->disconnect_wk); cancel_work_sync(&wdev->pmsr_free_wk); @@ -1731,6 +1768,23 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, if (rfkill_blocked(rdev->wiphy.rfkill)) return notifier_from_errno(-ERFKILL); + + /* NAN_DATA interfaces require a running NAN interface */ + if (wdev->iftype == NL80211_IFTYPE_NAN_DATA) { + struct wireless_dev *iter; + bool nan_started = false; + + list_for_each_entry(iter, &rdev->wiphy.wdev_list, list) { + if (iter->iftype == NL80211_IFTYPE_NAN && + wdev_running(iter)) { + nan_started = true; + break; + } + } + + if (!nan_started) + return notifier_from_errno(-ENOLINK); + } break; default: return NOTIFY_DONE; diff --git a/net/wireless/core.h b/net/wireless/core.h index c7ae1f8a9bd8..ae2d56d3ad90 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -318,6 +318,9 @@ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy, void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev); +void cfg80211_close_dependents(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); + /* free object */ void cfg80211_dev_free(struct cfg80211_registered_device *rdev); @@ -541,6 +544,9 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); +void cfg80211_leave_locked(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int link_id); + void cfg80211_leave(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, int link_id); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index de630e0d388b..7cea8fef6ae5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1764,6 +1764,7 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) return 0; return -ENOLINK; case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: if (wiphy_ext_feature_isset(wdev->wiphy, NL80211_EXT_FEATURE_SECURE_NAN)) return 0; @@ -4921,6 +4922,8 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) else dev_close(wdev->netdev); + cfg80211_close_dependents(rdev, wdev); + mutex_lock(&rdev->wiphy.mtx); return cfg80211_remove_virtual_intf(rdev, wdev); @@ -15964,6 +15967,10 @@ static int nl80211_stop_nan(struct sk_buff *skb, struct genl_info *info) if (wdev->iftype != NL80211_IFTYPE_NAN) return -EOPNOTSUPP; + cfg80211_close_dependents(rdev, wdev); + + guard(wiphy)(&rdev->wiphy); + cfg80211_stop_nan(rdev, wdev); return 0; @@ -18356,7 +18363,11 @@ nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *info) NL80211_FLAG_NEED_RTNL) \ SELECTOR(__sel, WIPHY_CLEAR, \ NL80211_FLAG_NEED_WIPHY | \ - NL80211_FLAG_CLEAR_SKB) + NL80211_FLAG_CLEAR_SKB) \ + SELECTOR(__sel, WDEV_UP_RTNL_NOMTX, \ + NL80211_FLAG_NEED_WDEV_UP | \ + NL80211_FLAG_NO_WIPHY_MTX | \ + NL80211_FLAG_NEED_RTNL) enum nl80211_internal_flags_selector { #define SELECTOR(_, name, value) NL80211_IFL_SEL_##name, @@ -19193,6 +19204,7 @@ static const struct genl_small_ops nl80211_small_ops[] = { .doit = nl80211_stop_nan, .flags = GENL_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP | + NL80211_FLAG_NO_WIPHY_MTX | NL80211_FLAG_NEED_RTNL), }, { diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 4b5450aec72e..5db2121c0b57 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2409,6 +2409,9 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) continue; chandef = wdev->u.ocb.chandef; break; + case NL80211_IFTYPE_NAN_DATA: + /* NAN channels are checked in NL80211_IFTYPE_NAN interface */ + break; default: /* others not implemented for now */ WARN_ON_ONCE(1); @@ -2445,11 +2448,14 @@ static void reg_leave_invalid_chans(struct wiphy *wiphy) struct wireless_dev *wdev; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - guard(wiphy)(wiphy); + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + bool valid; - list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) - if (!reg_wdev_chan_valid(wiphy, wdev)) + scoped_guard(wiphy, wiphy) + valid = reg_wdev_chan_valid(wiphy, wdev); + if (!valid) cfg80211_leave(rdev, wdev, -1); + } } static void reg_check_chans_work(struct work_struct *work) diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 3385a27468f7..d45ddc457c30 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -102,25 +102,26 @@ static int wiphy_suspend(struct device *dev) if (!rdev->wiphy.registered) goto out_unlock_rtnl; - wiphy_lock(&rdev->wiphy); if (rdev->wiphy.wowlan_config) { - cfg80211_process_wiphy_works(rdev, NULL); - if (rdev->ops->suspend) - ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config); - if (ret <= 0) - goto out_unlock_wiphy; + scoped_guard(wiphy, &rdev->wiphy) { + cfg80211_process_wiphy_works(rdev, NULL); + if (rdev->ops->suspend) + ret = rdev_suspend(rdev, + rdev->wiphy.wowlan_config); + if (ret <= 0) + goto out_unlock_rtnl; + } } /* Driver refused to configure wowlan (ret = 1) or no wowlan */ cfg80211_leave_all(rdev); - cfg80211_process_rdev_events(rdev); - cfg80211_process_wiphy_works(rdev, NULL); - if (rdev->ops->suspend) - ret = rdev_suspend(rdev, NULL); - -out_unlock_wiphy: - wiphy_unlock(&rdev->wiphy); + scoped_guard(wiphy, &rdev->wiphy) { + cfg80211_process_rdev_events(rdev); + cfg80211_process_wiphy_works(rdev, NULL); + if (rdev->ops->suspend) + ret = rdev_suspend(rdev, NULL); + } out_unlock_rtnl: if (ret == 0) rdev->suspended = true; diff --git a/net/wireless/util.c b/net/wireless/util.c index 1a861a6ea380..e2878d20a32d 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1144,8 +1144,15 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) ev->ij.channel); break; case EVENT_STOPPED: - cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev, - ev->link_id); + /* + * for NAN interfaces cfg80211_leave must be called but + * locking here doesn't allow this. + */ + if (WARN_ON(wdev->iftype == NL80211_IFTYPE_NAN)) + break; + + cfg80211_leave_locked(wiphy_to_rdev(wdev->wiphy), wdev, + ev->link_id); break; case EVENT_PORT_AUTHORIZED: __cfg80211_port_authorized(wdev, ev->pa.peer_addr, @@ -1184,6 +1191,13 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, if (otype == NL80211_IFTYPE_AP_VLAN) return -EOPNOTSUPP; + /* + * for NAN interfaces cfg80211_leave must be called for leaving, + * but locking here doesn't allow this. + */ + if (otype == NL80211_IFTYPE_NAN) + return -EOPNOTSUPP; + /* cannot change into P2P device or NAN */ if (ntype == NL80211_IFTYPE_P2P_DEVICE || ntype == NL80211_IFTYPE_NAN) @@ -1204,7 +1218,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, dev->ieee80211_ptr->use_4addr = false; rdev_set_qos_map(rdev, dev, NULL); - cfg80211_leave(rdev, dev->ieee80211_ptr, -1); + cfg80211_leave_locked(rdev, dev->ieee80211_ptr, -1); cfg80211_process_rdev_events(rdev); cfg80211_mlme_purge_registrations(dev->ieee80211_ptr); @@ -1232,6 +1246,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_NAN_DATA: dev->priv_flags |= IFF_DONT_BRIDGE; break; case NL80211_IFTYPE_P2P_GO: -- cgit v1.2.3 From bd11c96604693723297c403625c3059b33fb0618 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 18 Mar 2026 14:39:18 +0200 Subject: wifi: cfg80211: separately store HT, VHT and HE capabilities for NAN In NAN, unlike in other modes, there is only one set of (HT, VHT, HE) capabilities that is used for all channels (and bands) used in the NAN data path. This set of capabilities will have to be a special one, for example - have the minimum of (HT-for-5 GHz, HT-for-2.4 GHz), careful handling of the bits that have a different meaning for each band, etc. While we could use the exiting sband/iftype capabilities, and require identical capabilities for all bands (makes no sense since this means that we will have VHT capabilities in the 2.4 GHz slot), or require that only one of the sbands will be set, or have logic to extract the minimum and handle the conflicting bits - it seems simpler to add a dedicated set of capabilities which is special for NAN, and is band agnostic, to be populated by the driver. That way we also let the driver decide how it wants to handle the conflicting bits. Add this special set of these capabilities to wiphy:nan_capabilities, to be populated by the driver. Send it to user space. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260219114327.4b6f3e4a81b4.I45422adc0df3ad4101d857a92e83f0de5cf241e1@changeid Link: https://patch.msgid.link/20260318123926.206536-5-miriam.rachel.korenblit@intel.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 11 ++++++++ include/uapi/linux/nl80211.h | 43 +++++++++++++++++++++++++++++ net/wireless/core.c | 4 +++ net/wireless/nl80211.c | 65 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1797ece50295..60cd0fbe9a46 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5913,6 +5913,12 @@ enum wiphy_nan_flags { * @max_channel_switch_time: maximum channel switch time in milliseconds. * @dev_capabilities: NAN device capabilities as defined in Wi-Fi Aware (TM) * specification Table 79 (Capabilities field). + * @phy: Band-agnostic capabilities for NAN data interfaces. Since NAN + * operates on multiple channels simultaneously, these capabilities apply + * across all bands. Valid only if NL80211_IFTYPE_NAN_DATA is supported. + * @phy.ht: HT capabilities (mandatory for NAN data) + * @phy.vht: VHT capabilities (optional) + * @phy.he: HE capabilities (optional) */ struct wiphy_nan_capa { u32 flags; @@ -5920,6 +5926,11 @@ struct wiphy_nan_capa { u8 n_antennas; u16 max_channel_switch_time; u8 dev_capabilities; + struct { + struct ieee80211_sta_ht_cap ht; + struct ieee80211_sta_vht_cap vht; + struct ieee80211_sta_he_cap he; + } phy; }; #define CFG80211_HW_TIMESTAMP_ALL_PEERS 0xffff diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 3984c176f9e7..c94e957a3467 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4462,6 +4462,46 @@ enum nl80211_band_attr { #define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA +/** + * enum nl80211_nan_phy_cap_attr - NAN PHY capabilities attributes + * @__NL80211_NAN_PHY_CAP_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET: 16-byte attribute containing HT MCS set + * @NL80211_NAN_PHY_CAP_ATTR_HT_CAPA: HT capabilities (u16) + * @NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR: HT A-MPDU factor (u8) + * @NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY: HT A-MPDU density (u8) + * @NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET: 8-byte attribute containing VHT MCS set + * @NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA: VHT capabilities (u32) + * @NL80211_NAN_PHY_CAP_ATTR_HE_MAC: HE MAC capabilities + * @NL80211_NAN_PHY_CAP_ATTR_HE_PHY: HE PHY capabilities + * @NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET: HE supported NSS/MCS combinations + * @NL80211_NAN_PHY_CAP_ATTR_HE_PPE: HE PPE thresholds + * @NL80211_NAN_PHY_CAP_ATTR_MAX: highest NAN PHY cap attribute number + * @__NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST: internal use + */ +enum nl80211_nan_phy_cap_attr { + __NL80211_NAN_PHY_CAP_ATTR_INVALID, + + /* HT capabilities */ + NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET, + NL80211_NAN_PHY_CAP_ATTR_HT_CAPA, + NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR, + NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY, + + /* VHT capabilities */ + NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET, + NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA, + + /* HE capabilities */ + NL80211_NAN_PHY_CAP_ATTR_HE_MAC, + NL80211_NAN_PHY_CAP_ATTR_HE_PHY, + NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET, + NL80211_NAN_PHY_CAP_ATTR_HE_PPE, + + /* keep last */ + __NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST, + NL80211_NAN_PHY_CAP_ATTR_MAX = __NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST - 1 +}; + /** * enum nl80211_wmm_rule - regulatory wmm rule * @@ -8635,6 +8675,8 @@ enum nl80211_s1g_short_beacon_attrs { * @NL80211_NAN_CAPA_CAPABILITIES: u8 attribute containing the * capabilities of the device as defined in Wi-Fi Aware (TM) * specification Table 79 (Capabilities field). + * @NL80211_NAN_CAPA_PHY: nested attribute containing band-agnostic + * capabilities for NAN data path. See &enum nl80211_nan_phy_cap_attr. * @__NL80211_NAN_CAPABILITIES_LAST: Internal * @NL80211_NAN_CAPABILITIES_MAX: Highest NAN capability attribute. */ @@ -8647,6 +8689,7 @@ enum nl80211_nan_capabilities { NL80211_NAN_CAPA_NUM_ANTENNAS, NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME, NL80211_NAN_CAPA_CAPABILITIES, + NL80211_NAN_CAPA_PHY, /* keep last */ __NL80211_NAN_CAPABILITIES_LAST, NL80211_NAN_CAPABILITIES_MAX = __NL80211_NAN_CAPABILITIES_LAST - 1, diff --git a/net/wireless/core.c b/net/wireless/core.c index 200b97f912eb..6783e0672dcb 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -835,6 +835,10 @@ int wiphy_register(struct wiphy *wiphy) !(wiphy->nan_supported_bands & BIT(NL80211_BAND_2GHZ))))) return -EINVAL; + if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN_DATA)) && + !wiphy->nan_capa.phy.ht.ht_supported)) + return -EINVAL; + if (WARN_ON(wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))) return -EINVAL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7cea8fef6ae5..a9a829613b7b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2721,6 +2721,68 @@ fail: return -ENOBUFS; } +static int nl80211_put_nan_phy_cap(struct wiphy *wiphy, struct sk_buff *msg) +{ + struct nlattr *nl_phy_cap; + const struct ieee80211_sta_ht_cap *ht_cap; + const struct ieee80211_sta_vht_cap *vht_cap; + const struct ieee80211_sta_he_cap *he_cap; + + if (!cfg80211_iftype_allowed(wiphy, NL80211_IFTYPE_NAN_DATA, false, 0)) + return 0; + + ht_cap = &wiphy->nan_capa.phy.ht; + vht_cap = &wiphy->nan_capa.phy.vht; + he_cap = &wiphy->nan_capa.phy.he; + + /* HT is mandatory */ + if (WARN_ON(!ht_cap->ht_supported)) + return 0; + + nl_phy_cap = nla_nest_start_noflag(msg, NL80211_NAN_CAPA_PHY); + if (!nl_phy_cap) + return -ENOBUFS; + + if (nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET, + sizeof(ht_cap->mcs), &ht_cap->mcs) || + nla_put_u16(msg, NL80211_NAN_PHY_CAP_ATTR_HT_CAPA, ht_cap->cap) || + nla_put_u8(msg, NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR, + ht_cap->ampdu_factor) || + nla_put_u8(msg, NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY, + ht_cap->ampdu_density)) + goto fail; + + if (vht_cap->vht_supported) { + if (nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET, + sizeof(vht_cap->vht_mcs), &vht_cap->vht_mcs) || + nla_put_u32(msg, NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA, + vht_cap->cap)) + goto fail; + } + + if (he_cap->has_he) { + if (nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_MAC, + sizeof(he_cap->he_cap_elem.mac_cap_info), + he_cap->he_cap_elem.mac_cap_info) || + nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_PHY, + sizeof(he_cap->he_cap_elem.phy_cap_info), + he_cap->he_cap_elem.phy_cap_info) || + nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET, + sizeof(he_cap->he_mcs_nss_supp), + &he_cap->he_mcs_nss_supp) || + nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_PPE, + sizeof(he_cap->ppe_thres), he_cap->ppe_thres)) + goto fail; + } + + nla_nest_end(msg, nl_phy_cap); + return 0; + +fail: + nla_nest_cancel(msg, nl_phy_cap); + return -ENOBUFS; +} + static int nl80211_put_nan_capa(struct wiphy *wiphy, struct sk_buff *msg) { struct nlattr *nan_caps; @@ -2747,6 +2809,9 @@ static int nl80211_put_nan_capa(struct wiphy *wiphy, struct sk_buff *msg) wiphy->nan_capa.dev_capabilities)) goto fail; + if (nl80211_put_nan_phy_cap(wiphy, msg)) + goto fail; + nla_nest_end(msg, nan_caps); return 0; -- cgit v1.2.3 From 1f1101c29e55195db7b52bef47d11978442998e0 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 18 Mar 2026 14:39:19 +0200 Subject: wifi: nl80211: add support for NAN stations There are 2 types of logical links with a NAN peer: - management (NMI), which is used for Tx/Rx of NAN management frames. - data (NDI), which is used for Tx/Rx of data frames, or non-NAN management frames. The NMI station has two roles: - representation of the NAN peer - for example, the peer's schedule and the HT, VHT, HE capabilities - belong to the NMI station, and not to the NDI ones. - Tx/Rx of NAN management frames to/from the peer. The NDI station is used for Tx/Rx data frames of a specific NDP that was established with the NAN peer. Note that a peer can choose to reuse its NMI address as the NDI address. In that case, it is expected that two stations will be added even though they will have the same address. - An NDI station can only be added after the corresponding NMI station was configured with capabilities. - All the NDI stations will be removed before the NDI interface is brought down. - All NMI stations will be removed before NAN is stopped. - Before NMI sta removal, all corresponding NDI stations will be removed Add support for adding, removing, and changing NMI and NDI stations. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260219114327.d280936ee832.I6d859eee759bb5824a9ffd2984410faf879ba00e@changeid Link: https://patch.msgid.link/20260318123926.206536-6-miriam.rachel.korenblit@intel.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 56 ++++++++++++++++++++ include/uapi/linux/nl80211.h | 8 ++- net/wireless/nl80211.c | 120 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 161 insertions(+), 23 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 60cd0fbe9a46..654d71f60e8c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1831,6 +1831,7 @@ struct cfg80211_ttlm_params { * @eml_cap: EML capabilities of this station * @link_sta_params: link related params. * @epp_peer: EPP peer indication + * @nmi_mac: MAC address of the NMI station of the NAN peer */ struct station_parameters { struct net_device *vlan; @@ -1858,6 +1859,7 @@ struct station_parameters { u16 eml_cap; struct link_station_parameters link_sta_params; bool epp_peer; + const u8 *nmi_mac; }; /** @@ -1897,6 +1899,8 @@ struct station_del_parameters { * entry that is operating, has been marked authorized by userspace) * @CFG80211_STA_MESH_PEER_KERNEL: peer on mesh interface (kernel managed) * @CFG80211_STA_MESH_PEER_USER: peer on mesh interface (user managed) + * @CFG80211_STA_NAN_MGMT: NAN management interface station + * @CFG80211_STA_NAN_DATA: NAN data path station */ enum cfg80211_station_type { CFG80211_STA_AP_CLIENT, @@ -1908,6 +1912,8 @@ enum cfg80211_station_type { CFG80211_STA_TDLS_PEER_ACTIVE, CFG80211_STA_MESH_PEER_KERNEL, CFG80211_STA_MESH_PEER_USER, + CFG80211_STA_NAN_MGMT, + CFG80211_STA_NAN_DATA, }; /** @@ -3999,6 +4005,56 @@ struct cfg80211_qos_map { * * The local schedule specifies which channels the device is available on and * when. Must be cancelled before NAN is stopped. + * + * NAN Stations + * ~~~~~~~~~~~~ + * + * There are two types of stations corresponding to the two interface types: + * + * - NMI station: Represents the NAN peer. Peer-specific data such as the peer's + * schedule and the HT, VHT and HE capabilities belongs to the NMI station. + * Also used for Tx/Rx of NAN management frames to/from the peer. + * Added on the %NL80211_IFTYPE_NAN interface. + * + * - NDI station: Used for Tx/Rx of data frames (and non-NAN management frames) + * for a specific NDP established with the NAN peer. Added on the + * %NL80211_IFTYPE_NAN_DATA interface. + * + * A peer may reuse its NMI address as the NDI address. In that case, two + * separate stations should be added even though they share the same MAC + * address. + * + * HT, VHT and HE capabilities should not changes after it was set. It is the + * driver's responsibility to check that. + * + * An NDI station can only be added if the corresponding NMI station has already + * been configured with HT (and possibly VHT and HE) capabilities. It is the + * driver's responsibility to check that. + * + * All NDI stations must be removed before corresponding NMI station is removed. + * Therefore, removing a NMI station implies that the associated NDI station(s) + * (if any) will be removed first. + * + * NAN Dependencies + * ~~~~~~~~~~~~~~~~ + * + * The following diagram shows the dependencies between NAN components. + * An arrow from A to B means A must be started/added before B, and B must be + * stopped/removed before A: + * + * +-------------+ + * | NMI iface |---(local schedule) + * +------+------+ + * / \ + * v v + * +-----------+ +-------------+ + * | NDI iface | | NMI sta |---(peer schedule) + * +-----+-----+ +------+------+ + * \ / + * v v + * +----------+ + * | NDI sta | + * +----------+ */ /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index c94e957a3467..1897b9a35be8 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2677,7 +2677,8 @@ enum nl80211_commands { * a flow is assigned on each round of the DRR scheduler. * @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from * association request when used with NL80211_CMD_NEW_STATION). Can be set - * only if %NL80211_STA_FLAG_WME is set. + * only if %NL80211_STA_FLAG_WME is set (except for NAN, which uses WME + * anyway). * * @NL80211_ATTR_FTM_RESPONDER: nested attribute which user-space can include * in %NL80211_CMD_START_AP or %NL80211_CMD_SET_BEACON for fine timing @@ -3057,6 +3058,9 @@ enum nl80211_commands { * %NL80211_CMD_NAN_SCHED_UPDATE_DONE to indicate that the deferred * schedule update completed successfully. If this flag is not present, * the update failed. + * @NL80211_ATTR_NAN_NMI_MAC: The address of the NMI station to which this NDI + * station belongs. Used with %NL80211_CMD_NEW_STATION when adding an NDI + * station. * * @NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP: u32 attribute specifying * the signal interference bitmap detected on the operating bandwidth for @@ -3656,6 +3660,8 @@ enum nl80211_attrs { NL80211_ATTR_NAN_SCHED_DEFERRED, NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS, + NL80211_ATTR_NAN_NMI_MAC, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a9a829613b7b..89fb61d53e2f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1004,6 +1004,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_NAN_AVAIL_BLOB] = NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_nan_avail_blob), [NL80211_ATTR_NAN_SCHED_DEFERRED] = { .type = NLA_FLAG }, + [NL80211_ATTR_NAN_NMI_MAC] = NLA_POLICY_ETH_ADDR, }; /* policy for the key attributes */ @@ -7233,6 +7234,26 @@ static int parse_station_flags(struct genl_info *info, if ((params->sta_flags_mask | params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID)) return -EINVAL; + + if ((iftype == NL80211_IFTYPE_NAN || + iftype == NL80211_IFTYPE_NAN_DATA) && + params->sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_MFP))) + return -EINVAL; + + /* WME is always used in NAN */ + if (iftype == NL80211_IFTYPE_NAN_DATA) { + /* but don't let userspace control it */ + if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME)) + return -EINVAL; + + params->sta_flags_mask |= BIT(NL80211_STA_FLAG_WME); + params->sta_flags_set |= BIT(NL80211_STA_FLAG_WME); + } + return 0; } @@ -8115,7 +8136,7 @@ static int nl80211_dump_station(struct sk_buff *skb, /* nl80211_prepare_wdev_dump acquired it in the successful case */ __acquire(&rdev->wiphy.mtx); - if (!wdev->netdev) { + if (!wdev->netdev && wdev->iftype != NL80211_IFTYPE_NAN) { err = -EINVAL; goto out_err; } @@ -8302,10 +8323,12 @@ int cfg80211_check_station_change(struct wiphy *wiphy, return -EINVAL; if (params->link_sta_params.supported_rates) return -EINVAL; - if (params->ext_capab || params->link_sta_params.ht_capa || - params->link_sta_params.vht_capa || - params->link_sta_params.he_capa || - params->link_sta_params.eht_capa || + if (statype != CFG80211_STA_NAN_MGMT && + (params->link_sta_params.ht_capa || + params->link_sta_params.vht_capa || + params->link_sta_params.he_capa)) + return -EINVAL; + if (params->ext_capab || params->link_sta_params.eht_capa || params->link_sta_params.uhr_capa) return -EINVAL; if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU)) @@ -8377,6 +8400,19 @@ int cfg80211_check_station_change(struct wiphy *wiphy, params->plink_action != NL80211_PLINK_ACTION_BLOCK) return -EINVAL; break; + case CFG80211_STA_NAN_MGMT: + if (params->sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_MFP))) + return -EINVAL; + break; + case CFG80211_STA_NAN_DATA: + if (params->sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_MFP) | + BIT(NL80211_STA_FLAG_WME))) + return -EINVAL; + break; } /* @@ -8591,7 +8627,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); - if (!dev) + if (!dev && wdev->iftype != NL80211_IFTYPE_NAN && + wdev->iftype != NL80211_IFTYPE_NAN_DATA) return -EINVAL; if (!rdev->ops->change_station) @@ -8734,6 +8771,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: break; default: err = -EOPNOTSUPP; @@ -8762,7 +8801,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); - if (!dev) + if (!dev && wdev->iftype != NL80211_IFTYPE_NAN) return -EINVAL; if (!rdev->ops->add_station) @@ -8771,15 +8810,31 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; - if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) - return -EINVAL; + if (wdev->iftype == NL80211_IFTYPE_NAN || + wdev->iftype == NL80211_IFTYPE_NAN_DATA) { + if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) + return -EINVAL; + if (wdev->iftype == NL80211_IFTYPE_NAN_DATA) { + if (!info->attrs[NL80211_ATTR_NAN_NMI_MAC]) + return -EINVAL; - if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) - return -EINVAL; + /* Only NMI stations receive the HT/VHT/HE capabilities */ + if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || + info->attrs[NL80211_ATTR_VHT_CAPABILITY] || + info->attrs[NL80211_ATTR_HE_CAPABILITY]) + return -EINVAL; + } + } else { + if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) + return -EINVAL; - if (!info->attrs[NL80211_ATTR_STA_AID] && - !info->attrs[NL80211_ATTR_PEER_AID]) - return -EINVAL; + if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_STA_AID] && + !info->attrs[NL80211_ATTR_PEER_AID]) + return -EINVAL; + } params.link_sta_params.link_id = nl80211_link_id_or_invalid(info->attrs); @@ -8795,12 +8850,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); } - params.link_sta_params.supported_rates = - nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - params.link_sta_params.supported_rates_len = - nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - params.listen_interval = - nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); + if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { + params.link_sta_params.supported_rates = + nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); + params.link_sta_params.supported_rates_len = + nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); + } + + if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) + params.listen_interval = + nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); if (info->attrs[NL80211_ATTR_VLAN_ID]) params.vlan_id = nla_get_u16(info->attrs[NL80211_ATTR_VLAN_ID]); @@ -8819,7 +8878,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_PEER_AID]) params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]); - else + else if (info->attrs[NL80211_ATTR_STA_AID]) params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { @@ -8940,6 +8999,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + if (wdev->iftype == NL80211_IFTYPE_NAN || + wdev->iftype == NL80211_IFTYPE_NAN_DATA) { + if (params.sta_modify_mask & STATION_PARAM_APPLY_UAPSD) + return -EINVAL; + /* NAN NMI station must be added in associated or authorized state */ + if (!(params.sta_flags_set & (BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_AUTHENTICATED)))) + return -EINVAL; + } + /* Ensure that HT/VHT capabilities are not set for 6 GHz HE STA */ if (params.link_sta_params.he_6ghz_capa && (params.link_sta_params.ht_capa || params.link_sta_params.vht_capa)) @@ -9032,6 +9101,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED); break; + case NL80211_IFTYPE_NAN: + break; + case NL80211_IFTYPE_NAN_DATA: + params.nmi_mac = nla_data(info->attrs[NL80211_ATTR_NAN_NMI_MAC]); + break; default: return -EOPNOTSUPP; } @@ -9073,7 +9147,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); - if (!dev) + if (!dev && wdev->iftype != NL80211_IFTYPE_NAN) return -EINVAL; if (info->attrs[NL80211_ATTR_MAC]) @@ -9084,6 +9158,8 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: /* always accept these */ break; case NL80211_IFTYPE_ADHOC: -- cgit v1.2.3 From c4aa273ff6b5dae62f4981763bd91047ea6ffdda Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 18 Mar 2026 14:39:20 +0200 Subject: wifi: nl80211: define an API for configuring the NAN peer's schedule Add an NL80211 command to configure the NAN schedule of a NAN peer. Such a schedule contains a list of NAN channels, and a mapping from each time slots to the corresponding channel (or unscheduled). Also contains more information about the schedule, such as sequence ID and map ID. Not all of the restrictions are validated in this patch. In particular, comparison of two maps of the same peer requires storing/retrieving each map of each peer, only for validation. Therefore, it is the responsibilty of the driver to check that. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260219114327.5b13fa5af4f6.If0e214ff5b52c9666e985fefa3f7be0ad14d93fb@changeid Link: https://patch.msgid.link/20260318123926.206536-7-miriam.rachel.korenblit@intel.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 58 +++++++ include/uapi/linux/nl80211.h | 82 ++++++++- net/wireless/nl80211.c | 395 +++++++++++++++++++++++++++++++++++++++---- net/wireless/rdev-ops.h | 16 ++ net/wireless/trace.h | 28 +++ 5 files changed, 546 insertions(+), 33 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 654d71f60e8c..48ca5d3aa201 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4175,6 +4175,54 @@ struct cfg80211_nan_local_sched { struct cfg80211_nan_channel nan_channels[] __counted_by(n_channels); }; +/** + * struct cfg80211_nan_peer_map - NAN peer schedule map + * + * This struct defines a single NAN peer schedule map + * + * @map_id: map ID of this schedule map + * @schedule: a mapping of time slots to chandef indexes in the schedule's + * @nan_channels. Each slot lasts 16TUs. An unscheduled slot will be + * set to %NL80211_NAN_SCHED_NOT_AVAIL_SLOT. + */ +struct cfg80211_nan_peer_map { + u8 map_id; + u8 schedule[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; +}; + +#define CFG80211_NAN_MAX_PEER_MAPS 2 +#define CFG80211_NAN_INVALID_MAP_ID 0xff + +/** + * struct cfg80211_nan_peer_sched - NAN peer schedule + * + * This struct defines NAN peer schedule parameters for a peer. + * + * @peer_addr: MAC address of the peer (NMI address) + * @seq_id: sequence ID of the peer schedule. + * @committed_dw: committed DW as published by the peer. + * See %NL80211_ATTR_NAN_COMMITTED_DW + * @max_chan_switch: maximum channel switch time in microseconds as published + * by the peer. See %NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME. + * @init_ulw: initial ULWs as published by the peer. + * @ulw_size: number of bytes in @init_ulw. + * @n_channels: number of channel definitions in @nan_channels. + * @nan_channels: array of NAN channel definitions for this schedule. + * @maps: array of peer schedule maps. Unused entries have + * map_id = %CFG80211_NAN_INVALID_MAP_ID. + */ +struct cfg80211_nan_peer_sched { + const u8 *peer_addr; + u8 seq_id; + u16 committed_dw; + u16 max_chan_switch; + const u8 *init_ulw; + u16 ulw_size; + u8 n_channels; + struct cfg80211_nan_channel *nan_channels; + struct cfg80211_nan_peer_map maps[CFG80211_NAN_MAX_PEER_MAPS]; +}; + /** * enum cfg80211_nan_conf_changes - indicates changed fields in NAN * configuration @@ -4961,6 +5009,13 @@ struct mgmt_frame_regs { * radio should operate on. If the chandef of a NAN channel is not * changed, the channel entry must also remain unchanged. It is the * driver's responsibility to verify this. + * @nan_set_peer_sched: configure the peer schedule for NAN. The schedule + * consists of an array of %cfg80211_nan_channel and the schedule itself, + * in which each entry maps each time slot to a channel on which the + * radio should operate on. In addition, it contains more peer's schedule + * information such as committed DW, etc. When updating an existing peer + * schedule, the full new schedule is provided - partial updates are not + * supported, and the new schedule completely replaces the previous one. * * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS * @@ -5341,6 +5396,9 @@ struct cfg80211_ops { int (*nan_set_local_sched)(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_nan_local_sched *sched); + int (*nan_set_peer_sched)(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_nan_peer_sched *sched); int (*set_multicast_to_unicast)(struct wiphy *wiphy, struct net_device *dev, const bool enabled); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1897b9a35be8..e7f31a34eee4 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1381,6 +1381,26 @@ * %NL80211_CMD_NAN_SET_LOCAL_SCHED and %NL80211_ATTR_NAN_SCHED_DEFERRED) * has been completed. The presence of %NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS * indicates that the update was successful. + * @NL80211_CMD_NAN_SET_PEER_SCHED: Set the peer NAN schedule. NAN + * must be operational (%NL80211_CMD_START_NAN was executed). + * Required attributes: %NL80211_ATTR_MAC (peer NMI address) and + * %NL80211_ATTR_NAN_COMMITTED_DW. + * Optionally, the full schedule can be provided by including all of: + * %NL80211_ATTR_NAN_SEQ_ID, %NL80211_ATTR_NAN_CHANNEL (one or more), and + * %NL80211_ATTR_NAN_PEER_MAPS (see &enum nl80211_nan_peer_map_attrs). + * If any of these three optional attributes is provided, all three must + * be provided. + * Each peer channel must be compatible with at least one local channel + * set by %NL80211_CMD_SET_LOCAL_NAN_SCHED. Different maps must not + * contain compatible channels. + * For single-radio devices (n_radio <= 1), different maps must not + * schedule the same time slot, as the device cannot operate on multiple + * channels simultaneously. + * When updating an existing peer schedule, the full new schedule must be + * provided - partial updates are not supported. The new schedule will + * completely replace the previous one. + * The peer schedule is automatically removed when the NMI station is + * removed. * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1650,6 +1670,8 @@ enum nl80211_commands { NL80211_CMD_NAN_SCHED_UPDATE_DONE, + NL80211_CMD_NAN_SET_PEER_SCHED, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -3018,8 +3040,12 @@ enum nl80211_commands { * This attribute is used with %NL80211_CMD_NAN_SET_LOCAL_SCHED to specify * the channel definitions on which the radio needs to operate during * specific time slots. All of the channel definitions should be mutually - * incompatible. The number of channels should fit the current - * configuration of channels and the possible interface combinations. + * incompatible. + * This is also used with %NL80211_CMD_NAN_SET_PEER_SCHED to configure the + * peer NAN channels. In that case, the channel definitions can be + * compatible to each other, or even identical just with different RX NSS. + * The number of channels should fit the current configuration of channels + * and the possible interface combinations. * If an existing NAN channel is changed but the chandef isn't, the * channel entry must also remain unchanged. * @NL80211_ATTR_NAN_CHANNEL_ENTRY: a byte array of 6 bytes. contains the @@ -3027,7 +3053,7 @@ enum nl80211_commands { * 100 (Channel Entry format for the NAN Availability attribute). * @NL80211_ATTR_NAN_RX_NSS: (u8) RX NSS used for a NAN channel. This is * used with %NL80211_ATTR_NAN_CHANNEL when configuring NAN channels with - * %NL80211_CMD_NAN_SET_LOCAL_SCHED. + * %NL80211_CMD_NAN_SET_LOCAL_SCHED or %NL80211_CMD_NAN_SET_PEER_SCHED. * @NL80211_ATTR_NAN_TIME_SLOTS: an array of u8 values and 32 cells. each value * maps a time slot to the chandef on which the radio should operate on in * that time. %NL80211_NAN_SCHED_NOT_AVAIL_SLOT indicates unscheduled. @@ -3061,6 +3087,24 @@ enum nl80211_commands { * @NL80211_ATTR_NAN_NMI_MAC: The address of the NMI station to which this NDI * station belongs. Used with %NL80211_CMD_NEW_STATION when adding an NDI * station. + * @NL80211_ATTR_NAN_ULW: (Binary) The initial ULW(s) as published by the + * peer, as defined in the Wi-Fi Aware (TM) 4.0 specification Table 109 + * (Unaligned Schedule attribute format). Used to configure the device + * with the initial ULW(s) of a peer, before the device starts tracking it. + * @NL80211_ATTR_NAN_COMMITTED_DW: (u16) The committed DW as published by the + * peer, as defined in the Wi-Fi Aware (TM) 4.0 specification Table 80 + * (Committed DW Information field format). + * @NL80211_ATTR_NAN_SEQ_ID: (u8) The sequence ID of the peer schedule that + * %NL80211_CMD_NAN_SET_PEER_SCHED defines. The device follows the + * sequence ID in the frames to identify newer schedules. Once a schedule + * with a higher sequence ID is received, the device may stop communicating + * with that peer until a new peer schedule with a matching sequence ID is + * received. + * @NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME: (u16) The maximum channel switch + * time, in microseconds. + * @NL80211_ATTR_NAN_PEER_MAPS: Nested array of peer schedule maps. + * Used with %NL80211_CMD_NAN_SET_PEER_SCHED. Contains up to 2 entries, + * each containing nested attributes from &enum nl80211_nan_peer_map_attrs. * * @NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP: u32 attribute specifying * the signal interference bitmap detected on the operating bandwidth for @@ -3662,6 +3706,12 @@ enum nl80211_attrs { NL80211_ATTR_NAN_NMI_MAC, + NL80211_ATTR_NAN_ULW, + NL80211_ATTR_NAN_COMMITTED_DW, + NL80211_ATTR_NAN_SEQ_ID, + NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME, + NL80211_ATTR_NAN_PEER_MAPS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -8701,6 +8751,32 @@ enum nl80211_nan_capabilities { NL80211_NAN_CAPABILITIES_MAX = __NL80211_NAN_CAPABILITIES_LAST - 1, }; +/** + * enum nl80211_nan_peer_map_attrs - NAN peer schedule map attributes + * + * Nested attributes used within %NL80211_ATTR_NAN_PEER_MAPS to define + * individual peer schedule maps. + * + * @__NL80211_NAN_PEER_MAP_ATTR_INVALID: Invalid + * @NL80211_NAN_PEER_MAP_ATTR_MAP_ID: (u8) The map ID for this schedule map. + * @NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS: An array of u8 values with 32 cells. + * Each value maps a time slot to a channel index within the schedule's + * channel list (%NL80211_ATTR_NAN_CHANNEL attributes). + * %NL80211_NAN_SCHED_NOT_AVAIL_SLOT indicates unscheduled. + * @__NL80211_NAN_PEER_MAP_ATTR_LAST: Internal + * @NL80211_NAN_PEER_MAP_ATTR_MAX: Highest peer map attribute + */ +enum nl80211_nan_peer_map_attrs { + __NL80211_NAN_PEER_MAP_ATTR_INVALID, + + NL80211_NAN_PEER_MAP_ATTR_MAP_ID, + NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS, + + /* keep last */ + __NL80211_NAN_PEER_MAP_ATTR_LAST, + NL80211_NAN_PEER_MAP_ATTR_MAX = __NL80211_NAN_PEER_MAP_ATTR_LAST - 1, +}; + #define NL80211_NAN_SCHED_NOT_AVAIL_SLOT 0xff #endif /* __LINUX_NL80211_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 89fb61d53e2f..8f93e3548d2a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -367,6 +367,63 @@ static int validate_nan_avail_blob(const struct nlattr *attr, return 0; } +static int validate_nan_ulw(const struct nlattr *attr, + struct netlink_ext_ack *extack) +{ + const u8 *data = nla_data(attr); + unsigned int len = nla_len(attr); + unsigned int pos = 0; + + while (pos < len) { + u16 attr_len; + + /* Need at least: Attr ID (1) + Length (2) */ + if (pos + 3 > len) { + NL_SET_ERR_MSG_FMT(extack, + "ULW: Incomplete header (need 3 bytes, have %u)", + len - pos); + return -EINVAL; + } + + if (data[pos] != 0x17) { + NL_SET_ERR_MSG_FMT(extack, + "ULW: Invalid Attribute ID 0x%02x (expected 0x17)", + data[pos]); + return -EINVAL; + } + pos++; + + /* Length is in little-endian format */ + attr_len = get_unaligned_le16(&data[pos]); + pos += 2; + + /* + * Check if length is one of the valid values: 16 (no + * channel/band entry included), 18 (band entry included), + * 21 (channel entry included without Auxiliary channel bitmap), + * or 23 (channel entry included with Auxiliary channel bitmap). + */ + if (attr_len != 16 && attr_len != 18 && attr_len != 21 && + attr_len != 23) { + NL_SET_ERR_MSG_FMT(extack, + "ULW: Invalid length %u (must be 16, 18, 21, or 23)", + attr_len); + return -EINVAL; + } + + if (pos + attr_len > len) { + NL_SET_ERR_MSG_FMT(extack, + "ULW: Length field (%u) exceeds remaining data (%u)", + attr_len, len - pos); + return -EINVAL; + } + + pos += attr_len; + } + + return 0; +} + static int validate_uhr_capa(const struct nlattr *attr, struct netlink_ext_ack *extack) { @@ -589,6 +646,13 @@ nl80211_nan_band_conf_policy[NL80211_NAN_BAND_CONF_ATTR_MAX + 1] = { [NL80211_NAN_BAND_CONF_DISABLE_SCAN] = { .type = NLA_FLAG }, }; +static const struct nla_policy +nl80211_nan_peer_map_policy[NL80211_NAN_PEER_MAP_ATTR_MAX + 1] = { + [NL80211_NAN_PEER_MAP_ATTR_MAP_ID] = NLA_POLICY_MAX(NLA_U8, 15), + [NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS] = + NLA_POLICY_EXACT_LEN(CFG80211_NAN_SCHED_NUM_TIME_SLOTS), +}; + static const struct nla_policy nl80211_nan_conf_policy[NL80211_NAN_CONF_ATTR_MAX + 1] = { [NL80211_NAN_CONF_CLUSTER_ID] = @@ -1005,6 +1069,13 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_nan_avail_blob), [NL80211_ATTR_NAN_SCHED_DEFERRED] = { .type = NLA_FLAG }, [NL80211_ATTR_NAN_NMI_MAC] = NLA_POLICY_ETH_ADDR, + [NL80211_ATTR_NAN_ULW] = + NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_nan_ulw), + [NL80211_ATTR_NAN_COMMITTED_DW] = { .type = NLA_U16 }, + [NL80211_ATTR_NAN_SEQ_ID] = { .type = NLA_U8 }, + [NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME] = { .type = NLA_U16 }, + [NL80211_ATTR_NAN_PEER_MAPS] = + NLA_POLICY_NESTED_ARRAY(nl80211_nan_peer_map_policy), }; /* policy for the key attributes */ @@ -16659,8 +16730,8 @@ EXPORT_SYMBOL(cfg80211_nan_sched_update_done); static int nl80211_parse_nan_channel(struct cfg80211_registered_device *rdev, struct nlattr *channel, struct genl_info *info, - struct cfg80211_nan_local_sched *sched, - u8 index) + struct cfg80211_nan_channel *nan_channels, + u8 index, bool local) { struct nlattr **channel_parsed __free(kfree) = NULL; struct cfg80211_chan_def chandef; @@ -16695,40 +16766,304 @@ static int nl80211_parse_nan_channel(struct cfg80211_registered_device *rdev, return -EINVAL; } - for (int i = 0; i < index; i++) { - if (cfg80211_chandef_compatible(&sched->nan_channels[i].chandef, - &chandef)) { - NL_SET_ERR_MSG_ATTR(info->extack, channel, - "Channels in NAN schedule must be mutually incompatible"); - return -EINVAL; + if (local) { + for (int i = 0; i < index; i++) { + if (cfg80211_chandef_compatible(&nan_channels[i].chandef, + &chandef)) { + NL_SET_ERR_MSG_ATTR(info->extack, channel, + "Channels in NAN schedule must be mutually incompatible"); + return -EINVAL; + } } } - if (!channel_parsed[NL80211_ATTR_NAN_CHANNEL_ENTRY]) + if (!channel_parsed[NL80211_ATTR_NAN_CHANNEL_ENTRY]) { + NL_SET_ERR_MSG(info->extack, + "Missing NAN channel entry attribute"); return -EINVAL; + } - sched->nan_channels[index].channel_entry = + nan_channels[index].channel_entry = nla_data(channel_parsed[NL80211_ATTR_NAN_CHANNEL_ENTRY]); - if (!channel_parsed[NL80211_ATTR_NAN_RX_NSS]) + if (!channel_parsed[NL80211_ATTR_NAN_RX_NSS]) { + NL_SET_ERR_MSG(info->extack, + "Missing NAN RX NSS attribute"); return -EINVAL; + } - sched->nan_channels[index].rx_nss = + nan_channels[index].rx_nss = nla_get_u8(channel_parsed[NL80211_ATTR_NAN_RX_NSS]); n_rx_nss = u8_get_bits(rdev->wiphy.nan_capa.n_antennas, 0x03); - if (sched->nan_channels[index].rx_nss > n_rx_nss || - !sched->nan_channels[index].rx_nss) { + if ((local && nan_channels[index].rx_nss > n_rx_nss) || + !nan_channels[index].rx_nss) { NL_SET_ERR_MSG_ATTR(info->extack, channel, "Invalid RX NSS in NAN channel definition"); return -EINVAL; } - sched->nan_channels[index].chandef = chandef; + nan_channels[index].chandef = chandef; + + return 0; +} + +static int +nl80211_parse_nan_schedule(struct genl_info *info, struct nlattr *slots_attr, + u8 schedule[CFG80211_NAN_SCHED_NUM_TIME_SLOTS], + u8 n_channels) +{ + if (WARN_ON(nla_len(slots_attr) != CFG80211_NAN_SCHED_NUM_TIME_SLOTS)) + return -EINVAL; + + memcpy(schedule, nla_data(slots_attr), nla_len(slots_attr)); + + for (int slot = 0; slot < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; slot++) { + if (schedule[slot] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT && + schedule[slot] >= n_channels) { + NL_SET_ERR_MSG_FMT(info->extack, + "Invalid time slot: slot %d refers to channel index %d, n_channels=%d", + slot, schedule[slot], n_channels); + return -EINVAL; + } + } return 0; } +static int +nl80211_parse_nan_peer_map(struct genl_info *info, struct nlattr *map_attr, + struct cfg80211_nan_peer_map *map, u8 n_channels) +{ + struct nlattr *tb[NL80211_NAN_PEER_MAP_ATTR_MAX + 1]; + int ret; + + ret = nla_parse_nested(tb, NL80211_NAN_PEER_MAP_ATTR_MAX, map_attr, + nl80211_nan_peer_map_policy, info->extack); + if (ret) + return ret; + + if (!tb[NL80211_NAN_PEER_MAP_ATTR_MAP_ID] || + !tb[NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS]) { + NL_SET_ERR_MSG(info->extack, + "Missing required peer map attributes"); + return -EINVAL; + } + + map->map_id = nla_get_u8(tb[NL80211_NAN_PEER_MAP_ATTR_MAP_ID]); + + /* Parse schedule */ + return nl80211_parse_nan_schedule(info, + tb[NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS], + map->schedule, n_channels); +} + +static int nl80211_nan_validate_map_pair(struct wiphy *wiphy, + struct genl_info *info, + const struct cfg80211_nan_peer_map *map1, + const struct cfg80211_nan_peer_map *map2, + struct cfg80211_nan_channel *nan_channels) +{ + /* Check for duplicate map_id */ + if (map1->map_id == map2->map_id) { + NL_SET_ERR_MSG_FMT(info->extack, "Duplicate map_id %u", + map1->map_id); + return -EINVAL; + } + + /* Check for compatible channels between maps */ + for (int i = 0; i < ARRAY_SIZE(map1->schedule); i++) { + if (map1->schedule[i] == NL80211_NAN_SCHED_NOT_AVAIL_SLOT) + continue; + + for (int j = 0; j < ARRAY_SIZE(map2->schedule); j++) { + u8 ch1 = map1->schedule[i]; + u8 ch2 = map2->schedule[j]; + + if (ch2 == NL80211_NAN_SCHED_NOT_AVAIL_SLOT) + continue; + + if (cfg80211_chandef_compatible(&nan_channels[ch1].chandef, + &nan_channels[ch2].chandef)) { + NL_SET_ERR_MSG_FMT(info->extack, + "Maps %u and %u have compatible channels %d and %d", + map1->map_id, map2->map_id, + ch1, ch2); + return -EINVAL; + } + } + } + + /* + * Check for conflicting time slots between maps. + * Only check for single-radio devices (n_radio <= 1) which cannot + * operate on multiple channels simultaneously. + */ + if (wiphy->n_radio > 1) + return 0; + + for (int i = 0; i < ARRAY_SIZE(map1->schedule); i++) { + if (map1->schedule[i] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT && + map2->schedule[i] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT) { + NL_SET_ERR_MSG_FMT(info->extack, + "Maps %u and %u both schedule slot %d", + map1->map_id, map2->map_id, i); + return -EINVAL; + } + } + + return 0; +} + +static int nl80211_nan_set_peer_sched(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct cfg80211_nan_channel *nan_channels __free(kfree) = NULL; + struct cfg80211_nan_peer_sched sched = {}; + struct wireless_dev *wdev = info->user_ptr[1]; + struct nlattr *map_attr, *channel; + int ret, n_maps = 0, n_channels = 0, i = 0, rem; + + if (wdev->iftype != NL80211_IFTYPE_NAN) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_MAC] || + !info->attrs[NL80211_ATTR_NAN_COMMITTED_DW]) { + NL_SET_ERR_MSG(info->extack, + "Required NAN peer schedule attributes are missing"); + return -EINVAL; + } + + /* First count how many channel attributes we got */ + nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL, + info->nlhdr, GENL_HDRLEN, rem) + n_channels++; + + if (!((info->attrs[NL80211_ATTR_NAN_SEQ_ID] && + info->attrs[NL80211_ATTR_NAN_PEER_MAPS] && n_channels) || + ((!info->attrs[NL80211_ATTR_NAN_SEQ_ID] && + !info->attrs[NL80211_ATTR_NAN_PEER_MAPS] && !n_channels)))) { + NL_SET_ERR_MSG(info->extack, + "Either provide all of: seq id, channels and maps, or none"); + return -EINVAL; + } + + /* + * Limit the number of peer channels to: + * local_channels * 4 (possible BWs) * 2 (possible NSS values) + */ + if (n_channels && n_channels > wdev->u.nan.n_channels * 4 * 2) { + NL_SET_ERR_MSG_FMT(info->extack, + "Too many peer channels: %d (max %d)", + n_channels, + wdev->u.nan.n_channels * 4 * 2); + return -EINVAL; + } + + if (n_channels) { + nan_channels = kcalloc(n_channels, sizeof(*nan_channels), + GFP_KERNEL); + if (!nan_channels) + return -ENOMEM; + } + + /* Parse peer channels */ + nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL, + info->nlhdr, GENL_HDRLEN, rem) { + bool compatible = false; + + ret = nl80211_parse_nan_channel(rdev, channel, info, + nan_channels, i, false); + if (ret) + return ret; + + /* Verify channel is compatible with at least one local channel */ + for (int j = 0; j < wdev->u.nan.n_channels; j++) { + if (cfg80211_chandef_compatible(&nan_channels[i].chandef, + &wdev->u.nan.chandefs[j])) { + compatible = true; + break; + } + } + if (!compatible) { + NL_SET_ERR_MSG_FMT(info->extack, + "Channel %d not compatible with any local channel", + i); + return -EINVAL; + } + i++; + } + + sched.n_channels = n_channels; + sched.nan_channels = nan_channels; + sched.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + sched.seq_id = nla_get_u8_default(info->attrs[NL80211_ATTR_NAN_SEQ_ID], 0); + sched.committed_dw = nla_get_u16(info->attrs[NL80211_ATTR_NAN_COMMITTED_DW]); + sched.max_chan_switch = + nla_get_u16_default(info->attrs[NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME], 0); + + if (info->attrs[NL80211_ATTR_NAN_ULW]) { + sched.ulw_size = nla_len(info->attrs[NL80211_ATTR_NAN_ULW]); + sched.init_ulw = nla_data(info->attrs[NL80211_ATTR_NAN_ULW]); + } + + /* Initialize all maps as invalid */ + for (int j = 0; j < ARRAY_SIZE(sched.maps); j++) + sched.maps[j].map_id = CFG80211_NAN_INVALID_MAP_ID; + + if (info->attrs[NL80211_ATTR_NAN_PEER_MAPS]) { + /* Parse each map */ + nla_for_each_nested(map_attr, info->attrs[NL80211_ATTR_NAN_PEER_MAPS], + rem) { + if (n_maps >= ARRAY_SIZE(sched.maps)) { + NL_SET_ERR_MSG(info->extack, "Too many peer maps"); + return -EINVAL; + } + + ret = nl80211_parse_nan_peer_map(info, map_attr, + &sched.maps[n_maps], + n_channels); + if (ret) + return ret; + + /* Validate against previous maps */ + for (int j = 0; j < n_maps; j++) { + ret = nl80211_nan_validate_map_pair(&rdev->wiphy, info, + &sched.maps[j], + &sched.maps[n_maps], + nan_channels); + if (ret) + return ret; + } + + n_maps++; + } + } + + /* Verify each channel is scheduled at least once */ + for (int ch = 0; ch < n_channels; ch++) { + bool scheduled = false; + + for (int m = 0; m < n_maps && !scheduled; m++) { + for (int s = 0; s < ARRAY_SIZE(sched.maps[m].schedule); s++) { + if (sched.maps[m].schedule[s] == ch) { + scheduled = true; + break; + } + } + } + if (!scheduled) { + NL_SET_ERR_MSG_FMT(info->extack, + "Channel %d is not scheduled in any map", + ch); + return -EINVAL; + } + } + + return rdev_nan_set_peer_sched(rdev, wdev, &sched); +} + static bool nl80211_nan_is_sched_empty(struct cfg80211_nan_local_sched *sched) { if (!sched->n_channels) @@ -16748,7 +17083,7 @@ static int nl80211_nan_set_local_sched(struct sk_buff *skb, struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct cfg80211_nan_local_sched *sched __free(kfree) = NULL; struct wireless_dev *wdev = info->user_ptr[1]; - int rem, i = 0, n_channels = 0; + int rem, i = 0, n_channels = 0, ret; struct nlattr *channel; bool sched_empty; @@ -16775,26 +17110,20 @@ static int nl80211_nan_set_local_sched(struct sk_buff *skb, nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL, info->nlhdr, GENL_HDRLEN, rem) { - int ret = nl80211_parse_nan_channel(rdev, channel, info, sched, - i); + ret = nl80211_parse_nan_channel(rdev, channel, info, + sched->nan_channels, i, true); if (ret) return ret; i++; } - memcpy(sched->schedule, - nla_data(info->attrs[NL80211_ATTR_NAN_TIME_SLOTS]), - nla_len(info->attrs[NL80211_ATTR_NAN_TIME_SLOTS])); - - for (int slot = 0; slot < ARRAY_SIZE(sched->schedule); slot++) { - if (sched->schedule[slot] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT && - sched->schedule[slot] >= sched->n_channels) { - NL_SET_ERR_MSG(info->extack, - "Invalid time slot in NAN schedule"); - return -EINVAL; - } - } + /* Parse and validate schedule */ + ret = nl80211_parse_nan_schedule(info, + info->attrs[NL80211_ATTR_NAN_TIME_SLOTS], + sched->schedule, sched->n_channels); + if (ret) + return ret; sched_empty = nl80211_nan_is_sched_empty(sched); @@ -19646,6 +19975,12 @@ static const struct genl_small_ops nl80211_small_ops[] = { .flags = GENL_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), }, + { + .cmd = NL80211_CMD_NAN_SET_PEER_SCHED, + .doit = nl80211_nan_set_peer_sched, + .flags = GENL_ADMIN_PERM, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), + }, }; static struct genl_family nl80211_fam __ro_after_init = { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index b886dedb25c6..bba239a068f6 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1076,6 +1076,22 @@ rdev_nan_set_local_sched(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_nan_set_peer_sched(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_peer_sched *sched) +{ + int ret; + + trace_rdev_nan_set_peer_sched(&rdev->wiphy, wdev, sched); + if (rdev->ops->nan_set_peer_sched) + ret = rdev->ops->nan_set_peer_sched(&rdev->wiphy, wdev, sched); + else + ret = -EOPNOTSUPP; + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_acl_data *params) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index d32b83439363..df639d97cc0c 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2431,6 +2431,34 @@ TRACE_EVENT(rdev_nan_set_local_sched, CFG80211_NAN_SCHED_NUM_TIME_SLOTS, 1)) ); +TRACE_EVENT(rdev_nan_set_peer_sched, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_nan_peer_sched *sched), + TP_ARGS(wiphy, wdev, sched), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __array(u8, peer_addr, ETH_ALEN) + __field(u8, seq_id) + __field(u16, committed_dw) + __field(u16, max_chan_switch) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + memcpy(__entry->peer_addr, sched->peer_addr, ETH_ALEN); + __entry->seq_id = sched->seq_id; + __entry->committed_dw = sched->committed_dw; + __entry->max_chan_switch = sched->max_chan_switch; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT + ", peer: %pM, seq_id: %u, committed_dw: 0x%x, max_chan_switch: %u", + WIPHY_PR_ARG, WDEV_PR_ARG, __entry->peer_addr, + __entry->seq_id, __entry->committed_dw, + __entry->max_chan_switch + ) +); + TRACE_EVENT(rdev_set_mac_acl, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_acl_data *params), -- cgit v1.2.3 From f826534483bac96320a3686694e3e1a033087240 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 18 Mar 2026 14:39:22 +0200 Subject: wifi: nl80211: allow reporting spurious NAN Data frames Currently we have this ability for AP and GO. But it is now needed also for NAN_DATA mode - as per Wi-Fi Aware (TM) 4.0 specification 6.2.5: "If a NAN Device receives a unicast NAN Data frame destined for it, but with A1 address and A2 address that are not assigned to the NDP, it shall discard the frame, and should send a Data Path Termination NAF to the frame transmitter" To allow this, change NL80211_CMD_UNEXPECTED_FRAME to support also NAN_DATA, so drivers can report such cases and the user space can act accordingly. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260108102921.5cf9f1351655.I47c98ce37843730b8b9eb8bd8e9ef62ed6c17613@changeid Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260219094725.3846371-6-miriam.rachel.korenblit@intel.com Link: https://patch.msgid.link/20260318123926.206536-9-miriam.rachel.korenblit@intel.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 13 +++++++------ include/uapi/linux/nl80211.h | 5 +++-- net/wireless/mlme.c | 4 ++-- net/wireless/nl80211.c | 12 +++++++----- 4 files changed, 19 insertions(+), 15 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 48ca5d3aa201..0d19f34ea7ac 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -6938,8 +6938,8 @@ enum ieee80211_ap_reg_power { * the P2P Device. * @ps: powersave mode is enabled * @ps_timeout: dynamic powersave timeout - * @ap_unexpected_nlportid: (private) netlink port ID of application - * registered for unexpected class 3 frames (AP mode) + * @unexpected_nlportid: (private) netlink port ID of application + * registered for unexpected frames (AP mode or NAN_DATA mode) * @conn: (private) cfg80211 software SME connection state machine data * @connect_keys: (private) keys to set after connection is established * @conn_bss_type: connecting/connected BSS type @@ -7001,7 +7001,7 @@ struct wireless_dev { bool ps; int ps_timeout; - u32 ap_unexpected_nlportid; + u32 unexpected_nlportid; u32 owner_nlportid; bool nl_owner_dead; @@ -9572,9 +9572,10 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, * @addr: the transmitter address * @gfp: context flags * - * This function is used in AP mode (only!) to inform userspace that - * a spurious class 3 frame was received, to be able to deauth the - * sender. + * This function is used in AP mode to inform userspace that a spurious + * class 3 frame was received, to be able to deauth the sender. + * It is also used in NAN_DATA mode to report frames from unknown peers + * (A2 not assigned to any active NDP), per Wi-Fi Aware (TM) 4.0 specification 6.2.5. * Return: %true if the frame was passed to userspace (or this failed * for a reason other than not having a subscription.) */ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index e7f31a34eee4..cf6f1f6b9e36 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -906,8 +906,9 @@ * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP * (or GO) interface (i.e. hostapd) to ask for unexpected frames to * implement sending deauth to stations that send unexpected class 3 - * frames. Also used as the event sent by the kernel when such a frame - * is received. + * frames. For NAN_DATA interfaces, this is used to report frames from + * unknown peers (A2 not assigned to any active NDP). + * Also used as the event sent by the kernel when such a frame is received. * For the event, the %NL80211_ATTR_MAC attribute carries the TA and * other attributes like the interface index are present. * If used as the command it must have an interface index and you can diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 5cd86253a62e..e817ee297df0 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -782,8 +782,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) rdev_crit_proto_stop(rdev, wdev); } - if (nlportid == wdev->ap_unexpected_nlportid) - wdev->ap_unexpected_nlportid = 0; + if (nlportid == wdev->unexpected_nlportid) + wdev->unexpected_nlportid = 0; } void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8f93e3548d2a..7f47feaf4422 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -15777,13 +15777,14 @@ static int nl80211_register_unexpected_frame(struct sk_buff *skb, struct wireless_dev *wdev = dev->ieee80211_ptr; if (wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO) + wdev->iftype != NL80211_IFTYPE_P2P_GO && + wdev->iftype != NL80211_IFTYPE_NAN_DATA) return -EINVAL; - if (wdev->ap_unexpected_nlportid) + if (wdev->unexpected_nlportid) return -EBUSY; - wdev->ap_unexpected_nlportid = info->snd_portid; + wdev->unexpected_nlportid = info->snd_portid; return 0; } @@ -21281,7 +21282,7 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct sk_buff *msg; void *hdr; - u32 nlportid = READ_ONCE(wdev->ap_unexpected_nlportid); + u32 nlportid = READ_ONCE(wdev->unexpected_nlportid); if (!nlportid) return false; @@ -21321,7 +21322,8 @@ bool cfg80211_rx_spurious_frame(struct net_device *dev, const u8 *addr, trace_cfg80211_rx_spurious_frame(dev, addr, link_id); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO)) { + wdev->iftype != NL80211_IFTYPE_P2P_GO && + wdev->iftype != NL80211_IFTYPE_NAN_DATA)) { trace_cfg80211_return_bool(false); return false; } -- cgit v1.2.3 From 44ea50a5bf304d3d6b55e4a2f946ce3c45a4e648 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 18 Mar 2026 14:39:23 +0200 Subject: wifi: nl80211: add NL80211_CMD_NAN_ULW_UPDATE notification Add a new notification command that allows drivers to notify user space when the device's ULW (Unaligned Schedule) blob has been updated. This enables user space to attach the updated ULW blob to frames sent to NAN peers. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260219114327.32b715af4ebb.Ibdb6e33941afd94abf77245245f87e4338d729d3@changeid Link: https://patch.msgid.link/20260318123926.206536-10-miriam.rachel.korenblit@intel.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 14 ++++++++++++++ include/uapi/linux/nl80211.h | 5 +++++ net/wireless/nl80211.c | 43 +++++++++++++++++++++++++++++++++++++++++++ net/wireless/trace.h | 21 +++++++++++++++++++++ 4 files changed, 83 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0d19f34ea7ac..ee173f69c417 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -10574,6 +10574,20 @@ void cfg80211_nan_cluster_joined(struct wireless_dev *wdev, const u8 *cluster_id, bool new_cluster, gfp_t gfp); +/** + * cfg80211_nan_ulw_update - Notify user space about ULW update + * @wdev: Pointer to the wireless device structure + * @ulw: Pointer to the ULW blob data + * @ulw_len: Length of the ULW blob in bytes + * @gfp: Memory allocation flags + * + * This function is used by drivers to notify user space when the device's + * ULW (Unaligned Schedule) blob has been updated. User space can use this + * blob to attach to frames sent to peers. + */ +void cfg80211_nan_ulw_update(struct wireless_dev *wdev, + const u8 *ulw, size_t ulw_len, gfp_t gfp); + #ifdef CONFIG_CFG80211_DEBUGFS /** * wiphy_locked_debugfs_read - do a locked read in debugfs diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index cf6f1f6b9e36..947ec7079484 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1402,6 +1402,10 @@ * completely replace the previous one. * The peer schedule is automatically removed when the NMI station is * removed. + * @NL80211_CMD_NAN_ULW_UPDATE: Notification from the driver to user space + * with the updated ULW blob of the device. User space can use this blob + * to attach to frames sent to peers. This notification contains + * %NL80211_ATTR_NAN_ULW with the ULW blob. * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1673,6 +1677,7 @@ enum nl80211_commands { NL80211_CMD_NAN_SET_PEER_SCHED, + NL80211_CMD_NAN_ULW_UPDATE, /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7f47feaf4422..b5185655e687 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -22893,6 +22893,49 @@ void cfg80211_nan_cluster_joined(struct wireless_dev *wdev, } EXPORT_SYMBOL(cfg80211_nan_cluster_joined); +void cfg80211_nan_ulw_update(struct wireless_dev *wdev, + const u8 *ulw, size_t ulw_len, gfp_t gfp) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct sk_buff *msg; + void *hdr; + + trace_cfg80211_nan_ulw_update(wiphy, wdev, ulw, ulw_len); + + if (!wdev->owner_nlportid) + return; + + /* 32 for the wiphy idx, 64 for the wdev id, 100 for padding */ + msg = nlmsg_new(nla_total_size(sizeof(u32)) + + nla_total_size(ulw_len) + + nla_total_size(sizeof(u64)) + 100, + gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_ULW_UPDATE); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), + NL80211_ATTR_PAD) || + (ulw && ulw_len && + nla_put(msg, NL80211_ATTR_NAN_ULW, ulw_len, ulw))) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_unicast(wiphy_net(wiphy), msg, wdev->owner_nlportid); + + return; + + nla_put_failure: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_nan_ulw_update); + /* initialisation/exit functions */ int __init nl80211_init(void) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index df639d97cc0c..061bb84f1a48 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -4342,6 +4342,27 @@ TRACE_EVENT(cfg80211_nan_sched_update_done, TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT " success=%d", WIPHY_PR_ARG, WDEV_PR_ARG, __entry->success) ); + +TRACE_EVENT(cfg80211_nan_ulw_update, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + const u8 *ulw, size_t ulw_len), + TP_ARGS(wiphy, wdev, ulw, ulw_len), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __dynamic_array(u8, ulw, ulw_len) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + if (ulw && ulw_len) + memcpy(__get_dynamic_array(ulw), ulw, ulw_len); + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT " ulw: %s", + WIPHY_PR_ARG, WDEV_PR_ARG, + __print_array(__get_dynamic_array(ulw), + __get_dynamic_array_len(ulw), 1)) +); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH -- cgit v1.2.3 From 154b0296c0ecd3edb05555f824b6061438de2cd4 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 18 Mar 2026 14:39:24 +0200 Subject: wifi: nl80211: Add a notification to notify NAN channel evacuation If all available channel resources are used for NAN channels, and one of them is shared with another interface, and that interface needs to move to a different channel (for example STA interface that needs to do a channel or a link switch), then the driver can evacuate one of the NAN channels (i.e. detach it from its channel resource and announce to the peers that this channel is ULWed). In that case, the driver needs to notify user space about the channel evacuation, so the user space can adjust the local schedule accordingly. Add a notification to let userspace know about it. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260219114327.d5bebfd5ff73.Iaaf5ef17e1ab7a38c19d60558e68fcf517e2b400@changeid Link: https://patch.msgid.link/20260318123926.206536-11-miriam.rachel.korenblit@intel.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 19 ++++++++++++++++++ include/uapi/linux/nl80211.h | 27 +++++++++++++++++++------ net/wireless/nl80211.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ net/wireless/trace.h | 18 +++++++++++++++++ 4 files changed, 106 insertions(+), 6 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ee173f69c417..9d3639ff9c28 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -10588,6 +10588,25 @@ void cfg80211_nan_cluster_joined(struct wireless_dev *wdev, void cfg80211_nan_ulw_update(struct wireless_dev *wdev, const u8 *ulw, size_t ulw_len, gfp_t gfp); +/** + * cfg80211_nan_channel_evac - Notify user space about NAN channel evacuation + * @wdev: Pointer to the wireless device structure + * @chandef: Pointer to the channel definition of the NAN channel that was + * evacuated + * @gfp: Memory allocation flags + * + * This function is used by drivers to notify user space when a NAN + * channel has been evacuated (i.e. ULWed) due to channel resource conflicts + * with other interfaces. + * This can happen when another interface sharing the channel resource with NAN + * needs to move to a different channel (e.g. due to channel switch or link + * switch). User space may reconfigure the local schedule to exclude the + * evacuated channel. + */ +void cfg80211_nan_channel_evac(struct wireless_dev *wdev, + const struct cfg80211_chan_def *chandef, + gfp_t gfp); + #ifdef CONFIG_CFG80211_DEBUGFS /** * wiphy_locked_debugfs_read - do a locked read in debugfs diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 947ec7079484..3d55bf4be36f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1406,6 +1406,15 @@ * with the updated ULW blob of the device. User space can use this blob * to attach to frames sent to peers. This notification contains * %NL80211_ATTR_NAN_ULW with the ULW blob. + * @NL80211_CMD_NAN_CHANNEL_EVAC: Notification to indicate that a NAN + * channel has been evacuated due to resource conflicts with other + * interfaces. This can happen when another interface sharing the channel + * resource with NAN needs to move to a different channel (e.g., channel + * switch or link switch on a BSS interface). + * The notification contains %NL80211_ATTR_NAN_CHANNEL attribute + * identifying the evacuated channel. + * User space may reconfigure the local schedule in response to this + * notification. * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1678,6 +1687,9 @@ enum nl80211_commands { NL80211_CMD_NAN_SET_PEER_SCHED, NL80211_CMD_NAN_ULW_UPDATE, + + NL80211_CMD_NAN_CHANNEL_EVAC, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -3040,20 +3052,23 @@ enum nl80211_commands { * Currently only supported in mac80211 drivers. * @NL80211_ATTR_NAN_CHANNEL: This is a nested attribute. There can be multiple * attributes of this type, each one represents a channel definition and - * consists of top-level attributes like %NL80211_ATTR_WIPHY_FREQ. Must - * contain %NL80211_ATTR_NAN_CHANNEL_ENTRY and - * %NL80211_ATTR_NAN_RX_NSS. - * This attribute is used with %NL80211_CMD_NAN_SET_LOCAL_SCHED to specify + * consists of top-level attributes like %NL80211_ATTR_WIPHY_FREQ. + * When used with %NL80211_CMD_NAN_SET_LOCAL_SCHED, it specifies * the channel definitions on which the radio needs to operate during * specific time slots. All of the channel definitions should be mutually - * incompatible. - * This is also used with %NL80211_CMD_NAN_SET_PEER_SCHED to configure the + * incompatible. With this command, %NL80211_ATTR_NAN_CHANNEL_ENTRY and + * %NL80211_ATTR_NAN_RX_NSS are mandatory. + * When used with %NL80211_CMD_NAN_SET_PEER_SCHED, it configures the * peer NAN channels. In that case, the channel definitions can be * compatible to each other, or even identical just with different RX NSS. + * With this command, %NL80211_ATTR_NAN_CHANNEL_ENTRY and + * %NL80211_ATTR_NAN_RX_NSS are mandatory. * The number of channels should fit the current configuration of channels * and the possible interface combinations. * If an existing NAN channel is changed but the chandef isn't, the * channel entry must also remain unchanged. + * When used with %NL80211_CMD_NAN_CHANNEL_EVAC, this identifies the + * channels that were evacuated. * @NL80211_ATTR_NAN_CHANNEL_ENTRY: a byte array of 6 bytes. contains the * Channel Entry as defined in Wi-Fi Aware (TM) 4.0 specification Table * 100 (Channel Entry format for the NAN Availability attribute). diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b5185655e687..d65ebba0970b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -22936,6 +22936,54 @@ void cfg80211_nan_ulw_update(struct wireless_dev *wdev, } EXPORT_SYMBOL(cfg80211_nan_ulw_update); +void cfg80211_nan_channel_evac(struct wireless_dev *wdev, + const struct cfg80211_chan_def *chandef, + gfp_t gfp) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct sk_buff *msg; + struct nlattr *chan_attr; + void *hdr; + + trace_cfg80211_nan_channel_evac(wiphy, wdev, chandef); + + if (!wdev->owner_nlportid) + return; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_CHANNEL_EVAC); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), + NL80211_ATTR_PAD)) + goto nla_put_failure; + + chan_attr = nla_nest_start(msg, NL80211_ATTR_NAN_CHANNEL); + if (!chan_attr) + goto nla_put_failure; + + if (nl80211_send_chandef(msg, chandef)) + goto nla_put_failure; + + nla_nest_end(msg, chan_attr); + + genlmsg_end(msg, hdr); + + genlmsg_unicast(wiphy_net(wiphy), msg, wdev->owner_nlportid); + + return; + + nla_put_failure: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_nan_channel_evac); + /* initialisation/exit functions */ int __init nl80211_init(void) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 061bb84f1a48..eb5bedf9c92a 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -4363,6 +4363,24 @@ TRACE_EVENT(cfg80211_nan_ulw_update, __print_array(__get_dynamic_array(ulw), __get_dynamic_array_len(ulw), 1)) ); + +TRACE_EVENT(cfg80211_nan_channel_evac, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + const struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, wdev, chandef), + TP_STRUCT__entry( + WDEV_ENTRY + WIPHY_ENTRY + CHAN_DEF_ENTRY + ), + TP_fast_assign( + WDEV_ASSIGN; + WIPHY_ASSIGN; + CHAN_DEF_ASSIGN(chandef); + ), + TP_printk(WDEV_PR_FMT ", " WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, + WDEV_PR_ARG, WIPHY_PR_ARG, CHAN_DEF_PR_ARG) +); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH -- cgit v1.2.3 From 222edc843c158666d2d71793b4b7d0bbb9801883 Mon Sep 17 00:00:00 2001 From: Alan Maguire Date: Thu, 26 Mar 2026 14:54:36 +0000 Subject: btf: Add BTF kind layout encoding to UAPI BTF kind layouts provide information to parse BTF kinds. By separating parsing BTF from using all the information it provides, we allow BTF to encode new features even if they cannot be used by readers. This will be helpful in particular for cases where older tools are used to parse newer BTF with kinds the older tools do not recognize; the BTF can still be parsed in such cases using kind layout. The intent is to support encoding of kind layouts optionally so that tools like pahole can add this information. For each kind, we record - length of singular element following struct btf_type - length of each of the btf_vlen() elements following - a (currently unused) flags field The ideas here were discussed at [1], [2]; hence Suggested-by: Andrii Nakryiko Signed-off-by: Alan Maguire Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20260326145444.2076244-2-alan.maguire@oracle.com [1] https://lore.kernel.org/bpf/CAEf4BzYjWHRdNNw4B=eOXOs_ONrDwrgX4bn=Nuc1g8JPFC34MA@mail.gmail.com/ [2] https://lore.kernel.org/bpf/20230531201936.1992188-1-alan.maguire@oracle.com/ --- include/uapi/linux/btf.h | 12 ++++++++++++ tools/include/uapi/linux/btf.h | 12 ++++++++++++ 2 files changed, 24 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h index 266d4ffa6c07..638615ebddc2 100644 --- a/include/uapi/linux/btf.h +++ b/include/uapi/linux/btf.h @@ -8,6 +8,16 @@ #define BTF_MAGIC 0xeB9F #define BTF_VERSION 1 +/* + * BTF layout section consists of a struct btf_layout for each known + * kind at BTF encoding time. + */ +struct btf_layout { + __u8 info_sz; /* size of singular element after btf_type */ + __u8 elem_sz; /* size of each of btf_vlen(t) elements */ + __u16 flags; /* currently unused */ +}; + struct btf_header { __u16 magic; __u8 version; @@ -19,6 +29,8 @@ struct btf_header { __u32 type_len; /* length of type section */ __u32 str_off; /* offset of string section */ __u32 str_len; /* length of string section */ + __u32 layout_off; /* offset of layout section */ + __u32 layout_len; /* length of layout section */ }; /* Max # of type identifier */ diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h index 266d4ffa6c07..638615ebddc2 100644 --- a/tools/include/uapi/linux/btf.h +++ b/tools/include/uapi/linux/btf.h @@ -8,6 +8,16 @@ #define BTF_MAGIC 0xeB9F #define BTF_VERSION 1 +/* + * BTF layout section consists of a struct btf_layout for each known + * kind at BTF encoding time. + */ +struct btf_layout { + __u8 info_sz; /* size of singular element after btf_type */ + __u8 elem_sz; /* size of each of btf_vlen(t) elements */ + __u16 flags; /* currently unused */ +}; + struct btf_header { __u16 magic; __u8 version; @@ -19,6 +29,8 @@ struct btf_header { __u32 type_len; /* length of type section */ __u32 str_off; /* offset of string section */ __u32 str_len; /* length of string section */ + __u32 layout_off; /* offset of layout section */ + __u32 layout_len; /* length of layout section */ }; /* Max # of type identifier */ -- cgit v1.2.3 From 78723a62b969af404fde2468bb9782519d5f8ba7 Mon Sep 17 00:00:00 2001 From: Justin Iurman Date: Tue, 24 Mar 2026 10:14:33 +0100 Subject: seg6: add per-route tunnel source address Add SEG6_IPTUNNEL_SRC in the uapi for users to configure a specific tunnel source address. Make seg6_iptunnel handle the new attribute correctly. It has priority over the configured per-netns tunnel source address, if any. Cc: David Ahern Signed-off-by: Justin Iurman Reviewed-by: Andrea Mayer Link: https://patch.msgid.link/20260324091434.359341-2-justin.iurman@6wind.com Signed-off-by: Jakub Kicinski --- include/uapi/linux/seg6_iptunnel.h | 1 + net/ipv6/seg6_iptunnel.c | 114 +++++++++++++++++++++++++++---------- 2 files changed, 84 insertions(+), 31 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/seg6_iptunnel.h b/include/uapi/linux/seg6_iptunnel.h index ae78791372b8..485889b19900 100644 --- a/include/uapi/linux/seg6_iptunnel.h +++ b/include/uapi/linux/seg6_iptunnel.h @@ -20,6 +20,7 @@ enum { SEG6_IPTUNNEL_UNSPEC, SEG6_IPTUNNEL_SRH, + SEG6_IPTUNNEL_SRC, /* struct in6_addr */ __SEG6_IPTUNNEL_MAX, }; #define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1) diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 3e1b9991131a..e76cc0cc481e 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -49,6 +49,7 @@ static size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo) struct seg6_lwt { struct dst_cache cache; + struct in6_addr tunsrc; struct seg6_iptunnel_encap tuninfo[]; }; @@ -65,6 +66,7 @@ seg6_encap_lwtunnel(struct lwtunnel_state *lwt) static const struct nla_policy seg6_iptunnel_policy[SEG6_IPTUNNEL_MAX + 1] = { [SEG6_IPTUNNEL_SRH] = { .type = NLA_BINARY }, + [SEG6_IPTUNNEL_SRC] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)), }; static int nla_put_srh(struct sk_buff *skb, int attrtype, @@ -87,23 +89,32 @@ static int nla_put_srh(struct sk_buff *skb, int attrtype, } static void set_tun_src(struct net *net, struct net_device *dev, - struct in6_addr *daddr, struct in6_addr *saddr) + struct in6_addr *daddr, struct in6_addr *saddr, + struct in6_addr *route_tunsrc) { struct seg6_pernet_data *sdata = seg6_pernet(net); struct in6_addr *tun_src; - rcu_read_lock(); - - tun_src = rcu_dereference(sdata->tun_src); - - if (!ipv6_addr_any(tun_src)) { - memcpy(saddr, tun_src, sizeof(struct in6_addr)); + /* Priority order to select tunnel source address: + * 1. per route source address (if configured) + * 2. per network namespace source address (if configured) + * 3. dynamic resolution + */ + if (route_tunsrc && !ipv6_addr_any(route_tunsrc)) { + memcpy(saddr, route_tunsrc, sizeof(struct in6_addr)); } else { - ipv6_dev_get_saddr(net, dev, daddr, IPV6_PREFER_SRC_PUBLIC, - saddr); - } + rcu_read_lock(); + tun_src = rcu_dereference(sdata->tun_src); + + if (!ipv6_addr_any(tun_src)) { + memcpy(saddr, tun_src, sizeof(struct in6_addr)); + } else { + ipv6_dev_get_saddr(net, dev, daddr, + IPV6_PREFER_SRC_PUBLIC, saddr); + } - rcu_read_unlock(); + rcu_read_unlock(); + } } /* Compute flowlabel for outer IPv6 header */ @@ -125,7 +136,8 @@ static __be32 seg6_make_flowlabel(struct net *net, struct sk_buff *skb, } static int __seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, - int proto, struct dst_entry *cache_dst) + int proto, struct dst_entry *cache_dst, + struct in6_addr *route_tunsrc) { struct dst_entry *dst = skb_dst(skb); struct net_device *dev = dst_dev(dst); @@ -182,7 +194,7 @@ static int __seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, isrh->nexthdr = proto; hdr->daddr = isrh->segments[isrh->first_segment]; - set_tun_src(net, dev, &hdr->daddr, &hdr->saddr); + set_tun_src(net, dev, &hdr->daddr, &hdr->saddr, route_tunsrc); #ifdef CONFIG_IPV6_SEG6_HMAC if (sr_has_hmac(isrh)) { @@ -202,14 +214,15 @@ static int __seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, /* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */ int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto) { - return __seg6_do_srh_encap(skb, osrh, proto, NULL); + return __seg6_do_srh_encap(skb, osrh, proto, NULL, NULL); } EXPORT_SYMBOL_GPL(seg6_do_srh_encap); /* encapsulate an IPv6 packet within an outer IPv6 header with reduced SRH */ static int seg6_do_srh_encap_red(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto, - struct dst_entry *cache_dst) + struct dst_entry *cache_dst, + struct in6_addr *route_tunsrc) { __u8 first_seg = osrh->first_segment; struct dst_entry *dst = skb_dst(skb); @@ -272,7 +285,7 @@ static int seg6_do_srh_encap_red(struct sk_buff *skb, if (skip_srh) { hdr->nexthdr = proto; - set_tun_src(net, dev, &hdr->daddr, &hdr->saddr); + set_tun_src(net, dev, &hdr->daddr, &hdr->saddr, route_tunsrc); goto out; } @@ -308,7 +321,7 @@ static int seg6_do_srh_encap_red(struct sk_buff *skb, srcaddr: isrh->nexthdr = proto; - set_tun_src(net, dev, &hdr->daddr, &hdr->saddr); + set_tun_src(net, dev, &hdr->daddr, &hdr->saddr, route_tunsrc); #ifdef CONFIG_IPV6_SEG6_HMAC if (unlikely(!skip_srh && sr_has_hmac(isrh))) { @@ -383,9 +396,11 @@ static int seg6_do_srh(struct sk_buff *skb, struct dst_entry *cache_dst) { struct dst_entry *dst = skb_dst(skb); struct seg6_iptunnel_encap *tinfo; + struct seg6_lwt *slwt; int proto, err = 0; - tinfo = seg6_encap_lwtunnel(dst->lwtstate); + slwt = seg6_lwt_lwtunnel(dst->lwtstate); + tinfo = slwt->tuninfo; switch (tinfo->mode) { case SEG6_IPTUN_MODE_INLINE: @@ -410,11 +425,11 @@ static int seg6_do_srh(struct sk_buff *skb, struct dst_entry *cache_dst) return -EINVAL; if (tinfo->mode == SEG6_IPTUN_MODE_ENCAP) - err = __seg6_do_srh_encap(skb, tinfo->srh, - proto, cache_dst); + err = __seg6_do_srh_encap(skb, tinfo->srh, proto, + cache_dst, &slwt->tunsrc); else - err = seg6_do_srh_encap_red(skb, tinfo->srh, - proto, cache_dst); + err = seg6_do_srh_encap_red(skb, tinfo->srh, proto, + cache_dst, &slwt->tunsrc); if (err) return err; @@ -436,12 +451,12 @@ static int seg6_do_srh(struct sk_buff *skb, struct dst_entry *cache_dst) if (tinfo->mode == SEG6_IPTUN_MODE_L2ENCAP) err = __seg6_do_srh_encap(skb, tinfo->srh, - IPPROTO_ETHERNET, - cache_dst); + IPPROTO_ETHERNET, cache_dst, + &slwt->tunsrc); else err = seg6_do_srh_encap_red(skb, tinfo->srh, - IPPROTO_ETHERNET, - cache_dst); + IPPROTO_ETHERNET, cache_dst, + &slwt->tunsrc); if (err) return err; @@ -678,6 +693,10 @@ static int seg6_build_state(struct net *net, struct nlattr *nla, if (family != AF_INET6) return -EINVAL; + if (tb[SEG6_IPTUNNEL_SRC]) { + NL_SET_ERR_MSG(extack, "incompatible mode for tunsrc"); + return -EINVAL; + } break; case SEG6_IPTUN_MODE_ENCAP: break; @@ -702,13 +721,23 @@ static int seg6_build_state(struct net *net, struct nlattr *nla, slwt = seg6_lwt_lwtunnel(newts); err = dst_cache_init(&slwt->cache, GFP_ATOMIC); - if (err) { - kfree(newts); - return err; - } + if (err) + goto free_lwt_state; memcpy(&slwt->tuninfo, tuninfo, tuninfo_len); + if (tb[SEG6_IPTUNNEL_SRC]) { + slwt->tunsrc = nla_get_in6_addr(tb[SEG6_IPTUNNEL_SRC]); + + if (ipv6_addr_any(&slwt->tunsrc) || + ipv6_addr_is_multicast(&slwt->tunsrc) || + ipv6_addr_loopback(&slwt->tunsrc)) { + NL_SET_ERR_MSG(extack, "invalid tunsrc address"); + err = -EINVAL; + goto free_dst_cache; + } + } + newts->type = LWTUNNEL_ENCAP_SEG6; newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT; @@ -720,6 +749,12 @@ static int seg6_build_state(struct net *net, struct nlattr *nla, *ts = newts; return 0; + +free_dst_cache: + dst_cache_destroy(&slwt->cache); +free_lwt_state: + kfree(newts); + return err; } static void seg6_destroy_state(struct lwtunnel_state *lwt) @@ -731,29 +766,46 @@ static int seg6_fill_encap_info(struct sk_buff *skb, struct lwtunnel_state *lwtstate) { struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate); + struct seg6_lwt *slwt = seg6_lwt_lwtunnel(lwtstate); if (nla_put_srh(skb, SEG6_IPTUNNEL_SRH, tuninfo)) return -EMSGSIZE; + if (!ipv6_addr_any(&slwt->tunsrc) && + nla_put_in6_addr(skb, SEG6_IPTUNNEL_SRC, &slwt->tunsrc)) + return -EMSGSIZE; + return 0; } static int seg6_encap_nlsize(struct lwtunnel_state *lwtstate) { struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate); + struct seg6_lwt *slwt = seg6_lwt_lwtunnel(lwtstate); + int nlsize; + + nlsize = nla_total_size(SEG6_IPTUN_ENCAP_SIZE(tuninfo)); - return nla_total_size(SEG6_IPTUN_ENCAP_SIZE(tuninfo)); + if (!ipv6_addr_any(&slwt->tunsrc)) + nlsize += nla_total_size(sizeof(slwt->tunsrc)); + + return nlsize; } static int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) { struct seg6_iptunnel_encap *a_hdr = seg6_encap_lwtunnel(a); struct seg6_iptunnel_encap *b_hdr = seg6_encap_lwtunnel(b); + struct seg6_lwt *a_slwt = seg6_lwt_lwtunnel(a); + struct seg6_lwt *b_slwt = seg6_lwt_lwtunnel(b); int len = SEG6_IPTUN_ENCAP_SIZE(a_hdr); if (len != SEG6_IPTUN_ENCAP_SIZE(b_hdr)) return 1; + if (!ipv6_addr_equal(&a_slwt->tunsrc, &b_slwt->tunsrc)) + return 1; + return memcmp(a_hdr, b_hdr, len); } -- cgit v1.2.3 From 62346217fd722510c3551858ad7d0fcfab8cce7e Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Wed, 25 Feb 2026 07:51:36 -0500 Subject: NFSD: Add a key for signing filehandles A future patch will enable NFSD to sign filehandles by appending a Message Authentication Code(MAC). To do this, NFSD requires a secret 128-bit key that can persist across reboots. A persisted key allows the server to accept filehandles after a restart. Enable NFSD to be configured with this key via the netlink interface. Link: https://lore.kernel.org/linux-nfs/cover.1772022373.git.bcodding@hammerspace.com Signed-off-by: Benjamin Coddington Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever --- Documentation/netlink/specs/nfsd.yaml | 6 ++++++ fs/nfsd/netlink.c | 5 +++-- fs/nfsd/netns.h | 1 + fs/nfsd/nfsctl.c | 38 ++++++++++++++++++++++++++++++++++- fs/nfsd/trace.h | 22 ++++++++++++++++++++ include/uapi/linux/nfsd_netlink.h | 1 + 6 files changed, 70 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml index f87b5a05e5e9..8ab43c8253b2 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -81,6 +81,11 @@ attribute-sets: - name: min-threads type: u32 + - + name: fh-key + type: binary + checks: + exact-len: 16 - name: version attributes: @@ -163,6 +168,7 @@ operations: - leasetime - scope - min-threads + - fh-key - name: threads-get doc: get the maximum number of running threads diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index 887525964451..81c943345d13 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -24,12 +24,13 @@ const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1] = { }; /* NFSD_CMD_THREADS_SET - do */ -static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_MIN_THREADS + 1] = { +static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_FH_KEY + 1] = { [NFSD_A_SERVER_THREADS] = { .type = NLA_U32, }, [NFSD_A_SERVER_GRACETIME] = { .type = NLA_U32, }, [NFSD_A_SERVER_LEASETIME] = { .type = NLA_U32, }, [NFSD_A_SERVER_SCOPE] = { .type = NLA_NUL_STRING, }, [NFSD_A_SERVER_MIN_THREADS] = { .type = NLA_U32, }, + [NFSD_A_SERVER_FH_KEY] = NLA_POLICY_EXACT_LEN(16), }; /* NFSD_CMD_VERSION_SET - do */ @@ -58,7 +59,7 @@ static const struct genl_split_ops nfsd_nl_ops[] = { .cmd = NFSD_CMD_THREADS_SET, .doit = nfsd_nl_threads_set_doit, .policy = nfsd_threads_set_nl_policy, - .maxattr = NFSD_A_SERVER_MIN_THREADS, + .maxattr = NFSD_A_SERVER_FH_KEY, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index 3a89d4708e8a..6ad3fe5d7e12 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -227,6 +227,7 @@ struct nfsd_net { spinlock_t local_clients_lock; struct list_head local_clients; #endif + siphash_key_t *fh_key; }; /* Simple check to find out if a given net was properly initialized */ diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 0bf01ae411c5..20ec00f323b4 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -1581,6 +1581,32 @@ out_unlock: return ret; } +/** + * nfsd_nl_fh_key_set - helper to copy fh_key from userspace + * @attr: nlattr NFSD_A_SERVER_FH_KEY + * @nn: nfsd_net + * + * Callers should hold nfsd_mutex, returns 0 on success or negative errno. + * Callers must ensure the server is shut down (sv_nrthreads == 0), + * userspace documentation asserts the key may only be set when the server + * is not running. + */ +static int nfsd_nl_fh_key_set(const struct nlattr *attr, struct nfsd_net *nn) +{ + siphash_key_t *fh_key = nn->fh_key; + + if (!fh_key) { + fh_key = kmalloc(sizeof(siphash_key_t), GFP_KERNEL); + if (!fh_key) + return -ENOMEM; + nn->fh_key = fh_key; + } + + fh_key->key[0] = get_unaligned_le64(nla_data(attr)); + fh_key->key[1] = get_unaligned_le64(nla_data(attr) + 8); + return 0; +} + /** * nfsd_nl_threads_set_doit - set the number of running threads * @skb: reply buffer @@ -1622,7 +1648,8 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NFSD_A_SERVER_GRACETIME] || info->attrs[NFSD_A_SERVER_LEASETIME] || - info->attrs[NFSD_A_SERVER_SCOPE]) { + info->attrs[NFSD_A_SERVER_SCOPE] || + info->attrs[NFSD_A_SERVER_FH_KEY]) { ret = -EBUSY; if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads) goto out_unlock; @@ -1651,6 +1678,14 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info) attr = info->attrs[NFSD_A_SERVER_SCOPE]; if (attr) scope = nla_data(attr); + + attr = info->attrs[NFSD_A_SERVER_FH_KEY]; + if (attr) { + ret = nfsd_nl_fh_key_set(attr, nn); + trace_nfsd_ctl_fh_key_set((const char *)nn->fh_key, ret); + if (ret) + goto out_unlock; + } } attr = info->attrs[NFSD_A_SERVER_MIN_THREADS]; @@ -2237,6 +2272,7 @@ static __net_exit void nfsd_net_exit(struct net *net) { struct nfsd_net *nn = net_generic(net, nfsd_net_id); + kfree_sensitive(nn->fh_key); nfsd_proc_stat_shutdown(net); percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM); nfsd_idmap_shutdown(net); diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index d1d0b0dd0545..185a998996a0 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -2240,6 +2240,28 @@ TRACE_EVENT(nfsd_end_grace, ) ); +TRACE_EVENT(nfsd_ctl_fh_key_set, + TP_PROTO( + const char *key, + int result + ), + TP_ARGS(key, result), + TP_STRUCT__entry( + __field(u32, key_hash) + __field(int, result) + ), + TP_fast_assign( + if (key) + __entry->key_hash = ~crc32_le(0xFFFFFFFF, key, 16); + else + __entry->key_hash = 0; + __entry->result = result; + ), + TP_printk("key=0x%08x result=%d", + __entry->key_hash, __entry->result + ) +); + DECLARE_EVENT_CLASS(nfsd_copy_class, TP_PROTO( const struct nfsd4_copy *copy diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h index e9efbc9e63d8..97c7447f4d14 100644 --- a/include/uapi/linux/nfsd_netlink.h +++ b/include/uapi/linux/nfsd_netlink.h @@ -36,6 +36,7 @@ enum { NFSD_A_SERVER_LEASETIME, NFSD_A_SERVER_SCOPE, NFSD_A_SERVER_MIN_THREADS, + NFSD_A_SERVER_FH_KEY, __NFSD_A_SERVER_MAX, NFSD_A_SERVER_MAX = (__NFSD_A_SERVER_MAX - 1) -- cgit v1.2.3 From a002ad8a9bc89c084bc40933065c88336700837e Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Wed, 25 Feb 2026 07:51:37 -0500 Subject: NFSD/export: Add sign_fh export option In order to signal that filehandles on this export should be signed, add a "sign_fh" export option. Filehandle signing can help the server defend against certain filehandle guessing attacks. Setting the "sign_fh" export option sets NFSEXP_SIGN_FH. In a future patch NFSD uses this signal to append a MAC onto filehandles for that export. While we're in here, tidy a few stray expflags to more closely align to the export flag order. Link: https://lore.kernel.org/linux-nfs/cover.1772022373.git.bcodding@hammerspace.com Signed-off-by: Benjamin Coddington Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever --- fs/nfsd/export.c | 5 +++-- include/uapi/linux/nfsd/export.h | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 8e8a76a44ff0..7f4a51b832ef 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -1362,13 +1362,14 @@ static struct flags { { NFSEXP_ASYNC, {"async", "sync"}}, { NFSEXP_GATHERED_WRITES, {"wdelay", "no_wdelay"}}, { NFSEXP_NOREADDIRPLUS, {"nordirplus", ""}}, + { NFSEXP_SECURITY_LABEL, {"security_label", ""}}, + { NFSEXP_SIGN_FH, {"sign_fh", ""}}, { NFSEXP_NOHIDE, {"nohide", ""}}, - { NFSEXP_CROSSMOUNT, {"crossmnt", ""}}, { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, + { NFSEXP_CROSSMOUNT, {"crossmnt", ""}}, { NFSEXP_V4ROOT, {"v4root", ""}}, { NFSEXP_PNFS, {"pnfs", ""}}, - { NFSEXP_SECURITY_LABEL, {"security_label", ""}}, { 0, {"", ""}} }; diff --git a/include/uapi/linux/nfsd/export.h b/include/uapi/linux/nfsd/export.h index a73ca3703abb..de647cf166c3 100644 --- a/include/uapi/linux/nfsd/export.h +++ b/include/uapi/linux/nfsd/export.h @@ -34,7 +34,7 @@ #define NFSEXP_GATHERED_WRITES 0x0020 #define NFSEXP_NOREADDIRPLUS 0x0040 #define NFSEXP_SECURITY_LABEL 0x0080 -/* 0x100 currently unused */ +#define NFSEXP_SIGN_FH 0x0100 #define NFSEXP_NOHIDE 0x0200 #define NFSEXP_NOSUBTREECHECK 0x0400 #define NFSEXP_NOAUTHNLM 0x0800 /* Don't authenticate NLM requests - just trust */ @@ -55,7 +55,7 @@ #define NFSEXP_PNFS 0x20000 /* All flags that we claim to support. (Note we don't support NOACL.) */ -#define NFSEXP_ALLFLAGS 0x3FEFF +#define NFSEXP_ALLFLAGS 0x3FFFF /* The flags that may vary depending on security flavor: */ #define NFSEXP_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \ -- cgit v1.2.3 From ee90b493e0a04605b8976bf085dacc19d38e1e8f Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 19 Mar 2026 15:46:23 +0100 Subject: usb: uapi: add usb 3.0 authentication declarations This adds the USB authentication extensions to the uapi chapter 9 declarations, so that user space tools correctly operate on the descriptor and commands. This is necessary for sniffing and debugging in gadget mode to correctly work, even though the kernel does not use these requests in host mode. Signed-off-by: Oliver Neukum Link: https://patch.msgid.link/20260319144715.2957358-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/usb/ch9.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index 8003243a4937..62771e38a83d 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -102,6 +102,8 @@ #define USB_REQ_LOOPBACK_DATA_WRITE 0x15 #define USB_REQ_LOOPBACK_DATA_READ 0x16 #define USB_REQ_SET_INTERFACE_DS 0x17 +#define USB_REQ_AUTH_IN 0x18 +#define USB_REQ_AUTH_OUT 0x19 /* specific requests for USB Power Delivery */ #define USB_REQ_GET_PARTNER_PDO 20 @@ -1147,6 +1149,17 @@ struct usb_ptm_cap_descriptor { /*-------------------------------------------------------------------------*/ +struct usb_authentication_capability_descriptor { + __u8 bLength; + __u8 bDescriptorType; /* set to USB_DT_DEVICE_CAPABILITY */ + __u8 bmAttributes; + + __u8 bcdProtocolVersion; + __u8 bcdCapability; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + /* USB_DT_WIRELESS_ENDPOINT_COMP: companion descriptor associated with * each endpoint descriptor for a wireless device */ -- cgit v1.2.3 From dd13bda7338608c981da5ca79506b6fb902c8815 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 19 Mar 2026 15:46:24 +0100 Subject: USB: uapi: add BULK_MAX_PACKET_UPDATE The spec for Embedded USB2 Version 2.0 adds a new feature request. This needs to be added to uapi for monitoring. Signed-off-by: Oliver Neukum Link: https://patch.msgid.link/20260319144715.2957358-2-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/usb/ch9.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index 62771e38a83d..c3e593378377 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -123,15 +123,17 @@ * are at most sixteen features of each type.) Hubs may also support a * new USB_REQ_TEST_AND_SET_FEATURE to put ports into L1 suspend. */ -#define USB_DEVICE_SELF_POWERED 0 /* (read only) */ -#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */ -#define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */ -#define USB_DEVICE_BATTERY 2 /* (wireless) */ -#define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */ -#define USB_DEVICE_WUSB_DEVICE 3 /* (wireless)*/ -#define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */ -#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */ -#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ +#define USB_DEVICE_SELF_POWERED 0 /* (read only) */ +#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */ +#define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */ +#define USB_DEVICE_BATTERY 2 /* (wireless) */ +#define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */ +#define USB_DEVICE_WUSB_DEVICE 3 /* (wireless)*/ +#define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */ +#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */ +#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ + +#define USB_DEVICE_BULK_MAX_PACKET_UPDATE 8 /* (eUSB2v2) bump maxpacket to 1024 */ /* * Test Mode Selectors -- cgit v1.2.3 From 8800dbf6614aad1013ea5f348520a2ce5ba4b6c8 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 30 Mar 2026 15:48:32 +0100 Subject: KVM: arm64: Allow userspace to create protected VMs when pKVM is enabled Introduce a new VM type for KVM/arm64 to allow userspace to request the creation of a "protected VM" when the host has booted with pKVM enabled. For now, this feature results in a taint on first use as many aspects of a protected VM are not yet protected! Tested-by: Fuad Tabba Tested-by: Mostafa Saleh Signed-off-by: Will Deacon Link: https://patch.msgid.link/20260330144841.26181-32-will@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_pkvm.h | 2 +- arch/arm64/kvm/arm.c | 8 +++++++- arch/arm64/kvm/mmu.c | 3 --- arch/arm64/kvm/pkvm.c | 8 +++++++- include/uapi/linux/kvm.h | 5 +++++ 5 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include/uapi/linux') diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h index 7041e398fb4c..2954b311128c 100644 --- a/arch/arm64/include/asm/kvm_pkvm.h +++ b/arch/arm64/include/asm/kvm_pkvm.h @@ -17,7 +17,7 @@ #define HYP_MEMBLOCK_REGIONS 128 -int pkvm_init_host_vm(struct kvm *kvm); +int pkvm_init_host_vm(struct kvm *kvm, unsigned long type); int pkvm_create_hyp_vm(struct kvm *kvm); bool pkvm_hyp_vm_is_created(struct kvm *kvm); void pkvm_destroy_hyp_vm(struct kvm *kvm); diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 3589fc08266c..c2b666a46893 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -203,6 +203,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) { int ret; + if (type & ~KVM_VM_TYPE_ARM_MASK) + return -EINVAL; + mutex_init(&kvm->arch.config_lock); #ifdef CONFIG_LOCKDEP @@ -234,9 +237,12 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) * If any failures occur after this is successful, make sure to * call __pkvm_unreserve_vm to unreserve the VM in hyp. */ - ret = pkvm_init_host_vm(kvm); + ret = pkvm_init_host_vm(kvm, type); if (ret) goto err_uninit_mmu; + } else if (type & KVM_VM_TYPE_ARM_PROTECTED) { + ret = -EINVAL; + goto err_uninit_mmu; } kvm_vgic_early_init(kvm); diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 6a4151e3e4a3..45358ae8a300 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -881,9 +881,6 @@ static int kvm_init_ipa_range(struct kvm_s2_mmu *mmu, unsigned long type) u64 mmfr0, mmfr1; u32 phys_shift; - if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK) - return -EINVAL; - phys_shift = KVM_VM_TYPE_ARM_IPA_SIZE(type); if (is_protected_kvm_enabled()) { phys_shift = kvm_ipa_limit; diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c index 7f35df29e984..053e4f733e4b 100644 --- a/arch/arm64/kvm/pkvm.c +++ b/arch/arm64/kvm/pkvm.c @@ -225,9 +225,10 @@ void pkvm_destroy_hyp_vm(struct kvm *kvm) mutex_unlock(&kvm->arch.config_lock); } -int pkvm_init_host_vm(struct kvm *kvm) +int pkvm_init_host_vm(struct kvm *kvm, unsigned long type) { int ret; + bool protected = type & KVM_VM_TYPE_ARM_PROTECTED; if (pkvm_hyp_vm_is_created(kvm)) return -EINVAL; @@ -242,6 +243,11 @@ int pkvm_init_host_vm(struct kvm *kvm) return ret; kvm->arch.pkvm.handle = ret; + kvm->arch.pkvm.is_protected = protected; + if (protected) { + pr_warn_once("kvm: protected VMs are experimental and for development only, tainting kernel\n"); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + } return 0; } diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 80364d4dbebb..073b2bcaf560 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -703,6 +703,11 @@ struct kvm_enable_cap { #define KVM_VM_TYPE_ARM_IPA_SIZE_MASK 0xffULL #define KVM_VM_TYPE_ARM_IPA_SIZE(x) \ ((x) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK) + +#define KVM_VM_TYPE_ARM_PROTECTED (1UL << 31) +#define KVM_VM_TYPE_ARM_MASK (KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \ + KVM_VM_TYPE_ARM_PROTECTED) + /* * ioctls for /dev/kvm fds: */ -- cgit v1.2.3 From 499d2d2f4cf9f16634db47b06dee9676611b897f Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Tue, 10 Mar 2026 10:53:49 +0100 Subject: sed-opal: Add STACK_RESET command The TCG Opal device could enter a state where no new session can be created, blocking even Discovery or PSID reset. While a power cycle or waiting for the timeout should work, there is another possibility for recovery: using the Stack Reset command. The Stack Reset command is defined in the TCG Storage Architecture Core Specification and is mandatory for all Opal devices (see Section 3.3.6 of the Opal SSC specification). This patch implements the Stack Reset command. Sending it should clear all active sessions immediately, allowing subsequent commands to run successfully. While it is a TCG transport layer command, the Linux kernel implements only Opal ioctls, so it makes sense to use the IOC_OPAL ioctl interface. The Stack Reset takes no arguments; the response can be success or pending. If the command reports a pending state, userspace can try to repeat it; in this case, the code returns -EBUSY. Signed-off-by: Milan Broz Reviewed-by: Ondrej Kozina Link: https://patch.msgid.link/20260310095349.411287-1-gmazyland@gmail.com Signed-off-by: Jens Axboe --- block/opal_proto.h | 20 ++++++++++++++++++ block/sed-opal.c | 47 +++++++++++++++++++++++++++++++++++++++++++ include/linux/sed-opal.h | 1 + include/uapi/linux/sed-opal.h | 1 + 4 files changed, 69 insertions(+) (limited to 'include/uapi/linux') diff --git a/block/opal_proto.h b/block/opal_proto.h index d138785b8198..7c24247aa186 100644 --- a/block/opal_proto.h +++ b/block/opal_proto.h @@ -19,6 +19,7 @@ enum { TCG_SECP_00 = 0, TCG_SECP_01, + TCG_SECP_02, }; /* @@ -273,6 +274,25 @@ struct opal_header { struct opal_data_subpacket subpkt; }; +/* + * TCG_Storage_Architecture_Core_Spec_v2.01_r1.00 + * Section: 3.3.4.7.5 STACK_RESET + */ +#define OPAL_STACK_RESET 0x0002 + +struct opal_stack_reset { + u8 extendedComID[4]; + __be32 request_code; +}; + +struct opal_stack_reset_response { + u8 extendedComID[4]; + __be32 request_code; + u8 reserved0[2]; + __be16 data_length; + __be32 response; +}; + #define FC_TPER 0x0001 #define FC_LOCKING 0x0002 #define FC_GEOMETRY 0x0003 diff --git a/block/sed-opal.c b/block/sed-opal.c index c34d19e91201..79b290d9458a 100644 --- a/block/sed-opal.c +++ b/block/sed-opal.c @@ -3545,6 +3545,50 @@ static int opal_get_sum_ranges(struct opal_dev *dev, struct opal_sum_ranges *opa return ret; } +static int opal_stack_reset(struct opal_dev *dev) +{ + struct opal_stack_reset *req; + struct opal_stack_reset_response *resp; + int ret; + + mutex_lock(&dev->dev_lock); + + memset(dev->cmd, 0, IO_BUFFER_LENGTH); + req = (struct opal_stack_reset *)dev->cmd; + req->extendedComID[0] = dev->comid >> 8; + req->extendedComID[1] = dev->comid & 0xFF; + req->request_code = cpu_to_be32(OPAL_STACK_RESET); + + ret = dev->send_recv(dev->data, dev->comid, TCG_SECP_02, + dev->cmd, IO_BUFFER_LENGTH, true); + if (ret) { + pr_debug("Error sending stack reset: %d\n", ret); + goto out; + } + + memset(dev->resp, 0, IO_BUFFER_LENGTH); + ret = dev->send_recv(dev->data, dev->comid, TCG_SECP_02, + dev->resp, IO_BUFFER_LENGTH, false); + if (ret) { + pr_debug("Error receiving stack reset response: %d\n", ret); + goto out; + } + + resp = (struct opal_stack_reset_response *)dev->resp; + if (be16_to_cpu(resp->data_length) != 4) { + pr_debug("Stack reset pending\n"); + ret = -EBUSY; + goto out; + } + if (be32_to_cpu(resp->response) != 0) { + pr_debug("Stack reset failed: %u\n", be32_to_cpu(resp->response)); + ret = -EIO; + } +out: + mutex_unlock(&dev->dev_lock); + return ret; +} + int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg) { void *p; @@ -3642,6 +3686,9 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg) case IOC_OPAL_GET_SUM_STATUS: ret = opal_get_sum_ranges(dev, p, arg); break; + case IOC_OPAL_STACK_RESET: + ret = opal_stack_reset(dev); + break; default: break; diff --git a/include/linux/sed-opal.h b/include/linux/sed-opal.h index aa006edb612b..0630430cc01a 100644 --- a/include/linux/sed-opal.h +++ b/include/linux/sed-opal.h @@ -57,6 +57,7 @@ static inline bool is_sed_ioctl(unsigned int cmd) case IOC_OPAL_LR_SET_START_LEN: case IOC_OPAL_ENABLE_DISABLE_LR: case IOC_OPAL_GET_SUM_STATUS: + case IOC_OPAL_STACK_RESET: return true; } return false; diff --git a/include/uapi/linux/sed-opal.h b/include/uapi/linux/sed-opal.h index 9830298ec51c..ef4d3be6ca7f 100644 --- a/include/uapi/linux/sed-opal.h +++ b/include/uapi/linux/sed-opal.h @@ -245,5 +245,6 @@ struct opal_revert_lsp { #define IOC_OPAL_LR_SET_START_LEN _IOW('p', 243, struct opal_user_lr_setup) #define IOC_OPAL_ENABLE_DISABLE_LR _IOW('p', 244, struct opal_user_lr_setup) #define IOC_OPAL_GET_SUM_STATUS _IOW('p', 245, struct opal_sum_ranges) +#define IOC_OPAL_STACK_RESET _IO('p', 246) #endif /* _UAPI_SED_OPAL_H */ -- cgit v1.2.3 From f9a80c7ce49e2a77b769264712fe2f59479b5f5a Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Tue, 31 Mar 2026 06:06:12 -0700 Subject: bpf: Clarify BPF_RB_NO_WAKEUP behavior for bpf_ringbuf_discard() Clarify bpf_ringbuf_discard() documentation for BPF_RB_NO_WAKEUP. Discarded ring buffer records are still left in the ring buffer and are only skipped when user space consumes them. This can matter when BPF_RB_NO_WAKEUP is used: a later submit relying on adaptive wakeup might not wake the consumer, because the discarded record still needs to be consumed first. Scenario: epoll_wait(rb_fd); // blocks rec = bpf_ringbuf_reserve(&rb, ...); bpf_ringbuf_discard(rec, BPF_RB_NO_WAKEUP); rec = bpf_ringbuf_reserve(&rb, ...); bpf_ringbuf_submit(rec, 0); // valid record, but no wakeup Document this in bpf_ringbuf_discard() to make the interaction between discarded records, user-space consumption, and adaptive wakeups explicit. Reported-by: Shmulik Ladkani Signed-off-by: Eyal Birger Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20260331130612.3762433-1-eyal.birger@gmail.com ---- v2: adapt wording per feedback from Andrii. --- include/uapi/linux/bpf.h | 4 +++- tools/include/uapi/linux/bpf.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index c8d400b7680a..552bc5d9afbd 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -4645,7 +4645,9 @@ union bpf_attr { * Description * Discard reserved ring buffer sample, pointed to by *data*. * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification - * of new data availability is sent. + * of new data availability is sent. Discarded records remain in + * the ring buffer until consumed by user space, so a later submit + * using adaptive wakeup might not wake up the consumer. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. * If **0** is specified in *flags*, an adaptive notification diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 5e38b4887de6..677be9a47347 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -4645,7 +4645,9 @@ union bpf_attr { * Description * Discard reserved ring buffer sample, pointed to by *data*. * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification - * of new data availability is sent. + * of new data availability is sent. Discarded records remain in + * the ring buffer until consumed by user space, so a later submit + * using adaptive wakeup might not wake up the consumer. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. * If **0** is specified in *flags*, an adaptive notification -- cgit v1.2.3 From 825f2764919fca61a88ab2f93dfdfd1d22566264 Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Mon, 23 Mar 2026 12:43:54 +0000 Subject: io_uring/zcrx: implement device-less mode for zcrx Allow creating a zcrx instance without attaching it to a net device. All data will be copied through the fallback path. The user is also expected to use ZCRX_CTRL_FLUSH_RQ to handle overflows as it normally should even with a netdev, but it becomes even more relevant as there will likely be no one to automatically pick up buffers. Apart from that, it follows the zcrx uapi for the I/O path, and is useful for testing, experimentation, and potentially for the copy receive path in the future if improved. Signed-off-by: Pavel Begunkov Link: https://patch.msgid.link/674f8ad679c5a0bc79d538352b3042cf0999596e.1774261953.git.asml.silence@gmail.com [axboe: fix spelling error in uapi header and commit message] Signed-off-by: Jens Axboe --- include/uapi/linux/io_uring/zcrx.h | 9 ++++++++- io_uring/zcrx.c | 41 +++++++++++++++++++++++++------------- io_uring/zcrx.h | 2 +- 3 files changed, 36 insertions(+), 16 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/io_uring/zcrx.h b/include/uapi/linux/io_uring/zcrx.h index 3163a4b8aeb0..5ce02c7a6096 100644 --- a/include/uapi/linux/io_uring/zcrx.h +++ b/include/uapi/linux/io_uring/zcrx.h @@ -49,7 +49,14 @@ struct io_uring_zcrx_area_reg { }; enum zcrx_reg_flags { - ZCRX_REG_IMPORT = 1, + ZCRX_REG_IMPORT = 1, + + /* + * Register a zcrx instance without a net device. All data will be + * copied. The refill queue entries might not be automatically + * consumed and need to be flushed, see ZCRX_CTRL_FLUSH_RQ. + */ + ZCRX_REG_NODEV = 2, }; enum zcrx_features { diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index b0f889b11b73..c753f88b6575 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -127,10 +127,10 @@ static int io_import_dmabuf(struct io_zcrx_ifq *ifq, int dmabuf_fd = area_reg->dmabuf_fd; int i, ret; + if (!ifq->dev) + return -EINVAL; if (off) return -EINVAL; - if (WARN_ON_ONCE(!ifq->dev)) - return -EFAULT; if (!IS_ENABLED(CONFIG_DMA_SHARED_BUFFER)) return -EINVAL; @@ -211,11 +211,13 @@ static int io_import_umem(struct io_zcrx_ifq *ifq, if (ret) goto out_err; - ret = dma_map_sgtable(ifq->dev, &mem->page_sg_table, - DMA_FROM_DEVICE, IO_DMA_ATTR); - if (ret < 0) - goto out_err; - mapped = true; + if (ifq->dev) { + ret = dma_map_sgtable(ifq->dev, &mem->page_sg_table, + DMA_FROM_DEVICE, IO_DMA_ATTR); + if (ret < 0) + goto out_err; + mapped = true; + } mem->account_pages = io_count_account_pages(pages, nr_pages); ret = io_account_mem(ifq->user, ifq->mm_account, mem->account_pages); @@ -450,7 +452,8 @@ static int io_zcrx_create_area(struct io_zcrx_ifq *ifq, ret = io_import_area(ifq, &area->mem, area_reg); if (ret) goto err; - area->is_mapped = true; + if (ifq->dev) + area->is_mapped = true; if (buf_size_shift > io_area_max_shift(&area->mem)) { ret = -ERANGE; @@ -486,9 +489,11 @@ static int io_zcrx_create_area(struct io_zcrx_ifq *ifq, niov->type = NET_IOV_IOURING; } - ret = io_populate_area_dma(ifq, area); - if (ret) - goto err; + if (ifq->dev) { + ret = io_populate_area_dma(ifq, area); + if (ret) + goto err; + } area->free_count = nr_iovs; /* we're only supporting one area per ifq for now */ @@ -826,6 +831,8 @@ int io_register_zcrx_ifq(struct io_ring_ctx *ctx, return -EFAULT; if (reg.if_rxq == -1 || !reg.rq_entries) return -EINVAL; + if ((reg.if_rxq || reg.if_idx) && (reg.flags & ZCRX_REG_NODEV)) + return -EINVAL; if (reg.rq_entries > IO_RQ_MAX_ENTRIES) { if (!(ctx->flags & IORING_SETUP_CLAMP)) return -EINVAL; @@ -861,9 +868,15 @@ int io_register_zcrx_ifq(struct io_ring_ctx *ctx, if (ret) goto err; - ret = zcrx_register_netdev(ifq, ®, &area); - if (ret) - goto err; + if (!(reg.flags & ZCRX_REG_NODEV)) { + ret = zcrx_register_netdev(ifq, ®, &area); + if (ret) + goto err; + } else { + ret = io_zcrx_create_area(ifq, &area, ®); + if (ret) + goto err; + } reg.zcrx_id = id; diff --git a/io_uring/zcrx.h b/io_uring/zcrx.h index 0316a41a3561..f395656c3160 100644 --- a/io_uring/zcrx.h +++ b/io_uring/zcrx.h @@ -8,7 +8,7 @@ #include #include -#define ZCRX_SUPPORTED_REG_FLAGS (ZCRX_REG_IMPORT) +#define ZCRX_SUPPORTED_REG_FLAGS (ZCRX_REG_IMPORT | ZCRX_REG_NODEV) #define ZCRX_FEATURES (ZCRX_FEATURE_RX_PAGE_SIZE) struct io_zcrx_mem { -- cgit v1.2.3 From 4aebd7d5c72f805ef59985958ad76b8dbce60d8f Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Wed, 1 Apr 2026 17:12:21 +0200 Subject: KVM: s390: Add KVM capability for ESA mode guests Now that all the bits are properly addressed, provide a mechanism for testing ESA mode guests in nested configurations. Signed-off-by: Hendrik Brueckner [farman@us.ibm.com: Updated commit message] Reviewed-by: Janosch Frank Signed-off-by: Eric Farman Signed-off-by: Janosch Frank --- Documentation/virt/kvm/api.rst | 8 ++++++++ arch/s390/kvm/kvm-s390.c | 6 ++++++ include/uapi/linux/kvm.h | 1 + 3 files changed, 15 insertions(+) (limited to 'include/uapi/linux') diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 6f85e1b321dd..682ae9278943 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -9428,6 +9428,14 @@ KVM exits with the register state of either the L1 or L2 guest depending on which executed at the time of an exit. Userspace must take care to differentiate between these cases. +8.47 KVM_CAP_S390_VSIE_ESAMODE +------------------------------ + +:Architectures: s390 + +The presence of this capability indicates that the nested KVM guest can +start in ESA mode. + 9. Known KVM API problems ========================= diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index bc7d6fa66eaf..a583c0a00efd 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -629,6 +629,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_IRQFD_RESAMPLE: case KVM_CAP_S390_USER_OPEREXEC: case KVM_CAP_S390_KEYOP: + case KVM_CAP_S390_VSIE_ESAMODE: r = 1; break; case KVM_CAP_SET_GUEST_DEBUG2: @@ -926,6 +927,11 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) icpt_operexc_on_all_vcpus(kvm); r = 0; break; + case KVM_CAP_S390_VSIE_ESAMODE: + VM_EVENT(kvm, 3, "%s", "ENABLE: CAP_S390_VSIE_ESAMODE"); + kvm->arch.allow_vsie_esamode = 1; + r = 0; + break; default: r = -EINVAL; break; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 65500f5db379..e658f89d5d3e 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -985,6 +985,7 @@ struct kvm_enable_cap { #define KVM_CAP_ARM_SEA_TO_USER 245 #define KVM_CAP_S390_USER_OPEREXEC 246 #define KVM_CAP_S390_KEYOP 247 +#define KVM_CAP_S390_VSIE_ESAMODE 248 struct kvm_irq_routing_irqchip { __u32 irqchip; -- cgit v1.2.3 From 3fdea79c09d169b6ea172b8d36232c3773f39973 Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Thu, 2 Apr 2026 20:40:55 +0200 Subject: dpll: add frequency monitoring to netlink spec Add DPLL_A_FREQUENCY_MONITOR device attribute to allow control over the frequency monitor feature. The attribute uses the existing dpll_feature_state enum (enable/disable) and is present in both device-get reply and device-set request. Add DPLL_A_PIN_MEASURED_FREQUENCY pin attribute to expose the measured input frequency in millihertz (mHz). The attribute is present in the pin-get reply. Add DPLL_PIN_MEASURED_FREQUENCY_DIVIDER constant to allow userspace to extract integer and fractional parts. Reviewed-by: Vadim Fedorenko Signed-off-by: Ivan Vecera Link: https://patch.msgid.link/20260402184057.1890514-2-ivecera@redhat.com Signed-off-by: Jakub Kicinski --- Documentation/driver-api/dpll.rst | 20 ++++++++++++++++++++ Documentation/netlink/specs/dpll.yaml | 35 +++++++++++++++++++++++++++++++++++ drivers/dpll/dpll_nl.c | 5 +++-- include/uapi/linux/dpll.h | 5 ++++- 4 files changed, 62 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst index 83118c728ed9..93c191b2d089 100644 --- a/Documentation/driver-api/dpll.rst +++ b/Documentation/driver-api/dpll.rst @@ -250,6 +250,24 @@ in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute. ``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature =============================== ======================== +Frequency monitor +================= + +Some DPLL devices may offer the capability to measure the actual +frequency of all available input pins. The attribute and current feature state +shall be included in the response message of the ``DPLL_CMD_DEVICE_GET`` +command for supported DPLL devices. In such cases, users can also control +the feature using the ``DPLL_CMD_DEVICE_SET`` command by setting the +``enum dpll_feature_state`` values for the attribute. +Once enabled the measured input frequency for each input pin shall be +returned in the ``DPLL_A_PIN_MEASURED_FREQUENCY`` attribute. The value +is in millihertz (mHz), using ``DPLL_PIN_MEASURED_FREQUENCY_DIVIDER`` +as the divider. + + =============================== ======================== + ``DPLL_A_FREQUENCY_MONITOR`` attr state of a feature + =============================== ======================== + Embedded SYNC ============= @@ -411,6 +429,8 @@ according to attribute purpose. ``DPLL_A_PIN_STATE`` attr state of pin on the parent pin ``DPLL_A_PIN_CAPABILITIES`` attr bitmask of pin capabilities + ``DPLL_A_PIN_MEASURED_FREQUENCY`` attr measured frequency of + an input pin in mHz ==================================== ================================== ==================================== ================================= diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml index 3dd48a32f783..40465a3d7fc2 100644 --- a/Documentation/netlink/specs/dpll.yaml +++ b/Documentation/netlink/specs/dpll.yaml @@ -240,6 +240,20 @@ definitions: integer part of a measured phase offset value. Value of (DPLL_A_PHASE_OFFSET % DPLL_PHASE_OFFSET_DIVIDER) is a fractional part of a measured phase offset value. + - + type: const + name: pin-measured-frequency-divider + value: 1000 + doc: | + pin measured frequency divider allows userspace to calculate + a value of measured input frequency as a fractional value with + three digit decimal precision (millihertz). + Value of (DPLL_A_PIN_MEASURED_FREQUENCY / + DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part of + a measured frequency value. + Value of (DPLL_A_PIN_MEASURED_FREQUENCY % + DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part of + a measured frequency value. - type: enum name: feature-state @@ -319,6 +333,13 @@ attribute-sets: name: phase-offset-avg-factor type: u32 doc: Averaging factor applied to calculation of reported phase offset. + - + name: frequency-monitor + type: u32 + enum: feature-state + doc: Current or desired state of the frequency monitor feature. + If enabled, dpll device shall measure all currently available + inputs for their actual input frequency. - name: pin enum-name: dpll_a_pin @@ -456,6 +477,17 @@ attribute-sets: Value is in PPT (parts per trillion, 10^-12). Note: This attribute provides higher resolution than the standard fractional-frequency-offset (which is in PPM). + - + name: measured-frequency + type: u64 + doc: | + The measured frequency of the input pin in millihertz (mHz). + Value of (DPLL_A_PIN_MEASURED_FREQUENCY / + DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part (Hz) + of a measured frequency value. + Value of (DPLL_A_PIN_MEASURED_FREQUENCY % + DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part + of a measured frequency value. - name: pin-parent-device @@ -544,6 +576,7 @@ operations: - type - phase-offset-monitor - phase-offset-avg-factor + - frequency-monitor dump: reply: *dev-attrs @@ -563,6 +596,7 @@ operations: - mode - phase-offset-monitor - phase-offset-avg-factor + - frequency-monitor - name: device-create-ntf doc: Notification about device appearing @@ -643,6 +677,7 @@ operations: - esync-frequency-supported - esync-pulse - reference-sync + - measured-frequency dump: request: diff --git a/drivers/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c index a2b22d492114..1e652340a5d7 100644 --- a/drivers/dpll/dpll_nl.c +++ b/drivers/dpll/dpll_nl.c @@ -43,11 +43,12 @@ static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_ID + 1] = { }; /* DPLL_CMD_DEVICE_SET - do */ -static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_PHASE_OFFSET_AVG_FACTOR + 1] = { +static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_FREQUENCY_MONITOR + 1] = { [DPLL_A_ID] = { .type = NLA_U32, }, [DPLL_A_MODE] = NLA_POLICY_RANGE(NLA_U32, 1, 2), [DPLL_A_PHASE_OFFSET_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1), [DPLL_A_PHASE_OFFSET_AVG_FACTOR] = { .type = NLA_U32, }, + [DPLL_A_FREQUENCY_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1), }; /* DPLL_CMD_PIN_ID_GET - do */ @@ -115,7 +116,7 @@ static const struct genl_split_ops dpll_nl_ops[] = { .doit = dpll_nl_device_set_doit, .post_doit = dpll_post_doit, .policy = dpll_device_set_nl_policy, - .maxattr = DPLL_A_PHASE_OFFSET_AVG_FACTOR, + .maxattr = DPLL_A_FREQUENCY_MONITOR, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h index de0005f28e5c..871685f7c353 100644 --- a/include/uapi/linux/dpll.h +++ b/include/uapi/linux/dpll.h @@ -191,7 +191,8 @@ enum dpll_pin_capabilities { DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE = 4, }; -#define DPLL_PHASE_OFFSET_DIVIDER 1000 +#define DPLL_PHASE_OFFSET_DIVIDER 1000 +#define DPLL_PIN_MEASURED_FREQUENCY_DIVIDER 1000 /** * enum dpll_feature_state - Allow control (enable/disable) and status checking @@ -218,6 +219,7 @@ enum dpll_a { DPLL_A_CLOCK_QUALITY_LEVEL, DPLL_A_PHASE_OFFSET_MONITOR, DPLL_A_PHASE_OFFSET_AVG_FACTOR, + DPLL_A_FREQUENCY_MONITOR, __DPLL_A_MAX, DPLL_A_MAX = (__DPLL_A_MAX - 1) @@ -254,6 +256,7 @@ enum dpll_a_pin { DPLL_A_PIN_REFERENCE_SYNC, DPLL_A_PIN_PHASE_ADJUST_GRAN, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT, + DPLL_A_PIN_MEASURED_FREQUENCY, __DPLL_A_PIN_MAX, DPLL_A_PIN_MAX = (__DPLL_A_PIN_MAX - 1) -- cgit v1.2.3 From 2fb0ded237bb55dae45bc076666b348fc948ac9e Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Tue, 31 Mar 2026 23:31:52 +0800 Subject: ublk: add UBLK_U_CMD_REG_BUF/UNREG_BUF control commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add control commands for registering and unregistering shared memory buffers for zero-copy I/O: - UBLK_U_CMD_REG_BUF (0x18): pins pages from userspace, inserts PFN ranges into a per-device maple tree for O(log n) lookup during I/O. Buffer pointers are tracked in a per-device xarray. Returns the assigned buffer index. - UBLK_U_CMD_UNREG_BUF (0x19): removes PFN entries and unpins pages. Queue freeze/unfreeze is handled internally so userspace need not quiesce the device during registration. Also adds: - UBLK_IO_F_SHMEM_ZC flag and addr encoding helpers in UAPI header (16-bit buffer index supporting up to 65536 buffers) - Data structures (ublk_buf, ublk_buf_range) and xarray/maple tree - __ublk_ctrl_reg_buf() helper for PFN insertion with error unwinding - __ublk_ctrl_unreg_buf() helper for cleanup reuse - ublk_support_shmem_zc() / ublk_dev_support_shmem_zc() stubs (returning false — feature not enabled yet) Signed-off-by: Ming Lei Link: https://patch.msgid.link/20260331153207.3635125-2-ming.lei@redhat.com [axboe: fixup ublk_buf_reg -> ublk_shmem_buf_reg errors, comments] Signed-off-by: Jens Axboe --- drivers/block/ublk_drv.c | 295 ++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/ublk_cmd.h | 72 +++++++++++ 2 files changed, 367 insertions(+) (limited to 'include/uapi/linux') diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index eb96010625e5..e6a10a1c8cdb 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -46,6 +46,8 @@ #include #include #include +#include +#include #include #include @@ -58,6 +60,8 @@ #define UBLK_CMD_UPDATE_SIZE _IOC_NR(UBLK_U_CMD_UPDATE_SIZE) #define UBLK_CMD_QUIESCE_DEV _IOC_NR(UBLK_U_CMD_QUIESCE_DEV) #define UBLK_CMD_TRY_STOP_DEV _IOC_NR(UBLK_U_CMD_TRY_STOP_DEV) +#define UBLK_CMD_REG_BUF _IOC_NR(UBLK_U_CMD_REG_BUF) +#define UBLK_CMD_UNREG_BUF _IOC_NR(UBLK_U_CMD_UNREG_BUF) #define UBLK_IO_REGISTER_IO_BUF _IOC_NR(UBLK_U_IO_REGISTER_IO_BUF) #define UBLK_IO_UNREGISTER_IO_BUF _IOC_NR(UBLK_U_IO_UNREGISTER_IO_BUF) @@ -289,6 +293,20 @@ struct ublk_queue { struct ublk_io ios[] __counted_by(q_depth); }; +/* Per-registered shared memory buffer */ +struct ublk_buf { + struct page **pages; + unsigned int nr_pages; +}; + +/* Maple tree value: maps a PFN range to buffer location */ +struct ublk_buf_range { + unsigned long base_pfn; + unsigned short buf_index; + unsigned short flags; + unsigned int base_offset; /* byte offset within buffer */ +}; + struct ublk_device { struct gendisk *ub_disk; @@ -323,6 +341,10 @@ struct ublk_device { bool block_open; /* protected by open_mutex */ + /* shared memory zero copy */ + struct maple_tree buf_tree; + struct xarray bufs_xa; + struct ublk_queue *queues[]; }; @@ -334,6 +356,7 @@ struct ublk_params_header { static void ublk_io_release(void *priv); static void ublk_stop_dev_unlocked(struct ublk_device *ub); +static void ublk_buf_cleanup(struct ublk_device *ub); static void ublk_abort_queue(struct ublk_device *ub, struct ublk_queue *ubq); static inline struct request *__ublk_check_and_get_req(struct ublk_device *ub, u16 q_id, u16 tag, struct ublk_io *io); @@ -398,6 +421,16 @@ static inline bool ublk_dev_support_zero_copy(const struct ublk_device *ub) return ub->dev_info.flags & UBLK_F_SUPPORT_ZERO_COPY; } +static inline bool ublk_support_shmem_zc(const struct ublk_queue *ubq) +{ + return false; +} + +static inline bool ublk_dev_support_shmem_zc(const struct ublk_device *ub) +{ + return false; +} + static inline bool ublk_support_auto_buf_reg(const struct ublk_queue *ubq) { return ubq->flags & UBLK_F_AUTO_BUF_REG; @@ -1460,6 +1493,7 @@ static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req) iod->op_flags = ublk_op | ublk_req_build_flags(req); iod->nr_sectors = blk_rq_sectors(req); iod->start_sector = blk_rq_pos(req); + iod->addr = io->buf.addr; return BLK_STS_OK; @@ -1665,6 +1699,7 @@ static bool ublk_start_io(const struct ublk_queue *ubq, struct request *req, { unsigned mapped_bytes = ublk_map_io(ubq, req, io); + /* partially mapped, update io descriptor */ if (unlikely(mapped_bytes != blk_rq_bytes(req))) { /* @@ -4211,6 +4246,7 @@ static void ublk_cdev_rel(struct device *dev) { struct ublk_device *ub = container_of(dev, struct ublk_device, cdev_dev); + ublk_buf_cleanup(ub); blk_mq_free_tag_set(&ub->tag_set); ublk_deinit_queues(ub); ublk_free_dev_number(ub); @@ -4630,6 +4666,8 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header) mutex_init(&ub->mutex); spin_lock_init(&ub->lock); mutex_init(&ub->cancel_mutex); + mt_init(&ub->buf_tree); + xa_init_flags(&ub->bufs_xa, XA_FLAGS_ALLOC); INIT_WORK(&ub->partition_scan_work, ublk_partition_scan_work); ret = ublk_alloc_dev_number(ub, header->dev_id); @@ -5173,6 +5211,255 @@ exit: return err; } +/* + * Drain inflight I/O and quiesce the queue. Freeze drains all inflight + * requests, quiesce_nowait marks the queue so no new requests dispatch, + * then unfreeze allows new submissions (which won't dispatch due to + * quiesce). This keeps freeze and ub->mutex non-nested. + */ +static void ublk_quiesce_and_release(struct gendisk *disk) +{ + unsigned int memflags; + + memflags = blk_mq_freeze_queue(disk->queue); + blk_mq_quiesce_queue_nowait(disk->queue); + blk_mq_unfreeze_queue(disk->queue, memflags); +} + +static void ublk_unquiesce_and_resume(struct gendisk *disk) +{ + blk_mq_unquiesce_queue(disk->queue); +} + +/* Erase coalesced PFN ranges from the maple tree for pages [0, nr_pages) */ +static void ublk_buf_erase_ranges(struct ublk_device *ub, + struct ublk_buf *ubuf, + unsigned long nr_pages) +{ + unsigned long i; + + for (i = 0; i < nr_pages; ) { + unsigned long pfn = page_to_pfn(ubuf->pages[i]); + unsigned long start = i; + + while (i + 1 < nr_pages && + page_to_pfn(ubuf->pages[i + 1]) == pfn + (i - start) + 1) + i++; + i++; + kfree(mtree_erase(&ub->buf_tree, pfn)); + } +} + +static int __ublk_ctrl_reg_buf(struct ublk_device *ub, + struct ublk_buf *ubuf, int index, + unsigned short flags) +{ + unsigned long nr_pages = ubuf->nr_pages; + unsigned long i; + int ret; + + for (i = 0; i < nr_pages; ) { + unsigned long pfn = page_to_pfn(ubuf->pages[i]); + unsigned long start = i; + struct ublk_buf_range *range; + + /* Find run of consecutive PFNs */ + while (i + 1 < nr_pages && + page_to_pfn(ubuf->pages[i + 1]) == pfn + (i - start) + 1) + i++; + i++; /* past the last page in this run */ + + range = kzalloc(sizeof(*range), GFP_KERNEL); + if (!range) { + ret = -ENOMEM; + goto unwind; + } + range->buf_index = index; + range->flags = flags; + range->base_pfn = pfn; + range->base_offset = start << PAGE_SHIFT; + + ret = mtree_insert_range(&ub->buf_tree, pfn, + pfn + (i - start) - 1, + range, GFP_KERNEL); + if (ret) { + kfree(range); + goto unwind; + } + } + return 0; + +unwind: + ublk_buf_erase_ranges(ub, ubuf, i); + return ret; +} + +/* + * Register a shared memory buffer for zero-copy I/O. + * Pins pages, builds PFN maple tree, freezes/unfreezes the queue + * internally. Returns buffer index (>= 0) on success. + */ +static int ublk_ctrl_reg_buf(struct ublk_device *ub, + struct ublksrv_ctrl_cmd *header) +{ + void __user *argp = (void __user *)(unsigned long)header->addr; + struct ublk_shmem_buf_reg buf_reg; + unsigned long addr, size, nr_pages; + unsigned int gup_flags; + struct gendisk *disk; + struct ublk_buf *ubuf; + long pinned; + u32 index; + int ret; + + if (!ublk_dev_support_shmem_zc(ub)) + return -EOPNOTSUPP; + + memset(&buf_reg, 0, sizeof(buf_reg)); + if (copy_from_user(&buf_reg, argp, + min_t(size_t, header->len, sizeof(buf_reg)))) + return -EFAULT; + + if (buf_reg.flags & ~UBLK_SHMEM_BUF_READ_ONLY) + return -EINVAL; + + addr = buf_reg.addr; + size = buf_reg.len; + nr_pages = size >> PAGE_SHIFT; + + if (!size || !PAGE_ALIGNED(size) || !PAGE_ALIGNED(addr)) + return -EINVAL; + + disk = ublk_get_disk(ub); + if (!disk) + return -ENODEV; + + /* Pin pages before quiescing (may sleep) */ + ubuf = kzalloc(sizeof(*ubuf), GFP_KERNEL); + if (!ubuf) { + ret = -ENOMEM; + goto put_disk; + } + + ubuf->pages = kvmalloc_array(nr_pages, sizeof(*ubuf->pages), + GFP_KERNEL); + if (!ubuf->pages) { + ret = -ENOMEM; + goto err_free; + } + + gup_flags = FOLL_LONGTERM; + if (!(buf_reg.flags & UBLK_SHMEM_BUF_READ_ONLY)) + gup_flags |= FOLL_WRITE; + + pinned = pin_user_pages_fast(addr, nr_pages, gup_flags, ubuf->pages); + if (pinned < 0) { + ret = pinned; + goto err_free_pages; + } + if (pinned != nr_pages) { + ret = -EFAULT; + goto err_unpin; + } + ubuf->nr_pages = nr_pages; + + /* + * Drain inflight I/O and quiesce the queue so no new requests + * are dispatched while we modify the maple tree. Keep freeze + * and mutex non-nested to avoid lock dependency. + */ + ublk_quiesce_and_release(disk); + + mutex_lock(&ub->mutex); + + ret = xa_alloc(&ub->bufs_xa, &index, ubuf, xa_limit_16b, GFP_KERNEL); + if (ret) + goto err_unlock; + + ret = __ublk_ctrl_reg_buf(ub, ubuf, index, buf_reg.flags); + if (ret) { + xa_erase(&ub->bufs_xa, index); + goto err_unlock; + } + + mutex_unlock(&ub->mutex); + + ublk_unquiesce_and_resume(disk); + ublk_put_disk(disk); + return index; + +err_unlock: + mutex_unlock(&ub->mutex); + ublk_unquiesce_and_resume(disk); +err_unpin: + unpin_user_pages(ubuf->pages, pinned); +err_free_pages: + kvfree(ubuf->pages); +err_free: + kfree(ubuf); +put_disk: + ublk_put_disk(disk); + return ret; +} + +static void __ublk_ctrl_unreg_buf(struct ublk_device *ub, + struct ublk_buf *ubuf) +{ + ublk_buf_erase_ranges(ub, ubuf, ubuf->nr_pages); + unpin_user_pages(ubuf->pages, ubuf->nr_pages); + kvfree(ubuf->pages); + kfree(ubuf); +} + +static int ublk_ctrl_unreg_buf(struct ublk_device *ub, + struct ublksrv_ctrl_cmd *header) +{ + int index = (int)header->data[0]; + struct gendisk *disk; + struct ublk_buf *ubuf; + + if (!ublk_dev_support_shmem_zc(ub)) + return -EOPNOTSUPP; + + disk = ublk_get_disk(ub); + if (!disk) + return -ENODEV; + + /* Drain inflight I/O before modifying the maple tree */ + ublk_quiesce_and_release(disk); + + mutex_lock(&ub->mutex); + + ubuf = xa_erase(&ub->bufs_xa, index); + if (!ubuf) { + mutex_unlock(&ub->mutex); + ublk_unquiesce_and_resume(disk); + ublk_put_disk(disk); + return -ENOENT; + } + + __ublk_ctrl_unreg_buf(ub, ubuf); + + mutex_unlock(&ub->mutex); + + ublk_unquiesce_and_resume(disk); + ublk_put_disk(disk); + return 0; +} + +static void ublk_buf_cleanup(struct ublk_device *ub) +{ + struct ublk_buf *ubuf; + unsigned long index; + + xa_for_each(&ub->bufs_xa, index, ubuf) + __ublk_ctrl_unreg_buf(ub, ubuf); + xa_destroy(&ub->bufs_xa); + mtree_destroy(&ub->buf_tree); +} + + + static int ublk_ctrl_uring_cmd_permission(struct ublk_device *ub, u32 cmd_op, struct ublksrv_ctrl_cmd *header) { @@ -5230,6 +5517,8 @@ static int ublk_ctrl_uring_cmd_permission(struct ublk_device *ub, case UBLK_CMD_UPDATE_SIZE: case UBLK_CMD_QUIESCE_DEV: case UBLK_CMD_TRY_STOP_DEV: + case UBLK_CMD_REG_BUF: + case UBLK_CMD_UNREG_BUF: mask = MAY_READ | MAY_WRITE; break; default: @@ -5355,6 +5644,12 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd, case UBLK_CMD_TRY_STOP_DEV: ret = ublk_ctrl_try_stop_dev(ub); break; + case UBLK_CMD_REG_BUF: + ret = ublk_ctrl_reg_buf(ub, &header); + break; + case UBLK_CMD_UNREG_BUF: + ret = ublk_ctrl_unreg_buf(ub, &header); + break; default: ret = -EOPNOTSUPP; break; diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h index a88876756805..5b71c19d3b9c 100644 --- a/include/uapi/linux/ublk_cmd.h +++ b/include/uapi/linux/ublk_cmd.h @@ -57,6 +57,44 @@ _IOWR('u', 0x16, struct ublksrv_ctrl_cmd) #define UBLK_U_CMD_TRY_STOP_DEV \ _IOWR('u', 0x17, struct ublksrv_ctrl_cmd) +/* + * Register a shared memory buffer for zero-copy I/O. + * Input: ctrl_cmd.addr points to struct ublk_shmem_buf_reg (buffer VA + size) + * ctrl_cmd.len = sizeof(struct ublk_shmem_buf_reg) + * Result: >= 0 is the assigned buffer index, < 0 is error + * + * The kernel pins pages from the calling process's address space + * and inserts PFN ranges into a per-device maple tree. When a block + * request's pages match registered pages, the driver sets + * UBLK_IO_F_SHMEM_ZC and encodes the buffer index + offset in addr, + * allowing the server to access the data via its own mapping of the + * same shared memory — true zero copy. + * + * The memory can be backed by memfd, hugetlbfs, or any GUP-compatible + * shared mapping. Queue freeze is handled internally. + * + * The buffer VA and size are passed via a user buffer (not inline in + * ctrl_cmd) so that unprivileged devices can prepend the device path + * to ctrl_cmd.addr without corrupting the VA. + */ +#define UBLK_U_CMD_REG_BUF \ + _IOWR('u', 0x18, struct ublksrv_ctrl_cmd) +/* + * Unregister a shared memory buffer. + * Input: ctrl_cmd.data[0] = buffer index + */ +#define UBLK_U_CMD_UNREG_BUF \ + _IOWR('u', 0x19, struct ublksrv_ctrl_cmd) + +/* Parameter buffer for UBLK_U_CMD_REG_BUF, pointed to by ctrl_cmd.addr */ +struct ublk_shmem_buf_reg { + __u64 addr; /* userspace virtual address of shared memory */ + __u32 len; /* buffer size in bytes (page-aligned, max 4GB) */ + __u32 flags; +}; + +/* Pin pages without FOLL_WRITE; usable with write-sealed memfd */ +#define UBLK_SHMEM_BUF_READ_ONLY (1U << 0) /* * 64bits are enough now, and it should be easy to extend in case of * running out of feature flags @@ -370,6 +408,7 @@ /* Disable automatic partition scanning when device is started */ #define UBLK_F_NO_AUTO_PART_SCAN (1ULL << 18) + /* device state */ #define UBLK_S_DEV_DEAD 0 #define UBLK_S_DEV_LIVE 1 @@ -469,6 +508,12 @@ struct ublksrv_ctrl_dev_info { #define UBLK_IO_F_NEED_REG_BUF (1U << 17) /* Request has an integrity data buffer */ #define UBLK_IO_F_INTEGRITY (1UL << 18) +/* + * I/O buffer is in a registered shared memory buffer. When set, the addr + * field in ublksrv_io_desc encodes buffer index and byte offset instead + * of a userspace virtual address. + */ +#define UBLK_IO_F_SHMEM_ZC (1U << 19) /* * io cmd is described by this structure, and stored in share memory, indexed @@ -743,4 +788,31 @@ struct ublk_params { struct ublk_param_integrity integrity; }; +/* + * Shared memory zero-copy addr encoding for UBLK_IO_F_SHMEM_ZC. + * + * When UBLK_IO_F_SHMEM_ZC is set, ublksrv_io_desc.addr is encoded as: + * bits [0:31] = byte offset within the buffer (up to 4GB) + * bits [32:47] = buffer index (up to 65536) + * bits [48:63] = reserved (must be zero) + */ +#define UBLK_SHMEM_ZC_OFF_MASK 0xffffffffULL +#define UBLK_SHMEM_ZC_IDX_OFF 32 +#define UBLK_SHMEM_ZC_IDX_MASK 0xffffULL + +static inline __u64 ublk_shmem_zc_addr(__u16 index, __u32 offset) +{ + return ((__u64)index << UBLK_SHMEM_ZC_IDX_OFF) | offset; +} + +static inline __u16 ublk_shmem_zc_index(__u64 addr) +{ + return (addr >> UBLK_SHMEM_ZC_IDX_OFF) & UBLK_SHMEM_ZC_IDX_MASK; +} + +static inline __u32 ublk_shmem_zc_offset(__u64 addr) +{ + return (__u32)(addr & UBLK_SHMEM_ZC_OFF_MASK); +} + #endif -- cgit v1.2.3 From 08677040a91199175149d1fd465c02e3b3fc768a Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Tue, 31 Mar 2026 23:31:54 +0800 Subject: ublk: enable UBLK_F_SHMEM_ZC feature flag Add UBLK_F_SHMEM_ZC (1ULL << 19) to the UAPI header and UBLK_F_ALL. Switch ublk_support_shmem_zc() and ublk_dev_support_shmem_zc() from returning false to checking the actual flag, enabling the shared memory zero-copy feature for devices that request it. Signed-off-by: Ming Lei Link: https://patch.msgid.link/20260331153207.3635125-4-ming.lei@redhat.com [axboe: ublk_buf_reg -> ublk_shmem_buf_reg errors] Signed-off-by: Jens Axboe --- Documentation/block/ublk.rst | 117 ++++++++++++++++++++++++++++++++++++++++++ drivers/block/ublk_drv.c | 7 +-- include/uapi/linux/ublk_cmd.h | 7 +++ 3 files changed, 128 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/block/ublk.rst b/Documentation/block/ublk.rst index 6ad28039663d..e80cc415a739 100644 --- a/Documentation/block/ublk.rst +++ b/Documentation/block/ublk.rst @@ -485,6 +485,123 @@ Limitations in case that too many ublk devices are handled by this single io_ring_ctx and each one has very large queue depth +Shared Memory Zero Copy (UBLK_F_SHMEM_ZC) +------------------------------------------ + +The ``UBLK_F_SHMEM_ZC`` feature provides an alternative zero-copy path +that works by sharing physical memory pages between the client application +and the ublk server. Unlike the io_uring fixed buffer approach above, +shared memory zero copy does not require io_uring buffer registration +per I/O — instead, it relies on the kernel matching page frame numbers +(PFNs) at I/O time. This allows the ublk server to access the shared +buffer directly, which is unlikely for the io_uring fixed buffer +approach. + +Motivation +~~~~~~~~~~ + +Shared memory zero copy takes a different approach: if the client +application and the ublk server both map the same physical memory, there is +nothing to copy. The kernel detects the shared pages automatically and +tells the server where the data already lives. + +``UBLK_F_SHMEM_ZC`` can be thought of as a supplement for optimized client +applications — when the client is willing to allocate I/O buffers from +shared memory, the entire data path becomes zero-copy without any per-I/O +overhead. + +Use Cases +~~~~~~~~~ + +This feature is useful when the client application can be configured to +use a specific shared memory region for its I/O buffers: + +- **Custom storage clients** that allocate I/O buffers from shared memory + (memfd, hugetlbfs) and issue direct I/O to the ublk device +- **Database engines** that use pre-allocated buffer pools with O_DIRECT + +How It Works +~~~~~~~~~~~~ + +1. The ublk server and client both ``mmap()`` the same file (memfd or + hugetlbfs) with ``MAP_SHARED``. This gives both processes access to the + same physical pages. + +2. The ublk server registers its mapping with the kernel:: + + struct ublk_shmem_buf_reg buf = { .addr = mmap_va, .len = size }; + ublk_ctrl_cmd(UBLK_U_CMD_REG_BUF, .addr = &buf); + + The kernel pins the pages and builds a PFN lookup tree. + +3. When the client issues direct I/O (``O_DIRECT``) to ``/dev/ublkb*``, + the kernel checks whether the I/O buffer pages match any registered + pages by comparing PFNs. + +4. On a match, the kernel sets ``UBLK_IO_F_SHMEM_ZC`` in the I/O + descriptor and encodes the buffer index and offset in ``addr``:: + + if (iod->op_flags & UBLK_IO_F_SHMEM_ZC) { + /* Data is already in our shared mapping — zero copy */ + index = ublk_shmem_zc_index(iod->addr); + offset = ublk_shmem_zc_offset(iod->addr); + buf = shmem_table[index].mmap_base + offset; + } + +5. If pages do not match (e.g., the client used a non-shared buffer), + the I/O falls back to the normal copy path silently. + +The shared memory can be set up via two methods: + +- **Socket-based**: the client sends a memfd to the ublk server via + ``SCM_RIGHTS`` on a unix socket. The server mmaps and registers it. +- **Hugetlbfs-based**: both processes ``mmap(MAP_SHARED)`` the same + hugetlbfs file. No IPC needed — same file gives same physical pages. + +Advantages +~~~~~~~~~~ + +- **Simple**: no per-I/O buffer registration or unregistration commands. + Once the shared buffer is registered, all matching I/O is zero-copy + automatically. +- **Direct buffer access**: the ublk server can read and write the shared + buffer directly via its own mmap, without going through io_uring fixed + buffer operations. This is more friendly for server implementations. +- **Fast**: PFN matching is a single maple tree lookup per bvec. No + io_uring command round-trips for buffer management. +- **Compatible**: non-matching I/O silently falls back to the copy path. + The device works normally for any client, with zero-copy as an + optimization when shared memory is available. + +Limitations +~~~~~~~~~~~ + +- **Requires client cooperation**: the client must allocate its I/O + buffers from the shared memory region. This requires a custom or + configured client — standard applications using their own buffers + will not benefit. +- **Direct I/O only**: buffered I/O (without ``O_DIRECT``) goes through + the page cache, which allocates its own pages. These kernel-allocated + pages will never match the registered shared buffer. Only ``O_DIRECT`` + puts the client's buffer pages directly into the block I/O. + +Control Commands +~~~~~~~~~~~~~~~~ + +- ``UBLK_U_CMD_REG_BUF`` + + Register a shared memory buffer. ``ctrl_cmd.addr`` points to a + ``struct ublk_shmem_buf_reg`` containing the buffer virtual address and size. + Returns the assigned buffer index (>= 0) on success. The kernel pins + pages and builds the PFN lookup tree. Queue freeze is handled + internally. + +- ``UBLK_U_CMD_UNREG_BUF`` + + Unregister a previously registered buffer. ``ctrl_cmd.data[0]`` is the + buffer index. Unpins pages and removes PFN entries from the lookup + tree. + References ========== diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 264b41ceedd8..bdb1de41d526 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -85,7 +85,8 @@ | (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) ? UBLK_F_INTEGRITY : 0) \ | UBLK_F_SAFE_STOP_DEV \ | UBLK_F_BATCH_IO \ - | UBLK_F_NO_AUTO_PART_SCAN) + | UBLK_F_NO_AUTO_PART_SCAN \ + | UBLK_F_SHMEM_ZC) #define UBLK_F_ALL_RECOVERY_FLAGS (UBLK_F_USER_RECOVERY \ | UBLK_F_USER_RECOVERY_REISSUE \ @@ -425,7 +426,7 @@ static inline bool ublk_dev_support_zero_copy(const struct ublk_device *ub) static inline bool ublk_support_shmem_zc(const struct ublk_queue *ubq) { - return false; + return ubq->flags & UBLK_F_SHMEM_ZC; } static inline bool ublk_iod_is_shmem_zc(const struct ublk_queue *ubq, @@ -436,7 +437,7 @@ static inline bool ublk_iod_is_shmem_zc(const struct ublk_queue *ubq, static inline bool ublk_dev_support_shmem_zc(const struct ublk_device *ub) { - return false; + return ub->dev_info.flags & UBLK_F_SHMEM_ZC; } static inline bool ublk_support_auto_buf_reg(const struct ublk_queue *ubq) diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h index 5b71c19d3b9c..a7078b798791 100644 --- a/include/uapi/linux/ublk_cmd.h +++ b/include/uapi/linux/ublk_cmd.h @@ -408,6 +408,13 @@ struct ublk_shmem_buf_reg { /* Disable automatic partition scanning when device is started */ #define UBLK_F_NO_AUTO_PART_SCAN (1ULL << 18) +/* + * Enable shared memory zero copy. When enabled, the server can register + * shared memory buffers via UBLK_U_CMD_REG_BUF. If a block request's + * pages match a registered buffer, UBLK_IO_F_SHMEM_ZC is set and addr + * encodes the buffer index + offset instead of a userspace buffer address. + */ +#define UBLK_F_SHMEM_ZC (1ULL << 19) /* device state */ #define UBLK_S_DEV_DEAD 0 -- cgit v1.2.3 From e75e38055b9df5eafd663c6db00e634f534dc426 Mon Sep 17 00:00:00 2001 From: MickaĂ«l SalaĂĽn Date: Tue, 7 Apr 2026 18:41:05 +0200 Subject: landlock: Allow TSYNC with LOG_SUBDOMAINS_OFF and fd=-1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LANDLOCK_RESTRICT_SELF_TSYNC does not allow LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF with ruleset_fd=-1, preventing a multithreaded process from atomically propagating subdomain log muting to all threads without creating a domain layer. Relax the fd=-1 condition to accept TSYNC alongside LOG_SUBDOMAINS_OFF, and update the documentation accordingly. Add flag validation tests for all TSYNC combinations with ruleset_fd=-1, and audit tests verifying both transition directions: muting via TSYNC (logged to not logged) and override via TSYNC (not logged to logged). Cc: GĂĽnther Noack Cc: stable@vger.kernel.org Fixes: 42fc7e6543f6 ("landlock: Multithreading support for landlock_restrict_self()") Reviewed-by: GĂĽnther Noack Link: https://lore.kernel.org/r/20260407164107.2012589-2-mic@digikod.net Signed-off-by: MickaĂ«l SalaĂĽn --- include/uapi/linux/landlock.h | 4 +- security/landlock/syscalls.c | 14 +- tools/testing/selftests/landlock/audit_test.c | 233 ++++++++++++++++++++++++++ tools/testing/selftests/landlock/tsync_test.c | 77 +++++++++ 4 files changed, 322 insertions(+), 6 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index f88fa1f68b77..d37603efc273 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -116,7 +116,9 @@ struct landlock_ruleset_attr { * ``LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF``, this flag only affects * future nested domains, not the one being created. It can also be used * with a @ruleset_fd value of -1 to mute subdomain logs without creating a - * domain. + * domain. When combined with %LANDLOCK_RESTRICT_SELF_TSYNC and a + * @ruleset_fd value of -1, this configuration is propagated to all threads + * of the current process. * * The following flag supports policy enforcement in multithreaded processes: * diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 0d66a68677b7..a0bb664e0d31 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -512,10 +512,13 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32, /* * It is allowed to set LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF with - * -1 as ruleset_fd, but no other flag must be set. + * -1 as ruleset_fd, optionally combined with + * LANDLOCK_RESTRICT_SELF_TSYNC to propagate this configuration to all + * threads. No other flag must be set. */ if (!(ruleset_fd == -1 && - flags == LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF)) { + (flags & ~LANDLOCK_RESTRICT_SELF_TSYNC) == + LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF)) { /* Gets and checks the ruleset. */ ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ); if (IS_ERR(ruleset)) @@ -537,9 +540,10 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32, /* * The only case when a ruleset may not be set is if - * LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF is set and ruleset_fd is -1. - * We could optimize this case by not calling commit_creds() if this flag - * was already set, but it is not worth the complexity. + * LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF is set (optionally with + * LANDLOCK_RESTRICT_SELF_TSYNC) and ruleset_fd is -1. We could + * optimize this case by not calling commit_creds() if this flag was + * already set, but it is not worth the complexity. */ if (ruleset) { /* diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c index 20099b8667e7..897596cd7c80 100644 --- a/tools/testing/selftests/landlock/audit_test.c +++ b/tools/testing/selftests/landlock/audit_test.c @@ -162,6 +162,7 @@ TEST_F(audit, layers) struct thread_data { pid_t parent_pid; int ruleset_fd, pipe_child, pipe_parent; + bool mute_subdomains; }; static void *thread_audit_test(void *arg) @@ -367,6 +368,238 @@ TEST_F(audit, log_subdomains_off_fork) EXPECT_EQ(0, close(ruleset_fd)); } +/* + * Thread function: runs two rounds of (create domain, trigger denial, signal + * back), waiting for the main thread before each round. When mute_subdomains + * is set, phase 1 also mutes subdomain logs via the fd=-1 path before creating + * the domain. The ruleset_fd is kept open across both rounds so each + * restrict_self call stacks a new domain layer. + */ +static void *thread_sandbox_deny_twice(void *arg) +{ + const struct thread_data *data = (struct thread_data *)arg; + uintptr_t err = 0; + char buffer; + + /* Phase 1: optionally mutes, creates a domain, and triggers a denial. */ + if (read(data->pipe_parent, &buffer, 1) != 1) { + err = 1; + goto out; + } + + if (data->mute_subdomains && + landlock_restrict_self(-1, + LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF)) { + err = 2; + goto out; + } + + if (landlock_restrict_self(data->ruleset_fd, 0)) { + err = 3; + goto out; + } + + if (kill(data->parent_pid, 0) != -1 || errno != EPERM) { + err = 4; + goto out; + } + + if (write(data->pipe_child, ".", 1) != 1) { + err = 5; + goto out; + } + + /* Phase 2: stacks another domain and triggers a denial. */ + if (read(data->pipe_parent, &buffer, 1) != 1) { + err = 6; + goto out; + } + + if (landlock_restrict_self(data->ruleset_fd, 0)) { + err = 7; + goto out; + } + + if (kill(data->parent_pid, 0) != -1 || errno != EPERM) { + err = 8; + goto out; + } + + if (write(data->pipe_child, ".", 1) != 1) { + err = 9; + goto out; + } + +out: + close(data->ruleset_fd); + close(data->pipe_child); + close(data->pipe_parent); + return (void *)err; +} + +/* + * Verifies that LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF with + * LANDLOCK_RESTRICT_SELF_TSYNC and ruleset_fd=-1 propagates log_subdomains_off + * to a sibling thread, suppressing audit logging on domains it subsequently + * creates. + * + * Phase 1 (before TSYNC) acts as an inline baseline: the sibling creates a + * domain and triggers a denial that IS logged. + * + * Phase 2 (after TSYNC) verifies suppression: the sibling stacks another domain + * and triggers a denial that is NOT logged. + */ +TEST_F(audit, log_subdomains_off_tsync) +{ + const struct landlock_ruleset_attr ruleset_attr = { + .scoped = LANDLOCK_SCOPE_SIGNAL, + }; + struct audit_records records; + struct thread_data child_data = {}; + int pipe_child[2], pipe_parent[2]; + char buffer; + pthread_t thread; + void *thread_ret; + + child_data.parent_pid = getppid(); + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); + child_data.pipe_child = pipe_child[1]; + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); + child_data.pipe_parent = pipe_parent[0]; + child_data.ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, child_data.ruleset_fd); + + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); + + /* Creates the sibling thread. */ + ASSERT_EQ(0, pthread_create(&thread, NULL, thread_sandbox_deny_twice, + &child_data)); + + /* + * Phase 1: the sibling creates a domain and triggers a denial before + * any log muting. This proves the audit path works. + */ + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); + ASSERT_EQ(1, read(pipe_child[0], &buffer, 1)); + + /* The denial must be logged. */ + EXPECT_EQ(0, matches_log_signal(_metadata, self->audit_fd, + child_data.parent_pid, NULL)); + + /* Drains any remaining records (e.g. domain allocation). */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + + /* + * Mutes subdomain logs and propagates to the sibling thread via TSYNC, + * without creating a domain. + */ + ASSERT_EQ(0, landlock_restrict_self( + -1, LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF | + LANDLOCK_RESTRICT_SELF_TSYNC)); + + /* + * Phase 2: the sibling stacks another domain and triggers a denial. + * Because log_subdomains_off was propagated via TSYNC, the new domain + * has log_status=LANDLOCK_LOG_DISABLED. + */ + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); + ASSERT_EQ(1, read(pipe_child[0], &buffer, 1)); + + /* No denial record should appear. */ + EXPECT_EQ(-EAGAIN, matches_log_signal(_metadata, self->audit_fd, + child_data.parent_pid, NULL)); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + + EXPECT_EQ(0, close(pipe_child[0])); + EXPECT_EQ(0, close(pipe_parent[1])); + ASSERT_EQ(0, pthread_join(thread, &thread_ret)); + EXPECT_EQ(NULL, thread_ret); +} + +/* + * Verifies that LANDLOCK_RESTRICT_SELF_TSYNC without + * LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF overrides a sibling thread's + * log_subdomains_off, re-enabling audit logging on domains the sibling + * subsequently creates. + * + * Phase 1: the sibling sets log_subdomains_off, creates a muted domain, and + * triggers a denial that is NOT logged. + * + * Phase 2 (after TSYNC without LOG_SUBDOMAINS_OFF): the sibling stacks another + * domain and triggers a denial that IS logged, proving the muting was + * overridden. + */ +TEST_F(audit, tsync_override_log_subdomains_off) +{ + const struct landlock_ruleset_attr ruleset_attr = { + .scoped = LANDLOCK_SCOPE_SIGNAL, + }; + struct audit_records records; + struct thread_data child_data = {}; + int pipe_child[2], pipe_parent[2]; + char buffer; + pthread_t thread; + void *thread_ret; + + child_data.parent_pid = getppid(); + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); + child_data.pipe_child = pipe_child[1]; + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); + child_data.pipe_parent = pipe_parent[0]; + child_data.ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, child_data.ruleset_fd); + + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); + + child_data.mute_subdomains = true; + + /* Creates the sibling thread. */ + ASSERT_EQ(0, pthread_create(&thread, NULL, thread_sandbox_deny_twice, + &child_data)); + + /* + * Phase 1: the sibling mutes subdomain logs, creates a domain, and + * triggers a denial. The denial must not be logged. + */ + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); + ASSERT_EQ(1, read(pipe_child[0], &buffer, 1)); + + EXPECT_EQ(-EAGAIN, matches_log_signal(_metadata, self->audit_fd, + child_data.parent_pid, NULL)); + + /* Drains any remaining records. */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + + /* + * Overrides the sibling's log_subdomains_off by calling TSYNC without + * LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF. + */ + ASSERT_EQ(0, landlock_restrict_self(child_data.ruleset_fd, + LANDLOCK_RESTRICT_SELF_TSYNC)); + + /* + * Phase 2: the sibling stacks another domain and triggers a denial. + * Because TSYNC replaced its log_subdomains_off with 0, the new domain + * has log_status=LANDLOCK_LOG_PENDING. + */ + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); + ASSERT_EQ(1, read(pipe_child[0], &buffer, 1)); + + /* The denial must be logged. */ + EXPECT_EQ(0, matches_log_signal(_metadata, self->audit_fd, + child_data.parent_pid, NULL)); + + EXPECT_EQ(0, close(pipe_child[0])); + EXPECT_EQ(0, close(pipe_parent[1])); + ASSERT_EQ(0, pthread_join(thread, &thread_ret)); + EXPECT_EQ(NULL, thread_ret); +} + FIXTURE(audit_flags) { struct audit_filter audit_filter; diff --git a/tools/testing/selftests/landlock/tsync_test.c b/tools/testing/selftests/landlock/tsync_test.c index 2b9ad4f154f4..9cf1491bbaaf 100644 --- a/tools/testing/selftests/landlock/tsync_test.c +++ b/tools/testing/selftests/landlock/tsync_test.c @@ -247,4 +247,81 @@ TEST(tsync_interrupt) EXPECT_EQ(0, close(ruleset_fd)); } +/* clang-format off */ +FIXTURE(tsync_without_ruleset) {}; +/* clang-format on */ + +FIXTURE_VARIANT(tsync_without_ruleset) +{ + const __u32 flags; + const int expected_errno; +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(tsync_without_ruleset, tsync_only) { + /* clang-format on */ + .flags = LANDLOCK_RESTRICT_SELF_TSYNC, + .expected_errno = EBADF, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(tsync_without_ruleset, subdomains_off_same_exec_off) { + /* clang-format on */ + .flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF | + LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF | + LANDLOCK_RESTRICT_SELF_TSYNC, + .expected_errno = EBADF, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(tsync_without_ruleset, subdomains_off_new_exec_on) { + /* clang-format on */ + .flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF | + LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON | + LANDLOCK_RESTRICT_SELF_TSYNC, + .expected_errno = EBADF, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(tsync_without_ruleset, all_flags) { + /* clang-format on */ + .flags = LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF | + LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON | + LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF | + LANDLOCK_RESTRICT_SELF_TSYNC, + .expected_errno = EBADF, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(tsync_without_ruleset, subdomains_off) { + /* clang-format on */ + .flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF | + LANDLOCK_RESTRICT_SELF_TSYNC, + .expected_errno = 0, +}; + +FIXTURE_SETUP(tsync_without_ruleset) +{ + disable_caps(_metadata); +} + +FIXTURE_TEARDOWN(tsync_without_ruleset) +{ +} + +TEST_F(tsync_without_ruleset, check) +{ + int ret; + + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); + + ret = landlock_restrict_self(-1, variant->flags); + if (variant->expected_errno) { + EXPECT_EQ(-1, ret); + EXPECT_EQ(variant->expected_errno, errno); + } else { + EXPECT_EQ(0, ret); + } +} + TEST_HARNESS_MAIN -- cgit v1.2.3 From ae97330d1bd6a97646c2842d117577236cb40913 Mon Sep 17 00:00:00 2001 From: GĂĽnther Noack Date: Fri, 27 Mar 2026 17:48:29 +0100 Subject: landlock: Control pathname UNIX domain socket resolution by path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add a new access right LANDLOCK_ACCESS_FS_RESOLVE_UNIX, which controls the lookup operations for named UNIX domain sockets. The resolution happens during connect() and sendmsg() (depending on socket type). * Change access_mask_t from u16 to u32 (see below) * Hook into the path lookup in unix_find_bsd() in af_unix.c, using a LSM hook. Make policy decisions based on the new access rights * Increment the Landlock ABI version. * Minor test adaptations to keep the tests working. * Document the design rationale for scoped access rights, and cross-reference it from the header documentation. With this access right, access is granted if either of the following conditions is met: * The target socket's filesystem path was allow-listed using a LANDLOCK_RULE_PATH_BENEATH rule, *or*: * The target socket was created in the same Landlock domain in which LANDLOCK_ACCESS_FS_RESOLVE_UNIX was restricted. In case of a denial, connect() and sendmsg() return EACCES, which is the same error as it is returned if the user does not have the write bit in the traditional UNIX file system permissions of that file. The access_mask_t type grows from u16 to u32 to make space for the new access right. This also doubles the size of struct layer_access_masks from 32 byte to 64 byte. To avoid memory layout inconsistencies between architectures (especially m68k), pack and align struct access_masks [2]. Document the (possible future) interaction between scoped flags and other access rights in struct landlock_ruleset_attr, and summarize the rationale, as discussed in code review leading up to [3]. This feature was created with substantial discussion and input from Justin Suess, Tingmao Wang and MickaĂ«l SalaĂĽn. Cc: Tingmao Wang Cc: Justin Suess Cc: Kuniyuki Iwashima Suggested-by: Jann Horn Link[1]: https://github.com/landlock-lsm/linux/issues/36 Link[2]: https://lore.kernel.org/all/20260401.Re1Eesu1Yaij@digikod.net/ Link[3]: https://lore.kernel.org/all/20260205.8531e4005118@gnoack.org/ Signed-off-by: GĂĽnther Noack Acked-by: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/20260327164838.38231-5-gnoack3000@gmail.com [mic: Fix kernel-doc formatting, pack and align access_masks] Signed-off-by: MickaĂ«l SalaĂĽn --- Documentation/security/landlock.rst | 42 ++++++++- include/uapi/linux/landlock.h | 21 +++++ security/landlock/access.h | 4 +- security/landlock/audit.c | 1 + security/landlock/fs.c | 130 ++++++++++++++++++++++++++- security/landlock/limits.h | 2 +- security/landlock/syscalls.c | 2 +- tools/testing/selftests/landlock/base_test.c | 2 +- tools/testing/selftests/landlock/fs_test.c | 5 +- 9 files changed, 200 insertions(+), 9 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/security/landlock.rst b/Documentation/security/landlock.rst index 3e4d4d04cfae..c5186526e76f 100644 --- a/Documentation/security/landlock.rst +++ b/Documentation/security/landlock.rst @@ -7,7 +7,7 @@ Landlock LSM: kernel documentation ================================== :Author: MickaĂ«l SalaĂĽn -:Date: September 2025 +:Date: March 2026 Landlock's goal is to create scoped access-control (i.e. sandboxing). To harden a whole system, this feature should be available to any process, @@ -89,6 +89,46 @@ this is required to keep access controls consistent over the whole system, and this avoids unattended bypasses through file descriptor passing (i.e. confused deputy attack). +.. _scoped-flags-interaction: + +Interaction between scoped flags and other access rights +-------------------------------------------------------- + +The ``scoped`` flags in &struct landlock_ruleset_attr restrict the +use of *outgoing* IPC from the created Landlock domain, while they +permit reaching out to IPC endpoints *within* the created Landlock +domain. + +In the future, scoped flags *may* interact with other access rights, +e.g. so that abstract UNIX sockets can be allow-listed by name, or so +that signals can be allow-listed by signal number or target process. + +When introducing ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX``, we defined it to +implicitly have the same scoping semantics as a +``LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET`` flag would have: connecting to +UNIX sockets within the same domain (where +``LANDLOCK_ACCESS_FS_RESOLVE_UNIX`` is used) is unconditionally +allowed. + +The reasoning is: + +* Like other IPC mechanisms, connecting to named UNIX sockets in the + same domain should be expected and harmless. (If needed, users can + further refine their Landlock policies with nested domains or by + restricting ``LANDLOCK_ACCESS_FS_MAKE_SOCK``.) +* We reserve the option to still introduce + ``LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET`` in the future. (This would + be useful if we wanted to have a Landlock rule to permit IPC access + to other Landlock domains.) +* But we can postpone the point in time when users have to deal with + two interacting flags visible in the userspace API. (In particular, + it is possible that it won't be needed in practice, in which case we + can avoid the second flag altogether.) +* If we *do* introduce ``LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET`` in the + future, setting this scoped flag in a ruleset does *not reduce* the + restrictions, because access within the same scope is already + allowed based on ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX``. + Tests ===== diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index d37603efc273..10a346e55e95 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -250,6 +250,26 @@ struct landlock_net_port_attr { * * This access right is available since the fifth version of the Landlock * ABI. + * - %LANDLOCK_ACCESS_FS_RESOLVE_UNIX: Look up pathname UNIX domain sockets + * (:manpage:`unix(7)`). On UNIX domain sockets, this restricts both calls to + * :manpage:`connect(2)` as well as calls to :manpage:`sendmsg(2)` with an + * explicit recipient address. + * + * This access right only applies to connections to UNIX server sockets which + * were created outside of the newly created Landlock domain (e.g. from within + * a parent domain or from an unrestricted process). Newly created UNIX + * servers within the same Landlock domain continue to be accessible. In this + * regard, %LANDLOCK_ACCESS_FS_RESOLVE_UNIX has the same semantics as the + * ``LANDLOCK_SCOPE_*`` flags. + * + * If a resolve attempt is denied, the operation returns an ``EACCES`` error, + * in line with other filesystem access rights (but different to denials for + * abstract UNIX domain sockets). + * + * This access right is available since the ninth version of the Landlock ABI. + * + * The rationale for this design is described in + * :ref:`Documentation/security/landlock.rst `. * * Whether an opened file can be truncated with :manpage:`ftruncate(2)` or used * with `ioctl(2)` is determined during :manpage:`open(2)`, in the same way as @@ -335,6 +355,7 @@ struct landlock_net_port_attr { #define LANDLOCK_ACCESS_FS_REFER (1ULL << 13) #define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14) #define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15) +#define LANDLOCK_ACCESS_FS_RESOLVE_UNIX (1ULL << 16) /* clang-format on */ /** diff --git a/security/landlock/access.h b/security/landlock/access.h index 42c95747d7bd..c19d5bc13944 100644 --- a/security/landlock/access.h +++ b/security/landlock/access.h @@ -34,7 +34,7 @@ LANDLOCK_ACCESS_FS_IOCTL_DEV) /* clang-format on */ -typedef u16 access_mask_t; +typedef u32 access_mask_t; /* Makes sure all filesystem access rights can be stored. */ static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS); @@ -50,7 +50,7 @@ struct access_masks { access_mask_t fs : LANDLOCK_NUM_ACCESS_FS; access_mask_t net : LANDLOCK_NUM_ACCESS_NET; access_mask_t scope : LANDLOCK_NUM_SCOPE; -}; +} __packed __aligned(sizeof(u32)); union access_masks_all { struct access_masks masks; diff --git a/security/landlock/audit.c b/security/landlock/audit.c index 60ff217ab95b..8d0edf94037d 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -37,6 +37,7 @@ static const char *const fs_access_strings[] = { [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = "fs.refer", [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "fs.truncate", [BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = "fs.ioctl_dev", + [BIT_INDEX(LANDLOCK_ACCESS_FS_RESOLVE_UNIX)] = "fs.resolve_unix", }; static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS); diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 97065d51685a..fcf69b3d734d 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include @@ -314,7 +316,8 @@ retry: LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE | \ LANDLOCK_ACCESS_FS_TRUNCATE | \ - LANDLOCK_ACCESS_FS_IOCTL_DEV) + LANDLOCK_ACCESS_FS_IOCTL_DEV | \ + LANDLOCK_ACCESS_FS_RESOLVE_UNIX) /* clang-format on */ /* @@ -1557,6 +1560,130 @@ static int hook_path_truncate(const struct path *const path) return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE); } +/** + * unmask_scoped_access - Remove access right bits in @masks in all layers + * where @client and @server have the same domain + * + * This does the same as domain_is_scoped(), but unmasks bits in @masks. + * It can not return early as domain_is_scoped() does. + * + * A scoped access for a given access right bit is allowed iff, for all layer + * depths where the access bit is set, the client and server domain are the + * same. This function clears the access rights @access in @masks at all layer + * depths where the client and server domain are the same, so that, when they + * are all cleared, the access is allowed. + * + * @client: Client domain + * @server: Server domain + * @masks: Layer access masks to unmask + * @access: Access bits that control scoping + */ +static void unmask_scoped_access(const struct landlock_ruleset *const client, + const struct landlock_ruleset *const server, + struct layer_access_masks *const masks, + const access_mask_t access) +{ + int client_layer, server_layer; + const struct landlock_hierarchy *client_walker, *server_walker; + + /* This should not happen. */ + if (WARN_ON_ONCE(!client)) + return; + + /* Server has no Landlock domain; nothing to clear. */ + if (!server) + return; + + /* + * client_layer must be a signed integer with greater capacity + * than client->num_layers to ensure the following loop stops. + */ + BUILD_BUG_ON(sizeof(client_layer) > sizeof(client->num_layers)); + + client_layer = client->num_layers - 1; + client_walker = client->hierarchy; + server_layer = server->num_layers - 1; + server_walker = server->hierarchy; + + /* + * Clears the access bits at all layers where the client domain is the + * same as the server domain. We start the walk at min(client_layer, + * server_layer). The layer bits until there can not be cleared because + * either the client or the server domain is missing. + */ + for (; client_layer > server_layer; client_layer--) + client_walker = client_walker->parent; + + for (; server_layer > client_layer; server_layer--) + server_walker = server_walker->parent; + + for (; client_layer >= 0; client_layer--) { + if (masks->access[client_layer] & access && + client_walker == server_walker) + masks->access[client_layer] &= ~access; + + client_walker = client_walker->parent; + server_walker = server_walker->parent; + } +} + +static int hook_unix_find(const struct path *const path, struct sock *other, + int flags) +{ + const struct landlock_ruleset *dom_other; + const struct landlock_cred_security *subject; + struct layer_access_masks layer_masks; + struct landlock_request request = {}; + static const struct access_masks fs_resolve_unix = { + .fs = LANDLOCK_ACCESS_FS_RESOLVE_UNIX, + }; + + /* Lookup for the purpose of saving coredumps is OK. */ + if (unlikely(flags & SOCK_COREDUMP)) + return 0; + + subject = landlock_get_applicable_subject(current_cred(), + fs_resolve_unix, NULL); + + if (!subject) + return 0; + + /* + * Ignoring return value: that the domains apply was already checked in + * landlock_get_applicable_subject() above. + */ + landlock_init_layer_masks(subject->domain, fs_resolve_unix.fs, + &layer_masks, LANDLOCK_KEY_INODE); + + /* Checks the layers in which we are connecting within the same domain. */ + unix_state_lock(other); + if (unlikely(sock_flag(other, SOCK_DEAD) || !other->sk_socket || + !other->sk_socket->file)) { + unix_state_unlock(other); + /* + * We rely on the caller to catch the (non-reversible) SOCK_DEAD + * condition and retry the lookup. If we returned an error + * here, the lookup would not get retried. + */ + return 0; + } + dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain; + + /* Access to the same (or a lower) domain is always allowed. */ + unmask_scoped_access(subject->domain, dom_other, &layer_masks, + fs_resolve_unix.fs); + unix_state_unlock(other); + + /* Checks the connections to allow-listed paths. */ + if (is_access_to_paths_allowed(subject->domain, path, + fs_resolve_unix.fs, &layer_masks, + &request, NULL, 0, NULL, NULL, NULL)) + return 0; + + landlock_log_denial(subject, &request); + return -EACCES; +} + /* File hooks */ /** @@ -1834,6 +1961,7 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = { LSM_HOOK_INIT(path_unlink, hook_path_unlink), LSM_HOOK_INIT(path_rmdir, hook_path_rmdir), LSM_HOOK_INIT(path_truncate, hook_path_truncate), + LSM_HOOK_INIT(unix_find, hook_unix_find), LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security), LSM_HOOK_INIT(file_open, hook_file_open), diff --git a/security/landlock/limits.h b/security/landlock/limits.h index eb584f47288d..b454ad73b15e 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -19,7 +19,7 @@ #define LANDLOCK_MAX_NUM_LAYERS 16 #define LANDLOCK_MAX_NUM_RULES U32_MAX -#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_IOCTL_DEV +#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_RESOLVE_UNIX #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1) #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS) diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 5ebd606e84e6..accfd2e5a0cd 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -166,7 +166,7 @@ static const struct file_operations ruleset_fops = { * If the change involves a fix that requires userspace awareness, also update * the errata documentation in Documentation/userspace-api/landlock.rst . */ -const int landlock_abi_version = 8; +const int landlock_abi_version = 9; /** * sys_landlock_create_ruleset - Create a new ruleset diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c index 0fea236ef4bd..30d37234086c 100644 --- a/tools/testing/selftests/landlock/base_test.c +++ b/tools/testing/selftests/landlock/base_test.c @@ -76,7 +76,7 @@ TEST(abi_version) const struct landlock_ruleset_attr ruleset_attr = { .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, }; - ASSERT_EQ(8, landlock_create_ruleset(NULL, 0, + ASSERT_EQ(9, landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION)); ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index 968a91c927a4..b318627e7561 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -575,9 +575,10 @@ TEST_F_FORK(layout1, inval) LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE | \ LANDLOCK_ACCESS_FS_TRUNCATE | \ - LANDLOCK_ACCESS_FS_IOCTL_DEV) + LANDLOCK_ACCESS_FS_IOCTL_DEV | \ + LANDLOCK_ACCESS_FS_RESOLVE_UNIX) -#define ACCESS_LAST LANDLOCK_ACCESS_FS_IOCTL_DEV +#define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX #define ACCESS_ALL ( \ ACCESS_FILE | \ -- cgit v1.2.3 From 52e71eb95cc73e544f36041973bd3c4cd460a4fb Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 10 Mar 2026 08:49:25 +1030 Subject: btrfs: tree-checker: introduce checks for FREE_SPACE_INFO Introduce checks for FREE_SPACE_INFO item, which include: - Key alignment check The objectid is the logical bytenr of the chunk/bg, and offset is the length of the chunk/bg, thus they should all be aligned to the fs block size. - Item size check The FREE_SPACE_INFO should a fix size. - Flags check The flags member should have no other flags than BTRFS_FREE_SPACE_USING_BITMAPS. For future expansion, introduce a new macro BTRFS_FREE_SPACE_FLAGS_MASK for such checks. And since we're here, the BTRFS_FREE_SPACE_USING_BITMAPS should not use unsigned long long, as the flags is only 32 bits wide. So fix that to use unsigned long. - Extent count check That member shows how many free space bitmap/extent items there are inside the chunk/bg. We know the chunk size (from key->offset), thus there should be at most (key->offset >> sectorsize_bits) blocks inside the chunk. Use that value as the upper limit and if that counter is larger than that, there is a high chance it's a bitflip in high bits. Reviewed-by: Johannes Thumshirn Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/tree-checker.c | 50 +++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/btrfs_tree.h | 3 ++- 2 files changed, 52 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index b4e114efff45..c4826b0484e6 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -1945,6 +1945,53 @@ static int check_dev_extent_item(const struct extent_buffer *leaf, return 0; } +static int check_free_space_info(struct extent_buffer *leaf, struct btrfs_key *key, + int slot) +{ + struct btrfs_fs_info *fs_info = leaf->fs_info; + struct btrfs_free_space_info *fsi; + const u32 blocksize = fs_info->sectorsize; + u32 flags; + + if (unlikely(!IS_ALIGNED(key->objectid, blocksize))) { + generic_err(leaf, slot, + "free space info key objectid is not aligned to %u, has " BTRFS_KEY_FMT, + blocksize, BTRFS_KEY_FMT_VALUE(key)); + return -EUCLEAN; + } + if (unlikely(!IS_ALIGNED(key->offset, blocksize))) { + generic_err(leaf, slot, + "free space info key offset is not aligned to %u, has " BTRFS_KEY_FMT, + blocksize, BTRFS_KEY_FMT_VALUE(key)); + return -EUCLEAN; + } + if (unlikely(btrfs_item_size(leaf, slot) != + sizeof(struct btrfs_free_space_info))) { + generic_err(leaf, slot, + "invalid item size for free space info, has %u expect %zu", + btrfs_item_size(leaf, slot), + sizeof(struct btrfs_free_space_info)); + return -EUCLEAN; + } + fsi = btrfs_item_ptr(leaf, slot, struct btrfs_free_space_info); + flags = btrfs_free_space_flags(leaf, fsi); + if (unlikely(flags & ~BTRFS_FREE_SPACE_FLAGS_MASK)) { + generic_err(leaf, slot, + "unknown flags for free space info, has 0x%x valid mask 0x%lx", + flags, BTRFS_FREE_SPACE_FLAGS_MASK); + return -EUCLEAN; + } + if (unlikely(btrfs_free_space_extent_count(leaf, fsi) > + key->offset >> fs_info->sectorsize_bits)) { + generic_err(leaf, slot, + "suspicious extent count, has %u max valid %llu", + btrfs_free_space_extent_count(leaf, fsi), + key->offset >> fs_info->sectorsize_bits); + return -EUCLEAN; + } + return 0; +} + /* * Common point to switch the item-specific validation. */ @@ -2008,6 +2055,9 @@ static enum btrfs_tree_block_status check_leaf_item(struct extent_buffer *leaf, case BTRFS_RAID_STRIPE_KEY: ret = check_raid_stripe_extent(leaf, key, slot); break; + case BTRFS_FREE_SPACE_INFO_KEY: + ret = check_free_space_info(leaf, key, slot); + break; } if (unlikely(ret)) diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index f7843e6bb978..cc3b9f7dccaf 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -1245,7 +1245,8 @@ struct btrfs_free_space_info { __le32 flags; } __attribute__ ((__packed__)); -#define BTRFS_FREE_SPACE_USING_BITMAPS (1ULL << 0) +#define BTRFS_FREE_SPACE_USING_BITMAPS (1UL << 0) +#define BTRFS_FREE_SPACE_FLAGS_MASK (BTRFS_FREE_SPACE_USING_BITMAPS) #define BTRFS_QGROUP_LEVEL_SHIFT 48 static inline __u16 btrfs_qgroup_level(__u64 qgroupid) -- cgit v1.2.3 From 1f290c497cb644dd3b52e69b2eaa24a5ffb66094 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Mon, 9 Mar 2026 21:29:33 +0100 Subject: netfilter: nf_tables: Fix typo in enum description Fix the spelling of "options". Signed-off-by: Jelle van der Waa Signed-off-by: Florian Westphal --- include/uapi/linux/netfilter/nf_tables.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 45c71f7d21c2..dca9e72b0558 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -884,7 +884,7 @@ enum nft_exthdr_flags { * @NFT_EXTHDR_OP_TCPOPT: match against tcp options * @NFT_EXTHDR_OP_IPV4: match against ipv4 options * @NFT_EXTHDR_OP_SCTP: match against sctp chunks - * @NFT_EXTHDR_OP_DCCP: match against dccp otions + * @NFT_EXTHDR_OP_DCCP: match against dccp options */ enum nft_exthdr_op { NFT_EXTHDR_OP_IPV6, -- cgit v1.2.3 From 8e57338c3601d0cde806bd7e70c377109106c983 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 13 Mar 2026 13:12:30 +0100 Subject: netfilter: nf_tables: add netlink policy based cap on registers Should have no effect in practice; all of these use the nft_parse_register_load/store apis which is mandatory anyway due to the need to further validate the register load/store, e.g. that the size argument doesn't result in out-of-bounds load/store. OTOH this is a simple method to reject obviously wrong input at earlier stage. Signed-off-by: Florian Westphal --- include/uapi/linux/netfilter/nf_tables.h | 4 ++++ net/netfilter/nft_bitwise.c | 6 +++--- net/netfilter/nft_byteorder.c | 4 ++-- net/netfilter/nft_cmp.c | 2 +- net/netfilter/nft_ct.c | 4 ++-- net/netfilter/nft_exthdr.c | 4 ++-- net/netfilter/nft_fib.c | 2 +- net/netfilter/nft_hash.c | 4 ++-- net/netfilter/nft_immediate.c | 2 +- net/netfilter/nft_lookup.c | 4 ++-- net/netfilter/nft_meta.c | 4 ++-- net/netfilter/nft_numgen.c | 2 +- net/netfilter/nft_objref.c | 2 +- net/netfilter/nft_osf.c | 2 +- net/netfilter/nft_payload.c | 4 ++-- net/netfilter/nft_range.c | 2 +- net/netfilter/nft_rt.c | 2 +- net/netfilter/nft_socket.c | 2 +- net/netfilter/nft_tunnel.c | 2 +- net/netfilter/nft_xfrm.c | 2 +- 20 files changed, 32 insertions(+), 28 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index dca9e72b0558..0b708153469c 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -46,6 +46,10 @@ enum nft_registers { }; #define NFT_REG_MAX (__NFT_REG_MAX - 1) +#ifdef __KERNEL__ +#define NFT_REG32_MAX NFT_REG32_15 +#endif + #define NFT_REG_SIZE 16 #define NFT_REG32_SIZE 4 #define NFT_REG32_COUNT (NFT_REG32_15 - NFT_REG32_00 + 1) diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index a4ff781f334d..13808e9cd999 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -125,9 +125,9 @@ void nft_bitwise_eval(const struct nft_expr *expr, } static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = { - [NFTA_BITWISE_SREG] = { .type = NLA_U32 }, - [NFTA_BITWISE_SREG2] = { .type = NLA_U32 }, - [NFTA_BITWISE_DREG] = { .type = NLA_U32 }, + [NFTA_BITWISE_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_BITWISE_SREG2] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_BITWISE_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_BITWISE_LEN] = { .type = NLA_U32 }, [NFTA_BITWISE_MASK] = { .type = NLA_NESTED }, [NFTA_BITWISE_XOR] = { .type = NLA_NESTED }, diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c index 744878773dac..e00dddfa2fc0 100644 --- a/net/netfilter/nft_byteorder.c +++ b/net/netfilter/nft_byteorder.c @@ -87,8 +87,8 @@ void nft_byteorder_eval(const struct nft_expr *expr, } static const struct nla_policy nft_byteorder_policy[NFTA_BYTEORDER_MAX + 1] = { - [NFTA_BYTEORDER_SREG] = { .type = NLA_U32 }, - [NFTA_BYTEORDER_DREG] = { .type = NLA_U32 }, + [NFTA_BYTEORDER_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_BYTEORDER_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_BYTEORDER_OP] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_BYTEORDER_LEN] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_BYTEORDER_SIZE] = NLA_POLICY_MAX(NLA_BE32, 255), diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index b61dc9c3383e..e085c2a00b70 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c @@ -64,7 +64,7 @@ mismatch: } static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = { - [NFTA_CMP_SREG] = { .type = NLA_U32 }, + [NFTA_CMP_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_CMP_OP] = { .type = NLA_U32 }, [NFTA_CMP_DATA] = { .type = NLA_NESTED }, }; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 00dabd985883..afa7142c529a 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -336,10 +336,10 @@ static void nft_ct_set_eval(const struct nft_expr *expr, } static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { - [NFTA_CT_DREG] = { .type = NLA_U32 }, + [NFTA_CT_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_CT_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_CT_DIRECTION] = NLA_POLICY_MAX(NLA_U8, IP_CT_DIR_REPLY), - [NFTA_CT_SREG] = { .type = NLA_U32 }, + [NFTA_CT_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), }; #ifdef CONFIG_NF_CONNTRACK_ZONES diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index b997307d94f9..0407d6f708ae 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -486,13 +486,13 @@ err: #endif static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = { - [NFTA_EXTHDR_DREG] = { .type = NLA_U32 }, + [NFTA_EXTHDR_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_EXTHDR_TYPE] = { .type = NLA_U8 }, [NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 }, [NFTA_EXTHDR_LEN] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_EXTHDR_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_EXTHDR_F_PRESENT), [NFTA_EXTHDR_OP] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_EXTHDR_SREG] = { .type = NLA_U32 }, + [NFTA_EXTHDR_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), }; static int nft_exthdr_init(const struct nft_ctx *ctx, diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index f7dc0e54375f..327a5f33659c 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -19,7 +19,7 @@ NFTA_FIB_F_PRESENT) const struct nla_policy nft_fib_policy[NFTA_FIB_MAX + 1] = { - [NFTA_FIB_DREG] = { .type = NLA_U32 }, + [NFTA_FIB_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_FIB_RESULT] = { .type = NLA_U32 }, [NFTA_FIB_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFTA_FIB_F_ALL), diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 1cf41e0a0e0c..3bacc9b53789 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -58,8 +58,8 @@ static void nft_symhash_eval(const struct nft_expr *expr, } static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = { - [NFTA_HASH_SREG] = { .type = NLA_U32 }, - [NFTA_HASH_DREG] = { .type = NLA_U32 }, + [NFTA_HASH_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_HASH_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_HASH_LEN] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_HASH_MODULUS] = { .type = NLA_U32 }, [NFTA_HASH_SEED] = { .type = NLA_U32 }, diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index 37c29947b380..1b733c7b1b0e 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -25,7 +25,7 @@ void nft_immediate_eval(const struct nft_expr *expr, } static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = { - [NFTA_IMMEDIATE_DREG] = { .type = NLA_U32 }, + [NFTA_IMMEDIATE_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_IMMEDIATE_DATA] = { .type = NLA_NESTED }, }; diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index e4e619027542..9fafe5afc490 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -125,8 +125,8 @@ static const struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = { [NFTA_LOOKUP_SET] = { .type = NLA_STRING, .len = NFT_SET_MAXNAMELEN - 1 }, [NFTA_LOOKUP_SET_ID] = { .type = NLA_U32 }, - [NFTA_LOOKUP_SREG] = { .type = NLA_U32 }, - [NFTA_LOOKUP_DREG] = { .type = NLA_U32 }, + [NFTA_LOOKUP_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_LOOKUP_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_LOOKUP_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_LOOKUP_F_INV), }; diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index d0df6cf374d1..7478063339d4 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -460,9 +460,9 @@ void nft_meta_set_eval(const struct nft_expr *expr, EXPORT_SYMBOL_GPL(nft_meta_set_eval); const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { - [NFTA_META_DREG] = { .type = NLA_U32 }, + [NFTA_META_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_META_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_META_SREG] = { .type = NLA_U32 }, + [NFTA_META_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), }; EXPORT_SYMBOL_GPL(nft_meta_policy); diff --git a/net/netfilter/nft_numgen.c b/net/netfilter/nft_numgen.c index 4d69b3399195..b0c802370159 100644 --- a/net/netfilter/nft_numgen.c +++ b/net/netfilter/nft_numgen.c @@ -43,7 +43,7 @@ static void nft_ng_inc_eval(const struct nft_expr *expr, } static const struct nla_policy nft_ng_policy[NFTA_NG_MAX + 1] = { - [NFTA_NG_DREG] = { .type = NLA_U32 }, + [NFTA_NG_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_NG_MODULUS] = { .type = NLA_U32 }, [NFTA_NG_TYPE] = { .type = NLA_U32 }, [NFTA_NG_OFFSET] = { .type = NLA_U32 }, diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c index 633cce69568f..249ded517446 100644 --- a/net/netfilter/nft_objref.c +++ b/net/netfilter/nft_objref.c @@ -265,7 +265,7 @@ static const struct nla_policy nft_objref_policy[NFTA_OBJREF_MAX + 1] = { [NFTA_OBJREF_IMM_NAME] = { .type = NLA_STRING, .len = NFT_OBJ_MAXNAMELEN - 1 }, [NFTA_OBJREF_IMM_TYPE] = { .type = NLA_U32 }, - [NFTA_OBJREF_SET_SREG] = { .type = NLA_U32 }, + [NFTA_OBJREF_SET_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_OBJREF_SET_NAME] = { .type = NLA_STRING, .len = NFT_SET_MAXNAMELEN - 1 }, [NFTA_OBJREF_SET_ID] = { .type = NLA_U32 }, diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c index b2f44bc6bd3f..18003433476c 100644 --- a/net/netfilter/nft_osf.c +++ b/net/netfilter/nft_osf.c @@ -12,7 +12,7 @@ struct nft_osf { }; static const struct nla_policy nft_osf_policy[NFTA_OSF_MAX + 1] = { - [NFTA_OSF_DREG] = { .type = NLA_U32 }, + [NFTA_OSF_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_OSF_TTL] = { .type = NLA_U8 }, [NFTA_OSF_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_OSF_F_VERSION), }; diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 91b62083d942..3fa3c6c835be 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -209,8 +209,8 @@ err: } static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = { - [NFTA_PAYLOAD_SREG] = { .type = NLA_U32 }, - [NFTA_PAYLOAD_DREG] = { .type = NLA_U32 }, + [NFTA_PAYLOAD_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_PAYLOAD_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_PAYLOAD_BASE] = { .type = NLA_U32 }, [NFTA_PAYLOAD_OFFSET] = { .type = NLA_BE32 }, [NFTA_PAYLOAD_LEN] = NLA_POLICY_MAX(NLA_BE32, 255), diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c index cbb02644b836..f8a1641afccf 100644 --- a/net/netfilter/nft_range.c +++ b/net/netfilter/nft_range.c @@ -41,7 +41,7 @@ void nft_range_eval(const struct nft_expr *expr, } static const struct nla_policy nft_range_policy[NFTA_RANGE_MAX + 1] = { - [NFTA_RANGE_SREG] = { .type = NLA_U32 }, + [NFTA_RANGE_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_RANGE_OP] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_RANGE_FROM_DATA] = { .type = NLA_NESTED }, [NFTA_RANGE_TO_DATA] = { .type = NLA_NESTED }, diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c index ad527f3596c0..e23cd4759851 100644 --- a/net/netfilter/nft_rt.c +++ b/net/netfilter/nft_rt.c @@ -103,7 +103,7 @@ err: } static const struct nla_policy nft_rt_policy[NFTA_RT_MAX + 1] = { - [NFTA_RT_DREG] = { .type = NLA_U32 }, + [NFTA_RT_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_RT_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), }; diff --git a/net/netfilter/nft_socket.c b/net/netfilter/nft_socket.c index c55a1310226a..a146a45d7531 100644 --- a/net/netfilter/nft_socket.c +++ b/net/netfilter/nft_socket.c @@ -163,7 +163,7 @@ out_put_sk: static const struct nla_policy nft_socket_policy[NFTA_SOCKET_MAX + 1] = { [NFTA_SOCKET_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_SOCKET_DREG] = { .type = NLA_U32 }, + [NFTA_SOCKET_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_SOCKET_LEVEL] = NLA_POLICY_MAX(NLA_BE32, 255), }; diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index 65d06300f48a..0b987bc2132a 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -67,7 +67,7 @@ static void nft_tunnel_get_eval(const struct nft_expr *expr, static const struct nla_policy nft_tunnel_policy[NFTA_TUNNEL_MAX + 1] = { [NFTA_TUNNEL_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_TUNNEL_DREG] = { .type = NLA_U32 }, + [NFTA_TUNNEL_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_TUNNEL_MODE] = NLA_POLICY_MAX(NLA_BE32, NFT_TUNNEL_MODE_MAX), }; diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c index 6858cd2d16a4..65a75d88e5f0 100644 --- a/net/netfilter/nft_xfrm.c +++ b/net/netfilter/nft_xfrm.c @@ -19,7 +19,7 @@ static const struct nla_policy nft_xfrm_policy[NFTA_XFRM_MAX + 1] = { [NFTA_XFRM_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_XFRM_DIR] = NLA_POLICY_MAX(NLA_U8, XFRM_POLICY_OUT), [NFTA_XFRM_SPNUM] = NLA_POLICY_MAX(NLA_BE32, XFRM_MAX_DEPTH - 1), - [NFTA_XFRM_DREG] = { .type = NLA_U32 }, + [NFTA_XFRM_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), }; struct nft_xfrm { -- cgit v1.2.3 From 1bc45341a6ea4009ee9f2fbca9096b33a9ef71a2 Mon Sep 17 00:00:00 2001 From: Or Har-Toov Date: Tue, 7 Apr 2026 22:41:05 +0300 Subject: devlink: Add resource scope filtering to resource dump Allow filtering the resource dump to device-level or port-level resources using the 'scope' option. Example - dump only device-level resources: $ devlink resource show scope dev pci/0000:03:00.0: name max_local_SFs size 128 unit entry dpipe_tables none name max_external_SFs size 128 unit entry dpipe_tables none pci/0000:03:00.1: name max_local_SFs size 128 unit entry dpipe_tables none name max_external_SFs size 128 unit entry dpipe_tables none Example - dump only port-level resources: $ devlink resource show scope port pci/0000:03:00.0/196608: name max_SFs size 128 unit entry dpipe_tables none pci/0000:03:00.0/196609: name max_SFs size 128 unit entry dpipe_tables none pci/0000:03:00.1/196708: name max_SFs size 128 unit entry dpipe_tables none pci/0000:03:00.1/196709: name max_SFs size 128 unit entry dpipe_tables none Signed-off-by: Or Har-Toov Reviewed-by: Moshe Shemesh Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260407194107.148063-11-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/devlink.yaml | 24 +++++++++++++++++++++++- include/uapi/linux/devlink.h | 11 +++++++++++ net/devlink/netlink_gen.c | 5 +++-- net/devlink/resource.c | 19 ++++++++++++++++++- 4 files changed, 55 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/devlink.yaml b/Documentation/netlink/specs/devlink.yaml index 34aa81ba689e..247b147d689f 100644 --- a/Documentation/netlink/specs/devlink.yaml +++ b/Documentation/netlink/specs/devlink.yaml @@ -157,6 +157,14 @@ definitions: entries: - name: entry + - + type: enum + name: resource-scope + entries: + - + name: dev + - + name: port - type: enum name: reload-action @@ -873,6 +881,16 @@ attribute-sets: doc: Unique devlink instance index. checks: max: u32-max + - + name: resource-scope-mask + type: u32 + enum: resource-scope + enum-as-flags: true + doc: | + Bitmask selecting which resource classes to include in a + resource-dump response. Bit 0 (dev) selects device-level + resources; bit 1 (port) selects port-level resources. + When absent all classes are returned. - name: dl-dev-stats subset-of: devlink @@ -1775,7 +1793,11 @@ operations: - resource-list dump: request: - attributes: *dev-id-attrs + attributes: + - bus-name + - dev-name + - index + - resource-scope-mask reply: *resource-dump-reply - diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 7de2d8cc862f..0b165eac7619 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -645,6 +645,7 @@ enum devlink_attr { DEVLINK_ATTR_PARAM_RESET_DEFAULT, /* flag */ DEVLINK_ATTR_INDEX, /* uint */ + DEVLINK_ATTR_RESOURCE_SCOPE_MASK, /* u32 */ /* Add new attributes above here, update the spec in * Documentation/netlink/specs/devlink.yaml and re-generate @@ -704,6 +705,16 @@ enum devlink_resource_unit { DEVLINK_RESOURCE_UNIT_ENTRY, }; +enum devlink_resource_scope { + DEVLINK_RESOURCE_SCOPE_DEV_BIT, + DEVLINK_RESOURCE_SCOPE_PORT_BIT, +}; + +#define DEVLINK_RESOURCE_SCOPE_DEV \ + _BITUL(DEVLINK_RESOURCE_SCOPE_DEV_BIT) +#define DEVLINK_RESOURCE_SCOPE_PORT \ + _BITUL(DEVLINK_RESOURCE_SCOPE_PORT_BIT) + enum devlink_port_fn_attr_cap { DEVLINK_PORT_FN_ATTR_CAP_ROCE_BIT, DEVLINK_PORT_FN_ATTR_CAP_MIGRATABLE_BIT, diff --git a/net/devlink/netlink_gen.c b/net/devlink/netlink_gen.c index 9cc372d9ee41..81899786fd98 100644 --- a/net/devlink/netlink_gen.c +++ b/net/devlink/netlink_gen.c @@ -313,10 +313,11 @@ static const struct nla_policy devlink_resource_dump_do_nl_policy[DEVLINK_ATTR_I }; /* DEVLINK_CMD_RESOURCE_DUMP - dump */ -static const struct nla_policy devlink_resource_dump_dump_nl_policy[DEVLINK_ATTR_INDEX + 1] = { +static const struct nla_policy devlink_resource_dump_dump_nl_policy[DEVLINK_ATTR_RESOURCE_SCOPE_MASK + 1] = { [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, }, [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, }, [DEVLINK_ATTR_INDEX] = NLA_POLICY_FULL_RANGE(NLA_UINT, &devlink_attr_index_range), + [DEVLINK_ATTR_RESOURCE_SCOPE_MASK] = NLA_POLICY_MASK(NLA_U32, 0x3), }; /* DEVLINK_CMD_RELOAD - do */ @@ -974,7 +975,7 @@ const struct genl_split_ops devlink_nl_ops[75] = { .cmd = DEVLINK_CMD_RESOURCE_DUMP, .dumpit = devlink_nl_resource_dump_dumpit, .policy = devlink_resource_dump_dump_nl_policy, - .maxattr = DEVLINK_ATTR_INDEX, + .maxattr = DEVLINK_ATTR_RESOURCE_SCOPE_MASK, .flags = GENL_CMD_CAP_DUMP, }, { diff --git a/net/devlink/resource.c b/net/devlink/resource.c index bf5221fb3e64..3d2f42bc2fb5 100644 --- a/net/devlink/resource.c +++ b/net/devlink/resource.c @@ -398,11 +398,25 @@ devlink_nl_resource_dump_one(struct sk_buff *skb, struct devlink *devlink, struct netlink_callback *cb, int flags) { struct devlink_nl_dump_state *state = devlink_dump_state(cb); + const struct genl_info *info = genl_info_dump(cb); struct devlink_port *devlink_port; + struct nlattr *scope_attr = NULL; unsigned long port_idx; + u32 scope = 0; int err; - if (!state->port_ctx.index_valid) { + if (info->attrs && info->attrs[DEVLINK_ATTR_RESOURCE_SCOPE_MASK]) { + scope_attr = info->attrs[DEVLINK_ATTR_RESOURCE_SCOPE_MASK]; + scope = nla_get_u32(scope_attr); + if (!scope) { + NL_SET_ERR_MSG_ATTR(info->extack, scope_attr, + "empty resource scope selection"); + return -EINVAL; + } + } + + if (!state->port_ctx.index_valid && + (!scope || (scope & DEVLINK_RESOURCE_SCOPE_DEV))) { err = devlink_resource_dump_fill_one(skb, devlink, NULL, cb, flags, &state->idx); if (err) @@ -410,6 +424,8 @@ devlink_nl_resource_dump_one(struct sk_buff *skb, struct devlink *devlink, state->idx = 0; } + if (scope && !(scope & DEVLINK_RESOURCE_SCOPE_PORT)) + goto out; /* Check in case port was removed between dump callbacks. */ if (state->port_ctx.index_valid && !xa_load(&devlink->ports, state->port_ctx.index)) @@ -425,6 +441,7 @@ devlink_nl_resource_dump_one(struct sk_buff *skb, struct devlink *devlink, } state->idx = 0; } +out: state->port_ctx.index_valid = false; state->port_ctx.index = 0; return 0; -- cgit v1.2.3 From 229132c309d667bb05405fc8b539e7d90e0dfb3b Mon Sep 17 00:00:00 2001 From: Song Gao Date: Thu, 9 Apr 2026 18:56:37 +0800 Subject: LoongArch: KVM: Add DMSINTC device support Add device model for DMSINTC interrupt controller, implement basic create/destroy/set_attr interfaces, and register device model to kvm device table. Reviewed-by: Bibo Mao Signed-off-by: Song Gao Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/kvm_dmsintc.h | 24 +++++++ arch/loongarch/include/asm/kvm_host.h | 3 + arch/loongarch/include/uapi/asm/kvm.h | 4 ++ arch/loongarch/kvm/Makefile | 1 + arch/loongarch/kvm/intc/dmsintc.c | 108 +++++++++++++++++++++++++++++++ arch/loongarch/kvm/main.c | 6 ++ include/uapi/linux/kvm.h | 2 + 7 files changed, 148 insertions(+) create mode 100644 arch/loongarch/include/asm/kvm_dmsintc.h create mode 100644 arch/loongarch/kvm/intc/dmsintc.c (limited to 'include/uapi/linux') diff --git a/arch/loongarch/include/asm/kvm_dmsintc.h b/arch/loongarch/include/asm/kvm_dmsintc.h new file mode 100644 index 000000000000..3c5ec9805ed4 --- /dev/null +++ b/arch/loongarch/include/asm/kvm_dmsintc.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#ifndef __ASM_KVM_DMSINTC_H +#define __ASM_KVM_DMSINTC_H + +#include + +struct loongarch_dmsintc { + struct kvm *kvm; + uint64_t msg_addr_base; + uint64_t msg_addr_size; + uint32_t cpu_mask; +}; + +struct dmsintc_state { + atomic64_t vector_map[4]; +}; + +int kvm_loongarch_register_dmsintc_device(void); + +#endif diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index 19eb5e5c3984..130cedbb6b39 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -133,6 +134,7 @@ struct kvm_arch { s64 time_offset; struct kvm_context __percpu *vmcs; struct loongarch_ipi *ipi; + struct loongarch_dmsintc *dmsintc; struct loongarch_eiointc *eiointc; struct loongarch_pch_pic *pch_pic; }; @@ -247,6 +249,7 @@ struct kvm_vcpu_arch { struct kvm_mp_state mp_state; /* ipi state */ struct ipi_state ipi_state; + struct dmsintc_state dmsintc_state; /* cpucfg */ u32 cpucfg[KVM_MAX_CPUCFG_REGS]; diff --git a/arch/loongarch/include/uapi/asm/kvm.h b/arch/loongarch/include/uapi/asm/kvm.h index 419647aacdf3..cd0b5c11ca9c 100644 --- a/arch/loongarch/include/uapi/asm/kvm.h +++ b/arch/loongarch/include/uapi/asm/kvm.h @@ -155,4 +155,8 @@ struct kvm_iocsr_entry { #define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL 0x40000006 #define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT 0 +#define KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL 0x40000007 +#define KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_BASE 0x0 +#define KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_SIZE 0x1 + #endif /* __UAPI_ASM_LOONGARCH_KVM_H */ diff --git a/arch/loongarch/kvm/Makefile b/arch/loongarch/kvm/Makefile index cb41d9265662..ae469edec99c 100644 --- a/arch/loongarch/kvm/Makefile +++ b/arch/loongarch/kvm/Makefile @@ -17,6 +17,7 @@ kvm-y += tlb.o kvm-y += vcpu.o kvm-y += vm.o kvm-y += intc/ipi.o +kvm-y += intc/dmsintc.o kvm-y += intc/eiointc.o kvm-y += intc/pch_pic.o kvm-y += irqfd.o diff --git a/arch/loongarch/kvm/intc/dmsintc.c b/arch/loongarch/kvm/intc/dmsintc.c new file mode 100644 index 000000000000..8f0b91eb95dc --- /dev/null +++ b/arch/loongarch/kvm/intc/dmsintc.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include +#include +#include + +static int kvm_dmsintc_ctrl_access(struct kvm_device *dev, + struct kvm_device_attr *attr, bool is_write) +{ + int addr = attr->attr; + unsigned long cpu_bit, val; + void __user *data = (void __user *)attr->addr; + struct loongarch_dmsintc *s = dev->kvm->arch.dmsintc; + + switch (addr) { + case KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_BASE: + if (is_write) { + if (copy_from_user(&val, data, sizeof(s->msg_addr_base))) + return -EFAULT; + if (s->msg_addr_base) + return -EFAULT; /* Duplicate setting are not allowed. */ + if ((val & (BIT(AVEC_CPU_SHIFT) - 1)) != 0) + return -EINVAL; + s->msg_addr_base = val; + cpu_bit = find_first_bit((unsigned long *)&(s->msg_addr_base), 64) - AVEC_CPU_SHIFT; + cpu_bit = min(cpu_bit, AVEC_CPU_BIT); + s->cpu_mask = GENMASK(cpu_bit - 1, 0) & AVEC_CPU_MASK; + } + break; + case KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_SIZE: + if (is_write) { + if (copy_from_user(&val, data, sizeof(s->msg_addr_size))) + return -EFAULT; + if (s->msg_addr_size) + return -EFAULT; /*Duplicate setting are not allowed. */ + s->msg_addr_size = val; + } + break; + default: + kvm_err("%s: unknown dmsintc register, addr = %d\n", __func__, addr); + return -ENXIO; + } + + return 0; +} + +static int kvm_dmsintc_set_attr(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + switch (attr->group) { + case KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL: + return kvm_dmsintc_ctrl_access(dev, attr, true); + default: + kvm_err("%s: unknown group (%d)\n", __func__, attr->group); + return -EINVAL; + } +} + +static int kvm_dmsintc_create(struct kvm_device *dev, u32 type) +{ + struct kvm *kvm; + struct loongarch_dmsintc *s; + + if (!dev) { + kvm_err("%s: kvm_device ptr is invalid!\n", __func__); + return -EINVAL; + } + + kvm = dev->kvm; + if (kvm->arch.dmsintc) { + kvm_err("%s: LoongArch DMSINTC has already been created!\n", __func__); + return -EINVAL; + } + + s = kzalloc(sizeof(struct loongarch_dmsintc), GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->kvm = kvm; + kvm->arch.dmsintc = s; + + return 0; +} + +static void kvm_dmsintc_destroy(struct kvm_device *dev) +{ + + if (!dev || !dev->kvm || !dev->kvm->arch.dmsintc) + return; + + kfree(dev->kvm->arch.dmsintc); + kfree(dev); +} + +static struct kvm_device_ops kvm_dmsintc_dev_ops = { + .name = "kvm-loongarch-dmsintc", + .create = kvm_dmsintc_create, + .destroy = kvm_dmsintc_destroy, + .set_attr = kvm_dmsintc_set_attr, +}; + +int kvm_loongarch_register_dmsintc_device(void) +{ + return kvm_register_device_ops(&kvm_dmsintc_dev_ops, KVM_DEV_TYPE_LOONGARCH_DMSINTC); +} diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c index 304c83863e71..76ebff2faedd 100644 --- a/arch/loongarch/kvm/main.c +++ b/arch/loongarch/kvm/main.c @@ -416,6 +416,12 @@ static int kvm_loongarch_env_init(void) /* Register LoongArch PCH-PIC interrupt controller interface. */ ret = kvm_loongarch_register_pch_pic_device(); + if (ret) + return ret; + + /* Register LoongArch DMSINTC interrupt contrroller interface */ + if (cpu_has_msgint) + ret = kvm_loongarch_register_dmsintc_device(); return ret; } diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 80364d4dbebb..9e7887230bdd 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1224,6 +1224,8 @@ enum kvm_device_type { #define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_PCHPIC, #define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC + KVM_DEV_TYPE_LOONGARCH_DMSINTC, +#define KVM_DEV_TYPE_LOONGARCH_DMSINTC KVM_DEV_TYPE_LOONGARCH_DMSINTC KVM_DEV_TYPE_MAX, -- cgit v1.2.3 From 23b3b6f0b584b70a427d5bb826d320151890d7da Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 9 Apr 2026 21:30:13 +0800 Subject: ublk: widen ublk_shmem_buf_reg.len to __u64 for 4GB buffer support The __u32 len field cannot represent a 4GB buffer (0x100000000 overflows to 0). Change it to __u64 so buffers up to 4GB can be registered. Add a reserved field for alignment and validate it is zero. The kernel enforces a default max of 4GB (UBLK_SHMEM_BUF_SIZE_MAX) which may be increased in future. Signed-off-by: Ming Lei Link: https://patch.msgid.link/20260409133020.3780098-2-tom.leiming@gmail.com Signed-off-by: Jens Axboe --- drivers/block/ublk_drv.c | 9 ++++++++- include/uapi/linux/ublk_cmd.h | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 1af42850f5b1..3f8bb80b1e8f 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -63,6 +63,9 @@ #define UBLK_CMD_REG_BUF _IOC_NR(UBLK_U_CMD_REG_BUF) #define UBLK_CMD_UNREG_BUF _IOC_NR(UBLK_U_CMD_UNREG_BUF) +/* Default max shmem buffer size: 4GB (may be increased in future) */ +#define UBLK_SHMEM_BUF_SIZE_MAX (1ULL << 32) + #define UBLK_IO_REGISTER_IO_BUF _IOC_NR(UBLK_U_IO_REGISTER_IO_BUF) #define UBLK_IO_UNREGISTER_IO_BUF _IOC_NR(UBLK_U_IO_UNREGISTER_IO_BUF) @@ -5351,11 +5354,15 @@ static int ublk_ctrl_reg_buf(struct ublk_device *ub, if (buf_reg.flags & ~UBLK_SHMEM_BUF_READ_ONLY) return -EINVAL; + if (buf_reg.reserved) + return -EINVAL; + addr = buf_reg.addr; size = buf_reg.len; nr_pages = size >> PAGE_SHIFT; - if (!size || !PAGE_ALIGNED(size) || !PAGE_ALIGNED(addr)) + if (!size || size > UBLK_SHMEM_BUF_SIZE_MAX || + !PAGE_ALIGNED(size) || !PAGE_ALIGNED(addr)) return -EINVAL; disk = ublk_get_disk(ub); diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h index a7078b798791..6991370a72ce 100644 --- a/include/uapi/linux/ublk_cmd.h +++ b/include/uapi/linux/ublk_cmd.h @@ -89,8 +89,9 @@ /* Parameter buffer for UBLK_U_CMD_REG_BUF, pointed to by ctrl_cmd.addr */ struct ublk_shmem_buf_reg { __u64 addr; /* userspace virtual address of shared memory */ - __u32 len; /* buffer size in bytes (page-aligned, max 4GB) */ + __u64 len; /* buffer size in bytes, page-aligned, default max 4GB */ __u32 flags; + __u32 reserved; }; /* Pin pages without FOLL_WRITE; usable with write-sealed memfd */ -- cgit v1.2.3 From 7789c6bb76acf21539c2c74b0cc869bb57de99e6 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 3 Apr 2026 01:10:18 +0200 Subject: net: Add queue-create operation Add a ynl netdev family operation called queue-create that creates a new queue on a netdevice: name: queue-create attribute-set: queue flags: [admin-perm] do: request: attributes: - ifindex - type - lease reply: &queue-create-op attributes: - id This is a generic operation such that it can be extended for various use cases in future. Right now it is mandatory to specify ifindex, the queue type which is enforced to rx and a lease. The newly created queue id is returned to the caller. A queue from a virtual device can have a lease which refers to another queue from a physical device. This is useful for memory providers and AF_XDP operations which take an ifindex and queue id to allow applications to bind against virtual devices in containers. The lease couples both queues together and allows to proxy the operations from a virtual device in a container to the physical device. In future, the nested lease attribute can be lifted and made optional for other use-cases such as dynamic queue creation for physical netdevs. The lack of lease and the specification of the physical device as an ifindex will imply that we need a real queue to be allocated. Similarly, the queue type enforcement to rx can then be lifted as well to support tx. An early implementation had only driver-specific integration [0], but in order for other virtual devices to reuse, it makes sense to have this as a generic API in core net. For leasing queues, the virtual netdev must have real_num_rx_queues less than num_rx_queues at the time of calling queue-create. The queue-type must be rx as only rx queues are supported for leasing for now. We also enforce that the queue-create ifindex must point to a virtual device, and that the nested lease attribute's ifindex must point to a physical device. The nested lease attribute set contains a netns-id attribute which is optional and can specify a netns-id relative to the caller's netns. It requires cap_net_admin and if the netns-id attribute is not specified, the lease ifindex will be retrieved from the current netns. Also, it is modeled as an s32 type similarly as done elsewhere in the stack. Signed-off-by: Daniel Borkmann Co-developed-by: David Wei Signed-off-by: David Wei Acked-by: Stanislav Fomichev Reviewed-by: Nikolay Aleksandrov Link: https://bpfconf.ebpf.io/bpfconf2025/bpfconf2025_material/lsfmmbpf_2025_netkit_borkmann.pdf [0] Link: https://patch.msgid.link/20260402231031.447597-2-daniel@iogearbox.net Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/netdev.yaml | 46 +++++++++++++++++++++++++++++++++ include/uapi/linux/netdev.h | 11 ++++++++ net/core/netdev-genl-gen.c | 20 ++++++++++++++ net/core/netdev-genl-gen.h | 2 ++ net/core/netdev-genl.c | 5 ++++ tools/include/uapi/linux/netdev.h | 11 ++++++++ 6 files changed, 95 insertions(+) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml index 596c306ce52b..b93beb247a11 100644 --- a/Documentation/netlink/specs/netdev.yaml +++ b/Documentation/netlink/specs/netdev.yaml @@ -339,6 +339,15 @@ attribute-sets: doc: XSK information for this queue, if any. type: nest nested-attributes: xsk-info + - + name: lease + doc: | + A queue from a virtual device can have a lease which refers to + another queue from a physical device. This is useful for memory + providers and AF_XDP operations which take an ifindex and queue id + to allow applications to bind against virtual devices in containers. + type: nest + nested-attributes: lease - name: qstats doc: | @@ -537,6 +546,26 @@ attribute-sets: name: id - name: type + - + name: lease + attributes: + - + name: ifindex + doc: The netdev ifindex to lease the queue from. + type: u32 + checks: + min: 1 + - + name: queue + doc: The netdev queue to lease from. + type: nest + nested-attributes: queue-id + - + name: netns-id + doc: The network namespace id of the netdev. + type: s32 + checks: + min: 0 - name: dmabuf attributes: @@ -686,6 +715,7 @@ operations: - dmabuf - io-uring - xsk + - lease dump: request: attributes: @@ -797,6 +827,22 @@ operations: reply: attributes: - id + - + name: queue-create + doc: | + Create a new queue for the given netdevice. Whether this operation + is supported depends on the device and the driver. + attribute-set: queue + flags: [admin-perm] + do: + request: + attributes: + - ifindex + - type + - lease + reply: &queue-create-op + attributes: + - id kernel-family: headers: ["net/netdev_netlink.h"] diff --git a/include/uapi/linux/netdev.h b/include/uapi/linux/netdev.h index e0b579a1df4f..7df1056a35fd 100644 --- a/include/uapi/linux/netdev.h +++ b/include/uapi/linux/netdev.h @@ -160,6 +160,7 @@ enum { NETDEV_A_QUEUE_DMABUF, NETDEV_A_QUEUE_IO_URING, NETDEV_A_QUEUE_XSK, + NETDEV_A_QUEUE_LEASE, __NETDEV_A_QUEUE_MAX, NETDEV_A_QUEUE_MAX = (__NETDEV_A_QUEUE_MAX - 1) @@ -202,6 +203,15 @@ enum { NETDEV_A_QSTATS_MAX = (__NETDEV_A_QSTATS_MAX - 1) }; +enum { + NETDEV_A_LEASE_IFINDEX = 1, + NETDEV_A_LEASE_QUEUE, + NETDEV_A_LEASE_NETNS_ID, + + __NETDEV_A_LEASE_MAX, + NETDEV_A_LEASE_MAX = (__NETDEV_A_LEASE_MAX - 1) +}; + enum { NETDEV_A_DMABUF_IFINDEX = 1, NETDEV_A_DMABUF_QUEUES, @@ -228,6 +238,7 @@ enum { NETDEV_CMD_BIND_RX, NETDEV_CMD_NAPI_SET, NETDEV_CMD_BIND_TX, + NETDEV_CMD_QUEUE_CREATE, __NETDEV_CMD_MAX, NETDEV_CMD_MAX = (__NETDEV_CMD_MAX - 1) diff --git a/net/core/netdev-genl-gen.c b/net/core/netdev-genl-gen.c index ba673e81716f..81aecb5d3bc5 100644 --- a/net/core/netdev-genl-gen.c +++ b/net/core/netdev-genl-gen.c @@ -28,6 +28,12 @@ static const struct netlink_range_validation netdev_a_napi_defer_hard_irqs_range }; /* Common nested types */ +const struct nla_policy netdev_lease_nl_policy[NETDEV_A_LEASE_NETNS_ID + 1] = { + [NETDEV_A_LEASE_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1), + [NETDEV_A_LEASE_QUEUE] = NLA_POLICY_NESTED(netdev_queue_id_nl_policy), + [NETDEV_A_LEASE_NETNS_ID] = NLA_POLICY_MIN(NLA_S32, 0), +}; + const struct nla_policy netdev_page_pool_info_nl_policy[NETDEV_A_PAGE_POOL_IFINDEX + 1] = { [NETDEV_A_PAGE_POOL_ID] = NLA_POLICY_FULL_RANGE(NLA_UINT, &netdev_a_page_pool_id_range), [NETDEV_A_PAGE_POOL_IFINDEX] = NLA_POLICY_FULL_RANGE(NLA_U32, &netdev_a_page_pool_ifindex_range), @@ -107,6 +113,13 @@ static const struct nla_policy netdev_bind_tx_nl_policy[NETDEV_A_DMABUF_FD + 1] [NETDEV_A_DMABUF_FD] = { .type = NLA_U32, }, }; +/* NETDEV_CMD_QUEUE_CREATE - do */ +static const struct nla_policy netdev_queue_create_nl_policy[NETDEV_A_QUEUE_LEASE + 1] = { + [NETDEV_A_QUEUE_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1), + [NETDEV_A_QUEUE_TYPE] = NLA_POLICY_MAX(NLA_U32, 1), + [NETDEV_A_QUEUE_LEASE] = NLA_POLICY_NESTED(netdev_lease_nl_policy), +}; + /* Ops table for netdev */ static const struct genl_split_ops netdev_nl_ops[] = { { @@ -205,6 +218,13 @@ static const struct genl_split_ops netdev_nl_ops[] = { .maxattr = NETDEV_A_DMABUF_FD, .flags = GENL_CMD_CAP_DO, }, + { + .cmd = NETDEV_CMD_QUEUE_CREATE, + .doit = netdev_nl_queue_create_doit, + .policy = netdev_queue_create_nl_policy, + .maxattr = NETDEV_A_QUEUE_LEASE, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, }; static const struct genl_multicast_group netdev_nl_mcgrps[] = { diff --git a/net/core/netdev-genl-gen.h b/net/core/netdev-genl-gen.h index cffc08517a41..d71b435d72c1 100644 --- a/net/core/netdev-genl-gen.h +++ b/net/core/netdev-genl-gen.h @@ -14,6 +14,7 @@ #include /* Common nested types */ +extern const struct nla_policy netdev_lease_nl_policy[NETDEV_A_LEASE_NETNS_ID + 1]; extern const struct nla_policy netdev_page_pool_info_nl_policy[NETDEV_A_PAGE_POOL_IFINDEX + 1]; extern const struct nla_policy netdev_queue_id_nl_policy[NETDEV_A_QUEUE_TYPE + 1]; @@ -36,6 +37,7 @@ int netdev_nl_qstats_get_dumpit(struct sk_buff *skb, int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info); int netdev_nl_napi_set_doit(struct sk_buff *skb, struct genl_info *info); int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info); +int netdev_nl_queue_create_doit(struct sk_buff *skb, struct genl_info *info); enum { NETDEV_NLGRP_MGMT, diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index 470fabbeacd9..aae75431858d 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -1120,6 +1120,11 @@ err_genlmsg_free: return err; } +int netdev_nl_queue_create_doit(struct sk_buff *skb, struct genl_info *info) +{ + return -EOPNOTSUPP; +} + void netdev_nl_sock_priv_init(struct netdev_nl_sock *priv) { INIT_LIST_HEAD(&priv->bindings); diff --git a/tools/include/uapi/linux/netdev.h b/tools/include/uapi/linux/netdev.h index e0b579a1df4f..7df1056a35fd 100644 --- a/tools/include/uapi/linux/netdev.h +++ b/tools/include/uapi/linux/netdev.h @@ -160,6 +160,7 @@ enum { NETDEV_A_QUEUE_DMABUF, NETDEV_A_QUEUE_IO_URING, NETDEV_A_QUEUE_XSK, + NETDEV_A_QUEUE_LEASE, __NETDEV_A_QUEUE_MAX, NETDEV_A_QUEUE_MAX = (__NETDEV_A_QUEUE_MAX - 1) @@ -202,6 +203,15 @@ enum { NETDEV_A_QSTATS_MAX = (__NETDEV_A_QSTATS_MAX - 1) }; +enum { + NETDEV_A_LEASE_IFINDEX = 1, + NETDEV_A_LEASE_QUEUE, + NETDEV_A_LEASE_NETNS_ID, + + __NETDEV_A_LEASE_MAX, + NETDEV_A_LEASE_MAX = (__NETDEV_A_LEASE_MAX - 1) +}; + enum { NETDEV_A_DMABUF_IFINDEX = 1, NETDEV_A_DMABUF_QUEUES, @@ -228,6 +238,7 @@ enum { NETDEV_CMD_BIND_RX, NETDEV_CMD_NAPI_SET, NETDEV_CMD_BIND_TX, + NETDEV_CMD_QUEUE_CREATE, __NETDEV_CMD_MAX, NETDEV_CMD_MAX = (__NETDEV_CMD_MAX - 1) -- cgit v1.2.3 From 48103896053828a8b4d25839a39aa8514071914a Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 3 Apr 2026 01:10:27 +0200 Subject: netkit: Add single device mode for netkit Add a single device mode for netkit instead of netkit pairs. The primary target for the paired devices is to connect network namespaces, of course, and support has been implemented in projects like Cilium [0]. For the rxq leasing the plan is to support two main scenarios related to single device mode: * For the use-case of io_uring zero-copy, the control plane can either set up a netkit pair where the peer device can perform rxq leasing which is then tied to the lifetime of the peer device, or the control plane can use a regular netkit pair to connect the hostns to a Pod/container and dynamically add/remove rxq leasing through a single device without having to interrupt the device pair. In the case of io_uring, the memory pool is used as skb non-linear pages, and thus the skb will go its way through the regular stack into netkit. Things like the netkit policy when no BPF is attached or skb scrubbing etc apply as-is in case the paired devices are used, or if the backend memory is tied to the single device and traffic goes through a paired device. * For the use-case of AF_XDP, the control plane needs to use netkit in the single device mode. The single device mode currently enforces only a pass policy when no BPF is attached, and does not yet support BPF link attachments for AF_XDP. skbs sent to that device get dropped at the moment. Given AF_XDP operates at a lower layer of the stack tying this to the netkit pair did not make sense. In future, the plan is to allow BPF at the XDP layer which can: i) process traffic coming from the AF_XDP application (e.g. QEMU with AF_XDP backend) to filter egress traffic or to push selected egress traffic up to the single netkit device to the local stack (e.g. DHCP requests), and ii) vice-versa skbs sent to the single netkit into the AF_XDP application (e.g. DHCP replies). Also, the control-plane can dynamically manage rxq leasing for the single netkit device without having to interrupt (e.g. down/up cycle) the main netkit pair for the Pod which has traffic going in and out. Signed-off-by: Daniel Borkmann Co-developed-by: David Wei Signed-off-by: David Wei Reviewed-by: Jordan Rife Reviewed-by: Nikolay Aleksandrov Link: https://docs.cilium.io/en/stable/operations/performance/tuning/#netkit-device-mode [0] Link: https://patch.msgid.link/20260402231031.447597-11-daniel@iogearbox.net Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/rt-link.yaml | 11 +++ drivers/net/netkit.c | 131 +++++++++++++++++++------------ include/uapi/linux/if_link.h | 6 ++ 3 files changed, 99 insertions(+), 49 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index df4b56beb818..fcb5aaf0926f 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -825,6 +825,13 @@ definitions: entries: - name: none - name: default + - + name: netkit-pairing + type: enum + enum-name: netkit-pairing + entries: + - name: pair + - name: single - name: ovpn-mode enum-name: ovpn-mode @@ -2299,6 +2306,10 @@ attribute-sets: - name: tailroom type: u16 + - + name: pairing + type: u32 + enum: netkit-pairing - name: linkinfo-ovpn-attrs name-prefix: ifla-ovpn- diff --git a/drivers/net/netkit.c b/drivers/net/netkit.c index 5c0e01396e06..96c098a6db0d 100644 --- a/drivers/net/netkit.c +++ b/drivers/net/netkit.c @@ -26,6 +26,7 @@ struct netkit { __cacheline_group_begin(netkit_slowpath); enum netkit_mode mode; + enum netkit_pairing pair; bool primary; u32 headroom; __cacheline_group_end(netkit_slowpath); @@ -135,6 +136,10 @@ static int netkit_open(struct net_device *dev) struct netkit *nk = netkit_priv(dev); struct net_device *peer = rtnl_dereference(nk->peer); + if (nk->pair == NETKIT_DEVICE_SINGLE) { + netif_carrier_on(dev); + return 0; + } if (!peer) return -ENOTCONN; if (peer->flags & IFF_UP) { @@ -194,16 +199,17 @@ static void netkit_set_headroom(struct net_device *dev, int headroom) rcu_read_lock(); peer = rcu_dereference(nk->peer); - if (unlikely(!peer)) - goto out; - - nk2 = netkit_priv(peer); - nk->headroom = headroom; - headroom = max(nk->headroom, nk2->headroom); + if (!peer) { + nk->headroom = headroom; + dev->needed_headroom = headroom; + } else { + nk2 = netkit_priv(peer); + nk->headroom = headroom; + headroom = max(nk->headroom, nk2->headroom); - peer->needed_headroom = headroom; - dev->needed_headroom = headroom; -out: + peer->needed_headroom = headroom; + dev->needed_headroom = headroom; + } rcu_read_unlock(); } @@ -335,15 +341,17 @@ static int netkit_new_link(struct net_device *dev, enum netkit_scrub scrub_prim = NETKIT_SCRUB_DEFAULT; enum netkit_scrub scrub_peer = NETKIT_SCRUB_DEFAULT; struct nlattr *peer_tb[IFLA_MAX + 1], **tbp, *attr; + enum netkit_pairing pair = NETKIT_DEVICE_PAIR; enum netkit_action policy_prim = NETKIT_PASS; enum netkit_action policy_peer = NETKIT_PASS; + bool seen_peer = false, seen_scrub = false; struct nlattr **data = params->data; enum netkit_mode mode = NETKIT_L3; unsigned char ifname_assign_type; struct nlattr **tb = params->tb; u16 headroom = 0, tailroom = 0; struct ifinfomsg *ifmp = NULL; - struct net_device *peer; + struct net_device *peer = NULL; char ifname[IFNAMSIZ]; struct netkit *nk; int err; @@ -380,6 +388,13 @@ static int netkit_new_link(struct net_device *dev, headroom = nla_get_u16(data[IFLA_NETKIT_HEADROOM]); if (data[IFLA_NETKIT_TAILROOM]) tailroom = nla_get_u16(data[IFLA_NETKIT_TAILROOM]); + if (data[IFLA_NETKIT_PAIRING]) + pair = nla_get_u32(data[IFLA_NETKIT_PAIRING]); + + seen_scrub = data[IFLA_NETKIT_SCRUB]; + seen_peer = data[IFLA_NETKIT_PEER_INFO] || + data[IFLA_NETKIT_PEER_SCRUB] || + data[IFLA_NETKIT_PEER_POLICY]; } if (ifmp && tbp[IFLA_IFNAME]) { @@ -392,45 +407,47 @@ static int netkit_new_link(struct net_device *dev, if (mode != NETKIT_L2 && (tb[IFLA_ADDRESS] || tbp[IFLA_ADDRESS])) return -EOPNOTSUPP; + if (pair == NETKIT_DEVICE_SINGLE && + (tb != tbp || seen_peer || seen_scrub || + policy_prim != NETKIT_PASS)) + return -EOPNOTSUPP; - peer = rtnl_create_link(peer_net, ifname, ifname_assign_type, - &netkit_link_ops, tbp, extack); - if (IS_ERR(peer)) - return PTR_ERR(peer); - - netif_inherit_tso_max(peer, dev); - if (headroom) { - peer->needed_headroom = headroom; - dev->needed_headroom = headroom; - } - if (tailroom) { - peer->needed_tailroom = tailroom; - dev->needed_tailroom = tailroom; - } - - if (mode == NETKIT_L2 && !(ifmp && tbp[IFLA_ADDRESS])) - eth_hw_addr_random(peer); - if (ifmp && dev->ifindex) - peer->ifindex = ifmp->ifi_index; - - nk = netkit_priv(peer); - nk->primary = false; - nk->policy = policy_peer; - nk->scrub = scrub_peer; - nk->mode = mode; - nk->headroom = headroom; - bpf_mprog_bundle_init(&nk->bundle); + if (pair == NETKIT_DEVICE_PAIR) { + peer = rtnl_create_link(peer_net, ifname, ifname_assign_type, + &netkit_link_ops, tbp, extack); + if (IS_ERR(peer)) + return PTR_ERR(peer); + + netif_inherit_tso_max(peer, dev); + if (headroom) + peer->needed_headroom = headroom; + if (tailroom) + peer->needed_tailroom = tailroom; + if (mode == NETKIT_L2 && !(ifmp && tbp[IFLA_ADDRESS])) + eth_hw_addr_random(peer); + if (ifmp && dev->ifindex) + peer->ifindex = ifmp->ifi_index; - err = register_netdevice(peer); - if (err < 0) - goto err_register_peer; - netif_carrier_off(peer); - if (mode == NETKIT_L2) - dev_change_flags(peer, peer->flags & ~IFF_NOARP, NULL); + nk = netkit_priv(peer); + nk->primary = false; + nk->policy = policy_peer; + nk->scrub = scrub_peer; + nk->mode = mode; + nk->pair = pair; + nk->headroom = headroom; + bpf_mprog_bundle_init(&nk->bundle); + + err = register_netdevice(peer); + if (err < 0) + goto err_register_peer; + netif_carrier_off(peer); + if (mode == NETKIT_L2) + dev_change_flags(peer, peer->flags & ~IFF_NOARP, NULL); - err = rtnl_configure_link(peer, NULL, 0, NULL); - if (err < 0) - goto err_configure_peer; + err = rtnl_configure_link(peer, NULL, 0, NULL); + if (err < 0) + goto err_configure_peer; + } if (mode == NETKIT_L2 && !tb[IFLA_ADDRESS]) eth_hw_addr_random(dev); @@ -438,12 +455,17 @@ static int netkit_new_link(struct net_device *dev, nla_strscpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ); else strscpy(dev->name, "nk%d", IFNAMSIZ); + if (headroom) + dev->needed_headroom = headroom; + if (tailroom) + dev->needed_tailroom = tailroom; nk = netkit_priv(dev); nk->primary = true; nk->policy = policy_prim; nk->scrub = scrub_prim; nk->mode = mode; + nk->pair = pair; nk->headroom = headroom; bpf_mprog_bundle_init(&nk->bundle); @@ -455,10 +477,12 @@ static int netkit_new_link(struct net_device *dev, dev_change_flags(dev, dev->flags & ~IFF_NOARP, NULL); rcu_assign_pointer(netkit_priv(dev)->peer, peer); - rcu_assign_pointer(netkit_priv(peer)->peer, dev); + if (peer) + rcu_assign_pointer(netkit_priv(peer)->peer, dev); return 0; err_configure_peer: - unregister_netdevice(peer); + if (peer) + unregister_netdevice(peer); return err; err_register_peer: free_netdev(peer); @@ -518,6 +542,8 @@ static struct net_device *netkit_dev_fetch(struct net *net, u32 ifindex, u32 whi nk = netkit_priv(dev); if (!nk->primary) return ERR_PTR(-EACCES); + if (nk->pair == NETKIT_DEVICE_SINGLE) + return ERR_PTR(-EOPNOTSUPP); if (which == BPF_NETKIT_PEER) { dev = rcu_dereference_rtnl(nk->peer); if (!dev) @@ -879,6 +905,7 @@ static int netkit_change_link(struct net_device *dev, struct nlattr *tb[], { IFLA_NETKIT_PEER_INFO, "peer info" }, { IFLA_NETKIT_HEADROOM, "headroom" }, { IFLA_NETKIT_TAILROOM, "tailroom" }, + { IFLA_NETKIT_PAIRING, "pairing" }, }; if (!nk->primary) { @@ -898,9 +925,11 @@ static int netkit_change_link(struct net_device *dev, struct nlattr *tb[], } if (data[IFLA_NETKIT_POLICY]) { + err = -EOPNOTSUPP; attr = data[IFLA_NETKIT_POLICY]; policy = nla_get_u32(attr); - err = netkit_check_policy(policy, attr, extack); + if (nk->pair == NETKIT_DEVICE_PAIR) + err = netkit_check_policy(policy, attr, extack); if (err) return err; WRITE_ONCE(nk->policy, policy); @@ -931,6 +960,7 @@ static size_t netkit_get_size(const struct net_device *dev) nla_total_size(sizeof(u8)) + /* IFLA_NETKIT_PRIMARY */ nla_total_size(sizeof(u16)) + /* IFLA_NETKIT_HEADROOM */ nla_total_size(sizeof(u16)) + /* IFLA_NETKIT_TAILROOM */ + nla_total_size(sizeof(u32)) + /* IFLA_NETKIT_PAIRING */ 0; } @@ -951,6 +981,8 @@ static int netkit_fill_info(struct sk_buff *skb, const struct net_device *dev) return -EMSGSIZE; if (nla_put_u16(skb, IFLA_NETKIT_TAILROOM, dev->needed_tailroom)) return -EMSGSIZE; + if (nla_put_u32(skb, IFLA_NETKIT_PAIRING, nk->pair)) + return -EMSGSIZE; if (peer) { nk = netkit_priv(peer); @@ -972,6 +1004,7 @@ static const struct nla_policy netkit_policy[IFLA_NETKIT_MAX + 1] = { [IFLA_NETKIT_TAILROOM] = { .type = NLA_U16 }, [IFLA_NETKIT_SCRUB] = NLA_POLICY_MAX(NLA_U32, NETKIT_SCRUB_DEFAULT), [IFLA_NETKIT_PEER_SCRUB] = NLA_POLICY_MAX(NLA_U32, NETKIT_SCRUB_DEFAULT), + [IFLA_NETKIT_PAIRING] = NLA_POLICY_MAX(NLA_U32, NETKIT_DEVICE_SINGLE), [IFLA_NETKIT_PRIMARY] = { .type = NLA_REJECT, .reject_message = "Primary attribute is read-only" }, }; diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 83a96c56b8ca..280bb1780512 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -1296,6 +1296,11 @@ enum netkit_mode { NETKIT_L3, }; +enum netkit_pairing { + NETKIT_DEVICE_PAIR, + NETKIT_DEVICE_SINGLE, +}; + /* NETKIT_SCRUB_NONE leaves clearing skb->{mark,priority} up to * the BPF program if attached. This also means the latter can * consume the two fields if they were populated earlier. @@ -1320,6 +1325,7 @@ enum { IFLA_NETKIT_PEER_SCRUB, IFLA_NETKIT_HEADROOM, IFLA_NETKIT_TAILROOM, + IFLA_NETKIT_PAIRING, __IFLA_NETKIT_MAX, }; #define IFLA_NETKIT_MAX (__IFLA_NETKIT_MAX - 1) -- cgit v1.2.3 From 54fc83a1728535831df0f251e155d05574918115 Mon Sep 17 00:00:00 2001 From: Andy Roulin Date: Sun, 5 Apr 2026 13:52:22 -0700 Subject: net: bridge: add stp_mode attribute for STP mode selection The bridge-stp usermode helper is currently restricted to the initial network namespace, preventing userspace STP daemons (e.g. mstpd) from operating on bridges in other network namespaces. Since commit ff62198553e4 ("bridge: Only call /sbin/bridge-stp for the initial network namespace"), bridges in non-init namespaces silently fall back to kernel STP with no way to use userspace STP. Add a new bridge attribute IFLA_BR_STP_MODE that allows explicit per-bridge control over STP mode selection: BR_STP_MODE_AUTO (default) - Existing behavior: invoke the /sbin/bridge-stp helper in init_net only; fall back to kernel STP if it fails or in non-init namespaces. BR_STP_MODE_USER - Directly enable userspace STP (BR_USER_STP) without invoking the helper. Works in any network namespace. Userspace is responsible for ensuring an STP daemon manages the bridge. BR_STP_MODE_KERNEL - Directly enable kernel STP (BR_KERNEL_STP) without invoking the helper. The mode can only be changed while STP is disabled, or set to the same value (-EBUSY otherwise). IFLA_BR_STP_MODE is processed before IFLA_BR_STP_STATE in br_changelink(), so both can be set atomically in a single netlink message. The mode can also be changed in the same message that disables STP. The stp_mode struct field is u8 since all possible values fit, while NLA_U32 is used for the netlink attribute since it occupies the same space in the netlink message as NLA_U8. A new stp_helper_active boolean tracks whether the /sbin/bridge-stp helper was invoked during br_stp_start(), so that br_stp_stop() only calls the helper for stop when it was called for start. This avoids calling the helper asymmetrically when stp_mode changes between start and stop. Suggested-by: Ido Schimmel Assisted-by: Claude:claude-opus-4-6 Reviewed-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Andy Roulin Link: https://patch.msgid.link/20260405205224.3163000-2-aroulin@nvidia.com Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/rt-link.yaml | 12 ++++++++++ include/uapi/linux/if_link.h | 39 ++++++++++++++++++++++++++++++++ net/bridge/br_device.c | 1 + net/bridge/br_netlink.c | 24 +++++++++++++++++++- net/bridge/br_private.h | 2 ++ net/bridge/br_stp_if.c | 19 ++++++++++------ 6 files changed, 89 insertions(+), 8 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index fcb5aaf0926f..f23aa5f229c5 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -840,6 +840,14 @@ definitions: entries: - p2p - mp + - + name: br-stp-mode + type: enum + enum-name: br-stp-mode + entries: + - auto + - user + - kernel attribute-sets: - @@ -1550,6 +1558,10 @@ attribute-sets: - name: fdb-max-learned type: u32 + - + name: stp-mode + type: u32 + enum: br-stp-mode - name: linkinfo-brport-attrs name-prefix: ifla-brport- diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 280bb1780512..79ce4bc24cba 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -744,6 +744,11 @@ enum in6_addr_gen_mode { * @IFLA_BR_FDB_MAX_LEARNED * Set the number of max dynamically learned FDB entries for the current * bridge. + * + * @IFLA_BR_STP_MODE + * Set the STP mode for the bridge, which controls how the bridge + * selects between userspace and kernel STP. The valid values are + * documented below in the ``BR_STP_MODE_*`` constants. */ enum { IFLA_BR_UNSPEC, @@ -796,11 +801,45 @@ enum { IFLA_BR_MCAST_QUERIER_STATE, IFLA_BR_FDB_N_LEARNED, IFLA_BR_FDB_MAX_LEARNED, + IFLA_BR_STP_MODE, __IFLA_BR_MAX, }; #define IFLA_BR_MAX (__IFLA_BR_MAX - 1) +/** + * DOC: Bridge STP mode values + * + * @BR_STP_MODE_AUTO + * Default. The kernel invokes the ``/sbin/bridge-stp`` helper to hand + * the bridge to a userspace STP daemon (e.g. mstpd). Only attempted in + * the initial network namespace; in other namespaces this falls back to + * kernel STP. + * + * @BR_STP_MODE_USER + * Directly enable userspace STP (``BR_USER_STP``) without invoking the + * ``/sbin/bridge-stp`` helper. Works in any network namespace. + * Userspace is responsible for ensuring an STP daemon manages the + * bridge. + * + * @BR_STP_MODE_KERNEL + * Directly enable kernel STP (``BR_KERNEL_STP``) without invoking the + * helper. + * + * The mode controls how the bridge selects between userspace and kernel + * STP when STP is enabled via ``IFLA_BR_STP_STATE``. It can only be + * changed while STP is disabled (``IFLA_BR_STP_STATE`` == 0), returns + * ``-EBUSY`` otherwise. The default value is ``BR_STP_MODE_AUTO``. + */ +enum br_stp_mode { + BR_STP_MODE_AUTO, + BR_STP_MODE_USER, + BR_STP_MODE_KERNEL, + __BR_STP_MODE_MAX +}; + +#define BR_STP_MODE_MAX (__BR_STP_MODE_MAX - 1) + struct ifla_bridge_id { __u8 prio[2]; __u8 addr[6]; /* ETH_ALEN */ diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index f7502e62dd35..a35ceae0a6f2 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -518,6 +518,7 @@ void br_dev_setup(struct net_device *dev) ether_addr_copy(br->group_addr, eth_stp_addr); br->stp_enabled = BR_NO_STP; + br->stp_mode = BR_STP_MODE_AUTO; br->group_fwd_mask = BR_GROUPFWD_DEFAULT; br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 0264730938f4..6fd5386a1d64 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -1270,6 +1270,9 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { NLA_POLICY_EXACT_LEN(sizeof(struct br_boolopt_multi)), [IFLA_BR_FDB_N_LEARNED] = { .type = NLA_REJECT }, [IFLA_BR_FDB_MAX_LEARNED] = { .type = NLA_U32 }, + [IFLA_BR_STP_MODE] = NLA_POLICY_RANGE(NLA_U32, + BR_STP_MODE_AUTO, + BR_STP_MODE_MAX), }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -1306,6 +1309,23 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], return err; } + if (data[IFLA_BR_STP_MODE]) { + u32 mode = nla_get_u32(data[IFLA_BR_STP_MODE]); + + if (mode != br->stp_mode) { + bool stp_off = br->stp_enabled == BR_NO_STP || + (data[IFLA_BR_STP_STATE] && + !nla_get_u32(data[IFLA_BR_STP_STATE])); + + if (!stp_off) { + NL_SET_ERR_MSG_MOD(extack, + "Can't change STP mode while STP is enabled"); + return -EBUSY; + } + } + br->stp_mode = mode; + } + if (data[IFLA_BR_STP_STATE]) { u32 stp_enabled = nla_get_u32(data[IFLA_BR_STP_STATE]); @@ -1634,6 +1654,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u8)) + /* IFLA_BR_NF_CALL_ARPTABLES */ #endif nla_total_size(sizeof(struct br_boolopt_multi)) + /* IFLA_BR_MULTI_BOOLOPT */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_STP_MODE */ 0; } @@ -1686,7 +1707,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put(skb, IFLA_BR_MULTI_BOOLOPT, sizeof(bm), &bm) || nla_put_u32(skb, IFLA_BR_FDB_N_LEARNED, atomic_read(&br->fdb_n_learned)) || - nla_put_u32(skb, IFLA_BR_FDB_MAX_LEARNED, br->fdb_max_learned)) + nla_put_u32(skb, IFLA_BR_FDB_MAX_LEARNED, br->fdb_max_learned) || + nla_put_u32(skb, IFLA_BR_STP_MODE, br->stp_mode)) return -EMSGSIZE; #ifdef CONFIG_BRIDGE_VLAN_FILTERING diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 6dbca845e625..361a9b84451e 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -523,6 +523,8 @@ struct net_bridge { unsigned char topology_change; unsigned char topology_change_detected; u16 root_port; + u8 stp_mode; + bool stp_helper_active; unsigned long max_age; unsigned long hello_time; unsigned long forward_delay; diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index cc4b27ff1b08..28c1d3f7e22f 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -149,7 +149,9 @@ static void br_stp_start(struct net_bridge *br) { int err = -ENOENT; - if (net_eq(dev_net(br->dev), &init_net)) + /* AUTO mode: try bridge-stp helper in init_net only */ + if (br->stp_mode == BR_STP_MODE_AUTO && + net_eq(dev_net(br->dev), &init_net)) err = br_stp_call_user(br, "start"); if (err && err != -ENOENT) @@ -162,8 +164,9 @@ static void br_stp_start(struct net_bridge *br) else if (br->bridge_forward_delay > BR_MAX_FORWARD_DELAY) __br_set_forward_delay(br, BR_MAX_FORWARD_DELAY); - if (!err) { + if (br->stp_mode == BR_STP_MODE_USER || !err) { br->stp_enabled = BR_USER_STP; + br->stp_helper_active = !err; br_debug(br, "userspace STP started\n"); } else { br->stp_enabled = BR_KERNEL_STP; @@ -180,12 +183,14 @@ static void br_stp_start(struct net_bridge *br) static void br_stp_stop(struct net_bridge *br) { - int err; - if (br->stp_enabled == BR_USER_STP) { - err = br_stp_call_user(br, "stop"); - if (err) - br_err(br, "failed to stop userspace STP (%d)\n", err); + if (br->stp_helper_active) { + int err = br_stp_call_user(br, "stop"); + + if (err) + br_err(br, "failed to stop userspace STP (%d)\n", err); + br->stp_helper_active = false; + } /* To start timers on any ports left in blocking */ spin_lock_bh(&br->lock); -- cgit v1.2.3 From e7a62edd34b1b4bc5f979988efc2f81c075733fd Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Fri, 10 Apr 2026 19:10:20 +0200 Subject: net: phy: qcom: at803x: Use the correct bit to disable extended next page As noted in the blamed commit, the AR8035 and other PHYs from this family advertise the Extended Next Page support by default, which may be understood by some partners as this PHY being multi-gig capable. The fix is to disable XNP advertising, which is done by setting bit 12 of the Auto-Negotiation Advertisement Register (MII_ADVERTISE). The blamed commit incorrectly uses MDIO_AN_CTRL1_XNP, which is bit 13 as per 802.3 : 45.2.7.1 AN control register (Register 7.0) BIT 12 in MII_ADVERTISE is wrapped by ADVERTISE_RESV, used by some drivers such as the aquantia one. 802.3 Clause 28 defines bit 12 as Extended Next Page ability, at least in recent versions of the standard. Let's add a define for it and use it in the at803x driver. Fixes: 3c51fa5d2afe ("net: phy: ar803x: disable extended next page bit") Signed-off-by: Maxime Chevallier Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260410171021.1277138-1-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/qcom/at803x.c | 2 +- include/uapi/linux/mii.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c index 2995b08bac96..63726cf98cd4 100644 --- a/drivers/net/phy/qcom/at803x.c +++ b/drivers/net/phy/qcom/at803x.c @@ -524,7 +524,7 @@ static int at803x_config_init(struct phy_device *phydev) * behaviour but we still need to accommodate it. XNP is only needed * for 10Gbps support, so disable XNP. */ - return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); + return phy_modify(phydev, MII_ADVERTISE, ADVERTISE_XNP, 0); } static void at803x_link_change_notify(struct phy_device *phydev) diff --git a/include/uapi/linux/mii.h b/include/uapi/linux/mii.h index 39f7c44baf53..61d6edad4b94 100644 --- a/include/uapi/linux/mii.h +++ b/include/uapi/linux/mii.h @@ -82,7 +82,8 @@ #define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ #define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */ #define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */ -#define ADVERTISE_RESV 0x1000 /* Unused... */ +#define ADVERTISE_XNP 0x1000 /* Extended Next Page */ +#define ADVERTISE_RESV ADVERTISE_XNP /* Used to be reserved */ #define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ #define ADVERTISE_LPACK 0x4000 /* Ack link partners response */ #define ADVERTISE_NPAGE 0x8000 /* Next page bit */ -- cgit v1.2.3 From 404cd6bffe17e25e0f94ed2775ffdd6cd10ac3fd Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Mon, 6 Apr 2026 09:24:59 +0000 Subject: mshv_vtl: Fix vmemmap_shift exceeding MAX_FOLIO_ORDER When registering VTL0 memory via MSHV_ADD_VTL0_MEMORY, the kernel computes pgmap->vmemmap_shift as the number of trailing zeros in the OR of start_pfn and last_pfn, intending to use the largest compound page order both endpoints are aligned to. However, this value is not clamped to MAX_FOLIO_ORDER, so a sufficiently aligned range (e.g. physical range [0x800000000000, 0x800080000000), corresponding to start_pfn=0x800000000 with 35 trailing zeros) can produce a shift larger than what memremap_pages() accepts, triggering a WARN and returning -EINVAL: WARNING: ... memremap_pages+0x512/0x650 requested folio size unsupported The MAX_FOLIO_ORDER check was added by commit 646b67d57589 ("mm/memremap: reject unreasonable folio/compound page sizes in memremap_pages()"). Fix this by clamping vmemmap_shift to MAX_FOLIO_ORDER so we always request the largest order the kernel supports, in those cases, rather than an out-of-range value. Also fix the error path to propagate the actual error code from devm_memremap_pages() instead of hard-coding -EFAULT, which was masking the real -EINVAL return. Fixes: 7bfe3b8ea6e3 ("Drivers: hv: Introduce mshv_vtl driver") Cc: stable@vger.kernel.org Signed-off-by: Naman Jain Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/mshv_vtl_main.c | 12 +++++++++--- include/uapi/linux/mshv.h | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index 5856975f32e1..c19400701467 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -386,7 +386,6 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg) if (copy_from_user(&vtl0_mem, arg, sizeof(vtl0_mem))) return -EFAULT; - /* vtl0_mem.last_pfn is excluded in the pagemap range for VTL0 as per design */ if (vtl0_mem.last_pfn <= vtl0_mem.start_pfn) { dev_err(vtl->module_dev, "range start pfn (%llx) > end pfn (%llx)\n", vtl0_mem.start_pfn, vtl0_mem.last_pfn); @@ -397,6 +396,10 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg) if (!pgmap) return -ENOMEM; + /* + * vtl0_mem.last_pfn is excluded in the pagemap range for VTL0 as per design. + * last_pfn is not reserved or wasted, and reflects 'start_pfn + size' of pagemap range. + */ pgmap->ranges[0].start = PFN_PHYS(vtl0_mem.start_pfn); pgmap->ranges[0].end = PFN_PHYS(vtl0_mem.last_pfn) - 1; pgmap->nr_range = 1; @@ -405,8 +408,11 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg) /* * Determine the highest page order that can be used for the given memory range. * This works best when the range is aligned; i.e. both the start and the length. + * Clamp to MAX_FOLIO_ORDER to avoid a WARN in memremap_pages() when the range + * alignment exceeds the maximum supported folio order for this kernel config. */ - pgmap->vmemmap_shift = count_trailing_zeros(vtl0_mem.start_pfn | vtl0_mem.last_pfn); + pgmap->vmemmap_shift = min(count_trailing_zeros(vtl0_mem.start_pfn | vtl0_mem.last_pfn), + MAX_FOLIO_ORDER); dev_dbg(vtl->module_dev, "Add VTL0 memory: start: 0x%llx, end_pfn: 0x%llx, page order: %lu\n", vtl0_mem.start_pfn, vtl0_mem.last_pfn, pgmap->vmemmap_shift); @@ -415,7 +421,7 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg) if (IS_ERR(addr)) { dev_err(vtl->module_dev, "devm_memremap_pages error: %ld\n", PTR_ERR(addr)); kfree(pgmap); - return -EFAULT; + return PTR_ERR(addr); } /* Don't free pgmap, since it has to stick around until the memory diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h index e0645a34b55b..32ff92b6342b 100644 --- a/include/uapi/linux/mshv.h +++ b/include/uapi/linux/mshv.h @@ -357,7 +357,7 @@ struct mshv_vtl_sint_post_msg { struct mshv_vtl_ram_disposition { __u64 start_pfn; - __u64 last_pfn; + __u64 last_pfn; /* last_pfn is excluded from the range [start_pfn, last_pfn) */ }; struct mshv_vtl_set_poll_file { -- cgit v1.2.3 From 6d5431555de032f5ad9e08a7fb372f37bf493903 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 16 Apr 2026 11:28:28 -0700 Subject: caif: remove CAIF NETWORK LAYER Remove CAIF (Communication CPU to Application CPU Interface), the ST-Ericsson modem protocol. The subsystem has been orphaned since 2013. The last meaningful changes from the maintainers were in March 2013: a8c7687bf216 ("caif_virtio: Check that vringh_config is not null") b2273be8d2df ("caif_virtio: Use vringh_notify_enable correctly") 0d2e1a2926b1 ("caif_virtio: Introduce caif over virtio") Not-so-coincidentally, according to "the Internet" ST-Ericsson officially shut down its modem joint venture in Aug 2013. If anyone is using this code please yell! In the 13 years since, the code has accumulated 200 non-merge commits, of which 71 were cross-tree API changes, 21 carried Fixes: tags, and the remaining ~110 were cleanups, doc conversions, treewide refactors, and one partial removal (caif_hsi, ca75bcf0a83b). We are still getting fixes to this code, in the last 10 days there were 3 reports on security@ about CAIF that I have been CCed on. UAPI constants (AF_CAIF, ARPHRD_CAIF, N_CAIF, VIRTIO_ID_CAIF) and the SELinux classmap entry are intentionally kept for ABI stability. Acked-by: Michael S. Tsirkin Acked-by: Greg Kroah-Hartman Reviewed-by: Linus Walleij Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260416182829.1440262-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- Documentation/networking/caif/caif.rst | 138 --- Documentation/networking/caif/index.rst | 12 - Documentation/networking/caif/linux_caif.rst | 195 ---- Documentation/networking/index.rst | 1 - .../translations/zh_CN/networking/index.rst | 1 - MAINTAINERS | 9 - arch/arm/configs/u8500_defconfig | 1 - drivers/net/Kconfig | 2 - drivers/net/Makefile | 1 - drivers/net/caif/Kconfig | 33 - drivers/net/caif/Makefile | 8 - drivers/net/caif/caif_serial.c | 443 -------- drivers/net/caif/caif_virtio.c | 791 -------------- include/linux/virtio_caif.h | 24 - include/net/caif/caif_dev.h | 128 --- include/net/caif/caif_device.h | 55 - include/net/caif/caif_layer.h | 277 ----- include/net/caif/cfcnfg.h | 90 -- include/net/caif/cfctrl.h | 130 --- include/net/caif/cffrml.h | 21 - include/net/caif/cfmuxl.h | 20 - include/net/caif/cfpkt.h | 232 ---- include/net/caif/cfserl.h | 13 - include/net/caif/cfsrvl.h | 61 -- include/uapi/linux/caif/caif_socket.h | 195 ---- include/uapi/linux/caif/if_caif.h | 35 - net/Kconfig | 1 - net/Makefile | 1 - net/caif/Kconfig | 54 - net/caif/Makefile | 16 - net/caif/caif_dev.c | 586 ---------- net/caif/caif_socket.c | 1114 -------------------- net/caif/caif_usb.c | 216 ---- net/caif/cfcnfg.c | 612 ----------- net/caif/cfctrl.c | 631 ----------- net/caif/cfdbgl.c | 55 - net/caif/cfdgml.c | 113 -- net/caif/cffrml.c | 204 ---- net/caif/cfmuxl.c | 267 ----- net/caif/cfpkt_skbuff.c | 373 ------- net/caif/cfrfml.c | 299 ------ net/caif/cfserl.c | 192 ---- net/caif/cfsrvl.c | 224 ---- net/caif/cfutill.c | 104 -- net/caif/cfveil.c | 101 -- net/caif/cfvidl.c | 65 -- net/caif/chnl_net.c | 531 ---------- 47 files changed, 8675 deletions(-) delete mode 100644 Documentation/networking/caif/caif.rst delete mode 100644 Documentation/networking/caif/index.rst delete mode 100644 Documentation/networking/caif/linux_caif.rst delete mode 100644 drivers/net/caif/Kconfig delete mode 100644 drivers/net/caif/Makefile delete mode 100644 drivers/net/caif/caif_serial.c delete mode 100644 drivers/net/caif/caif_virtio.c delete mode 100644 include/linux/virtio_caif.h delete mode 100644 include/net/caif/caif_dev.h delete mode 100644 include/net/caif/caif_device.h delete mode 100644 include/net/caif/caif_layer.h delete mode 100644 include/net/caif/cfcnfg.h delete mode 100644 include/net/caif/cfctrl.h delete mode 100644 include/net/caif/cffrml.h delete mode 100644 include/net/caif/cfmuxl.h delete mode 100644 include/net/caif/cfpkt.h delete mode 100644 include/net/caif/cfserl.h delete mode 100644 include/net/caif/cfsrvl.h delete mode 100644 include/uapi/linux/caif/caif_socket.h delete mode 100644 include/uapi/linux/caif/if_caif.h delete mode 100644 net/caif/Kconfig delete mode 100644 net/caif/Makefile delete mode 100644 net/caif/caif_dev.c delete mode 100644 net/caif/caif_socket.c delete mode 100644 net/caif/caif_usb.c delete mode 100644 net/caif/cfcnfg.c delete mode 100644 net/caif/cfctrl.c delete mode 100644 net/caif/cfdbgl.c delete mode 100644 net/caif/cfdgml.c delete mode 100644 net/caif/cffrml.c delete mode 100644 net/caif/cfmuxl.c delete mode 100644 net/caif/cfpkt_skbuff.c delete mode 100644 net/caif/cfrfml.c delete mode 100644 net/caif/cfserl.c delete mode 100644 net/caif/cfsrvl.c delete mode 100644 net/caif/cfutill.c delete mode 100644 net/caif/cfveil.c delete mode 100644 net/caif/cfvidl.c delete mode 100644 net/caif/chnl_net.c (limited to 'include/uapi/linux') diff --git a/Documentation/networking/caif/caif.rst b/Documentation/networking/caif/caif.rst deleted file mode 100644 index d922d419c513..000000000000 --- a/Documentation/networking/caif/caif.rst +++ /dev/null @@ -1,138 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 -.. include:: - - -================ -Using Linux CAIF -================ - - -:Copyright: |copy| ST-Ericsson AB 2010 - -:Author: Sjur Brendeland/ sjur.brandeland@stericsson.com - -Start -===== - -If you have compiled CAIF for modules do:: - - $modprobe crc_ccitt - $modprobe caif - $modprobe caif_socket - $modprobe chnl_net - - -Preparing the setup with a STE modem -==================================== - -If you are working on integration of CAIF you should make sure -that the kernel is built with module support. - -There are some things that need to be tweaked to get the host TTY correctly -set up to talk to the modem. -Since the CAIF stack is running in the kernel and we want to use the existing -TTY, we are installing our physical serial driver as a line discipline above -the TTY device. - -To achieve this we need to install the N_CAIF ldisc from user space. -The benefit is that we can hook up to any TTY. - -The use of Start-of-frame-extension (STX) must also be set as -module parameter "ser_use_stx". - -Normally Frame Checksum is always used on UART, but this is also provided as a -module parameter "ser_use_fcs". - -:: - - $ modprobe caif_serial ser_ttyname=/dev/ttyS0 ser_use_stx=yes - $ ifconfig caif_ttyS0 up - -PLEASE NOTE: - There is a limitation in Android shell. - It only accepts one argument to insmod/modprobe! - -Trouble shooting -================ - -There are debugfs parameters provided for serial communication. -/sys/kernel/debug/caif_serial// - -* ser_state: Prints the bit-mask status where - - - 0x02 means SENDING, this is a transient state. - - 0x10 means FLOW_OFF_SENT, i.e. the previous frame has not been sent - and is blocking further send operation. Flow OFF has been propagated - to all CAIF Channels using this TTY. - -* tty_status: Prints the bit-mask tty status information - - - 0x01 - tty->warned is on. - - 0x04 - tty->packed is on. - - 0x08 - tty->flow.tco_stopped is on. - - 0x10 - tty->hw_stopped is on. - - 0x20 - tty->flow.stopped is on. - -* last_tx_msg: Binary blob Prints the last transmitted frame. - - This can be printed with:: - - $od --format=x1 /sys/kernel/debug/caif_serial//last_rx_msg. - - The first two tx messages sent look like this. Note: The initial - byte 02 is start of frame extension (STX) used for re-syncing - upon errors. - - - Enumeration:: - - 0000000 02 05 00 00 03 01 d2 02 - | | | | | | - STX(1) | | | | - Length(2)| | | - Control Channel(1) - Command:Enumeration(1) - Link-ID(1) - Checksum(2) - - - Channel Setup:: - - 0000000 02 07 00 00 00 21 a1 00 48 df - | | | | | | | | - STX(1) | | | | | | - Length(2)| | | | | - Control Channel(1) - Command:Channel Setup(1) - Channel Type(1) - Priority and Link-ID(1) - Endpoint(1) - Checksum(2) - -* last_rx_msg: Prints the last transmitted frame. - - The RX messages for LinkSetup look almost identical but they have the - bit 0x20 set in the command bit, and Channel Setup has added one byte - before Checksum containing Channel ID. - - NOTE: - Several CAIF Messages might be concatenated. The maximum debug - buffer size is 128 bytes. - -Error Scenarios -=============== - -- last_tx_msg contains channel setup message and last_rx_msg is empty -> - The host seems to be able to send over the UART, at least the CAIF ldisc get - notified that sending is completed. - -- last_tx_msg contains enumeration message and last_rx_msg is empty -> - The host is not able to send the message from UART, the tty has not been - able to complete the transmit operation. - -- if /sys/kernel/debug/caif_serial//tty_status is non-zero there - might be problems transmitting over UART. - - E.g. host and modem wiring is not correct you will typically see - tty_status = 0x10 (hw_stopped) and ser_state = 0x10 (FLOW_OFF_SENT). - - You will probably see the enumeration message in last_tx_message - and empty last_rx_message. diff --git a/Documentation/networking/caif/index.rst b/Documentation/networking/caif/index.rst deleted file mode 100644 index ec29b6f4bdb4..000000000000 --- a/Documentation/networking/caif/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -CAIF -==== - -Contents: - -.. toctree:: - :maxdepth: 2 - - linux_caif - caif diff --git a/Documentation/networking/caif/linux_caif.rst b/Documentation/networking/caif/linux_caif.rst deleted file mode 100644 index a0480862ab8c..000000000000 --- a/Documentation/networking/caif/linux_caif.rst +++ /dev/null @@ -1,195 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 -.. include:: - -========== -Linux CAIF -========== - -Copyright |copy| ST-Ericsson AB 2010 - -:Author: Sjur Brendeland/ sjur.brandeland@stericsson.com -:License terms: GNU General Public License (GPL) version 2 - - -Introduction -============ - -CAIF is a MUX protocol used by ST-Ericsson cellular modems for -communication between Modem and host. The host processes can open virtual AT -channels, initiate GPRS Data connections, Video channels and Utility Channels. -The Utility Channels are general purpose pipes between modem and host. - -ST-Ericsson modems support a number of transports between modem -and host. Currently, UART and Loopback are available for Linux. - - -Architecture -============ - -The implementation of CAIF is divided into: - -* CAIF Socket Layer and GPRS IP Interface. -* CAIF Core Protocol Implementation -* CAIF Link Layer, implemented as NET devices. - -:: - - RTNL - ! - ! +------+ +------+ - ! +------+! +------+! - ! ! IP !! !Socket!! - +-------> !interf!+ ! API !+ <- CAIF Client APIs - ! +------+ +------! - ! ! ! - ! +-----------+ - ! ! - ! +------+ <- CAIF Core Protocol - ! ! CAIF ! - ! ! Core ! - ! +------+ - ! +----------!---------+ - ! ! ! ! - ! +------+ +-----+ +------+ - +--> ! HSI ! ! TTY ! ! USB ! <- Link Layer (Net Devices) - +------+ +-----+ +------+ - - - -Implementation -============== - - -CAIF Core Protocol Layer ------------------------- - -CAIF Core layer implements the CAIF protocol as defined by ST-Ericsson. -It implements the CAIF protocol stack in a layered approach, where -each layer described in the specification is implemented as a separate layer. -The architecture is inspired by the design patterns "Protocol Layer" and -"Protocol Packet". - -CAIF structure -^^^^^^^^^^^^^^ - -The Core CAIF implementation contains: - - - Simple implementation of CAIF. - - Layered architecture (a la Streams), each layer in the CAIF - specification is implemented in a separate c-file. - - Clients must call configuration function to add PHY layer. - - Clients must implement CAIF layer to consume/produce - CAIF payload with receive and transmit functions. - - Clients must call configuration function to add and connect the - Client layer. - - When receiving / transmitting CAIF Packets (cfpkt), ownership is passed - to the called function (except for framing layers' receive function) - -Layered Architecture -==================== - -The CAIF protocol can be divided into two parts: Support functions and Protocol -Implementation. The support functions include: - - - CFPKT CAIF Packet. Implementation of CAIF Protocol Packet. The - CAIF Packet has functions for creating, destroying and adding content - and for adding/extracting header and trailers to protocol packets. - -The CAIF Protocol implementation contains: - - - CFCNFG CAIF Configuration layer. Configures the CAIF Protocol - Stack and provides a Client interface for adding Link-Layer and - Driver interfaces on top of the CAIF Stack. - - - CFCTRL CAIF Control layer. Encodes and Decodes control messages - such as enumeration and channel setup. Also matches request and - response messages. - - - CFSERVL General CAIF Service Layer functionality; handles flow - control and remote shutdown requests. - - - CFVEI CAIF VEI layer. Handles CAIF AT Channels on VEI (Virtual - External Interface). This layer encodes/decodes VEI frames. - - - CFDGML CAIF Datagram layer. Handles CAIF Datagram layer (IP - traffic), encodes/decodes Datagram frames. - - - CFMUX CAIF Mux layer. Handles multiplexing between multiple - physical bearers and multiple channels such as VEI, Datagram, etc. - The MUX keeps track of the existing CAIF Channels and - Physical Instances and selects the appropriate instance based - on Channel-Id and Physical-ID. - - - CFFRML CAIF Framing layer. Handles Framing i.e. Frame length - and frame checksum. - - - CFSERL CAIF Serial layer. Handles concatenation/split of frames - into CAIF Frames with correct length. - -:: - - +---------+ - | Config | - | CFCNFG | - +---------+ - ! - +---------+ +---------+ +---------+ - | AT | | Control | | Datagram| - | CFVEIL | | CFCTRL | | CFDGML | - +---------+ +---------+ +---------+ - \_____________!______________/ - ! - +---------+ - | MUX | - | | - +---------+ - _____!_____ - / \ - +---------+ +---------+ - | CFFRML | | CFFRML | - | Framing | | Framing | - +---------+ +---------+ - ! ! - +---------+ +---------+ - | | | Serial | - | | | CFSERL | - +---------+ +---------+ - - -In this layered approach the following "rules" apply. - - - All layers embed the same structure "struct cflayer" - - A layer does not depend on any other layer's private data. - - Layers are stacked by setting the pointers:: - - layer->up , layer->dn - - - In order to send data upwards, each layer should do:: - - layer->up->receive(layer->up, packet); - - - In order to send data downwards, each layer should do:: - - layer->dn->transmit(layer->dn, packet); - - -CAIF Socket and IP interface -============================ - -The IP interface and CAIF socket API are implemented on top of the -CAIF Core protocol. The IP Interface and CAIF socket have an instance of -'struct cflayer', just like the CAIF Core protocol stack. -Net device and Socket implement the 'receive()' function defined by -'struct cflayer', just like the rest of the CAIF stack. In this way, transmit and -receive of packets is handled as by the rest of the layers: the 'dn->transmit()' -function is called in order to transmit data. - -Configuration of Link Layer ---------------------------- -The Link Layer is implemented as Linux network devices (struct net_device). -Payload handling and registration is done using standard Linux mechanisms. - -The CAIF Protocol relies on a loss-less link layer without implementing -retransmission. This implies that packet drops must not happen. -Therefore a flow-control mechanism is implemented where the physical -interface can initiate flow stop for all CAIF Channels. diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst index c2406bd8ae0b..2e946924ad3f 100644 --- a/Documentation/networking/index.rst +++ b/Documentation/networking/index.rst @@ -17,7 +17,6 @@ Contents: diagnostic/index dsa/index devlink/index - caif/index ethtool-netlink ieee802154 iso15765-2 diff --git a/Documentation/translations/zh_CN/networking/index.rst b/Documentation/translations/zh_CN/networking/index.rst index c276c0993c51..333e9f6cafff 100644 --- a/Documentation/translations/zh_CN/networking/index.rst +++ b/Documentation/translations/zh_CN/networking/index.rst @@ -42,7 +42,6 @@ Todolist: * diagnostic/index * dsa/index * devlink/index -* caif/index * ethtool-netlink * ieee802154 * iso15765-2 diff --git a/MAINTAINERS b/MAINTAINERS index e7dc9e6fad2e..2b1b5e93c272 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5674,15 +5674,6 @@ T: git git://linuxtv.org/media.git F: Documentation/admin-guide/media/cafe_ccic* F: drivers/media/platform/marvell/ -CAIF NETWORK LAYER -L: netdev@vger.kernel.org -S: Orphan -F: Documentation/networking/caif/ -F: drivers/net/caif/ -F: include/net/caif/ -F: include/uapi/linux/caif/ -F: net/caif/ - CAKE QDISC M: Toke Høiland-Jørgensen L: cake@lists.bufferbloat.net (moderated for non-subscribers) diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig index e88533b78327..de4af7c750ca 100644 --- a/arch/arm/configs/u8500_defconfig +++ b/arch/arm/configs/u8500_defconfig @@ -37,7 +37,6 @@ CONFIG_CFG80211=y CONFIG_CFG80211_DEBUGFS=y CONFIG_MAC80211=y CONFIG_MAC80211_LEDS=y -CONFIG_CAIF=y CONFIG_NFC=m CONFIG_NFC_HCI=m CONFIG_NFC_SHDLC=y diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index edaab759dc50..8ec98f6dfef9 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -503,8 +503,6 @@ source "drivers/net/arcnet/Kconfig" source "drivers/atm/Kconfig" -source "drivers/net/caif/Kconfig" - source "drivers/net/dsa/Kconfig" source "drivers/net/ethernet/Kconfig" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 5b01215f6829..3b2d28127634 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -48,7 +48,6 @@ obj-$(CONFIG_MHI_NET) += mhi_net.o # Networking Drivers # obj-$(CONFIG_ARCNET) += arcnet/ -obj-$(CONFIG_CAIF) += caif/ obj-$(CONFIG_CAN) += can/ ifdef CONFIG_NET_DSA obj-y += dsa/ diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig deleted file mode 100644 index 709660cb38f8..000000000000 --- a/drivers/net/caif/Kconfig +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# CAIF physical drivers -# - -menuconfig CAIF_DRIVERS - bool "CAIF transport drivers" - depends on CAIF - help - Enable this to see CAIF physical drivers. - -if CAIF_DRIVERS - -config CAIF_TTY - tristate "CAIF TTY transport driver" - depends on CAIF && TTY - default n - help - The CAIF TTY transport driver is a Line Discipline (ldisc) - identified as N_CAIF. When this ldisc is opened from user space - it will redirect the TTY's traffic into the CAIF stack. - -config CAIF_VIRTIO - tristate "CAIF virtio transport driver" - depends on CAIF && HAS_DMA - select VHOST_RING - select VIRTIO - select GENERIC_ALLOCATOR - default n - help - The CAIF driver for CAIF over Virtio. - -endif # CAIF_DRIVERS diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile deleted file mode 100644 index 97f664f8016c..000000000000 --- a/drivers/net/caif/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -ccflags-$(CONFIG_CAIF_DEBUG) := -DDEBUG - -# Serial interface -obj-$(CONFIG_CAIF_TTY) += caif_serial.o - -# Virtio interface -obj-$(CONFIG_CAIF_VIRTIO) += caif_virtio.o diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c deleted file mode 100644 index 1873d8287bb9..000000000000 --- a/drivers/net/caif/caif_serial.c +++ /dev/null @@ -1,443 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Sjur Brendeland"); -MODULE_DESCRIPTION("CAIF serial device TTY line discipline"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_CAIF); - -#define SEND_QUEUE_LOW 10 -#define SEND_QUEUE_HIGH 100 -#define CAIF_SENDING 1 /* Bit 1 = 0x02*/ -#define CAIF_FLOW_OFF_SENT 4 /* Bit 4 = 0x10 */ -#define MAX_WRITE_CHUNK 4096 -#define ON 1 -#define OFF 0 -#define CAIF_MAX_MTU 4096 - -static DEFINE_SPINLOCK(ser_lock); -static LIST_HEAD(ser_list); -static LIST_HEAD(ser_release_list); - -static bool ser_loop; -module_param(ser_loop, bool, 0444); -MODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode."); - -static bool ser_use_stx = true; -module_param(ser_use_stx, bool, 0444); -MODULE_PARM_DESC(ser_use_stx, "STX enabled or not."); - -static bool ser_use_fcs = true; - -module_param(ser_use_fcs, bool, 0444); -MODULE_PARM_DESC(ser_use_fcs, "FCS enabled or not."); - -static int ser_write_chunk = MAX_WRITE_CHUNK; -module_param(ser_write_chunk, int, 0444); - -MODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART."); - -static struct dentry *debugfsdir; - -static int caif_net_open(struct net_device *dev); -static int caif_net_close(struct net_device *dev); - -struct ser_device { - struct caif_dev_common common; - struct list_head node; - struct net_device *dev; - struct sk_buff_head head; - struct tty_struct *tty; - bool tx_started; - unsigned long state; -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_tty_dir; - struct debugfs_blob_wrapper tx_blob; - struct debugfs_blob_wrapper rx_blob; - u8 rx_data[128]; - u8 tx_data[128]; - u8 tty_status; - -#endif -}; - -static void caifdev_setup(struct net_device *dev); -static void ldisc_tx_wakeup(struct tty_struct *tty); -#ifdef CONFIG_DEBUG_FS -static inline void update_tty_status(struct ser_device *ser) -{ - ser->tty_status = - ser->tty->flow.stopped << 5 | - ser->tty->flow.tco_stopped << 3 | - ser->tty->ctrl.packet << 2; -} -static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) -{ - ser->debugfs_tty_dir = debugfs_create_dir(tty->name, debugfsdir); - - debugfs_create_blob("last_tx_msg", 0400, ser->debugfs_tty_dir, - &ser->tx_blob); - - debugfs_create_blob("last_rx_msg", 0400, ser->debugfs_tty_dir, - &ser->rx_blob); - - debugfs_create_xul("ser_state", 0400, ser->debugfs_tty_dir, - &ser->state); - - debugfs_create_x8("tty_status", 0400, ser->debugfs_tty_dir, - &ser->tty_status); - - ser->tx_blob.data = ser->tx_data; - ser->tx_blob.size = 0; - ser->rx_blob.data = ser->rx_data; - ser->rx_blob.size = 0; -} - -static inline void debugfs_deinit(struct ser_device *ser) -{ - debugfs_remove_recursive(ser->debugfs_tty_dir); -} - -static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) -{ - if (size > sizeof(ser->rx_data)) - size = sizeof(ser->rx_data); - memcpy(ser->rx_data, data, size); - ser->rx_blob.data = ser->rx_data; - ser->rx_blob.size = size; -} -#else -static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) -{ -} - -static inline void debugfs_deinit(struct ser_device *ser) -{ -} - -static inline void update_tty_status(struct ser_device *ser) -{ -} - -static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) -{ -} -#endif - -static void ldisc_receive(struct tty_struct *tty, const u8 *data, - const u8 *flags, size_t count) -{ - struct sk_buff *skb = NULL; - struct ser_device *ser; - int ret; - - ser = tty->disc_data; - - /* - * NOTE: flags may contain information about break or overrun. - * This is not yet handled. - */ - - - /* - * Workaround for garbage at start of transmission, - * only enable if STX handling is not enabled. - */ - if (!ser->common.use_stx && !ser->tx_started) { - dev_info(&ser->dev->dev, - "Bytes received before initial transmission -" - "bytes discarded.\n"); - return; - } - - BUG_ON(ser->dev == NULL); - - /* Get a suitable caif packet and copy in data. */ - skb = netdev_alloc_skb(ser->dev, count+1); - if (skb == NULL) - return; - skb_put_data(skb, data, count); - - skb->protocol = htons(ETH_P_CAIF); - skb_reset_mac_header(skb); - debugfs_rx(ser, data, count); - /* Push received packet up the stack. */ - ret = netif_rx(skb); - if (!ret) { - ser->dev->stats.rx_packets++; - ser->dev->stats.rx_bytes += count; - } else - ++ser->dev->stats.rx_dropped; - update_tty_status(ser); -} - -static int handle_tx(struct ser_device *ser) -{ - struct tty_struct *tty; - struct sk_buff *skb; - int tty_wr, len, room; - - tty = ser->tty; - ser->tx_started = true; - - /* Enter critical section */ - if (test_and_set_bit(CAIF_SENDING, &ser->state)) - return 0; - - /* skb_peek is safe because handle_tx is called after skb_queue_tail */ - while ((skb = skb_peek(&ser->head)) != NULL) { - - /* Make sure you don't write too much */ - len = skb->len; - room = tty_write_room(tty); - if (!room) - break; - if (room > ser_write_chunk) - room = ser_write_chunk; - if (len > room) - len = room; - - /* Write to tty or loopback */ - if (!ser_loop) { - tty_wr = tty->ops->write(tty, skb->data, len); - update_tty_status(ser); - } else { - tty_wr = len; - ldisc_receive(tty, skb->data, NULL, len); - } - ser->dev->stats.tx_packets++; - ser->dev->stats.tx_bytes += tty_wr; - - /* Error on TTY ?! */ - if (tty_wr < 0) - goto error; - /* Reduce buffer written, and discard if empty */ - skb_pull(skb, tty_wr); - if (skb->len == 0) { - struct sk_buff *tmp = skb_dequeue(&ser->head); - WARN_ON(tmp != skb); - dev_consume_skb_any(skb); - } - } - /* Send flow off if queue is empty */ - if (ser->head.qlen <= SEND_QUEUE_LOW && - test_and_clear_bit(CAIF_FLOW_OFF_SENT, &ser->state) && - ser->common.flowctrl != NULL) - ser->common.flowctrl(ser->dev, ON); - clear_bit(CAIF_SENDING, &ser->state); - return 0; -error: - clear_bit(CAIF_SENDING, &ser->state); - return tty_wr; -} - -static netdev_tx_t caif_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct ser_device *ser; - - ser = netdev_priv(dev); - - /* Send flow off once, on high water mark */ - if (ser->head.qlen > SEND_QUEUE_HIGH && - !test_and_set_bit(CAIF_FLOW_OFF_SENT, &ser->state) && - ser->common.flowctrl != NULL) - - ser->common.flowctrl(ser->dev, OFF); - - skb_queue_tail(&ser->head, skb); - return handle_tx(ser); -} - - -static void ldisc_tx_wakeup(struct tty_struct *tty) -{ - struct ser_device *ser; - - ser = tty->disc_data; - BUG_ON(ser == NULL); - WARN_ON(ser->tty != tty); - handle_tx(ser); -} - - -static void ser_release(struct work_struct *work) -{ - struct list_head list; - struct ser_device *ser, *tmp; - struct tty_struct *tty; - - spin_lock(&ser_lock); - list_replace_init(&ser_release_list, &list); - spin_unlock(&ser_lock); - - if (!list_empty(&list)) { - rtnl_lock(); - list_for_each_entry_safe(ser, tmp, &list, node) { - tty = ser->tty; - dev_close(ser->dev); - unregister_netdevice(ser->dev); - debugfs_deinit(ser); - tty_kref_put(tty->link); - tty_kref_put(tty); - } - rtnl_unlock(); - } -} - -static DECLARE_WORK(ser_release_work, ser_release); - -static int ldisc_open(struct tty_struct *tty) -{ - struct ser_device *ser; - struct net_device *dev; - char name[64]; - int result; - - /* No write no play */ - if (tty->ops->write == NULL) - return -EOPNOTSUPP; - if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_TTY_CONFIG)) - return -EPERM; - - /* release devices to avoid name collision */ - ser_release(NULL); - - result = snprintf(name, sizeof(name), "cf%s", tty->name); - if (result >= IFNAMSIZ) - return -EINVAL; - dev = alloc_netdev(sizeof(*ser), name, NET_NAME_UNKNOWN, - caifdev_setup); - if (!dev) - return -ENOMEM; - - ser = netdev_priv(dev); - ser->tty = tty_kref_get(tty); - tty_kref_get(tty->link); - ser->dev = dev; - debugfs_init(ser, tty); - tty->receive_room = 4096; - tty->disc_data = ser; - set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - rtnl_lock(); - result = register_netdevice(dev); - if (result) { - tty_kref_put(tty->link); - tty_kref_put(tty); - rtnl_unlock(); - free_netdev(dev); - return -ENODEV; - } - - spin_lock(&ser_lock); - list_add(&ser->node, &ser_list); - spin_unlock(&ser_lock); - rtnl_unlock(); - netif_stop_queue(dev); - update_tty_status(ser); - return 0; -} - -static void ldisc_close(struct tty_struct *tty) -{ - struct ser_device *ser = tty->disc_data; - - spin_lock(&ser_lock); - list_move(&ser->node, &ser_release_list); - spin_unlock(&ser_lock); - schedule_work(&ser_release_work); -} - -/* The line discipline structure. */ -static struct tty_ldisc_ops caif_ldisc = { - .owner = THIS_MODULE, - .num = N_CAIF, - .name = "n_caif", - .open = ldisc_open, - .close = ldisc_close, - .receive_buf = ldisc_receive, - .write_wakeup = ldisc_tx_wakeup -}; - -static const struct net_device_ops netdev_ops = { - .ndo_open = caif_net_open, - .ndo_stop = caif_net_close, - .ndo_start_xmit = caif_xmit -}; - -static void caifdev_setup(struct net_device *dev) -{ - struct ser_device *serdev = netdev_priv(dev); - - dev->features = 0; - dev->netdev_ops = &netdev_ops; - dev->type = ARPHRD_CAIF; - dev->flags = IFF_POINTOPOINT | IFF_NOARP; - dev->mtu = CAIF_MAX_MTU; - dev->priv_flags |= IFF_NO_QUEUE; - dev->needs_free_netdev = true; - skb_queue_head_init(&serdev->head); - serdev->common.link_select = CAIF_LINK_LOW_LATENCY; - serdev->common.use_frag = true; - serdev->common.use_stx = ser_use_stx; - serdev->common.use_fcs = ser_use_fcs; - serdev->dev = dev; -} - - -static int caif_net_open(struct net_device *dev) -{ - netif_wake_queue(dev); - return 0; -} - -static int caif_net_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -static int __init caif_ser_init(void) -{ - int ret; - - ret = tty_register_ldisc(&caif_ldisc); - if (ret < 0) - pr_err("cannot register CAIF ldisc=%d err=%d\n", N_CAIF, ret); - - debugfsdir = debugfs_create_dir("caif_serial", NULL); - return ret; -} - -static void __exit caif_ser_exit(void) -{ - spin_lock(&ser_lock); - list_splice(&ser_list, &ser_release_list); - spin_unlock(&ser_lock); - ser_release(NULL); - cancel_work_sync(&ser_release_work); - tty_unregister_ldisc(&caif_ldisc); - debugfs_remove_recursive(debugfsdir); -} - -module_init(caif_ser_init); -module_exit(caif_ser_exit); diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c deleted file mode 100644 index 8ac1a4b8e055..000000000000 --- a/drivers/net/caif/caif_virtio.c +++ /dev/null @@ -1,791 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2013 - * Authors: Vicram Arv - * Dmitry Tarnyagin - * Sjur Brendeland - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Vicram Arv"); -MODULE_AUTHOR("Sjur Brendeland"); -MODULE_DESCRIPTION("Virtio CAIF Driver"); - -/* NAPI schedule quota */ -#define CFV_DEFAULT_QUOTA 32 - -/* Defaults used if virtio config space is unavailable */ -#define CFV_DEF_MTU_SIZE 4096 -#define CFV_DEF_HEADROOM 32 -#define CFV_DEF_TAILROOM 32 - -/* Required IP header alignment */ -#define IP_HDR_ALIGN 4 - -/* struct cfv_napi_contxt - NAPI context info - * @riov: IOV holding data read from the ring. Note that riov may - * still hold data when cfv_rx_poll() returns. - * @head: Last descriptor ID we received from vringh_getdesc_kern. - * We use this to put descriptor back on the used ring. USHRT_MAX is - * used to indicate invalid head-id. - */ -struct cfv_napi_context { - struct vringh_kiov riov; - unsigned short head; -}; - -/* struct cfv_stats - statistics for debugfs - * @rx_napi_complete: Number of NAPI completions (RX) - * @rx_napi_resched: Number of calls where the full quota was used (RX) - * @rx_nomem: Number of SKB alloc failures (RX) - * @rx_kicks: Number of RX kicks - * @tx_full_ring: Number times TX ring was full - * @tx_no_mem: Number of times TX went out of memory - * @tx_flow_on: Number of flow on (TX) - * @tx_kicks: Number of TX kicks - */ -struct cfv_stats { - u32 rx_napi_complete; - u32 rx_napi_resched; - u32 rx_nomem; - u32 rx_kicks; - u32 tx_full_ring; - u32 tx_no_mem; - u32 tx_flow_on; - u32 tx_kicks; -}; - -/* struct cfv_info - Caif Virtio control structure - * @cfdev: caif common header - * @vdev: Associated virtio device - * @vr_rx: rx/downlink host vring - * @vq_tx: tx/uplink virtqueue - * @ndev: CAIF link layer device - * @watermark_tx: indicates number of free descriptors we need - * to reopen the tx-queues after overload. - * @tx_lock: protects vq_tx from concurrent use - * @tx_release_tasklet: Tasklet for freeing consumed TX buffers - * @napi: Napi context used in cfv_rx_poll() - * @ctx: Context data used in cfv_rx_poll() - * @tx_hr: transmit headroom - * @rx_hr: receive headroom - * @tx_tr: transmit tail room - * @rx_tr: receive tail room - * @mtu: transmit max size - * @mru: receive max size - * @allocsz: size of dma memory reserved for TX buffers - * @alloc_addr: virtual address to dma memory for TX buffers - * @alloc_dma: dma address to dma memory for TX buffers - * @genpool: Gen Pool used for allocating TX buffers - * @reserved_mem: Pointer to memory reserve allocated from genpool - * @reserved_size: Size of memory reserve allocated from genpool - * @stats: Statistics exposed in sysfs - * @debugfs: Debugfs dentry for statistic counters - */ -struct cfv_info { - struct caif_dev_common cfdev; - struct virtio_device *vdev; - struct vringh *vr_rx; - struct virtqueue *vq_tx; - struct net_device *ndev; - unsigned int watermark_tx; - /* Protect access to vq_tx */ - spinlock_t tx_lock; - struct tasklet_struct tx_release_tasklet; - struct napi_struct napi; - struct cfv_napi_context ctx; - u16 tx_hr; - u16 rx_hr; - u16 tx_tr; - u16 rx_tr; - u32 mtu; - u32 mru; - size_t allocsz; - void *alloc_addr; - dma_addr_t alloc_dma; - struct gen_pool *genpool; - unsigned long reserved_mem; - size_t reserved_size; - struct cfv_stats stats; - struct dentry *debugfs; -}; - -/* struct buf_info - maintains transmit buffer data handle - * @size: size of transmit buffer - * @dma_handle: handle to allocated dma device memory area - * @vaddr: virtual address mapping to allocated memory area - */ -struct buf_info { - size_t size; - u8 *vaddr; -}; - -/* Called from virtio device, in IRQ context */ -static void cfv_release_cb(struct virtqueue *vq_tx) -{ - struct cfv_info *cfv = vq_tx->vdev->priv; - - ++cfv->stats.tx_kicks; - tasklet_schedule(&cfv->tx_release_tasklet); -} - -static void free_buf_info(struct cfv_info *cfv, struct buf_info *buf_info) -{ - if (!buf_info) - return; - gen_pool_free(cfv->genpool, (unsigned long) buf_info->vaddr, - buf_info->size); - kfree(buf_info); -} - -/* This is invoked whenever the remote processor completed processing - * a TX msg we just sent, and the buffer is put back to the used ring. - */ -static void cfv_release_used_buf(struct virtqueue *vq_tx) -{ - struct cfv_info *cfv = vq_tx->vdev->priv; - unsigned long flags; - - BUG_ON(vq_tx != cfv->vq_tx); - - for (;;) { - unsigned int len; - struct buf_info *buf_info; - - /* Get used buffer from used ring to recycle used descriptors */ - spin_lock_irqsave(&cfv->tx_lock, flags); - buf_info = virtqueue_get_buf(vq_tx, &len); - spin_unlock_irqrestore(&cfv->tx_lock, flags); - - /* Stop looping if there are no more buffers to free */ - if (!buf_info) - break; - - free_buf_info(cfv, buf_info); - - /* watermark_tx indicates if we previously stopped the tx - * queues. If we have enough free stots in the virtio ring, - * re-establish memory reserved and open up tx queues. - */ - if (cfv->vq_tx->num_free <= cfv->watermark_tx) - continue; - - /* Re-establish memory reserve */ - if (cfv->reserved_mem == 0 && cfv->genpool) - cfv->reserved_mem = - gen_pool_alloc(cfv->genpool, - cfv->reserved_size); - - /* Open up the tx queues */ - if (cfv->reserved_mem) { - cfv->watermark_tx = - virtqueue_get_vring_size(cfv->vq_tx); - netif_tx_wake_all_queues(cfv->ndev); - /* Buffers are recycled in cfv_netdev_tx, so - * disable notifications when queues are opened. - */ - virtqueue_disable_cb(cfv->vq_tx); - ++cfv->stats.tx_flow_on; - } else { - /* if no memory reserve, wait for more free slots */ - WARN_ON(cfv->watermark_tx > - virtqueue_get_vring_size(cfv->vq_tx)); - cfv->watermark_tx += - virtqueue_get_vring_size(cfv->vq_tx) / 4; - } - } -} - -/* Allocate a SKB and copy packet data to it */ -static struct sk_buff *cfv_alloc_and_copy_skb(int *err, - struct cfv_info *cfv, - u8 *frm, u32 frm_len) -{ - struct sk_buff *skb; - u32 cfpkt_len, pad_len; - - *err = 0; - /* Verify that packet size with down-link header and mtu size */ - if (frm_len > cfv->mru || frm_len <= cfv->rx_hr + cfv->rx_tr) { - netdev_err(cfv->ndev, - "Invalid frmlen:%u mtu:%u hr:%d tr:%d\n", - frm_len, cfv->mru, cfv->rx_hr, - cfv->rx_tr); - *err = -EPROTO; - return NULL; - } - - cfpkt_len = frm_len - (cfv->rx_hr + cfv->rx_tr); - pad_len = (unsigned long)(frm + cfv->rx_hr) & (IP_HDR_ALIGN - 1); - - skb = netdev_alloc_skb(cfv->ndev, frm_len + pad_len); - if (!skb) { - *err = -ENOMEM; - return NULL; - } - - skb_reserve(skb, cfv->rx_hr + pad_len); - - skb_put_data(skb, frm + cfv->rx_hr, cfpkt_len); - return skb; -} - -/* Get packets from the host vring */ -static int cfv_rx_poll(struct napi_struct *napi, int quota) -{ - struct cfv_info *cfv = container_of(napi, struct cfv_info, napi); - int rxcnt = 0; - int err = 0; - void *buf; - struct sk_buff *skb; - struct vringh_kiov *riov = &cfv->ctx.riov; - unsigned int skb_len; - - do { - skb = NULL; - - /* Put the previous iovec back on the used ring and - * fetch a new iovec if we have processed all elements. - */ - if (riov->i == riov->used) { - if (cfv->ctx.head != USHRT_MAX) { - vringh_complete_kern(cfv->vr_rx, - cfv->ctx.head, - 0); - cfv->ctx.head = USHRT_MAX; - } - - err = vringh_getdesc_kern( - cfv->vr_rx, - riov, - NULL, - &cfv->ctx.head, - GFP_ATOMIC); - - if (err <= 0) - goto exit; - } - - buf = phys_to_virt((unsigned long) riov->iov[riov->i].iov_base); - /* TODO: Add check on valid buffer address */ - - skb = cfv_alloc_and_copy_skb(&err, cfv, buf, - riov->iov[riov->i].iov_len); - if (unlikely(err)) - goto exit; - - /* Push received packet up the stack. */ - skb_len = skb->len; - skb->protocol = htons(ETH_P_CAIF); - skb_reset_mac_header(skb); - skb->dev = cfv->ndev; - err = netif_receive_skb(skb); - if (unlikely(err)) { - ++cfv->ndev->stats.rx_dropped; - } else { - ++cfv->ndev->stats.rx_packets; - cfv->ndev->stats.rx_bytes += skb_len; - } - - ++riov->i; - ++rxcnt; - } while (rxcnt < quota); - - ++cfv->stats.rx_napi_resched; - goto out; - -exit: - switch (err) { - case 0: - ++cfv->stats.rx_napi_complete; - - /* Really out of packets? (stolen from virtio_net)*/ - napi_complete(napi); - if (unlikely(!vringh_notify_enable_kern(cfv->vr_rx)) && - napi_schedule_prep(napi)) { - vringh_notify_disable_kern(cfv->vr_rx); - __napi_schedule(napi); - } - break; - - case -ENOMEM: - ++cfv->stats.rx_nomem; - dev_kfree_skb(skb); - /* Stop NAPI poll on OOM, we hope to be polled later */ - napi_complete(napi); - vringh_notify_enable_kern(cfv->vr_rx); - break; - - default: - /* We're doomed, any modem fault is fatal */ - netdev_warn(cfv->ndev, "Bad ring, disable device\n"); - cfv->ndev->stats.rx_dropped = riov->used - riov->i; - napi_complete(napi); - vringh_notify_disable_kern(cfv->vr_rx); - netif_carrier_off(cfv->ndev); - break; - } -out: - if (rxcnt && vringh_need_notify_kern(cfv->vr_rx) > 0) - vringh_notify(cfv->vr_rx); - return rxcnt; -} - -static void cfv_recv(struct virtio_device *vdev, struct vringh *vr_rx) -{ - struct cfv_info *cfv = vdev->priv; - - ++cfv->stats.rx_kicks; - vringh_notify_disable_kern(cfv->vr_rx); - napi_schedule(&cfv->napi); -} - -static void cfv_destroy_genpool(struct cfv_info *cfv) -{ - if (cfv->alloc_addr) - dma_free_coherent(cfv->vdev->dev.parent->parent, - cfv->allocsz, cfv->alloc_addr, - cfv->alloc_dma); - - if (!cfv->genpool) - return; - gen_pool_free(cfv->genpool, cfv->reserved_mem, - cfv->reserved_size); - gen_pool_destroy(cfv->genpool); - cfv->genpool = NULL; -} - -static int cfv_create_genpool(struct cfv_info *cfv) -{ - int err; - - /* dma_alloc can only allocate whole pages, and we need a more - * fine graned allocation so we use genpool. We ask for space needed - * by IP and a full ring. If the dma allcoation fails we retry with a - * smaller allocation size. - */ - err = -ENOMEM; - cfv->allocsz = (virtqueue_get_vring_size(cfv->vq_tx) * - (ETH_DATA_LEN + cfv->tx_hr + cfv->tx_tr) * 11)/10; - if (cfv->allocsz <= (num_possible_cpus() + 1) * cfv->ndev->mtu) - return -EINVAL; - - for (;;) { - if (cfv->allocsz <= num_possible_cpus() * cfv->ndev->mtu) { - netdev_info(cfv->ndev, "Not enough device memory\n"); - return -ENOMEM; - } - - cfv->alloc_addr = dma_alloc_coherent( - cfv->vdev->dev.parent->parent, - cfv->allocsz, &cfv->alloc_dma, - GFP_ATOMIC); - if (cfv->alloc_addr) - break; - - cfv->allocsz = (cfv->allocsz * 3) >> 2; - } - - netdev_dbg(cfv->ndev, "Allocated %zd bytes from dma-memory\n", - cfv->allocsz); - - /* Allocate on 128 bytes boundaries (1 << 7)*/ - cfv->genpool = gen_pool_create(7, -1); - if (!cfv->genpool) - goto err; - - err = gen_pool_add_virt(cfv->genpool, (unsigned long)cfv->alloc_addr, - (phys_addr_t)virt_to_phys(cfv->alloc_addr), - cfv->allocsz, -1); - if (err) - goto err; - - /* Reserve some memory for low memory situations. If we hit the roof - * in the memory pool, we stop TX flow and release the reserve. - */ - cfv->reserved_size = num_possible_cpus() * cfv->ndev->mtu; - cfv->reserved_mem = gen_pool_alloc(cfv->genpool, - cfv->reserved_size); - if (!cfv->reserved_mem) { - err = -ENOMEM; - goto err; - } - - cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx); - return 0; -err: - cfv_destroy_genpool(cfv); - return err; -} - -/* Enable the CAIF interface and allocate the memory-pool */ -static int cfv_netdev_open(struct net_device *netdev) -{ - struct cfv_info *cfv = netdev_priv(netdev); - - if (cfv_create_genpool(cfv)) - return -ENOMEM; - - netif_carrier_on(netdev); - napi_enable(&cfv->napi); - - /* Schedule NAPI to read any pending packets */ - napi_schedule(&cfv->napi); - return 0; -} - -/* Disable the CAIF interface and free the memory-pool */ -static int cfv_netdev_close(struct net_device *netdev) -{ - struct cfv_info *cfv = netdev_priv(netdev); - unsigned long flags; - struct buf_info *buf_info; - - /* Disable interrupts, queues and NAPI polling */ - netif_carrier_off(netdev); - virtqueue_disable_cb(cfv->vq_tx); - vringh_notify_disable_kern(cfv->vr_rx); - napi_disable(&cfv->napi); - - /* Release any TX buffers on both used and available rings */ - cfv_release_used_buf(cfv->vq_tx); - spin_lock_irqsave(&cfv->tx_lock, flags); - while ((buf_info = virtqueue_detach_unused_buf(cfv->vq_tx))) - free_buf_info(cfv, buf_info); - spin_unlock_irqrestore(&cfv->tx_lock, flags); - - /* Release all dma allocated memory and destroy the pool */ - cfv_destroy_genpool(cfv); - return 0; -} - -/* Allocate a buffer in dma-memory and copy skb to it */ -static struct buf_info *cfv_alloc_and_copy_to_shm(struct cfv_info *cfv, - struct sk_buff *skb, - struct scatterlist *sg) -{ - struct caif_payload_info *info = (void *)&skb->cb; - struct buf_info *buf_info = NULL; - u8 pad_len, hdr_ofs; - - if (!cfv->genpool) - goto err; - - if (unlikely(cfv->tx_hr + skb->len + cfv->tx_tr > cfv->mtu)) { - netdev_warn(cfv->ndev, "Invalid packet len (%d > %d)\n", - cfv->tx_hr + skb->len + cfv->tx_tr, cfv->mtu); - goto err; - } - - buf_info = kmalloc_obj(struct buf_info, GFP_ATOMIC); - if (unlikely(!buf_info)) - goto err; - - /* Make the IP header aligned in the buffer */ - hdr_ofs = cfv->tx_hr + info->hdr_len; - pad_len = hdr_ofs & (IP_HDR_ALIGN - 1); - buf_info->size = cfv->tx_hr + skb->len + cfv->tx_tr + pad_len; - - /* allocate dma memory buffer */ - buf_info->vaddr = (void *)gen_pool_alloc(cfv->genpool, buf_info->size); - if (unlikely(!buf_info->vaddr)) - goto err; - - /* copy skbuf contents to send buffer */ - skb_copy_bits(skb, 0, buf_info->vaddr + cfv->tx_hr + pad_len, skb->len); - sg_init_one(sg, buf_info->vaddr + pad_len, - skb->len + cfv->tx_hr + cfv->rx_hr); - - return buf_info; -err: - kfree(buf_info); - return NULL; -} - -/* Put the CAIF packet on the virtio ring and kick the receiver */ -static netdev_tx_t cfv_netdev_tx(struct sk_buff *skb, struct net_device *netdev) -{ - struct cfv_info *cfv = netdev_priv(netdev); - struct buf_info *buf_info; - struct scatterlist sg; - unsigned long flags; - bool flow_off = false; - int ret; - - /* garbage collect released buffers */ - cfv_release_used_buf(cfv->vq_tx); - spin_lock_irqsave(&cfv->tx_lock, flags); - - /* Flow-off check takes into account number of cpus to make sure - * virtqueue will not be overfilled in any possible smp conditions. - * - * Flow-on is triggered when sufficient buffers are freed - */ - if (unlikely(cfv->vq_tx->num_free <= num_present_cpus())) { - flow_off = true; - cfv->stats.tx_full_ring++; - } - - /* If we run out of memory, we release the memory reserve and retry - * allocation. - */ - buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); - if (unlikely(!buf_info)) { - cfv->stats.tx_no_mem++; - flow_off = true; - - if (cfv->reserved_mem && cfv->genpool) { - gen_pool_free(cfv->genpool, cfv->reserved_mem, - cfv->reserved_size); - cfv->reserved_mem = 0; - buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); - } - } - - if (unlikely(flow_off)) { - /* Turn flow on when a 1/4 of the descriptors are released */ - cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx) / 4; - /* Enable notifications of recycled TX buffers */ - virtqueue_enable_cb(cfv->vq_tx); - netif_tx_stop_all_queues(netdev); - } - - if (unlikely(!buf_info)) { - /* If the memory reserve does it's job, this shouldn't happen */ - netdev_warn(cfv->ndev, "Out of gen_pool memory\n"); - goto err; - } - - ret = virtqueue_add_outbuf(cfv->vq_tx, &sg, 1, buf_info, GFP_ATOMIC); - if (unlikely((ret < 0))) { - /* If flow control works, this shouldn't happen */ - netdev_warn(cfv->ndev, "Failed adding buffer to TX vring:%d\n", - ret); - goto err; - } - - /* update netdev statistics */ - cfv->ndev->stats.tx_packets++; - cfv->ndev->stats.tx_bytes += skb->len; - spin_unlock_irqrestore(&cfv->tx_lock, flags); - - /* tell the remote processor it has a pending message to read */ - virtqueue_kick(cfv->vq_tx); - - dev_kfree_skb(skb); - return NETDEV_TX_OK; -err: - spin_unlock_irqrestore(&cfv->tx_lock, flags); - cfv->ndev->stats.tx_dropped++; - free_buf_info(cfv, buf_info); - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -static void cfv_tx_release_tasklet(struct tasklet_struct *t) -{ - struct cfv_info *cfv = from_tasklet(cfv, t, tx_release_tasklet); - cfv_release_used_buf(cfv->vq_tx); -} - -static const struct net_device_ops cfv_netdev_ops = { - .ndo_open = cfv_netdev_open, - .ndo_stop = cfv_netdev_close, - .ndo_start_xmit = cfv_netdev_tx, -}; - -static void cfv_netdev_setup(struct net_device *netdev) -{ - netdev->netdev_ops = &cfv_netdev_ops; - netdev->type = ARPHRD_CAIF; - netdev->tx_queue_len = 100; - netdev->flags = IFF_POINTOPOINT | IFF_NOARP; - netdev->mtu = CFV_DEF_MTU_SIZE; - netdev->needs_free_netdev = true; -} - -/* Create debugfs counters for the device */ -static inline void debugfs_init(struct cfv_info *cfv) -{ - cfv->debugfs = debugfs_create_dir(netdev_name(cfv->ndev), NULL); - - debugfs_create_u32("rx-napi-complete", 0400, cfv->debugfs, - &cfv->stats.rx_napi_complete); - debugfs_create_u32("rx-napi-resched", 0400, cfv->debugfs, - &cfv->stats.rx_napi_resched); - debugfs_create_u32("rx-nomem", 0400, cfv->debugfs, - &cfv->stats.rx_nomem); - debugfs_create_u32("rx-kicks", 0400, cfv->debugfs, - &cfv->stats.rx_kicks); - debugfs_create_u32("tx-full-ring", 0400, cfv->debugfs, - &cfv->stats.tx_full_ring); - debugfs_create_u32("tx-no-mem", 0400, cfv->debugfs, - &cfv->stats.tx_no_mem); - debugfs_create_u32("tx-kicks", 0400, cfv->debugfs, - &cfv->stats.tx_kicks); - debugfs_create_u32("tx-flow-on", 0400, cfv->debugfs, - &cfv->stats.tx_flow_on); -} - -/* Setup CAIF for the a virtio device */ -static int cfv_probe(struct virtio_device *vdev) -{ - vrh_callback_t *vrh_cbs = cfv_recv; - const char *cfv_netdev_name = "cfvrt"; - struct net_device *netdev; - struct cfv_info *cfv; - int err; - - netdev = alloc_netdev(sizeof(struct cfv_info), cfv_netdev_name, - NET_NAME_UNKNOWN, cfv_netdev_setup); - if (!netdev) - return -ENOMEM; - - cfv = netdev_priv(netdev); - cfv->vdev = vdev; - cfv->ndev = netdev; - - spin_lock_init(&cfv->tx_lock); - - /* Get the RX virtio ring. This is a "host side vring". */ - err = -ENODEV; - if (!vdev->vringh_config || !vdev->vringh_config->find_vrhs) - goto err; - - err = vdev->vringh_config->find_vrhs(vdev, 1, &cfv->vr_rx, &vrh_cbs); - if (err) - goto err; - - /* Get the TX virtio ring. This is a "guest side vring". */ - cfv->vq_tx = virtio_find_single_vq(vdev, cfv_release_cb, "output"); - if (IS_ERR(cfv->vq_tx)) { - err = PTR_ERR(cfv->vq_tx); - goto err; - } - - /* Get the CAIF configuration from virtio config space, if available */ - if (vdev->config->get) { - virtio_cread(vdev, struct virtio_caif_transf_config, headroom, - &cfv->tx_hr); - virtio_cread(vdev, struct virtio_caif_transf_config, headroom, - &cfv->rx_hr); - virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, - &cfv->tx_tr); - virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, - &cfv->rx_tr); - virtio_cread(vdev, struct virtio_caif_transf_config, mtu, - &cfv->mtu); - virtio_cread(vdev, struct virtio_caif_transf_config, mtu, - &cfv->mru); - } else { - cfv->tx_hr = CFV_DEF_HEADROOM; - cfv->rx_hr = CFV_DEF_HEADROOM; - cfv->tx_tr = CFV_DEF_TAILROOM; - cfv->rx_tr = CFV_DEF_TAILROOM; - cfv->mtu = CFV_DEF_MTU_SIZE; - cfv->mru = CFV_DEF_MTU_SIZE; - } - - netdev->needed_headroom = cfv->tx_hr; - netdev->needed_tailroom = cfv->tx_tr; - - /* Disable buffer release interrupts unless we have stopped TX queues */ - virtqueue_disable_cb(cfv->vq_tx); - - netdev->mtu = cfv->mtu - cfv->tx_tr; - vdev->priv = cfv; - - /* Initialize NAPI poll context data */ - vringh_kiov_init(&cfv->ctx.riov, NULL, 0); - cfv->ctx.head = USHRT_MAX; - netif_napi_add_weight(netdev, &cfv->napi, cfv_rx_poll, - CFV_DEFAULT_QUOTA); - - tasklet_setup(&cfv->tx_release_tasklet, cfv_tx_release_tasklet); - - /* Carrier is off until netdevice is opened */ - netif_carrier_off(netdev); - - /* serialize netdev register + virtio_device_ready() with ndo_open() */ - rtnl_lock(); - - /* register Netdev */ - err = register_netdevice(netdev); - if (err) { - rtnl_unlock(); - dev_err(&vdev->dev, "Unable to register netdev (%d)\n", err); - goto err; - } - - virtio_device_ready(vdev); - - rtnl_unlock(); - - debugfs_init(cfv); - - return 0; -err: - netdev_warn(cfv->ndev, "CAIF Virtio probe failed:%d\n", err); - - if (cfv->vr_rx) - vdev->vringh_config->del_vrhs(cfv->vdev); - if (cfv->vq_tx) - vdev->config->del_vqs(cfv->vdev); - free_netdev(netdev); - return err; -} - -static void cfv_remove(struct virtio_device *vdev) -{ - struct cfv_info *cfv = vdev->priv; - - rtnl_lock(); - dev_close(cfv->ndev); - rtnl_unlock(); - - tasklet_kill(&cfv->tx_release_tasklet); - debugfs_remove_recursive(cfv->debugfs); - - vringh_kiov_cleanup(&cfv->ctx.riov); - virtio_reset_device(vdev); - vdev->vringh_config->del_vrhs(cfv->vdev); - cfv->vr_rx = NULL; - vdev->config->del_vqs(cfv->vdev); - unregister_netdev(cfv->ndev); -} - -static struct virtio_device_id id_table[] = { - { VIRTIO_ID_CAIF, VIRTIO_DEV_ANY_ID }, - { 0 }, -}; - -static unsigned int features[] = { -}; - -static struct virtio_driver caif_virtio_driver = { - .feature_table = features, - .feature_table_size = ARRAY_SIZE(features), - .driver.name = KBUILD_MODNAME, - .id_table = id_table, - .probe = cfv_probe, - .remove = cfv_remove, -}; - -module_virtio_driver(caif_virtio_driver); -MODULE_DEVICE_TABLE(virtio, id_table); diff --git a/include/linux/virtio_caif.h b/include/linux/virtio_caif.h deleted file mode 100644 index ea722479510c..000000000000 --- a/include/linux/virtio_caif.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) ST-Ericsson AB 2012 - * Author: Sjur Brændeland - * - * This header is BSD licensed so - * anyone can use the definitions to implement compatible remote processors - */ - -#ifndef VIRTIO_CAIF_H -#define VIRTIO_CAIF_H - -#include -struct virtio_caif_transf_config { - __virtio16 headroom; - __virtio16 tailroom; - __virtio32 mtu; - u8 reserved[4]; -}; - -struct virtio_caif_config { - struct virtio_caif_transf_config uplink, downlink; - u8 reserved[8]; -}; -#endif diff --git a/include/net/caif/caif_dev.h b/include/net/caif/caif_dev.h deleted file mode 100644 index b655d8666f55..000000000000 --- a/include/net/caif/caif_dev.h +++ /dev/null @@ -1,128 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CAIF_DEV_H_ -#define CAIF_DEV_H_ - -#include -#include -#include -#include -#include -#include - -/** - * struct caif_param - CAIF parameters. - * @size: Length of data - * @data: Binary Data Blob - */ -struct caif_param { - u16 size; - u8 data[256]; -}; - -/** - * struct caif_connect_request - Request data for CAIF channel setup. - * @protocol: Type of CAIF protocol to use (at, datagram etc) - * @sockaddr: Socket address to connect. - * @priority: Priority of the connection. - * @link_selector: Link selector (high bandwidth or low latency) - * @ifindex: kernel index of the interface. - * @param: Connect Request parameters (CAIF_SO_REQ_PARAM). - * - * This struct is used when connecting a CAIF channel. - * It contains all CAIF channel configuration options. - */ -struct caif_connect_request { - enum caif_protocol_type protocol; - struct sockaddr_caif sockaddr; - enum caif_channel_priority priority; - enum caif_link_selector link_selector; - int ifindex; - struct caif_param param; -}; - -/** - * caif_connect_client - Connect a client to CAIF Core Stack. - * @config: Channel setup parameters, specifying what address - * to connect on the Modem. - * @client_layer: User implementation of client layer. This layer - * MUST have receive and control callback functions - * implemented. - * @ifindex: Link layer interface index used for this connection. - * @headroom: Head room needed by CAIF protocol. - * @tailroom: Tail room needed by CAIF protocol. - * - * This function connects a CAIF channel. The Client must implement - * the struct cflayer. This layer represents the Client layer and holds - * receive functions and control callback functions. Control callback - * function will receive information about connect/disconnect responses, - * flow control etc (see enum caif_control). - * E.g. CAIF Socket will call this function for each socket it connects - * and have one client_layer instance for each socket. - */ -int caif_connect_client(struct net *net, - struct caif_connect_request *conn_req, - struct cflayer *client_layer, int *ifindex, - int *headroom, int *tailroom); - -/** - * caif_disconnect_client - Disconnects a client from the CAIF stack. - * - * @client_layer: Client layer to be disconnected. - */ -int caif_disconnect_client(struct net *net, struct cflayer *client_layer); - - -/** - * caif_client_register_refcnt - register ref-count functions provided by client. - * - * @adapt_layer: Client layer using CAIF Stack. - * @hold: Function provided by client layer increasing ref-count - * @put: Function provided by client layer decreasing ref-count - * - * Client of the CAIF Stack must register functions for reference counting. - * These functions are called by the CAIF Stack for every upstream packet, - * and must therefore be implemented efficiently. - * - * Client should call caif_free_client when reference count degrease to zero. - */ - -void caif_client_register_refcnt(struct cflayer *adapt_layer, - void (*hold)(struct cflayer *lyr), - void (*put)(struct cflayer *lyr)); -/** - * caif_free_client - Free memory used to manage the client in the CAIF Stack. - * - * @client_layer: Client layer to be removed. - * - * This function must be called from client layer in order to free memory. - * Caller must guarantee that no packets are in flight upstream when calling - * this function. - */ -void caif_free_client(struct cflayer *adap_layer); - -/** - * struct caif_enroll_dev - Enroll a net-device as a CAIF Link layer - * @dev: Network device to enroll. - * @caifdev: Configuration information from CAIF Link Layer - * @link_support: Link layer support layer - * @head_room: Head room needed by link support layer - * @layer: Lowest layer in CAIF stack - * @rcv_fun: Receive function for CAIF stack. - * - * This function enroll a CAIF link layer into CAIF Stack and - * expects the interface to be able to handle CAIF payload. - * The link_support layer is used to add any Link Layer specific - * framing. - */ -int caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev, - struct cflayer *link_support, int head_room, - struct cflayer **layer, int (**rcv_func)( - struct sk_buff *, struct net_device *, - struct packet_type *, struct net_device *)); - -#endif /* CAIF_DEV_H_ */ diff --git a/include/net/caif/caif_device.h b/include/net/caif/caif_device.h deleted file mode 100644 index 91d1fd5b44a4..000000000000 --- a/include/net/caif/caif_device.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CAIF_DEVICE_H_ -#define CAIF_DEVICE_H_ -#include -#include -#include -#include -#include - -/** - * struct caif_dev_common - data shared between CAIF drivers and stack. - * @flowctrl: Flow Control callback function. This function is - * supplied by CAIF Core Stack and is used by CAIF - * Link Layer to send flow-stop to CAIF Core. - * The flow information will be distributed to all - * clients of CAIF. - * - * @link_select: Profile of device, either high-bandwidth or - * low-latency. This member is set by CAIF Link - * Layer Device in order to indicate if this device - * is a high bandwidth or low latency device. - * - * @use_frag: CAIF Frames may be fragmented. - * Is set by CAIF Link Layer in order to indicate if the - * interface receives fragmented frames that must be - * assembled by CAIF Core Layer. - * - * @use_fcs: Indicate if Frame CheckSum (fcs) is used. - * Is set if the physical interface is - * using Frame Checksum on the CAIF Frames. - * - * @use_stx: Indicate STart of frame eXtension (stx) in use. - * Is set if the CAIF Link Layer expects - * CAIF Frames to start with the STX byte. - * - * This structure is shared between the CAIF drivers and the CAIF stack. - * It is used by the device to register its behavior. - * CAIF Core layer must set the member flowctrl in order to supply - * CAIF Link Layer with the flow control function. - * - */ - struct caif_dev_common { - void (*flowctrl)(struct net_device *net, int on); - enum caif_link_selector link_select; - int use_frag; - int use_fcs; - int use_stx; -}; - -#endif /* CAIF_DEVICE_H_ */ diff --git a/include/net/caif/caif_layer.h b/include/net/caif/caif_layer.h deleted file mode 100644 index 053e7c6a6a66..000000000000 --- a/include/net/caif/caif_layer.h +++ /dev/null @@ -1,277 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CAIF_LAYER_H_ -#define CAIF_LAYER_H_ - -#include - -struct cflayer; -struct cfpkt; -struct caif_payload_info; - -#define CAIF_LAYER_NAME_SZ 16 - -/** - * caif_assert() - Assert function for CAIF. - * @assert: expression to evaluate. - * - * This function will print a error message and a do WARN_ON if the - * assertion fails. Normally this will do a stack up at the current location. - */ -#define caif_assert(assert) \ -do { \ - if (!(assert)) { \ - pr_err("caif:Assert detected:'%s'\n", #assert); \ - WARN_ON(!(assert)); \ - } \ -} while (0) - -/** - * enum caif_ctrlcmd - CAIF Stack Control Signaling sent in layer.ctrlcmd(). - * - * @CAIF_CTRLCMD_FLOW_OFF_IND: Flow Control is OFF, transmit function - * should stop sending data - * - * @CAIF_CTRLCMD_FLOW_ON_IND: Flow Control is ON, transmit function - * can start sending data - * - * @CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: Remote end modem has decided to close - * down channel - * - * @CAIF_CTRLCMD_INIT_RSP: Called initially when the layer below - * has finished initialization - * - * @CAIF_CTRLCMD_DEINIT_RSP: Called when de-initialization is - * complete - * - * @CAIF_CTRLCMD_INIT_FAIL_RSP: Called if initialization fails - * - * @_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: CAIF Link layer temporarily cannot - * send more packets. - * @_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND: Called if CAIF Link layer is able - * to send packets again. - * @_CAIF_CTRLCMD_PHYIF_DOWN_IND: Called if CAIF Link layer is going - * down. - * - * These commands are sent upwards in the CAIF stack to the CAIF Client. - * They are used for signaling originating from the modem or CAIF Link Layer. - * These are either responses (*_RSP) or events (*_IND). - */ -enum caif_ctrlcmd { - CAIF_CTRLCMD_FLOW_OFF_IND, - CAIF_CTRLCMD_FLOW_ON_IND, - CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, - CAIF_CTRLCMD_INIT_RSP, - CAIF_CTRLCMD_DEINIT_RSP, - CAIF_CTRLCMD_INIT_FAIL_RSP, - _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, - _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND, - _CAIF_CTRLCMD_PHYIF_DOWN_IND, -}; - -/** - * enum caif_modemcmd - Modem Control Signaling, sent from CAIF Client - * to the CAIF Link Layer or modem. - * - * @CAIF_MODEMCMD_FLOW_ON_REQ: Flow Control is ON, transmit function - * can start sending data. - * - * @CAIF_MODEMCMD_FLOW_OFF_REQ: Flow Control is OFF, transmit function - * should stop sending data. - * - * @_CAIF_MODEMCMD_PHYIF_USEFULL: Notify physical layer that it is in use - * - * @_CAIF_MODEMCMD_PHYIF_USELESS: Notify physical layer that it is - * no longer in use. - * - * These are requests sent 'downwards' in the stack. - * Flow ON, OFF can be indicated to the modem. - */ -enum caif_modemcmd { - CAIF_MODEMCMD_FLOW_ON_REQ = 0, - CAIF_MODEMCMD_FLOW_OFF_REQ = 1, - _CAIF_MODEMCMD_PHYIF_USEFULL = 3, - _CAIF_MODEMCMD_PHYIF_USELESS = 4 -}; - -/** - * enum caif_direction - CAIF Packet Direction. - * Indicate if a packet is to be sent out or to be received in. - * @CAIF_DIR_IN: Incoming packet received. - * @CAIF_DIR_OUT: Outgoing packet to be transmitted. - */ -enum caif_direction { - CAIF_DIR_IN = 0, - CAIF_DIR_OUT = 1 -}; - -/** - * struct cflayer - CAIF Stack layer. - * Defines the framework for the CAIF Core Stack. - * @up: Pointer up to the layer above. - * @dn: Pointer down to the layer below. - * @node: List node used when layer participate in a list. - * @receive: Packet receive function. - * @transmit: Packet transmit function. - * @ctrlcmd: Used for control signalling upwards in the stack. - * @modemcmd: Used for control signaling downwards in the stack. - * @id: The identity of this layer - * @name: Name of the layer. - * - * This structure defines the layered structure in CAIF. - * - * It defines CAIF layering structure, used by all CAIF Layers and the - * layers interfacing CAIF. - * - * In order to integrate with CAIF an adaptation layer on top of the CAIF stack - * and PHY layer below the CAIF stack - * must be implemented. These layer must follow the design principles below. - * - * Principles for layering of protocol layers: - * - All layers must use this structure. If embedding it, then place this - * structure first in the layer specific structure. - * - * - Each layer should not depend on any others layer's private data. - * - * - In order to send data upwards do - * layer->up->receive(layer->up, packet); - * - * - In order to send data downwards do - * layer->dn->transmit(layer->dn, info, packet); - */ -struct cflayer { - struct cflayer *up; - struct cflayer *dn; - struct list_head node; - - /* - * receive() - Receive Function (non-blocking). - * Contract: Each layer must implement a receive function passing the - * CAIF packets upwards in the stack. - * Packet handling rules: - * - The CAIF packet (cfpkt) ownership is passed to the - * called receive function. This means that the - * packet cannot be accessed after passing it to the - * above layer using up->receive(). - * - * - If parsing of the packet fails, the packet must be - * destroyed and negative error code returned - * from the function. - * EXCEPTION: If the framing layer (cffrml) returns - * -EILSEQ, the packet is not freed. - * - * - If parsing succeeds (and above layers return OK) then - * the function must return a value >= 0. - * - * Returns result < 0 indicates an error, 0 or positive value - * indicates success. - * - * @layr: Pointer to the current layer the receive function is - * implemented for (this pointer). - * @cfpkt: Pointer to CaifPacket to be handled. - */ - int (*receive)(struct cflayer *layr, struct cfpkt *cfpkt); - - /* - * transmit() - Transmit Function (non-blocking). - * Contract: Each layer must implement a transmit function passing the - * CAIF packet downwards in the stack. - * Packet handling rules: - * - The CAIF packet (cfpkt) ownership is passed to the - * transmit function. This means that the packet - * cannot be accessed after passing it to the below - * layer using dn->transmit(). - * - * - Upon error the packet ownership is still passed on, - * so the packet shall be freed where error is detected. - * Callers of the transmit function shall not free packets, - * but errors shall be returned. - * - * - Return value less than zero means error, zero or - * greater than zero means OK. - * - * Returns result < 0 indicates an error, 0 or positive value - * indicates success. - * - * @layr: Pointer to the current layer the receive function - * isimplemented for (this pointer). - * @cfpkt: Pointer to CaifPacket to be handled. - */ - int (*transmit) (struct cflayer *layr, struct cfpkt *cfpkt); - - /* - * cttrlcmd() - Control Function upwards in CAIF Stack (non-blocking). - * Used for signaling responses (CAIF_CTRLCMD_*_RSP) - * and asynchronous events from the modem (CAIF_CTRLCMD_*_IND) - * - * @layr: Pointer to the current layer the receive function - * is implemented for (this pointer). - * @ctrl: Control Command. - */ - void (*ctrlcmd) (struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid); - - /* - * modemctrl() - Control Function used for controlling the modem. - * Used to signal down-wards in the CAIF stack. - * Returns 0 on success, < 0 upon failure. - * - * @layr: Pointer to the current layer the receive function - * is implemented for (this pointer). - * @ctrl: Control Command. - */ - int (*modemcmd) (struct cflayer *layr, enum caif_modemcmd ctrl); - - unsigned int id; - char name[CAIF_LAYER_NAME_SZ]; -}; - -/** - * layer_set_up() - Set the up pointer for a specified layer. - * @layr: Layer where up pointer shall be set. - * @above: Layer above. - */ -#define layer_set_up(layr, above) ((layr)->up = (struct cflayer *)(above)) - -/** - * layer_set_dn() - Set the down pointer for a specified layer. - * @layr: Layer where down pointer shall be set. - * @below: Layer below. - */ -#define layer_set_dn(layr, below) ((layr)->dn = (struct cflayer *)(below)) - -/** - * struct dev_info - Physical Device info information about physical layer. - * @dev: Pointer to native physical device. - * @id: Physical ID of the physical connection used by the - * logical CAIF connection. Used by service layers to - * identify their physical id to Caif MUX (CFMUXL)so - * that the MUX can add the correct physical ID to the - * packet. - */ -struct dev_info { - void *dev; - unsigned int id; -}; - -/** - * struct caif_payload_info - Payload information embedded in packet (sk_buff). - * - * @dev_info: Information about the receiving device. - * - * @hdr_len: Header length, used to align pay load on 32bit boundary. - * - * @channel_id: Channel ID of the logical CAIF connection. - * Used by mux to insert channel id into the caif packet. - */ -struct caif_payload_info { - struct dev_info *dev_info; - unsigned short hdr_len; - unsigned short channel_id; -}; - -#endif /* CAIF_LAYER_H_ */ diff --git a/include/net/caif/cfcnfg.h b/include/net/caif/cfcnfg.h deleted file mode 100644 index 8819ff4db35a..000000000000 --- a/include/net/caif/cfcnfg.h +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFCNFG_H_ -#define CFCNFG_H_ -#include -#include -#include -#include - -struct cfcnfg; - -/** - * enum cfcnfg_phy_preference - Physical preference HW Abstraction - * - * @CFPHYPREF_UNSPECIFIED: Default physical interface - * - * @CFPHYPREF_LOW_LAT: Default physical interface for low-latency - * traffic - * @CFPHYPREF_HIGH_BW: Default physical interface for high-bandwidth - * traffic - * @CFPHYPREF_LOOP: TEST only Loopback interface simulating modem - * responses. - * - */ -enum cfcnfg_phy_preference { - CFPHYPREF_UNSPECIFIED, - CFPHYPREF_LOW_LAT, - CFPHYPREF_HIGH_BW, - CFPHYPREF_LOOP -}; - -/** - * cfcnfg_create() - Get the CAIF configuration object given network. - * @net: Network for the CAIF configuration object. - */ -struct cfcnfg *get_cfcnfg(struct net *net); - -/** - * cfcnfg_create() - Create the CAIF configuration object. - */ -struct cfcnfg *cfcnfg_create(void); - -/** - * cfcnfg_remove() - Remove the CFCNFG object - * @cfg: config object - */ -void cfcnfg_remove(struct cfcnfg *cfg); - -/** - * cfcnfg_add_phy_layer() - Adds a physical layer to the CAIF stack. - * @cnfg: Pointer to a CAIF configuration object, created by - * cfcnfg_create(). - * @dev: Pointer to link layer device - * @phy_layer: Specify the physical layer. The transmit function - * MUST be set in the structure. - * @pref: The phy (link layer) preference. - * @link_support: Protocol implementation for link layer specific protocol. - * @fcs: Specify if checksum is used in CAIF Framing Layer. - * @head_room: Head space needed by link specific protocol. - */ -int -cfcnfg_add_phy_layer(struct cfcnfg *cnfg, - struct net_device *dev, struct cflayer *phy_layer, - enum cfcnfg_phy_preference pref, - struct cflayer *link_support, - bool fcs, int head_room); - -/** - * cfcnfg_del_phy_layer - Deletes an phy layer from the CAIF stack. - * - * @cnfg: Pointer to a CAIF configuration object, created by - * cfcnfg_create(). - * @phy_layer: Adaptation layer to be removed. - */ -int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer); - -/** - * cfcnfg_set_phy_state() - Set the state of the physical interface device. - * @cnfg: Configuration object - * @phy_layer: Physical Layer representation - * @up: State of device - */ -int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer, - bool up); - -#endif /* CFCNFG_H_ */ diff --git a/include/net/caif/cfctrl.h b/include/net/caif/cfctrl.h deleted file mode 100644 index 86d17315c8a1..000000000000 --- a/include/net/caif/cfctrl.h +++ /dev/null @@ -1,130 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFCTRL_H_ -#define CFCTRL_H_ -#include -#include - -/* CAIF Control packet commands */ -enum cfctrl_cmd { - CFCTRL_CMD_LINK_SETUP = 0, - CFCTRL_CMD_LINK_DESTROY = 1, - CFCTRL_CMD_LINK_ERR = 2, - CFCTRL_CMD_ENUM = 3, - CFCTRL_CMD_SLEEP = 4, - CFCTRL_CMD_WAKE = 5, - CFCTRL_CMD_LINK_RECONF = 6, - CFCTRL_CMD_START_REASON = 7, - CFCTRL_CMD_RADIO_SET = 8, - CFCTRL_CMD_MODEM_SET = 9, - CFCTRL_CMD_MASK = 0xf -}; - -/* Channel types */ -enum cfctrl_srv { - CFCTRL_SRV_DECM = 0, - CFCTRL_SRV_VEI = 1, - CFCTRL_SRV_VIDEO = 2, - CFCTRL_SRV_DBG = 3, - CFCTRL_SRV_DATAGRAM = 4, - CFCTRL_SRV_RFM = 5, - CFCTRL_SRV_UTIL = 6, - CFCTRL_SRV_MASK = 0xf -}; - -#define CFCTRL_RSP_BIT 0x20 -#define CFCTRL_ERR_BIT 0x10 - -struct cfctrl_rsp { - void (*linksetup_rsp)(struct cflayer *layer, u8 linkid, - enum cfctrl_srv serv, u8 phyid, - struct cflayer *adapt_layer); - void (*linkdestroy_rsp)(struct cflayer *layer, u8 linkid); - void (*linkerror_ind)(void); - void (*enum_rsp)(void); - void (*sleep_rsp)(void); - void (*wake_rsp)(void); - void (*restart_rsp)(void); - void (*radioset_rsp)(void); - void (*reject_rsp)(struct cflayer *layer, u8 linkid, - struct cflayer *client_layer); -}; - -/* Link Setup Parameters for CAIF-Links. */ -struct cfctrl_link_param { - enum cfctrl_srv linktype;/* (T3,T0) Type of Channel */ - u8 priority; /* (P4,P0) Priority of the channel */ - u8 phyid; /* (U2-U0) Physical interface to connect */ - u8 endpoint; /* (E1,E0) Endpoint for data channels */ - u8 chtype; /* (H1,H0) Channel-Type, applies to - * VEI, DEBUG */ - union { - struct { - u8 connid; /* (D7,D0) Video LinkId */ - } video; - - struct { - u32 connid; /* (N31,Ngit0) Connection ID used - * for Datagram */ - } datagram; - - struct { - u32 connid; /* Connection ID used for RFM */ - char volume[20]; /* Volume to mount for RFM */ - } rfm; /* Configuration for RFM */ - - struct { - u16 fifosize_kb; /* Psock FIFO size in KB */ - u16 fifosize_bufs; /* Psock # signal buffers */ - char name[16]; /* Name of the PSOCK service */ - u8 params[255]; /* Link setup Parameters> */ - u16 paramlen; /* Length of Link Setup - * Parameters */ - } utility; /* Configuration for Utility Links (Psock) */ - } u; -}; - -/* This structure is used internally in CFCTRL */ -struct cfctrl_request_info { - int sequence_no; - enum cfctrl_cmd cmd; - u8 channel_id; - struct cfctrl_link_param param; - struct cflayer *client_layer; - struct list_head list; -}; - -struct cfctrl { - struct cfsrvl serv; - struct cfctrl_rsp res; - atomic_t req_seq_no; - atomic_t rsp_seq_no; - struct list_head list; - /* Protects from simultaneous access to first_req list */ - spinlock_t info_list_lock; -#ifndef CAIF_NO_LOOP - u8 loop_linkid; - int loop_linkused[256]; - /* Protects simultaneous access to loop_linkid and loop_linkused */ - spinlock_t loop_linkid_lock; -#endif - -}; - -void cfctrl_enum_req(struct cflayer *cfctrl, u8 physlinkid); -int cfctrl_linkup_request(struct cflayer *cfctrl, - struct cfctrl_link_param *param, - struct cflayer *user_layer); -int cfctrl_linkdown_req(struct cflayer *cfctrl, u8 linkid, - struct cflayer *client); - -struct cflayer *cfctrl_create(void); -struct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer); -int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer); -void cfctrl_remove(struct cflayer *layr); - -#endif /* CFCTRL_H_ */ diff --git a/include/net/caif/cffrml.h b/include/net/caif/cffrml.h deleted file mode 100644 index 1ab8a80ede4d..000000000000 --- a/include/net/caif/cffrml.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFFRML_H_ -#define CFFRML_H_ -#include -#include - -struct cffrml; -struct cflayer *cffrml_create(u16 phyid, bool use_fcs); -void cffrml_free(struct cflayer *layr); -void cffrml_set_uplayer(struct cflayer *this, struct cflayer *up); -void cffrml_set_dnlayer(struct cflayer *this, struct cflayer *dn); -void cffrml_put(struct cflayer *layr); -void cffrml_hold(struct cflayer *layr); -int cffrml_refcnt_read(struct cflayer *layr); - -#endif /* CFFRML_H_ */ diff --git a/include/net/caif/cfmuxl.h b/include/net/caif/cfmuxl.h deleted file mode 100644 index 92ccb2648309..000000000000 --- a/include/net/caif/cfmuxl.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFMUXL_H_ -#define CFMUXL_H_ -#include - -struct cfsrvl; -struct cffrml; - -struct cflayer *cfmuxl_create(void); -int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid); -struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid); -int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *up, u8 phyid); -struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 linkid); - -#endif /* CFMUXL_H_ */ diff --git a/include/net/caif/cfpkt.h b/include/net/caif/cfpkt.h deleted file mode 100644 index acf664227d96..000000000000 --- a/include/net/caif/cfpkt.h +++ /dev/null @@ -1,232 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFPKT_H_ -#define CFPKT_H_ -#include -#include -struct cfpkt; - -/* Create a CAIF packet. - * len: Length of packet to be created - * @return New packet. - */ -struct cfpkt *cfpkt_create(u16 len); - -/* - * Destroy a CAIF Packet. - * pkt Packet to be destroyed. - */ -void cfpkt_destroy(struct cfpkt *pkt); - -/* - * Extract header from packet. - * - * pkt Packet to extract header data from. - * data Pointer to copy the header data into. - * len Length of head data to copy. - * @return zero on success and error code upon failure - */ -int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len); - -static inline u8 cfpkt_extr_head_u8(struct cfpkt *pkt) -{ - u8 tmp; - - cfpkt_extr_head(pkt, &tmp, 1); - - return tmp; -} - -static inline u16 cfpkt_extr_head_u16(struct cfpkt *pkt) -{ - __le16 tmp; - - cfpkt_extr_head(pkt, &tmp, 2); - - return le16_to_cpu(tmp); -} - -static inline u32 cfpkt_extr_head_u32(struct cfpkt *pkt) -{ - __le32 tmp; - - cfpkt_extr_head(pkt, &tmp, 4); - - return le32_to_cpu(tmp); -} - -/* - * Peek header from packet. - * Reads data from packet without changing packet. - * - * pkt Packet to extract header data from. - * data Pointer to copy the header data into. - * len Length of head data to copy. - * @return zero on success and error code upon failure - */ -int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len); - -/* - * Extract header from trailer (end of packet). - * - * pkt Packet to extract header data from. - * data Pointer to copy the trailer data into. - * len Length of header data to copy. - * @return zero on success and error code upon failure - */ -int cfpkt_extr_trail(struct cfpkt *pkt, void *data, u16 len); - -/* - * Add header to packet. - * - * - * pkt Packet to add header data to. - * data Pointer to data to copy into the header. - * len Length of header data to copy. - * @return zero on success and error code upon failure - */ -int cfpkt_add_head(struct cfpkt *pkt, const void *data, u16 len); - -/* - * Add trailer to packet. - * - * - * pkt Packet to add trailer data to. - * data Pointer to data to copy into the trailer. - * len Length of trailer data to copy. - * @return zero on success and error code upon failure - */ -int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len); - -/* - * Pad trailer on packet. - * Moves data pointer in packet, no content copied. - * - * pkt Packet in which to pad trailer. - * len Length of padding to add. - * @return zero on success and error code upon failure - */ -int cfpkt_pad_trail(struct cfpkt *pkt, u16 len); - -/* - * Add a single byte to packet body (tail). - * - * pkt Packet in which to add byte. - * data Byte to add. - * @return zero on success and error code upon failure - */ -int cfpkt_addbdy(struct cfpkt *pkt, const u8 data); - -/* - * Add a data to packet body (tail). - * - * pkt Packet in which to add data. - * data Pointer to data to copy into the packet body. - * len Length of data to add. - * @return zero on success and error code upon failure - */ -int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len); - -/* - * Checks whether there are more data to process in packet. - * pkt Packet to check. - * @return true if more data are available in packet false otherwise - */ -bool cfpkt_more(struct cfpkt *pkt); - -/* - * Checks whether the packet is erroneous, - * i.e. if it has been attempted to extract more data than available in packet - * or writing more data than has been allocated in cfpkt_create(). - * pkt Packet to check. - * @return true on error false otherwise - */ -bool cfpkt_erroneous(struct cfpkt *pkt); - -/* - * Get the packet length. - * pkt Packet to get length from. - * @return Number of bytes in packet. - */ -u16 cfpkt_getlen(struct cfpkt *pkt); - -/* - * Set the packet length, by adjusting the trailer pointer according to length. - * pkt Packet to set length. - * len Packet length. - * @return Number of bytes in packet. - */ -int cfpkt_setlen(struct cfpkt *pkt, u16 len); - -/* - * cfpkt_append - Appends a packet's data to another packet. - * dstpkt: Packet to append data into, WILL BE FREED BY THIS FUNCTION - * addpkt: Packet to be appended and automatically released, - * WILL BE FREED BY THIS FUNCTION. - * expectlen: Packet's expected total length. This should be considered - * as a hint. - * NB: Input packets will be destroyed after appending and cannot be used - * after calling this function. - * @return The new appended packet. - */ -struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, struct cfpkt *addpkt, - u16 expectlen); - -/* - * cfpkt_split - Split a packet into two packets at the specified split point. - * pkt: Packet to be split (will contain the first part of the data on exit) - * pos: Position to split packet in two parts. - * @return The new packet, containing the second part of the data. - */ -struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos); - -/* - * Iteration function, iterates the packet buffers from start to end. - * - * Checksum iteration function used to iterate buffers - * (we may have packets consisting of a chain of buffers) - * pkt: Packet to calculate checksum for - * iter_func: Function pointer to iteration function - * chks: Checksum calculated so far. - * buf: Pointer to the buffer to checksum - * len: Length of buf. - * data: Initial checksum value. - * @return Checksum of buffer. - */ - -int cfpkt_iterate(struct cfpkt *pkt, - u16 (*iter_func)(u16 chks, void *buf, u16 len), - u16 data); - -/* Map from a "native" packet (e.g. Linux Socket Buffer) to a CAIF packet. - * dir - Direction indicating whether this packet is to be sent or received. - * nativepkt - The native packet to be transformed to a CAIF packet - * @return The mapped CAIF Packet CFPKT. - */ -struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt); - -/* Map from a CAIF packet to a "native" packet (e.g. Linux Socket Buffer). - * pkt - The CAIF packet to be transformed into a "native" packet. - * @return The native packet transformed from a CAIF packet. - */ -void *cfpkt_tonative(struct cfpkt *pkt); - -/* - * Returns packet information for a packet. - * pkt Packet to get info from; - * @return Packet information - */ -struct caif_payload_info *cfpkt_info(struct cfpkt *pkt); - -/** cfpkt_set_prio - set priority for a CAIF packet. - * - * @pkt: The CAIF packet to be adjusted. - * @prio: one of TC_PRIO_ constants. - */ -void cfpkt_set_prio(struct cfpkt *pkt, int prio); - -#endif /* CFPKT_H_ */ diff --git a/include/net/caif/cfserl.h b/include/net/caif/cfserl.h deleted file mode 100644 index 67cce8757175..000000000000 --- a/include/net/caif/cfserl.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFSERL_H_ -#define CFSERL_H_ -#include - -struct cflayer *cfserl_create(int instance, bool use_stx); -void cfserl_release(struct cflayer *layer); -#endif diff --git a/include/net/caif/cfsrvl.h b/include/net/caif/cfsrvl.h deleted file mode 100644 index a000dc45f966..000000000000 --- a/include/net/caif/cfsrvl.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFSRVL_H_ -#define CFSRVL_H_ -#include -#include -#include -#include -#include - -struct cfsrvl { - struct cflayer layer; - bool open; - bool phy_flow_on; - bool modem_flow_on; - bool supports_flowctrl; - void (*release)(struct cflayer *layer); - struct dev_info dev_info; - void (*hold)(struct cflayer *lyr); - void (*put)(struct cflayer *lyr); - struct rcu_head rcu; -}; - -struct cflayer *cfvei_create(u8 linkid, struct dev_info *dev_info); -struct cflayer *cfdgml_create(u8 linkid, struct dev_info *dev_info); -struct cflayer *cfutill_create(u8 linkid, struct dev_info *dev_info); -struct cflayer *cfvidl_create(u8 linkid, struct dev_info *dev_info); -struct cflayer *cfrfml_create(u8 linkid, struct dev_info *dev_info, - int mtu_size); -struct cflayer *cfdbgl_create(u8 linkid, struct dev_info *dev_info); - -bool cfsrvl_phyid_match(struct cflayer *layer, int phyid); - -void cfsrvl_init(struct cfsrvl *service, - u8 channel_id, - struct dev_info *dev_info, - bool supports_flowctrl); -bool cfsrvl_ready(struct cfsrvl *service, int *err); - -static inline void cfsrvl_get(struct cflayer *layr) -{ - struct cfsrvl *s = container_of(layr, struct cfsrvl, layer); - if (layr == NULL || layr->up == NULL || s->hold == NULL) - return; - - s->hold(layr->up); -} - -static inline void cfsrvl_put(struct cflayer *layr) -{ - struct cfsrvl *s = container_of(layr, struct cfsrvl, layer); - if (layr == NULL || layr->up == NULL || s->hold == NULL) - return; - - s->put(layr->up); -} -#endif /* CFSRVL_H_ */ diff --git a/include/uapi/linux/caif/caif_socket.h b/include/uapi/linux/caif/caif_socket.h deleted file mode 100644 index d9970bbaa156..000000000000 --- a/include/uapi/linux/caif/caif_socket.h +++ /dev/null @@ -1,195 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* linux/caif_socket.h - * CAIF Definitions for CAIF socket and network layer - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef _LINUX_CAIF_SOCKET_H -#define _LINUX_CAIF_SOCKET_H - -#include -#include - -/** - * enum caif_link_selector - Physical Link Selection. - * @CAIF_LINK_HIGH_BANDW: Physical interface for high-bandwidth - * traffic. - * @CAIF_LINK_LOW_LATENCY: Physical interface for low-latency - * traffic. - * - * CAIF Link Layers can register their link properties. - * This enum is used for choosing between CAIF Link Layers when - * setting up CAIF Channels when multiple CAIF Link Layers exists. - */ -enum caif_link_selector { - CAIF_LINK_HIGH_BANDW, - CAIF_LINK_LOW_LATENCY -}; - -/** - * enum caif_channel_priority - CAIF channel priorities. - * - * @CAIF_PRIO_MIN: Min priority for a channel. - * @CAIF_PRIO_LOW: Low-priority channel. - * @CAIF_PRIO_NORMAL: Normal/default priority level. - * @CAIF_PRIO_HIGH: High priority level - * @CAIF_PRIO_MAX: Max priority for channel - * - * Priority can be set on CAIF Channels in order to - * prioritize between traffic on different CAIF Channels. - * These priority levels are recommended, but the priority value - * is not restricted to the values defined in this enum, any value - * between CAIF_PRIO_MIN and CAIF_PRIO_MAX could be used. - */ -enum caif_channel_priority { - CAIF_PRIO_MIN = 0x01, - CAIF_PRIO_LOW = 0x04, - CAIF_PRIO_NORMAL = 0x0f, - CAIF_PRIO_HIGH = 0x14, - CAIF_PRIO_MAX = 0x1F -}; - -/** - * enum caif_protocol_type - CAIF Channel type. - * @CAIFPROTO_AT: Classic AT channel. - * @CAIFPROTO_DATAGRAM: Datagram channel. - * @CAIFPROTO_DATAGRAM_LOOP: Datagram loopback channel, used for testing. - * @CAIFPROTO_UTIL: Utility (Psock) channel. - * @CAIFPROTO_RFM: Remote File Manager - * @CAIFPROTO_DEBUG: Debug link - * - * This enum defines the CAIF Channel type to be used. This defines - * the service to connect to on the modem. - */ -enum caif_protocol_type { - CAIFPROTO_AT, - CAIFPROTO_DATAGRAM, - CAIFPROTO_DATAGRAM_LOOP, - CAIFPROTO_UTIL, - CAIFPROTO_RFM, - CAIFPROTO_DEBUG, - _CAIFPROTO_MAX -}; -#define CAIFPROTO_MAX _CAIFPROTO_MAX - -/** - * enum caif_at_type - AT Service Endpoint - * @CAIF_ATTYPE_PLAIN: Connects to a plain vanilla AT channel. - */ -enum caif_at_type { - CAIF_ATTYPE_PLAIN = 2 -}; - /** - * enum caif_debug_type - Content selection for debug connection - * @CAIF_DEBUG_TRACE_INTERACTIVE: Connection will contain - * both trace and interactive debug. - * @CAIF_DEBUG_TRACE: Connection contains trace only. - * @CAIF_DEBUG_INTERACTIVE: Connection to interactive debug. - */ -enum caif_debug_type { - CAIF_DEBUG_TRACE_INTERACTIVE = 0, - CAIF_DEBUG_TRACE, - CAIF_DEBUG_INTERACTIVE, -}; - -/** - * enum caif_debug_service - Debug Service Endpoint - * @CAIF_RADIO_DEBUG_SERVICE: Debug service on the Radio sub-system - * @CAIF_APP_DEBUG_SERVICE: Debug for the applications sub-system - */ -enum caif_debug_service { - CAIF_RADIO_DEBUG_SERVICE = 1, - CAIF_APP_DEBUG_SERVICE -}; - -/** - * struct sockaddr_caif - the sockaddr structure for CAIF sockets. - * @family: Address family number, must be AF_CAIF. - * @u: Union of address data 'switched' by family. - * : - * @u.at: Applies when family = CAIFPROTO_AT. - * - * @u.at.type: Type of AT link to set up (enum caif_at_type). - * - * @u.util: Applies when family = CAIFPROTO_UTIL - * - * @u.util.service: Utility service name. - * - * @u.dgm: Applies when family = CAIFPROTO_DATAGRAM - * - * @u.dgm.connection_id: Datagram connection id. - * - * @u.dgm.nsapi: NSAPI of the PDP-Context. - * - * @u.rfm: Applies when family = CAIFPROTO_RFM - * - * @u.rfm.connection_id: Connection ID for RFM. - * - * @u.rfm.volume: Volume to mount. - * - * @u.dbg: Applies when family = CAIFPROTO_DEBUG. - * - * @u.dbg.type: Type of debug connection to set up - * (caif_debug_type). - * - * @u.dbg.service: Service sub-system to connect (caif_debug_service - * Description: - * This structure holds the connect parameters used for setting up a - * CAIF Channel. It defines the service to connect to on the modem. - */ -struct sockaddr_caif { - __kernel_sa_family_t family; - union { - struct { - __u8 type; /* type: enum caif_at_type */ - } at; /* CAIFPROTO_AT */ - struct { - char service[16]; - } util; /* CAIFPROTO_UTIL */ - union { - __u32 connection_id; - __u8 nsapi; - } dgm; /* CAIFPROTO_DATAGRAM(_LOOP)*/ - struct { - __u32 connection_id; - char volume[16]; - } rfm; /* CAIFPROTO_RFM */ - struct { - __u8 type; /* type:enum caif_debug_type */ - __u8 service; /* service:caif_debug_service */ - } dbg; /* CAIFPROTO_DEBUG */ - } u; -}; - -/** - * enum caif_socket_opts - CAIF option values for getsockopt and setsockopt. - * - * @CAIFSO_LINK_SELECT: Selector used if multiple CAIF Link layers are - * available. Either a high bandwidth - * link can be selected (CAIF_LINK_HIGH_BANDW) or - * a low latency link (CAIF_LINK_LOW_LATENCY). - * This option is of type __u32. - * Alternatively SO_BINDTODEVICE can be used. - * - * @CAIFSO_REQ_PARAM: Used to set the request parameters for a - * utility channel. (maximum 256 bytes). This - * option must be set before connecting. - * - * @CAIFSO_RSP_PARAM: Gets the response parameters for a utility - * channel. (maximum 256 bytes). This option - * is valid after a successful connect. - * - * - * This enum defines the CAIF Socket options to be used on a socket - * of type PF_CAIF. - * - */ -enum caif_socket_opts { - CAIFSO_LINK_SELECT = 127, - CAIFSO_REQ_PARAM = 128, - CAIFSO_RSP_PARAM = 129, -}; - -#endif /* _LINUX_CAIF_SOCKET_H */ diff --git a/include/uapi/linux/caif/if_caif.h b/include/uapi/linux/caif/if_caif.h deleted file mode 100644 index 74bca19403fa..000000000000 --- a/include/uapi/linux/caif/if_caif.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef IF_CAIF_H_ -#define IF_CAIF_H_ -#include -#include -#include - -/** - * enum ifla_caif - CAIF NetlinkRT parameters. - * @IFLA_CAIF_IPV4_CONNID: Connection ID for IPv4 PDP Context. - * The type of attribute is NLA_U32. - * @IFLA_CAIF_IPV6_CONNID: Connection ID for IPv6 PDP Context. - * The type of attribute is NLA_U32. - * @IFLA_CAIF_LOOPBACK: If different from zero, device is doing loopback - * The type of attribute is NLA_U8. - * - * When using RT Netlink to create, destroy or configure a CAIF IP interface, - * enum ifla_caif is used to specify the configuration attributes. - */ -enum ifla_caif { - __IFLA_CAIF_UNSPEC, - IFLA_CAIF_IPV4_CONNID, - IFLA_CAIF_IPV6_CONNID, - IFLA_CAIF_LOOPBACK, - __IFLA_CAIF_MAX -}; -#define IFLA_CAIF_MAX (__IFLA_CAIF_MAX-1) - -#endif /*IF_CAIF_H_*/ diff --git a/net/Kconfig b/net/Kconfig index 62266eaf0e95..5c588dbcbdbd 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -439,7 +439,6 @@ endif # WIRELESS source "net/rfkill/Kconfig" source "net/9p/Kconfig" -source "net/caif/Kconfig" source "net/ceph/Kconfig" source "net/nfc/Kconfig" source "net/psample/Kconfig" diff --git a/net/Makefile b/net/Makefile index 90e3d72bf58b..98e182829eff 100644 --- a/net/Makefile +++ b/net/Makefile @@ -53,7 +53,6 @@ obj-$(CONFIG_IUCV) += iucv/ obj-$(CONFIG_SMC) += smc/ obj-$(CONFIG_RFKILL) += rfkill/ obj-$(CONFIG_NET_9P) += 9p/ -obj-$(CONFIG_CAIF) += caif/ obj-$(CONFIG_DCB) += dcb/ obj-$(CONFIG_6LOWPAN) += 6lowpan/ obj-$(CONFIG_IEEE802154) += ieee802154/ diff --git a/net/caif/Kconfig b/net/caif/Kconfig deleted file mode 100644 index 87205251cc25..000000000000 --- a/net/caif/Kconfig +++ /dev/null @@ -1,54 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# CAIF net configurations -# - -menuconfig CAIF - tristate "CAIF support" - select CRC_CCITT - default n - help - The "Communication CPU to Application CPU Interface" (CAIF) is a packet - based connection-oriented MUX protocol developed by ST-Ericsson for use - with its modems. It is accessed from user space as sockets (PF_CAIF). - - Say Y (or M) here if you build for a phone product (e.g. Android or - MeeGo) that uses CAIF as transport. If unsure say N. - - If you select to build it as module then CAIF_NETDEV also needs to be - built as a module. You will also need to say Y (or M) to any CAIF - physical devices that your platform requires. - - See Documentation/networking/caif for a further explanation on how to - use and configure CAIF. - -config CAIF_DEBUG - bool "Enable Debug" - depends on CAIF - default n - help - Enable the inclusion of debug code in the CAIF stack. - Be aware that doing this will impact performance. - If unsure say N. - -config CAIF_NETDEV - tristate "CAIF GPRS Network device" - depends on CAIF - default CAIF - help - Say Y if you will be using a CAIF based GPRS network device. - This can be either built-in or a loadable module. - If you select to build it as a built-in then the main CAIF device must - also be a built-in. - If unsure say Y. - -config CAIF_USB - tristate "CAIF USB support" - depends on CAIF - default n - help - Say Y if you are using CAIF over USB CDC NCM. - This can be either built-in or a loadable module. - If you select to build it as a built-in then the main CAIF device must - also be a built-in. - If unsure say N. diff --git a/net/caif/Makefile b/net/caif/Makefile deleted file mode 100644 index 4f6c0517cdfb..000000000000 --- a/net/caif/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -ccflags-$(CONFIG_CAIF_DEBUG) := -DDEBUG - -caif-y := caif_dev.o \ - cfcnfg.o cfmuxl.o cfctrl.o \ - cffrml.o cfveil.o cfdbgl.o\ - cfserl.o cfdgml.o \ - cfrfml.o cfvidl.o cfutill.o \ - cfsrvl.o cfpkt_skbuff.o - -obj-$(CONFIG_CAIF) += caif.o -obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o -obj-$(CONFIG_CAIF) += caif_socket.o -obj-$(CONFIG_CAIF_USB) += caif_usb.o - -export-y := caif.o diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c deleted file mode 100644 index 922de3d611c0..000000000000 --- a/net/caif/caif_dev.c +++ /dev/null @@ -1,586 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * CAIF Interface registration. - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - * - * Borrowed heavily from file: pn_dev.c. Thanks to Remi Denis-Courmont - * and Sakari Ailus - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol support"); -MODULE_LICENSE("GPL"); - -/* Used for local tracking of the CAIF net devices */ -struct caif_device_entry { - struct cflayer layer; - struct list_head list; - struct net_device *netdev; - int __percpu *pcpu_refcnt; - spinlock_t flow_lock; - struct sk_buff *xoff_skb; - void (*xoff_skb_dtor)(struct sk_buff *skb); - bool xoff; -}; - -struct caif_device_entry_list { - struct list_head list; - /* Protects simulanous deletes in list */ - struct mutex lock; -}; - -struct caif_net { - struct cfcnfg *cfg; - struct caif_device_entry_list caifdevs; -}; - -static unsigned int caif_net_id; -static int q_high = 50; /* Percent */ - -struct cfcnfg *get_cfcnfg(struct net *net) -{ - struct caif_net *caifn; - caifn = net_generic(net, caif_net_id); - return caifn->cfg; -} -EXPORT_SYMBOL(get_cfcnfg); - -static struct caif_device_entry_list *caif_device_list(struct net *net) -{ - struct caif_net *caifn; - caifn = net_generic(net, caif_net_id); - return &caifn->caifdevs; -} - -static void caifd_put(struct caif_device_entry *e) -{ - this_cpu_dec(*e->pcpu_refcnt); -} - -static void caifd_hold(struct caif_device_entry *e) -{ - this_cpu_inc(*e->pcpu_refcnt); -} - -static int caifd_refcnt_read(struct caif_device_entry *e) -{ - int i, refcnt = 0; - for_each_possible_cpu(i) - refcnt += *per_cpu_ptr(e->pcpu_refcnt, i); - return refcnt; -} - -/* Allocate new CAIF device. */ -static struct caif_device_entry *caif_device_alloc(struct net_device *dev) -{ - struct caif_device_entry *caifd; - - caifd = kzalloc_obj(*caifd); - if (!caifd) - return NULL; - caifd->pcpu_refcnt = alloc_percpu(int); - if (!caifd->pcpu_refcnt) { - kfree(caifd); - return NULL; - } - caifd->netdev = dev; - dev_hold(dev); - return caifd; -} - -static struct caif_device_entry *caif_get(struct net_device *dev) -{ - struct caif_device_entry_list *caifdevs = - caif_device_list(dev_net(dev)); - struct caif_device_entry *caifd; - - list_for_each_entry_rcu(caifd, &caifdevs->list, list, - lockdep_rtnl_is_held()) { - if (caifd->netdev == dev) - return caifd; - } - return NULL; -} - -static void caif_flow_cb(struct sk_buff *skb) -{ - struct caif_device_entry *caifd; - void (*dtor)(struct sk_buff *skb) = NULL; - bool send_xoff; - - WARN_ON(skb->dev == NULL); - - rcu_read_lock(); - caifd = caif_get(skb->dev); - - WARN_ON(caifd == NULL); - if (!caifd) { - rcu_read_unlock(); - return; - } - - caifd_hold(caifd); - rcu_read_unlock(); - - spin_lock_bh(&caifd->flow_lock); - send_xoff = caifd->xoff; - caifd->xoff = false; - dtor = caifd->xoff_skb_dtor; - - if (WARN_ON(caifd->xoff_skb != skb)) - skb = NULL; - - caifd->xoff_skb = NULL; - caifd->xoff_skb_dtor = NULL; - - spin_unlock_bh(&caifd->flow_lock); - - if (dtor && skb) - dtor(skb); - - if (send_xoff) - caifd->layer.up-> - ctrlcmd(caifd->layer.up, - _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND, - caifd->layer.id); - caifd_put(caifd); -} - -static int transmit(struct cflayer *layer, struct cfpkt *pkt) -{ - int err, high = 0, qlen = 0; - struct caif_device_entry *caifd = - container_of(layer, struct caif_device_entry, layer); - struct sk_buff *skb; - struct netdev_queue *txq; - - rcu_read_lock_bh(); - - skb = cfpkt_tonative(pkt); - skb->dev = caifd->netdev; - skb_reset_network_header(skb); - skb->protocol = htons(ETH_P_CAIF); - - /* Check if we need to handle xoff */ - if (likely(caifd->netdev->priv_flags & IFF_NO_QUEUE)) - goto noxoff; - - if (unlikely(caifd->xoff)) - goto noxoff; - - if (likely(!netif_queue_stopped(caifd->netdev))) { - struct Qdisc *sch; - - /* If we run with a TX queue, check if the queue is too long*/ - txq = netdev_get_tx_queue(skb->dev, 0); - sch = rcu_dereference_bh(txq->qdisc); - if (likely(qdisc_is_empty(sch))) - goto noxoff; - - /* can check for explicit qdisc len value only !NOLOCK, - * always set flow off otherwise - */ - high = (caifd->netdev->tx_queue_len * q_high) / 100; - if (!(sch->flags & TCQ_F_NOLOCK) && likely(sch->q.qlen < high)) - goto noxoff; - } - - /* Hold lock while accessing xoff */ - spin_lock_bh(&caifd->flow_lock); - if (caifd->xoff) { - spin_unlock_bh(&caifd->flow_lock); - goto noxoff; - } - - /* - * Handle flow off, we do this by temporary hi-jacking this - * skb's destructor function, and replace it with our own - * flow-on callback. The callback will set flow-on and call - * the original destructor. - */ - - pr_debug("queue has stopped(%d) or is full (%d > %d)\n", - netif_queue_stopped(caifd->netdev), - qlen, high); - caifd->xoff = true; - caifd->xoff_skb = skb; - caifd->xoff_skb_dtor = skb->destructor; - skb->destructor = caif_flow_cb; - spin_unlock_bh(&caifd->flow_lock); - - caifd->layer.up->ctrlcmd(caifd->layer.up, - _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, - caifd->layer.id); -noxoff: - rcu_read_unlock_bh(); - - err = dev_queue_xmit(skb); - if (err > 0) - err = -EIO; - - return err; -} - -/* - * Stuff received packets into the CAIF stack. - * On error, returns non-zero and releases the skb. - */ -static int receive(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pkttype, struct net_device *orig_dev) -{ - struct cfpkt *pkt; - struct caif_device_entry *caifd; - int err; - - pkt = cfpkt_fromnative(CAIF_DIR_IN, skb); - - rcu_read_lock(); - caifd = caif_get(dev); - - if (!caifd || !caifd->layer.up || !caifd->layer.up->receive || - !netif_oper_up(caifd->netdev)) { - rcu_read_unlock(); - kfree_skb(skb); - return NET_RX_DROP; - } - - /* Hold reference to netdevice while using CAIF stack */ - caifd_hold(caifd); - rcu_read_unlock(); - - err = caifd->layer.up->receive(caifd->layer.up, pkt); - - /* For -EILSEQ the packet is not freed so free it now */ - if (err == -EILSEQ) - cfpkt_destroy(pkt); - - /* Release reference to stack upwards */ - caifd_put(caifd); - - if (err != 0) - err = NET_RX_DROP; - return err; -} - -static struct packet_type caif_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_CAIF), - .func = receive, -}; - -static void dev_flowctrl(struct net_device *dev, int on) -{ - struct caif_device_entry *caifd; - - rcu_read_lock(); - - caifd = caif_get(dev); - if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { - rcu_read_unlock(); - return; - } - - caifd_hold(caifd); - rcu_read_unlock(); - - caifd->layer.up->ctrlcmd(caifd->layer.up, - on ? - _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND : - _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, - caifd->layer.id); - caifd_put(caifd); -} - -int caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev, - struct cflayer *link_support, int head_room, - struct cflayer **layer, - int (**rcv_func)(struct sk_buff *, struct net_device *, - struct packet_type *, - struct net_device *)) -{ - struct caif_device_entry *caifd; - enum cfcnfg_phy_preference pref; - struct cfcnfg *cfg = get_cfcnfg(dev_net(dev)); - struct caif_device_entry_list *caifdevs; - int res; - - caifdevs = caif_device_list(dev_net(dev)); - caifd = caif_device_alloc(dev); - if (!caifd) - return -ENOMEM; - *layer = &caifd->layer; - spin_lock_init(&caifd->flow_lock); - - switch (caifdev->link_select) { - case CAIF_LINK_HIGH_BANDW: - pref = CFPHYPREF_HIGH_BW; - break; - case CAIF_LINK_LOW_LATENCY: - pref = CFPHYPREF_LOW_LAT; - break; - default: - pref = CFPHYPREF_HIGH_BW; - break; - } - mutex_lock(&caifdevs->lock); - list_add_rcu(&caifd->list, &caifdevs->list); - - strscpy(caifd->layer.name, dev->name, - sizeof(caifd->layer.name)); - caifd->layer.transmit = transmit; - res = cfcnfg_add_phy_layer(cfg, - dev, - &caifd->layer, - pref, - link_support, - caifdev->use_fcs, - head_room); - mutex_unlock(&caifdevs->lock); - if (rcv_func) - *rcv_func = receive; - return res; -} -EXPORT_SYMBOL(caif_enroll_dev); - -/* notify Caif of device events */ -static int caif_device_notify(struct notifier_block *me, unsigned long what, - void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct caif_device_entry *caifd = NULL; - struct caif_dev_common *caifdev; - struct cfcnfg *cfg; - struct cflayer *layer, *link_support; - int head_room = 0; - struct caif_device_entry_list *caifdevs; - int res; - - cfg = get_cfcnfg(dev_net(dev)); - caifdevs = caif_device_list(dev_net(dev)); - - caifd = caif_get(dev); - if (caifd == NULL && dev->type != ARPHRD_CAIF) - return 0; - - switch (what) { - case NETDEV_REGISTER: - if (caifd != NULL) - break; - - caifdev = netdev_priv(dev); - - link_support = NULL; - if (caifdev->use_frag) { - head_room = 1; - link_support = cfserl_create(dev->ifindex, - caifdev->use_stx); - if (!link_support) { - pr_warn("Out of memory\n"); - break; - } - } - res = caif_enroll_dev(dev, caifdev, link_support, head_room, - &layer, NULL); - if (res) - cfserl_release(link_support); - caifdev->flowctrl = dev_flowctrl; - break; - - case NETDEV_UP: - rcu_read_lock(); - - caifd = caif_get(dev); - if (caifd == NULL) { - rcu_read_unlock(); - break; - } - - caifd->xoff = false; - cfcnfg_set_phy_state(cfg, &caifd->layer, true); - rcu_read_unlock(); - - break; - - case NETDEV_DOWN: - rcu_read_lock(); - - caifd = caif_get(dev); - if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { - rcu_read_unlock(); - return -EINVAL; - } - - cfcnfg_set_phy_state(cfg, &caifd->layer, false); - caifd_hold(caifd); - rcu_read_unlock(); - - caifd->layer.up->ctrlcmd(caifd->layer.up, - _CAIF_CTRLCMD_PHYIF_DOWN_IND, - caifd->layer.id); - - spin_lock_bh(&caifd->flow_lock); - - /* - * Replace our xoff-destructor with original destructor. - * We trust that skb->destructor *always* is called before - * the skb reference is invalid. The hijacked SKB destructor - * takes the flow_lock so manipulating the skb->destructor here - * should be safe. - */ - if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL) - caifd->xoff_skb->destructor = caifd->xoff_skb_dtor; - - caifd->xoff = false; - caifd->xoff_skb_dtor = NULL; - caifd->xoff_skb = NULL; - - spin_unlock_bh(&caifd->flow_lock); - caifd_put(caifd); - break; - - case NETDEV_UNREGISTER: - mutex_lock(&caifdevs->lock); - - caifd = caif_get(dev); - if (caifd == NULL) { - mutex_unlock(&caifdevs->lock); - break; - } - list_del_rcu(&caifd->list); - - /* - * NETDEV_UNREGISTER is called repeatedly until all reference - * counts for the net-device are released. If references to - * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for - * the next call to NETDEV_UNREGISTER. - * - * If any packets are in flight down the CAIF Stack, - * cfcnfg_del_phy_layer will return nonzero. - * If no packets are in flight, the CAIF Stack associated - * with the net-device un-registering is freed. - */ - - if (caifd_refcnt_read(caifd) != 0 || - cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) { - - pr_info("Wait for device inuse\n"); - /* Enrole device if CAIF Stack is still in use */ - list_add_rcu(&caifd->list, &caifdevs->list); - mutex_unlock(&caifdevs->lock); - break; - } - - synchronize_rcu(); - dev_put(caifd->netdev); - free_percpu(caifd->pcpu_refcnt); - kfree(caifd); - - mutex_unlock(&caifdevs->lock); - break; - } - return 0; -} - -static struct notifier_block caif_device_notifier = { - .notifier_call = caif_device_notify, - .priority = 0, -}; - -/* Per-namespace Caif devices handling */ -static int caif_init_net(struct net *net) -{ - struct caif_net *caifn = net_generic(net, caif_net_id); - INIT_LIST_HEAD(&caifn->caifdevs.list); - mutex_init(&caifn->caifdevs.lock); - - caifn->cfg = cfcnfg_create(); - if (!caifn->cfg) - return -ENOMEM; - - return 0; -} - -static void caif_exit_net(struct net *net) -{ - struct caif_device_entry *caifd, *tmp; - struct caif_device_entry_list *caifdevs = - caif_device_list(net); - struct cfcnfg *cfg = get_cfcnfg(net); - - rtnl_lock(); - mutex_lock(&caifdevs->lock); - - list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) { - int i = 0; - list_del_rcu(&caifd->list); - cfcnfg_set_phy_state(cfg, &caifd->layer, false); - - while (i < 10 && - (caifd_refcnt_read(caifd) != 0 || - cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) { - - pr_info("Wait for device inuse\n"); - msleep(250); - i++; - } - synchronize_rcu(); - dev_put(caifd->netdev); - free_percpu(caifd->pcpu_refcnt); - kfree(caifd); - } - cfcnfg_remove(cfg); - - mutex_unlock(&caifdevs->lock); - rtnl_unlock(); -} - -static struct pernet_operations caif_net_ops = { - .init = caif_init_net, - .exit = caif_exit_net, - .id = &caif_net_id, - .size = sizeof(struct caif_net), -}; - -/* Initialize Caif devices list */ -static int __init caif_device_init(void) -{ - int result; - - result = register_pernet_subsys(&caif_net_ops); - - if (result) - return result; - - register_netdevice_notifier(&caif_device_notifier); - dev_add_pack(&caif_packet_type); - - return result; -} - -static void __exit caif_device_exit(void) -{ - unregister_netdevice_notifier(&caif_device_notifier); - dev_remove_pack(&caif_packet_type); - unregister_pernet_subsys(&caif_net_ops); -} - -module_init(caif_device_init); -module_exit(caif_device_exit); diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c deleted file mode 100644 index af218742af5a..000000000000 --- a/net/caif/caif_socket.c +++ /dev/null @@ -1,1114 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol socket support (AF_CAIF)"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(AF_CAIF); - -/* - * CAIF state is re-using the TCP socket states. - * caif_states stored in sk_state reflect the state as reported by - * the CAIF stack, while sk_socket->state is the state of the socket. - */ -enum caif_states { - CAIF_CONNECTED = TCP_ESTABLISHED, - CAIF_CONNECTING = TCP_SYN_SENT, - CAIF_DISCONNECTED = TCP_CLOSE -}; - -#define TX_FLOW_ON_BIT 1 -#define RX_FLOW_ON_BIT 2 - -struct caifsock { - struct sock sk; /* must be first member */ - struct cflayer layer; - unsigned long flow_state; - struct caif_connect_request conn_req; - struct mutex readlock; - struct dentry *debugfs_socket_dir; - int headroom, tailroom, maxframe; -}; - -static int rx_flow_is_on(struct caifsock *cf_sk) -{ - return test_bit(RX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static int tx_flow_is_on(struct caifsock *cf_sk) -{ - return test_bit(TX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static void set_rx_flow_off(struct caifsock *cf_sk) -{ - clear_bit(RX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static void set_rx_flow_on(struct caifsock *cf_sk) -{ - set_bit(RX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static void set_tx_flow_off(struct caifsock *cf_sk) -{ - clear_bit(TX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static void set_tx_flow_on(struct caifsock *cf_sk) -{ - set_bit(TX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static void caif_read_lock(struct sock *sk) -{ - struct caifsock *cf_sk; - cf_sk = container_of(sk, struct caifsock, sk); - mutex_lock(&cf_sk->readlock); -} - -static void caif_read_unlock(struct sock *sk) -{ - struct caifsock *cf_sk; - cf_sk = container_of(sk, struct caifsock, sk); - mutex_unlock(&cf_sk->readlock); -} - -static int sk_rcvbuf_lowwater(struct caifsock *cf_sk) -{ - /* A quarter of full buffer is used a low water mark */ - return cf_sk->sk.sk_rcvbuf / 4; -} - -static void caif_flow_ctrl(struct sock *sk, int mode) -{ - struct caifsock *cf_sk; - cf_sk = container_of(sk, struct caifsock, sk); - if (cf_sk->layer.dn && cf_sk->layer.dn->modemcmd) - cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, mode); -} - -/* - * Copied from sock.c:sock_queue_rcv_skb(), but changed so packets are - * not dropped, but CAIF is sending flow off instead. - */ -static void caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) -{ - int err; - unsigned long flags; - struct sk_buff_head *list = &sk->sk_receive_queue; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - bool queued = false; - - if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= - (unsigned int)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) { - net_dbg_ratelimited("sending flow OFF (queue len = %d %d)\n", - atomic_read(&cf_sk->sk.sk_rmem_alloc), - sk_rcvbuf_lowwater(cf_sk)); - set_rx_flow_off(cf_sk); - caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ); - } - - err = sk_filter(sk, skb); - if (err) - goto out; - - if (!sk_rmem_schedule(sk, skb, skb->truesize) && rx_flow_is_on(cf_sk)) { - set_rx_flow_off(cf_sk); - net_dbg_ratelimited("sending flow OFF due to rmem_schedule\n"); - caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ); - } - skb->dev = NULL; - skb_set_owner_r(skb, sk); - spin_lock_irqsave(&list->lock, flags); - queued = !sock_flag(sk, SOCK_DEAD); - if (queued) - __skb_queue_tail(list, skb); - spin_unlock_irqrestore(&list->lock, flags); -out: - if (queued) - sk->sk_data_ready(sk); - else - kfree_skb(skb); -} - -/* Packet Receive Callback function called from CAIF Stack */ -static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt) -{ - struct caifsock *cf_sk; - struct sk_buff *skb; - - cf_sk = container_of(layr, struct caifsock, layer); - skb = cfpkt_tonative(pkt); - - if (unlikely(cf_sk->sk.sk_state != CAIF_CONNECTED)) { - kfree_skb(skb); - return 0; - } - caif_queue_rcv_skb(&cf_sk->sk, skb); - return 0; -} - -static void cfsk_hold(struct cflayer *layr) -{ - struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); - sock_hold(&cf_sk->sk); -} - -static void cfsk_put(struct cflayer *layr) -{ - struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); - sock_put(&cf_sk->sk); -} - -/* Packet Control Callback function called from CAIF */ -static void caif_ctrl_cb(struct cflayer *layr, - enum caif_ctrlcmd flow, - int phyid) -{ - struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); - switch (flow) { - case CAIF_CTRLCMD_FLOW_ON_IND: - /* OK from modem to start sending again */ - set_tx_flow_on(cf_sk); - cf_sk->sk.sk_state_change(&cf_sk->sk); - break; - - case CAIF_CTRLCMD_FLOW_OFF_IND: - /* Modem asks us to shut up */ - set_tx_flow_off(cf_sk); - cf_sk->sk.sk_state_change(&cf_sk->sk); - break; - - case CAIF_CTRLCMD_INIT_RSP: - /* We're now connected */ - caif_client_register_refcnt(&cf_sk->layer, - cfsk_hold, cfsk_put); - cf_sk->sk.sk_state = CAIF_CONNECTED; - set_tx_flow_on(cf_sk); - cf_sk->sk.sk_shutdown = 0; - cf_sk->sk.sk_state_change(&cf_sk->sk); - break; - - case CAIF_CTRLCMD_DEINIT_RSP: - /* We're now disconnected */ - cf_sk->sk.sk_state = CAIF_DISCONNECTED; - cf_sk->sk.sk_state_change(&cf_sk->sk); - break; - - case CAIF_CTRLCMD_INIT_FAIL_RSP: - /* Connect request failed */ - cf_sk->sk.sk_err = ECONNREFUSED; - cf_sk->sk.sk_state = CAIF_DISCONNECTED; - cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; - /* - * Socket "standards" seems to require POLLOUT to - * be set at connect failure. - */ - set_tx_flow_on(cf_sk); - cf_sk->sk.sk_state_change(&cf_sk->sk); - break; - - case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: - /* Modem has closed this connection, or device is down. */ - cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; - cf_sk->sk.sk_err = ECONNRESET; - set_rx_flow_on(cf_sk); - sk_error_report(&cf_sk->sk); - break; - - default: - pr_debug("Unexpected flow command %d\n", flow); - } -} - -static void caif_check_flow_release(struct sock *sk) -{ - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - - if (rx_flow_is_on(cf_sk)) - return; - - if (atomic_read(&sk->sk_rmem_alloc) <= sk_rcvbuf_lowwater(cf_sk)) { - set_rx_flow_on(cf_sk); - caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_ON_REQ); - } -} - -/* - * Copied from unix_dgram_recvmsg, but removed credit checks, - * changed locking, address handling and added MSG_TRUNC. - */ -static int caif_seqpkt_recvmsg(struct socket *sock, struct msghdr *m, - size_t len, int flags) - -{ - struct sock *sk = sock->sk; - struct sk_buff *skb; - int ret; - int copylen; - - ret = -EOPNOTSUPP; - if (flags & MSG_OOB) - goto read_error; - - skb = skb_recv_datagram(sk, flags, &ret); - if (!skb) - goto read_error; - copylen = skb->len; - if (len < copylen) { - m->msg_flags |= MSG_TRUNC; - copylen = len; - } - - ret = skb_copy_datagram_msg(skb, 0, m, copylen); - if (ret) - goto out_free; - - ret = (flags & MSG_TRUNC) ? skb->len : copylen; -out_free: - skb_free_datagram(sk, skb); - caif_check_flow_release(sk); - return ret; - -read_error: - return ret; -} - - -/* Copied from unix_stream_wait_data, identical except for lock call. */ -static long caif_stream_data_wait(struct sock *sk, long timeo) -{ - DEFINE_WAIT(wait); - lock_sock(sk); - - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - - if (!skb_queue_empty(&sk->sk_receive_queue) || - sk->sk_err || - sk->sk_state != CAIF_CONNECTED || - sock_flag(sk, SOCK_DEAD) || - (sk->sk_shutdown & RCV_SHUTDOWN) || - signal_pending(current) || - !timeo) - break; - - sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - - if (sock_flag(sk, SOCK_DEAD)) - break; - - sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); - } - - finish_wait(sk_sleep(sk), &wait); - release_sock(sk); - return timeo; -} - - -/* - * Copied from unix_stream_recvmsg, but removed credit checks, - * changed locking calls, changed address handling. - */ -static int caif_stream_recvmsg(struct socket *sock, struct msghdr *msg, - size_t size, int flags) -{ - struct sock *sk = sock->sk; - int copied = 0; - int target; - int err = 0; - long timeo; - - err = -EOPNOTSUPP; - if (flags&MSG_OOB) - goto out; - - /* - * Lock the socket to prevent queue disordering - * while sleeps in memcpy_tomsg - */ - err = -EAGAIN; - if (sk->sk_state == CAIF_CONNECTING) - goto out; - - caif_read_lock(sk); - target = sock_rcvlowat(sk, flags&MSG_WAITALL, size); - timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT); - - do { - int chunk; - struct sk_buff *skb; - - lock_sock(sk); - if (sock_flag(sk, SOCK_DEAD)) { - err = -ECONNRESET; - goto unlock; - } - skb = skb_dequeue(&sk->sk_receive_queue); - caif_check_flow_release(sk); - - if (skb == NULL) { - if (copied >= target) - goto unlock; - /* - * POSIX 1003.1g mandates this order. - */ - err = sock_error(sk); - if (err) - goto unlock; - err = -ECONNRESET; - if (sk->sk_shutdown & RCV_SHUTDOWN) - goto unlock; - - err = -EPIPE; - if (sk->sk_state != CAIF_CONNECTED) - goto unlock; - if (sock_flag(sk, SOCK_DEAD)) - goto unlock; - - release_sock(sk); - - err = -EAGAIN; - if (!timeo) - break; - - caif_read_unlock(sk); - - timeo = caif_stream_data_wait(sk, timeo); - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - goto out; - } - caif_read_lock(sk); - continue; -unlock: - release_sock(sk); - break; - } - release_sock(sk); - chunk = min_t(unsigned int, skb->len, size); - if (memcpy_to_msg(msg, skb->data, chunk)) { - skb_queue_head(&sk->sk_receive_queue, skb); - if (copied == 0) - copied = -EFAULT; - break; - } - copied += chunk; - size -= chunk; - - /* Mark read part of skb as used */ - if (!(flags & MSG_PEEK)) { - skb_pull(skb, chunk); - - /* put the skb back if we didn't use it up. */ - if (skb->len) { - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - kfree_skb(skb); - - } else { - /* - * It is questionable, see note in unix_dgram_recvmsg. - */ - /* put message back and return */ - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - } while (size); - caif_read_unlock(sk); - -out: - return copied ? : err; -} - -/* - * Copied from sock.c:sock_wait_for_wmem, but change to wait for - * CAIF flow-on and sock_writable. - */ -static long caif_wait_for_flow_on(struct caifsock *cf_sk, - int wait_writeable, long timeo, int *err) -{ - struct sock *sk = &cf_sk->sk; - DEFINE_WAIT(wait); - for (;;) { - *err = 0; - if (tx_flow_is_on(cf_sk) && - (!wait_writeable || sock_writeable(&cf_sk->sk))) - break; - *err = -ETIMEDOUT; - if (!timeo) - break; - *err = -ERESTARTSYS; - if (signal_pending(current)) - break; - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - *err = -ECONNRESET; - if (sk->sk_shutdown & SHUTDOWN_MASK) - break; - *err = -sk->sk_err; - if (sk->sk_err) - break; - *err = -EPIPE; - if (cf_sk->sk.sk_state != CAIF_CONNECTED) - break; - timeo = schedule_timeout(timeo); - } - finish_wait(sk_sleep(sk), &wait); - return timeo; -} - -/* - * Transmit a SKB. The device may temporarily request re-transmission - * by returning EAGAIN. - */ -static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk, - int noblock, long timeo) -{ - struct cfpkt *pkt; - - pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb); - memset(skb->cb, 0, sizeof(struct caif_payload_info)); - cfpkt_set_prio(pkt, cf_sk->sk.sk_priority); - - if (cf_sk->layer.dn == NULL) { - kfree_skb(skb); - return -EINVAL; - } - - return cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt); -} - -/* Copied from af_unix:unix_dgram_sendmsg, and adapted to CAIF */ -static int caif_seqpkt_sendmsg(struct socket *sock, struct msghdr *msg, - size_t len) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - int buffer_size; - int ret = 0; - struct sk_buff *skb = NULL; - int noblock; - long timeo; - caif_assert(cf_sk); - ret = sock_error(sk); - if (ret) - goto err; - - ret = -EOPNOTSUPP; - if (msg->msg_flags&MSG_OOB) - goto err; - - ret = -EOPNOTSUPP; - if (msg->msg_namelen) - goto err; - - noblock = msg->msg_flags & MSG_DONTWAIT; - - timeo = sock_sndtimeo(sk, noblock); - timeo = caif_wait_for_flow_on(container_of(sk, struct caifsock, sk), - 1, timeo, &ret); - - if (ret) - goto err; - ret = -EPIPE; - if (cf_sk->sk.sk_state != CAIF_CONNECTED || - sock_flag(sk, SOCK_DEAD) || - (sk->sk_shutdown & RCV_SHUTDOWN)) - goto err; - - /* Error if trying to write more than maximum frame size. */ - ret = -EMSGSIZE; - if (len > cf_sk->maxframe && cf_sk->sk.sk_protocol != CAIFPROTO_RFM) - goto err; - - buffer_size = len + cf_sk->headroom + cf_sk->tailroom; - - ret = -ENOMEM; - skb = sock_alloc_send_skb(sk, buffer_size, noblock, &ret); - - if (!skb || skb_tailroom(skb) < buffer_size) - goto err; - - skb_reserve(skb, cf_sk->headroom); - - ret = memcpy_from_msg(skb_put(skb, len), msg, len); - - if (ret) - goto err; - ret = transmit_skb(skb, cf_sk, noblock, timeo); - if (ret < 0) - /* skb is already freed */ - return ret; - - return len; -err: - kfree_skb(skb); - return ret; -} - -/* - * Copied from unix_stream_sendmsg and adapted to CAIF: - * Changed removed permission handling and added waiting for flow on - * and other minor adaptations. - */ -static int caif_stream_sendmsg(struct socket *sock, struct msghdr *msg, - size_t len) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - int err, size; - struct sk_buff *skb; - int sent = 0; - long timeo; - - err = -EOPNOTSUPP; - if (unlikely(msg->msg_flags&MSG_OOB)) - goto out_err; - - if (unlikely(msg->msg_namelen)) - goto out_err; - - timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); - timeo = caif_wait_for_flow_on(cf_sk, 1, timeo, &err); - - if (unlikely(sk->sk_shutdown & SEND_SHUTDOWN)) - goto pipe_err; - - while (sent < len) { - - size = len-sent; - - if (size > cf_sk->maxframe) - size = cf_sk->maxframe; - - /* If size is more than half of sndbuf, chop up message */ - if (size > ((sk->sk_sndbuf >> 1) - 64)) - size = (sk->sk_sndbuf >> 1) - 64; - - if (size > SKB_MAX_ALLOC) - size = SKB_MAX_ALLOC; - - skb = sock_alloc_send_skb(sk, - size + cf_sk->headroom + - cf_sk->tailroom, - msg->msg_flags&MSG_DONTWAIT, - &err); - if (skb == NULL) - goto out_err; - - skb_reserve(skb, cf_sk->headroom); - /* - * If you pass two values to the sock_alloc_send_skb - * it tries to grab the large buffer with GFP_NOFS - * (which can fail easily), and if it fails grab the - * fallback size buffer which is under a page and will - * succeed. [Alan] - */ - size = min_t(int, size, skb_tailroom(skb)); - - err = memcpy_from_msg(skb_put(skb, size), msg, size); - if (err) { - kfree_skb(skb); - goto out_err; - } - err = transmit_skb(skb, cf_sk, - msg->msg_flags&MSG_DONTWAIT, timeo); - if (err < 0) - /* skb is already freed */ - goto pipe_err; - - sent += size; - } - - return sent; - -pipe_err: - if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL)) - send_sig(SIGPIPE, current, 0); - err = -EPIPE; -out_err: - return sent ? : err; -} - -static int setsockopt(struct socket *sock, int lvl, int opt, sockptr_t ov, - unsigned int ol) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - int linksel; - - if (cf_sk->sk.sk_socket->state != SS_UNCONNECTED) - return -ENOPROTOOPT; - - switch (opt) { - case CAIFSO_LINK_SELECT: - if (ol < sizeof(int)) - return -EINVAL; - if (lvl != SOL_CAIF) - goto bad_sol; - if (copy_from_sockptr(&linksel, ov, sizeof(int))) - return -EINVAL; - lock_sock(&(cf_sk->sk)); - cf_sk->conn_req.link_selector = linksel; - release_sock(&cf_sk->sk); - return 0; - - case CAIFSO_REQ_PARAM: - if (lvl != SOL_CAIF) - goto bad_sol; - if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL) - return -ENOPROTOOPT; - lock_sock(&(cf_sk->sk)); - if (ol > sizeof(cf_sk->conn_req.param.data) || - copy_from_sockptr(&cf_sk->conn_req.param.data, ov, ol)) { - release_sock(&cf_sk->sk); - return -EINVAL; - } - cf_sk->conn_req.param.size = ol; - release_sock(&cf_sk->sk); - return 0; - - default: - return -ENOPROTOOPT; - } - - return 0; -bad_sol: - return -ENOPROTOOPT; - -} - -/* - * caif_connect() - Connect a CAIF Socket - * Copied and modified af_irda.c:irda_connect(). - * - * Note : by consulting "errno", the user space caller may learn the cause - * of the failure. Most of them are visible in the function, others may come - * from subroutines called and are listed here : - * o -EAFNOSUPPORT: bad socket family or type. - * o -ESOCKTNOSUPPORT: bad socket type or protocol - * o -EINVAL: bad socket address, or CAIF link type - * o -ECONNREFUSED: remote end refused the connection. - * o -EINPROGRESS: connect request sent but timed out (or non-blocking) - * o -EISCONN: already connected. - * o -ETIMEDOUT: Connection timed out (send timeout) - * o -ENODEV: No link layer to send request - * o -ECONNRESET: Received Shutdown indication or lost link layer - * o -ENOMEM: Out of memory - * - * State Strategy: - * o sk_state: holds the CAIF_* protocol state, it's updated by - * caif_ctrl_cb. - * o sock->state: holds the SS_* socket state and is updated by connect and - * disconnect. - */ -static int caif_connect(struct socket *sock, struct sockaddr_unsized *uaddr, - int addr_len, int flags) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - long timeo; - int err; - int ifindex, headroom, tailroom; - unsigned int mtu; - struct net_device *dev; - - lock_sock(sk); - - err = -EINVAL; - if (addr_len < offsetofend(struct sockaddr, sa_family)) - goto out; - - err = -EAFNOSUPPORT; - if (uaddr->sa_family != AF_CAIF) - goto out; - - switch (sock->state) { - case SS_UNCONNECTED: - /* Normal case, a fresh connect */ - caif_assert(sk->sk_state == CAIF_DISCONNECTED); - break; - case SS_CONNECTING: - switch (sk->sk_state) { - case CAIF_CONNECTED: - sock->state = SS_CONNECTED; - err = -EISCONN; - goto out; - case CAIF_DISCONNECTED: - /* Reconnect allowed */ - break; - case CAIF_CONNECTING: - err = -EALREADY; - if (flags & O_NONBLOCK) - goto out; - goto wait_connect; - } - break; - case SS_CONNECTED: - caif_assert(sk->sk_state == CAIF_CONNECTED || - sk->sk_state == CAIF_DISCONNECTED); - if (sk->sk_shutdown & SHUTDOWN_MASK) { - /* Allow re-connect after SHUTDOWN_IND */ - caif_disconnect_client(sock_net(sk), &cf_sk->layer); - caif_free_client(&cf_sk->layer); - break; - } - /* No reconnect on a seqpacket socket */ - err = -EISCONN; - goto out; - case SS_DISCONNECTING: - case SS_FREE: - caif_assert(1); /*Should never happen */ - break; - } - sk->sk_state = CAIF_DISCONNECTED; - sock->state = SS_UNCONNECTED; - sk_stream_kill_queues(&cf_sk->sk); - - err = -EINVAL; - if (addr_len != sizeof(struct sockaddr_caif)) - goto out; - - memcpy(&cf_sk->conn_req.sockaddr, uaddr, - sizeof(struct sockaddr_caif)); - - /* Move to connecting socket, start sending Connect Requests */ - sock->state = SS_CONNECTING; - sk->sk_state = CAIF_CONNECTING; - - /* Check priority value comming from socket */ - /* if priority value is out of range it will be ajusted */ - if (cf_sk->sk.sk_priority > CAIF_PRIO_MAX) - cf_sk->conn_req.priority = CAIF_PRIO_MAX; - else if (cf_sk->sk.sk_priority < CAIF_PRIO_MIN) - cf_sk->conn_req.priority = CAIF_PRIO_MIN; - else - cf_sk->conn_req.priority = cf_sk->sk.sk_priority; - - /*ifindex = id of the interface.*/ - cf_sk->conn_req.ifindex = cf_sk->sk.sk_bound_dev_if; - - cf_sk->layer.receive = caif_sktrecv_cb; - - err = caif_connect_client(sock_net(sk), &cf_sk->conn_req, - &cf_sk->layer, &ifindex, &headroom, &tailroom); - - if (err < 0) { - cf_sk->sk.sk_socket->state = SS_UNCONNECTED; - cf_sk->sk.sk_state = CAIF_DISCONNECTED; - goto out; - } - - err = -ENODEV; - rcu_read_lock(); - dev = dev_get_by_index_rcu(sock_net(sk), ifindex); - if (!dev) { - rcu_read_unlock(); - goto out; - } - cf_sk->headroom = LL_RESERVED_SPACE_EXTRA(dev, headroom); - mtu = dev->mtu; - rcu_read_unlock(); - - cf_sk->tailroom = tailroom; - cf_sk->maxframe = mtu - (headroom + tailroom); - if (cf_sk->maxframe < 1) { - pr_warn("CAIF Interface MTU too small (%d)\n", dev->mtu); - err = -ENODEV; - goto out; - } - - err = -EINPROGRESS; -wait_connect: - - if (sk->sk_state != CAIF_CONNECTED && (flags & O_NONBLOCK)) - goto out; - - timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); - - release_sock(sk); - err = -ERESTARTSYS; - timeo = wait_event_interruptible_timeout(*sk_sleep(sk), - sk->sk_state != CAIF_CONNECTING, - timeo); - lock_sock(sk); - if (timeo < 0) - goto out; /* -ERESTARTSYS */ - - err = -ETIMEDOUT; - if (timeo == 0 && sk->sk_state != CAIF_CONNECTED) - goto out; - if (sk->sk_state != CAIF_CONNECTED) { - sock->state = SS_UNCONNECTED; - err = sock_error(sk); - if (!err) - err = -ECONNREFUSED; - goto out; - } - sock->state = SS_CONNECTED; - err = 0; -out: - release_sock(sk); - return err; -} - -/* - * caif_release() - Disconnect a CAIF Socket - * Copied and modified af_irda.c:irda_release(). - */ -static int caif_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - - if (!sk) - return 0; - - set_tx_flow_off(cf_sk); - - /* - * Ensure that packets are not queued after this point in time. - * caif_queue_rcv_skb checks SOCK_DEAD holding the queue lock, - * this ensures no packets when sock is dead. - */ - spin_lock_bh(&sk->sk_receive_queue.lock); - sock_set_flag(sk, SOCK_DEAD); - spin_unlock_bh(&sk->sk_receive_queue.lock); - sock->sk = NULL; - - WARN_ON(IS_ERR(cf_sk->debugfs_socket_dir)); - debugfs_remove_recursive(cf_sk->debugfs_socket_dir); - - lock_sock(&(cf_sk->sk)); - sk->sk_state = CAIF_DISCONNECTED; - sk->sk_shutdown = SHUTDOWN_MASK; - - caif_disconnect_client(sock_net(sk), &cf_sk->layer); - cf_sk->sk.sk_socket->state = SS_DISCONNECTING; - wake_up_interruptible_poll(sk_sleep(sk), EPOLLERR|EPOLLHUP); - - sock_orphan(sk); - sk_stream_kill_queues(&cf_sk->sk); - release_sock(sk); - sock_put(sk); - return 0; -} - -/* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */ -static __poll_t caif_poll(struct file *file, - struct socket *sock, poll_table *wait) -{ - struct sock *sk = sock->sk; - __poll_t mask; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - - sock_poll_wait(file, sock, wait); - mask = 0; - - /* exceptional events? */ - if (sk->sk_err) - mask |= EPOLLERR; - if (sk->sk_shutdown == SHUTDOWN_MASK) - mask |= EPOLLHUP; - if (sk->sk_shutdown & RCV_SHUTDOWN) - mask |= EPOLLRDHUP; - - /* readable? */ - if (!skb_queue_empty_lockless(&sk->sk_receive_queue) || - (sk->sk_shutdown & RCV_SHUTDOWN)) - mask |= EPOLLIN | EPOLLRDNORM; - - /* - * we set writable also when the other side has shut down the - * connection. This prevents stuck sockets. - */ - if (sock_writeable(sk) && tx_flow_is_on(cf_sk)) - mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; - - return mask; -} - -static const struct proto_ops caif_seqpacket_ops = { - .family = PF_CAIF, - .owner = THIS_MODULE, - .release = caif_release, - .bind = sock_no_bind, - .connect = caif_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = sock_no_getname, - .poll = caif_poll, - .ioctl = sock_no_ioctl, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = setsockopt, - .sendmsg = caif_seqpkt_sendmsg, - .recvmsg = caif_seqpkt_recvmsg, - .mmap = sock_no_mmap, -}; - -static const struct proto_ops caif_stream_ops = { - .family = PF_CAIF, - .owner = THIS_MODULE, - .release = caif_release, - .bind = sock_no_bind, - .connect = caif_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = sock_no_getname, - .poll = caif_poll, - .ioctl = sock_no_ioctl, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = setsockopt, - .sendmsg = caif_stream_sendmsg, - .recvmsg = caif_stream_recvmsg, - .mmap = sock_no_mmap, -}; - -/* This function is called when a socket is finally destroyed. */ -static void caif_sock_destructor(struct sock *sk) -{ - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - caif_assert(!refcount_read(&sk->sk_wmem_alloc)); - caif_assert(sk_unhashed(sk)); - caif_assert(!sk->sk_socket); - if (!sock_flag(sk, SOCK_DEAD)) { - pr_debug("Attempt to release alive CAIF socket: %p\n", sk); - return; - } - sk_stream_kill_queues(&cf_sk->sk); - WARN_ON_ONCE(sk->sk_forward_alloc); - caif_free_client(&cf_sk->layer); -} - -static int caif_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk = NULL; - struct caifsock *cf_sk = NULL; - static struct proto prot = {.name = "PF_CAIF", - .owner = THIS_MODULE, - .obj_size = sizeof(struct caifsock), - .useroffset = offsetof(struct caifsock, conn_req.param), - .usersize = sizeof_field(struct caifsock, conn_req.param) - }; - - if (!capable(CAP_SYS_ADMIN) && !capable(CAP_NET_ADMIN)) - return -EPERM; - /* - * The sock->type specifies the socket type to use. - * The CAIF socket is a packet stream in the sense - * that it is packet based. CAIF trusts the reliability - * of the link, no resending is implemented. - */ - if (sock->type == SOCK_SEQPACKET) - sock->ops = &caif_seqpacket_ops; - else if (sock->type == SOCK_STREAM) - sock->ops = &caif_stream_ops; - else - return -ESOCKTNOSUPPORT; - - if (protocol < 0 || protocol >= CAIFPROTO_MAX) - return -EPROTONOSUPPORT; - /* - * Set the socket state to unconnected. The socket state - * is really not used at all in the net/core or socket.c but the - * initialization makes sure that sock->state is not uninitialized. - */ - sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot, kern); - if (!sk) - return -ENOMEM; - - cf_sk = container_of(sk, struct caifsock, sk); - - /* Store the protocol */ - sk->sk_protocol = (unsigned char) protocol; - - /* Initialize default priority for well-known cases */ - switch (protocol) { - case CAIFPROTO_AT: - sk->sk_priority = TC_PRIO_CONTROL; - break; - case CAIFPROTO_RFM: - sk->sk_priority = TC_PRIO_INTERACTIVE_BULK; - break; - default: - sk->sk_priority = TC_PRIO_BESTEFFORT; - } - - /* - * Lock in order to try to stop someone from opening the socket - * too early. - */ - lock_sock(&(cf_sk->sk)); - - /* Initialize the nozero default sock structure data. */ - sock_init_data(sock, sk); - sk->sk_destruct = caif_sock_destructor; - - mutex_init(&cf_sk->readlock); /* single task reading lock */ - cf_sk->layer.ctrlcmd = caif_ctrl_cb; - cf_sk->sk.sk_socket->state = SS_UNCONNECTED; - cf_sk->sk.sk_state = CAIF_DISCONNECTED; - - set_tx_flow_off(cf_sk); - set_rx_flow_on(cf_sk); - - /* Set default options on configuration */ - cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY; - cf_sk->conn_req.protocol = protocol; - release_sock(&cf_sk->sk); - return 0; -} - - -static const struct net_proto_family caif_family_ops = { - .family = PF_CAIF, - .create = caif_create, - .owner = THIS_MODULE, -}; - -static int __init caif_sktinit_module(void) -{ - return sock_register(&caif_family_ops); -} - -static void __exit caif_sktexit_module(void) -{ - sock_unregister(PF_CAIF); -} -module_init(caif_sktinit_module); -module_exit(caif_sktexit_module); diff --git a/net/caif/caif_usb.c b/net/caif/caif_usb.c deleted file mode 100644 index 4d44960d4c2f..000000000000 --- a/net/caif/caif_usb.c +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * CAIF USB handler - * Copyright (C) ST-Ericsson AB 2011 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol USB support"); -MODULE_LICENSE("GPL"); - -#define CFUSB_PAD_DESCR_SZ 1 /* Alignment descriptor length */ -#define CFUSB_ALIGNMENT 4 /* Number of bytes to align. */ -#define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1) -#define STE_USB_VID 0x04cc /* USB Product ID for ST-Ericsson */ -#define STE_USB_PID_CAIF 0x230f /* Product id for CAIF Modems */ - -struct cfusbl { - struct cflayer layer; - u8 tx_eth_hdr[ETH_HLEN]; -}; - -static bool pack_added; - -static int cfusbl_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 hpad; - - /* Remove padding. */ - cfpkt_extr_head(pkt, &hpad, 1); - cfpkt_extr_head(pkt, NULL, hpad); - return layr->up->receive(layr->up, pkt); -} - -static int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - struct caif_payload_info *info; - u8 hpad; - u8 zeros[CFUSB_ALIGNMENT]; - struct sk_buff *skb; - struct cfusbl *usbl = container_of(layr, struct cfusbl, layer); - - skb = cfpkt_tonative(pkt); - - skb_reset_network_header(skb); - skb->protocol = htons(ETH_P_IP); - - info = cfpkt_info(pkt); - hpad = (info->hdr_len + CFUSB_PAD_DESCR_SZ) & (CFUSB_ALIGNMENT - 1); - - if (skb_headroom(skb) < ETH_HLEN + CFUSB_PAD_DESCR_SZ + hpad) { - pr_warn("Headroom too small\n"); - kfree_skb(skb); - return -EIO; - } - memset(zeros, 0, hpad); - - cfpkt_add_head(pkt, zeros, hpad); - cfpkt_add_head(pkt, &hpad, 1); - cfpkt_add_head(pkt, usbl->tx_eth_hdr, sizeof(usbl->tx_eth_hdr)); - return layr->dn->transmit(layr->dn, pkt); -} - -static void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - if (layr->up && layr->up->ctrlcmd) - layr->up->ctrlcmd(layr->up, ctrl, layr->id); -} - -static struct cflayer *cfusbl_create(int phyid, const u8 ethaddr[ETH_ALEN], - u8 braddr[ETH_ALEN]) -{ - struct cfusbl *this = kmalloc_obj(struct cfusbl, GFP_ATOMIC); - - if (!this) - return NULL; - - caif_assert(offsetof(struct cfusbl, layer) == 0); - - memset(&this->layer, 0, sizeof(this->layer)); - this->layer.receive = cfusbl_receive; - this->layer.transmit = cfusbl_transmit; - this->layer.ctrlcmd = cfusbl_ctrlcmd; - snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "usb%d", phyid); - this->layer.id = phyid; - - /* - * Construct TX ethernet header: - * 0-5 destination address - * 5-11 source address - * 12-13 protocol type - */ - ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], braddr); - ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], ethaddr); - this->tx_eth_hdr[12] = cpu_to_be16(ETH_P_802_EX1) & 0xff; - this->tx_eth_hdr[13] = (cpu_to_be16(ETH_P_802_EX1) >> 8) & 0xff; - pr_debug("caif ethernet TX-header dst:%pM src:%pM type:%02x%02x\n", - this->tx_eth_hdr, this->tx_eth_hdr + ETH_ALEN, - this->tx_eth_hdr[12], this->tx_eth_hdr[13]); - - return (struct cflayer *) this; -} - -static void cfusbl_release(struct cflayer *layer) -{ - kfree(layer); -} - -static struct packet_type caif_usb_type __read_mostly = { - .type = cpu_to_be16(ETH_P_802_EX1), -}; - -static int cfusbl_device_notify(struct notifier_block *me, unsigned long what, - void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct caif_dev_common common; - struct cflayer *layer, *link_support; - struct usbnet *usbnet; - struct usb_device *usbdev; - int res; - - if (what == NETDEV_UNREGISTER && dev->reg_state >= NETREG_UNREGISTERED) - return 0; - - /* Check whether we have a NCM device, and find its VID/PID. */ - if (!(dev->dev.parent && dev->dev.parent->driver && - strcmp(dev->dev.parent->driver->name, "cdc_ncm") == 0)) - return 0; - - usbnet = netdev_priv(dev); - usbdev = usbnet->udev; - - pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n", - le16_to_cpu(usbdev->descriptor.idVendor), - le16_to_cpu(usbdev->descriptor.idProduct)); - - /* Check for VID/PID that supports CAIF */ - if (!(le16_to_cpu(usbdev->descriptor.idVendor) == STE_USB_VID && - le16_to_cpu(usbdev->descriptor.idProduct) == STE_USB_PID_CAIF)) - return 0; - - if (what == NETDEV_UNREGISTER) - module_put(THIS_MODULE); - - if (what != NETDEV_REGISTER) - return 0; - - __module_get(THIS_MODULE); - - memset(&common, 0, sizeof(common)); - common.use_frag = false; - common.use_fcs = false; - common.use_stx = false; - common.link_select = CAIF_LINK_HIGH_BANDW; - common.flowctrl = NULL; - - link_support = cfusbl_create(dev->ifindex, dev->dev_addr, - dev->broadcast); - - if (!link_support) - return -ENOMEM; - - if (dev->num_tx_queues > 1) - pr_warn("USB device uses more than one tx queue\n"); - - res = caif_enroll_dev(dev, &common, link_support, CFUSB_MAX_HEADLEN, - &layer, &caif_usb_type.func); - if (res) - goto err; - - if (!pack_added) - dev_add_pack(&caif_usb_type); - pack_added = true; - - strscpy(layer->name, dev->name, sizeof(layer->name)); - - return 0; -err: - cfusbl_release(link_support); - return res; -} - -static struct notifier_block caif_device_notifier = { - .notifier_call = cfusbl_device_notify, - .priority = 0, -}; - -static int __init cfusbl_init(void) -{ - return register_netdevice_notifier(&caif_device_notifier); -} - -static void __exit cfusbl_exit(void) -{ - unregister_netdevice_notifier(&caif_device_notifier); - dev_remove_pack(&caif_usb_type); -} - -module_init(cfusbl_init); -module_exit(cfusbl_exit); diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c deleted file mode 100644 index 8a80914783e8..000000000000 --- a/net/caif/cfcnfg.c +++ /dev/null @@ -1,612 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) container_of(layr, struct cfcnfg, layer) - -/* Information about CAIF physical interfaces held by Config Module in order - * to manage physical interfaces - */ -struct cfcnfg_phyinfo { - struct list_head node; - bool up; - - /* Pointer to the layer below the MUX (framing layer) */ - struct cflayer *frm_layer; - /* Pointer to the lowest actual physical layer */ - struct cflayer *phy_layer; - /* Unique identifier of the physical interface */ - unsigned int id; - /* Preference of the physical in interface */ - enum cfcnfg_phy_preference pref; - - /* Information about the physical device */ - struct dev_info dev_info; - - /* Interface index */ - int ifindex; - - /* Protocol head room added for CAIF link layer */ - int head_room; - - /* Use Start of frame checksum */ - bool use_fcs; -}; - -struct cfcnfg { - struct cflayer layer; - struct cflayer *ctrl; - struct cflayer *mux; - struct list_head phys; - struct mutex lock; -}; - -static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, - enum cfctrl_srv serv, u8 phyid, - struct cflayer *adapt_layer); -static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id); -static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, - struct cflayer *adapt_layer); -static void cfctrl_resp_func(void); -static void cfctrl_enum_resp(void); - -struct cfcnfg *cfcnfg_create(void) -{ - struct cfcnfg *this; - struct cfctrl_rsp *resp; - - might_sleep(); - - /* Initiate this layer */ - this = kzalloc_obj(struct cfcnfg, GFP_ATOMIC); - if (!this) - return NULL; - this->mux = cfmuxl_create(); - if (!this->mux) - goto out_of_mem; - this->ctrl = cfctrl_create(); - if (!this->ctrl) - goto out_of_mem; - /* Initiate response functions */ - resp = cfctrl_get_respfuncs(this->ctrl); - resp->enum_rsp = cfctrl_enum_resp; - resp->linkerror_ind = cfctrl_resp_func; - resp->linkdestroy_rsp = cfcnfg_linkdestroy_rsp; - resp->sleep_rsp = cfctrl_resp_func; - resp->wake_rsp = cfctrl_resp_func; - resp->restart_rsp = cfctrl_resp_func; - resp->radioset_rsp = cfctrl_resp_func; - resp->linksetup_rsp = cfcnfg_linkup_rsp; - resp->reject_rsp = cfcnfg_reject_rsp; - INIT_LIST_HEAD(&this->phys); - - cfmuxl_set_uplayer(this->mux, this->ctrl, 0); - layer_set_dn(this->ctrl, this->mux); - layer_set_up(this->ctrl, this); - mutex_init(&this->lock); - - return this; -out_of_mem: - synchronize_rcu(); - - kfree(this->mux); - kfree(this->ctrl); - kfree(this); - return NULL; -} - -void cfcnfg_remove(struct cfcnfg *cfg) -{ - might_sleep(); - if (cfg) { - synchronize_rcu(); - - kfree(cfg->mux); - cfctrl_remove(cfg->ctrl); - kfree(cfg); - } -} - -static void cfctrl_resp_func(void) -{ -} - -static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg, - u8 phyid) -{ - struct cfcnfg_phyinfo *phy; - - list_for_each_entry_rcu(phy, &cnfg->phys, node) - if (phy->id == phyid) - return phy; - return NULL; -} - -static void cfctrl_enum_resp(void) -{ -} - -static struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg, - enum cfcnfg_phy_preference phy_pref) -{ - /* Try to match with specified preference */ - struct cfcnfg_phyinfo *phy; - - list_for_each_entry_rcu(phy, &cnfg->phys, node) { - if (phy->up && phy->pref == phy_pref && - phy->frm_layer != NULL) - - return &phy->dev_info; - } - - /* Otherwise just return something */ - list_for_each_entry_rcu(phy, &cnfg->phys, node) - if (phy->up) - return &phy->dev_info; - - return NULL; -} - -static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) -{ - struct cfcnfg_phyinfo *phy; - - list_for_each_entry_rcu(phy, &cnfg->phys, node) - if (phy->ifindex == ifi && phy->up) - return phy->id; - return -ENODEV; -} - -int caif_disconnect_client(struct net *net, struct cflayer *adap_layer) -{ - u8 channel_id; - struct cfcnfg *cfg = get_cfcnfg(net); - - caif_assert(adap_layer != NULL); - cfctrl_cancel_req(cfg->ctrl, adap_layer); - channel_id = adap_layer->id; - if (channel_id != 0) { - struct cflayer *servl; - servl = cfmuxl_remove_uplayer(cfg->mux, channel_id); - cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer); - if (servl != NULL) - layer_set_up(servl, NULL); - } else - pr_debug("nothing to disconnect\n"); - - /* Do RCU sync before initiating cleanup */ - synchronize_rcu(); - if (adap_layer->ctrlcmd != NULL) - adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); - return 0; - -} -EXPORT_SYMBOL(caif_disconnect_client); - -static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id) -{ -} - -static const int protohead[CFCTRL_SRV_MASK] = { - [CFCTRL_SRV_VEI] = 4, - [CFCTRL_SRV_DATAGRAM] = 7, - [CFCTRL_SRV_UTIL] = 4, - [CFCTRL_SRV_RFM] = 3, - [CFCTRL_SRV_DBG] = 3, -}; - - -static int caif_connect_req_to_link_param(struct cfcnfg *cnfg, - struct caif_connect_request *s, - struct cfctrl_link_param *l) -{ - struct dev_info *dev_info; - enum cfcnfg_phy_preference pref; - int res; - - memset(l, 0, sizeof(*l)); - /* In caif protocol low value is high priority */ - l->priority = CAIF_PRIO_MAX - s->priority + 1; - - if (s->ifindex != 0) { - res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex); - if (res < 0) - return res; - l->phyid = res; - } else { - switch (s->link_selector) { - case CAIF_LINK_HIGH_BANDW: - pref = CFPHYPREF_HIGH_BW; - break; - case CAIF_LINK_LOW_LATENCY: - pref = CFPHYPREF_LOW_LAT; - break; - default: - return -EINVAL; - } - dev_info = cfcnfg_get_phyid(cnfg, pref); - if (dev_info == NULL) - return -ENODEV; - l->phyid = dev_info->id; - } - switch (s->protocol) { - case CAIFPROTO_AT: - l->linktype = CFCTRL_SRV_VEI; - l->endpoint = (s->sockaddr.u.at.type >> 2) & 0x3; - l->chtype = s->sockaddr.u.at.type & 0x3; - break; - case CAIFPROTO_DATAGRAM: - l->linktype = CFCTRL_SRV_DATAGRAM; - l->chtype = 0x00; - l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; - break; - case CAIFPROTO_DATAGRAM_LOOP: - l->linktype = CFCTRL_SRV_DATAGRAM; - l->chtype = 0x03; - l->endpoint = 0x00; - l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; - break; - case CAIFPROTO_RFM: - l->linktype = CFCTRL_SRV_RFM; - l->u.datagram.connid = s->sockaddr.u.rfm.connection_id; - strscpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume, - sizeof(l->u.rfm.volume)); - break; - case CAIFPROTO_UTIL: - l->linktype = CFCTRL_SRV_UTIL; - l->endpoint = 0x00; - l->chtype = 0x00; - strscpy(l->u.utility.name, s->sockaddr.u.util.service, - sizeof(l->u.utility.name)); - caif_assert(sizeof(l->u.utility.name) > 10); - l->u.utility.paramlen = s->param.size; - if (l->u.utility.paramlen > sizeof(l->u.utility.params)) - l->u.utility.paramlen = sizeof(l->u.utility.params); - - memcpy(l->u.utility.params, s->param.data, - l->u.utility.paramlen); - - break; - case CAIFPROTO_DEBUG: - l->linktype = CFCTRL_SRV_DBG; - l->endpoint = s->sockaddr.u.dbg.service; - l->chtype = s->sockaddr.u.dbg.type; - break; - default: - return -EINVAL; - } - return 0; -} - -int caif_connect_client(struct net *net, struct caif_connect_request *conn_req, - struct cflayer *adap_layer, int *ifindex, - int *proto_head, int *proto_tail) -{ - struct cflayer *frml; - struct cfcnfg_phyinfo *phy; - int err; - struct cfctrl_link_param param; - struct cfcnfg *cfg = get_cfcnfg(net); - - rcu_read_lock(); - err = caif_connect_req_to_link_param(cfg, conn_req, ¶m); - if (err) - goto unlock; - - phy = cfcnfg_get_phyinfo_rcu(cfg, param.phyid); - if (!phy) { - err = -ENODEV; - goto unlock; - } - err = -EINVAL; - - if (adap_layer == NULL) { - pr_err("adap_layer is zero\n"); - goto unlock; - } - if (adap_layer->receive == NULL) { - pr_err("adap_layer->receive is NULL\n"); - goto unlock; - } - if (adap_layer->ctrlcmd == NULL) { - pr_err("adap_layer->ctrlcmd == NULL\n"); - goto unlock; - } - - err = -ENODEV; - frml = phy->frm_layer; - if (frml == NULL) { - pr_err("Specified PHY type does not exist!\n"); - goto unlock; - } - caif_assert(param.phyid == phy->id); - caif_assert(phy->frm_layer->id == - param.phyid); - caif_assert(phy->phy_layer->id == - param.phyid); - - *ifindex = phy->ifindex; - *proto_tail = 2; - *proto_head = protohead[param.linktype] + phy->head_room; - - rcu_read_unlock(); - - /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */ - cfctrl_enum_req(cfg->ctrl, param.phyid); - return cfctrl_linkup_request(cfg->ctrl, ¶m, adap_layer); - -unlock: - rcu_read_unlock(); - return err; -} -EXPORT_SYMBOL(caif_connect_client); - -static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, - struct cflayer *adapt_layer) -{ - if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) - adapt_layer->ctrlcmd(adapt_layer, - CAIF_CTRLCMD_INIT_FAIL_RSP, 0); -} - -static void -cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, - u8 phyid, struct cflayer *adapt_layer) -{ - struct cfcnfg *cnfg = container_obj(layer); - struct cflayer *servicel = NULL; - struct cfcnfg_phyinfo *phyinfo; - struct net_device *netdev; - - if (channel_id == 0) { - pr_warn("received channel_id zero\n"); - if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) - adapt_layer->ctrlcmd(adapt_layer, - CAIF_CTRLCMD_INIT_FAIL_RSP, 0); - return; - } - - rcu_read_lock(); - - if (adapt_layer == NULL) { - pr_debug("link setup response but no client exist, send linkdown back\n"); - cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); - goto unlock; - } - - caif_assert(cnfg != NULL); - caif_assert(phyid != 0); - - phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); - if (phyinfo == NULL) { - pr_err("ERROR: Link Layer Device disappeared while connecting\n"); - goto unlock; - } - - caif_assert(phyinfo != NULL); - caif_assert(phyinfo->id == phyid); - caif_assert(phyinfo->phy_layer != NULL); - caif_assert(phyinfo->phy_layer->id == phyid); - - adapt_layer->id = channel_id; - - switch (serv) { - case CFCTRL_SRV_VEI: - servicel = cfvei_create(channel_id, &phyinfo->dev_info); - break; - case CFCTRL_SRV_DATAGRAM: - servicel = cfdgml_create(channel_id, - &phyinfo->dev_info); - break; - case CFCTRL_SRV_RFM: - netdev = phyinfo->dev_info.dev; - servicel = cfrfml_create(channel_id, &phyinfo->dev_info, - netdev->mtu); - break; - case CFCTRL_SRV_UTIL: - servicel = cfutill_create(channel_id, &phyinfo->dev_info); - break; - case CFCTRL_SRV_VIDEO: - servicel = cfvidl_create(channel_id, &phyinfo->dev_info); - break; - case CFCTRL_SRV_DBG: - servicel = cfdbgl_create(channel_id, &phyinfo->dev_info); - break; - default: - pr_err("Protocol error. Link setup response - unknown channel type\n"); - goto unlock; - } - if (!servicel) - goto unlock; - layer_set_dn(servicel, cnfg->mux); - cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id); - layer_set_up(servicel, adapt_layer); - layer_set_dn(adapt_layer, servicel); - - rcu_read_unlock(); - - servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0); - return; -unlock: - rcu_read_unlock(); -} - -int -cfcnfg_add_phy_layer(struct cfcnfg *cnfg, - struct net_device *dev, struct cflayer *phy_layer, - enum cfcnfg_phy_preference pref, - struct cflayer *link_support, - bool fcs, int head_room) -{ - struct cflayer *frml; - struct cfcnfg_phyinfo *phyinfo = NULL; - int i, res = 0; - u8 phyid; - - mutex_lock(&cnfg->lock); - - /* CAIF protocol allow maximum 6 link-layers */ - for (i = 0; i < 7; i++) { - phyid = (dev->ifindex + i) & 0x7; - if (phyid == 0) - continue; - if (cfcnfg_get_phyinfo_rcu(cnfg, phyid) == NULL) - goto got_phyid; - } - pr_warn("Too many CAIF Link Layers (max 6)\n"); - res = -EEXIST; - goto out; - -got_phyid: - phyinfo = kzalloc_obj(struct cfcnfg_phyinfo, GFP_ATOMIC); - if (!phyinfo) { - res = -ENOMEM; - goto out; - } - - phy_layer->id = phyid; - phyinfo->pref = pref; - phyinfo->id = phyid; - phyinfo->dev_info.id = phyid; - phyinfo->dev_info.dev = dev; - phyinfo->phy_layer = phy_layer; - phyinfo->ifindex = dev->ifindex; - phyinfo->head_room = head_room; - phyinfo->use_fcs = fcs; - - frml = cffrml_create(phyid, fcs); - - if (!frml) { - res = -ENOMEM; - goto out_err; - } - phyinfo->frm_layer = frml; - layer_set_up(frml, cnfg->mux); - - if (link_support != NULL) { - link_support->id = phyid; - layer_set_dn(frml, link_support); - layer_set_up(link_support, frml); - layer_set_dn(link_support, phy_layer); - layer_set_up(phy_layer, link_support); - } else { - layer_set_dn(frml, phy_layer); - layer_set_up(phy_layer, frml); - } - - list_add_rcu(&phyinfo->node, &cnfg->phys); -out: - mutex_unlock(&cnfg->lock); - return res; - -out_err: - kfree(phyinfo); - mutex_unlock(&cnfg->lock); - return res; -} -EXPORT_SYMBOL(cfcnfg_add_phy_layer); - -int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer, - bool up) -{ - struct cfcnfg_phyinfo *phyinfo; - - rcu_read_lock(); - phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phy_layer->id); - if (phyinfo == NULL) { - rcu_read_unlock(); - return -ENODEV; - } - - if (phyinfo->up == up) { - rcu_read_unlock(); - return 0; - } - phyinfo->up = up; - - if (up) { - cffrml_hold(phyinfo->frm_layer); - cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer, - phy_layer->id); - } else { - cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); - cffrml_put(phyinfo->frm_layer); - } - - rcu_read_unlock(); - return 0; -} -EXPORT_SYMBOL(cfcnfg_set_phy_state); - -int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer) -{ - struct cflayer *frml, *frml_dn; - u16 phyid; - struct cfcnfg_phyinfo *phyinfo; - - might_sleep(); - - mutex_lock(&cnfg->lock); - - phyid = phy_layer->id; - phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); - - if (phyinfo == NULL) { - mutex_unlock(&cnfg->lock); - return 0; - } - caif_assert(phyid == phyinfo->id); - caif_assert(phy_layer == phyinfo->phy_layer); - caif_assert(phy_layer->id == phyid); - caif_assert(phyinfo->frm_layer->id == phyid); - - list_del_rcu(&phyinfo->node); - synchronize_rcu(); - - /* Fail if reference count is not zero */ - if (cffrml_refcnt_read(phyinfo->frm_layer) != 0) { - pr_info("Wait for device inuse\n"); - list_add_rcu(&phyinfo->node, &cnfg->phys); - mutex_unlock(&cnfg->lock); - return -EAGAIN; - } - - frml = phyinfo->frm_layer; - frml_dn = frml->dn; - cffrml_set_uplayer(frml, NULL); - cffrml_set_dnlayer(frml, NULL); - if (phy_layer != frml_dn) { - layer_set_up(frml_dn, NULL); - layer_set_dn(frml_dn, NULL); - } - layer_set_up(phy_layer, NULL); - - if (phyinfo->phy_layer != frml_dn) - kfree(frml_dn); - - cffrml_free(frml); - kfree(phyinfo); - mutex_unlock(&cnfg->lock); - - return 0; -} -EXPORT_SYMBOL(cfcnfg_del_phy_layer); diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c deleted file mode 100644 index c6cc2bfed65d..000000000000 --- a/net/caif/cfctrl.c +++ /dev/null @@ -1,631 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) container_of(layr, struct cfctrl, serv.layer) -#define UTILITY_NAME_LENGTH 16 -#define CFPKT_CTRL_PKT_LEN 20 - -#ifdef CAIF_NO_LOOP -static int handle_loop(struct cfctrl *ctrl, - int cmd, struct cfpkt *pkt){ - return -1; -} -#else -static int handle_loop(struct cfctrl *ctrl, - int cmd, struct cfpkt *pkt); -#endif -static int cfctrl_recv(struct cflayer *layr, struct cfpkt *pkt); -static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid); - - -struct cflayer *cfctrl_create(void) -{ - struct dev_info dev_info; - struct cfctrl *this = - kzalloc_obj(struct cfctrl, GFP_ATOMIC); - if (!this) - return NULL; - caif_assert(offsetof(struct cfctrl, serv.layer) == 0); - memset(&dev_info, 0, sizeof(dev_info)); - dev_info.id = 0xff; - cfsrvl_init(&this->serv, 0, &dev_info, false); - atomic_set(&this->req_seq_no, 1); - atomic_set(&this->rsp_seq_no, 1); - this->serv.layer.receive = cfctrl_recv; - sprintf(this->serv.layer.name, "ctrl"); - this->serv.layer.ctrlcmd = cfctrl_ctrlcmd; -#ifndef CAIF_NO_LOOP - spin_lock_init(&this->loop_linkid_lock); - this->loop_linkid = 1; -#endif - spin_lock_init(&this->info_list_lock); - INIT_LIST_HEAD(&this->list); - return &this->serv.layer; -} - -void cfctrl_remove(struct cflayer *layer) -{ - struct cfctrl_request_info *p, *tmp; - struct cfctrl *ctrl = container_obj(layer); - - spin_lock_bh(&ctrl->info_list_lock); - list_for_each_entry_safe(p, tmp, &ctrl->list, list) { - list_del(&p->list); - kfree(p); - } - spin_unlock_bh(&ctrl->info_list_lock); - kfree(layer); -} - -static bool param_eq(const struct cfctrl_link_param *p1, - const struct cfctrl_link_param *p2) -{ - bool eq = - p1->linktype == p2->linktype && - p1->priority == p2->priority && - p1->phyid == p2->phyid && - p1->endpoint == p2->endpoint && p1->chtype == p2->chtype; - - if (!eq) - return false; - - switch (p1->linktype) { - case CFCTRL_SRV_VEI: - return true; - case CFCTRL_SRV_DATAGRAM: - return p1->u.datagram.connid == p2->u.datagram.connid; - case CFCTRL_SRV_RFM: - return - p1->u.rfm.connid == p2->u.rfm.connid && - strcmp(p1->u.rfm.volume, p2->u.rfm.volume) == 0; - case CFCTRL_SRV_UTIL: - return - p1->u.utility.fifosize_kb == p2->u.utility.fifosize_kb - && p1->u.utility.fifosize_bufs == - p2->u.utility.fifosize_bufs - && strcmp(p1->u.utility.name, p2->u.utility.name) == 0 - && p1->u.utility.paramlen == p2->u.utility.paramlen - && memcmp(p1->u.utility.params, p2->u.utility.params, - p1->u.utility.paramlen) == 0; - - case CFCTRL_SRV_VIDEO: - return p1->u.video.connid == p2->u.video.connid; - case CFCTRL_SRV_DBG: - return true; - case CFCTRL_SRV_DECM: - return false; - default: - return false; - } - return false; -} - -static bool cfctrl_req_eq(const struct cfctrl_request_info *r1, - const struct cfctrl_request_info *r2) -{ - if (r1->cmd != r2->cmd) - return false; - if (r1->cmd == CFCTRL_CMD_LINK_SETUP) - return param_eq(&r1->param, &r2->param); - else - return r1->channel_id == r2->channel_id; -} - -/* Insert request at the end */ -static void cfctrl_insert_req(struct cfctrl *ctrl, - struct cfctrl_request_info *req) -{ - spin_lock_bh(&ctrl->info_list_lock); - atomic_inc(&ctrl->req_seq_no); - req->sequence_no = atomic_read(&ctrl->req_seq_no); - list_add_tail(&req->list, &ctrl->list); - spin_unlock_bh(&ctrl->info_list_lock); -} - -/* Compare and remove request */ -static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl, - struct cfctrl_request_info *req) -{ - struct cfctrl_request_info *p, *tmp, *first; - - first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list); - - list_for_each_entry_safe(p, tmp, &ctrl->list, list) { - if (cfctrl_req_eq(req, p)) { - if (p != first) - pr_warn("Requests are not received in order\n"); - - atomic_set(&ctrl->rsp_seq_no, - p->sequence_no); - list_del(&p->list); - goto out; - } - } - p = NULL; -out: - return p; -} - -struct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer) -{ - struct cfctrl *this = container_obj(layer); - return &this->res; -} - -static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl) -{ - info->hdr_len = 0; - info->channel_id = cfctrl->serv.layer.id; - info->dev_info = &cfctrl->serv.dev_info; -} - -void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid) -{ - struct cfpkt *pkt; - struct cfctrl *cfctrl = container_obj(layer); - struct cflayer *dn = cfctrl->serv.layer.dn; - - if (!dn) { - pr_debug("not able to send enum request\n"); - return; - } - pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); - if (!pkt) - return; - caif_assert(offsetof(struct cfctrl, serv.layer) == 0); - init_info(cfpkt_info(pkt), cfctrl); - cfpkt_info(pkt)->dev_info->id = physlinkid; - cfctrl->serv.dev_info.id = physlinkid; - cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM); - cfpkt_addbdy(pkt, physlinkid); - cfpkt_set_prio(pkt, TC_PRIO_CONTROL); - dn->transmit(dn, pkt); -} - -int cfctrl_linkup_request(struct cflayer *layer, - struct cfctrl_link_param *param, - struct cflayer *user_layer) -{ - struct cfctrl *cfctrl = container_obj(layer); - struct cflayer *dn = cfctrl->serv.layer.dn; - char utility_name[UTILITY_NAME_LENGTH]; - struct cfctrl_request_info *req; - struct cfpkt *pkt; - u32 tmp32; - u16 tmp16; - u8 tmp8; - int ret; - - if (!dn) { - pr_debug("not able to send linkup request\n"); - return -ENODEV; - } - - if (cfctrl_cancel_req(layer, user_layer) > 0) { - /* Slight Paranoia, check if already connecting */ - pr_err("Duplicate connect request for same client\n"); - WARN_ON(1); - return -EALREADY; - } - - pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); - if (!pkt) - return -ENOMEM; - cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP); - cfpkt_addbdy(pkt, (param->chtype << 4) | param->linktype); - cfpkt_addbdy(pkt, (param->priority << 3) | param->phyid); - cfpkt_addbdy(pkt, param->endpoint & 0x03); - - switch (param->linktype) { - case CFCTRL_SRV_VEI: - break; - case CFCTRL_SRV_VIDEO: - cfpkt_addbdy(pkt, (u8) param->u.video.connid); - break; - case CFCTRL_SRV_DBG: - break; - case CFCTRL_SRV_DATAGRAM: - tmp32 = cpu_to_le32(param->u.datagram.connid); - cfpkt_add_body(pkt, &tmp32, 4); - break; - case CFCTRL_SRV_RFM: - /* Construct a frame, convert DatagramConnectionID to network - * format long and copy it out... - */ - tmp32 = cpu_to_le32(param->u.rfm.connid); - cfpkt_add_body(pkt, &tmp32, 4); - /* Add volume name, including zero termination... */ - cfpkt_add_body(pkt, param->u.rfm.volume, - strlen(param->u.rfm.volume) + 1); - break; - case CFCTRL_SRV_UTIL: - tmp16 = cpu_to_le16(param->u.utility.fifosize_kb); - cfpkt_add_body(pkt, &tmp16, 2); - tmp16 = cpu_to_le16(param->u.utility.fifosize_bufs); - cfpkt_add_body(pkt, &tmp16, 2); - strscpy_pad(utility_name, param->u.utility.name); - cfpkt_add_body(pkt, utility_name, UTILITY_NAME_LENGTH); - tmp8 = param->u.utility.paramlen; - cfpkt_add_body(pkt, &tmp8, 1); - cfpkt_add_body(pkt, param->u.utility.params, - param->u.utility.paramlen); - break; - default: - pr_warn("Request setup of bad link type = %d\n", - param->linktype); - cfpkt_destroy(pkt); - return -EINVAL; - } - req = kzalloc_obj(*req); - if (!req) { - cfpkt_destroy(pkt); - return -ENOMEM; - } - - req->client_layer = user_layer; - req->cmd = CFCTRL_CMD_LINK_SETUP; - req->param = *param; - cfctrl_insert_req(cfctrl, req); - init_info(cfpkt_info(pkt), cfctrl); - /* - * NOTE:Always send linkup and linkdown request on the same - * device as the payload. Otherwise old queued up payload - * might arrive with the newly allocated channel ID. - */ - cfpkt_info(pkt)->dev_info->id = param->phyid; - cfpkt_set_prio(pkt, TC_PRIO_CONTROL); - ret = - dn->transmit(dn, pkt); - if (ret < 0) { - int count; - - count = cfctrl_cancel_req(&cfctrl->serv.layer, - user_layer); - if (count != 1) { - pr_err("Could not remove request (%d)", count); - return -ENODEV; - } - } - return 0; -} - -int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid, - struct cflayer *client) -{ - int ret; - struct cfpkt *pkt; - struct cfctrl *cfctrl = container_obj(layer); - struct cflayer *dn = cfctrl->serv.layer.dn; - - if (!dn) { - pr_debug("not able to send link-down request\n"); - return -ENODEV; - } - pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); - if (!pkt) - return -ENOMEM; - cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY); - cfpkt_addbdy(pkt, channelid); - init_info(cfpkt_info(pkt), cfctrl); - cfpkt_set_prio(pkt, TC_PRIO_CONTROL); - ret = - dn->transmit(dn, pkt); -#ifndef CAIF_NO_LOOP - cfctrl->loop_linkused[channelid] = 0; -#endif - return ret; -} - -int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer) -{ - struct cfctrl_request_info *p, *tmp; - struct cfctrl *ctrl = container_obj(layr); - int found = 0; - spin_lock_bh(&ctrl->info_list_lock); - - list_for_each_entry_safe(p, tmp, &ctrl->list, list) { - if (p->client_layer == adap_layer) { - list_del(&p->list); - kfree(p); - found++; - } - } - - spin_unlock_bh(&ctrl->info_list_lock); - return found; -} - -static int cfctrl_link_setup(struct cfctrl *cfctrl, struct cfpkt *pkt, u8 cmdrsp) -{ - u8 len; - u8 linkid = 0; - enum cfctrl_srv serv; - enum cfctrl_srv servtype; - u8 endpoint; - u8 physlinkid; - u8 prio; - u8 tmp; - u8 *cp; - int i; - struct cfctrl_link_param linkparam; - struct cfctrl_request_info rsp, *req; - - memset(&linkparam, 0, sizeof(linkparam)); - - tmp = cfpkt_extr_head_u8(pkt); - - serv = tmp & CFCTRL_SRV_MASK; - linkparam.linktype = serv; - - servtype = tmp >> 4; - linkparam.chtype = servtype; - - tmp = cfpkt_extr_head_u8(pkt); - physlinkid = tmp & 0x07; - prio = tmp >> 3; - - linkparam.priority = prio; - linkparam.phyid = physlinkid; - endpoint = cfpkt_extr_head_u8(pkt); - linkparam.endpoint = endpoint & 0x03; - - switch (serv) { - case CFCTRL_SRV_VEI: - case CFCTRL_SRV_DBG: - if (CFCTRL_ERR_BIT & cmdrsp) - break; - /* Link ID */ - linkid = cfpkt_extr_head_u8(pkt); - break; - case CFCTRL_SRV_VIDEO: - tmp = cfpkt_extr_head_u8(pkt); - linkparam.u.video.connid = tmp; - if (CFCTRL_ERR_BIT & cmdrsp) - break; - /* Link ID */ - linkid = cfpkt_extr_head_u8(pkt); - break; - - case CFCTRL_SRV_DATAGRAM: - linkparam.u.datagram.connid = cfpkt_extr_head_u32(pkt); - if (CFCTRL_ERR_BIT & cmdrsp) - break; - /* Link ID */ - linkid = cfpkt_extr_head_u8(pkt); - break; - case CFCTRL_SRV_RFM: - /* Construct a frame, convert - * DatagramConnectionID - * to network format long and copy it out... - */ - linkparam.u.rfm.connid = cfpkt_extr_head_u32(pkt); - cp = (u8 *) linkparam.u.rfm.volume; - for (tmp = cfpkt_extr_head_u8(pkt); - cfpkt_more(pkt) && tmp != '\0'; - tmp = cfpkt_extr_head_u8(pkt)) - *cp++ = tmp; - *cp = '\0'; - - if (CFCTRL_ERR_BIT & cmdrsp) - break; - /* Link ID */ - linkid = cfpkt_extr_head_u8(pkt); - - break; - case CFCTRL_SRV_UTIL: - /* Construct a frame, convert - * DatagramConnectionID - * to network format long and copy it out... - */ - /* Fifosize KB */ - linkparam.u.utility.fifosize_kb = cfpkt_extr_head_u16(pkt); - /* Fifosize bufs */ - linkparam.u.utility.fifosize_bufs = cfpkt_extr_head_u16(pkt); - /* name */ - cp = (u8 *) linkparam.u.utility.name; - caif_assert(sizeof(linkparam.u.utility.name) - >= UTILITY_NAME_LENGTH); - for (i = 0; i < UTILITY_NAME_LENGTH && cfpkt_more(pkt); i++) { - tmp = cfpkt_extr_head_u8(pkt); - *cp++ = tmp; - } - /* Length */ - len = cfpkt_extr_head_u8(pkt); - linkparam.u.utility.paramlen = len; - /* Param Data */ - cp = linkparam.u.utility.params; - while (cfpkt_more(pkt) && len--) { - tmp = cfpkt_extr_head_u8(pkt); - *cp++ = tmp; - } - if (CFCTRL_ERR_BIT & cmdrsp) - break; - /* Link ID */ - linkid = cfpkt_extr_head_u8(pkt); - /* Length */ - len = cfpkt_extr_head_u8(pkt); - /* Param Data */ - cfpkt_extr_head(pkt, NULL, len); - break; - default: - pr_warn("Request setup, invalid type (%d)\n", serv); - return -1; - } - - rsp.cmd = CFCTRL_CMD_LINK_SETUP; - rsp.param = linkparam; - spin_lock_bh(&cfctrl->info_list_lock); - req = cfctrl_remove_req(cfctrl, &rsp); - - if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) || - cfpkt_erroneous(pkt)) { - pr_err("Invalid O/E bit or parse error " - "on CAIF control channel\n"); - cfctrl->res.reject_rsp(cfctrl->serv.layer.up, 0, - req ? req->client_layer : NULL); - } else { - cfctrl->res.linksetup_rsp(cfctrl->serv.layer.up, linkid, - serv, physlinkid, - req ? req->client_layer : NULL); - } - - kfree(req); - - spin_unlock_bh(&cfctrl->info_list_lock); - - return 0; -} - -static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) -{ - u8 cmdrsp; - u8 cmd; - int ret = 0; - u8 linkid = 0; - struct cfctrl *cfctrl = container_obj(layer); - - cmdrsp = cfpkt_extr_head_u8(pkt); - cmd = cmdrsp & CFCTRL_CMD_MASK; - if (cmd != CFCTRL_CMD_LINK_ERR - && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp) - && CFCTRL_ERR_BIT != (CFCTRL_ERR_BIT & cmdrsp)) { - if (handle_loop(cfctrl, cmd, pkt) != 0) - cmdrsp |= CFCTRL_ERR_BIT; - } - - switch (cmd) { - case CFCTRL_CMD_LINK_SETUP: - ret = cfctrl_link_setup(cfctrl, pkt, cmdrsp); - break; - case CFCTRL_CMD_LINK_DESTROY: - linkid = cfpkt_extr_head_u8(pkt); - cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid); - break; - case CFCTRL_CMD_LINK_ERR: - pr_err("Frame Error Indication received\n"); - cfctrl->res.linkerror_ind(); - break; - case CFCTRL_CMD_ENUM: - cfctrl->res.enum_rsp(); - break; - case CFCTRL_CMD_SLEEP: - cfctrl->res.sleep_rsp(); - break; - case CFCTRL_CMD_WAKE: - cfctrl->res.wake_rsp(); - break; - case CFCTRL_CMD_LINK_RECONF: - cfctrl->res.restart_rsp(); - break; - case CFCTRL_CMD_RADIO_SET: - cfctrl->res.radioset_rsp(); - break; - default: - pr_err("Unrecognized Control Frame\n"); - ret = -1; - goto error; - } -error: - cfpkt_destroy(pkt); - return ret; -} - -static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - struct cfctrl *this = container_obj(layr); - switch (ctrl) { - case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: - case CAIF_CTRLCMD_FLOW_OFF_IND: - spin_lock_bh(&this->info_list_lock); - if (!list_empty(&this->list)) - pr_debug("Received flow off in control layer\n"); - spin_unlock_bh(&this->info_list_lock); - break; - case _CAIF_CTRLCMD_PHYIF_DOWN_IND: { - struct cfctrl_request_info *p, *tmp; - - /* Find all connect request and report failure */ - spin_lock_bh(&this->info_list_lock); - list_for_each_entry_safe(p, tmp, &this->list, list) { - if (p->param.phyid == phyid) { - list_del(&p->list); - p->client_layer->ctrlcmd(p->client_layer, - CAIF_CTRLCMD_INIT_FAIL_RSP, - phyid); - kfree(p); - } - } - spin_unlock_bh(&this->info_list_lock); - break; - } - default: - break; - } -} - -#ifndef CAIF_NO_LOOP -static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt) -{ - static int last_linkid; - static int dec; - u8 linkid, linktype, tmp; - switch (cmd) { - case CFCTRL_CMD_LINK_SETUP: - spin_lock_bh(&ctrl->loop_linkid_lock); - if (!dec) { - for (linkid = last_linkid + 1; linkid < 254; linkid++) - if (!ctrl->loop_linkused[linkid]) - goto found; - } - dec = 1; - for (linkid = last_linkid - 1; linkid > 1; linkid--) - if (!ctrl->loop_linkused[linkid]) - goto found; - spin_unlock_bh(&ctrl->loop_linkid_lock); - return -1; -found: - if (linkid < 10) - dec = 0; - - if (!ctrl->loop_linkused[linkid]) - ctrl->loop_linkused[linkid] = 1; - - last_linkid = linkid; - - cfpkt_add_trail(pkt, &linkid, 1); - spin_unlock_bh(&ctrl->loop_linkid_lock); - cfpkt_peek_head(pkt, &linktype, 1); - if (linktype == CFCTRL_SRV_UTIL) { - tmp = 0x01; - cfpkt_add_trail(pkt, &tmp, 1); - cfpkt_add_trail(pkt, &tmp, 1); - } - break; - - case CFCTRL_CMD_LINK_DESTROY: - spin_lock_bh(&ctrl->loop_linkid_lock); - cfpkt_peek_head(pkt, &linkid, 1); - ctrl->loop_linkused[linkid] = 0; - spin_unlock_bh(&ctrl->loop_linkid_lock); - break; - default: - break; - } - return 0; -} -#endif diff --git a/net/caif/cfdbgl.c b/net/caif/cfdbgl.c deleted file mode 100644 index 57ad3f82e004..000000000000 --- a/net/caif/cfdbgl.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include - -#define container_obj(layr) ((struct cfsrvl *) layr) - -static int cfdbgl_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cflayer *cfdbgl_create(u8 channel_id, struct dev_info *dev_info) -{ - struct cfsrvl *dbg = kzalloc_obj(struct cfsrvl, GFP_ATOMIC); - if (!dbg) - return NULL; - caif_assert(offsetof(struct cfsrvl, layer) == 0); - cfsrvl_init(dbg, channel_id, dev_info, false); - dbg->layer.receive = cfdbgl_receive; - dbg->layer.transmit = cfdbgl_transmit; - snprintf(dbg->layer.name, CAIF_LAYER_NAME_SZ, "dbg%d", channel_id); - return &dbg->layer; -} - -static int cfdbgl_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - return layr->up->receive(layr->up, pkt); -} - -static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - struct cfsrvl *service = container_obj(layr); - struct caif_payload_info *info; - int ret; - - if (!cfsrvl_ready(service, &ret)) { - cfpkt_destroy(pkt); - return ret; - } - - /* Add info for MUX-layer to route the packet out */ - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - info->dev_info = &service->dev_info; - - return layr->dn->transmit(layr->dn, pkt); -} diff --git a/net/caif/cfdgml.c b/net/caif/cfdgml.c deleted file mode 100644 index c451ddd155a7..000000000000 --- a/net/caif/cfdgml.c +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include - - -#define container_obj(layr) ((struct cfsrvl *) layr) - -#define DGM_CMD_BIT 0x80 -#define DGM_FLOW_OFF 0x81 -#define DGM_FLOW_ON 0x80 -#define DGM_MTU 1500 - -static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cflayer *cfdgml_create(u8 channel_id, struct dev_info *dev_info) -{ - struct cfsrvl *dgm = kzalloc_obj(struct cfsrvl, GFP_ATOMIC); - if (!dgm) - return NULL; - caif_assert(offsetof(struct cfsrvl, layer) == 0); - cfsrvl_init(dgm, channel_id, dev_info, true); - dgm->layer.receive = cfdgml_receive; - dgm->layer.transmit = cfdgml_transmit; - snprintf(dgm->layer.name, CAIF_LAYER_NAME_SZ, "dgm%d", channel_id); - return &dgm->layer; -} - -static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 cmd = -1; - u8 dgmhdr[3]; - int ret; - caif_assert(layr->up != NULL); - caif_assert(layr->receive != NULL); - caif_assert(layr->ctrlcmd != NULL); - - if (cfpkt_extr_head(pkt, &cmd, 1) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - - if ((cmd & DGM_CMD_BIT) == 0) { - if (cfpkt_extr_head(pkt, &dgmhdr, 3) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - ret = layr->up->receive(layr->up, pkt); - return ret; - } - - switch (cmd) { - case DGM_FLOW_OFF: /* FLOW OFF */ - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0); - cfpkt_destroy(pkt); - return 0; - case DGM_FLOW_ON: /* FLOW ON */ - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0); - cfpkt_destroy(pkt); - return 0; - default: - cfpkt_destroy(pkt); - pr_info("Unknown datagram control %d (0x%x)\n", cmd, cmd); - return -EPROTO; - } -} - -static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 packet_type; - u32 zero = 0; - struct caif_payload_info *info; - struct cfsrvl *service = container_obj(layr); - int ret; - - if (!cfsrvl_ready(service, &ret)) { - cfpkt_destroy(pkt); - return ret; - } - - /* STE Modem cannot handle more than 1500 bytes datagrams */ - if (cfpkt_getlen(pkt) > DGM_MTU) { - cfpkt_destroy(pkt); - return -EMSGSIZE; - } - - cfpkt_add_head(pkt, &zero, 3); - packet_type = 0x08; /* B9 set - UNCLASSIFIED */ - cfpkt_add_head(pkt, &packet_type, 1); - - /* Add info for MUX-layer to route the packet out. */ - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - /* To optimize alignment, we add up the size of CAIF header - * before payload. - */ - info->hdr_len = 4; - info->dev_info = &service->dev_info; - return layr->dn->transmit(layr->dn, pkt); -} diff --git a/net/caif/cffrml.c b/net/caif/cffrml.c deleted file mode 100644 index 0f4979d89fcb..000000000000 --- a/net/caif/cffrml.c +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * CAIF Framing Layer. - * - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) container_of(layr, struct cffrml, layer) - -struct cffrml { - struct cflayer layer; - bool dofcs; /* !< FCS active */ - int __percpu *pcpu_refcnt; -}; - -static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt); -static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid); - -static u32 cffrml_rcv_error; -static u32 cffrml_rcv_checsum_error; -struct cflayer *cffrml_create(u16 phyid, bool use_fcs) -{ - struct cffrml *this = kzalloc_obj(struct cffrml, GFP_ATOMIC); - if (!this) - return NULL; - this->pcpu_refcnt = alloc_percpu(int); - if (this->pcpu_refcnt == NULL) { - kfree(this); - return NULL; - } - - caif_assert(offsetof(struct cffrml, layer) == 0); - - this->layer.receive = cffrml_receive; - this->layer.transmit = cffrml_transmit; - this->layer.ctrlcmd = cffrml_ctrlcmd; - snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "frm%d", phyid); - this->dofcs = use_fcs; - this->layer.id = phyid; - return (struct cflayer *) this; -} - -void cffrml_free(struct cflayer *layer) -{ - struct cffrml *this = container_obj(layer); - free_percpu(this->pcpu_refcnt); - kfree(layer); -} - -void cffrml_set_uplayer(struct cflayer *this, struct cflayer *up) -{ - this->up = up; -} - -void cffrml_set_dnlayer(struct cflayer *this, struct cflayer *dn) -{ - this->dn = dn; -} - -static u16 cffrml_checksum(u16 chks, void *buf, u16 len) -{ - /* FIXME: FCS should be moved to glue in order to use OS-Specific - * solutions - */ - return crc_ccitt(chks, buf, len); -} - -static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u16 tmp; - u16 len; - u16 hdrchks; - int pktchks; - struct cffrml *this; - this = container_obj(layr); - - cfpkt_extr_head(pkt, &tmp, 2); - len = le16_to_cpu(tmp); - - /* Subtract for FCS on length if FCS is not used. */ - if (!this->dofcs) { - if (len < 2) { - ++cffrml_rcv_error; - pr_err("Invalid frame length (%d)\n", len); - cfpkt_destroy(pkt); - return -EPROTO; - } - len -= 2; - } - - if (cfpkt_setlen(pkt, len) < 0) { - ++cffrml_rcv_error; - pr_err("Framing length error (%d)\n", len); - cfpkt_destroy(pkt); - return -EPROTO; - } - /* - * Don't do extract if FCS is false, rather do setlen - then we don't - * get a cache-miss. - */ - if (this->dofcs) { - cfpkt_extr_trail(pkt, &tmp, 2); - hdrchks = le16_to_cpu(tmp); - pktchks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff); - if (pktchks != hdrchks) { - cfpkt_add_trail(pkt, &tmp, 2); - ++cffrml_rcv_error; - ++cffrml_rcv_checsum_error; - pr_info("Frame checksum error (0x%x != 0x%x)\n", - hdrchks, pktchks); - return -EILSEQ; - } - } - if (cfpkt_erroneous(pkt)) { - ++cffrml_rcv_error; - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - - if (layr->up == NULL) { - pr_err("Layr up is missing!\n"); - cfpkt_destroy(pkt); - return -EINVAL; - } - - return layr->up->receive(layr->up, pkt); -} - -static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - u16 chks; - u16 len; - __le16 data; - - struct cffrml *this = container_obj(layr); - if (this->dofcs) { - chks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff); - data = cpu_to_le16(chks); - cfpkt_add_trail(pkt, &data, 2); - } else { - cfpkt_pad_trail(pkt, 2); - } - len = cfpkt_getlen(pkt); - data = cpu_to_le16(len); - cfpkt_add_head(pkt, &data, 2); - cfpkt_info(pkt)->hdr_len += 2; - if (cfpkt_erroneous(pkt)) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - - if (layr->dn == NULL) { - cfpkt_destroy(pkt); - return -ENODEV; - - } - return layr->dn->transmit(layr->dn, pkt); -} - -static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - if (layr->up && layr->up->ctrlcmd) - layr->up->ctrlcmd(layr->up, ctrl, layr->id); -} - -void cffrml_put(struct cflayer *layr) -{ - struct cffrml *this = container_obj(layr); - if (layr != NULL && this->pcpu_refcnt != NULL) - this_cpu_dec(*this->pcpu_refcnt); -} - -void cffrml_hold(struct cflayer *layr) -{ - struct cffrml *this = container_obj(layr); - if (layr != NULL && this->pcpu_refcnt != NULL) - this_cpu_inc(*this->pcpu_refcnt); -} - -int cffrml_refcnt_read(struct cflayer *layr) -{ - int i, refcnt = 0; - struct cffrml *this = container_obj(layr); - for_each_possible_cpu(i) - refcnt += *per_cpu_ptr(this->pcpu_refcnt, i); - return refcnt; -} diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c deleted file mode 100644 index 77a1f31639b7..000000000000 --- a/net/caif/cfmuxl.c +++ /dev/null @@ -1,267 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) container_of(layr, struct cfmuxl, layer) - -#define CAIF_CTRL_CHANNEL 0 -#define UP_CACHE_SIZE 8 -#define DN_CACHE_SIZE 8 - -struct cfmuxl { - struct cflayer layer; - struct list_head srvl_list; - struct list_head frml_list; - struct cflayer *up_cache[UP_CACHE_SIZE]; - struct cflayer *dn_cache[DN_CACHE_SIZE]; - /* - * Set when inserting or removing downwards layers. - */ - spinlock_t transmit_lock; - - /* - * Set when inserting or removing upwards layers. - */ - spinlock_t receive_lock; - -}; - -static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt); -static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid); -static struct cflayer *get_up(struct cfmuxl *muxl, u16 id); - -struct cflayer *cfmuxl_create(void) -{ - struct cfmuxl *this = kzalloc_obj(struct cfmuxl, GFP_ATOMIC); - - if (!this) - return NULL; - this->layer.receive = cfmuxl_receive; - this->layer.transmit = cfmuxl_transmit; - this->layer.ctrlcmd = cfmuxl_ctrlcmd; - INIT_LIST_HEAD(&this->srvl_list); - INIT_LIST_HEAD(&this->frml_list); - spin_lock_init(&this->transmit_lock); - spin_lock_init(&this->receive_lock); - snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "mux"); - return &this->layer; -} - -int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid) -{ - struct cfmuxl *muxl = (struct cfmuxl *) layr; - - spin_lock_bh(&muxl->transmit_lock); - list_add_rcu(&dn->node, &muxl->frml_list); - spin_unlock_bh(&muxl->transmit_lock); - return 0; -} - -static struct cflayer *get_from_id(struct list_head *list, u16 id) -{ - struct cflayer *lyr; - list_for_each_entry_rcu(lyr, list, node) { - if (lyr->id == id) - return lyr; - } - - return NULL; -} - -int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid) -{ - struct cfmuxl *muxl = container_obj(layr); - struct cflayer *old; - - spin_lock_bh(&muxl->receive_lock); - - /* Two entries with same id is wrong, so remove old layer from mux */ - old = get_from_id(&muxl->srvl_list, linkid); - if (old != NULL) - list_del_rcu(&old->node); - - list_add_rcu(&up->node, &muxl->srvl_list); - spin_unlock_bh(&muxl->receive_lock); - - return 0; -} - -struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid) -{ - struct cfmuxl *muxl = container_obj(layr); - struct cflayer *dn; - int idx = phyid % DN_CACHE_SIZE; - - spin_lock_bh(&muxl->transmit_lock); - RCU_INIT_POINTER(muxl->dn_cache[idx], NULL); - dn = get_from_id(&muxl->frml_list, phyid); - if (dn == NULL) - goto out; - - list_del_rcu(&dn->node); - caif_assert(dn != NULL); -out: - spin_unlock_bh(&muxl->transmit_lock); - return dn; -} - -static struct cflayer *get_up(struct cfmuxl *muxl, u16 id) -{ - struct cflayer *up; - int idx = id % UP_CACHE_SIZE; - up = rcu_dereference(muxl->up_cache[idx]); - if (up == NULL || up->id != id) { - spin_lock_bh(&muxl->receive_lock); - up = get_from_id(&muxl->srvl_list, id); - rcu_assign_pointer(muxl->up_cache[idx], up); - spin_unlock_bh(&muxl->receive_lock); - } - return up; -} - -static struct cflayer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info) -{ - struct cflayer *dn; - int idx = dev_info->id % DN_CACHE_SIZE; - dn = rcu_dereference(muxl->dn_cache[idx]); - if (dn == NULL || dn->id != dev_info->id) { - spin_lock_bh(&muxl->transmit_lock); - dn = get_from_id(&muxl->frml_list, dev_info->id); - rcu_assign_pointer(muxl->dn_cache[idx], dn); - spin_unlock_bh(&muxl->transmit_lock); - } - return dn; -} - -struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id) -{ - struct cflayer *up; - struct cfmuxl *muxl = container_obj(layr); - int idx = id % UP_CACHE_SIZE; - - if (id == 0) { - pr_warn("Trying to remove control layer\n"); - return NULL; - } - - spin_lock_bh(&muxl->receive_lock); - up = get_from_id(&muxl->srvl_list, id); - if (up == NULL) - goto out; - - RCU_INIT_POINTER(muxl->up_cache[idx], NULL); - list_del_rcu(&up->node); -out: - spin_unlock_bh(&muxl->receive_lock); - return up; -} - -static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - int ret; - struct cfmuxl *muxl = container_obj(layr); - u8 id; - struct cflayer *up; - if (cfpkt_extr_head(pkt, &id, 1) < 0) { - pr_err("erroneous Caif Packet\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - rcu_read_lock(); - up = get_up(muxl, id); - - if (up == NULL) { - pr_debug("Received data on unknown link ID = %d (0x%x)" - " up == NULL", id, id); - cfpkt_destroy(pkt); - /* - * Don't return ERROR, since modem misbehaves and sends out - * flow on before linksetup response. - */ - - rcu_read_unlock(); - return /* CFGLU_EPROT; */ 0; - } - - /* We can't hold rcu_lock during receive, so take a ref count instead */ - cfsrvl_get(up); - rcu_read_unlock(); - - ret = up->receive(up, pkt); - - cfsrvl_put(up); - return ret; -} - -static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - struct cfmuxl *muxl = container_obj(layr); - int err; - u8 linkid; - struct cflayer *dn; - struct caif_payload_info *info = cfpkt_info(pkt); - BUG_ON(!info); - - rcu_read_lock(); - - dn = get_dn(muxl, info->dev_info); - if (dn == NULL) { - pr_debug("Send data on unknown phy ID = %d (0x%x)\n", - info->dev_info->id, info->dev_info->id); - rcu_read_unlock(); - cfpkt_destroy(pkt); - return -ENOTCONN; - } - - info->hdr_len += 1; - linkid = info->channel_id; - cfpkt_add_head(pkt, &linkid, 1); - - /* We can't hold rcu_lock during receive, so take a ref count instead */ - cffrml_hold(dn); - - rcu_read_unlock(); - - err = dn->transmit(dn, pkt); - - cffrml_put(dn); - return err; -} - -static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - struct cfmuxl *muxl = container_obj(layr); - struct cflayer *layer; - - rcu_read_lock(); - list_for_each_entry_rcu(layer, &muxl->srvl_list, node) { - - if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) { - - if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND || - ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) && - layer->id != 0) - cfmuxl_remove_uplayer(layr, layer->id); - - /* NOTE: ctrlcmd is not allowed to block */ - layer->ctrlcmd(layer, ctrl, phyid); - } - } - rcu_read_unlock(); -} diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c deleted file mode 100644 index 96236d21b18e..000000000000 --- a/net/caif/cfpkt_skbuff.c +++ /dev/null @@ -1,373 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include - -#define PKT_PREFIX 48 -#define PKT_POSTFIX 2 -#define PKT_LEN_WHEN_EXTENDING 128 -#define PKT_ERROR(pkt, errmsg) \ -do { \ - cfpkt_priv(pkt)->erronous = true; \ - skb_reset_tail_pointer(&pkt->skb); \ - pr_warn(errmsg); \ -} while (0) - -/* - * net/caif/ is generic and does not - * understand SKB, so we do this typecast - */ -struct cfpkt { - struct sk_buff skb; -}; - -/* Private data inside SKB */ -struct cfpkt_priv_data { - struct dev_info dev_info; - bool erronous; -}; - -static inline struct cfpkt_priv_data *cfpkt_priv(struct cfpkt *pkt) -{ - return (struct cfpkt_priv_data *) pkt->skb.cb; -} - -static inline bool is_erronous(struct cfpkt *pkt) -{ - return cfpkt_priv(pkt)->erronous; -} - -static inline struct sk_buff *pkt_to_skb(struct cfpkt *pkt) -{ - return &pkt->skb; -} - -static inline struct cfpkt *skb_to_pkt(struct sk_buff *skb) -{ - return (struct cfpkt *) skb; -} - -struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt) -{ - struct cfpkt *pkt = skb_to_pkt(nativepkt); - cfpkt_priv(pkt)->erronous = false; - return pkt; -} -EXPORT_SYMBOL(cfpkt_fromnative); - -void *cfpkt_tonative(struct cfpkt *pkt) -{ - return (void *) pkt; -} -EXPORT_SYMBOL(cfpkt_tonative); - -static struct cfpkt *cfpkt_create_pfx(u16 len, u16 pfx) -{ - struct sk_buff *skb; - - skb = alloc_skb(len + pfx, GFP_ATOMIC); - if (unlikely(skb == NULL)) - return NULL; - - skb_reserve(skb, pfx); - return skb_to_pkt(skb); -} - -inline struct cfpkt *cfpkt_create(u16 len) -{ - return cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX); -} - -void cfpkt_destroy(struct cfpkt *pkt) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - kfree_skb(skb); -} - -inline bool cfpkt_more(struct cfpkt *pkt) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - return skb->len > 0; -} - -int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - if (skb_headlen(skb) >= len) { - memcpy(data, skb->data, len); - return 0; - } - return !cfpkt_extr_head(pkt, data, len) && - !cfpkt_add_head(pkt, data, len); -} - -int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - u8 *from; - if (unlikely(is_erronous(pkt))) - return -EPROTO; - - if (unlikely(len > skb->len)) { - PKT_ERROR(pkt, "read beyond end of packet\n"); - return -EPROTO; - } - - if (unlikely(len > skb_headlen(skb))) { - if (unlikely(skb_linearize(skb) != 0)) { - PKT_ERROR(pkt, "linearize failed\n"); - return -EPROTO; - } - } - from = skb_pull(skb, len); - from -= len; - if (data) - memcpy(data, from, len); - return 0; -} -EXPORT_SYMBOL(cfpkt_extr_head); - -int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - u8 *data = dta; - u8 *from; - if (unlikely(is_erronous(pkt))) - return -EPROTO; - - if (unlikely(skb_linearize(skb) != 0)) { - PKT_ERROR(pkt, "linearize failed\n"); - return -EPROTO; - } - if (unlikely(skb->data + len > skb_tail_pointer(skb))) { - PKT_ERROR(pkt, "read beyond end of packet\n"); - return -EPROTO; - } - from = skb_tail_pointer(skb) - len; - skb_trim(skb, skb->len - len); - memcpy(data, from, len); - return 0; -} - -int cfpkt_pad_trail(struct cfpkt *pkt, u16 len) -{ - return cfpkt_add_body(pkt, NULL, len); -} - -int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - struct sk_buff *lastskb; - u8 *to; - u16 addlen = 0; - - - if (unlikely(is_erronous(pkt))) - return -EPROTO; - - lastskb = skb; - - /* Check whether we need to add space at the tail */ - if (unlikely(skb_tailroom(skb) < len)) { - if (likely(len < PKT_LEN_WHEN_EXTENDING)) - addlen = PKT_LEN_WHEN_EXTENDING; - else - addlen = len; - } - - /* Check whether we need to change the SKB before writing to the tail */ - if (unlikely((addlen > 0) || skb_cloned(skb) || skb_shared(skb))) { - - /* Make sure data is writable */ - if (unlikely(skb_cow_data(skb, addlen, &lastskb) < 0)) { - PKT_ERROR(pkt, "cow failed\n"); - return -EPROTO; - } - } - - /* All set to put the last SKB and optionally write data there. */ - to = pskb_put(skb, lastskb, len); - if (likely(data)) - memcpy(to, data, len); - return 0; -} - -inline int cfpkt_addbdy(struct cfpkt *pkt, u8 data) -{ - return cfpkt_add_body(pkt, &data, 1); -} - -int cfpkt_add_head(struct cfpkt *pkt, const void *data2, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - struct sk_buff *lastskb; - u8 *to; - const u8 *data = data2; - int ret; - if (unlikely(is_erronous(pkt))) - return -EPROTO; - if (unlikely(skb_headroom(skb) < len)) { - PKT_ERROR(pkt, "no headroom\n"); - return -EPROTO; - } - - /* Make sure data is writable */ - ret = skb_cow_data(skb, 0, &lastskb); - if (unlikely(ret < 0)) { - PKT_ERROR(pkt, "cow failed\n"); - return ret; - } - - to = skb_push(skb, len); - memcpy(to, data, len); - return 0; -} -EXPORT_SYMBOL(cfpkt_add_head); - -inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len) -{ - return cfpkt_add_body(pkt, data, len); -} - -inline u16 cfpkt_getlen(struct cfpkt *pkt) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - return skb->len; -} - -int cfpkt_iterate(struct cfpkt *pkt, - u16 (*iter_func)(u16, void *, u16), - u16 data) -{ - /* - * Don't care about the performance hit of linearizing, - * Checksum should not be used on high-speed interfaces anyway. - */ - if (unlikely(is_erronous(pkt))) - return -EPROTO; - if (unlikely(skb_linearize(&pkt->skb) != 0)) { - PKT_ERROR(pkt, "linearize failed\n"); - return -EPROTO; - } - return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt)); -} - -int cfpkt_setlen(struct cfpkt *pkt, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - - - if (unlikely(is_erronous(pkt))) - return -EPROTO; - - if (likely(len <= skb->len)) { - if (unlikely(skb->data_len)) - ___pskb_trim(skb, len); - else - skb_trim(skb, len); - - return cfpkt_getlen(pkt); - } - - /* Need to expand SKB */ - if (unlikely(!cfpkt_pad_trail(pkt, len - skb->len))) - PKT_ERROR(pkt, "skb_pad_trail failed\n"); - - return cfpkt_getlen(pkt); -} - -struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, - struct cfpkt *addpkt, - u16 expectlen) -{ - struct sk_buff *dst = pkt_to_skb(dstpkt); - struct sk_buff *add = pkt_to_skb(addpkt); - u16 addlen = skb_headlen(add); - u16 neededtailspace; - struct sk_buff *tmp; - u16 dstlen; - u16 createlen; - if (unlikely(is_erronous(dstpkt) || is_erronous(addpkt))) { - return dstpkt; - } - - neededtailspace = max(expectlen, addlen); - - if (dst->tail + neededtailspace > dst->end) { - /* Create a dumplicate of 'dst' with more tail space */ - struct cfpkt *tmppkt; - dstlen = skb_headlen(dst); - createlen = dstlen + neededtailspace; - tmppkt = cfpkt_create(createlen + PKT_PREFIX + PKT_POSTFIX); - if (tmppkt == NULL) - return NULL; - tmp = pkt_to_skb(tmppkt); - skb_put_data(tmp, dst->data, dstlen); - cfpkt_destroy(dstpkt); - dst = tmp; - } - skb_put_data(dst, add->data, skb_headlen(add)); - cfpkt_destroy(addpkt); - return skb_to_pkt(dst); -} - -struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos) -{ - struct sk_buff *skb2; - struct sk_buff *skb = pkt_to_skb(pkt); - struct cfpkt *tmppkt; - u8 *split = skb->data + pos; - u16 len2nd = skb_tail_pointer(skb) - split; - - if (unlikely(is_erronous(pkt))) - return NULL; - - if (skb->data + pos > skb_tail_pointer(skb)) { - PKT_ERROR(pkt, "trying to split beyond end of packet\n"); - return NULL; - } - - /* Create a new packet for the second part of the data */ - tmppkt = cfpkt_create_pfx(len2nd + PKT_PREFIX + PKT_POSTFIX, - PKT_PREFIX); - if (tmppkt == NULL) - return NULL; - skb2 = pkt_to_skb(tmppkt); - - - if (skb2 == NULL) - return NULL; - - skb_put_data(skb2, split, len2nd); - - /* Reduce the length of the original packet */ - skb_trim(skb, pos); - - skb2->priority = skb->priority; - return skb_to_pkt(skb2); -} - -bool cfpkt_erroneous(struct cfpkt *pkt) -{ - return cfpkt_priv(pkt)->erronous; -} - -struct caif_payload_info *cfpkt_info(struct cfpkt *pkt) -{ - return (struct caif_payload_info *)&pkt_to_skb(pkt)->cb; -} -EXPORT_SYMBOL(cfpkt_info); - -void cfpkt_set_prio(struct cfpkt *pkt, int prio) -{ - pkt_to_skb(pkt)->priority = prio; -} -EXPORT_SYMBOL(cfpkt_set_prio); diff --git a/net/caif/cfrfml.c b/net/caif/cfrfml.c deleted file mode 100644 index 93732ebbd1e2..000000000000 --- a/net/caif/cfrfml.c +++ /dev/null @@ -1,299 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) container_of(layr, struct cfrfml, serv.layer) -#define RFM_SEGMENTATION_BIT 0x01 -#define RFM_HEAD_SIZE 7 - -static int cfrfml_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cfrfml { - struct cfsrvl serv; - struct cfpkt *incomplete_frm; - int fragment_size; - u8 seghead[6]; - u16 pdu_size; - /* Protects serialized processing of packets */ - spinlock_t sync; -}; - -static void cfrfml_release(struct cflayer *layer) -{ - struct cfsrvl *srvl = container_of(layer, struct cfsrvl, layer); - struct cfrfml *rfml = container_obj(&srvl->layer); - - if (rfml->incomplete_frm) - cfpkt_destroy(rfml->incomplete_frm); - - kfree(srvl); -} - -struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info, - int mtu_size) -{ - int tmp; - struct cfrfml *this = kzalloc_obj(struct cfrfml, GFP_ATOMIC); - - if (!this) - return NULL; - - cfsrvl_init(&this->serv, channel_id, dev_info, false); - this->serv.release = cfrfml_release; - this->serv.layer.receive = cfrfml_receive; - this->serv.layer.transmit = cfrfml_transmit; - - /* Round down to closest multiple of 16 */ - tmp = (mtu_size - RFM_HEAD_SIZE - 6) / 16; - tmp *= 16; - - this->fragment_size = tmp; - spin_lock_init(&this->sync); - snprintf(this->serv.layer.name, CAIF_LAYER_NAME_SZ, - "rfm%d", channel_id); - - return &this->serv.layer; -} - -static struct cfpkt *rfm_append(struct cfrfml *rfml, char *seghead, - struct cfpkt *pkt, int *err) -{ - struct cfpkt *tmppkt; - *err = -EPROTO; - /* n-th but not last segment */ - - if (cfpkt_extr_head(pkt, seghead, 6) < 0) - return NULL; - - /* Verify correct header */ - if (memcmp(seghead, rfml->seghead, 6) != 0) - return NULL; - - tmppkt = cfpkt_append(rfml->incomplete_frm, pkt, - rfml->pdu_size + RFM_HEAD_SIZE); - - /* If cfpkt_append failes input pkts are not freed */ - *err = -ENOMEM; - if (tmppkt == NULL) - return NULL; - - *err = 0; - return tmppkt; -} - -static int cfrfml_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 tmp; - bool segmented; - int err; - u8 seghead[6]; - struct cfrfml *rfml; - struct cfpkt *tmppkt = NULL; - - caif_assert(layr->up != NULL); - caif_assert(layr->receive != NULL); - rfml = container_obj(layr); - spin_lock(&rfml->sync); - - err = -EPROTO; - if (cfpkt_extr_head(pkt, &tmp, 1) < 0) - goto out; - segmented = tmp & RFM_SEGMENTATION_BIT; - - if (segmented) { - if (rfml->incomplete_frm == NULL) { - /* Initial Segment */ - if (cfpkt_peek_head(pkt, rfml->seghead, 6) != 0) - goto out; - - rfml->pdu_size = get_unaligned_le16(rfml->seghead+4); - - if (cfpkt_erroneous(pkt)) - goto out; - rfml->incomplete_frm = pkt; - pkt = NULL; - } else { - - tmppkt = rfm_append(rfml, seghead, pkt, &err); - if (tmppkt == NULL) - goto out; - - if (cfpkt_erroneous(tmppkt)) - goto out; - - rfml->incomplete_frm = tmppkt; - - - if (cfpkt_erroneous(tmppkt)) - goto out; - } - err = 0; - goto out; - } - - if (rfml->incomplete_frm) { - - /* Last Segment */ - tmppkt = rfm_append(rfml, seghead, pkt, &err); - if (tmppkt == NULL) - goto out; - - if (cfpkt_erroneous(tmppkt)) - goto out; - - rfml->incomplete_frm = NULL; - pkt = tmppkt; - tmppkt = NULL; - - /* Verify that length is correct */ - err = -EPROTO; - if (rfml->pdu_size != cfpkt_getlen(pkt) - RFM_HEAD_SIZE + 1) - goto out; - } - - err = rfml->serv.layer.up->receive(rfml->serv.layer.up, pkt); - -out: - - if (err != 0) { - if (tmppkt) - cfpkt_destroy(tmppkt); - if (pkt) - cfpkt_destroy(pkt); - if (rfml->incomplete_frm) - cfpkt_destroy(rfml->incomplete_frm); - rfml->incomplete_frm = NULL; - - pr_info("Connection error %d triggered on RFM link\n", err); - - /* Trigger connection error upon failure.*/ - layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, - rfml->serv.dev_info.id); - } - spin_unlock(&rfml->sync); - - if (unlikely(err == -EAGAIN)) - /* It is not possible to recover after drop of a fragment */ - err = -EIO; - - return err; -} - - -static int cfrfml_transmit_segment(struct cfrfml *rfml, struct cfpkt *pkt) -{ - caif_assert(cfpkt_getlen(pkt) < rfml->fragment_size + RFM_HEAD_SIZE); - - /* Add info for MUX-layer to route the packet out. */ - cfpkt_info(pkt)->channel_id = rfml->serv.layer.id; - - /* - * To optimize alignment, we add up the size of CAIF header before - * payload. - */ - cfpkt_info(pkt)->hdr_len = RFM_HEAD_SIZE; - cfpkt_info(pkt)->dev_info = &rfml->serv.dev_info; - - return rfml->serv.layer.dn->transmit(rfml->serv.layer.dn, pkt); -} - -static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - int err; - u8 seg; - u8 head[6]; - struct cfpkt *rearpkt = NULL; - struct cfpkt *frontpkt = pkt; - struct cfrfml *rfml = container_obj(layr); - - caif_assert(layr->dn != NULL); - caif_assert(layr->dn->transmit != NULL); - - if (!cfsrvl_ready(&rfml->serv, &err)) - goto out; - - err = -EPROTO; - if (cfpkt_getlen(pkt) <= RFM_HEAD_SIZE-1) - goto out; - - err = 0; - if (cfpkt_getlen(pkt) > rfml->fragment_size + RFM_HEAD_SIZE) - err = cfpkt_peek_head(pkt, head, 6); - - if (err != 0) - goto out; - - while (cfpkt_getlen(frontpkt) > rfml->fragment_size + RFM_HEAD_SIZE) { - - seg = 1; - err = -EPROTO; - - if (cfpkt_add_head(frontpkt, &seg, 1) < 0) - goto out; - /* - * On OOM error cfpkt_split returns NULL. - * - * NOTE: Segmented pdu is not correctly aligned. - * This has negative performance impact. - */ - - rearpkt = cfpkt_split(frontpkt, rfml->fragment_size); - if (rearpkt == NULL) - goto out; - - err = cfrfml_transmit_segment(rfml, frontpkt); - - if (err != 0) { - frontpkt = NULL; - goto out; - } - - frontpkt = rearpkt; - rearpkt = NULL; - - err = -EPROTO; - if (cfpkt_add_head(frontpkt, head, 6) < 0) - goto out; - - } - - seg = 0; - err = -EPROTO; - - if (cfpkt_add_head(frontpkt, &seg, 1) < 0) - goto out; - - err = cfrfml_transmit_segment(rfml, frontpkt); - - frontpkt = NULL; -out: - - if (err != 0) { - pr_info("Connection error %d triggered on RFM link\n", err); - /* Trigger connection error upon failure.*/ - - layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, - rfml->serv.dev_info.id); - - if (rearpkt) - cfpkt_destroy(rearpkt); - - if (frontpkt) - cfpkt_destroy(frontpkt); - } - - return err; -} diff --git a/net/caif/cfserl.c b/net/caif/cfserl.c deleted file mode 100644 index faf78fb754e2..000000000000 --- a/net/caif/cfserl.c +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) ((struct cfserl *) layr) - -#define CFSERL_STX 0x02 -#define SERIAL_MINIUM_PACKET_SIZE 4 -#define SERIAL_MAX_FRAMESIZE 4096 -struct cfserl { - struct cflayer layer; - struct cfpkt *incomplete_frm; - /* Protects parallel processing of incoming packets */ - spinlock_t sync; - bool usestx; -}; - -static int cfserl_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfserl_transmit(struct cflayer *layr, struct cfpkt *pkt); -static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid); - -void cfserl_release(struct cflayer *layer) -{ - kfree(layer); -} - -struct cflayer *cfserl_create(int instance, bool use_stx) -{ - struct cfserl *this = kzalloc_obj(struct cfserl, GFP_ATOMIC); - if (!this) - return NULL; - caif_assert(offsetof(struct cfserl, layer) == 0); - this->layer.receive = cfserl_receive; - this->layer.transmit = cfserl_transmit; - this->layer.ctrlcmd = cfserl_ctrlcmd; - this->usestx = use_stx; - spin_lock_init(&this->sync); - snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "ser1"); - return &this->layer; -} - -static int cfserl_receive(struct cflayer *l, struct cfpkt *newpkt) -{ - struct cfserl *layr = container_obj(l); - u16 pkt_len; - struct cfpkt *pkt = NULL; - struct cfpkt *tail_pkt = NULL; - u8 tmp8; - u16 tmp; - u8 stx = CFSERL_STX; - int ret; - u16 expectlen = 0; - - caif_assert(newpkt != NULL); - spin_lock(&layr->sync); - - if (layr->incomplete_frm != NULL) { - layr->incomplete_frm = - cfpkt_append(layr->incomplete_frm, newpkt, expectlen); - pkt = layr->incomplete_frm; - if (pkt == NULL) { - spin_unlock(&layr->sync); - return -ENOMEM; - } - } else { - pkt = newpkt; - } - layr->incomplete_frm = NULL; - - do { - /* Search for STX at start of pkt if STX is used */ - if (layr->usestx) { - cfpkt_extr_head(pkt, &tmp8, 1); - if (tmp8 != CFSERL_STX) { - while (cfpkt_more(pkt) - && tmp8 != CFSERL_STX) { - cfpkt_extr_head(pkt, &tmp8, 1); - } - if (!cfpkt_more(pkt)) { - cfpkt_destroy(pkt); - layr->incomplete_frm = NULL; - spin_unlock(&layr->sync); - return -EPROTO; - } - } - } - - pkt_len = cfpkt_getlen(pkt); - - /* - * pkt_len is the accumulated length of the packet data - * we have received so far. - * Exit if frame doesn't hold length. - */ - - if (pkt_len < 2) { - if (layr->usestx) - cfpkt_add_head(pkt, &stx, 1); - layr->incomplete_frm = pkt; - spin_unlock(&layr->sync); - return 0; - } - - /* - * Find length of frame. - * expectlen is the length we need for a full frame. - */ - cfpkt_peek_head(pkt, &tmp, 2); - expectlen = le16_to_cpu(tmp) + 2; - /* - * Frame error handling - */ - if (expectlen < SERIAL_MINIUM_PACKET_SIZE - || expectlen > SERIAL_MAX_FRAMESIZE) { - if (!layr->usestx) { - if (pkt != NULL) - cfpkt_destroy(pkt); - layr->incomplete_frm = NULL; - spin_unlock(&layr->sync); - return -EPROTO; - } - continue; - } - - if (pkt_len < expectlen) { - /* Too little received data */ - if (layr->usestx) - cfpkt_add_head(pkt, &stx, 1); - layr->incomplete_frm = pkt; - spin_unlock(&layr->sync); - return 0; - } - - /* - * Enough data for at least one frame. - * Split the frame, if too long - */ - if (pkt_len > expectlen) - tail_pkt = cfpkt_split(pkt, expectlen); - else - tail_pkt = NULL; - - /* Send the first part of packet upwards.*/ - spin_unlock(&layr->sync); - ret = layr->layer.up->receive(layr->layer.up, pkt); - spin_lock(&layr->sync); - if (ret == -EILSEQ) { - if (layr->usestx) { - if (tail_pkt != NULL) - pkt = cfpkt_append(pkt, tail_pkt, 0); - /* Start search for next STX if frame failed */ - continue; - } else { - cfpkt_destroy(pkt); - pkt = NULL; - } - } - - pkt = tail_pkt; - - } while (pkt != NULL); - - spin_unlock(&layr->sync); - return 0; -} - -static int cfserl_transmit(struct cflayer *layer, struct cfpkt *newpkt) -{ - struct cfserl *layr = container_obj(layer); - u8 tmp8 = CFSERL_STX; - if (layr->usestx) - cfpkt_add_head(newpkt, &tmp8, 1); - return layer->dn->transmit(layer->dn, newpkt); -} - -static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - layr->up->ctrlcmd(layr->up, ctrl, phyid); -} diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c deleted file mode 100644 index d687fd0b4ed3..000000000000 --- a/net/caif/cfsrvl.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SRVL_CTRL_PKT_SIZE 1 -#define SRVL_FLOW_OFF 0x81 -#define SRVL_FLOW_ON 0x80 -#define SRVL_SET_PIN 0x82 - -#define container_obj(layr) container_of(layr, struct cfsrvl, layer) - -static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - struct cfsrvl *service = container_obj(layr); - - if (layr->up == NULL || layr->up->ctrlcmd == NULL) - return; - - switch (ctrl) { - case CAIF_CTRLCMD_INIT_RSP: - service->open = true; - layr->up->ctrlcmd(layr->up, ctrl, phyid); - break; - case CAIF_CTRLCMD_DEINIT_RSP: - case CAIF_CTRLCMD_INIT_FAIL_RSP: - service->open = false; - layr->up->ctrlcmd(layr->up, ctrl, phyid); - break; - case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: - if (phyid != service->dev_info.id) - break; - if (service->modem_flow_on) - layr->up->ctrlcmd(layr->up, - CAIF_CTRLCMD_FLOW_OFF_IND, phyid); - service->phy_flow_on = false; - break; - case _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND: - if (phyid != service->dev_info.id) - return; - if (service->modem_flow_on) { - layr->up->ctrlcmd(layr->up, - CAIF_CTRLCMD_FLOW_ON_IND, - phyid); - } - service->phy_flow_on = true; - break; - case CAIF_CTRLCMD_FLOW_OFF_IND: - if (service->phy_flow_on) { - layr->up->ctrlcmd(layr->up, - CAIF_CTRLCMD_FLOW_OFF_IND, phyid); - } - service->modem_flow_on = false; - break; - case CAIF_CTRLCMD_FLOW_ON_IND: - if (service->phy_flow_on) { - layr->up->ctrlcmd(layr->up, - CAIF_CTRLCMD_FLOW_ON_IND, phyid); - } - service->modem_flow_on = true; - break; - case _CAIF_CTRLCMD_PHYIF_DOWN_IND: - /* In case interface is down, let's fake a remove shutdown */ - layr->up->ctrlcmd(layr->up, - CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, phyid); - break; - case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: - layr->up->ctrlcmd(layr->up, ctrl, phyid); - break; - default: - pr_warn("Unexpected ctrl in cfsrvl (%d)\n", ctrl); - /* We have both modem and phy flow on, send flow on */ - layr->up->ctrlcmd(layr->up, ctrl, phyid); - service->phy_flow_on = true; - break; - } -} - -static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl) -{ - struct cfsrvl *service = container_obj(layr); - - caif_assert(layr != NULL); - caif_assert(layr->dn != NULL); - caif_assert(layr->dn->transmit != NULL); - - if (!service->supports_flowctrl) - return 0; - - switch (ctrl) { - case CAIF_MODEMCMD_FLOW_ON_REQ: - { - struct cfpkt *pkt; - struct caif_payload_info *info; - u8 flow_on = SRVL_FLOW_ON; - pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); - if (!pkt) - return -ENOMEM; - - if (cfpkt_add_head(pkt, &flow_on, 1) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - info->hdr_len = 1; - info->dev_info = &service->dev_info; - cfpkt_set_prio(pkt, TC_PRIO_CONTROL); - return layr->dn->transmit(layr->dn, pkt); - } - case CAIF_MODEMCMD_FLOW_OFF_REQ: - { - struct cfpkt *pkt; - struct caif_payload_info *info; - u8 flow_off = SRVL_FLOW_OFF; - pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); - if (!pkt) - return -ENOMEM; - - if (cfpkt_add_head(pkt, &flow_off, 1) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - info->hdr_len = 1; - info->dev_info = &service->dev_info; - cfpkt_set_prio(pkt, TC_PRIO_CONTROL); - return layr->dn->transmit(layr->dn, pkt); - } - default: - break; - } - return -EINVAL; -} - -static void cfsrvl_release(struct cflayer *layer) -{ - struct cfsrvl *service = container_of(layer, struct cfsrvl, layer); - kfree(service); -} - -void cfsrvl_init(struct cfsrvl *service, - u8 channel_id, - struct dev_info *dev_info, - bool supports_flowctrl) -{ - caif_assert(offsetof(struct cfsrvl, layer) == 0); - service->open = false; - service->modem_flow_on = true; - service->phy_flow_on = true; - service->layer.id = channel_id; - service->layer.ctrlcmd = cfservl_ctrlcmd; - service->layer.modemcmd = cfservl_modemcmd; - service->dev_info = *dev_info; - service->supports_flowctrl = supports_flowctrl; - service->release = cfsrvl_release; -} - -bool cfsrvl_ready(struct cfsrvl *service, int *err) -{ - if (!service->open) { - *err = -ENOTCONN; - return false; - } - return true; -} - -bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) -{ - struct cfsrvl *servl = container_obj(layer); - return servl->dev_info.id == phyid; -} - -void caif_free_client(struct cflayer *adap_layer) -{ - struct cflayer *serv_layer; - struct cfsrvl *servl; - - if (!adap_layer) - return; - - serv_layer = adap_layer->dn; - if (!serv_layer) - return; - - layer_set_dn(adap_layer, NULL); - layer_set_up(serv_layer, NULL); - - servl = container_obj(serv_layer); - servl->release(&servl->layer); -} -EXPORT_SYMBOL(caif_free_client); - -void caif_client_register_refcnt(struct cflayer *adapt_layer, - void (*hold)(struct cflayer *lyr), - void (*put)(struct cflayer *lyr)) -{ - struct cfsrvl *service; - - if (WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL)) - return; - service = container_of(adapt_layer->dn, struct cfsrvl, layer); - service->hold = hold; - service->put = put; -} -EXPORT_SYMBOL(caif_client_register_refcnt); diff --git a/net/caif/cfutill.c b/net/caif/cfutill.c deleted file mode 100644 index 5111090bb2c0..000000000000 --- a/net/caif/cfutill.c +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) ((struct cfsrvl *) layr) -#define UTIL_PAYLOAD 0x00 -#define UTIL_CMD_BIT 0x80 -#define UTIL_REMOTE_SHUTDOWN 0x82 -#define UTIL_FLOW_OFF 0x81 -#define UTIL_FLOW_ON 0x80 - -static int cfutill_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cflayer *cfutill_create(u8 channel_id, struct dev_info *dev_info) -{ - struct cfsrvl *util = kzalloc_obj(struct cfsrvl, GFP_ATOMIC); - if (!util) - return NULL; - caif_assert(offsetof(struct cfsrvl, layer) == 0); - cfsrvl_init(util, channel_id, dev_info, true); - util->layer.receive = cfutill_receive; - util->layer.transmit = cfutill_transmit; - snprintf(util->layer.name, CAIF_LAYER_NAME_SZ, "util1"); - return &util->layer; -} - -static int cfutill_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 cmd = -1; - struct cfsrvl *service = container_obj(layr); - caif_assert(layr != NULL); - caif_assert(layr->up != NULL); - caif_assert(layr->up->receive != NULL); - caif_assert(layr->up->ctrlcmd != NULL); - if (cfpkt_extr_head(pkt, &cmd, 1) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - - switch (cmd) { - case UTIL_PAYLOAD: - return layr->up->receive(layr->up, pkt); - case UTIL_FLOW_OFF: - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0); - cfpkt_destroy(pkt); - return 0; - case UTIL_FLOW_ON: - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0); - cfpkt_destroy(pkt); - return 0; - case UTIL_REMOTE_SHUTDOWN: /* Remote Shutdown Request */ - pr_err("REMOTE SHUTDOWN REQUEST RECEIVED\n"); - layr->ctrlcmd(layr, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, 0); - service->open = false; - cfpkt_destroy(pkt); - return 0; - default: - cfpkt_destroy(pkt); - pr_warn("Unknown service control %d (0x%x)\n", cmd, cmd); - return -EPROTO; - } -} - -static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 zero = 0; - struct caif_payload_info *info; - int ret; - struct cfsrvl *service = container_obj(layr); - caif_assert(layr != NULL); - caif_assert(layr->dn != NULL); - caif_assert(layr->dn->transmit != NULL); - - if (!cfsrvl_ready(service, &ret)) { - cfpkt_destroy(pkt); - return ret; - } - - cfpkt_add_head(pkt, &zero, 1); - /* Add info for MUX-layer to route the packet out. */ - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - /* - * To optimize alignment, we add up the size of CAIF header before - * payload. - */ - info->hdr_len = 1; - info->dev_info = &service->dev_info; - return layr->dn->transmit(layr->dn, pkt); -} diff --git a/net/caif/cfveil.c b/net/caif/cfveil.c deleted file mode 100644 index 53f844c49bbb..000000000000 --- a/net/caif/cfveil.c +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include - -#define VEI_PAYLOAD 0x00 -#define VEI_CMD_BIT 0x80 -#define VEI_FLOW_OFF 0x81 -#define VEI_FLOW_ON 0x80 -#define VEI_SET_PIN 0x82 - -#define container_obj(layr) container_of(layr, struct cfsrvl, layer) - -static int cfvei_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cflayer *cfvei_create(u8 channel_id, struct dev_info *dev_info) -{ - struct cfsrvl *vei = kzalloc_obj(struct cfsrvl, GFP_ATOMIC); - if (!vei) - return NULL; - caif_assert(offsetof(struct cfsrvl, layer) == 0); - cfsrvl_init(vei, channel_id, dev_info, true); - vei->layer.receive = cfvei_receive; - vei->layer.transmit = cfvei_transmit; - snprintf(vei->layer.name, CAIF_LAYER_NAME_SZ, "vei%d", channel_id); - return &vei->layer; -} - -static int cfvei_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 cmd; - int ret; - caif_assert(layr->up != NULL); - caif_assert(layr->receive != NULL); - caif_assert(layr->ctrlcmd != NULL); - - - if (cfpkt_extr_head(pkt, &cmd, 1) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - switch (cmd) { - case VEI_PAYLOAD: - ret = layr->up->receive(layr->up, pkt); - return ret; - case VEI_FLOW_OFF: - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0); - cfpkt_destroy(pkt); - return 0; - case VEI_FLOW_ON: - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0); - cfpkt_destroy(pkt); - return 0; - case VEI_SET_PIN: /* SET RS232 PIN */ - cfpkt_destroy(pkt); - return 0; - default: /* SET RS232 PIN */ - pr_warn("Unknown VEI control packet %d (0x%x)!\n", cmd, cmd); - cfpkt_destroy(pkt); - return -EPROTO; - } -} - -static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 tmp = 0; - struct caif_payload_info *info; - int ret; - struct cfsrvl *service = container_obj(layr); - if (!cfsrvl_ready(service, &ret)) - goto err; - caif_assert(layr->dn != NULL); - caif_assert(layr->dn->transmit != NULL); - - if (cfpkt_add_head(pkt, &tmp, 1) < 0) { - pr_err("Packet is erroneous!\n"); - ret = -EPROTO; - goto err; - } - - /* Add info-> for MUX-layer to route the packet out. */ - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - info->hdr_len = 1; - info->dev_info = &service->dev_info; - return layr->dn->transmit(layr->dn, pkt); -err: - cfpkt_destroy(pkt); - return ret; -} diff --git a/net/caif/cfvidl.c b/net/caif/cfvidl.c deleted file mode 100644 index 39e075b0a259..000000000000 --- a/net/caif/cfvidl.c +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) ((struct cfsrvl *) layr) - -static int cfvidl_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cflayer *cfvidl_create(u8 channel_id, struct dev_info *dev_info) -{ - struct cfsrvl *vid = kzalloc_obj(struct cfsrvl, GFP_ATOMIC); - if (!vid) - return NULL; - caif_assert(offsetof(struct cfsrvl, layer) == 0); - - cfsrvl_init(vid, channel_id, dev_info, false); - vid->layer.receive = cfvidl_receive; - vid->layer.transmit = cfvidl_transmit; - snprintf(vid->layer.name, CAIF_LAYER_NAME_SZ, "vid1"); - return &vid->layer; -} - -static int cfvidl_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u32 videoheader; - if (cfpkt_extr_head(pkt, &videoheader, 4) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - return layr->up->receive(layr->up, pkt); -} - -static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - struct cfsrvl *service = container_obj(layr); - struct caif_payload_info *info; - u32 videoheader = 0; - int ret; - - if (!cfsrvl_ready(service, &ret)) { - cfpkt_destroy(pkt); - return ret; - } - - cfpkt_add_head(pkt, &videoheader, 4); - /* Add info for MUX-layer to route the packet out */ - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - info->dev_info = &service->dev_info; - return layr->dn->transmit(layr->dn, pkt); -} diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c deleted file mode 100644 index fa6a3c2634a8..000000000000 --- a/net/caif/chnl_net.c +++ /dev/null @@ -1,531 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Authors: Sjur Brendeland - * Daniel Martensson - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* GPRS PDP connection has MTU to 1500 */ -#define GPRS_PDP_MTU 1500 -/* 5 sec. connect timeout */ -#define CONNECT_TIMEOUT (5 * HZ) -#define CAIF_NET_DEFAULT_QUEUE_LEN 500 -#define UNDEF_CONNID 0xffffffff - -/*This list is protected by the rtnl lock. */ -static LIST_HEAD(chnl_net_list); - -MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol GPRS network device"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_RTNL_LINK("caif"); - -enum caif_states { - CAIF_CONNECTED = 1, - CAIF_CONNECTING, - CAIF_DISCONNECTED, - CAIF_SHUTDOWN -}; - -struct chnl_net { - struct cflayer chnl; - struct caif_connect_request conn_req; - struct list_head list_field; - struct net_device *netdev; - wait_queue_head_t netmgmt_wq; - /* Flow status to remember and control the transmission. */ - bool flowenabled; - enum caif_states state; -}; - -static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) -{ - struct sk_buff *skb; - struct chnl_net *priv; - int pktlen; - const u8 *ip_version; - u8 buf; - - priv = container_of(layr, struct chnl_net, chnl); - - skb = (struct sk_buff *) cfpkt_tonative(pkt); - - /* Get length of CAIF packet. */ - pktlen = skb->len; - - /* Pass some minimum information and - * send the packet to the net stack. - */ - skb->dev = priv->netdev; - - /* check the version of IP */ - ip_version = skb_header_pointer(skb, 0, 1, &buf); - if (!ip_version) { - kfree_skb(skb); - return -EINVAL; - } - - switch (*ip_version >> 4) { - case 4: - skb->protocol = htons(ETH_P_IP); - break; - case 6: - skb->protocol = htons(ETH_P_IPV6); - break; - default: - kfree_skb(skb); - priv->netdev->stats.rx_errors++; - return -EINVAL; - } - - /* If we change the header in loop mode, the checksum is corrupted. */ - if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb->ip_summed = CHECKSUM_NONE; - - netif_rx(skb); - - /* Update statistics. */ - priv->netdev->stats.rx_packets++; - priv->netdev->stats.rx_bytes += pktlen; - - return 0; -} - -static int delete_device(struct chnl_net *dev) -{ - ASSERT_RTNL(); - if (dev->netdev) - unregister_netdevice(dev->netdev); - return 0; -} - -static void close_work(struct work_struct *work) -{ - struct chnl_net *dev = NULL; - struct list_head *list_node; - struct list_head *_tmp; - - rtnl_lock(); - list_for_each_safe(list_node, _tmp, &chnl_net_list) { - dev = list_entry(list_node, struct chnl_net, list_field); - if (dev->state == CAIF_SHUTDOWN) - dev_close(dev->netdev); - } - rtnl_unlock(); -} -static DECLARE_WORK(close_worker, close_work); - -static void chnl_hold(struct cflayer *lyr) -{ - struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); - dev_hold(priv->netdev); -} - -static void chnl_put(struct cflayer *lyr) -{ - struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); - dev_put(priv->netdev); -} - -static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, - int phyid) -{ - struct chnl_net *priv = container_of(layr, struct chnl_net, chnl); - pr_debug("NET flowctrl func called flow: %s\n", - flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" : - flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" : - flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" : - flow == CAIF_CTRLCMD_DEINIT_RSP ? "CLOSE/DEINIT" : - flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "OPEN_FAIL" : - flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? - "REMOTE_SHUTDOWN" : "UNKNOWN CTRL COMMAND"); - - - - switch (flow) { - case CAIF_CTRLCMD_FLOW_OFF_IND: - priv->flowenabled = false; - netif_stop_queue(priv->netdev); - break; - case CAIF_CTRLCMD_DEINIT_RSP: - priv->state = CAIF_DISCONNECTED; - break; - case CAIF_CTRLCMD_INIT_FAIL_RSP: - priv->state = CAIF_DISCONNECTED; - wake_up_interruptible(&priv->netmgmt_wq); - break; - case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: - priv->state = CAIF_SHUTDOWN; - netif_tx_disable(priv->netdev); - schedule_work(&close_worker); - break; - case CAIF_CTRLCMD_FLOW_ON_IND: - priv->flowenabled = true; - netif_wake_queue(priv->netdev); - break; - case CAIF_CTRLCMD_INIT_RSP: - caif_client_register_refcnt(&priv->chnl, chnl_hold, chnl_put); - priv->state = CAIF_CONNECTED; - priv->flowenabled = true; - netif_wake_queue(priv->netdev); - wake_up_interruptible(&priv->netmgmt_wq); - break; - default: - break; - } -} - -static netdev_tx_t chnl_net_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct chnl_net *priv; - struct cfpkt *pkt = NULL; - int len; - int result = -1; - /* Get our private data. */ - priv = netdev_priv(dev); - - if (skb->len > priv->netdev->mtu) { - pr_warn("Size of skb exceeded MTU\n"); - kfree_skb(skb); - dev->stats.tx_errors++; - return NETDEV_TX_OK; - } - - if (!priv->flowenabled) { - pr_debug("dropping packets flow off\n"); - kfree_skb(skb); - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - - if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) - swap(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); - - /* Store original SKB length. */ - len = skb->len; - - pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb); - - /* Send the packet down the stack. */ - result = priv->chnl.dn->transmit(priv->chnl.dn, pkt); - if (result) { - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - - /* Update statistics. */ - dev->stats.tx_packets++; - dev->stats.tx_bytes += len; - - return NETDEV_TX_OK; -} - -static int chnl_net_open(struct net_device *dev) -{ - struct chnl_net *priv = NULL; - int result = -1; - int llifindex, headroom, tailroom, mtu; - struct net_device *lldev; - ASSERT_RTNL(); - priv = netdev_priv(dev); - if (!priv) { - pr_debug("chnl_net_open: no priv\n"); - return -ENODEV; - } - - if (priv->state != CAIF_CONNECTING) { - priv->state = CAIF_CONNECTING; - result = caif_connect_client(dev_net(dev), &priv->conn_req, - &priv->chnl, &llifindex, - &headroom, &tailroom); - if (result != 0) { - pr_debug("err: " - "Unable to register and open device," - " Err:%d\n", - result); - goto error; - } - - lldev = __dev_get_by_index(dev_net(dev), llifindex); - - if (lldev == NULL) { - pr_debug("no interface?\n"); - result = -ENODEV; - goto error; - } - - dev->needed_tailroom = tailroom + lldev->needed_tailroom; - dev->hard_header_len = headroom + lldev->hard_header_len + - lldev->needed_tailroom; - - /* - * MTU, head-room etc is not know before we have a - * CAIF link layer device available. MTU calculation may - * override initial RTNL configuration. - * MTU is minimum of current mtu, link layer mtu pluss - * CAIF head and tail, and PDP GPRS contexts max MTU. - */ - mtu = min_t(int, dev->mtu, lldev->mtu - (headroom + tailroom)); - mtu = min_t(int, GPRS_PDP_MTU, mtu); - dev_set_mtu(dev, mtu); - - if (mtu < 100) { - pr_warn("CAIF Interface MTU too small (%d)\n", mtu); - result = -ENODEV; - goto error; - } - } - - rtnl_unlock(); /* Release RTNL lock during connect wait */ - - result = wait_event_interruptible_timeout(priv->netmgmt_wq, - priv->state != CAIF_CONNECTING, - CONNECT_TIMEOUT); - - rtnl_lock(); - - if (result == -ERESTARTSYS) { - pr_debug("wait_event_interruptible woken by a signal\n"); - result = -ERESTARTSYS; - goto error; - } - - if (result == 0) { - pr_debug("connect timeout\n"); - result = -ETIMEDOUT; - goto error; - } - - if (priv->state != CAIF_CONNECTED) { - pr_debug("connect failed\n"); - result = -ECONNREFUSED; - goto error; - } - pr_debug("CAIF Netdevice connected\n"); - return 0; - -error: - caif_disconnect_client(dev_net(dev), &priv->chnl); - priv->state = CAIF_DISCONNECTED; - pr_debug("state disconnected\n"); - return result; - -} - -static int chnl_net_stop(struct net_device *dev) -{ - struct chnl_net *priv; - - ASSERT_RTNL(); - priv = netdev_priv(dev); - priv->state = CAIF_DISCONNECTED; - caif_disconnect_client(dev_net(dev), &priv->chnl); - return 0; -} - -static int chnl_net_init(struct net_device *dev) -{ - struct chnl_net *priv; - ASSERT_RTNL(); - priv = netdev_priv(dev); - INIT_LIST_HEAD(&priv->list_field); - return 0; -} - -static void chnl_net_uninit(struct net_device *dev) -{ - struct chnl_net *priv; - ASSERT_RTNL(); - priv = netdev_priv(dev); - list_del_init(&priv->list_field); -} - -static const struct net_device_ops netdev_ops = { - .ndo_open = chnl_net_open, - .ndo_stop = chnl_net_stop, - .ndo_init = chnl_net_init, - .ndo_uninit = chnl_net_uninit, - .ndo_start_xmit = chnl_net_start_xmit, -}; - -static void chnl_net_destructor(struct net_device *dev) -{ - struct chnl_net *priv = netdev_priv(dev); - caif_free_client(&priv->chnl); -} - -static void ipcaif_net_setup(struct net_device *dev) -{ - struct chnl_net *priv; - dev->netdev_ops = &netdev_ops; - dev->needs_free_netdev = true; - dev->priv_destructor = chnl_net_destructor; - dev->flags |= IFF_NOARP; - dev->flags |= IFF_POINTOPOINT; - dev->mtu = GPRS_PDP_MTU; - dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN; - - priv = netdev_priv(dev); - priv->chnl.receive = chnl_recv_cb; - priv->chnl.ctrlcmd = chnl_flowctrl_cb; - priv->netdev = dev; - priv->conn_req.protocol = CAIFPROTO_DATAGRAM; - priv->conn_req.link_selector = CAIF_LINK_HIGH_BANDW; - priv->conn_req.priority = CAIF_PRIO_LOW; - /* Insert illegal value */ - priv->conn_req.sockaddr.u.dgm.connection_id = UNDEF_CONNID; - priv->flowenabled = false; - - init_waitqueue_head(&priv->netmgmt_wq); -} - - -static int ipcaif_fill_info(struct sk_buff *skb, const struct net_device *dev) -{ - struct chnl_net *priv; - u8 loop; - priv = netdev_priv(dev); - if (nla_put_u32(skb, IFLA_CAIF_IPV4_CONNID, - priv->conn_req.sockaddr.u.dgm.connection_id) || - nla_put_u32(skb, IFLA_CAIF_IPV6_CONNID, - priv->conn_req.sockaddr.u.dgm.connection_id)) - goto nla_put_failure; - loop = priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP; - if (nla_put_u8(skb, IFLA_CAIF_LOOPBACK, loop)) - goto nla_put_failure; - return 0; -nla_put_failure: - return -EMSGSIZE; - -} - -static void caif_netlink_parms(struct nlattr *data[], - struct caif_connect_request *conn_req) -{ - if (!data) { - pr_warn("no params data found\n"); - return; - } - if (data[IFLA_CAIF_IPV4_CONNID]) - conn_req->sockaddr.u.dgm.connection_id = - nla_get_u32(data[IFLA_CAIF_IPV4_CONNID]); - if (data[IFLA_CAIF_IPV6_CONNID]) - conn_req->sockaddr.u.dgm.connection_id = - nla_get_u32(data[IFLA_CAIF_IPV6_CONNID]); - if (data[IFLA_CAIF_LOOPBACK]) { - if (nla_get_u8(data[IFLA_CAIF_LOOPBACK])) - conn_req->protocol = CAIFPROTO_DATAGRAM_LOOP; - else - conn_req->protocol = CAIFPROTO_DATAGRAM; - } -} - -static int ipcaif_newlink(struct net_device *dev, - struct rtnl_newlink_params *params, - struct netlink_ext_ack *extack) -{ - struct nlattr **data = params->data; - int ret; - struct chnl_net *caifdev; - ASSERT_RTNL(); - caifdev = netdev_priv(dev); - caif_netlink_parms(data, &caifdev->conn_req); - - ret = register_netdevice(dev); - if (ret) - pr_warn("device rtml registration failed\n"); - else - list_add(&caifdev->list_field, &chnl_net_list); - - /* Use ifindex as connection id, and use loopback channel default. */ - if (caifdev->conn_req.sockaddr.u.dgm.connection_id == UNDEF_CONNID) { - caifdev->conn_req.sockaddr.u.dgm.connection_id = dev->ifindex; - caifdev->conn_req.protocol = CAIFPROTO_DATAGRAM_LOOP; - } - return ret; -} - -static int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[], - struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - struct chnl_net *caifdev; - ASSERT_RTNL(); - caifdev = netdev_priv(dev); - caif_netlink_parms(data, &caifdev->conn_req); - netdev_state_change(dev); - return 0; -} - -static size_t ipcaif_get_size(const struct net_device *dev) -{ - return - /* IFLA_CAIF_IPV4_CONNID */ - nla_total_size(4) + - /* IFLA_CAIF_IPV6_CONNID */ - nla_total_size(4) + - /* IFLA_CAIF_LOOPBACK */ - nla_total_size(2) + - 0; -} - -static const struct nla_policy ipcaif_policy[IFLA_CAIF_MAX + 1] = { - [IFLA_CAIF_IPV4_CONNID] = { .type = NLA_U32 }, - [IFLA_CAIF_IPV6_CONNID] = { .type = NLA_U32 }, - [IFLA_CAIF_LOOPBACK] = { .type = NLA_U8 } -}; - - -static struct rtnl_link_ops ipcaif_link_ops __read_mostly = { - .kind = "caif", - .priv_size = sizeof(struct chnl_net), - .setup = ipcaif_net_setup, - .maxtype = IFLA_CAIF_MAX, - .policy = ipcaif_policy, - .newlink = ipcaif_newlink, - .changelink = ipcaif_changelink, - .get_size = ipcaif_get_size, - .fill_info = ipcaif_fill_info, - -}; - -static int __init chnl_init_module(void) -{ - return rtnl_link_register(&ipcaif_link_ops); -} - -static void __exit chnl_exit_module(void) -{ - struct chnl_net *dev = NULL; - struct list_head *list_node; - struct list_head *_tmp; - rtnl_link_unregister(&ipcaif_link_ops); - rtnl_lock(); - list_for_each_safe(list_node, _tmp, &chnl_net_list) { - dev = list_entry(list_node, struct chnl_net, list_field); - list_del_init(list_node); - delete_device(dev); - } - rtnl_unlock(); -} - -module_init(chnl_init_module); -module_exit(chnl_exit_module); -- cgit v1.2.3 From 4f10f1dfb235a28bd86cf0b00d86a59696ddbe5b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 20 Apr 2026 19:21:07 -0700 Subject: net: remove ISDN subsystem and Bluetooth CMTP Remove the ISDN (mISDN, CAPI) subsystem and Bluetooth CMTP protocol from the kernel tree. ISDN is a pretty old technology and it's unclear whether anyone still uses it. I went over the last few years of git history and all the commits are either tree-wide conversions or syzbot/static analyzer fixes. When we discussed removal in the past IIRC there were some concerns about ISDN still being used in parts of Germany. Unfortunately, the code base is quite old, none of the current maintainers are familiar with it and AI tools will have a field day finding bugs here. Delete this code and preserve it in an out-of-tree repository for any remaining users: https://github.com/linux-netdev/mod-orphan UAPI constants AF_ISDN/PF_ISDN and the SELinux isdn_socket class are preserved for ABI stability, but the rest of uAPI is removed. Signed-off-by: Jakub Kicinski Acked-by: Greg Kroah-Hartman Acked-by: Stephen Hemminger Acked-by: Luiz Augusto von Dentz Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260421022108.1299678-1-kuba@kernel.org Signed-off-by: Paolo Abeni --- CREDITS | 5 + Documentation/isdn/credits.rst | 73 - Documentation/isdn/index.rst | 14 - Documentation/isdn/interface_capi.rst | 336 -- Documentation/isdn/m_isdn.rst | 9 - Documentation/subsystem-apis.rst | 1 - MAINTAINERS | 19 - drivers/Kconfig | 2 - drivers/Makefile | 1 - drivers/isdn/Kconfig | 27 - drivers/isdn/Makefile | 8 - drivers/isdn/capi/Kconfig | 32 - drivers/isdn/capi/Makefile | 6 - drivers/isdn/capi/capi.c | 1435 ------- drivers/isdn/capi/capiutil.c | 677 ---- drivers/isdn/capi/kcapi.c | 933 ----- drivers/isdn/capi/kcapi.h | 182 - drivers/isdn/capi/kcapi_proc.c | 231 -- drivers/isdn/hardware/Makefile | 6 - drivers/isdn/hardware/mISDN/Kconfig | 98 - drivers/isdn/hardware/mISDN/Makefile | 19 - drivers/isdn/hardware/mISDN/avmfritz.c | 1164 ------ drivers/isdn/hardware/mISDN/hfc_multi.h | 1236 ------ drivers/isdn/hardware/mISDN/hfc_multi_8xx.h | 167 - drivers/isdn/hardware/mISDN/hfc_pci.h | 214 -- drivers/isdn/hardware/mISDN/hfcmulti.c | 5540 --------------------------- drivers/isdn/hardware/mISDN/hfcpci.c | 2360 ------------ drivers/isdn/hardware/mISDN/hfcsusb.c | 2157 ----------- drivers/isdn/hardware/mISDN/hfcsusb.h | 425 -- drivers/isdn/hardware/mISDN/iohelper.h | 96 - drivers/isdn/hardware/mISDN/ipac.h | 393 -- drivers/isdn/hardware/mISDN/isar.h | 256 -- drivers/isdn/hardware/mISDN/isdnhdlc.c | 617 --- drivers/isdn/hardware/mISDN/isdnhdlc.h | 69 - drivers/isdn/hardware/mISDN/mISDNinfineon.c | 1168 ------ drivers/isdn/hardware/mISDN/mISDNipac.c | 1636 -------- drivers/isdn/hardware/mISDN/mISDNisar.c | 1694 -------- drivers/isdn/hardware/mISDN/netjet.c | 1154 ------ drivers/isdn/hardware/mISDN/netjet.h | 44 - drivers/isdn/hardware/mISDN/speedfax.c | 520 --- drivers/isdn/hardware/mISDN/w6692.c | 1417 ------- drivers/isdn/hardware/mISDN/w6692.h | 177 - drivers/isdn/mISDN/Kconfig | 48 - drivers/isdn/mISDN/Makefile | 14 - drivers/isdn/mISDN/clock.c | 197 - drivers/isdn/mISDN/core.c | 400 -- drivers/isdn/mISDN/core.h | 69 - drivers/isdn/mISDN/dsp.h | 277 -- drivers/isdn/mISDN/dsp_audio.c | 421 -- drivers/isdn/mISDN/dsp_biquad.h | 51 - drivers/isdn/mISDN/dsp_blowfish.c | 667 ---- drivers/isdn/mISDN/dsp_cmx.c | 1949 ---------- drivers/isdn/mISDN/dsp_core.c | 1227 ------ drivers/isdn/mISDN/dsp_dtmf.c | 313 -- drivers/isdn/mISDN/dsp_ecdis.h | 96 - drivers/isdn/mISDN/dsp_hwec.c | 122 - drivers/isdn/mISDN/dsp_hwec.h | 10 - drivers/isdn/mISDN/dsp_pipeline.c | 300 -- drivers/isdn/mISDN/dsp_tones.c | 550 --- drivers/isdn/mISDN/fsm.c | 176 - drivers/isdn/mISDN/fsm.h | 58 - drivers/isdn/mISDN/hwchannel.c | 516 --- drivers/isdn/mISDN/l1oip.h | 92 - drivers/isdn/mISDN/l1oip_codec.c | 358 -- drivers/isdn/mISDN/l1oip_core.c | 1505 -------- drivers/isdn/mISDN/layer1.c | 415 -- drivers/isdn/mISDN/layer1.h | 16 - drivers/isdn/mISDN/layer2.c | 2266 ----------- drivers/isdn/mISDN/layer2.h | 131 - drivers/isdn/mISDN/socket.c | 825 ---- drivers/isdn/mISDN/stack.c | 654 ---- drivers/isdn/mISDN/tei.c | 1416 ------- drivers/isdn/mISDN/timerdev.c | 295 -- include/linux/isdn/capilli.h | 95 - include/linux/isdn/capiutil.h | 60 - include/linux/kernelcapi.h | 45 - include/linux/mISDNdsp.h | 40 - include/linux/mISDNhw.h | 192 - include/linux/mISDNif.h | 603 --- include/uapi/linux/capi.h | 134 - include/uapi/linux/isdn/capicmd.h | 117 - include/uapi/linux/kernelcapi.h | 48 - net/bluetooth/Kconfig | 3 - net/bluetooth/Makefile | 1 - net/bluetooth/cmtp/Kconfig | 12 - net/bluetooth/cmtp/Makefile | 8 - net/bluetooth/cmtp/capi.c | 579 --- net/bluetooth/cmtp/cmtp.h | 129 - net/bluetooth/cmtp/core.c | 519 --- net/bluetooth/cmtp/sock.c | 271 -- 90 files changed, 5 insertions(+), 44903 deletions(-) delete mode 100644 Documentation/isdn/credits.rst delete mode 100644 Documentation/isdn/index.rst delete mode 100644 Documentation/isdn/interface_capi.rst delete mode 100644 Documentation/isdn/m_isdn.rst delete mode 100644 drivers/isdn/Kconfig delete mode 100644 drivers/isdn/Makefile delete mode 100644 drivers/isdn/capi/Kconfig delete mode 100644 drivers/isdn/capi/Makefile delete mode 100644 drivers/isdn/capi/capi.c delete mode 100644 drivers/isdn/capi/capiutil.c delete mode 100644 drivers/isdn/capi/kcapi.c delete mode 100644 drivers/isdn/capi/kcapi.h delete mode 100644 drivers/isdn/capi/kcapi_proc.c delete mode 100644 drivers/isdn/hardware/Makefile delete mode 100644 drivers/isdn/hardware/mISDN/Kconfig delete mode 100644 drivers/isdn/hardware/mISDN/Makefile delete mode 100644 drivers/isdn/hardware/mISDN/avmfritz.c delete mode 100644 drivers/isdn/hardware/mISDN/hfc_multi.h delete mode 100644 drivers/isdn/hardware/mISDN/hfc_multi_8xx.h delete mode 100644 drivers/isdn/hardware/mISDN/hfc_pci.h delete mode 100644 drivers/isdn/hardware/mISDN/hfcmulti.c delete mode 100644 drivers/isdn/hardware/mISDN/hfcpci.c delete mode 100644 drivers/isdn/hardware/mISDN/hfcsusb.c delete mode 100644 drivers/isdn/hardware/mISDN/hfcsusb.h delete mode 100644 drivers/isdn/hardware/mISDN/iohelper.h delete mode 100644 drivers/isdn/hardware/mISDN/ipac.h delete mode 100644 drivers/isdn/hardware/mISDN/isar.h delete mode 100644 drivers/isdn/hardware/mISDN/isdnhdlc.c delete mode 100644 drivers/isdn/hardware/mISDN/isdnhdlc.h delete mode 100644 drivers/isdn/hardware/mISDN/mISDNinfineon.c delete mode 100644 drivers/isdn/hardware/mISDN/mISDNipac.c delete mode 100644 drivers/isdn/hardware/mISDN/mISDNisar.c delete mode 100644 drivers/isdn/hardware/mISDN/netjet.c delete mode 100644 drivers/isdn/hardware/mISDN/netjet.h delete mode 100644 drivers/isdn/hardware/mISDN/speedfax.c delete mode 100644 drivers/isdn/hardware/mISDN/w6692.c delete mode 100644 drivers/isdn/hardware/mISDN/w6692.h delete mode 100644 drivers/isdn/mISDN/Kconfig delete mode 100644 drivers/isdn/mISDN/Makefile delete mode 100644 drivers/isdn/mISDN/clock.c delete mode 100644 drivers/isdn/mISDN/core.c delete mode 100644 drivers/isdn/mISDN/core.h delete mode 100644 drivers/isdn/mISDN/dsp.h delete mode 100644 drivers/isdn/mISDN/dsp_audio.c delete mode 100644 drivers/isdn/mISDN/dsp_biquad.h delete mode 100644 drivers/isdn/mISDN/dsp_blowfish.c delete mode 100644 drivers/isdn/mISDN/dsp_cmx.c delete mode 100644 drivers/isdn/mISDN/dsp_core.c delete mode 100644 drivers/isdn/mISDN/dsp_dtmf.c delete mode 100644 drivers/isdn/mISDN/dsp_ecdis.h delete mode 100644 drivers/isdn/mISDN/dsp_hwec.c delete mode 100644 drivers/isdn/mISDN/dsp_hwec.h delete mode 100644 drivers/isdn/mISDN/dsp_pipeline.c delete mode 100644 drivers/isdn/mISDN/dsp_tones.c delete mode 100644 drivers/isdn/mISDN/fsm.c delete mode 100644 drivers/isdn/mISDN/fsm.h delete mode 100644 drivers/isdn/mISDN/hwchannel.c delete mode 100644 drivers/isdn/mISDN/l1oip.h delete mode 100644 drivers/isdn/mISDN/l1oip_codec.c delete mode 100644 drivers/isdn/mISDN/l1oip_core.c delete mode 100644 drivers/isdn/mISDN/layer1.c delete mode 100644 drivers/isdn/mISDN/layer1.h delete mode 100644 drivers/isdn/mISDN/layer2.c delete mode 100644 drivers/isdn/mISDN/layer2.h delete mode 100644 drivers/isdn/mISDN/socket.c delete mode 100644 drivers/isdn/mISDN/stack.c delete mode 100644 drivers/isdn/mISDN/tei.c delete mode 100644 drivers/isdn/mISDN/timerdev.c delete mode 100644 include/linux/isdn/capilli.h delete mode 100644 include/linux/isdn/capiutil.h delete mode 100644 include/linux/kernelcapi.h delete mode 100644 include/linux/mISDNdsp.h delete mode 100644 include/linux/mISDNhw.h delete mode 100644 include/linux/mISDNif.h delete mode 100644 include/uapi/linux/capi.h delete mode 100644 include/uapi/linux/isdn/capicmd.h delete mode 100644 include/uapi/linux/kernelcapi.h delete mode 100644 net/bluetooth/cmtp/Kconfig delete mode 100644 net/bluetooth/cmtp/Makefile delete mode 100644 net/bluetooth/cmtp/capi.c delete mode 100644 net/bluetooth/cmtp/cmtp.h delete mode 100644 net/bluetooth/cmtp/core.c delete mode 100644 net/bluetooth/cmtp/sock.c (limited to 'include/uapi/linux') diff --git a/CREDITS b/CREDITS index a03b00452a1e..eeeece8ed868 100644 --- a/CREDITS +++ b/CREDITS @@ -3649,6 +3649,11 @@ S: Dag Hammerskjolds v. 3E S: S-226 64 LUND S: Sweden +N: Tilman Schmidt +E: tilman@imap.cc +D: Siemens Gigaset ISDN driver author and maintainer +D: ISDN CAPI subsystem contributions + N: Henning P. Schmiedehausen E: hps@tanstaafl.de D: added PCI support to the serial driver diff --git a/Documentation/isdn/credits.rst b/Documentation/isdn/credits.rst deleted file mode 100644 index 319323f2091f..000000000000 --- a/Documentation/isdn/credits.rst +++ /dev/null @@ -1,73 +0,0 @@ -======= -Credits -======= - - -I want to thank all who contributed to this project and especially to: -(in alphabetical order) - -Thomas Bogendörfer (tsbogend@bigbug.franken.de) - Tester, lots of bugfixes and hints. - -Alan Cox (alan@lxorguk.ukuu.org.uk) - For help getting into standard-kernel. - -Henner Eisen (eis@baty.hanse.de) - For X.25 implementation. - -Volker Götz (volker@oops.franken.de) - For contribution of man-pages, the imontty-tool and a perfect - maintaining of the mailing-list at hub-wue. - -Matthias Hessler (hessler@isdn4linux.de) - For creating and maintaining the FAQ. - -Bernhard Hailer (Bernhard.Hailer@lrz.uni-muenchen.de) - For creating the FAQ, and the leafsite HOWTO. - -Michael 'Ghandi' Herold (michael@abadonna.franken.de) - For contribution of the vbox answering machine. - -Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) - For his Sync-PPP-code. - -Karsten Keil (keil@isdn4linux.de) - For adding 1TR6-support to the Teles-driver. - For the HiSax-driver. - -Michael Knigge (knick@cove.han.de) - For contributing the imon-tool - -Andreas Kool (akool@Kool.f.EUnet.de) - For contribution of the isdnlog/isdnrep-tool - -Pedro Roque Marques (roque@di.fc.ul.pt) - For lot of new ideas and the pcbit driver. - -Eberhard Mönkeberg (emoenke@gwdg.de) - For testing and help to get into kernel. - -Thomas Neumann (tn@ruhr.de) - For help with Cisco-SLARP and keepalive - -Jan den Ouden (denouden@groovin.xs4all.nl) - For contribution of the original teles-driver - -Carsten Paeth (calle@calle.in-berlin.de) - For the AVM-B1-CAPI2.0 driver - -Thomas Pfeiffer (pfeiffer@pds.de) - For V.110, extended T.70 and Hylafax extensions in isdn_tty.c - -Max Riegel (riegel@max.franken.de) - For making the ICN hardware-documentation and test-equipment available. - -Armin Schindler (mac@melware.de) - For the eicon active card driver. - -Gerhard 'Fido' Schneider (fido@wuff.mayn.de) - For heavy-duty-beta-testing with his BBS ;) - -Thomas Uhl (uhl@think.de) - For distributing the cards. - For pushing me to work ;-) diff --git a/Documentation/isdn/index.rst b/Documentation/isdn/index.rst deleted file mode 100644 index d1125a16a746..000000000000 --- a/Documentation/isdn/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -==== -ISDN -==== - -.. toctree:: - :maxdepth: 2 - - interface_capi - - m_isdn - - credits diff --git a/Documentation/isdn/interface_capi.rst b/Documentation/isdn/interface_capi.rst deleted file mode 100644 index 4d63b34b35cf..000000000000 --- a/Documentation/isdn/interface_capi.rst +++ /dev/null @@ -1,336 +0,0 @@ -========================================= -Kernel CAPI Interface to Hardware Drivers -========================================= - -1. Overview -=========== - -From the CAPI 2.0 specification: -COMMON-ISDN-API (CAPI) is an application programming interface standard used -to access ISDN equipment connected to basic rate interfaces (BRI) and primary -rate interfaces (PRI). - -Kernel CAPI operates as a dispatching layer between CAPI applications and CAPI -hardware drivers. Hardware drivers register ISDN devices (controllers, in CAPI -lingo) with Kernel CAPI to indicate their readiness to provide their service -to CAPI applications. CAPI applications also register with Kernel CAPI, -requesting association with a CAPI device. Kernel CAPI then dispatches the -application registration to an available device, forwarding it to the -corresponding hardware driver. Kernel CAPI then forwards CAPI messages in both -directions between the application and the hardware driver. - -Format and semantics of CAPI messages are specified in the CAPI 2.0 standard. -This standard is freely available from https://www.capi.org. - - -2. Driver and Device Registration -================================= - -CAPI drivers must register each of the ISDN devices they control with Kernel -CAPI by calling the Kernel CAPI function attach_capi_ctr() with a pointer to a -struct capi_ctr before they can be used. This structure must be filled with -the names of the driver and controller, and a number of callback function -pointers which are subsequently used by Kernel CAPI for communicating with the -driver. The registration can be revoked by calling the function -detach_capi_ctr() with a pointer to the same struct capi_ctr. - -Before the device can be actually used, the driver must fill in the device -information fields 'manu', 'version', 'profile' and 'serial' in the capi_ctr -structure of the device, and signal its readiness by calling capi_ctr_ready(). -From then on, Kernel CAPI may call the registered callback functions for the -device. - -If the device becomes unusable for any reason (shutdown, disconnect ...), the -driver has to call capi_ctr_down(). This will prevent further calls to the -callback functions by Kernel CAPI. - - -3. Application Registration and Communication -============================================= - -Kernel CAPI forwards registration requests from applications (calls to CAPI -operation CAPI_REGISTER) to an appropriate hardware driver by calling its -register_appl() callback function. A unique Application ID (ApplID, u16) is -allocated by Kernel CAPI and passed to register_appl() along with the -parameter structure provided by the application. This is analogous to the -open() operation on regular files or character devices. - -After a successful return from register_appl(), CAPI messages from the -application may be passed to the driver for the device via calls to the -send_message() callback function. Conversely, the driver may call Kernel -CAPI's capi_ctr_handle_message() function to pass a received CAPI message to -Kernel CAPI for forwarding to an application, specifying its ApplID. - -Deregistration requests (CAPI operation CAPI_RELEASE) from applications are -forwarded as calls to the release_appl() callback function, passing the same -ApplID as with register_appl(). After return from release_appl(), no CAPI -messages for that application may be passed to or from the device anymore. - - -4. Data Structures -================== - -4.1 struct capi_driver ----------------------- - -This structure describes a Kernel CAPI driver itself. It is used in the -register_capi_driver() and unregister_capi_driver() functions, and contains -the following non-private fields, all to be set by the driver before calling -register_capi_driver(): - -``char name[32]`` - the name of the driver, as a zero-terminated ASCII string -``char revision[32]`` - the revision number of the driver, as a zero-terminated ASCII string - -4.2 struct capi_ctr -------------------- - -This structure describes an ISDN device (controller) handled by a Kernel CAPI -driver. After registration via the attach_capi_ctr() function it is passed to -all controller specific lower layer interface and callback functions to -identify the controller to operate on. - -It contains the following non-private fields: - -to be set by the driver before calling attach_capi_ctr(): -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``struct module *owner`` - pointer to the driver module owning the device - -``void *driverdata`` - an opaque pointer to driver specific data, not touched by Kernel CAPI - -``char name[32]`` - the name of the controller, as a zero-terminated ASCII string - -``char *driver_name`` - the name of the driver, as a zero-terminated ASCII string - -``int (*load_firmware)(struct capi_ctr *ctrlr, capiloaddata *ldata)`` - (optional) pointer to a callback function for sending firmware and - configuration data to the device - - The function may return before the operation has completed. - - Completion must be signalled by a call to capi_ctr_ready(). - - Return value: 0 on success, error code on error - Called in process context. - -``void (*reset_ctr)(struct capi_ctr *ctrlr)`` - (optional) pointer to a callback function for stopping the device, - releasing all registered applications - - The function may return before the operation has completed. - - Completion must be signalled by a call to capi_ctr_down(). - - Called in process context. - -``void (*register_appl)(struct capi_ctr *ctrlr, u16 applid, capi_register_params *rparam)`` - pointers to callback function for registration of - applications with the device - - Calls to these functions are serialized by Kernel CAPI so that only - one call to any of them is active at any time. - -``void (*release_appl)(struct capi_ctr *ctrlr, u16 applid)`` - pointers to callback functions deregistration of - applications with the device - - Calls to these functions are serialized by Kernel CAPI so that only - one call to any of them is active at any time. - -``u16 (*send_message)(struct capi_ctr *ctrlr, struct sk_buff *skb)`` - pointer to a callback function for sending a CAPI message to the - device - - Return value: CAPI error code - - If the method returns 0 (CAPI_NOERROR) the driver has taken ownership - of the skb and the caller may no longer access it. If it returns a - non-zero (error) value then ownership of the skb returns to the caller - who may reuse or free it. - - The return value should only be used to signal problems with respect - to accepting or queueing the message. Errors occurring during the - actual processing of the message should be signaled with an - appropriate reply message. - - May be called in process or interrupt context. - - Calls to this function are not serialized by Kernel CAPI, ie. it must - be prepared to be re-entered. - -``char *(*procinfo)(struct capi_ctr *ctrlr)`` - pointer to a callback function returning the entry for the device in - the CAPI controller info table, /proc/capi/controller - -Note: - Callback functions except send_message() are never called in interrupt - context. - -to be filled in before calling capi_ctr_ready(): -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``u8 manu[CAPI_MANUFACTURER_LEN]`` - value to return for CAPI_GET_MANUFACTURER - -``capi_version version`` - value to return for CAPI_GET_VERSION - -``capi_profile profile`` - value to return for CAPI_GET_PROFILE - -``u8 serial[CAPI_SERIAL_LEN]`` - value to return for CAPI_GET_SERIAL - - -4.3 SKBs --------- - -CAPI messages are passed between Kernel CAPI and the driver via send_message() -and capi_ctr_handle_message(), stored in the data portion of a socket buffer -(skb). Each skb contains a single CAPI message coded according to the CAPI 2.0 -standard. - -For the data transfer messages, DATA_B3_REQ and DATA_B3_IND, the actual -payload data immediately follows the CAPI message itself within the same skb. -The Data and Data64 parameters are not used for processing. The Data64 -parameter may be omitted by setting the length field of the CAPI message to 22 -instead of 30. - - -4.4 The _cmsg Structure ------------------------ - -(declared in ) - -The _cmsg structure stores the contents of a CAPI 2.0 message in an easily -accessible form. It contains members for all possible CAPI 2.0 parameters, -including subparameters of the Additional Info and B Protocol structured -parameters, with the following exceptions: - -* second Calling party number (CONNECT_IND) - -* Data64 (DATA_B3_REQ and DATA_B3_IND) - -* Sending complete (subparameter of Additional Info, CONNECT_REQ and INFO_REQ) - -* Global Configuration (subparameter of B Protocol, CONNECT_REQ, CONNECT_RESP - and SELECT_B_PROTOCOL_REQ) - -Only those parameters appearing in the message type currently being processed -are actually used. Unused members should be set to zero. - -Members are named after the CAPI 2.0 standard names of the parameters they -represent. See for the exact spelling. Member data -types are: - -=========== ================================================================= -u8 for CAPI parameters of type 'byte' - -u16 for CAPI parameters of type 'word' - -u32 for CAPI parameters of type 'dword' - -_cstruct for CAPI parameters of type 'struct' - The member is a pointer to a buffer containing the parameter in - CAPI encoding (length + content). It may also be NULL, which will - be taken to represent an empty (zero length) parameter. - Subparameters are stored in encoded form within the content part. - -_cmstruct alternative representation for CAPI parameters of type 'struct' - (used only for the 'Additional Info' and 'B Protocol' parameters) - The representation is a single byte containing one of the values: - CAPI_DEFAULT: The parameter is empty/absent. - CAPI_COMPOSE: The parameter is present. - Subparameter values are stored individually in the corresponding - _cmsg structure members. -=========== ================================================================= - - -5. Lower Layer Interface Functions -================================== - -:: - - int attach_capi_ctr(struct capi_ctr *ctrlr) - int detach_capi_ctr(struct capi_ctr *ctrlr) - -register/unregister a device (controller) with Kernel CAPI - -:: - - void capi_ctr_ready(struct capi_ctr *ctrlr) - void capi_ctr_down(struct capi_ctr *ctrlr) - -signal controller ready/not ready - -:: - - void capi_ctr_handle_message(struct capi_ctr * ctrlr, u16 applid, - struct sk_buff *skb) - -pass a received CAPI message to Kernel CAPI -for forwarding to the specified application - - -6. Helper Functions and Macros -============================== - -Macros to extract/set element values from/in a CAPI message header -(from ): - -====================== ============================= ==================== -Get Macro Set Macro Element (Type) -====================== ============================= ==================== -CAPIMSG_LEN(m) CAPIMSG_SETLEN(m, len) Total Length (u16) -CAPIMSG_APPID(m) CAPIMSG_SETAPPID(m, applid) ApplID (u16) -CAPIMSG_COMMAND(m) CAPIMSG_SETCOMMAND(m,cmd) Command (u8) -CAPIMSG_SUBCOMMAND(m) CAPIMSG_SETSUBCOMMAND(m, cmd) Subcommand (u8) -CAPIMSG_CMD(m) - Command*256 - + Subcommand (u16) -CAPIMSG_MSGID(m) CAPIMSG_SETMSGID(m, msgid) Message Number (u16) - -CAPIMSG_CONTROL(m) CAPIMSG_SETCONTROL(m, contr) Controller/PLCI/NCCI - (u32) -CAPIMSG_DATALEN(m) CAPIMSG_SETDATALEN(m, len) Data Length (u16) -====================== ============================= ==================== - - -Library functions for working with _cmsg structures -(from ): - -``char *capi_cmd2str(u8 Command, u8 Subcommand)`` - Returns the CAPI 2.0 message name corresponding to the given command - and subcommand values, as a static ASCII string. The return value may - be NULL if the command/subcommand is not one of those defined in the - CAPI 2.0 standard. - - -7. Debugging -============ - -The module kernelcapi has a module parameter showcapimsgs controlling some -debugging output produced by the module. It can only be set when the module is -loaded, via a parameter "showcapimsgs=" to the modprobe command, either on -the command line or in the configuration file. - -If the lowest bit of showcapimsgs is set, kernelcapi logs controller and -application up and down events. - -In addition, every registered CAPI controller has an associated traceflag -parameter controlling how CAPI messages sent from and to the controller are -logged. The traceflag parameter is initialized with the value of the -showcapimsgs parameter when the controller is registered, but can later be -changed via the MANUFACTURER_REQ command KCAPI_CMD_TRACE. - -If the value of traceflag is non-zero, CAPI messages are logged. -DATA_B3 messages are only logged if the value of traceflag is > 2. - -If the lowest bit of traceflag is set, only the command/subcommand and message -length are logged. Otherwise, kernelcapi logs a readable representation of -the entire message. diff --git a/Documentation/isdn/m_isdn.rst b/Documentation/isdn/m_isdn.rst deleted file mode 100644 index 5847a164287e..000000000000 --- a/Documentation/isdn/m_isdn.rst +++ /dev/null @@ -1,9 +0,0 @@ -============ -mISDN Driver -============ - -mISDN is a new modular ISDN driver, in the long term it should replace -the old I4L driver architecture for passive ISDN cards. -It was designed to allow a broad range of applications and interfaces -but only have the basic function in kernel, the interface to the user -space is based on sockets with a own address family AF_ISDN. diff --git a/Documentation/subsystem-apis.rst b/Documentation/subsystem-apis.rst index ff4fe8c936c8..b1ad48bb4001 100644 --- a/Documentation/subsystem-apis.rst +++ b/Documentation/subsystem-apis.rst @@ -46,7 +46,6 @@ Networking interfaces networking/index netlabel/index infiniband/index - isdn/index mhi/index Storage interfaces diff --git a/MAINTAINERS b/MAINTAINERS index 2b1b5e93c272..e4856d3427d9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13599,25 +13599,6 @@ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending.git master F: drivers/infiniband/ulp/isert -ISDN/CMTP OVER BLUETOOTH -L: netdev@vger.kernel.org -S: Orphan -W: http://www.isdn4linux.de -F: Documentation/isdn/ -F: drivers/isdn/capi/ -F: include/linux/isdn/ -F: include/uapi/linux/isdn/ -F: net/bluetooth/cmtp/ - -ISDN/mISDN SUBSYSTEM -L: netdev@vger.kernel.org -S: Orphan -W: http://www.isdn4linux.de -F: drivers/isdn/Kconfig -F: drivers/isdn/Makefile -F: drivers/isdn/hardware/ -F: drivers/isdn/mISDN/ - ISL28022 HARDWARE MONITORING DRIVER M: Carsten SpieĂź L: linux-hwmon@vger.kernel.org diff --git a/drivers/Kconfig b/drivers/Kconfig index c0f1fb893ec0..f2bed2ddeb66 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -61,8 +61,6 @@ source "drivers/macintosh/Kconfig" source "drivers/net/Kconfig" -source "drivers/isdn/Kconfig" - # input before char - char/joystick depends on it. As does USB. source "drivers/input/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 53fbd2e0acdd..0841ea851847 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -124,7 +124,6 @@ obj-$(CONFIG_WATCHDOG) += watchdog/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_ACCESSIBILITY) += accessibility/ -obj-$(CONFIG_ISDN) += isdn/ obj-$(CONFIG_EDAC) += edac/ obj-$(CONFIG_EISA) += eisa/ obj-$(CONFIG_PM_OPP) += opp/ diff --git a/drivers/isdn/Kconfig b/drivers/isdn/Kconfig deleted file mode 100644 index 6fd1b3f84a29..000000000000 --- a/drivers/isdn/Kconfig +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# ISDN device configuration -# - -menuconfig ISDN - bool "ISDN support" - depends on NET && NETDEVICES - help - ISDN ("Integrated Services Digital Network", called RNIS in France) - is a fully digital telephone service that can be used for voice and - data connections. If your computer is equipped with an ISDN - adapter you can use it to connect to your Internet service provider - (with SLIP or PPP) faster than via a conventional telephone modem - (though still much slower than with DSL) or to make and accept - voice calls (eg. turning your PC into a software answering machine - or PABX). - - Select this option if you want your kernel to support ISDN. - -if ISDN - -source "drivers/isdn/capi/Kconfig" - -source "drivers/isdn/mISDN/Kconfig" - -endif # ISDN diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile deleted file mode 100644 index d14334f4007e..000000000000 --- a/drivers/isdn/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Makefile for the kernel ISDN subsystem and device drivers. - -# Object files in subdirectories - -obj-$(CONFIG_BT_CMTP) += capi/ -obj-$(CONFIG_MISDN) += mISDN/ -obj-$(CONFIG_ISDN) += hardware/ diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig deleted file mode 100644 index fdb43a632215..000000000000 --- a/drivers/isdn/capi/Kconfig +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config ISDN_CAPI - def_bool ISDN && BT - help - This provides CAPI (the Common ISDN Application Programming - Interface) Version 2.0, a standard making it easy for programs to - access ISDN hardware in a device independent way. (For details see - .) CAPI supports making and accepting voice - and data connections, controlling call options and protocols, - as well as ISDN supplementary services like call forwarding or - three-party conferences (if supported by the specific hardware - driver). - - This subsystem requires a hardware specific driver. - See CONFIG_BT_CMTP for the last remaining regular driver - in the kernel that uses the CAPI subsystem. - -config CAPI_TRACE - def_bool BT_CMTP - help - If you say Y here, the kernelcapi driver can make verbose traces - of CAPI messages. This feature can be enabled/disabled via IOCTL for - every controller (default disabled). - -config ISDN_CAPI_MIDDLEWARE - def_bool BT_CMTP && TTY - help - This option will enhance the capabilities of the /dev/capi20 - interface. It will provide a means of moving a data connection, - established via the usual /dev/capi20 interface to a special tty - device. If you want to use pppd with pppdcapiplugin to dial up to - your ISP, say Y here. diff --git a/drivers/isdn/capi/Makefile b/drivers/isdn/capi/Makefile deleted file mode 100644 index 4fd3a4d7133f..000000000000 --- a/drivers/isdn/capi/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Makefile for the CAPI subsystem used by BT_CMTP - -obj-$(CONFIG_BT_CMTP) += kernelcapi.o -kernelcapi-y := kcapi.o capiutil.o capi.o -kernelcapi-$(CONFIG_PROC_FS) += kcapi_proc.o diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c deleted file mode 100644 index aa28b1d32c4e..000000000000 --- a/drivers/isdn/capi/capi.c +++ /dev/null @@ -1,1435 +0,0 @@ -/* $Id: capi.c,v 1.1.2.7 2004/04/28 09:48:59 armin Exp $ - * - * CAPI 2.0 Interface for Linux - * - * Copyright 1996 by Carsten Paeth - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "kcapi.h" - -MODULE_DESCRIPTION("CAPI4Linux: kernel CAPI layer and /dev/capi20 interface"); -MODULE_AUTHOR("Carsten Paeth"); -MODULE_LICENSE("GPL"); - -/* -------- driver information -------------------------------------- */ - -static DEFINE_MUTEX(capi_mutex); -static const struct class capi_class = { - .name = "capi", -}; -static int capi_major = 68; /* allocated */ - -module_param_named(major, capi_major, uint, 0); - -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE -#define CAPINC_NR_PORTS 32 -#define CAPINC_MAX_PORTS 256 - -static int capi_ttyminors = CAPINC_NR_PORTS; - -module_param_named(ttyminors, capi_ttyminors, uint, 0); -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - -/* -------- defines ------------------------------------------------- */ - -#define CAPINC_MAX_RECVQUEUE 10 -#define CAPINC_MAX_SENDQUEUE 10 -#define CAPI_MAX_BLKSIZE 2048 - -/* -------- data structures ----------------------------------------- */ - -struct capidev; -struct capincci; -struct capiminor; - -struct ackqueue_entry { - struct list_head list; - u16 datahandle; -}; - -struct capiminor { - unsigned int minor; - - struct capi20_appl *ap; - u32 ncci; - atomic_t datahandle; - atomic_t msgid; - - struct tty_port port; - int ttyinstop; - int ttyoutstop; - - struct sk_buff_head inqueue; - - struct sk_buff_head outqueue; - int outbytes; - struct sk_buff *outskb; - spinlock_t outlock; - - /* transmit path */ - struct list_head ackqueue; - int nack; - spinlock_t ackqlock; -}; - -struct capincci { - struct list_head list; - u32 ncci; - struct capidev *cdev; -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - struct capiminor *minorp; -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ -}; - -struct capidev { - struct list_head list; - struct capi20_appl ap; - u16 errcode; - unsigned userflags; - - struct sk_buff_head recvqueue; - wait_queue_head_t recvwait; - - struct list_head nccis; - - struct mutex lock; -}; - -/* -------- global variables ---------------------------------------- */ - -static DEFINE_MUTEX(capidev_list_lock); -static LIST_HEAD(capidev_list); - -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - -static DEFINE_SPINLOCK(capiminors_lock); -static struct capiminor **capiminors; - -static struct tty_driver *capinc_tty_driver; - -/* -------- datahandles --------------------------------------------- */ - -static int capiminor_add_ack(struct capiminor *mp, u16 datahandle) -{ - struct ackqueue_entry *n; - - n = kmalloc_obj(*n, GFP_ATOMIC); - if (unlikely(!n)) { - printk(KERN_ERR "capi: alloc datahandle failed\n"); - return -1; - } - n->datahandle = datahandle; - INIT_LIST_HEAD(&n->list); - spin_lock_bh(&mp->ackqlock); - list_add_tail(&n->list, &mp->ackqueue); - mp->nack++; - spin_unlock_bh(&mp->ackqlock); - return 0; -} - -static int capiminor_del_ack(struct capiminor *mp, u16 datahandle) -{ - struct ackqueue_entry *p, *tmp; - - spin_lock_bh(&mp->ackqlock); - list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) { - if (p->datahandle == datahandle) { - list_del(&p->list); - mp->nack--; - spin_unlock_bh(&mp->ackqlock); - kfree(p); - return 0; - } - } - spin_unlock_bh(&mp->ackqlock); - return -1; -} - -static void capiminor_del_all_ack(struct capiminor *mp) -{ - struct ackqueue_entry *p, *tmp; - - list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) { - list_del(&p->list); - kfree(p); - mp->nack--; - } -} - - -/* -------- struct capiminor ---------------------------------------- */ - -static void capiminor_destroy(struct tty_port *port) -{ - struct capiminor *mp = container_of(port, struct capiminor, port); - - kfree_skb(mp->outskb); - skb_queue_purge(&mp->inqueue); - skb_queue_purge(&mp->outqueue); - capiminor_del_all_ack(mp); - kfree(mp); -} - -static const struct tty_port_operations capiminor_port_ops = { - .destruct = capiminor_destroy, -}; - -static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci) -{ - struct capiminor *mp; - struct device *dev; - unsigned int minor; - - mp = kzalloc_obj(*mp); - if (!mp) { - printk(KERN_ERR "capi: can't alloc capiminor\n"); - return NULL; - } - - mp->ap = ap; - mp->ncci = ncci; - INIT_LIST_HEAD(&mp->ackqueue); - spin_lock_init(&mp->ackqlock); - - skb_queue_head_init(&mp->inqueue); - skb_queue_head_init(&mp->outqueue); - spin_lock_init(&mp->outlock); - - tty_port_init(&mp->port); - mp->port.ops = &capiminor_port_ops; - - /* Allocate the least unused minor number. */ - spin_lock(&capiminors_lock); - for (minor = 0; minor < capi_ttyminors; minor++) - if (!capiminors[minor]) { - capiminors[minor] = mp; - break; - } - spin_unlock(&capiminors_lock); - - if (minor == capi_ttyminors) { - printk(KERN_NOTICE "capi: out of minors\n"); - goto err_out1; - } - - mp->minor = minor; - - dev = tty_port_register_device(&mp->port, capinc_tty_driver, minor, - NULL); - if (IS_ERR(dev)) - goto err_out2; - - return mp; - -err_out2: - spin_lock(&capiminors_lock); - capiminors[minor] = NULL; - spin_unlock(&capiminors_lock); - -err_out1: - tty_port_put(&mp->port); - return NULL; -} - -static struct capiminor *capiminor_get(unsigned int minor) -{ - struct capiminor *mp; - - spin_lock(&capiminors_lock); - mp = capiminors[minor]; - if (mp) - tty_port_get(&mp->port); - spin_unlock(&capiminors_lock); - - return mp; -} - -static inline void capiminor_put(struct capiminor *mp) -{ - tty_port_put(&mp->port); -} - -static void capiminor_free(struct capiminor *mp) -{ - tty_unregister_device(capinc_tty_driver, mp->minor); - - spin_lock(&capiminors_lock); - capiminors[mp->minor] = NULL; - spin_unlock(&capiminors_lock); - - capiminor_put(mp); -} - -/* -------- struct capincci ----------------------------------------- */ - -static void capincci_alloc_minor(struct capidev *cdev, struct capincci *np) -{ - if (cdev->userflags & CAPIFLAG_HIGHJACKING) - np->minorp = capiminor_alloc(&cdev->ap, np->ncci); -} - -static void capincci_free_minor(struct capincci *np) -{ - struct capiminor *mp = np->minorp; - - if (mp) { - tty_port_tty_vhangup(&mp->port); - capiminor_free(mp); - } -} - -static inline unsigned int capincci_minor_opencount(struct capincci *np) -{ - struct capiminor *mp = np->minorp; - unsigned int count = 0; - struct tty_struct *tty; - - if (mp) { - tty = tty_port_tty_get(&mp->port); - if (tty) { - count = tty->count; - tty_kref_put(tty); - } - } - return count; -} - -#else /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ - -static inline void -capincci_alloc_minor(struct capidev *cdev, struct capincci *np) { } -static inline void capincci_free_minor(struct capincci *np) { } - -#endif /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ - -static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci) -{ - struct capincci *np; - - np = kzalloc_obj(*np); - if (!np) - return NULL; - np->ncci = ncci; - np->cdev = cdev; - - capincci_alloc_minor(cdev, np); - - list_add_tail(&np->list, &cdev->nccis); - - return np; -} - -static void capincci_free(struct capidev *cdev, u32 ncci) -{ - struct capincci *np, *tmp; - - list_for_each_entry_safe(np, tmp, &cdev->nccis, list) - if (ncci == 0xffffffff || np->ncci == ncci) { - capincci_free_minor(np); - list_del(&np->list); - kfree(np); - } -} - -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE -static struct capincci *capincci_find(struct capidev *cdev, u32 ncci) -{ - struct capincci *np; - - list_for_each_entry(np, &cdev->nccis, list) - if (np->ncci == ncci) - return np; - return NULL; -} - -/* -------- handle data queue --------------------------------------- */ - -static struct sk_buff * -gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb) -{ - struct sk_buff *nskb; - nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_KERNEL); - if (nskb) { - u16 datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 4 + 2); - unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN); - capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN); - capimsg_setu16(s, 2, mp->ap->applid); - capimsg_setu8 (s, 4, CAPI_DATA_B3); - capimsg_setu8 (s, 5, CAPI_RESP); - capimsg_setu16(s, 6, atomic_inc_return(&mp->msgid)); - capimsg_setu32(s, 8, mp->ncci); - capimsg_setu16(s, 12, datahandle); - } - return nskb; -} - -static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) -{ - unsigned int datalen = skb->len - CAPIMSG_LEN(skb->data); - struct tty_struct *tty; - struct sk_buff *nskb; - u16 errcode, datahandle; - struct tty_ldisc *ld; - int ret = -1; - - tty = tty_port_tty_get(&mp->port); - if (!tty) { - pr_debug("capi: currently no receiver\n"); - return -1; - } - - ld = tty_ldisc_ref(tty); - if (!ld) { - /* fatal error, do not requeue */ - ret = 0; - kfree_skb(skb); - goto deref_tty; - } - - if (ld->ops->receive_buf == NULL) { - pr_debug("capi: ldisc has no receive_buf function\n"); - /* fatal error, do not requeue */ - goto free_skb; - } - if (mp->ttyinstop) { - pr_debug("capi: recv tty throttled\n"); - goto deref_ldisc; - } - - if (tty->receive_room < datalen) { - pr_debug("capi: no room in tty\n"); - goto deref_ldisc; - } - - nskb = gen_data_b3_resp_for(mp, skb); - if (!nskb) { - printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); - goto deref_ldisc; - } - - datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4); - - errcode = capi20_put_message(mp->ap, nskb); - - if (errcode == CAPI_NOERROR) { - skb_pull(skb, CAPIMSG_LEN(skb->data)); - pr_debug("capi: DATA_B3_RESP %u len=%d => ldisc\n", - datahandle, skb->len); - ld->ops->receive_buf(tty, skb->data, NULL, skb->len); - } else { - printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", - errcode); - kfree_skb(nskb); - - if (errcode == CAPI_SENDQUEUEFULL) - goto deref_ldisc; - } - -free_skb: - ret = 0; - kfree_skb(skb); - -deref_ldisc: - tty_ldisc_deref(ld); - -deref_tty: - tty_kref_put(tty); - return ret; -} - -static void handle_minor_recv(struct capiminor *mp) -{ - struct sk_buff *skb; - - while ((skb = skb_dequeue(&mp->inqueue)) != NULL) - if (handle_recv_skb(mp, skb) < 0) { - skb_queue_head(&mp->inqueue, skb); - return; - } -} - -static void handle_minor_send(struct capiminor *mp) -{ - struct tty_struct *tty; - struct sk_buff *skb; - u16 len; - u16 errcode; - u16 datahandle; - - tty = tty_port_tty_get(&mp->port); - if (!tty) - return; - - if (mp->ttyoutstop) { - pr_debug("capi: send: tty stopped\n"); - tty_kref_put(tty); - return; - } - - while (1) { - spin_lock_bh(&mp->outlock); - skb = __skb_dequeue(&mp->outqueue); - if (!skb) { - spin_unlock_bh(&mp->outlock); - break; - } - len = (u16)skb->len; - mp->outbytes -= len; - spin_unlock_bh(&mp->outlock); - - datahandle = atomic_inc_return(&mp->datahandle); - skb_push(skb, CAPI_DATA_B3_REQ_LEN); - memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN); - capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN); - capimsg_setu16(skb->data, 2, mp->ap->applid); - capimsg_setu8 (skb->data, 4, CAPI_DATA_B3); - capimsg_setu8 (skb->data, 5, CAPI_REQ); - capimsg_setu16(skb->data, 6, atomic_inc_return(&mp->msgid)); - capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */ - capimsg_setu32(skb->data, 12, (u32)(long)skb->data);/* Data32 */ - capimsg_setu16(skb->data, 16, len); /* Data length */ - capimsg_setu16(skb->data, 18, datahandle); - capimsg_setu16(skb->data, 20, 0); /* Flags */ - - if (capiminor_add_ack(mp, datahandle) < 0) { - skb_pull(skb, CAPI_DATA_B3_REQ_LEN); - - spin_lock_bh(&mp->outlock); - __skb_queue_head(&mp->outqueue, skb); - mp->outbytes += len; - spin_unlock_bh(&mp->outlock); - - break; - } - errcode = capi20_put_message(mp->ap, skb); - if (errcode == CAPI_NOERROR) { - pr_debug("capi: DATA_B3_REQ %u len=%u\n", - datahandle, len); - continue; - } - capiminor_del_ack(mp, datahandle); - - if (errcode == CAPI_SENDQUEUEFULL) { - skb_pull(skb, CAPI_DATA_B3_REQ_LEN); - - spin_lock_bh(&mp->outlock); - __skb_queue_head(&mp->outqueue, skb); - mp->outbytes += len; - spin_unlock_bh(&mp->outlock); - - break; - } - - /* ups, drop packet */ - printk(KERN_ERR "capi: put_message = %x\n", errcode); - kfree_skb(skb); - } - tty_kref_put(tty); -} - -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ -/* -------- function called by lower level -------------------------- */ - -static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) -{ - struct capidev *cdev = ap->private; -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - struct capiminor *mp; - u16 datahandle; - struct capincci *np; -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - - mutex_lock(&cdev->lock); - - if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) { - u16 info = CAPIMSG_U16(skb->data, 12); // Info field - if ((info & 0xff00) == 0) - capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); - } - if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) - capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); - - if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) { - skb_queue_tail(&cdev->recvqueue, skb); - wake_up_interruptible(&cdev->recvwait); - goto unlock_out; - } - -#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE - skb_queue_tail(&cdev->recvqueue, skb); - wake_up_interruptible(&cdev->recvwait); - -#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - - np = capincci_find(cdev, CAPIMSG_CONTROL(skb->data)); - if (!np) { - printk(KERN_ERR "BUG: capi_signal: ncci not found\n"); - skb_queue_tail(&cdev->recvqueue, skb); - wake_up_interruptible(&cdev->recvwait); - goto unlock_out; - } - - mp = np->minorp; - if (!mp) { - skb_queue_tail(&cdev->recvqueue, skb); - wake_up_interruptible(&cdev->recvwait); - goto unlock_out; - } - if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) { - datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 4 + 2); - pr_debug("capi_signal: DATA_B3_IND %u len=%d\n", - datahandle, skb->len-CAPIMSG_LEN(skb->data)); - skb_queue_tail(&mp->inqueue, skb); - - handle_minor_recv(mp); - - } else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) { - - datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4); - pr_debug("capi_signal: DATA_B3_CONF %u 0x%x\n", - datahandle, - CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 2)); - kfree_skb(skb); - capiminor_del_ack(mp, datahandle); - tty_port_tty_wakeup(&mp->port); - handle_minor_send(mp); - - } else { - /* ups, let capi application handle it :-) */ - skb_queue_tail(&cdev->recvqueue, skb); - wake_up_interruptible(&cdev->recvwait); - } -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - -unlock_out: - mutex_unlock(&cdev->lock); -} - -/* -------- file_operations for capidev ----------------------------- */ - -static ssize_t -capi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - struct capidev *cdev = file->private_data; - struct sk_buff *skb; - size_t copied; - int err; - - if (!cdev->ap.applid) - return -ENODEV; - - skb = skb_dequeue(&cdev->recvqueue); - if (!skb) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - err = wait_event_interruptible(cdev->recvwait, - (skb = skb_dequeue(&cdev->recvqueue))); - if (err) - return err; - } - if (skb->len > count) { - skb_queue_head(&cdev->recvqueue, skb); - return -EMSGSIZE; - } - if (copy_to_user(buf, skb->data, skb->len)) { - skb_queue_head(&cdev->recvqueue, skb); - return -EFAULT; - } - copied = skb->len; - - kfree_skb(skb); - - return copied; -} - -static ssize_t -capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - struct capidev *cdev = file->private_data; - struct sk_buff *skb; - u16 mlen; - - if (!cdev->ap.applid) - return -ENODEV; - - if (count < CAPIMSG_BASELEN) - return -EINVAL; - - skb = alloc_skb(count, GFP_USER); - if (!skb) - return -ENOMEM; - - if (copy_from_user(skb_put(skb, count), buf, count)) { - kfree_skb(skb); - return -EFAULT; - } - mlen = CAPIMSG_LEN(skb->data); - if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { - if (count < CAPI_DATA_B3_REQ_LEN || - (size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) { - kfree_skb(skb); - return -EINVAL; - } - } else { - if (mlen != count) { - kfree_skb(skb); - return -EINVAL; - } - } - CAPIMSG_SETAPPID(skb->data, cdev->ap.applid); - - if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) { - if (count < CAPI_DISCONNECT_B3_RESP_LEN) { - kfree_skb(skb); - return -EINVAL; - } - mutex_lock(&cdev->lock); - capincci_free(cdev, CAPIMSG_NCCI(skb->data)); - mutex_unlock(&cdev->lock); - } - - cdev->errcode = capi20_put_message(&cdev->ap, skb); - - if (cdev->errcode) { - kfree_skb(skb); - return -EIO; - } - return count; -} - -static __poll_t -capi_poll(struct file *file, poll_table *wait) -{ - struct capidev *cdev = file->private_data; - __poll_t mask = 0; - - if (!cdev->ap.applid) - return EPOLLERR; - - poll_wait(file, &(cdev->recvwait), wait); - mask = EPOLLOUT | EPOLLWRNORM; - if (!skb_queue_empty_lockless(&cdev->recvqueue)) - mask |= EPOLLIN | EPOLLRDNORM; - return mask; -} - -static int -capi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct capidev *cdev = file->private_data; - capi_ioctl_struct data; - int retval = -EINVAL; - void __user *argp = (void __user *)arg; - - switch (cmd) { - case CAPI_REGISTER: - mutex_lock(&cdev->lock); - - if (cdev->ap.applid) { - retval = -EEXIST; - goto register_out; - } - if (copy_from_user(&cdev->ap.rparam, argp, - sizeof(struct capi_register_params))) { - retval = -EFAULT; - goto register_out; - } - cdev->ap.private = cdev; - cdev->ap.recv_message = capi_recv_message; - cdev->errcode = capi20_register(&cdev->ap); - retval = (int)cdev->ap.applid; - if (cdev->errcode) { - cdev->ap.applid = 0; - retval = -EIO; - } - -register_out: - mutex_unlock(&cdev->lock); - return retval; - - case CAPI_GET_VERSION: - if (copy_from_user(&data.contr, argp, - sizeof(data.contr))) - return -EFAULT; - cdev->errcode = capi20_get_version(data.contr, &data.version); - if (cdev->errcode) - return -EIO; - if (copy_to_user(argp, &data.version, - sizeof(data.version))) - return -EFAULT; - return 0; - - case CAPI_GET_SERIAL: - if (copy_from_user(&data.contr, argp, - sizeof(data.contr))) - return -EFAULT; - cdev->errcode = capi20_get_serial(data.contr, data.serial); - if (cdev->errcode) - return -EIO; - if (copy_to_user(argp, data.serial, - sizeof(data.serial))) - return -EFAULT; - return 0; - - case CAPI_GET_PROFILE: - if (copy_from_user(&data.contr, argp, - sizeof(data.contr))) - return -EFAULT; - - if (data.contr == 0) { - cdev->errcode = capi20_get_profile(data.contr, &data.profile); - if (cdev->errcode) - return -EIO; - - retval = copy_to_user(argp, - &data.profile.ncontroller, - sizeof(data.profile.ncontroller)); - - } else { - cdev->errcode = capi20_get_profile(data.contr, &data.profile); - if (cdev->errcode) - return -EIO; - - retval = copy_to_user(argp, &data.profile, - sizeof(data.profile)); - } - if (retval) - return -EFAULT; - return 0; - - case CAPI_GET_MANUFACTURER: - if (copy_from_user(&data.contr, argp, - sizeof(data.contr))) - return -EFAULT; - cdev->errcode = capi20_get_manufacturer(data.contr, data.manufacturer); - if (cdev->errcode) - return -EIO; - - if (copy_to_user(argp, data.manufacturer, - sizeof(data.manufacturer))) - return -EFAULT; - - return 0; - - case CAPI_GET_ERRCODE: - data.errcode = cdev->errcode; - cdev->errcode = CAPI_NOERROR; - if (arg) { - if (copy_to_user(argp, &data.errcode, - sizeof(data.errcode))) - return -EFAULT; - } - return data.errcode; - - case CAPI_INSTALLED: - if (capi20_isinstalled() == CAPI_NOERROR) - return 0; - return -ENXIO; - - case CAPI_MANUFACTURER_CMD: { - struct capi_manufacturer_cmd mcmd; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (copy_from_user(&mcmd, argp, sizeof(mcmd))) - return -EFAULT; - return capi20_manufacturer(mcmd.cmd, mcmd.data); - } - case CAPI_SET_FLAGS: - case CAPI_CLR_FLAGS: { - unsigned userflags; - - if (copy_from_user(&userflags, argp, sizeof(userflags))) - return -EFAULT; - - mutex_lock(&cdev->lock); - if (cmd == CAPI_SET_FLAGS) - cdev->userflags |= userflags; - else - cdev->userflags &= ~userflags; - mutex_unlock(&cdev->lock); - return 0; - } - case CAPI_GET_FLAGS: - if (copy_to_user(argp, &cdev->userflags, - sizeof(cdev->userflags))) - return -EFAULT; - return 0; - -#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE - case CAPI_NCCI_OPENCOUNT: - return 0; - -#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - case CAPI_NCCI_OPENCOUNT: { - struct capincci *nccip; - unsigned ncci; - int count = 0; - - if (copy_from_user(&ncci, argp, sizeof(ncci))) - return -EFAULT; - - mutex_lock(&cdev->lock); - nccip = capincci_find(cdev, (u32)ncci); - if (nccip) - count = capincci_minor_opencount(nccip); - mutex_unlock(&cdev->lock); - return count; - } - - case CAPI_NCCI_GETUNIT: { - struct capincci *nccip; - struct capiminor *mp; - unsigned ncci; - int unit = -ESRCH; - - if (copy_from_user(&ncci, argp, sizeof(ncci))) - return -EFAULT; - - mutex_lock(&cdev->lock); - nccip = capincci_find(cdev, (u32)ncci); - if (nccip) { - mp = nccip->minorp; - if (mp) - unit = mp->minor; - } - mutex_unlock(&cdev->lock); - return unit; - } -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - - default: - return -EINVAL; - } -} - -static long -capi_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int ret; - - mutex_lock(&capi_mutex); - ret = capi_ioctl(file, cmd, arg); - mutex_unlock(&capi_mutex); - - return ret; -} - -#ifdef CONFIG_COMPAT -static long -capi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int ret; - - if (cmd == CAPI_MANUFACTURER_CMD) { - struct { - compat_ulong_t cmd; - compat_uptr_t data; - } mcmd32; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (copy_from_user(&mcmd32, compat_ptr(arg), sizeof(mcmd32))) - return -EFAULT; - - mutex_lock(&capi_mutex); - ret = capi20_manufacturer(mcmd32.cmd, compat_ptr(mcmd32.data)); - mutex_unlock(&capi_mutex); - - return ret; - } - - return capi_unlocked_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static int capi_open(struct inode *inode, struct file *file) -{ - struct capidev *cdev; - - cdev = kzalloc_obj(*cdev); - if (!cdev) - return -ENOMEM; - - mutex_init(&cdev->lock); - skb_queue_head_init(&cdev->recvqueue); - init_waitqueue_head(&cdev->recvwait); - INIT_LIST_HEAD(&cdev->nccis); - file->private_data = cdev; - - mutex_lock(&capidev_list_lock); - list_add_tail(&cdev->list, &capidev_list); - mutex_unlock(&capidev_list_lock); - - return stream_open(inode, file); -} - -static int capi_release(struct inode *inode, struct file *file) -{ - struct capidev *cdev = file->private_data; - - mutex_lock(&capidev_list_lock); - list_del(&cdev->list); - mutex_unlock(&capidev_list_lock); - - if (cdev->ap.applid) - capi20_release(&cdev->ap); - skb_queue_purge(&cdev->recvqueue); - capincci_free(cdev, 0xffffffff); - - kfree(cdev); - return 0; -} - -static const struct file_operations capi_fops = -{ - .owner = THIS_MODULE, - .read = capi_read, - .write = capi_write, - .poll = capi_poll, - .unlocked_ioctl = capi_unlocked_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = capi_compat_ioctl, -#endif - .open = capi_open, - .release = capi_release, -}; - -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE -/* -------- tty_operations for capincci ----------------------------- */ - -static int -capinc_tty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct capiminor *mp = capiminor_get(tty->index); - int ret = tty_standard_install(driver, tty); - - if (ret == 0) - tty->driver_data = mp; - else - capiminor_put(mp); - return ret; -} - -static void capinc_tty_cleanup(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - tty->driver_data = NULL; - capiminor_put(mp); -} - -static int capinc_tty_open(struct tty_struct *tty, struct file *filp) -{ - struct capiminor *mp = tty->driver_data; - int err; - - err = tty_port_open(&mp->port, tty, filp); - if (err) - return err; - - handle_minor_recv(mp); - return 0; -} - -static void capinc_tty_close(struct tty_struct *tty, struct file *filp) -{ - struct capiminor *mp = tty->driver_data; - - tty_port_close(&mp->port, tty, filp); -} - -static ssize_t capinc_tty_write(struct tty_struct *tty, const u8 *buf, - size_t count) -{ - struct capiminor *mp = tty->driver_data; - struct sk_buff *skb; - - pr_debug("capinc_tty_write(count=%zu)\n", count); - - spin_lock_bh(&mp->outlock); - skb = mp->outskb; - if (skb) { - mp->outskb = NULL; - __skb_queue_tail(&mp->outqueue, skb); - mp->outbytes += skb->len; - } - - skb = alloc_skb(CAPI_DATA_B3_REQ_LEN + count, GFP_ATOMIC); - if (!skb) { - printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n"); - spin_unlock_bh(&mp->outlock); - return -ENOMEM; - } - - skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); - skb_put_data(skb, buf, count); - - __skb_queue_tail(&mp->outqueue, skb); - mp->outbytes += skb->len; - spin_unlock_bh(&mp->outlock); - - handle_minor_send(mp); - - return count; -} - -static int capinc_tty_put_char(struct tty_struct *tty, u8 ch) -{ - struct capiminor *mp = tty->driver_data; - bool invoke_send = false; - struct sk_buff *skb; - int ret = 1; - - pr_debug("capinc_put_char(%u)\n", ch); - - spin_lock_bh(&mp->outlock); - skb = mp->outskb; - if (skb) { - if (skb_tailroom(skb) > 0) { - skb_put_u8(skb, ch); - goto unlock_out; - } - mp->outskb = NULL; - __skb_queue_tail(&mp->outqueue, skb); - mp->outbytes += skb->len; - invoke_send = true; - } - - skb = alloc_skb(CAPI_DATA_B3_REQ_LEN + CAPI_MAX_BLKSIZE, GFP_ATOMIC); - if (skb) { - skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); - skb_put_u8(skb, ch); - mp->outskb = skb; - } else { - printk(KERN_ERR "capinc_put_char: char %u lost\n", ch); - ret = 0; - } - -unlock_out: - spin_unlock_bh(&mp->outlock); - - if (invoke_send) - handle_minor_send(mp); - - return ret; -} - -static void capinc_tty_flush_chars(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - struct sk_buff *skb; - - spin_lock_bh(&mp->outlock); - skb = mp->outskb; - if (skb) { - mp->outskb = NULL; - __skb_queue_tail(&mp->outqueue, skb); - mp->outbytes += skb->len; - spin_unlock_bh(&mp->outlock); - - handle_minor_send(mp); - } else - spin_unlock_bh(&mp->outlock); - - handle_minor_recv(mp); -} - -static unsigned int capinc_tty_write_room(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - unsigned int room; - - room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue); - room *= CAPI_MAX_BLKSIZE; - pr_debug("capinc_tty_write_room = %u\n", room); - return room; -} - -static unsigned int capinc_tty_chars_in_buffer(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - - pr_debug("capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n", - mp->outbytes, mp->nack, - skb_queue_len(&mp->outqueue), - skb_queue_len(&mp->inqueue)); - return mp->outbytes; -} - -static void capinc_tty_throttle(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - mp->ttyinstop = 1; -} - -static void capinc_tty_unthrottle(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - - mp->ttyinstop = 0; - handle_minor_recv(mp); -} - -static void capinc_tty_stop(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - - mp->ttyoutstop = 1; -} - -static void capinc_tty_start(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - - mp->ttyoutstop = 0; - handle_minor_send(mp); -} - -static void capinc_tty_hangup(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - - tty_port_hangup(&mp->port); -} - -static void capinc_tty_send_xchar(struct tty_struct *tty, u8 ch) -{ - pr_debug("capinc_tty_send_xchar(%u)\n", ch); -} - -static const struct tty_operations capinc_ops = { - .open = capinc_tty_open, - .close = capinc_tty_close, - .write = capinc_tty_write, - .put_char = capinc_tty_put_char, - .flush_chars = capinc_tty_flush_chars, - .write_room = capinc_tty_write_room, - .chars_in_buffer = capinc_tty_chars_in_buffer, - .throttle = capinc_tty_throttle, - .unthrottle = capinc_tty_unthrottle, - .stop = capinc_tty_stop, - .start = capinc_tty_start, - .hangup = capinc_tty_hangup, - .send_xchar = capinc_tty_send_xchar, - .install = capinc_tty_install, - .cleanup = capinc_tty_cleanup, -}; - -static int __init capinc_tty_init(void) -{ - struct tty_driver *drv; - int err; - - if (capi_ttyminors > CAPINC_MAX_PORTS) - capi_ttyminors = CAPINC_MAX_PORTS; - if (capi_ttyminors <= 0) - capi_ttyminors = CAPINC_NR_PORTS; - - capiminors = kzalloc_objs(struct capiminor *, capi_ttyminors); - if (!capiminors) - return -ENOMEM; - - drv = tty_alloc_driver(capi_ttyminors, TTY_DRIVER_REAL_RAW | - TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_DYNAMIC_DEV); - if (IS_ERR(drv)) { - kfree(capiminors); - return PTR_ERR(drv); - } - drv->driver_name = "capi_nc"; - drv->name = "capi!"; - drv->major = 0; - drv->minor_start = 0; - drv->type = TTY_DRIVER_TYPE_SERIAL; - drv->subtype = SERIAL_TYPE_NORMAL; - drv->init_termios = tty_std_termios; - drv->init_termios.c_iflag = ICRNL; - drv->init_termios.c_oflag = OPOST | ONLCR; - drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - drv->init_termios.c_lflag = 0; - tty_set_operations(drv, &capinc_ops); - - err = tty_register_driver(drv); - if (err) { - tty_driver_kref_put(drv); - kfree(capiminors); - printk(KERN_ERR "Couldn't register capi_nc driver\n"); - return err; - } - capinc_tty_driver = drv; - return 0; -} - -static void __exit capinc_tty_exit(void) -{ - tty_unregister_driver(capinc_tty_driver); - tty_driver_kref_put(capinc_tty_driver); - kfree(capiminors); -} - -#else /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ - -static inline int capinc_tty_init(void) -{ - return 0; -} - -static inline void capinc_tty_exit(void) { } - -#endif /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ - -/* -------- /proc functions ----------------------------------------- */ - -/* - * /proc/capi/capi20: - * minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt - */ -static int __maybe_unused capi20_proc_show(struct seq_file *m, void *v) -{ - struct capidev *cdev; - struct list_head *l; - - mutex_lock(&capidev_list_lock); - list_for_each(l, &capidev_list) { - cdev = list_entry(l, struct capidev, list); - seq_printf(m, "0 %d %lu %lu %lu %lu\n", - cdev->ap.applid, - cdev->ap.nrecvctlpkt, - cdev->ap.nrecvdatapkt, - cdev->ap.nsentctlpkt, - cdev->ap.nsentdatapkt); - } - mutex_unlock(&capidev_list_lock); - return 0; -} - -/* - * /proc/capi/capi20ncci: - * applid ncci - */ -static int __maybe_unused capi20ncci_proc_show(struct seq_file *m, void *v) -{ - struct capidev *cdev; - struct capincci *np; - - mutex_lock(&capidev_list_lock); - list_for_each_entry(cdev, &capidev_list, list) { - mutex_lock(&cdev->lock); - list_for_each_entry(np, &cdev->nccis, list) - seq_printf(m, "%d 0x%x\n", cdev->ap.applid, np->ncci); - mutex_unlock(&cdev->lock); - } - mutex_unlock(&capidev_list_lock); - return 0; -} - -static void __init proc_init(void) -{ - proc_create_single("capi/capi20", 0, NULL, capi20_proc_show); - proc_create_single("capi/capi20ncci", 0, NULL, capi20ncci_proc_show); -} - -static void __exit proc_exit(void) -{ - remove_proc_entry("capi/capi20", NULL); - remove_proc_entry("capi/capi20ncci", NULL); -} - -/* -------- init function and module interface ---------------------- */ - - -static int __init capi_init(void) -{ - const char *compileinfo; - int major_ret; - int ret; - - ret = kcapi_init(); - if (ret) - return ret; - - major_ret = register_chrdev(capi_major, "capi20", &capi_fops); - if (major_ret < 0) { - printk(KERN_ERR "capi20: unable to get major %d\n", capi_major); - kcapi_exit(); - return major_ret; - } - - ret = class_register(&capi_class); - if (ret) { - unregister_chrdev(capi_major, "capi20"); - kcapi_exit(); - return ret; - } - - device_create(&capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi20"); - - if (capinc_tty_init() < 0) { - device_destroy(&capi_class, MKDEV(capi_major, 0)); - class_unregister(&capi_class); - unregister_chrdev(capi_major, "capi20"); - kcapi_exit(); - return -ENOMEM; - } - - proc_init(); - -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - compileinfo = " (middleware)"; -#else - compileinfo = " (no middleware)"; -#endif - printk(KERN_NOTICE "CAPI 2.0 started up with major %d%s\n", - capi_major, compileinfo); - - return 0; -} - -static void __exit capi_exit(void) -{ - proc_exit(); - - device_destroy(&capi_class, MKDEV(capi_major, 0)); - class_unregister(&capi_class); - unregister_chrdev(capi_major, "capi20"); - - capinc_tty_exit(); - - kcapi_exit(); -} - -module_init(capi_init); -module_exit(capi_exit); diff --git a/drivers/isdn/capi/capiutil.c b/drivers/isdn/capi/capiutil.c deleted file mode 100644 index eec9b36343b7..000000000000 --- a/drivers/isdn/capi/capiutil.c +++ /dev/null @@ -1,677 +0,0 @@ -/* $Id: capiutil.c,v 1.13.6.4 2001/09/23 22:24:33 kai Exp $ - * - * CAPI 2.0 convert capi message to capi message struct - * - * From CAPI 2.0 Development Kit AVM 1995 (msg.c) - * Rewritten for Linux 1996 by Carsten Paeth - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "kcapi.h" - -/* from CAPI2.0 DDK AVM Berlin GmbH */ - -typedef struct { - int typ; - size_t off; -} _cdef; - -#define _CBYTE 1 -#define _CWORD 2 -#define _CDWORD 3 -#define _CSTRUCT 4 -#define _CMSTRUCT 5 -#define _CEND 6 - -static _cdef cdef[] = -{ - /*00 */ - {_CEND}, - /*01 */ - {_CEND}, - /*02 */ - {_CEND}, - /*03 */ - {_CDWORD, offsetof(_cmsg, adr.adrController)}, - /*04 */ - {_CMSTRUCT, offsetof(_cmsg, AdditionalInfo)}, - /*05 */ - {_CSTRUCT, offsetof(_cmsg, B1configuration)}, - /*06 */ - {_CWORD, offsetof(_cmsg, B1protocol)}, - /*07 */ - {_CSTRUCT, offsetof(_cmsg, B2configuration)}, - /*08 */ - {_CWORD, offsetof(_cmsg, B2protocol)}, - /*09 */ - {_CSTRUCT, offsetof(_cmsg, B3configuration)}, - /*0a */ - {_CWORD, offsetof(_cmsg, B3protocol)}, - /*0b */ - {_CSTRUCT, offsetof(_cmsg, BC)}, - /*0c */ - {_CSTRUCT, offsetof(_cmsg, BChannelinformation)}, - /*0d */ - {_CMSTRUCT, offsetof(_cmsg, BProtocol)}, - /*0e */ - {_CSTRUCT, offsetof(_cmsg, CalledPartyNumber)}, - /*0f */ - {_CSTRUCT, offsetof(_cmsg, CalledPartySubaddress)}, - /*10 */ - {_CSTRUCT, offsetof(_cmsg, CallingPartyNumber)}, - /*11 */ - {_CSTRUCT, offsetof(_cmsg, CallingPartySubaddress)}, - /*12 */ - {_CDWORD, offsetof(_cmsg, CIPmask)}, - /*13 */ - {_CDWORD, offsetof(_cmsg, CIPmask2)}, - /*14 */ - {_CWORD, offsetof(_cmsg, CIPValue)}, - /*15 */ - {_CDWORD, offsetof(_cmsg, Class)}, - /*16 */ - {_CSTRUCT, offsetof(_cmsg, ConnectedNumber)}, - /*17 */ - {_CSTRUCT, offsetof(_cmsg, ConnectedSubaddress)}, - /*18 */ - {_CDWORD, offsetof(_cmsg, Data)}, - /*19 */ - {_CWORD, offsetof(_cmsg, DataHandle)}, - /*1a */ - {_CWORD, offsetof(_cmsg, DataLength)}, - /*1b */ - {_CSTRUCT, offsetof(_cmsg, FacilityConfirmationParameter)}, - /*1c */ - {_CSTRUCT, offsetof(_cmsg, Facilitydataarray)}, - /*1d */ - {_CSTRUCT, offsetof(_cmsg, FacilityIndicationParameter)}, - /*1e */ - {_CSTRUCT, offsetof(_cmsg, FacilityRequestParameter)}, - /*1f */ - {_CWORD, offsetof(_cmsg, FacilitySelector)}, - /*20 */ - {_CWORD, offsetof(_cmsg, Flags)}, - /*21 */ - {_CDWORD, offsetof(_cmsg, Function)}, - /*22 */ - {_CSTRUCT, offsetof(_cmsg, HLC)}, - /*23 */ - {_CWORD, offsetof(_cmsg, Info)}, - /*24 */ - {_CSTRUCT, offsetof(_cmsg, InfoElement)}, - /*25 */ - {_CDWORD, offsetof(_cmsg, InfoMask)}, - /*26 */ - {_CWORD, offsetof(_cmsg, InfoNumber)}, - /*27 */ - {_CSTRUCT, offsetof(_cmsg, Keypadfacility)}, - /*28 */ - {_CSTRUCT, offsetof(_cmsg, LLC)}, - /*29 */ - {_CSTRUCT, offsetof(_cmsg, ManuData)}, - /*2a */ - {_CDWORD, offsetof(_cmsg, ManuID)}, - /*2b */ - {_CSTRUCT, offsetof(_cmsg, NCPI)}, - /*2c */ - {_CWORD, offsetof(_cmsg, Reason)}, - /*2d */ - {_CWORD, offsetof(_cmsg, Reason_B3)}, - /*2e */ - {_CWORD, offsetof(_cmsg, Reject)}, - /*2f */ - {_CSTRUCT, offsetof(_cmsg, Useruserdata)} -}; - -static unsigned char *cpars[] = -{ - /* ALERT_REQ */ [0x01] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01", - /* CONNECT_REQ */ [0x02] = "\x03\x14\x0e\x10\x0f\x11\x0d\x06\x08\x0a\x05\x07\x09\x01\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01", - /* DISCONNECT_REQ */ [0x04] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01", - /* LISTEN_REQ */ [0x05] = "\x03\x25\x12\x13\x10\x11\x01", - /* INFO_REQ */ [0x08] = "\x03\x0e\x04\x0c\x27\x2f\x1c\x01\x01", - /* FACILITY_REQ */ [0x09] = "\x03\x1f\x1e\x01", - /* SELECT_B_PROTOCOL_REQ */ [0x0a] = "\x03\x0d\x06\x08\x0a\x05\x07\x09\x01\x01", - /* CONNECT_B3_REQ */ [0x0b] = "\x03\x2b\x01", - /* DISCONNECT_B3_REQ */ [0x0d] = "\x03\x2b\x01", - /* DATA_B3_REQ */ [0x0f] = "\x03\x18\x1a\x19\x20\x01", - /* RESET_B3_REQ */ [0x10] = "\x03\x2b\x01", - /* ALERT_CONF */ [0x13] = "\x03\x23\x01", - /* CONNECT_CONF */ [0x14] = "\x03\x23\x01", - /* DISCONNECT_CONF */ [0x16] = "\x03\x23\x01", - /* LISTEN_CONF */ [0x17] = "\x03\x23\x01", - /* MANUFACTURER_REQ */ [0x18] = "\x03\x2a\x15\x21\x29\x01", - /* INFO_CONF */ [0x1a] = "\x03\x23\x01", - /* FACILITY_CONF */ [0x1b] = "\x03\x23\x1f\x1b\x01", - /* SELECT_B_PROTOCOL_CONF */ [0x1c] = "\x03\x23\x01", - /* CONNECT_B3_CONF */ [0x1d] = "\x03\x23\x01", - /* DISCONNECT_B3_CONF */ [0x1f] = "\x03\x23\x01", - /* DATA_B3_CONF */ [0x21] = "\x03\x19\x23\x01", - /* RESET_B3_CONF */ [0x22] = "\x03\x23\x01", - /* CONNECT_IND */ [0x26] = "\x03\x14\x0e\x10\x0f\x11\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01", - /* CONNECT_ACTIVE_IND */ [0x27] = "\x03\x16\x17\x28\x01", - /* DISCONNECT_IND */ [0x28] = "\x03\x2c\x01", - /* MANUFACTURER_CONF */ [0x2a] = "\x03\x2a\x15\x21\x29\x01", - /* INFO_IND */ [0x2c] = "\x03\x26\x24\x01", - /* FACILITY_IND */ [0x2d] = "\x03\x1f\x1d\x01", - /* CONNECT_B3_IND */ [0x2f] = "\x03\x2b\x01", - /* CONNECT_B3_ACTIVE_IND */ [0x30] = "\x03\x2b\x01", - /* DISCONNECT_B3_IND */ [0x31] = "\x03\x2d\x2b\x01", - /* DATA_B3_IND */ [0x33] = "\x03\x18\x1a\x19\x20\x01", - /* RESET_B3_IND */ [0x34] = "\x03\x2b\x01", - /* CONNECT_B3_T90_ACTIVE_IND */ [0x35] = "\x03\x2b\x01", - /* CONNECT_RESP */ [0x38] = "\x03\x2e\x0d\x06\x08\x0a\x05\x07\x09\x01\x16\x17\x28\x04\x0c\x27\x2f\x1c\x01\x01", - /* CONNECT_ACTIVE_RESP */ [0x39] = "\x03\x01", - /* DISCONNECT_RESP */ [0x3a] = "\x03\x01", - /* MANUFACTURER_IND */ [0x3c] = "\x03\x2a\x15\x21\x29\x01", - /* INFO_RESP */ [0x3e] = "\x03\x01", - /* FACILITY_RESP */ [0x3f] = "\x03\x1f\x01", - /* CONNECT_B3_RESP */ [0x41] = "\x03\x2e\x2b\x01", - /* CONNECT_B3_ACTIVE_RESP */ [0x42] = "\x03\x01", - /* DISCONNECT_B3_RESP */ [0x43] = "\x03\x01", - /* DATA_B3_RESP */ [0x45] = "\x03\x19\x01", - /* RESET_B3_RESP */ [0x46] = "\x03\x01", - /* CONNECT_B3_T90_ACTIVE_RESP */ [0x47] = "\x03\x01", - /* MANUFACTURER_RESP */ [0x4e] = "\x03\x2a\x15\x21\x29\x01", -}; - -/*-------------------------------------------------------*/ - -#define byteTLcpy(x, y) *(u8 *)(x) = *(u8 *)(y); -#define wordTLcpy(x, y) *(u16 *)(x) = *(u16 *)(y); -#define dwordTLcpy(x, y) memcpy(x, y, 4); -#define structTLcpy(x, y, l) memcpy(x, y, l) -#define structTLcpyovl(x, y, l) memmove(x, y, l) - -#define byteTRcpy(x, y) *(u8 *)(y) = *(u8 *)(x); -#define wordTRcpy(x, y) *(u16 *)(y) = *(u16 *)(x); -#define dwordTRcpy(x, y) memcpy(y, x, 4); -#define structTRcpy(x, y, l) memcpy(y, x, l) -#define structTRcpyovl(x, y, l) memmove(y, x, l) - -/*-------------------------------------------------------*/ -static unsigned command_2_index(u8 c, u8 sc) -{ - if (c & 0x80) - c = 0x9 + (c & 0x0f); - else if (c == 0x41) - c = 0x9 + 0x1; - if (c > 0x18) - c = 0x00; - return (sc & 3) * (0x9 + 0x9) + c; -} - -/** - * capi_cmd2par() - find parameter string for CAPI 2.0 command/subcommand - * @cmd: command number - * @subcmd: subcommand number - * - * Return value: static string, NULL if command/subcommand unknown - */ - -static unsigned char *capi_cmd2par(u8 cmd, u8 subcmd) -{ - return cpars[command_2_index(cmd, subcmd)]; -} - -/*-------------------------------------------------------*/ -#define TYP (cdef[cmsg->par[cmsg->p]].typ) -#define OFF (((u8 *)cmsg) + cdef[cmsg->par[cmsg->p]].off) - -static void jumpcstruct(_cmsg *cmsg) -{ - unsigned layer; - for (cmsg->p++, layer = 1; layer;) { - /* $$$$$ assert (cmsg->p); */ - cmsg->p++; - switch (TYP) { - case _CMSTRUCT: - layer++; - break; - case _CEND: - layer--; - break; - } - } -} - -/*-------------------------------------------------------*/ - -static char *mnames[] = -{ - [0x01] = "ALERT_REQ", - [0x02] = "CONNECT_REQ", - [0x04] = "DISCONNECT_REQ", - [0x05] = "LISTEN_REQ", - [0x08] = "INFO_REQ", - [0x09] = "FACILITY_REQ", - [0x0a] = "SELECT_B_PROTOCOL_REQ", - [0x0b] = "CONNECT_B3_REQ", - [0x0d] = "DISCONNECT_B3_REQ", - [0x0f] = "DATA_B3_REQ", - [0x10] = "RESET_B3_REQ", - [0x13] = "ALERT_CONF", - [0x14] = "CONNECT_CONF", - [0x16] = "DISCONNECT_CONF", - [0x17] = "LISTEN_CONF", - [0x18] = "MANUFACTURER_REQ", - [0x1a] = "INFO_CONF", - [0x1b] = "FACILITY_CONF", - [0x1c] = "SELECT_B_PROTOCOL_CONF", - [0x1d] = "CONNECT_B3_CONF", - [0x1f] = "DISCONNECT_B3_CONF", - [0x21] = "DATA_B3_CONF", - [0x22] = "RESET_B3_CONF", - [0x26] = "CONNECT_IND", - [0x27] = "CONNECT_ACTIVE_IND", - [0x28] = "DISCONNECT_IND", - [0x2a] = "MANUFACTURER_CONF", - [0x2c] = "INFO_IND", - [0x2d] = "FACILITY_IND", - [0x2f] = "CONNECT_B3_IND", - [0x30] = "CONNECT_B3_ACTIVE_IND", - [0x31] = "DISCONNECT_B3_IND", - [0x33] = "DATA_B3_IND", - [0x34] = "RESET_B3_IND", - [0x35] = "CONNECT_B3_T90_ACTIVE_IND", - [0x38] = "CONNECT_RESP", - [0x39] = "CONNECT_ACTIVE_RESP", - [0x3a] = "DISCONNECT_RESP", - [0x3c] = "MANUFACTURER_IND", - [0x3e] = "INFO_RESP", - [0x3f] = "FACILITY_RESP", - [0x41] = "CONNECT_B3_RESP", - [0x42] = "CONNECT_B3_ACTIVE_RESP", - [0x43] = "DISCONNECT_B3_RESP", - [0x45] = "DATA_B3_RESP", - [0x46] = "RESET_B3_RESP", - [0x47] = "CONNECT_B3_T90_ACTIVE_RESP", - [0x4e] = "MANUFACTURER_RESP" -}; - -/** - * capi_cmd2str() - convert CAPI 2.0 command/subcommand number to name - * @cmd: command number - * @subcmd: subcommand number - * - * Return value: static string - */ - -char *capi_cmd2str(u8 cmd, u8 subcmd) -{ - char *result; - - result = mnames[command_2_index(cmd, subcmd)]; - if (result == NULL) - result = "INVALID_COMMAND"; - return result; -} - - -/*-------------------------------------------------------*/ - -#ifdef CONFIG_CAPI_TRACE - -/*-------------------------------------------------------*/ - -static char *pnames[] = -{ - /*00 */ NULL, - /*01 */ NULL, - /*02 */ NULL, - /*03 */ "Controller/PLCI/NCCI", - /*04 */ "AdditionalInfo", - /*05 */ "B1configuration", - /*06 */ "B1protocol", - /*07 */ "B2configuration", - /*08 */ "B2protocol", - /*09 */ "B3configuration", - /*0a */ "B3protocol", - /*0b */ "BC", - /*0c */ "BChannelinformation", - /*0d */ "BProtocol", - /*0e */ "CalledPartyNumber", - /*0f */ "CalledPartySubaddress", - /*10 */ "CallingPartyNumber", - /*11 */ "CallingPartySubaddress", - /*12 */ "CIPmask", - /*13 */ "CIPmask2", - /*14 */ "CIPValue", - /*15 */ "Class", - /*16 */ "ConnectedNumber", - /*17 */ "ConnectedSubaddress", - /*18 */ "Data32", - /*19 */ "DataHandle", - /*1a */ "DataLength", - /*1b */ "FacilityConfirmationParameter", - /*1c */ "Facilitydataarray", - /*1d */ "FacilityIndicationParameter", - /*1e */ "FacilityRequestParameter", - /*1f */ "FacilitySelector", - /*20 */ "Flags", - /*21 */ "Function", - /*22 */ "HLC", - /*23 */ "Info", - /*24 */ "InfoElement", - /*25 */ "InfoMask", - /*26 */ "InfoNumber", - /*27 */ "Keypadfacility", - /*28 */ "LLC", - /*29 */ "ManuData", - /*2a */ "ManuID", - /*2b */ "NCPI", - /*2c */ "Reason", - /*2d */ "Reason_B3", - /*2e */ "Reject", - /*2f */ "Useruserdata" -}; - -#include - -/*-------------------------------------------------------*/ -static _cdebbuf *bufprint(_cdebbuf *cdb, char *fmt, ...) -{ - va_list f; - size_t n, r; - - if (!cdb) - return NULL; - va_start(f, fmt); - r = cdb->size - cdb->pos; - n = vsnprintf(cdb->p, r, fmt, f); - va_end(f); - if (n >= r) { - /* truncated, need bigger buffer */ - size_t ns = 2 * cdb->size; - u_char *nb; - - while ((ns - cdb->pos) <= n) - ns *= 2; - nb = kmalloc(ns, GFP_ATOMIC); - if (!nb) { - cdebbuf_free(cdb); - return NULL; - } - memcpy(nb, cdb->buf, cdb->pos); - kfree(cdb->buf); - nb[cdb->pos] = 0; - cdb->buf = nb; - cdb->p = cdb->buf + cdb->pos; - cdb->size = ns; - va_start(f, fmt); - r = cdb->size - cdb->pos; - n = vsnprintf(cdb->p, r, fmt, f); - va_end(f); - } - cdb->p += n; - cdb->pos += n; - return cdb; -} - -static _cdebbuf *printstructlen(_cdebbuf *cdb, u8 *m, unsigned len) -{ - unsigned hex = 0; - - if (!cdb) - return NULL; - for (; len; len--, m++) - if (isalnum(*m) || *m == ' ') { - if (hex) - cdb = bufprint(cdb, ">"); - cdb = bufprint(cdb, "%c", *m); - hex = 0; - } else { - if (!hex) - cdb = bufprint(cdb, "<%02x", *m); - else - cdb = bufprint(cdb, " %02x", *m); - hex = 1; - } - if (hex) - cdb = bufprint(cdb, ">"); - return cdb; -} - -static _cdebbuf *printstruct(_cdebbuf *cdb, u8 *m) -{ - unsigned len; - - if (m[0] != 0xff) { - len = m[0]; - m += 1; - } else { - len = ((u16 *) (m + 1))[0]; - m += 3; - } - cdb = printstructlen(cdb, m, len); - return cdb; -} - -/*-------------------------------------------------------*/ -#define NAME (pnames[cmsg->par[cmsg->p]]) - -static _cdebbuf *protocol_message_2_pars(_cdebbuf *cdb, _cmsg *cmsg, int level) -{ - if (!cmsg->par) - return NULL; /* invalid command/subcommand */ - - for (; TYP != _CEND; cmsg->p++) { - int slen = 29 + 3 - level; - int i; - - if (!cdb) - return NULL; - cdb = bufprint(cdb, " "); - for (i = 0; i < level - 1; i++) - cdb = bufprint(cdb, " "); - - switch (TYP) { - case _CBYTE: - cdb = bufprint(cdb, "%-*s = 0x%x\n", slen, NAME, *(u8 *) (cmsg->m + cmsg->l)); - cmsg->l++; - break; - case _CWORD: - cdb = bufprint(cdb, "%-*s = 0x%x\n", slen, NAME, *(u16 *) (cmsg->m + cmsg->l)); - cmsg->l += 2; - break; - case _CDWORD: - cdb = bufprint(cdb, "%-*s = 0x%lx\n", slen, NAME, *(u32 *) (cmsg->m + cmsg->l)); - cmsg->l += 4; - break; - case _CSTRUCT: - cdb = bufprint(cdb, "%-*s = ", slen, NAME); - if (cmsg->m[cmsg->l] == '\0') - cdb = bufprint(cdb, "default"); - else - cdb = printstruct(cdb, cmsg->m + cmsg->l); - cdb = bufprint(cdb, "\n"); - if (cmsg->m[cmsg->l] != 0xff) - cmsg->l += 1 + cmsg->m[cmsg->l]; - else - cmsg->l += 3 + *(u16 *) (cmsg->m + cmsg->l + 1); - - break; - - case _CMSTRUCT: -/*----- Metastruktur 0 -----*/ - if (cmsg->m[cmsg->l] == '\0') { - cdb = bufprint(cdb, "%-*s = default\n", slen, NAME); - cmsg->l++; - jumpcstruct(cmsg); - } else { - char *name = NAME; - unsigned _l = cmsg->l; - cdb = bufprint(cdb, "%-*s\n", slen, name); - cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1; - cmsg->p++; - cdb = protocol_message_2_pars(cdb, cmsg, level + 1); - } - break; - } - } - return cdb; -} -/*-------------------------------------------------------*/ - -static _cdebbuf *g_debbuf; -static u_long g_debbuf_lock; -static _cmsg *g_cmsg; - -static _cdebbuf *cdebbuf_alloc(void) -{ - _cdebbuf *cdb; - - if (likely(!test_and_set_bit(1, &g_debbuf_lock))) { - cdb = g_debbuf; - goto init; - } else - cdb = kmalloc_obj(_cdebbuf, GFP_ATOMIC); - if (!cdb) - return NULL; - cdb->buf = kmalloc(CDEBUG_SIZE, GFP_ATOMIC); - if (!cdb->buf) { - kfree(cdb); - return NULL; - } - cdb->size = CDEBUG_SIZE; -init: - cdb->buf[0] = 0; - cdb->p = cdb->buf; - cdb->pos = 0; - return cdb; -} - -/** - * cdebbuf_free() - free CAPI debug buffer - * @cdb: buffer to free - */ - -void cdebbuf_free(_cdebbuf *cdb) -{ - if (likely(cdb == g_debbuf)) { - test_and_clear_bit(1, &g_debbuf_lock); - return; - } - if (likely(cdb)) - kfree(cdb->buf); - kfree(cdb); -} - - -/** - * capi_message2str() - format CAPI 2.0 message for printing - * @msg: CAPI 2.0 message - * - * Allocates a CAPI debug buffer and fills it with a printable representation - * of the CAPI 2.0 message in @msg. - * Return value: allocated debug buffer, NULL on error - * The returned buffer should be freed by a call to cdebbuf_free() after use. - */ - -_cdebbuf *capi_message2str(u8 *msg) -{ - _cdebbuf *cdb; - _cmsg *cmsg; - - cdb = cdebbuf_alloc(); - if (unlikely(!cdb)) - return NULL; - if (likely(cdb == g_debbuf)) - cmsg = g_cmsg; - else - cmsg = kmalloc_obj(_cmsg, GFP_ATOMIC); - if (unlikely(!cmsg)) { - cdebbuf_free(cdb); - return NULL; - } - cmsg->m = msg; - cmsg->l = 8; - cmsg->p = 0; - byteTRcpy(cmsg->m + 4, &cmsg->Command); - byteTRcpy(cmsg->m + 5, &cmsg->Subcommand); - cmsg->par = capi_cmd2par(cmsg->Command, cmsg->Subcommand); - - cdb = bufprint(cdb, "%-26s ID=%03d #0x%04x LEN=%04d\n", - capi_cmd2str(cmsg->Command, cmsg->Subcommand), - ((unsigned short *) msg)[1], - ((unsigned short *) msg)[3], - ((unsigned short *) msg)[0]); - - cdb = protocol_message_2_pars(cdb, cmsg, 1); - if (unlikely(cmsg != g_cmsg)) - kfree(cmsg); - return cdb; -} - -int __init cdebug_init(void) -{ - g_cmsg = kmalloc_obj(_cmsg); - if (!g_cmsg) - return -ENOMEM; - g_debbuf = kmalloc_obj(_cdebbuf); - if (!g_debbuf) { - kfree(g_cmsg); - return -ENOMEM; - } - g_debbuf->buf = kmalloc(CDEBUG_GSIZE, GFP_KERNEL); - if (!g_debbuf->buf) { - kfree(g_cmsg); - kfree(g_debbuf); - return -ENOMEM; - } - g_debbuf->size = CDEBUG_GSIZE; - g_debbuf->buf[0] = 0; - g_debbuf->p = g_debbuf->buf; - g_debbuf->pos = 0; - return 0; -} - -void cdebug_exit(void) -{ - if (g_debbuf) - kfree(g_debbuf->buf); - kfree(g_debbuf); - kfree(g_cmsg); -} - -#else /* !CONFIG_CAPI_TRACE */ - -static _cdebbuf g_debbuf = {"CONFIG_CAPI_TRACE not enabled", NULL, 0, 0}; - -_cdebbuf *capi_message2str(u8 *msg) -{ - return &g_debbuf; -} - -_cdebbuf *capi_cmsg2str(_cmsg *cmsg) -{ - return &g_debbuf; -} - -void cdebbuf_free(_cdebbuf *cdb) -{ -} - -int __init cdebug_init(void) -{ - return 0; -} - -void cdebug_exit(void) -{ -} - -#endif diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c deleted file mode 100644 index f9fa17d68095..000000000000 --- a/drivers/isdn/capi/kcapi.c +++ /dev/null @@ -1,933 +0,0 @@ -/* $Id: kcapi.c,v 1.1.2.8 2004/03/26 19:57:20 armin Exp $ - * - * Kernel CAPI 2.0 Module - * - * Copyright 1999 by Carsten Paeth - * Copyright 2002 by Kai Germaschewski - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include "kcapi.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int showcapimsgs; -static struct workqueue_struct *kcapi_wq; - -module_param(showcapimsgs, uint, 0); - -/* ------------------------------------------------------------- */ - -struct capictr_event { - struct work_struct work; - unsigned int type; - u32 controller; -}; - -/* ------------------------------------------------------------- */ - -static const struct capi_version driver_version = {2, 0, 1, 1 << 4}; -static char driver_serial[CAPI_SERIAL_LEN] = "0004711"; -static char capi_manufakturer[64] = "AVM Berlin"; - -#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) - -struct capi_ctr *capi_controller[CAPI_MAXCONTR]; -DEFINE_MUTEX(capi_controller_lock); - -struct capi20_appl *capi_applications[CAPI_MAXAPPL]; - -static int ncontrollers; - -/* -------- controller ref counting -------------------------------------- */ - -static inline struct capi_ctr * -capi_ctr_get(struct capi_ctr *ctr) -{ - if (!try_module_get(ctr->owner)) - return NULL; - return ctr; -} - -static inline void -capi_ctr_put(struct capi_ctr *ctr) -{ - module_put(ctr->owner); -} - -/* ------------------------------------------------------------- */ - -static inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr) -{ - if (contr < 1 || contr - 1 >= CAPI_MAXCONTR) - return NULL; - - return capi_controller[contr - 1]; -} - -static inline struct capi20_appl *__get_capi_appl_by_nr(u16 applid) -{ - lockdep_assert_held(&capi_controller_lock); - - if (applid < 1 || applid - 1 >= CAPI_MAXAPPL) - return NULL; - - return capi_applications[applid - 1]; -} - -static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid) -{ - if (applid < 1 || applid - 1 >= CAPI_MAXAPPL) - return NULL; - - return rcu_dereference(capi_applications[applid - 1]); -} - -/* -------- util functions ------------------------------------ */ - -static inline int capi_cmd_valid(u8 cmd) -{ - switch (cmd) { - case CAPI_ALERT: - case CAPI_CONNECT: - case CAPI_CONNECT_ACTIVE: - case CAPI_CONNECT_B3_ACTIVE: - case CAPI_CONNECT_B3: - case CAPI_CONNECT_B3_T90_ACTIVE: - case CAPI_DATA_B3: - case CAPI_DISCONNECT_B3: - case CAPI_DISCONNECT: - case CAPI_FACILITY: - case CAPI_INFO: - case CAPI_LISTEN: - case CAPI_MANUFACTURER: - case CAPI_RESET_B3: - case CAPI_SELECT_B_PROTOCOL: - return 1; - } - return 0; -} - -static inline int capi_subcmd_valid(u8 subcmd) -{ - switch (subcmd) { - case CAPI_REQ: - case CAPI_CONF: - case CAPI_IND: - case CAPI_RESP: - return 1; - } - return 0; -} - -/* ------------------------------------------------------------ */ - -static void -register_appl(struct capi_ctr *ctr, u16 applid, capi_register_params *rparam) -{ - ctr = capi_ctr_get(ctr); - - if (ctr) - ctr->register_appl(ctr, applid, rparam); - else - printk(KERN_WARNING "%s: cannot get controller resources\n", - __func__); -} - - -static void release_appl(struct capi_ctr *ctr, u16 applid) -{ - DBG("applid %#x", applid); - - ctr->release_appl(ctr, applid); - capi_ctr_put(ctr); -} - -static void notify_up(u32 contr) -{ - struct capi20_appl *ap; - struct capi_ctr *ctr; - u16 applid; - - mutex_lock(&capi_controller_lock); - - if (showcapimsgs & 1) - printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr) { - if (ctr->state == CAPI_CTR_RUNNING) - goto unlock_out; - - ctr->state = CAPI_CTR_RUNNING; - - for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { - ap = __get_capi_appl_by_nr(applid); - if (ap) - register_appl(ctr, applid, &ap->rparam); - } - } else - printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); - -unlock_out: - mutex_unlock(&capi_controller_lock); -} - -static void ctr_down(struct capi_ctr *ctr, int new_state) -{ - struct capi20_appl *ap; - u16 applid; - - if (ctr->state == CAPI_CTR_DETECTED || ctr->state == CAPI_CTR_DETACHED) - return; - - ctr->state = new_state; - - memset(ctr->manu, 0, sizeof(ctr->manu)); - memset(&ctr->version, 0, sizeof(ctr->version)); - memset(&ctr->profile, 0, sizeof(ctr->profile)); - memset(ctr->serial, 0, sizeof(ctr->serial)); - - for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { - ap = __get_capi_appl_by_nr(applid); - if (ap) - capi_ctr_put(ctr); - } -} - -static void notify_down(u32 contr) -{ - struct capi_ctr *ctr; - - mutex_lock(&capi_controller_lock); - - if (showcapimsgs & 1) - printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr) - ctr_down(ctr, CAPI_CTR_DETECTED); - else - printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); - - mutex_unlock(&capi_controller_lock); -} - -static void do_notify_work(struct work_struct *work) -{ - struct capictr_event *event = - container_of(work, struct capictr_event, work); - - switch (event->type) { - case CAPICTR_UP: - notify_up(event->controller); - break; - case CAPICTR_DOWN: - notify_down(event->controller); - break; - } - - kfree(event); -} - -static int notify_push(unsigned int event_type, u32 controller) -{ - struct capictr_event *event = kmalloc_obj(*event, GFP_ATOMIC); - - if (!event) - return -ENOMEM; - - INIT_WORK(&event->work, do_notify_work); - event->type = event_type; - event->controller = controller; - - queue_work(kcapi_wq, &event->work); - return 0; -} - -/* -------- Receiver ------------------------------------------ */ - -static void recv_handler(struct work_struct *work) -{ - struct sk_buff *skb; - struct capi20_appl *ap = - container_of(work, struct capi20_appl, recv_work); - - if ((!ap) || (ap->release_in_progress)) - return; - - mutex_lock(&ap->recv_mtx); - while ((skb = skb_dequeue(&ap->recv_queue))) { - if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND) - ap->nrecvdatapkt++; - else - ap->nrecvctlpkt++; - - ap->recv_message(ap, skb); - } - mutex_unlock(&ap->recv_mtx); -} - -/** - * capi_ctr_handle_message() - handle incoming CAPI message - * @ctr: controller descriptor structure. - * @appl: application ID. - * @skb: message. - * - * Called by hardware driver to pass a CAPI message to the application. - */ - -void capi_ctr_handle_message(struct capi_ctr *ctr, u16 appl, - struct sk_buff *skb) -{ - struct capi20_appl *ap; - int showctl = 0; - u8 cmd, subcmd; - _cdebbuf *cdb; - - if (ctr->state != CAPI_CTR_RUNNING) { - cdb = capi_message2str(skb->data); - if (cdb) { - printk(KERN_INFO "kcapi: controller [%03d] not active, got: %s", - ctr->cnr, cdb->buf); - cdebbuf_free(cdb); - } else - printk(KERN_INFO "kcapi: controller [%03d] not active, cannot trace\n", - ctr->cnr); - goto error; - } - - cmd = CAPIMSG_COMMAND(skb->data); - subcmd = CAPIMSG_SUBCOMMAND(skb->data); - if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) { - ctr->nrecvdatapkt++; - if (ctr->traceflag > 2) - showctl |= 2; - } else { - ctr->nrecvctlpkt++; - if (ctr->traceflag) - showctl |= 2; - } - showctl |= (ctr->traceflag & 1); - if (showctl & 2) { - if (showctl & 1) { - printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u\n", - ctr->cnr, CAPIMSG_APPID(skb->data), - capi_cmd2str(cmd, subcmd), - CAPIMSG_LEN(skb->data)); - } else { - cdb = capi_message2str(skb->data); - if (cdb) { - printk(KERN_DEBUG "kcapi: got [%03d] %s\n", - ctr->cnr, cdb->buf); - cdebbuf_free(cdb); - } else - printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u, cannot trace\n", - ctr->cnr, CAPIMSG_APPID(skb->data), - capi_cmd2str(cmd, subcmd), - CAPIMSG_LEN(skb->data)); - } - - } - - rcu_read_lock(); - ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data)); - if (!ap) { - rcu_read_unlock(); - cdb = capi_message2str(skb->data); - if (cdb) { - printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n", - CAPIMSG_APPID(skb->data), cdb->buf); - cdebbuf_free(cdb); - } else - printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s) cannot trace\n", - CAPIMSG_APPID(skb->data), - capi_cmd2str(cmd, subcmd)); - goto error; - } - skb_queue_tail(&ap->recv_queue, skb); - queue_work(kcapi_wq, &ap->recv_work); - rcu_read_unlock(); - - return; - -error: - kfree_skb(skb); -} - -EXPORT_SYMBOL(capi_ctr_handle_message); - -/** - * capi_ctr_ready() - signal CAPI controller ready - * @ctr: controller descriptor structure. - * - * Called by hardware driver to signal that the controller is up and running. - */ - -void capi_ctr_ready(struct capi_ctr *ctr) -{ - printk(KERN_NOTICE "kcapi: controller [%03d] \"%s\" ready.\n", - ctr->cnr, ctr->name); - - notify_push(CAPICTR_UP, ctr->cnr); -} - -EXPORT_SYMBOL(capi_ctr_ready); - -/** - * capi_ctr_down() - signal CAPI controller not ready - * @ctr: controller descriptor structure. - * - * Called by hardware driver to signal that the controller is down and - * unavailable for use. - */ - -void capi_ctr_down(struct capi_ctr *ctr) -{ - printk(KERN_NOTICE "kcapi: controller [%03d] down.\n", ctr->cnr); - - notify_push(CAPICTR_DOWN, ctr->cnr); -} - -EXPORT_SYMBOL(capi_ctr_down); - -/* ------------------------------------------------------------- */ - -/** - * attach_capi_ctr() - register CAPI controller - * @ctr: controller descriptor structure. - * - * Called by hardware driver to register a controller with the CAPI subsystem. - * Return value: 0 on success, error code < 0 on error - */ - -int attach_capi_ctr(struct capi_ctr *ctr) -{ - int i; - - mutex_lock(&capi_controller_lock); - - for (i = 0; i < CAPI_MAXCONTR; i++) { - if (!capi_controller[i]) - break; - } - if (i == CAPI_MAXCONTR) { - mutex_unlock(&capi_controller_lock); - printk(KERN_ERR "kcapi: out of controller slots\n"); - return -EBUSY; - } - capi_controller[i] = ctr; - - ctr->nrecvctlpkt = 0; - ctr->nrecvdatapkt = 0; - ctr->nsentctlpkt = 0; - ctr->nsentdatapkt = 0; - ctr->cnr = i + 1; - ctr->state = CAPI_CTR_DETECTED; - ctr->blocked = 0; - ctr->traceflag = showcapimsgs; - - sprintf(ctr->procfn, "capi/controllers/%d", ctr->cnr); - ctr->procent = proc_create_single_data(ctr->procfn, 0, NULL, - ctr->proc_show, ctr); - - ncontrollers++; - - mutex_unlock(&capi_controller_lock); - - printk(KERN_NOTICE "kcapi: controller [%03d]: %s attached\n", - ctr->cnr, ctr->name); - return 0; -} - -EXPORT_SYMBOL(attach_capi_ctr); - -/** - * detach_capi_ctr() - unregister CAPI controller - * @ctr: controller descriptor structure. - * - * Called by hardware driver to remove the registration of a controller - * with the CAPI subsystem. - * Return value: 0 on success, error code < 0 on error - */ - -int detach_capi_ctr(struct capi_ctr *ctr) -{ - int err = 0; - - mutex_lock(&capi_controller_lock); - - ctr_down(ctr, CAPI_CTR_DETACHED); - - if (ctr->cnr < 1 || ctr->cnr - 1 >= CAPI_MAXCONTR) { - err = -EINVAL; - goto unlock_out; - } - - if (capi_controller[ctr->cnr - 1] != ctr) { - err = -EINVAL; - goto unlock_out; - } - capi_controller[ctr->cnr - 1] = NULL; - ncontrollers--; - - if (ctr->procent) - remove_proc_entry(ctr->procfn, NULL); - - printk(KERN_NOTICE "kcapi: controller [%03d]: %s unregistered\n", - ctr->cnr, ctr->name); - -unlock_out: - mutex_unlock(&capi_controller_lock); - - return err; -} - -EXPORT_SYMBOL(detach_capi_ctr); - -/* ------------------------------------------------------------- */ -/* -------- CAPI2.0 Interface ---------------------------------- */ -/* ------------------------------------------------------------- */ - -/** - * capi20_isinstalled() - CAPI 2.0 operation CAPI_INSTALLED - * - * Return value: CAPI result code (CAPI_NOERROR if at least one ISDN controller - * is ready for use, CAPI_REGNOTINSTALLED otherwise) - */ - -u16 capi20_isinstalled(void) -{ - u16 ret = CAPI_REGNOTINSTALLED; - int i; - - mutex_lock(&capi_controller_lock); - - for (i = 0; i < CAPI_MAXCONTR; i++) - if (capi_controller[i] && - capi_controller[i]->state == CAPI_CTR_RUNNING) { - ret = CAPI_NOERROR; - break; - } - - mutex_unlock(&capi_controller_lock); - - return ret; -} - -/** - * capi20_register() - CAPI 2.0 operation CAPI_REGISTER - * @ap: CAPI application descriptor structure. - * - * Register an application's presence with CAPI. - * A unique application ID is assigned and stored in @ap->applid. - * After this function returns successfully, the message receive - * callback function @ap->recv_message() may be called at any time - * until capi20_release() has been called for the same @ap. - * Return value: CAPI result code - */ - -u16 capi20_register(struct capi20_appl *ap) -{ - int i; - u16 applid; - - DBG(""); - - if (ap->rparam.datablklen < 128) - return CAPI_LOGBLKSIZETOSMALL; - - ap->nrecvctlpkt = 0; - ap->nrecvdatapkt = 0; - ap->nsentctlpkt = 0; - ap->nsentdatapkt = 0; - mutex_init(&ap->recv_mtx); - skb_queue_head_init(&ap->recv_queue); - INIT_WORK(&ap->recv_work, recv_handler); - ap->release_in_progress = 0; - - mutex_lock(&capi_controller_lock); - - for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { - if (capi_applications[applid - 1] == NULL) - break; - } - if (applid > CAPI_MAXAPPL) { - mutex_unlock(&capi_controller_lock); - return CAPI_TOOMANYAPPLS; - } - - ap->applid = applid; - capi_applications[applid - 1] = ap; - - for (i = 0; i < CAPI_MAXCONTR; i++) { - if (!capi_controller[i] || - capi_controller[i]->state != CAPI_CTR_RUNNING) - continue; - register_appl(capi_controller[i], applid, &ap->rparam); - } - - mutex_unlock(&capi_controller_lock); - - if (showcapimsgs & 1) { - printk(KERN_DEBUG "kcapi: appl %d up\n", applid); - } - - return CAPI_NOERROR; -} - -/** - * capi20_release() - CAPI 2.0 operation CAPI_RELEASE - * @ap: CAPI application descriptor structure. - * - * Terminate an application's registration with CAPI. - * After this function returns successfully, the message receive - * callback function @ap->recv_message() will no longer be called. - * Return value: CAPI result code - */ - -u16 capi20_release(struct capi20_appl *ap) -{ - int i; - - DBG("applid %#x", ap->applid); - - mutex_lock(&capi_controller_lock); - - ap->release_in_progress = 1; - capi_applications[ap->applid - 1] = NULL; - - synchronize_rcu(); - - for (i = 0; i < CAPI_MAXCONTR; i++) { - if (!capi_controller[i] || - capi_controller[i]->state != CAPI_CTR_RUNNING) - continue; - release_appl(capi_controller[i], ap->applid); - } - - mutex_unlock(&capi_controller_lock); - - flush_workqueue(kcapi_wq); - skb_queue_purge(&ap->recv_queue); - - if (showcapimsgs & 1) { - printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid); - } - - return CAPI_NOERROR; -} - -/** - * capi20_put_message() - CAPI 2.0 operation CAPI_PUT_MESSAGE - * @ap: CAPI application descriptor structure. - * @skb: CAPI message. - * - * Transfer a single message to CAPI. - * Return value: CAPI result code - */ - -u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb) -{ - struct capi_ctr *ctr; - int showctl = 0; - u8 cmd, subcmd; - - DBG("applid %#x", ap->applid); - - if (ncontrollers == 0) - return CAPI_REGNOTINSTALLED; - if ((ap->applid == 0) || ap->release_in_progress) - return CAPI_ILLAPPNR; - if (skb->len < 12 - || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data)) - || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data))) - return CAPI_ILLCMDORSUBCMDORMSGTOSMALL; - - /* - * The controller reference is protected by the existence of the - * application passed to us. We assume that the caller properly - * synchronizes this service with capi20_release. - */ - ctr = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data)); - if (!ctr || ctr->state != CAPI_CTR_RUNNING) - return CAPI_REGNOTINSTALLED; - if (ctr->blocked) - return CAPI_SENDQUEUEFULL; - - cmd = CAPIMSG_COMMAND(skb->data); - subcmd = CAPIMSG_SUBCOMMAND(skb->data); - - if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) { - ctr->nsentdatapkt++; - ap->nsentdatapkt++; - if (ctr->traceflag > 2) - showctl |= 2; - } else { - ctr->nsentctlpkt++; - ap->nsentctlpkt++; - if (ctr->traceflag) - showctl |= 2; - } - showctl |= (ctr->traceflag & 1); - if (showctl & 2) { - if (showctl & 1) { - printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u\n", - CAPIMSG_CONTROLLER(skb->data), - CAPIMSG_APPID(skb->data), - capi_cmd2str(cmd, subcmd), - CAPIMSG_LEN(skb->data)); - } else { - _cdebbuf *cdb = capi_message2str(skb->data); - if (cdb) { - printk(KERN_DEBUG "kcapi: put [%03d] %s\n", - CAPIMSG_CONTROLLER(skb->data), - cdb->buf); - cdebbuf_free(cdb); - } else - printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u cannot trace\n", - CAPIMSG_CONTROLLER(skb->data), - CAPIMSG_APPID(skb->data), - capi_cmd2str(cmd, subcmd), - CAPIMSG_LEN(skb->data)); - } - } - return ctr->send_message(ctr, skb); -} - -/** - * capi20_get_manufacturer() - CAPI 2.0 operation CAPI_GET_MANUFACTURER - * @contr: controller number. - * @buf: result buffer (64 bytes). - * - * Retrieve information about the manufacturer of the specified ISDN controller - * or (for @contr == 0) the driver itself. - * Return value: CAPI result code - */ - -u16 capi20_get_manufacturer(u32 contr, u8 buf[CAPI_MANUFACTURER_LEN]) -{ - struct capi_ctr *ctr; - u16 ret; - - if (contr == 0) { - strscpy_pad(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); - return CAPI_NOERROR; - } - - mutex_lock(&capi_controller_lock); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr && ctr->state == CAPI_CTR_RUNNING) { - strscpy_pad(buf, ctr->manu, CAPI_MANUFACTURER_LEN); - ret = CAPI_NOERROR; - } else - ret = CAPI_REGNOTINSTALLED; - - mutex_unlock(&capi_controller_lock); - return ret; -} - -/** - * capi20_get_version() - CAPI 2.0 operation CAPI_GET_VERSION - * @contr: controller number. - * @verp: result structure. - * - * Retrieve version information for the specified ISDN controller - * or (for @contr == 0) the driver itself. - * Return value: CAPI result code - */ - -u16 capi20_get_version(u32 contr, struct capi_version *verp) -{ - struct capi_ctr *ctr; - u16 ret; - - if (contr == 0) { - *verp = driver_version; - return CAPI_NOERROR; - } - - mutex_lock(&capi_controller_lock); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr && ctr->state == CAPI_CTR_RUNNING) { - memcpy(verp, &ctr->version, sizeof(capi_version)); - ret = CAPI_NOERROR; - } else - ret = CAPI_REGNOTINSTALLED; - - mutex_unlock(&capi_controller_lock); - return ret; -} - -/** - * capi20_get_serial() - CAPI 2.0 operation CAPI_GET_SERIAL_NUMBER - * @contr: controller number. - * @serial: result buffer (8 bytes). - * - * Retrieve the serial number of the specified ISDN controller - * or (for @contr == 0) the driver itself. - * Return value: CAPI result code - */ - -u16 capi20_get_serial(u32 contr, u8 serial[CAPI_SERIAL_LEN]) -{ - struct capi_ctr *ctr; - u16 ret; - - if (contr == 0) { - strscpy(serial, driver_serial, CAPI_SERIAL_LEN); - return CAPI_NOERROR; - } - - mutex_lock(&capi_controller_lock); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr && ctr->state == CAPI_CTR_RUNNING) { - strscpy(serial, ctr->serial, CAPI_SERIAL_LEN); - ret = CAPI_NOERROR; - } else - ret = CAPI_REGNOTINSTALLED; - - mutex_unlock(&capi_controller_lock); - return ret; -} - -/** - * capi20_get_profile() - CAPI 2.0 operation CAPI_GET_PROFILE - * @contr: controller number. - * @profp: result structure. - * - * Retrieve capability information for the specified ISDN controller - * or (for @contr == 0) the number of installed controllers. - * Return value: CAPI result code - */ - -u16 capi20_get_profile(u32 contr, struct capi_profile *profp) -{ - struct capi_ctr *ctr; - u16 ret; - - if (contr == 0) { - profp->ncontroller = ncontrollers; - return CAPI_NOERROR; - } - - mutex_lock(&capi_controller_lock); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr && ctr->state == CAPI_CTR_RUNNING) { - memcpy(profp, &ctr->profile, sizeof(struct capi_profile)); - ret = CAPI_NOERROR; - } else - ret = CAPI_REGNOTINSTALLED; - - mutex_unlock(&capi_controller_lock); - return ret; -} - -/** - * capi20_manufacturer() - CAPI 2.0 operation CAPI_MANUFACTURER - * @cmd: command. - * @data: parameter. - * - * Perform manufacturer specific command. - * Return value: CAPI result code - */ - -int capi20_manufacturer(unsigned long cmd, void __user *data) -{ - struct capi_ctr *ctr; - int retval; - - switch (cmd) { - case KCAPI_CMD_TRACE: - { - kcapi_flagdef fdef; - - if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef))) - return -EFAULT; - - mutex_lock(&capi_controller_lock); - - ctr = get_capi_ctr_by_nr(fdef.contr); - if (ctr) { - ctr->traceflag = fdef.flag; - printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n", - ctr->cnr, ctr->traceflag); - retval = 0; - } else - retval = -ESRCH; - - mutex_unlock(&capi_controller_lock); - - return retval; - } - - default: - printk(KERN_ERR "kcapi: manufacturer command %lu unknown.\n", - cmd); - break; - - } - return -EINVAL; -} - -/* ------------------------------------------------------------- */ -/* -------- Init & Cleanup ------------------------------------- */ -/* ------------------------------------------------------------- */ - -/* - * init / exit functions - */ - -int __init kcapi_init(void) -{ - int err; - - kcapi_wq = alloc_workqueue("kcapi", WQ_PERCPU, 0); - if (!kcapi_wq) - return -ENOMEM; - - err = cdebug_init(); - if (err) { - destroy_workqueue(kcapi_wq); - return err; - } - - if (IS_ENABLED(CONFIG_PROC_FS)) - kcapi_proc_init(); - - return 0; -} - -void kcapi_exit(void) -{ - if (IS_ENABLED(CONFIG_PROC_FS)) - kcapi_proc_exit(); - - cdebug_exit(); - destroy_workqueue(kcapi_wq); -} diff --git a/drivers/isdn/capi/kcapi.h b/drivers/isdn/capi/kcapi.h deleted file mode 100644 index 479623e1db2a..000000000000 --- a/drivers/isdn/capi/kcapi.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Kernel CAPI 2.0 Module - * - * Copyright 1999 by Carsten Paeth - * Copyright 2002 by Kai Germaschewski - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - - -#include -#include -#include -#include - -#ifdef KCAPI_DEBUG -#define DBG(format, arg...) do { \ - printk(KERN_DEBUG "%s: " format "\n" , __func__ , ## arg); \ - } while (0) -#else -#define DBG(format, arg...) /* */ -#endif - -enum { - CAPI_CTR_DETACHED = 0, - CAPI_CTR_DETECTED = 1, - CAPI_CTR_LOADING = 2, - CAPI_CTR_RUNNING = 3, -}; - -extern struct capi_ctr *capi_controller[CAPI_MAXCONTR]; -extern struct mutex capi_controller_lock; - -extern struct capi20_appl *capi_applications[CAPI_MAXAPPL]; - -void kcapi_proc_init(void); -void kcapi_proc_exit(void); - -struct capi20_appl { - u16 applid; - capi_register_params rparam; - void (*recv_message)(struct capi20_appl *ap, struct sk_buff *skb); - void *private; - - /* internal to kernelcapi.o */ - unsigned long nrecvctlpkt; - unsigned long nrecvdatapkt; - unsigned long nsentctlpkt; - unsigned long nsentdatapkt; - struct mutex recv_mtx; - struct sk_buff_head recv_queue; - struct work_struct recv_work; - int release_in_progress; -}; - -u16 capi20_isinstalled(void); -u16 capi20_register(struct capi20_appl *ap); -u16 capi20_release(struct capi20_appl *ap); -u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb); -u16 capi20_get_manufacturer(u32 contr, u8 buf[CAPI_MANUFACTURER_LEN]); -u16 capi20_get_version(u32 contr, struct capi_version *verp); -u16 capi20_get_serial(u32 contr, u8 serial[CAPI_SERIAL_LEN]); -u16 capi20_get_profile(u32 contr, struct capi_profile *profp); -int capi20_manufacturer(unsigned long cmd, void __user *data); - -#define CAPICTR_UP 0 -#define CAPICTR_DOWN 1 - -int kcapi_init(void); -void kcapi_exit(void); - -/*----- basic-type definitions -----*/ - -typedef __u8 *_cstruct; - -typedef enum { - CAPI_COMPOSE, - CAPI_DEFAULT -} _cmstruct; - -/* - The _cmsg structure contains all possible CAPI 2.0 parameter. - All parameters are stored here first. The function CAPI_CMSG_2_MESSAGE - assembles the parameter and builds CAPI2.0 conform messages. - CAPI_MESSAGE_2_CMSG disassembles CAPI 2.0 messages and stores the - parameter in the _cmsg structure - */ - -typedef struct { - /* Header */ - __u16 ApplId; - __u8 Command; - __u8 Subcommand; - __u16 Messagenumber; - - /* Parameter */ - union { - __u32 adrController; - __u32 adrPLCI; - __u32 adrNCCI; - } adr; - - _cmstruct AdditionalInfo; - _cstruct B1configuration; - __u16 B1protocol; - _cstruct B2configuration; - __u16 B2protocol; - _cstruct B3configuration; - __u16 B3protocol; - _cstruct BC; - _cstruct BChannelinformation; - _cmstruct BProtocol; - _cstruct CalledPartyNumber; - _cstruct CalledPartySubaddress; - _cstruct CallingPartyNumber; - _cstruct CallingPartySubaddress; - __u32 CIPmask; - __u32 CIPmask2; - __u16 CIPValue; - __u32 Class; - _cstruct ConnectedNumber; - _cstruct ConnectedSubaddress; - __u32 Data; - __u16 DataHandle; - __u16 DataLength; - _cstruct FacilityConfirmationParameter; - _cstruct Facilitydataarray; - _cstruct FacilityIndicationParameter; - _cstruct FacilityRequestParameter; - __u16 FacilitySelector; - __u16 Flags; - __u32 Function; - _cstruct HLC; - __u16 Info; - _cstruct InfoElement; - __u32 InfoMask; - __u16 InfoNumber; - _cstruct Keypadfacility; - _cstruct LLC; - _cstruct ManuData; - __u32 ManuID; - _cstruct NCPI; - __u16 Reason; - __u16 Reason_B3; - __u16 Reject; - _cstruct Useruserdata; - - /* intern */ - unsigned l, p; - unsigned char *par; - __u8 *m; - - /* buffer to construct message */ - __u8 buf[180]; - -} _cmsg; - -/*-----------------------------------------------------------------------*/ - -/* - * Debugging / Tracing functions - */ - -char *capi_cmd2str(__u8 cmd, __u8 subcmd); - -typedef struct { - u_char *buf; - u_char *p; - size_t size; - size_t pos; -} _cdebbuf; - -#define CDEBUG_SIZE 1024 -#define CDEBUG_GSIZE 4096 - -void cdebbuf_free(_cdebbuf *cdb); -int cdebug_init(void); -void cdebug_exit(void); - -_cdebbuf *capi_message2str(__u8 *msg); diff --git a/drivers/isdn/capi/kcapi_proc.c b/drivers/isdn/capi/kcapi_proc.c deleted file mode 100644 index 77e951206809..000000000000 --- a/drivers/isdn/capi/kcapi_proc.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Kernel CAPI 2.0 Module - /proc/capi handling - * - * Copyright 1999 by Carsten Paeth - * Copyright 2002 by Kai Germaschewski - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - - -#include "kcapi.h" -#include -#include -#include -#include - -static char *state2str(unsigned short state) -{ - switch (state) { - case CAPI_CTR_DETECTED: return "detected"; - case CAPI_CTR_LOADING: return "loading"; - case CAPI_CTR_RUNNING: return "running"; - default: return "???"; - } -} - -// /proc/capi -// =========================================================================== - -// /proc/capi/controller: -// cnr driver cardstate name driverinfo -// /proc/capi/contrstats: -// cnr nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt -// --------------------------------------------------------------------------- - -static void *controller_start(struct seq_file *seq, loff_t *pos) - __acquires(capi_controller_lock) -{ - mutex_lock(&capi_controller_lock); - - if (*pos < CAPI_MAXCONTR) - return &capi_controller[*pos]; - - return NULL; -} - -static void *controller_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - if (*pos < CAPI_MAXCONTR) - return &capi_controller[*pos]; - - return NULL; -} - -static void controller_stop(struct seq_file *seq, void *v) - __releases(capi_controller_lock) -{ - mutex_unlock(&capi_controller_lock); -} - -static int controller_show(struct seq_file *seq, void *v) -{ - struct capi_ctr *ctr = *(struct capi_ctr **) v; - - if (!ctr) - return 0; - - seq_printf(seq, "%d %-10s %-8s %-16s %s\n", - ctr->cnr, ctr->driver_name, - state2str(ctr->state), - ctr->name, - ctr->procinfo ? ctr->procinfo(ctr) : ""); - - return 0; -} - -static int contrstats_show(struct seq_file *seq, void *v) -{ - struct capi_ctr *ctr = *(struct capi_ctr **) v; - - if (!ctr) - return 0; - - seq_printf(seq, "%d %lu %lu %lu %lu\n", - ctr->cnr, - ctr->nrecvctlpkt, - ctr->nrecvdatapkt, - ctr->nsentctlpkt, - ctr->nsentdatapkt); - - return 0; -} - -static const struct seq_operations seq_controller_ops = { - .start = controller_start, - .next = controller_next, - .stop = controller_stop, - .show = controller_show, -}; - -static const struct seq_operations seq_contrstats_ops = { - .start = controller_start, - .next = controller_next, - .stop = controller_stop, - .show = contrstats_show, -}; - -// /proc/capi/applications: -// applid l3cnt dblkcnt dblklen #ncci recvqueuelen -// /proc/capi/applstats: -// applid nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt -// --------------------------------------------------------------------------- - -static void *applications_start(struct seq_file *seq, loff_t *pos) - __acquires(capi_controller_lock) -{ - mutex_lock(&capi_controller_lock); - - if (*pos < CAPI_MAXAPPL) - return &capi_applications[*pos]; - - return NULL; -} - -static void * -applications_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - if (*pos < CAPI_MAXAPPL) - return &capi_applications[*pos]; - - return NULL; -} - -static void applications_stop(struct seq_file *seq, void *v) - __releases(capi_controller_lock) -{ - mutex_unlock(&capi_controller_lock); -} - -static int -applications_show(struct seq_file *seq, void *v) -{ - struct capi20_appl *ap = *(struct capi20_appl **) v; - - if (!ap) - return 0; - - seq_printf(seq, "%u %d %d %d\n", - ap->applid, - ap->rparam.level3cnt, - ap->rparam.datablkcnt, - ap->rparam.datablklen); - - return 0; -} - -static int -applstats_show(struct seq_file *seq, void *v) -{ - struct capi20_appl *ap = *(struct capi20_appl **) v; - - if (!ap) - return 0; - - seq_printf(seq, "%u %lu %lu %lu %lu\n", - ap->applid, - ap->nrecvctlpkt, - ap->nrecvdatapkt, - ap->nsentctlpkt, - ap->nsentdatapkt); - - return 0; -} - -static const struct seq_operations seq_applications_ops = { - .start = applications_start, - .next = applications_next, - .stop = applications_stop, - .show = applications_show, -}; - -static const struct seq_operations seq_applstats_ops = { - .start = applications_start, - .next = applications_next, - .stop = applications_stop, - .show = applstats_show, -}; - -// --------------------------------------------------------------------------- - -/* /proc/capi/drivers is always empty */ -static ssize_t empty_read(struct file *file, char __user *buf, - size_t size, loff_t *off) -{ - return 0; -} - -static const struct proc_ops empty_proc_ops = { - .proc_read = empty_read, - .proc_lseek = default_llseek, -}; - -// --------------------------------------------------------------------------- - -void __init -kcapi_proc_init(void) -{ - proc_mkdir("capi", NULL); - proc_mkdir("capi/controllers", NULL); - proc_create_seq("capi/controller", 0, NULL, &seq_controller_ops); - proc_create_seq("capi/contrstats", 0, NULL, &seq_contrstats_ops); - proc_create_seq("capi/applications", 0, NULL, &seq_applications_ops); - proc_create_seq("capi/applstats", 0, NULL, &seq_applstats_ops); - proc_create("capi/driver", 0, NULL, &empty_proc_ops); -} - -void -kcapi_proc_exit(void) -{ - remove_proc_entry("capi/driver", NULL); - remove_proc_entry("capi/controller", NULL); - remove_proc_entry("capi/contrstats", NULL); - remove_proc_entry("capi/applications", NULL); - remove_proc_entry("capi/applstats", NULL); - remove_proc_entry("capi/controllers", NULL); - remove_proc_entry("capi", NULL); -} diff --git a/drivers/isdn/hardware/Makefile b/drivers/isdn/hardware/Makefile deleted file mode 100644 index 96f9eb2e46ba..000000000000 --- a/drivers/isdn/hardware/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# Makefile for the CAPI hardware drivers - -# Object files in subdirectories - -obj-$(CONFIG_MISDN) += mISDN/ diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig deleted file mode 100644 index a35bff8a93f5..000000000000 --- a/drivers/isdn/hardware/mISDN/Kconfig +++ /dev/null @@ -1,98 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Hardware for mISDN -# -comment "mISDN hardware drivers" - -config MISDN_HFCPCI - tristate "Support for HFC PCI cards" - depends on MISDN - depends on PCI - help - Enable support for cards with Cologne Chip AG's - HFC PCI chip. - -config MISDN_HFCMULTI - tristate "Support for HFC multiport cards (HFC-4S/8S/E1)" - depends on (PCI || CPM1) && HAS_IOPORT - depends on MISDN - help - Enable support for cards with Cologne Chip AG's HFC multiport - chip. There are three types of chips that are quite similar, - but the interface is different: - * HFC-4S (4 S/T interfaces on one chip) - * HFC-8S (8 S/T interfaces on one chip) - * HFC-E1 (E1 interface for 2Mbit ISDN) - -config MISDN_HFCMULTI_8xx - bool "Support for XHFC embedded board in HFC multiport driver" - depends on MISDN - depends on MISDN_HFCMULTI - depends on CPM1 - default CPM1 - help - Enable support for the XHFC embedded solution from Speech Design. - -config MISDN_HFCUSB - tristate "Support for HFC-S USB based TAs" - depends on USB - help - Enable support for USB ISDN TAs with Cologne Chip AG's - HFC-S USB ISDN Controller - -config MISDN_AVMFRITZ - tristate "Support for AVM FRITZ!CARD PCI" - depends on MISDN - depends on PCI && HAS_IOPORT - select MISDN_IPAC - help - Enable support for AVMs FRITZ!CARD PCI cards - -config MISDN_SPEEDFAX - tristate "Support for Sedlbauer Speedfax+" - depends on MISDN - depends on PCI && HAS_IOPORT - select MISDN_IPAC - select MISDN_ISAR - help - Enable support for Sedlbauer Speedfax+. - -config MISDN_INFINEON - tristate "Support for cards with Infineon chipset" - depends on MISDN - depends on PCI && HAS_IOPORT - select MISDN_IPAC - help - Enable support for cards with ISAC + HSCX, IPAC or IPAC-SX - chip from Infineon (former manufacturer Siemens). - -config MISDN_W6692 - tristate "Support for cards with Winbond 6692" - depends on MISDN - depends on PCI && HAS_IOPORT - help - Enable support for Winbond 6692 PCI chip based cards. - -config MISDN_NETJET - tristate "Support for NETJet cards" - depends on MISDN - depends on PCI && HAS_IOPORT - depends on TTY - select MISDN_IPAC - select MISDN_HDLC - help - Enable support for Traverse Technologies NETJet PCI cards. - -config MISDN_HDLC - tristate - select CRC_CCITT - select BITREVERSE - -config MISDN_IPAC - tristate - depends on MISDN - -config MISDN_ISAR - tristate - depends on MISDN - diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile deleted file mode 100644 index 3f50f8c4753f..000000000000 --- a/drivers/isdn/hardware/mISDN/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the modular ISDN hardware drivers -# -# - -obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o -obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o -obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o -obj-$(CONFIG_MISDN_AVMFRITZ) += avmfritz.o -obj-$(CONFIG_MISDN_SPEEDFAX) += speedfax.o -obj-$(CONFIG_MISDN_INFINEON) += mISDNinfineon.o -obj-$(CONFIG_MISDN_W6692) += w6692.o -obj-$(CONFIG_MISDN_NETJET) += netjet.o -# chip modules -obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o -obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o - -obj-$(CONFIG_MISDN_HDLC) += isdnhdlc.o diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c deleted file mode 100644 index 55e0b9efa194..000000000000 --- a/drivers/isdn/hardware/mISDN/avmfritz.c +++ /dev/null @@ -1,1164 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * avm_fritz.c low level stuff for AVM FRITZ!CARD PCI ISDN cards - * Thanks to AVM, Berlin for informations - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ -#include -#include -#include -#include -#include -#include -#include -#include "ipac.h" - - -#define AVMFRITZ_REV "2.3" - -static int AVM_cnt; -static int debug; - -enum { - AVM_FRITZ_PCI, - AVM_FRITZ_PCIV2, -}; - -#define HDLC_FIFO 0x0 -#define HDLC_STATUS 0x4 -#define CHIP_WINDOW 0x10 - -#define CHIP_INDEX 0x4 -#define AVM_HDLC_1 0x00 -#define AVM_HDLC_2 0x01 -#define AVM_ISAC_FIFO 0x02 -#define AVM_ISAC_REG_LOW 0x04 -#define AVM_ISAC_REG_HIGH 0x06 - -#define AVM_STATUS0_IRQ_ISAC 0x01 -#define AVM_STATUS0_IRQ_HDLC 0x02 -#define AVM_STATUS0_IRQ_TIMER 0x04 -#define AVM_STATUS0_IRQ_MASK 0x07 - -#define AVM_STATUS0_RESET 0x01 -#define AVM_STATUS0_DIS_TIMER 0x02 -#define AVM_STATUS0_RES_TIMER 0x04 -#define AVM_STATUS0_ENA_IRQ 0x08 -#define AVM_STATUS0_TESTBIT 0x10 - -#define AVM_STATUS1_INT_SEL 0x0f -#define AVM_STATUS1_ENA_IOM 0x80 - -#define HDLC_MODE_ITF_FLG 0x01 -#define HDLC_MODE_TRANS 0x02 -#define HDLC_MODE_CCR_7 0x04 -#define HDLC_MODE_CCR_16 0x08 -#define HDLC_FIFO_SIZE_128 0x20 -#define HDLC_MODE_TESTLOOP 0x80 - -#define HDLC_INT_XPR 0x80 -#define HDLC_INT_XDU 0x40 -#define HDLC_INT_RPR 0x20 -#define HDLC_INT_MASK 0xE0 - -#define HDLC_STAT_RME 0x01 -#define HDLC_STAT_RDO 0x10 -#define HDLC_STAT_CRCVFRRAB 0x0E -#define HDLC_STAT_CRCVFR 0x06 -#define HDLC_STAT_RML_MASK_V1 0x3f00 -#define HDLC_STAT_RML_MASK_V2 0x7f00 - -#define HDLC_CMD_XRS 0x80 -#define HDLC_CMD_XME 0x01 -#define HDLC_CMD_RRS 0x20 -#define HDLC_CMD_XML_MASK 0x3f00 - -#define HDLC_FIFO_SIZE_V1 32 -#define HDLC_FIFO_SIZE_V2 128 - -/* Fritz PCI v2.0 */ - -#define AVM_HDLC_FIFO_1 0x10 -#define AVM_HDLC_FIFO_2 0x18 - -#define AVM_HDLC_STATUS_1 0x14 -#define AVM_HDLC_STATUS_2 0x1c - -#define AVM_ISACX_INDEX 0x04 -#define AVM_ISACX_DATA 0x08 - -/* data struct */ -#define LOG_SIZE 63 - -struct hdlc_stat_reg { -#ifdef __BIG_ENDIAN - u8 fill; - u8 mode; - u8 xml; - u8 cmd; -#else - u8 cmd; - u8 xml; - u8 mode; - u8 fill; -#endif -} __attribute__((packed)); - -struct hdlc_hw { - union { - u32 ctrl; - struct hdlc_stat_reg sr; - } ctrl; - u32 stat; -}; - -struct fritzcard { - struct list_head list; - struct pci_dev *pdev; - char name[MISDN_MAX_IDLEN]; - u8 type; - u8 ctrlreg; - u16 irq; - u32 irqcnt; - u32 addr; - spinlock_t lock; /* hw lock */ - struct isac_hw isac; - struct hdlc_hw hdlc[2]; - struct bchannel bch[2]; - char log[LOG_SIZE + 1]; -}; - -static LIST_HEAD(Cards); -static DEFINE_RWLOCK(card_lock); /* protect Cards */ - -static void -_set_debug(struct fritzcard *card) -{ - card->isac.dch.debug = debug; - card->bch[0].debug = debug; - card->bch[1].debug = debug; -} - -static int -set_debug(const char *val, const struct kernel_param *kp) -{ - int ret; - struct fritzcard *card; - - ret = param_set_uint(val, kp); - if (!ret) { - read_lock(&card_lock); - list_for_each_entry(card, &Cards, list) - _set_debug(card); - read_unlock(&card_lock); - } - return ret; -} - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for AVM FRITZ!CARD PCI ISDN cards"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(AVMFRITZ_REV); -module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "avmfritz debug mask"); - -/* Interface functions */ - -static u8 -ReadISAC_V1(void *p, u8 offset) -{ - struct fritzcard *fc = p; - u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; - - outb(idx, fc->addr + CHIP_INDEX); - return inb(fc->addr + CHIP_WINDOW + (offset & 0xf)); -} - -static void -WriteISAC_V1(void *p, u8 offset, u8 value) -{ - struct fritzcard *fc = p; - u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; - - outb(idx, fc->addr + CHIP_INDEX); - outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf)); -} - -static void -ReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size) -{ - struct fritzcard *fc = p; - - outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); - insb(fc->addr + CHIP_WINDOW, data, size); -} - -static void -WriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size) -{ - struct fritzcard *fc = p; - - outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); - outsb(fc->addr + CHIP_WINDOW, data, size); -} - -static u8 -ReadISAC_V2(void *p, u8 offset) -{ - struct fritzcard *fc = p; - - outl(offset, fc->addr + AVM_ISACX_INDEX); - return 0xff & inl(fc->addr + AVM_ISACX_DATA); -} - -static void -WriteISAC_V2(void *p, u8 offset, u8 value) -{ - struct fritzcard *fc = p; - - outl(offset, fc->addr + AVM_ISACX_INDEX); - outl(value, fc->addr + AVM_ISACX_DATA); -} - -static void -ReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size) -{ - struct fritzcard *fc = p; - int i; - - outl(off, fc->addr + AVM_ISACX_INDEX); - for (i = 0; i < size; i++) - data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA); -} - -static void -WriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size) -{ - struct fritzcard *fc = p; - int i; - - outl(off, fc->addr + AVM_ISACX_INDEX); - for (i = 0; i < size; i++) - outl(data[i], fc->addr + AVM_ISACX_DATA); -} - -static struct bchannel * -Sel_BCS(struct fritzcard *fc, u32 channel) -{ - if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) && - (fc->bch[0].nr & channel)) - return &fc->bch[0]; - else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) && - (fc->bch[1].nr & channel)) - return &fc->bch[1]; - else - return NULL; -} - -static inline void -__write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { - u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1; - - outl(idx, fc->addr + CHIP_INDEX); - outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS); -} - -static inline void -__write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { - outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 : - AVM_HDLC_STATUS_1)); -} - -static void -write_ctrl(struct bchannel *bch, int which) { - struct fritzcard *fc = bch->hw; - struct hdlc_hw *hdlc; - - hdlc = &fc->hdlc[(bch->nr - 1) & 1]; - pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr, - which, hdlc->ctrl.ctrl); - switch (fc->type) { - case AVM_FRITZ_PCIV2: - __write_ctrl_pciv2(fc, hdlc, bch->nr); - break; - case AVM_FRITZ_PCI: - __write_ctrl_pci(fc, hdlc, bch->nr); - break; - } -} - - -static inline u32 -__read_status_pci(u_long addr, u32 channel) -{ - outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX); - return inl(addr + CHIP_WINDOW + HDLC_STATUS); -} - -static inline u32 -__read_status_pciv2(u_long addr, u32 channel) -{ - return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 : - AVM_HDLC_STATUS_1)); -} - - -static u32 -read_status(struct fritzcard *fc, u32 channel) -{ - switch (fc->type) { - case AVM_FRITZ_PCIV2: - return __read_status_pciv2(fc->addr, channel); - case AVM_FRITZ_PCI: - return __read_status_pci(fc->addr, channel); - } - /* dummy */ - return 0; -} - -static void -enable_hwirq(struct fritzcard *fc) -{ - fc->ctrlreg |= AVM_STATUS0_ENA_IRQ; - outb(fc->ctrlreg, fc->addr + 2); -} - -static void -disable_hwirq(struct fritzcard *fc) -{ - fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ; - outb(fc->ctrlreg, fc->addr + 2); -} - -static int -modehdlc(struct bchannel *bch, int protocol) -{ - struct fritzcard *fc = bch->hw; - struct hdlc_hw *hdlc; - u8 mode; - - hdlc = &fc->hdlc[(bch->nr - 1) & 1]; - pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name, - '@' + bch->nr, bch->state, protocol, bch->nr); - hdlc->ctrl.ctrl = 0; - mode = (fc->type == AVM_FRITZ_PCIV2) ? HDLC_FIFO_SIZE_128 : 0; - - switch (protocol) { - case -1: /* used for init */ - bch->state = -1; - fallthrough; - case ISDN_P_NONE: - if (bch->state == ISDN_P_NONE) - break; - hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; - hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; - write_ctrl(bch, 5); - bch->state = ISDN_P_NONE; - test_and_clear_bit(FLG_HDLC, &bch->Flags); - test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case ISDN_P_B_RAW: - bch->state = protocol; - hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; - hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; - write_ctrl(bch, 5); - hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; - write_ctrl(bch, 1); - hdlc->ctrl.sr.cmd = 0; - test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case ISDN_P_B_HDLC: - bch->state = protocol; - hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; - hdlc->ctrl.sr.mode = mode | HDLC_MODE_ITF_FLG; - write_ctrl(bch, 5); - hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; - write_ctrl(bch, 1); - hdlc->ctrl.sr.cmd = 0; - test_and_set_bit(FLG_HDLC, &bch->Flags); - break; - default: - pr_info("%s: protocol not known %x\n", fc->name, protocol); - return -ENOPROTOOPT; - } - return 0; -} - -static void -hdlc_empty_fifo(struct bchannel *bch, int count) -{ - u32 *ptr; - u8 *p; - u32 val, addr; - int cnt; - struct fritzcard *fc = bch->hw; - - pr_debug("%s: %s %d\n", fc->name, __func__, count); - if (test_bit(FLG_RX_OFF, &bch->Flags)) { - p = NULL; - bch->dropcnt += count; - } else { - cnt = bchannel_get_rxbuf(bch, count); - if (cnt < 0) { - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - fc->name, bch->nr, count); - return; - } - p = skb_put(bch->rx_skb, count); - } - ptr = (u32 *)p; - if (fc->type == AVM_FRITZ_PCIV2) - addr = fc->addr + (bch->nr == 2 ? - AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); - else { - addr = fc->addr + CHIP_WINDOW; - outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr); - } - cnt = 0; - while (cnt < count) { - val = le32_to_cpu(inl(addr)); - if (p) { - put_unaligned(val, ptr); - ptr++; - } - cnt += 4; - } - if (p && (debug & DEBUG_HW_BFIFO)) { - snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ", - bch->nr, fc->name, count); - print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); - } -} - -static void -hdlc_fill_fifo(struct bchannel *bch) -{ - struct fritzcard *fc = bch->hw; - struct hdlc_hw *hdlc; - int count, fs, cnt = 0, idx; - bool fillempty = false; - u8 *p; - u32 *ptr, val, addr; - - idx = (bch->nr - 1) & 1; - hdlc = &fc->hdlc[idx]; - fs = (fc->type == AVM_FRITZ_PCIV2) ? - HDLC_FIFO_SIZE_V2 : HDLC_FIFO_SIZE_V1; - if (!bch->tx_skb) { - if (!test_bit(FLG_TX_EMPTY, &bch->Flags)) - return; - count = fs; - p = bch->fill; - fillempty = true; - } else { - count = bch->tx_skb->len - bch->tx_idx; - if (count <= 0) - return; - p = bch->tx_skb->data + bch->tx_idx; - } - hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME; - if (count > fs) { - count = fs; - } else { - if (test_bit(FLG_HDLC, &bch->Flags)) - hdlc->ctrl.sr.cmd |= HDLC_CMD_XME; - } - ptr = (u32 *)p; - if (!fillempty) { - pr_debug("%s.B%d: %d/%d/%d", fc->name, bch->nr, count, - bch->tx_idx, bch->tx_skb->len); - bch->tx_idx += count; - } else { - pr_debug("%s.B%d: fillempty %d\n", fc->name, bch->nr, count); - } - hdlc->ctrl.sr.xml = ((count == fs) ? 0 : count); - if (fc->type == AVM_FRITZ_PCIV2) { - __write_ctrl_pciv2(fc, hdlc, bch->nr); - addr = fc->addr + (bch->nr == 2 ? - AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); - } else { - __write_ctrl_pci(fc, hdlc, bch->nr); - addr = fc->addr + CHIP_WINDOW; - } - if (fillempty) { - while (cnt < count) { - /* all bytes the same - no worry about endian */ - outl(*ptr, addr); - cnt += 4; - } - } else { - while (cnt < count) { - val = get_unaligned(ptr); - outl(cpu_to_le32(val), addr); - ptr++; - cnt += 4; - } - } - if ((debug & DEBUG_HW_BFIFO) && !fillempty) { - snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ", - bch->nr, fc->name, count); - print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); - } -} - -static void -HDLC_irq_xpr(struct bchannel *bch) -{ - if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) { - hdlc_fill_fifo(bch); - } else { - dev_kfree_skb(bch->tx_skb); - if (get_next_bframe(bch)) { - hdlc_fill_fifo(bch); - test_and_clear_bit(FLG_TX_EMPTY, &bch->Flags); - } else if (test_bit(FLG_TX_EMPTY, &bch->Flags)) { - hdlc_fill_fifo(bch); - } - } -} - -static void -HDLC_irq(struct bchannel *bch, u32 stat) -{ - struct fritzcard *fc = bch->hw; - int len, fs; - u32 rmlMask; - struct hdlc_hw *hdlc; - - hdlc = &fc->hdlc[(bch->nr - 1) & 1]; - pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat); - if (fc->type == AVM_FRITZ_PCIV2) { - rmlMask = HDLC_STAT_RML_MASK_V2; - fs = HDLC_FIFO_SIZE_V2; - } else { - rmlMask = HDLC_STAT_RML_MASK_V1; - fs = HDLC_FIFO_SIZE_V1; - } - if (stat & HDLC_INT_RPR) { - if (stat & HDLC_STAT_RDO) { - pr_warn("%s: ch%d stat %x RDO\n", - fc->name, bch->nr, stat); - hdlc->ctrl.sr.xml = 0; - hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS; - write_ctrl(bch, 1); - hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS; - write_ctrl(bch, 1); - if (bch->rx_skb) - skb_trim(bch->rx_skb, 0); - } else { - len = (stat & rmlMask) >> 8; - if (!len) - len = fs; - hdlc_empty_fifo(bch, len); - if (!bch->rx_skb) - goto handle_tx; - if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { - recv_Bchannel(bch, 0, false); - } else if (stat & HDLC_STAT_RME) { - if ((stat & HDLC_STAT_CRCVFRRAB) == - HDLC_STAT_CRCVFR) { - recv_Bchannel(bch, 0, false); - } else { - pr_warn("%s: got invalid frame\n", - fc->name); - skb_trim(bch->rx_skb, 0); - } - } - } - } -handle_tx: - if (stat & HDLC_INT_XDU) { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame on HDLC - * in transparent mode we send the next data - */ - pr_warn("%s: ch%d stat %x XDU %s\n", fc->name, bch->nr, - stat, bch->tx_skb ? "tx_skb" : "no tx_skb"); - if (bch->tx_skb && bch->tx_skb->len) { - if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) - bch->tx_idx = 0; - } else if (test_bit(FLG_FILLEMPTY, &bch->Flags)) { - test_and_set_bit(FLG_TX_EMPTY, &bch->Flags); - } - hdlc->ctrl.sr.xml = 0; - hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS; - write_ctrl(bch, 1); - hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS; - HDLC_irq_xpr(bch); - return; - } else if (stat & HDLC_INT_XPR) - HDLC_irq_xpr(bch); -} - -static inline void -HDLC_irq_main(struct fritzcard *fc) -{ - u32 stat; - struct bchannel *bch; - - stat = read_status(fc, 1); - if (stat & HDLC_INT_MASK) { - bch = Sel_BCS(fc, 1); - if (bch) - HDLC_irq(bch, stat); - else - pr_debug("%s: spurious ch1 IRQ\n", fc->name); - } - stat = read_status(fc, 2); - if (stat & HDLC_INT_MASK) { - bch = Sel_BCS(fc, 2); - if (bch) - HDLC_irq(bch, stat); - else - pr_debug("%s: spurious ch2 IRQ\n", fc->name); - } -} - -static irqreturn_t -avm_fritz_interrupt(int intno, void *dev_id) -{ - struct fritzcard *fc = dev_id; - u8 val; - u8 sval; - - spin_lock(&fc->lock); - sval = inb(fc->addr + 2); - pr_debug("%s: irq stat0 %x\n", fc->name, sval); - if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) { - /* shared IRQ from other HW */ - spin_unlock(&fc->lock); - return IRQ_NONE; - } - fc->irqcnt++; - - if (!(sval & AVM_STATUS0_IRQ_ISAC)) { - val = ReadISAC_V1(fc, ISAC_ISTA); - mISDNisac_irq(&fc->isac, val); - } - if (!(sval & AVM_STATUS0_IRQ_HDLC)) - HDLC_irq_main(fc); - spin_unlock(&fc->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -avm_fritzv2_interrupt(int intno, void *dev_id) -{ - struct fritzcard *fc = dev_id; - u8 val; - u8 sval; - - spin_lock(&fc->lock); - sval = inb(fc->addr + 2); - pr_debug("%s: irq stat0 %x\n", fc->name, sval); - if (!(sval & AVM_STATUS0_IRQ_MASK)) { - /* shared IRQ from other HW */ - spin_unlock(&fc->lock); - return IRQ_NONE; - } - fc->irqcnt++; - - if (sval & AVM_STATUS0_IRQ_HDLC) - HDLC_irq_main(fc); - if (sval & AVM_STATUS0_IRQ_ISAC) { - val = ReadISAC_V2(fc, ISACX_ISTA); - mISDNisac_irq(&fc->isac, val); - } - if (sval & AVM_STATUS0_IRQ_TIMER) { - pr_debug("%s: timer irq\n", fc->name); - outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2); - udelay(1); - outb(fc->ctrlreg, fc->addr + 2); - } - spin_unlock(&fc->lock); - return IRQ_HANDLED; -} - -static int -avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct fritzcard *fc = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&fc->lock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - hdlc_fill_fifo(bch); - ret = 0; - } - spin_unlock_irqrestore(&fc->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(&fc->lock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = modehdlc(bch, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(&fc->lock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - spin_lock_irqsave(&fc->lock, flags); - mISDN_clear_bchannel(bch); - modehdlc(bch, ISDN_P_NONE); - spin_unlock_irqrestore(&fc->lock, flags); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static void -inithdlc(struct fritzcard *fc) -{ - modehdlc(&fc->bch[0], -1); - modehdlc(&fc->bch[1], -1); -} - -static void -clear_pending_hdlc_ints(struct fritzcard *fc) -{ - u32 val; - - val = read_status(fc, 1); - pr_debug("%s: HDLC 1 STA %x\n", fc->name, val); - val = read_status(fc, 2); - pr_debug("%s: HDLC 2 STA %x\n", fc->name, val); -} - -static void -reset_avm(struct fritzcard *fc) -{ - switch (fc->type) { - case AVM_FRITZ_PCI: - fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER; - break; - case AVM_FRITZ_PCIV2: - fc->ctrlreg = AVM_STATUS0_RESET; - break; - } - if (debug & DEBUG_HW) - pr_notice("%s: reset\n", fc->name); - disable_hwirq(fc); - mdelay(5); - switch (fc->type) { - case AVM_FRITZ_PCI: - fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER; - disable_hwirq(fc); - outb(AVM_STATUS1_ENA_IOM, fc->addr + 3); - break; - case AVM_FRITZ_PCIV2: - fc->ctrlreg = 0; - disable_hwirq(fc); - break; - } - mdelay(1); - if (debug & DEBUG_HW) - pr_notice("%s: S0/S1 %x/%x\n", fc->name, - inb(fc->addr + 2), inb(fc->addr + 3)); -} - -static int -init_card(struct fritzcard *fc) -{ - int ret, cnt = 3; - u_long flags; - - reset_avm(fc); /* disable IRQ */ - if (fc->type == AVM_FRITZ_PCIV2) - ret = request_irq(fc->irq, avm_fritzv2_interrupt, - IRQF_SHARED, fc->name, fc); - else - ret = request_irq(fc->irq, avm_fritz_interrupt, - IRQF_SHARED, fc->name, fc); - if (ret) { - pr_info("%s: couldn't get interrupt %d\n", - fc->name, fc->irq); - return ret; - } - while (cnt--) { - spin_lock_irqsave(&fc->lock, flags); - ret = fc->isac.init(&fc->isac); - if (ret) { - spin_unlock_irqrestore(&fc->lock, flags); - pr_info("%s: ISAC init failed with %d\n", - fc->name, ret); - break; - } - clear_pending_hdlc_ints(fc); - inithdlc(fc); - enable_hwirq(fc); - /* RESET Receiver and Transmitter */ - if (fc->type == AVM_FRITZ_PCIV2) { - WriteISAC_V2(fc, ISACX_MASK, 0); - WriteISAC_V2(fc, ISACX_CMDRD, 0x41); - } else { - WriteISAC_V1(fc, ISAC_MASK, 0); - WriteISAC_V1(fc, ISAC_CMDR, 0x41); - } - spin_unlock_irqrestore(&fc->lock, flags); - /* Timeout 10ms */ - msleep_interruptible(10); - if (debug & DEBUG_HW) - pr_notice("%s: IRQ %d count %d\n", fc->name, - fc->irq, fc->irqcnt); - if (!fc->irqcnt) { - pr_info("%s: IRQ(%d) getting no IRQs during init %d\n", - fc->name, fc->irq, 3 - cnt); - reset_avm(fc); - } else - return 0; - } - free_irq(fc->irq, fc); - return -EIO; -} - -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} - -static int -avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct fritzcard *fc = bch->hw; - int ret = -EINVAL; - u_long flags; - - pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - cancel_work_sync(&bch->workq); - spin_lock_irqsave(&fc->lock, flags); - mISDN_clear_bchannel(bch); - modehdlc(bch, ISDN_P_NONE); - spin_unlock_irqrestore(&fc->lock, flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd); - } - return ret; -} - -static int -channel_ctrl(struct fritzcard *fc, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_LOOP: - /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ - if (cq->channel < 0 || cq->channel > 3) { - ret = -EINVAL; - break; - } - ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel); - break; - case MISDN_CTRL_L1_TIMER3: - ret = fc->isac.ctrl(&fc->isac, HW_TIMER3_VALUE, cq->p1); - break; - default: - pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -open_bchannel(struct fritzcard *fc, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &fc->bch[rq->adr.channel - 1]; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - return 0; -} - -/* - * device control function - */ -static int -avm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct fritzcard *fc = dch->hw; - struct channel_req *rq; - int err = 0; - - pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if (rq->protocol == ISDN_P_TE_S0) - err = fc->isac.open(&fc->isac, rq); - else - err = open_bchannel(fc, rq); - if (err) - break; - if (!try_module_get(THIS_MODULE)) - pr_info("%s: cannot get module\n", fc->name); - break; - case CLOSE_CHANNEL: - pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id, - __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(fc, arg); - break; - default: - pr_debug("%s: %s unknown command %x\n", - fc->name, __func__, cmd); - return -EINVAL; - } - return err; -} - -static int -setup_fritz(struct fritzcard *fc) -{ - u32 val, ver; - - if (!request_region(fc->addr, 32, fc->name)) { - pr_info("%s: AVM config port %x-%x already in use\n", - fc->name, fc->addr, fc->addr + 31); - return -EIO; - } - switch (fc->type) { - case AVM_FRITZ_PCI: - val = inl(fc->addr); - outl(AVM_HDLC_1, fc->addr + CHIP_INDEX); - ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24; - if (debug & DEBUG_HW) { - pr_notice("%s: PCI stat %#x\n", fc->name, val); - pr_notice("%s: PCI Class %X Rev %d\n", fc->name, - val & 0xff, (val >> 8) & 0xff); - pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); - } - ASSIGN_FUNC(V1, ISAC, fc->isac); - fc->isac.type = IPAC_TYPE_ISAC; - break; - case AVM_FRITZ_PCIV2: - val = inl(fc->addr); - ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24; - if (debug & DEBUG_HW) { - pr_notice("%s: PCI V2 stat %#x\n", fc->name, val); - pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name, - val & 0xff, (val >> 8) & 0xff); - pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); - } - ASSIGN_FUNC(V2, ISAC, fc->isac); - fc->isac.type = IPAC_TYPE_ISACX; - break; - default: - release_region(fc->addr, 32); - pr_info("%s: AVM unknown type %d\n", fc->name, fc->type); - return -ENODEV; - } - pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name, - (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" : - "AVM Fritz!CARD PCIv2", fc->irq, fc->addr); - return 0; -} - -static void -release_card(struct fritzcard *card) -{ - u_long flags; - - disable_hwirq(card); - spin_lock_irqsave(&card->lock, flags); - modehdlc(&card->bch[0], ISDN_P_NONE); - modehdlc(&card->bch[1], ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - card->isac.release(&card->isac); - free_irq(card->irq, card); - mISDN_freebchannel(&card->bch[1]); - mISDN_freebchannel(&card->bch[0]); - mISDN_unregister_device(&card->isac.dch.dev); - release_region(card->addr, 32); - pci_disable_device(card->pdev); - pci_set_drvdata(card->pdev, NULL); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - kfree(card); - AVM_cnt--; -} - -static int -setup_instance(struct fritzcard *card) -{ - int i, err; - unsigned short minsize; - u_long flags; - - snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1); - write_lock_irqsave(&card_lock, flags); - list_add_tail(&card->list, &Cards); - write_unlock_irqrestore(&card_lock, flags); - - _set_debug(card); - card->isac.name = card->name; - spin_lock_init(&card->lock); - card->isac.hwlock = &card->lock; - mISDNisac_init(&card->isac, card); - - card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - card->isac.dch.dev.D.ctrl = avm_dctrl; - for (i = 0; i < 2; i++) { - card->bch[i].nr = i + 1; - set_channelmap(i + 1, card->isac.dch.dev.channelmap); - if (AVM_FRITZ_PCIV2 == card->type) - minsize = HDLC_FIFO_SIZE_V2; - else - minsize = HDLC_FIFO_SIZE_V1; - mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, minsize); - card->bch[i].hw = card; - card->bch[i].ch.send = avm_l2l1B; - card->bch[i].ch.ctrl = avm_bctrl; - card->bch[i].ch.nr = i + 1; - list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels); - } - err = setup_fritz(card); - if (err) - goto error; - err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev, - card->name); - if (err) - goto error_reg; - err = init_card(card); - if (!err) { - AVM_cnt++; - pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt); - return 0; - } - mISDN_unregister_device(&card->isac.dch.dev); -error_reg: - release_region(card->addr, 32); -error: - card->isac.release(&card->isac); - mISDN_freebchannel(&card->bch[1]); - mISDN_freebchannel(&card->bch[0]); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - kfree(card); - return err; -} - -static int -fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - struct fritzcard *card; - - card = kzalloc_obj(struct fritzcard); - if (!card) { - pr_info("No kmem for fritzcard\n"); - return err; - } - if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) - card->type = AVM_FRITZ_PCIV2; - else - card->type = AVM_FRITZ_PCI; - card->pdev = pdev; - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - - pr_notice("mISDN: found adapter %s at %s\n", - (char *) ent->driver_data, pci_name(pdev)); - - card->addr = pci_resource_start(pdev, 1); - card->irq = pdev->irq; - pci_set_drvdata(pdev, card); - err = setup_instance(card); - if (err) - pci_set_drvdata(pdev, NULL); - return err; -} - -static void -fritz_remove_pci(struct pci_dev *pdev) -{ - struct fritzcard *card = pci_get_drvdata(pdev); - - if (card) - release_card(card); - else - if (debug) - pr_info("%s: drvdata already removed\n", __func__); -} - -static const struct pci_device_id fcpci_ids[] = { - { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID, - 0, 0, (unsigned long) "Fritz!Card PCI"}, - { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID, - 0, 0, (unsigned long) "Fritz!Card PCI v2" }, - { } -}; -MODULE_DEVICE_TABLE(pci, fcpci_ids); - -static struct pci_driver fcpci_driver = { - .name = "fcpci", - .probe = fritzpci_probe, - .remove = fritz_remove_pci, - .id_table = fcpci_ids, -}; - -static int __init AVM_init(void) -{ - int err; - - pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV); - err = pci_register_driver(&fcpci_driver); - return err; -} - -static void __exit AVM_cleanup(void) -{ - pci_unregister_driver(&fcpci_driver); -} - -module_init(AVM_init); -module_exit(AVM_cleanup); diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h deleted file mode 100644 index 5acf826d913c..000000000000 --- a/drivers/isdn/hardware/mISDN/hfc_multi.h +++ /dev/null @@ -1,1236 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * see notice in hfc_multi.c - */ - -#define DEBUG_HFCMULTI_FIFO 0x00010000 -#define DEBUG_HFCMULTI_CRC 0x00020000 -#define DEBUG_HFCMULTI_INIT 0x00040000 -#define DEBUG_HFCMULTI_PLXSD 0x00080000 -#define DEBUG_HFCMULTI_MODE 0x00100000 -#define DEBUG_HFCMULTI_MSG 0x00200000 -#define DEBUG_HFCMULTI_STATE 0x00400000 -#define DEBUG_HFCMULTI_FILL 0x00800000 -#define DEBUG_HFCMULTI_SYNC 0x01000000 -#define DEBUG_HFCMULTI_DTMF 0x02000000 -#define DEBUG_HFCMULTI_LOCK 0x80000000 - -#define PCI_ENA_REGIO 0x01 -#define PCI_ENA_MEMIO 0x02 - -#define XHFC_IRQ 4 /* SIU_IRQ2 */ -#define XHFC_MEMBASE 0xFE000000 -#define XHFC_MEMSIZE 0x00001000 -#define XHFC_OFFSET 0x00001000 -#define PA_XHFC_A0 0x0020 /* PA10 */ -#define PB_XHFC_IRQ1 0x00000100 /* PB23 */ -#define PB_XHFC_IRQ2 0x00000200 /* PB22 */ -#define PB_XHFC_IRQ3 0x00000400 /* PB21 */ -#define PB_XHFC_IRQ4 0x00000800 /* PB20 */ - -/* - * NOTE: some registers are assigned multiple times due to different modes - * also registers are assigned differen for HFC-4s/8s and HFC-E1 - */ - -/* - #define MAX_FRAME_SIZE 2048 -*/ - -struct hfc_chan { - struct dchannel *dch; /* link if channel is a D-channel */ - struct bchannel *bch; /* link if channel is a B-channel */ - int port; /* the interface port this */ - /* channel is associated with */ - int nt_timer; /* -1 if off, 0 if elapsed, >0 if running */ - int los, ais, slip_tx, slip_rx, rdi; /* current alarms */ - int jitter; - u_long cfg; /* port configuration */ - int sync; /* sync state (used by E1) */ - u_int protocol; /* current protocol */ - int slot_tx; /* current pcm slot */ - int bank_tx; /* current pcm bank */ - int slot_rx; - int bank_rx; - int conf; /* conference setting of TX slot */ - int txpending; /* if there is currently data in */ - /* the FIFO 0=no, 1=yes, 2=splloop */ - int Zfill; /* rx-fifo level on last hfcmulti_tx */ - int rx_off; /* set to turn fifo receive off */ - int coeff_count; /* curren coeff block */ - s32 *coeff; /* memory pointer to 8 coeff blocks */ -}; - - -struct hfcm_hw { - u_char r_ctrl; - u_char r_irq_ctrl; - u_char r_cirm; - u_char r_ram_sz; - u_char r_pcm_md0; - u_char r_irqmsk_misc; - u_char r_dtmf; - u_char r_st_sync; - u_char r_sci_msk; - u_char r_tx0, r_tx1; - u_char a_st_ctrl0[8]; - u_char r_bert_wd_md; - timer_t timer; -}; - - -/* for each stack these flags are used (cfg) */ -#define HFC_CFG_NONCAP_TX 1 /* S/T TX interface has less capacity */ -#define HFC_CFG_DIS_ECHANNEL 2 /* disable E-channel processing */ -#define HFC_CFG_REG_ECHANNEL 3 /* register E-channel */ -#define HFC_CFG_OPTICAL 4 /* the E1 interface is optical */ -#define HFC_CFG_REPORT_LOS 5 /* the card should report loss of signal */ -#define HFC_CFG_REPORT_AIS 6 /* the card should report alarm ind. sign. */ -#define HFC_CFG_REPORT_SLIP 7 /* the card should report bit slips */ -#define HFC_CFG_REPORT_RDI 8 /* the card should report remote alarm */ -#define HFC_CFG_DTMF 9 /* enable DTMF-detection */ -#define HFC_CFG_CRC4 10 /* disable CRC-4 Multiframe mode, */ -/* use double frame instead. */ - -#define HFC_TYPE_E1 1 /* controller is HFC-E1 */ -#define HFC_TYPE_4S 4 /* controller is HFC-4S */ -#define HFC_TYPE_8S 8 /* controller is HFC-8S */ -#define HFC_TYPE_XHFC 5 /* controller is XHFC */ - -#define HFC_CHIP_EXRAM_128 0 /* external ram 128k */ -#define HFC_CHIP_EXRAM_512 1 /* external ram 256k */ -#define HFC_CHIP_REVISION0 2 /* old fifo handling */ -#define HFC_CHIP_PCM_SLAVE 3 /* PCM is slave */ -#define HFC_CHIP_PCM_MASTER 4 /* PCM is master */ -#define HFC_CHIP_RX_SYNC 5 /* disable pll sync for pcm */ -#define HFC_CHIP_DTMF 6 /* DTMF decoding is enabled */ -#define HFC_CHIP_CONF 7 /* conference handling is enabled */ -#define HFC_CHIP_ULAW 8 /* ULAW mode */ -#define HFC_CHIP_CLOCK2 9 /* double clock mode */ -#define HFC_CHIP_E1CLOCK_GET 10 /* always get clock from E1 interface */ -#define HFC_CHIP_E1CLOCK_PUT 11 /* always put clock from E1 interface */ -#define HFC_CHIP_WATCHDOG 12 /* whether we should send signals */ -/* to the watchdog */ -#define HFC_CHIP_B410P 13 /* whether we have a b410p with echocan in */ -/* hw */ -#define HFC_CHIP_PLXSD 14 /* whether we have a Speech-Design PLX */ -#define HFC_CHIP_EMBSD 15 /* whether we have a SD Embedded board */ - -#define HFC_IO_MODE_PCIMEM 0x00 /* normal memory mapped IO */ -#define HFC_IO_MODE_REGIO 0x01 /* PCI io access */ -#define HFC_IO_MODE_PLXSD 0x02 /* access HFC via PLX9030 */ -#define HFC_IO_MODE_EMBSD 0x03 /* direct access */ - -/* table entry in the PCI devices list */ -struct hm_map { - char *vendor_name; - char *card_name; - int type; - int ports; - int clock2; - int leds; - int opticalsupport; - int dip_type; - int io_mode; - int irq; -}; - -struct hfc_multi { - struct list_head list; - struct hm_map *mtyp; - int id; - int pcm; /* id of pcm bus */ - int ctype; /* controller type */ - int ports; - - u_int irq; /* irq used by card */ - u_int irqcnt; - struct pci_dev *pci_dev; - int io_mode; /* selects mode */ -#ifdef HFC_REGISTER_DEBUG - void (*HFC_outb)(struct hfc_multi *hc, u_char reg, - u_char val, const char *function, int line); - void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, - u_char val, const char *function, int line); - u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg, - const char *function, int line); - u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg, - const char *function, int line); - u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg, - const char *function, int line); - u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg, - const char *function, int line); - void (*HFC_wait)(struct hfc_multi *hc, - const char *function, int line); - void (*HFC_wait_nodebug)(struct hfc_multi *hc, - const char *function, int line); -#else - void (*HFC_outb)(struct hfc_multi *hc, u_char reg, - u_char val); - void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, - u_char val); - u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg); - u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg); - u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg); - u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg); - void (*HFC_wait)(struct hfc_multi *hc); - void (*HFC_wait_nodebug)(struct hfc_multi *hc); -#endif - void (*read_fifo)(struct hfc_multi *hc, u_char *data, - int len); - void (*write_fifo)(struct hfc_multi *hc, u_char *data, - int len); - u_long pci_origmembase, plx_origmembase; - void __iomem *pci_membase; /* PCI memory */ - void __iomem *plx_membase; /* PLX memory */ - u_long xhfc_origmembase; - u_char *xhfc_membase; - u_long *xhfc_memaddr, *xhfc_memdata; -#ifdef CONFIG_MISDN_HFCMULTI_8xx - struct immap *immap; -#endif - u_long pb_irqmsk; /* Portbit mask to check the IRQ line */ - u_long pci_iobase; /* PCI IO */ - struct hfcm_hw hw; /* remember data of write-only-registers */ - - u_long chip; /* chip configuration */ - int masterclk; /* port that provides master clock -1=off */ - unsigned char silence;/* silence byte */ - unsigned char silence_data[128];/* silence block */ - int dtmf; /* flag that dtmf is currently in process */ - int Flen; /* F-buffer size */ - int Zlen; /* Z-buffer size (must be int for calculation)*/ - int max_trans; /* maximum transparent fifo fill */ - int Zmin; /* Z-buffer offset */ - int DTMFbase; /* base address of DTMF coefficients */ - - u_int slots; /* number of PCM slots */ - u_int leds; /* type of leds */ - u_long ledstate; /* save last state of leds */ - int opticalsupport; /* has the e1 board */ - /* an optical Interface */ - - u_int bmask[32]; /* bitmask of bchannels for port */ - u_char dnum[32]; /* array of used dchannel numbers for port */ - u_char created[32]; /* what port is created */ - u_int activity_tx; /* if there is data TX / RX */ - u_int activity_rx; /* bitmask according to port number */ - /* (will be cleared after */ - /* showing led-states) */ - u_int flash[8]; /* counter for flashing 8 leds on activity */ - - u_long wdcount; /* every 500 ms we need to */ - /* send the watchdog a signal */ - u_char wdbyte; /* watchdog toggle byte */ - int e1_state; /* keep track of last state */ - int e1_getclock; /* if sync is retrieved from interface */ - int syncronized; /* keep track of existing sync interface */ - int e1_resync; /* resync jobs */ - - spinlock_t lock; /* the lock */ - - struct mISDNclock *iclock; /* isdn clock support */ - int iclock_on; - - /* - * the channel index is counted from 0, regardless where the channel - * is located on the hfc-channel. - * the bch->channel is equvalent to the hfc-channel - */ - struct hfc_chan chan[32]; - signed char slot_owner[256]; /* owner channel of slot */ -}; - -/* PLX GPIOs */ -#define PLX_GPIO4_DIR_BIT 13 -#define PLX_GPIO4_BIT 14 -#define PLX_GPIO5_DIR_BIT 16 -#define PLX_GPIO5_BIT 17 -#define PLX_GPIO6_DIR_BIT 19 -#define PLX_GPIO6_BIT 20 -#define PLX_GPIO7_DIR_BIT 22 -#define PLX_GPIO7_BIT 23 -#define PLX_GPIO8_DIR_BIT 25 -#define PLX_GPIO8_BIT 26 - -#define PLX_GPIO4 (1 << PLX_GPIO4_BIT) -#define PLX_GPIO5 (1 << PLX_GPIO5_BIT) -#define PLX_GPIO6 (1 << PLX_GPIO6_BIT) -#define PLX_GPIO7 (1 << PLX_GPIO7_BIT) -#define PLX_GPIO8 (1 << PLX_GPIO8_BIT) - -#define PLX_GPIO4_DIR (1 << PLX_GPIO4_DIR_BIT) -#define PLX_GPIO5_DIR (1 << PLX_GPIO5_DIR_BIT) -#define PLX_GPIO6_DIR (1 << PLX_GPIO6_DIR_BIT) -#define PLX_GPIO7_DIR (1 << PLX_GPIO7_DIR_BIT) -#define PLX_GPIO8_DIR (1 << PLX_GPIO8_DIR_BIT) - -#define PLX_TERM_ON PLX_GPIO7 -#define PLX_SLAVE_EN_N PLX_GPIO5 -#define PLX_MASTER_EN PLX_GPIO6 -#define PLX_SYNC_O_EN PLX_GPIO4 -#define PLX_DSP_RES_N PLX_GPIO8 -/* GPIO4..8 Enable & Set to OUT, SLAVE_EN_N = 1 */ -#define PLX_GPIOC_INIT (PLX_GPIO4_DIR | PLX_GPIO5_DIR | PLX_GPIO6_DIR \ - | PLX_GPIO7_DIR | PLX_GPIO8_DIR | PLX_SLAVE_EN_N) - -/* PLX Interrupt Control/STATUS */ -#define PLX_INTCSR_LINTI1_ENABLE 0x01 -#define PLX_INTCSR_LINTI1_STATUS 0x04 -#define PLX_INTCSR_LINTI2_ENABLE 0x08 -#define PLX_INTCSR_LINTI2_STATUS 0x20 -#define PLX_INTCSR_PCIINT_ENABLE 0x40 - -/* PLX Registers */ -#define PLX_INTCSR 0x4c -#define PLX_CNTRL 0x50 -#define PLX_GPIOC 0x54 - - -/* - * REGISTER SETTING FOR HFC-4S/8S AND HFC-E1 - */ - -/* write only registers */ -#define R_CIRM 0x00 -#define R_CTRL 0x01 -#define R_BRG_PCM_CFG 0x02 -#define R_RAM_ADDR0 0x08 -#define R_RAM_ADDR1 0x09 -#define R_RAM_ADDR2 0x0A -#define R_FIRST_FIFO 0x0B -#define R_RAM_SZ 0x0C -#define R_FIFO_MD 0x0D -#define R_INC_RES_FIFO 0x0E -#define R_FSM_IDX 0x0F -#define R_FIFO 0x0F -#define R_SLOT 0x10 -#define R_IRQMSK_MISC 0x11 -#define R_SCI_MSK 0x12 -#define R_IRQ_CTRL 0x13 -#define R_PCM_MD0 0x14 -#define R_PCM_MD1 0x15 -#define R_PCM_MD2 0x15 -#define R_SH0H 0x15 -#define R_SH1H 0x15 -#define R_SH0L 0x15 -#define R_SH1L 0x15 -#define R_SL_SEL0 0x15 -#define R_SL_SEL1 0x15 -#define R_SL_SEL2 0x15 -#define R_SL_SEL3 0x15 -#define R_SL_SEL4 0x15 -#define R_SL_SEL5 0x15 -#define R_SL_SEL6 0x15 -#define R_SL_SEL7 0x15 -#define R_ST_SEL 0x16 -#define R_ST_SYNC 0x17 -#define R_CONF_EN 0x18 -#define R_TI_WD 0x1A -#define R_BERT_WD_MD 0x1B -#define R_DTMF 0x1C -#define R_DTMF_N 0x1D -#define R_E1_WR_STA 0x20 -#define R_E1_RD_STA 0x20 -#define R_LOS0 0x22 -#define R_LOS1 0x23 -#define R_RX0 0x24 -#define R_RX_FR0 0x25 -#define R_RX_FR1 0x26 -#define R_TX0 0x28 -#define R_TX1 0x29 -#define R_TX_FR0 0x2C - -#define R_TX_FR1 0x2D -#define R_TX_FR2 0x2E -#define R_JATT_ATT 0x2F /* undocumented */ -#define A_ST_RD_STATE 0x30 -#define A_ST_WR_STATE 0x30 -#define R_RX_OFF 0x30 -#define A_ST_CTRL0 0x31 -#define R_SYNC_OUT 0x31 -#define A_ST_CTRL1 0x32 -#define A_ST_CTRL2 0x33 -#define A_ST_SQ_WR 0x34 -#define R_TX_OFF 0x34 -#define R_SYNC_CTRL 0x35 -#define A_ST_CLK_DLY 0x37 -#define R_PWM0 0x38 -#define R_PWM1 0x39 -#define A_ST_B1_TX 0x3C -#define A_ST_B2_TX 0x3D -#define A_ST_D_TX 0x3E -#define R_GPIO_OUT0 0x40 -#define R_GPIO_OUT1 0x41 -#define R_GPIO_EN0 0x42 -#define R_GPIO_EN1 0x43 -#define R_GPIO_SEL 0x44 -#define R_BRG_CTRL 0x45 -#define R_PWM_MD 0x46 -#define R_BRG_MD 0x47 -#define R_BRG_TIM0 0x48 -#define R_BRG_TIM1 0x49 -#define R_BRG_TIM2 0x4A -#define R_BRG_TIM3 0x4B -#define R_BRG_TIM_SEL01 0x4C -#define R_BRG_TIM_SEL23 0x4D -#define R_BRG_TIM_SEL45 0x4E -#define R_BRG_TIM_SEL67 0x4F -#define A_SL_CFG 0xD0 -#define A_CONF 0xD1 -#define A_CH_MSK 0xF4 -#define A_CON_HDLC 0xFA -#define A_SUBCH_CFG 0xFB -#define A_CHANNEL 0xFC -#define A_FIFO_SEQ 0xFD -#define A_IRQ_MSK 0xFF - -/* read only registers */ -#define A_Z12 0x04 -#define A_Z1L 0x04 -#define A_Z1 0x04 -#define A_Z1H 0x05 -#define A_Z2L 0x06 -#define A_Z2 0x06 -#define A_Z2H 0x07 -#define A_F1 0x0C -#define A_F12 0x0C -#define A_F2 0x0D -#define R_IRQ_OVIEW 0x10 -#define R_IRQ_MISC 0x11 -#define R_IRQ_STATECH 0x12 -#define R_CONF_OFLOW 0x14 -#define R_RAM_USE 0x15 -#define R_CHIP_ID 0x16 -#define R_BERT_STA 0x17 -#define R_F0_CNTL 0x18 -#define R_F0_CNTH 0x19 -#define R_BERT_EC 0x1A -#define R_BERT_ECL 0x1A -#define R_BERT_ECH 0x1B -#define R_STATUS 0x1C -#define R_CHIP_RV 0x1F -#define R_STATE 0x20 -#define R_SYNC_STA 0x24 -#define R_RX_SL0_0 0x25 -#define R_RX_SL0_1 0x26 -#define R_RX_SL0_2 0x27 -#define R_JATT_DIR 0x2b /* undocumented */ -#define R_SLIP 0x2c -#define A_ST_RD_STA 0x30 -#define R_FAS_EC 0x30 -#define R_FAS_ECL 0x30 -#define R_FAS_ECH 0x31 -#define R_VIO_EC 0x32 -#define R_VIO_ECL 0x32 -#define R_VIO_ECH 0x33 -#define A_ST_SQ_RD 0x34 -#define R_CRC_EC 0x34 -#define R_CRC_ECL 0x34 -#define R_CRC_ECH 0x35 -#define R_E_EC 0x36 -#define R_E_ECL 0x36 -#define R_E_ECH 0x37 -#define R_SA6_SA13_EC 0x38 -#define R_SA6_SA13_ECL 0x38 -#define R_SA6_SA13_ECH 0x39 -#define R_SA6_SA23_EC 0x3A -#define R_SA6_SA23_ECL 0x3A -#define R_SA6_SA23_ECH 0x3B -#define A_ST_B1_RX 0x3C -#define A_ST_B2_RX 0x3D -#define A_ST_D_RX 0x3E -#define A_ST_E_RX 0x3F -#define R_GPIO_IN0 0x40 -#define R_GPIO_IN1 0x41 -#define R_GPI_IN0 0x44 -#define R_GPI_IN1 0x45 -#define R_GPI_IN2 0x46 -#define R_GPI_IN3 0x47 -#define R_INT_DATA 0x88 -#define R_IRQ_FIFO_BL0 0xC8 -#define R_IRQ_FIFO_BL1 0xC9 -#define R_IRQ_FIFO_BL2 0xCA -#define R_IRQ_FIFO_BL3 0xCB -#define R_IRQ_FIFO_BL4 0xCC -#define R_IRQ_FIFO_BL5 0xCD -#define R_IRQ_FIFO_BL6 0xCE -#define R_IRQ_FIFO_BL7 0xCF - -/* read and write registers */ -#define A_FIFO_DATA0 0x80 -#define A_FIFO_DATA1 0x80 -#define A_FIFO_DATA2 0x80 -#define A_FIFO_DATA0_NOINC 0x84 -#define A_FIFO_DATA1_NOINC 0x84 -#define A_FIFO_DATA2_NOINC 0x84 -#define R_RAM_DATA 0xC0 - - -/* - * BIT SETTING FOR HFC-4S/8S AND HFC-E1 - */ - -/* chapter 2: universal bus interface */ -/* R_CIRM */ -#define V_IRQ_SEL 0x01 -#define V_SRES 0x08 -#define V_HFCRES 0x10 -#define V_PCMRES 0x20 -#define V_STRES 0x40 -#define V_ETRES 0x40 -#define V_RLD_EPR 0x80 -/* R_CTRL */ -#define V_FIFO_LPRIO 0x02 -#define V_SLOW_RD 0x04 -#define V_EXT_RAM 0x08 -#define V_CLK_OFF 0x20 -#define V_ST_CLK 0x40 -/* R_RAM_ADDR0 */ -#define V_RAM_ADDR2 0x01 -#define V_ADDR_RES 0x40 -#define V_ADDR_INC 0x80 -/* R_RAM_SZ */ -#define V_RAM_SZ 0x01 -#define V_PWM0_16KHZ 0x10 -#define V_PWM1_16KHZ 0x20 -#define V_FZ_MD 0x80 -/* R_CHIP_ID */ -#define V_PNP_IRQ 0x01 -#define V_CHIP_ID 0x10 - -/* chapter 3: data flow */ -/* R_FIRST_FIFO */ -#define V_FIRST_FIRO_DIR 0x01 -#define V_FIRST_FIFO_NUM 0x02 -/* R_FIFO_MD */ -#define V_FIFO_MD 0x01 -#define V_CSM_MD 0x04 -#define V_FSM_MD 0x08 -#define V_FIFO_SZ 0x10 -/* R_FIFO */ -#define V_FIFO_DIR 0x01 -#define V_FIFO_NUM 0x02 -#define V_REV 0x80 -/* R_SLOT */ -#define V_SL_DIR 0x01 -#define V_SL_NUM 0x02 -/* A_SL_CFG */ -#define V_CH_DIR 0x01 -#define V_CH_SEL 0x02 -#define V_ROUTING 0x40 -/* A_CON_HDLC */ -#define V_IFF 0x01 -#define V_HDLC_TRP 0x02 -#define V_TRP_IRQ 0x04 -#define V_DATA_FLOW 0x20 -/* A_SUBCH_CFG */ -#define V_BIT_CNT 0x01 -#define V_START_BIT 0x08 -#define V_LOOP_FIFO 0x40 -#define V_INV_DATA 0x80 -/* A_CHANNEL */ -#define V_CH_DIR0 0x01 -#define V_CH_NUM0 0x02 -/* A_FIFO_SEQ */ -#define V_NEXT_FIFO_DIR 0x01 -#define V_NEXT_FIFO_NUM 0x02 -#define V_SEQ_END 0x40 - -/* chapter 4: FIFO handling and HDLC controller */ -/* R_INC_RES_FIFO */ -#define V_INC_F 0x01 -#define V_RES_F 0x02 -#define V_RES_LOST 0x04 - -/* chapter 5: S/T interface */ -/* R_SCI_MSK */ -#define V_SCI_MSK_ST0 0x01 -#define V_SCI_MSK_ST1 0x02 -#define V_SCI_MSK_ST2 0x04 -#define V_SCI_MSK_ST3 0x08 -#define V_SCI_MSK_ST4 0x10 -#define V_SCI_MSK_ST5 0x20 -#define V_SCI_MSK_ST6 0x40 -#define V_SCI_MSK_ST7 0x80 -/* R_ST_SEL */ -#define V_ST_SEL 0x01 -#define V_MULT_ST 0x08 -/* R_ST_SYNC */ -#define V_SYNC_SEL 0x01 -#define V_AUTO_SYNC 0x08 -/* A_ST_WR_STA */ -#define V_ST_SET_STA 0x01 -#define V_ST_LD_STA 0x10 -#define V_ST_ACT 0x20 -#define V_SET_G2_G3 0x80 -/* A_ST_CTRL0 */ -#define V_B1_EN 0x01 -#define V_B2_EN 0x02 -#define V_ST_MD 0x04 -#define V_D_PRIO 0x08 -#define V_SQ_EN 0x10 -#define V_96KHZ 0x20 -#define V_TX_LI 0x40 -#define V_ST_STOP 0x80 -/* A_ST_CTRL1 */ -#define V_G2_G3_EN 0x01 -#define V_D_HI 0x04 -#define V_E_IGNO 0x08 -#define V_E_LO 0x10 -#define V_B12_SWAP 0x80 -/* A_ST_CTRL2 */ -#define V_B1_RX_EN 0x01 -#define V_B2_RX_EN 0x02 -#define V_ST_TRIS 0x40 -/* A_ST_CLK_DLY */ -#define V_ST_CK_DLY 0x01 -#define V_ST_SMPL 0x10 -/* A_ST_D_TX */ -#define V_ST_D_TX 0x40 -/* R_IRQ_STATECH */ -#define V_SCI_ST0 0x01 -#define V_SCI_ST1 0x02 -#define V_SCI_ST2 0x04 -#define V_SCI_ST3 0x08 -#define V_SCI_ST4 0x10 -#define V_SCI_ST5 0x20 -#define V_SCI_ST6 0x40 -#define V_SCI_ST7 0x80 -/* A_ST_RD_STA */ -#define V_ST_STA 0x01 -#define V_FR_SYNC_ST 0x10 -#define V_TI2_EXP 0x20 -#define V_INFO0 0x40 -#define V_G2_G3 0x80 -/* A_ST_SQ_RD */ -#define V_ST_SQ 0x01 -#define V_MF_RX_RDY 0x10 -#define V_MF_TX_RDY 0x80 -/* A_ST_D_RX */ -#define V_ST_D_RX 0x40 -/* A_ST_E_RX */ -#define V_ST_E_RX 0x40 - -/* chapter 5: E1 interface */ -/* R_E1_WR_STA */ -/* R_E1_RD_STA */ -#define V_E1_SET_STA 0x01 -#define V_E1_LD_STA 0x10 -/* R_RX0 */ -#define V_RX_CODE 0x01 -#define V_RX_FBAUD 0x04 -#define V_RX_CMI 0x08 -#define V_RX_INV_CMI 0x10 -#define V_RX_INV_CLK 0x20 -#define V_RX_INV_DATA 0x40 -#define V_AIS_ITU 0x80 -/* R_RX_FR0 */ -#define V_NO_INSYNC 0x01 -#define V_AUTO_RESYNC 0x02 -#define V_AUTO_RECO 0x04 -#define V_SWORD_COND 0x08 -#define V_SYNC_LOSS 0x10 -#define V_XCRC_SYNC 0x20 -#define V_MF_RESYNC 0x40 -#define V_RESYNC 0x80 -/* R_RX_FR1 */ -#define V_RX_MF 0x01 -#define V_RX_MF_SYNC 0x02 -#define V_RX_SL0_RAM 0x04 -#define V_ERR_SIM 0x20 -#define V_RES_NMF 0x40 -/* R_TX0 */ -#define V_TX_CODE 0x01 -#define V_TX_FBAUD 0x04 -#define V_TX_CMI_CODE 0x08 -#define V_TX_INV_CMI_CODE 0x10 -#define V_TX_INV_CLK 0x20 -#define V_TX_INV_DATA 0x40 -#define V_OUT_EN 0x80 -/* R_TX1 */ -#define V_INV_CLK 0x01 -#define V_EXCHG_DATA_LI 0x02 -#define V_AIS_OUT 0x04 -#define V_ATX 0x20 -#define V_NTRI 0x40 -#define V_AUTO_ERR_RES 0x80 -/* R_TX_FR0 */ -#define V_TRP_FAS 0x01 -#define V_TRP_NFAS 0x02 -#define V_TRP_RAL 0x04 -#define V_TRP_SA 0x08 -/* R_TX_FR1 */ -#define V_TX_FAS 0x01 -#define V_TX_NFAS 0x02 -#define V_TX_RAL 0x04 -#define V_TX_SA 0x08 -/* R_TX_FR2 */ -#define V_TX_MF 0x01 -#define V_TRP_SL0 0x02 -#define V_TX_SL0_RAM 0x04 -#define V_TX_E 0x10 -#define V_NEG_E 0x20 -#define V_XS12_ON 0x40 -#define V_XS15_ON 0x80 -/* R_RX_OFF */ -#define V_RX_SZ 0x01 -#define V_RX_INIT 0x04 -/* R_SYNC_OUT */ -#define V_SYNC_E1_RX 0x01 -#define V_IPATS0 0x20 -#define V_IPATS1 0x40 -#define V_IPATS2 0x80 -/* R_TX_OFF */ -#define V_TX_SZ 0x01 -#define V_TX_INIT 0x04 -/* R_SYNC_CTRL */ -#define V_EXT_CLK_SYNC 0x01 -#define V_SYNC_OFFS 0x02 -#define V_PCM_SYNC 0x04 -#define V_NEG_CLK 0x08 -#define V_HCLK 0x10 -/* - #define V_JATT_AUTO_DEL 0x20 - #define V_JATT_AUTO 0x40 -*/ -#define V_JATT_OFF 0x80 -/* R_STATE */ -#define V_E1_STA 0x01 -#define V_ALT_FR_RX 0x40 -#define V_ALT_FR_TX 0x80 -/* R_SYNC_STA */ -#define V_RX_STA 0x01 -#define V_FR_SYNC_E1 0x04 -#define V_SIG_LOS 0x08 -#define V_MFA_STA 0x10 -#define V_AIS 0x40 -#define V_NO_MF_SYNC 0x80 -/* R_RX_SL0_0 */ -#define V_SI_FAS 0x01 -#define V_SI_NFAS 0x02 -#define V_A 0x04 -#define V_CRC_OK 0x08 -#define V_TX_E1 0x10 -#define V_TX_E2 0x20 -#define V_RX_E1 0x40 -#define V_RX_E2 0x80 -/* R_SLIP */ -#define V_SLIP_RX 0x01 -#define V_FOSLIP_RX 0x08 -#define V_SLIP_TX 0x10 -#define V_FOSLIP_TX 0x80 - -/* chapter 6: PCM interface */ -/* R_PCM_MD0 */ -#define V_PCM_MD 0x01 -#define V_C4_POL 0x02 -#define V_F0_NEG 0x04 -#define V_F0_LEN 0x08 -#define V_PCM_ADDR 0x10 -/* R_SL_SEL0 */ -#define V_SL_SEL0 0x01 -#define V_SH_SEL0 0x80 -/* R_SL_SEL1 */ -#define V_SL_SEL1 0x01 -#define V_SH_SEL1 0x80 -/* R_SL_SEL2 */ -#define V_SL_SEL2 0x01 -#define V_SH_SEL2 0x80 -/* R_SL_SEL3 */ -#define V_SL_SEL3 0x01 -#define V_SH_SEL3 0x80 -/* R_SL_SEL4 */ -#define V_SL_SEL4 0x01 -#define V_SH_SEL4 0x80 -/* R_SL_SEL5 */ -#define V_SL_SEL5 0x01 -#define V_SH_SEL5 0x80 -/* R_SL_SEL6 */ -#define V_SL_SEL6 0x01 -#define V_SH_SEL6 0x80 -/* R_SL_SEL7 */ -#define V_SL_SEL7 0x01 -#define V_SH_SEL7 0x80 -/* R_PCM_MD1 */ -#define V_ODEC_CON 0x01 -#define V_PLL_ADJ 0x04 -#define V_PCM_DR 0x10 -#define V_PCM_LOOP 0x40 -/* R_PCM_MD2 */ -#define V_SYNC_PLL 0x02 -#define V_SYNC_SRC 0x04 -#define V_SYNC_OUT 0x08 -#define V_ICR_FR_TIME 0x40 -#define V_EN_PLL 0x80 - -/* chapter 7: pulse width modulation */ -/* R_PWM_MD */ -#define V_EXT_IRQ_EN 0x08 -#define V_PWM0_MD 0x10 -#define V_PWM1_MD 0x40 - -/* chapter 8: multiparty audio conferences */ -/* R_CONF_EN */ -#define V_CONF_EN 0x01 -#define V_ULAW 0x80 -/* A_CONF */ -#define V_CONF_NUM 0x01 -#define V_NOISE_SUPPR 0x08 -#define V_ATT_LEV 0x20 -#define V_CONF_SL 0x80 -/* R_CONF_OFLOW */ -#define V_CONF_OFLOW0 0x01 -#define V_CONF_OFLOW1 0x02 -#define V_CONF_OFLOW2 0x04 -#define V_CONF_OFLOW3 0x08 -#define V_CONF_OFLOW4 0x10 -#define V_CONF_OFLOW5 0x20 -#define V_CONF_OFLOW6 0x40 -#define V_CONF_OFLOW7 0x80 - -/* chapter 9: DTMF contoller */ -/* R_DTMF0 */ -#define V_DTMF_EN 0x01 -#define V_HARM_SEL 0x02 -#define V_DTMF_RX_CH 0x04 -#define V_DTMF_STOP 0x08 -#define V_CHBL_SEL 0x10 -#define V_RST_DTMF 0x40 -#define V_ULAW_SEL 0x80 - -/* chapter 10: BERT */ -/* R_BERT_WD_MD */ -#define V_PAT_SEQ 0x01 -#define V_BERT_ERR 0x08 -#define V_AUTO_WD_RES 0x20 -#define V_WD_RES 0x80 -/* R_BERT_STA */ -#define V_BERT_SYNC_SRC 0x01 -#define V_BERT_SYNC 0x10 -#define V_BERT_INV_DATA 0x20 - -/* chapter 11: auxiliary interface */ -/* R_BRG_PCM_CFG */ -#define V_BRG_EN 0x01 -#define V_BRG_MD 0x02 -#define V_PCM_CLK 0x20 -#define V_ADDR_WRDLY 0x40 -/* R_BRG_CTRL */ -#define V_BRG_CS 0x01 -#define V_BRG_ADDR 0x08 -#define V_BRG_CS_SRC 0x80 -/* R_BRG_MD */ -#define V_BRG_MD0 0x01 -#define V_BRG_MD1 0x02 -#define V_BRG_MD2 0x04 -#define V_BRG_MD3 0x08 -#define V_BRG_MD4 0x10 -#define V_BRG_MD5 0x20 -#define V_BRG_MD6 0x40 -#define V_BRG_MD7 0x80 -/* R_BRG_TIM0 */ -#define V_BRG_TIM0_IDLE 0x01 -#define V_BRG_TIM0_CLK 0x10 -/* R_BRG_TIM1 */ -#define V_BRG_TIM1_IDLE 0x01 -#define V_BRG_TIM1_CLK 0x10 -/* R_BRG_TIM2 */ -#define V_BRG_TIM2_IDLE 0x01 -#define V_BRG_TIM2_CLK 0x10 -/* R_BRG_TIM3 */ -#define V_BRG_TIM3_IDLE 0x01 -#define V_BRG_TIM3_CLK 0x10 -/* R_BRG_TIM_SEL01 */ -#define V_BRG_WR_SEL0 0x01 -#define V_BRG_RD_SEL0 0x04 -#define V_BRG_WR_SEL1 0x10 -#define V_BRG_RD_SEL1 0x40 -/* R_BRG_TIM_SEL23 */ -#define V_BRG_WR_SEL2 0x01 -#define V_BRG_RD_SEL2 0x04 -#define V_BRG_WR_SEL3 0x10 -#define V_BRG_RD_SEL3 0x40 -/* R_BRG_TIM_SEL45 */ -#define V_BRG_WR_SEL4 0x01 -#define V_BRG_RD_SEL4 0x04 -#define V_BRG_WR_SEL5 0x10 -#define V_BRG_RD_SEL5 0x40 -/* R_BRG_TIM_SEL67 */ -#define V_BRG_WR_SEL6 0x01 -#define V_BRG_RD_SEL6 0x04 -#define V_BRG_WR_SEL7 0x10 -#define V_BRG_RD_SEL7 0x40 - -/* chapter 12: clock, reset, interrupt, timer and watchdog */ -/* R_IRQMSK_MISC */ -#define V_STA_IRQMSK 0x01 -#define V_TI_IRQMSK 0x02 -#define V_PROC_IRQMSK 0x04 -#define V_DTMF_IRQMSK 0x08 -#define V_IRQ1S_MSK 0x10 -#define V_SA6_IRQMSK 0x20 -#define V_RX_EOMF_MSK 0x40 -#define V_TX_EOMF_MSK 0x80 -/* R_IRQ_CTRL */ -#define V_FIFO_IRQ 0x01 -#define V_GLOB_IRQ_EN 0x08 -#define V_IRQ_POL 0x10 -/* R_TI_WD */ -#define V_EV_TS 0x01 -#define V_WD_TS 0x10 -/* A_IRQ_MSK */ -#define V_IRQ 0x01 -#define V_BERT_EN 0x02 -#define V_MIX_IRQ 0x04 -/* R_IRQ_OVIEW */ -#define V_IRQ_FIFO_BL0 0x01 -#define V_IRQ_FIFO_BL1 0x02 -#define V_IRQ_FIFO_BL2 0x04 -#define V_IRQ_FIFO_BL3 0x08 -#define V_IRQ_FIFO_BL4 0x10 -#define V_IRQ_FIFO_BL5 0x20 -#define V_IRQ_FIFO_BL6 0x40 -#define V_IRQ_FIFO_BL7 0x80 -/* R_IRQ_MISC */ -#define V_STA_IRQ 0x01 -#define V_TI_IRQ 0x02 -#define V_IRQ_PROC 0x04 -#define V_DTMF_IRQ 0x08 -#define V_IRQ1S 0x10 -#define V_SA6_IRQ 0x20 -#define V_RX_EOMF 0x40 -#define V_TX_EOMF 0x80 -/* R_STATUS */ -#define V_BUSY 0x01 -#define V_PROC 0x02 -#define V_DTMF_STA 0x04 -#define V_LOST_STA 0x08 -#define V_SYNC_IN 0x10 -#define V_EXT_IRQSTA 0x20 -#define V_MISC_IRQSTA 0x40 -#define V_FR_IRQSTA 0x80 -/* R_IRQ_FIFO_BL0 */ -#define V_IRQ_FIFO0_TX 0x01 -#define V_IRQ_FIFO0_RX 0x02 -#define V_IRQ_FIFO1_TX 0x04 -#define V_IRQ_FIFO1_RX 0x08 -#define V_IRQ_FIFO2_TX 0x10 -#define V_IRQ_FIFO2_RX 0x20 -#define V_IRQ_FIFO3_TX 0x40 -#define V_IRQ_FIFO3_RX 0x80 -/* R_IRQ_FIFO_BL1 */ -#define V_IRQ_FIFO4_TX 0x01 -#define V_IRQ_FIFO4_RX 0x02 -#define V_IRQ_FIFO5_TX 0x04 -#define V_IRQ_FIFO5_RX 0x08 -#define V_IRQ_FIFO6_TX 0x10 -#define V_IRQ_FIFO6_RX 0x20 -#define V_IRQ_FIFO7_TX 0x40 -#define V_IRQ_FIFO7_RX 0x80 -/* R_IRQ_FIFO_BL2 */ -#define V_IRQ_FIFO8_TX 0x01 -#define V_IRQ_FIFO8_RX 0x02 -#define V_IRQ_FIFO9_TX 0x04 -#define V_IRQ_FIFO9_RX 0x08 -#define V_IRQ_FIFO10_TX 0x10 -#define V_IRQ_FIFO10_RX 0x20 -#define V_IRQ_FIFO11_TX 0x40 -#define V_IRQ_FIFO11_RX 0x80 -/* R_IRQ_FIFO_BL3 */ -#define V_IRQ_FIFO12_TX 0x01 -#define V_IRQ_FIFO12_RX 0x02 -#define V_IRQ_FIFO13_TX 0x04 -#define V_IRQ_FIFO13_RX 0x08 -#define V_IRQ_FIFO14_TX 0x10 -#define V_IRQ_FIFO14_RX 0x20 -#define V_IRQ_FIFO15_TX 0x40 -#define V_IRQ_FIFO15_RX 0x80 -/* R_IRQ_FIFO_BL4 */ -#define V_IRQ_FIFO16_TX 0x01 -#define V_IRQ_FIFO16_RX 0x02 -#define V_IRQ_FIFO17_TX 0x04 -#define V_IRQ_FIFO17_RX 0x08 -#define V_IRQ_FIFO18_TX 0x10 -#define V_IRQ_FIFO18_RX 0x20 -#define V_IRQ_FIFO19_TX 0x40 -#define V_IRQ_FIFO19_RX 0x80 -/* R_IRQ_FIFO_BL5 */ -#define V_IRQ_FIFO20_TX 0x01 -#define V_IRQ_FIFO20_RX 0x02 -#define V_IRQ_FIFO21_TX 0x04 -#define V_IRQ_FIFO21_RX 0x08 -#define V_IRQ_FIFO22_TX 0x10 -#define V_IRQ_FIFO22_RX 0x20 -#define V_IRQ_FIFO23_TX 0x40 -#define V_IRQ_FIFO23_RX 0x80 -/* R_IRQ_FIFO_BL6 */ -#define V_IRQ_FIFO24_TX 0x01 -#define V_IRQ_FIFO24_RX 0x02 -#define V_IRQ_FIFO25_TX 0x04 -#define V_IRQ_FIFO25_RX 0x08 -#define V_IRQ_FIFO26_TX 0x10 -#define V_IRQ_FIFO26_RX 0x20 -#define V_IRQ_FIFO27_TX 0x40 -#define V_IRQ_FIFO27_RX 0x80 -/* R_IRQ_FIFO_BL7 */ -#define V_IRQ_FIFO28_TX 0x01 -#define V_IRQ_FIFO28_RX 0x02 -#define V_IRQ_FIFO29_TX 0x04 -#define V_IRQ_FIFO29_RX 0x08 -#define V_IRQ_FIFO30_TX 0x10 -#define V_IRQ_FIFO30_RX 0x20 -#define V_IRQ_FIFO31_TX 0x40 -#define V_IRQ_FIFO31_RX 0x80 - -/* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */ -/* R_GPIO_OUT0 */ -#define V_GPIO_OUT0 0x01 -#define V_GPIO_OUT1 0x02 -#define V_GPIO_OUT2 0x04 -#define V_GPIO_OUT3 0x08 -#define V_GPIO_OUT4 0x10 -#define V_GPIO_OUT5 0x20 -#define V_GPIO_OUT6 0x40 -#define V_GPIO_OUT7 0x80 -/* R_GPIO_OUT1 */ -#define V_GPIO_OUT8 0x01 -#define V_GPIO_OUT9 0x02 -#define V_GPIO_OUT10 0x04 -#define V_GPIO_OUT11 0x08 -#define V_GPIO_OUT12 0x10 -#define V_GPIO_OUT13 0x20 -#define V_GPIO_OUT14 0x40 -#define V_GPIO_OUT15 0x80 -/* R_GPIO_EN0 */ -#define V_GPIO_EN0 0x01 -#define V_GPIO_EN1 0x02 -#define V_GPIO_EN2 0x04 -#define V_GPIO_EN3 0x08 -#define V_GPIO_EN4 0x10 -#define V_GPIO_EN5 0x20 -#define V_GPIO_EN6 0x40 -#define V_GPIO_EN7 0x80 -/* R_GPIO_EN1 */ -#define V_GPIO_EN8 0x01 -#define V_GPIO_EN9 0x02 -#define V_GPIO_EN10 0x04 -#define V_GPIO_EN11 0x08 -#define V_GPIO_EN12 0x10 -#define V_GPIO_EN13 0x20 -#define V_GPIO_EN14 0x40 -#define V_GPIO_EN15 0x80 -/* R_GPIO_SEL */ -#define V_GPIO_SEL0 0x01 -#define V_GPIO_SEL1 0x02 -#define V_GPIO_SEL2 0x04 -#define V_GPIO_SEL3 0x08 -#define V_GPIO_SEL4 0x10 -#define V_GPIO_SEL5 0x20 -#define V_GPIO_SEL6 0x40 -#define V_GPIO_SEL7 0x80 -/* R_GPIO_IN0 */ -#define V_GPIO_IN0 0x01 -#define V_GPIO_IN1 0x02 -#define V_GPIO_IN2 0x04 -#define V_GPIO_IN3 0x08 -#define V_GPIO_IN4 0x10 -#define V_GPIO_IN5 0x20 -#define V_GPIO_IN6 0x40 -#define V_GPIO_IN7 0x80 -/* R_GPIO_IN1 */ -#define V_GPIO_IN8 0x01 -#define V_GPIO_IN9 0x02 -#define V_GPIO_IN10 0x04 -#define V_GPIO_IN11 0x08 -#define V_GPIO_IN12 0x10 -#define V_GPIO_IN13 0x20 -#define V_GPIO_IN14 0x40 -#define V_GPIO_IN15 0x80 -/* R_GPI_IN0 */ -#define V_GPI_IN0 0x01 -#define V_GPI_IN1 0x02 -#define V_GPI_IN2 0x04 -#define V_GPI_IN3 0x08 -#define V_GPI_IN4 0x10 -#define V_GPI_IN5 0x20 -#define V_GPI_IN6 0x40 -#define V_GPI_IN7 0x80 -/* R_GPI_IN1 */ -#define V_GPI_IN8 0x01 -#define V_GPI_IN9 0x02 -#define V_GPI_IN10 0x04 -#define V_GPI_IN11 0x08 -#define V_GPI_IN12 0x10 -#define V_GPI_IN13 0x20 -#define V_GPI_IN14 0x40 -#define V_GPI_IN15 0x80 -/* R_GPI_IN2 */ -#define V_GPI_IN16 0x01 -#define V_GPI_IN17 0x02 -#define V_GPI_IN18 0x04 -#define V_GPI_IN19 0x08 -#define V_GPI_IN20 0x10 -#define V_GPI_IN21 0x20 -#define V_GPI_IN22 0x40 -#define V_GPI_IN23 0x80 -/* R_GPI_IN3 */ -#define V_GPI_IN24 0x01 -#define V_GPI_IN25 0x02 -#define V_GPI_IN26 0x04 -#define V_GPI_IN27 0x08 -#define V_GPI_IN28 0x10 -#define V_GPI_IN29 0x20 -#define V_GPI_IN30 0x40 -#define V_GPI_IN31 0x80 - -/* map of all registers, used for debugging */ - -#ifdef HFC_REGISTER_DEBUG -struct hfc_register_names { - char *name; - u_char reg; -} hfc_register_names[] = { - /* write registers */ - {"R_CIRM", 0x00}, - {"R_CTRL", 0x01}, - {"R_BRG_PCM_CFG ", 0x02}, - {"R_RAM_ADDR0", 0x08}, - {"R_RAM_ADDR1", 0x09}, - {"R_RAM_ADDR2", 0x0A}, - {"R_FIRST_FIFO", 0x0B}, - {"R_RAM_SZ", 0x0C}, - {"R_FIFO_MD", 0x0D}, - {"R_INC_RES_FIFO", 0x0E}, - {"R_FIFO / R_FSM_IDX", 0x0F}, - {"R_SLOT", 0x10}, - {"R_IRQMSK_MISC", 0x11}, - {"R_SCI_MSK", 0x12}, - {"R_IRQ_CTRL", 0x13}, - {"R_PCM_MD0", 0x14}, - {"R_0x15", 0x15}, - {"R_ST_SEL", 0x16}, - {"R_ST_SYNC", 0x17}, - {"R_CONF_EN", 0x18}, - {"R_TI_WD", 0x1A}, - {"R_BERT_WD_MD", 0x1B}, - {"R_DTMF", 0x1C}, - {"R_DTMF_N", 0x1D}, - {"R_E1_XX_STA", 0x20}, - {"R_LOS0", 0x22}, - {"R_LOS1", 0x23}, - {"R_RX0", 0x24}, - {"R_RX_FR0", 0x25}, - {"R_RX_FR1", 0x26}, - {"R_TX0", 0x28}, - {"R_TX1", 0x29}, - {"R_TX_FR0", 0x2C}, - {"R_TX_FR1", 0x2D}, - {"R_TX_FR2", 0x2E}, - {"R_JATT_ATT", 0x2F}, - {"A_ST_xx_STA/R_RX_OFF", 0x30}, - {"A_ST_CTRL0/R_SYNC_OUT", 0x31}, - {"A_ST_CTRL1", 0x32}, - {"A_ST_CTRL2", 0x33}, - {"A_ST_SQ_WR", 0x34}, - {"R_TX_OFF", 0x34}, - {"R_SYNC_CTRL", 0x35}, - {"A_ST_CLK_DLY", 0x37}, - {"R_PWM0", 0x38}, - {"R_PWM1", 0x39}, - {"A_ST_B1_TX", 0x3C}, - {"A_ST_B2_TX", 0x3D}, - {"A_ST_D_TX", 0x3E}, - {"R_GPIO_OUT0", 0x40}, - {"R_GPIO_OUT1", 0x41}, - {"R_GPIO_EN0", 0x42}, - {"R_GPIO_EN1", 0x43}, - {"R_GPIO_SEL", 0x44}, - {"R_BRG_CTRL", 0x45}, - {"R_PWM_MD", 0x46}, - {"R_BRG_MD", 0x47}, - {"R_BRG_TIM0", 0x48}, - {"R_BRG_TIM1", 0x49}, - {"R_BRG_TIM2", 0x4A}, - {"R_BRG_TIM3", 0x4B}, - {"R_BRG_TIM_SEL01", 0x4C}, - {"R_BRG_TIM_SEL23", 0x4D}, - {"R_BRG_TIM_SEL45", 0x4E}, - {"R_BRG_TIM_SEL67", 0x4F}, - {"A_FIFO_DATA0-2", 0x80}, - {"A_FIFO_DATA0-2_NOINC", 0x84}, - {"R_RAM_DATA", 0xC0}, - {"A_SL_CFG", 0xD0}, - {"A_CONF", 0xD1}, - {"A_CH_MSK", 0xF4}, - {"A_CON_HDLC", 0xFA}, - {"A_SUBCH_CFG", 0xFB}, - {"A_CHANNEL", 0xFC}, - {"A_FIFO_SEQ", 0xFD}, - {"A_IRQ_MSK", 0xFF}, - {NULL, 0}, - - /* read registers */ - {"A_Z1", 0x04}, - {"A_Z1H", 0x05}, - {"A_Z2", 0x06}, - {"A_Z2H", 0x07}, - {"A_F1", 0x0C}, - {"A_F2", 0x0D}, - {"R_IRQ_OVIEW", 0x10}, - {"R_IRQ_MISC", 0x11}, - {"R_IRQ_STATECH", 0x12}, - {"R_CONF_OFLOW", 0x14}, - {"R_RAM_USE", 0x15}, - {"R_CHIP_ID", 0x16}, - {"R_BERT_STA", 0x17}, - {"R_F0_CNTL", 0x18}, - {"R_F0_CNTH", 0x19}, - {"R_BERT_ECL", 0x1A}, - {"R_BERT_ECH", 0x1B}, - {"R_STATUS", 0x1C}, - {"R_CHIP_RV", 0x1F}, - {"R_STATE", 0x20}, - {"R_SYNC_STA", 0x24}, - {"R_RX_SL0_0", 0x25}, - {"R_RX_SL0_1", 0x26}, - {"R_RX_SL0_2", 0x27}, - {"R_JATT_DIR", 0x2b}, - {"R_SLIP", 0x2c}, - {"A_ST_RD_STA", 0x30}, - {"R_FAS_ECL", 0x30}, - {"R_FAS_ECH", 0x31}, - {"R_VIO_ECL", 0x32}, - {"R_VIO_ECH", 0x33}, - {"R_CRC_ECL / A_ST_SQ_RD", 0x34}, - {"R_CRC_ECH", 0x35}, - {"R_E_ECL", 0x36}, - {"R_E_ECH", 0x37}, - {"R_SA6_SA13_ECL", 0x38}, - {"R_SA6_SA13_ECH", 0x39}, - {"R_SA6_SA23_ECL", 0x3A}, - {"R_SA6_SA23_ECH", 0x3B}, - {"A_ST_B1_RX", 0x3C}, - {"A_ST_B2_RX", 0x3D}, - {"A_ST_D_RX", 0x3E}, - {"A_ST_E_RX", 0x3F}, - {"R_GPIO_IN0", 0x40}, - {"R_GPIO_IN1", 0x41}, - {"R_GPI_IN0", 0x44}, - {"R_GPI_IN1", 0x45}, - {"R_GPI_IN2", 0x46}, - {"R_GPI_IN3", 0x47}, - {"A_FIFO_DATA0-2", 0x80}, - {"A_FIFO_DATA0-2_NOINC", 0x84}, - {"R_INT_DATA", 0x88}, - {"R_RAM_DATA", 0xC0}, - {"R_IRQ_FIFO_BL0", 0xC8}, - {"R_IRQ_FIFO_BL1", 0xC9}, - {"R_IRQ_FIFO_BL2", 0xCA}, - {"R_IRQ_FIFO_BL3", 0xCB}, - {"R_IRQ_FIFO_BL4", 0xCC}, - {"R_IRQ_FIFO_BL5", 0xCD}, - {"R_IRQ_FIFO_BL6", 0xCE}, - {"R_IRQ_FIFO_BL7", 0xCF}, -}; -#endif /* HFC_REGISTER_DEBUG */ diff --git a/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h b/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h deleted file mode 100644 index 448ded8f9d24..000000000000 --- a/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h +++ /dev/null @@ -1,167 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * For License see notice in hfc_multi.c - * - * special IO and init functions for the embedded XHFC board - * from Speech Design - * - */ - -#include - -/* Change this to the value used by your board */ -#ifndef IMAP_ADDR -#define IMAP_ADDR 0xFFF00000 -#endif - -static void -#ifdef HFC_REGISTER_DEBUG -HFC_outb_embsd(struct hfc_multi *hc, u_char reg, u_char val, - const char *function, int line) -#else - HFC_outb_embsd(struct hfc_multi *hc, u_char reg, u_char val) -#endif -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - writeb(reg, hc->xhfc_memaddr); - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - writeb(val, hc->xhfc_memdata); -} -static u_char -#ifdef HFC_REGISTER_DEBUG -HFC_inb_embsd(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inb_embsd(struct hfc_multi *hc, u_char reg) -#endif -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - writeb(reg, hc->xhfc_memaddr); - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - return readb(hc->xhfc_memdata); -} -static u_short -#ifdef HFC_REGISTER_DEBUG -HFC_inw_embsd(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inw_embsd(struct hfc_multi *hc, u_char reg) -#endif -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - writeb(reg, hc->xhfc_memaddr); - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - return readb(hc->xhfc_memdata); -} -static void -#ifdef HFC_REGISTER_DEBUG -HFC_wait_embsd(struct hfc_multi *hc, const char *function, int line) -#else - HFC_wait_embsd(struct hfc_multi *hc) -#endif -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - writeb(R_STATUS, hc->xhfc_memaddr); - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - while (readb(hc->xhfc_memdata) & V_BUSY) - cpu_relax(); -} - -/* write fifo data (EMBSD) */ -void -write_fifo_embsd(struct hfc_multi *hc, u_char *data, int len) -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - *hc->xhfc_memaddr = A_FIFO_DATA0; - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - while (len) { - *hc->xhfc_memdata = *data; - data++; - len--; - } -} - -/* read fifo data (EMBSD) */ -void -read_fifo_embsd(struct hfc_multi *hc, u_char *data, int len) -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - *hc->xhfc_memaddr = A_FIFO_DATA0; - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - while (len) { - *data = (u_char)(*hc->xhfc_memdata); - data++; - len--; - } -} - -static int -setup_embedded(struct hfc_multi *hc, struct hm_map *m) -{ - printk(KERN_INFO - "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", - m->vendor_name, m->card_name, m->clock2 ? "double" : "normal"); - - hc->pci_dev = NULL; - if (m->clock2) - test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); - - hc->leds = m->leds; - hc->ledstate = 0xAFFEAFFE; - hc->opticalsupport = m->opticalsupport; - - hc->pci_iobase = 0; - hc->pci_membase = 0; - hc->xhfc_membase = NULL; - hc->xhfc_memaddr = NULL; - hc->xhfc_memdata = NULL; - - /* set memory access methods */ - if (m->io_mode) /* use mode from card config */ - hc->io_mode = m->io_mode; - switch (hc->io_mode) { - case HFC_IO_MODE_EMBSD: - test_and_set_bit(HFC_CHIP_EMBSD, &hc->chip); - hc->slots = 128; /* required */ - hc->HFC_outb = HFC_outb_embsd; - hc->HFC_inb = HFC_inb_embsd; - hc->HFC_inw = HFC_inw_embsd; - hc->HFC_wait = HFC_wait_embsd; - hc->read_fifo = read_fifo_embsd; - hc->write_fifo = write_fifo_embsd; - hc->xhfc_origmembase = XHFC_MEMBASE + XHFC_OFFSET * hc->id; - hc->xhfc_membase = (u_char *)ioremap(hc->xhfc_origmembase, - XHFC_MEMSIZE); - if (!hc->xhfc_membase) { - printk(KERN_WARNING - "HFC-multi: failed to remap xhfc address space. " - "(internal error)\n"); - return -EIO; - } - hc->xhfc_memaddr = (u_long *)(hc->xhfc_membase + 4); - hc->xhfc_memdata = (u_long *)(hc->xhfc_membase); - printk(KERN_INFO - "HFC-multi: xhfc_membase:%#lx xhfc_origmembase:%#lx " - "xhfc_memaddr:%#lx xhfc_memdata:%#lx\n", - (u_long)hc->xhfc_membase, hc->xhfc_origmembase, - (u_long)hc->xhfc_memaddr, (u_long)hc->xhfc_memdata); - break; - default: - printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); - return -EIO; - } - - /* Prepare the MPC8XX PortA 10 as output (address/data selector) */ - hc->immap = (struct immap *)(IMAP_ADDR); - hc->immap->im_ioport.iop_papar &= ~(PA_XHFC_A0); - hc->immap->im_ioport.iop_paodr &= ~(PA_XHFC_A0); - hc->immap->im_ioport.iop_padir |= PA_XHFC_A0; - - /* Prepare the MPC8xx PortB __X__ as input (ISDN__X__IRQ) */ - hc->pb_irqmsk = (PB_XHFC_IRQ1 << hc->id); - hc->immap->im_cpm.cp_pbpar &= ~(hc->pb_irqmsk); - hc->immap->im_cpm.cp_pbodr &= ~(hc->pb_irqmsk); - hc->immap->im_cpm.cp_pbdir &= ~(hc->pb_irqmsk); - - /* At this point the needed config is done */ - /* fifos are still not enabled */ - return 0; -} diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h deleted file mode 100644 index a0e4806c11fa..000000000000 --- a/drivers/isdn/hardware/mISDN/hfc_pci.h +++ /dev/null @@ -1,214 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * specific defines for CCD's HFC 2BDS0 PCI chips - * - * Author Werner Cornelius (werner@isdn4linux.de) - * - * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de) - */ - -/* - * thresholds for transparent B-channel mode - * change mask and threshold simultaneously - */ -#define HFCPCI_BTRANS_THRESHOLD 128 -#define HFCPCI_FILLEMPTY 64 -#define HFCPCI_BTRANS_THRESMASK 0x00 - -/* defines for PCI config */ -#define PCI_ENA_MEMIO 0x02 -#define PCI_ENA_MASTER 0x04 - -/* GCI/IOM bus monitor registers */ -#define HCFPCI_C_I 0x08 -#define HFCPCI_TRxR 0x0C -#define HFCPCI_MON1_D 0x28 -#define HFCPCI_MON2_D 0x2C - -/* GCI/IOM bus timeslot registers */ -#define HFCPCI_B1_SSL 0x80 -#define HFCPCI_B2_SSL 0x84 -#define HFCPCI_AUX1_SSL 0x88 -#define HFCPCI_AUX2_SSL 0x8C -#define HFCPCI_B1_RSL 0x90 -#define HFCPCI_B2_RSL 0x94 -#define HFCPCI_AUX1_RSL 0x98 -#define HFCPCI_AUX2_RSL 0x9C - -/* GCI/IOM bus data registers */ -#define HFCPCI_B1_D 0xA0 -#define HFCPCI_B2_D 0xA4 -#define HFCPCI_AUX1_D 0xA8 -#define HFCPCI_AUX2_D 0xAC - -/* GCI/IOM bus configuration registers */ -#define HFCPCI_MST_EMOD 0xB4 -#define HFCPCI_MST_MODE 0xB8 -#define HFCPCI_CONNECT 0xBC - - -/* Interrupt and status registers */ -#define HFCPCI_FIFO_EN 0x44 -#define HFCPCI_TRM 0x48 -#define HFCPCI_B_MODE 0x4C -#define HFCPCI_CHIP_ID 0x58 -#define HFCPCI_CIRM 0x60 -#define HFCPCI_CTMT 0x64 -#define HFCPCI_INT_M1 0x68 -#define HFCPCI_INT_M2 0x6C -#define HFCPCI_INT_S1 0x78 -#define HFCPCI_INT_S2 0x7C -#define HFCPCI_STATUS 0x70 - -/* S/T section registers */ -#define HFCPCI_STATES 0xC0 -#define HFCPCI_SCTRL 0xC4 -#define HFCPCI_SCTRL_E 0xC8 -#define HFCPCI_SCTRL_R 0xCC -#define HFCPCI_SQ 0xD0 -#define HFCPCI_CLKDEL 0xDC -#define HFCPCI_B1_REC 0xF0 -#define HFCPCI_B1_SEND 0xF0 -#define HFCPCI_B2_REC 0xF4 -#define HFCPCI_B2_SEND 0xF4 -#define HFCPCI_D_REC 0xF8 -#define HFCPCI_D_SEND 0xF8 -#define HFCPCI_E_REC 0xFC - - -/* bits in status register (READ) */ -#define HFCPCI_PCI_PROC 0x02 -#define HFCPCI_NBUSY 0x04 -#define HFCPCI_TIMER_ELAP 0x10 -#define HFCPCI_STATINT 0x20 -#define HFCPCI_FRAMEINT 0x40 -#define HFCPCI_ANYINT 0x80 - -/* bits in CTMT (Write) */ -#define HFCPCI_CLTIMER 0x80 -#define HFCPCI_TIM3_125 0x04 -#define HFCPCI_TIM25 0x10 -#define HFCPCI_TIM50 0x14 -#define HFCPCI_TIM400 0x18 -#define HFCPCI_TIM800 0x1C -#define HFCPCI_AUTO_TIMER 0x20 -#define HFCPCI_TRANSB2 0x02 -#define HFCPCI_TRANSB1 0x01 - -/* bits in CIRM (Write) */ -#define HFCPCI_AUX_MSK 0x07 -#define HFCPCI_RESET 0x08 -#define HFCPCI_B1_REV 0x40 -#define HFCPCI_B2_REV 0x80 - -/* bits in INT_M1 and INT_S1 */ -#define HFCPCI_INTS_B1TRANS 0x01 -#define HFCPCI_INTS_B2TRANS 0x02 -#define HFCPCI_INTS_DTRANS 0x04 -#define HFCPCI_INTS_B1REC 0x08 -#define HFCPCI_INTS_B2REC 0x10 -#define HFCPCI_INTS_DREC 0x20 -#define HFCPCI_INTS_L1STATE 0x40 -#define HFCPCI_INTS_TIMER 0x80 - -/* bits in INT_M2 */ -#define HFCPCI_PROC_TRANS 0x01 -#define HFCPCI_GCI_I_CHG 0x02 -#define HFCPCI_GCI_MON_REC 0x04 -#define HFCPCI_IRQ_ENABLE 0x08 -#define HFCPCI_PMESEL 0x80 - -/* bits in STATES */ -#define HFCPCI_STATE_MSK 0x0F -#define HFCPCI_LOAD_STATE 0x10 -#define HFCPCI_ACTIVATE 0x20 -#define HFCPCI_DO_ACTION 0x40 -#define HFCPCI_NT_G2_G3 0x80 - -/* bits in HFCD_MST_MODE */ -#define HFCPCI_MASTER 0x01 -#define HFCPCI_SLAVE 0x00 -#define HFCPCI_F0IO_POSITIV 0x02 -#define HFCPCI_F0_NEGATIV 0x04 -#define HFCPCI_F0_2C4 0x08 -/* remaining bits are for codecs control */ - -/* bits in HFCD_SCTRL */ -#define SCTRL_B1_ENA 0x01 -#define SCTRL_B2_ENA 0x02 -#define SCTRL_MODE_TE 0x00 -#define SCTRL_MODE_NT 0x04 -#define SCTRL_LOW_PRIO 0x08 -#define SCTRL_SQ_ENA 0x10 -#define SCTRL_TEST 0x20 -#define SCTRL_NONE_CAP 0x40 -#define SCTRL_PWR_DOWN 0x80 - -/* bits in SCTRL_E */ -#define HFCPCI_AUTO_AWAKE 0x01 -#define HFCPCI_DBIT_1 0x04 -#define HFCPCI_IGNORE_COL 0x08 -#define HFCPCI_CHG_B1_B2 0x80 - -/* bits in FIFO_EN register */ -#define HFCPCI_FIFOEN_B1 0x03 -#define HFCPCI_FIFOEN_B2 0x0C -#define HFCPCI_FIFOEN_DTX 0x10 -#define HFCPCI_FIFOEN_B1TX 0x01 -#define HFCPCI_FIFOEN_B1RX 0x02 -#define HFCPCI_FIFOEN_B2TX 0x04 -#define HFCPCI_FIFOEN_B2RX 0x08 - - -/* definitions of fifo memory area */ -#define MAX_D_FRAMES 15 -#define MAX_B_FRAMES 31 -#define B_SUB_VAL 0x200 -#define B_FIFO_SIZE (0x2000 - B_SUB_VAL) -#define D_FIFO_SIZE 512 -#define D_FREG_MASK 0xF - -struct zt { - __le16 z1; /* Z1 pointer 16 Bit */ - __le16 z2; /* Z2 pointer 16 Bit */ -}; - -struct dfifo { - u_char data[D_FIFO_SIZE]; /* FIFO data space */ - u_char fill1[0x20A0 - D_FIFO_SIZE]; /* reserved, do not use */ - u_char f1, f2; /* f pointers */ - u_char fill2[0x20C0 - 0x20A2]; /* reserved, do not use */ - /* mask index with D_FREG_MASK for access */ - struct zt za[MAX_D_FRAMES + 1]; - u_char fill3[0x4000 - 0x2100]; /* align 16K */ -}; - -struct bzfifo { - struct zt za[MAX_B_FRAMES + 1]; /* only range 0x0..0x1F allowed */ - u_char f1, f2; /* f pointers */ - u_char fill[0x2100 - 0x2082]; /* alignment */ -}; - - -union fifo_area { - struct { - struct dfifo d_tx; /* D-send channel */ - struct dfifo d_rx; /* D-receive channel */ - } d_chan; - struct { - u_char fill1[0x200]; - u_char txdat_b1[B_FIFO_SIZE]; - struct bzfifo txbz_b1; - struct bzfifo txbz_b2; - u_char txdat_b2[B_FIFO_SIZE]; - u_char fill2[D_FIFO_SIZE]; - u_char rxdat_b1[B_FIFO_SIZE]; - struct bzfifo rxbz_b1; - struct bzfifo rxbz_b2; - u_char rxdat_b2[B_FIFO_SIZE]; - } b_chans; - u_char fill[32768]; -}; - -#define Write_hfc(a, b, c) (writeb(c, (a->hw.pci_io) + b)) -#define Read_hfc(a, b) (readb((a->hw.pci_io) + b)) diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c deleted file mode 100644 index b3d28976b33a..000000000000 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ /dev/null @@ -1,5540 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * hfcmulti.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards - * - * Author Andreas Eversberg (jolly@eversberg.eu) - * ported to mqueue mechanism: - * Peter Sprenger (sprengermoving-bytes.de) - * - * inspired by existing hfc-pci driver: - * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) - * Copyright 2008 by Karsten Keil (kkeil@suse.de) - * Copyright 2008 by Andreas Eversberg (jolly@eversberg.eu) - * - * Thanks to Cologne Chip AG for this great controller! - */ - -/* - * module parameters: - * type: - * By default (0), the card is automatically detected. - * Or use the following combinations: - * Bit 0-7 = 0x00001 = HFC-E1 (1 port) - * or Bit 0-7 = 0x00004 = HFC-4S (4 ports) - * or Bit 0-7 = 0x00008 = HFC-8S (8 ports) - * Bit 8 = 0x00100 = uLaw (instead of aLaw) - * Bit 9 = 0x00200 = Disable DTMF detect on all B-channels via hardware - * Bit 10 = spare - * Bit 11 = 0x00800 = Force PCM bus into slave mode. (otherwise auto) - * or Bit 12 = 0x01000 = Force PCM bus into master mode. (otherwise auto) - * Bit 13 = spare - * Bit 14 = 0x04000 = Use external ram (128K) - * Bit 15 = 0x08000 = Use external ram (512K) - * Bit 16 = 0x10000 = Use 64 timeslots instead of 32 - * or Bit 17 = 0x20000 = Use 128 timeslots instead of anything else - * Bit 18 = spare - * Bit 19 = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog) - * (all other bits are reserved and shall be 0) - * example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM - * bus (PCM master) - * - * port: (optional or required for all ports on all installed cards) - * HFC-4S/HFC-8S only bits: - * Bit 0 = 0x001 = Use master clock for this S/T interface - * (only once per chip). - * Bit 1 = 0x002 = transmitter line setup (non capacitive mode) - * Don't use this unless you know what you are doing! - * Bit 2 = 0x004 = Disable E-channel. (No E-channel processing) - * example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock - * received from port 1 - * - * HFC-E1 only bits: - * Bit 0 = 0x0001 = interface: 0=copper, 1=optical - * Bit 1 = 0x0002 = reserved (later for 32 B-channels transparent mode) - * Bit 2 = 0x0004 = Report LOS - * Bit 3 = 0x0008 = Report AIS - * Bit 4 = 0x0010 = Report SLIP - * Bit 5 = 0x0020 = Report RDI - * Bit 8 = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame - * mode instead. - * Bit 9 = 0x0200 = Force get clock from interface, even in NT mode. - * or Bit 10 = 0x0400 = Force put clock to interface, even in TE mode. - * Bit 11 = 0x0800 = Use direct RX clock for PCM sync rather than PLL. - * (E1 only) - * Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0 - * for default. - * (all other bits are reserved and shall be 0) - * - * debug: - * NOTE: only one debug value must be given for all cards - * enable debugging (see hfc_multi.h for debug options) - * - * poll: - * NOTE: only one poll value must be given for all cards - * Give the number of samples for each fifo process. - * By default 128 is used. Decrease to reduce delay, increase to - * reduce cpu load. If unsure, don't mess with it! - * Valid is 8, 16, 32, 64, 128, 256. - * - * pcm: - * NOTE: only one pcm value must be given for every card. - * The PCM bus id tells the mISDNdsp module about the connected PCM bus. - * By default (0), the PCM bus id is 100 for the card that is PCM master. - * If multiple cards are PCM master (because they are not interconnected), - * each card with PCM master will have increasing PCM id. - * All PCM buses with the same ID are expected to be connected and have - * common time slots slots. - * Only one chip of the PCM bus must be master, the others slave. - * -1 means no support of PCM bus not even. - * Omit this value, if all cards are interconnected or none is connected. - * If unsure, don't give this parameter. - * - * dmask and bmask: - * NOTE: One dmask value must be given for every HFC-E1 card. - * If omitted, the E1 card has D-channel on time slot 16, which is default. - * dmask is a 32 bit mask. The bit must be set for an alternate time slot. - * If multiple bits are set, multiple virtual card fragments are created. - * For each bit set, a bmask value must be given. Each bit on the bmask - * value stands for a B-channel. The bmask may not overlap with dmask or - * with other bmask values for that card. - * Example: dmask=0x00020002 bmask=0x0000fffc,0xfffc0000 - * This will create one fragment with D-channel on slot 1 with - * B-channels on slots 2..15, and a second fragment with D-channel - * on slot 17 with B-channels on slot 18..31. Slot 16 is unused. - * If bit 0 is set (dmask=0x00000001) the D-channel is on slot 0 and will - * not function. - * Example: dmask=0x00000001 bmask=0xfffffffe - * This will create a port with all 31 usable timeslots as - * B-channels. - * If no bits are set on bmask, no B-channel is created for that fragment. - * Example: dmask=0xfffffffe bmask=0,0,0,0.... (31 0-values for bmask) - * This will create 31 ports with one D-channel only. - * If you don't know how to use it, you don't need it! - * - * iomode: - * NOTE: only one mode value must be given for every card. - * -> See hfc_multi.h for HFC_IO_MODE_* values - * By default, the IO mode is pci memory IO (MEMIO). - * Some cards require specific IO mode, so it cannot be changed. - * It may be useful to set IO mode to register io (REGIO) to solve - * PCI bridge problems. - * If unsure, don't give this parameter. - * - * clockdelay_nt: - * NOTE: only one clockdelay_nt value must be given once for all cards. - * Give the value of the clock control register (A_ST_CLK_DLY) - * of the S/T interfaces in NT mode. - * This register is needed for the TBR3 certification, so don't change it. - * - * clockdelay_te: - * NOTE: only one clockdelay_te value must be given once - * Give the value of the clock control register (A_ST_CLK_DLY) - * of the S/T interfaces in TE mode. - * This register is needed for the TBR3 certification, so don't change it. - * - * clock: - * NOTE: only one clock value must be given once - * Selects interface with clock source for mISDN and applications. - * Set to card number starting with 1. Set to -1 to disable. - * By default, the first card is used as clock source. - * - * hwid: - * NOTE: only one hwid value must be given once - * Enable special embedded devices with XHFC controllers. - */ - -/* - * debug register access (never use this, it will flood your system log) - * #define HFC_REGISTER_DEBUG - */ - -#define HFC_MULTI_VERSION "2.03" - -#include -#include -#include -#include -#include -#include -#include - -/* - #define IRQCOUNT_DEBUG - #define IRQ_DEBUG -*/ - -#include "hfc_multi.h" -#ifdef ECHOPREP -#include "gaintab.h" -#endif - -#define MAX_CARDS 8 -#define MAX_PORTS (8 * MAX_CARDS) -#define MAX_FRAGS (32 * MAX_CARDS) - -static LIST_HEAD(HFClist); -static DEFINE_SPINLOCK(HFClock); /* global hfc list lock */ - -static void ph_state_change(struct dchannel *); - -static struct hfc_multi *syncmaster; -static int plxsd_master; /* if we have a master card (yet) */ -static DEFINE_SPINLOCK(plx_lock); /* may not acquire other lock inside */ - -#define TYP_E1 1 -#define TYP_4S 4 -#define TYP_8S 8 - -static int poll_timer = 6; /* default = 128 samples = 16ms */ -/* number of POLL_TIMER interrupts for G2 timeout (ca 1s) */ -static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 }; -#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ -#define CLKDEL_NT 0x6c /* CLKDEL in NT mode - (0x60 MUST be included!) */ - -#define DIP_4S 0x1 /* DIP Switches for Beronet 1S/2S/4S cards */ -#define DIP_8S 0x2 /* DIP Switches for Beronet 8S+ cards */ -#define DIP_E1 0x3 /* DIP Switches for Beronet E1 cards */ - -/* - * module stuff - */ - -static uint type[MAX_CARDS]; -static int pcm[MAX_CARDS]; -static uint dmask[MAX_CARDS]; -static uint bmask[MAX_FRAGS]; -static uint iomode[MAX_CARDS]; -static uint port[MAX_PORTS]; -static uint debug; -static uint poll; -static int clock; -static uint timer; -static uint clockdelay_te = CLKDEL_TE; -static uint clockdelay_nt = CLKDEL_NT; -#define HWID_NONE 0 -#define HWID_MINIP4 1 -#define HWID_MINIP8 2 -#define HWID_MINIP16 3 -static uint hwid = HWID_NONE; - -static int HFC_cnt, E1_cnt, bmask_cnt, Port_cnt, PCM_cnt = 99; - -MODULE_AUTHOR("Andreas Eversberg"); -MODULE_DESCRIPTION("mISDN driver for hfc-4s/hfc-8s/hfc-e1 based cards"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(HFC_MULTI_VERSION); -module_param(debug, uint, S_IRUGO | S_IWUSR); -module_param(poll, uint, S_IRUGO | S_IWUSR); -module_param(clock, int, S_IRUGO | S_IWUSR); -module_param(timer, uint, S_IRUGO | S_IWUSR); -module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR); -module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR); -module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(pcm, int, NULL, S_IRUGO | S_IWUSR); -module_param_array(dmask, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(bmask, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); -module_param(hwid, uint, S_IRUGO | S_IWUSR); /* The hardware ID */ - -#ifdef HFC_REGISTER_DEBUG -#define HFC_outb(hc, reg, val) \ - (hc->HFC_outb(hc, reg, val, __func__, __LINE__)) -#define HFC_outb_nodebug(hc, reg, val) \ - (hc->HFC_outb_nodebug(hc, reg, val, __func__, __LINE__)) -#define HFC_inb(hc, reg) \ - (hc->HFC_inb(hc, reg, __func__, __LINE__)) -#define HFC_inb_nodebug(hc, reg) \ - (hc->HFC_inb_nodebug(hc, reg, __func__, __LINE__)) -#define HFC_inw(hc, reg) \ - (hc->HFC_inw(hc, reg, __func__, __LINE__)) -#define HFC_inw_nodebug(hc, reg) \ - (hc->HFC_inw_nodebug(hc, reg, __func__, __LINE__)) -#define HFC_wait(hc) \ - (hc->HFC_wait(hc, __func__, __LINE__)) -#define HFC_wait_nodebug(hc) \ - (hc->HFC_wait_nodebug(hc, __func__, __LINE__)) -#else -#define HFC_outb(hc, reg, val) (hc->HFC_outb(hc, reg, val)) -#define HFC_outb_nodebug(hc, reg, val) (hc->HFC_outb_nodebug(hc, reg, val)) -#define HFC_inb(hc, reg) (hc->HFC_inb(hc, reg)) -#define HFC_inb_nodebug(hc, reg) (hc->HFC_inb_nodebug(hc, reg)) -#define HFC_inw(hc, reg) (hc->HFC_inw(hc, reg)) -#define HFC_inw_nodebug(hc, reg) (hc->HFC_inw_nodebug(hc, reg)) -#define HFC_wait(hc) (hc->HFC_wait(hc)) -#define HFC_wait_nodebug(hc) (hc->HFC_wait_nodebug(hc)) -#endif - -#ifdef CONFIG_MISDN_HFCMULTI_8xx -#include "hfc_multi_8xx.h" -#endif - -/* HFC_IO_MODE_PCIMEM */ -static void -#ifdef HFC_REGISTER_DEBUG -HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val, - const char *function, int line) -#else - HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val) -#endif -{ - writeb(val, hc->pci_membase + reg); -} -static u_char -#ifdef HFC_REGISTER_DEBUG -HFC_inb_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inb_pcimem(struct hfc_multi *hc, u_char reg) -#endif -{ - return readb(hc->pci_membase + reg); -} -static u_short -#ifdef HFC_REGISTER_DEBUG -HFC_inw_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inw_pcimem(struct hfc_multi *hc, u_char reg) -#endif -{ - return readw(hc->pci_membase + reg); -} -static void -#ifdef HFC_REGISTER_DEBUG -HFC_wait_pcimem(struct hfc_multi *hc, const char *function, int line) -#else - HFC_wait_pcimem(struct hfc_multi *hc) -#endif -{ - while (readb(hc->pci_membase + R_STATUS) & V_BUSY) - cpu_relax(); -} - -/* HFC_IO_MODE_REGIO */ -static void -#ifdef HFC_REGISTER_DEBUG -HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val, - const char *function, int line) -#else - HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val) -#endif -{ - outb(reg, hc->pci_iobase + 4); - outb(val, hc->pci_iobase); -} -static u_char -#ifdef HFC_REGISTER_DEBUG -HFC_inb_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inb_regio(struct hfc_multi *hc, u_char reg) -#endif -{ - outb(reg, hc->pci_iobase + 4); - return inb(hc->pci_iobase); -} -static u_short -#ifdef HFC_REGISTER_DEBUG -HFC_inw_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inw_regio(struct hfc_multi *hc, u_char reg) -#endif -{ - outb(reg, hc->pci_iobase + 4); - return inw(hc->pci_iobase); -} -static void -#ifdef HFC_REGISTER_DEBUG -HFC_wait_regio(struct hfc_multi *hc, const char *function, int line) -#else - HFC_wait_regio(struct hfc_multi *hc) -#endif -{ - outb(R_STATUS, hc->pci_iobase + 4); - while (inb(hc->pci_iobase) & V_BUSY) - cpu_relax(); -} - -#ifdef HFC_REGISTER_DEBUG -static void -HFC_outb_debug(struct hfc_multi *hc, u_char reg, u_char val, - const char *function, int line) -{ - char regname[256] = "", bits[9] = "xxxxxxxx"; - int i; - - i = -1; - while (hfc_register_names[++i].name) { - if (hfc_register_names[i].reg == reg) - strcat(regname, hfc_register_names[i].name); - } - if (regname[0] == '\0') - strcpy(regname, "register"); - - bits[7] = '0' + (!!(val & 1)); - bits[6] = '0' + (!!(val & 2)); - bits[5] = '0' + (!!(val & 4)); - bits[4] = '0' + (!!(val & 8)); - bits[3] = '0' + (!!(val & 16)); - bits[2] = '0' + (!!(val & 32)); - bits[1] = '0' + (!!(val & 64)); - bits[0] = '0' + (!!(val & 128)); - printk(KERN_DEBUG - "HFC_outb(chip %d, %02x=%s, 0x%02x=%s); in %s() line %d\n", - hc->id, reg, regname, val, bits, function, line); - HFC_outb_nodebug(hc, reg, val); -} -static u_char -HFC_inb_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) -{ - char regname[256] = "", bits[9] = "xxxxxxxx"; - u_char val = HFC_inb_nodebug(hc, reg); - int i; - - i = 0; - while (hfc_register_names[i++].name) - ; - while (hfc_register_names[++i].name) { - if (hfc_register_names[i].reg == reg) - strcat(regname, hfc_register_names[i].name); - } - if (regname[0] == '\0') - strcpy(regname, "register"); - - bits[7] = '0' + (!!(val & 1)); - bits[6] = '0' + (!!(val & 2)); - bits[5] = '0' + (!!(val & 4)); - bits[4] = '0' + (!!(val & 8)); - bits[3] = '0' + (!!(val & 16)); - bits[2] = '0' + (!!(val & 32)); - bits[1] = '0' + (!!(val & 64)); - bits[0] = '0' + (!!(val & 128)); - printk(KERN_DEBUG - "HFC_inb(chip %d, %02x=%s) = 0x%02x=%s; in %s() line %d\n", - hc->id, reg, regname, val, bits, function, line); - return val; -} -static u_short -HFC_inw_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) -{ - char regname[256] = ""; - u_short val = HFC_inw_nodebug(hc, reg); - int i; - - i = 0; - while (hfc_register_names[i++].name) - ; - while (hfc_register_names[++i].name) { - if (hfc_register_names[i].reg == reg) - strcat(regname, hfc_register_names[i].name); - } - if (regname[0] == '\0') - strcpy(regname, "register"); - - printk(KERN_DEBUG - "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n", - hc->id, reg, regname, val, function, line); - return val; -} -static void -HFC_wait_debug(struct hfc_multi *hc, const char *function, int line) -{ - printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n", - hc->id, function, line); - HFC_wait_nodebug(hc); -} -#endif - -/* write fifo data (REGIO) */ -static void -write_fifo_regio(struct hfc_multi *hc, u_char *data, int len) -{ - outb(A_FIFO_DATA0, (hc->pci_iobase) + 4); - while (len >> 2) { - outl(cpu_to_le32(*(u32 *)data), hc->pci_iobase); - data += 4; - len -= 4; - } - while (len >> 1) { - outw(cpu_to_le16(*(u16 *)data), hc->pci_iobase); - data += 2; - len -= 2; - } - while (len) { - outb(*data, hc->pci_iobase); - data++; - len--; - } -} -/* write fifo data (PCIMEM) */ -static void -write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) -{ - while (len >> 2) { - writel(cpu_to_le32(*(u32 *)data), - hc->pci_membase + A_FIFO_DATA0); - data += 4; - len -= 4; - } - while (len >> 1) { - writew(cpu_to_le16(*(u16 *)data), - hc->pci_membase + A_FIFO_DATA0); - data += 2; - len -= 2; - } - while (len) { - writeb(*data, hc->pci_membase + A_FIFO_DATA0); - data++; - len--; - } -} - -/* read fifo data (REGIO) */ -static void -read_fifo_regio(struct hfc_multi *hc, u_char *data, int len) -{ - outb(A_FIFO_DATA0, (hc->pci_iobase) + 4); - while (len >> 2) { - *(u32 *)data = le32_to_cpu(inl(hc->pci_iobase)); - data += 4; - len -= 4; - } - while (len >> 1) { - *(u16 *)data = le16_to_cpu(inw(hc->pci_iobase)); - data += 2; - len -= 2; - } - while (len) { - *data = inb(hc->pci_iobase); - data++; - len--; - } -} - -/* read fifo data (PCIMEM) */ -static void -read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) -{ - while (len >> 2) { - *(u32 *)data = - le32_to_cpu(readl(hc->pci_membase + A_FIFO_DATA0)); - data += 4; - len -= 4; - } - while (len >> 1) { - *(u16 *)data = - le16_to_cpu(readw(hc->pci_membase + A_FIFO_DATA0)); - data += 2; - len -= 2; - } - while (len) { - *data = readb(hc->pci_membase + A_FIFO_DATA0); - data++; - len--; - } -} - -static void -enable_hwirq(struct hfc_multi *hc) -{ - hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN; - HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); -} - -static void -disable_hwirq(struct hfc_multi *hc) -{ - hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN); - HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); -} - -#define NUM_EC 2 -#define MAX_TDM_CHAN 32 - - -static inline void -enablepcibridge(struct hfc_multi *c) -{ - HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /* was _io before */ -} - -static inline void -disablepcibridge(struct hfc_multi *c) -{ - HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /* was _io before */ -} - -static inline unsigned char -readpcibridge(struct hfc_multi *hc, unsigned char address) -{ - unsigned short cipv; - unsigned char data; - - if (!hc->pci_iobase) - return 0; - - /* slow down a PCI read access by 1 PCI clock cycle */ - HFC_outb(hc, R_CTRL, 0x4); /*was _io before*/ - - if (address == 0) - cipv = 0x4000; - else - cipv = 0x5800; - - /* select local bridge port address by writing to CIP port */ - /* data = HFC_inb(c, cipv); * was _io before */ - outw(cipv, hc->pci_iobase + 4); - data = inb(hc->pci_iobase); - - /* restore R_CTRL for normal PCI read cycle speed */ - HFC_outb(hc, R_CTRL, 0x0); /* was _io before */ - - return data; -} - -static inline void -writepcibridge(struct hfc_multi *hc, unsigned char address, unsigned char data) -{ - unsigned short cipv; - unsigned int datav; - - if (!hc->pci_iobase) - return; - - if (address == 0) - cipv = 0x4000; - else - cipv = 0x5800; - - /* select local bridge port address by writing to CIP port */ - outw(cipv, hc->pci_iobase + 4); - /* define a 32 bit dword with 4 identical bytes for write sequence */ - datav = data | ((__u32) data << 8) | ((__u32) data << 16) | - ((__u32) data << 24); - - /* - * write this 32 bit dword to the bridge data port - * this will initiate a write sequence of up to 4 writes to the same - * address on the local bus interface the number of write accesses - * is undefined but >=1 and depends on the next PCI transaction - * during write sequence on the local bus - */ - outl(datav, hc->pci_iobase); -} - -static inline void -cpld_set_reg(struct hfc_multi *hc, unsigned char reg) -{ - /* Do data pin read low byte */ - HFC_outb(hc, R_GPIO_OUT1, reg); -} - -static inline void -cpld_write_reg(struct hfc_multi *hc, unsigned char reg, unsigned char val) -{ - cpld_set_reg(hc, reg); - - enablepcibridge(hc); - writepcibridge(hc, 1, val); - disablepcibridge(hc); - - return; -} - -static inline void -vpm_write_address(struct hfc_multi *hc, unsigned short addr) -{ - cpld_write_reg(hc, 0, 0xff & addr); - cpld_write_reg(hc, 1, 0x01 & (addr >> 8)); -} - -static inline unsigned char -vpm_in(struct hfc_multi *c, int which, unsigned short addr) -{ - unsigned char res; - - vpm_write_address(c, addr); - - if (!which) - cpld_set_reg(c, 2); - else - cpld_set_reg(c, 3); - - enablepcibridge(c); - res = readpcibridge(c, 1); - disablepcibridge(c); - - cpld_set_reg(c, 0); - - return res; -} - -static inline void -vpm_out(struct hfc_multi *c, int which, unsigned short addr, - unsigned char data) -{ - vpm_write_address(c, addr); - - enablepcibridge(c); - - if (!which) - cpld_set_reg(c, 2); - else - cpld_set_reg(c, 3); - - writepcibridge(c, 1, data); - - cpld_set_reg(c, 0); - - disablepcibridge(c); - - { - unsigned char regin; - regin = vpm_in(c, which, addr); - if (regin != data) - printk(KERN_DEBUG "Wrote 0x%x to register 0x%x but got back " - "0x%x\n", data, addr, regin); - } - -} - - -static void -vpm_init(struct hfc_multi *wc) -{ - unsigned char reg; - unsigned int mask; - unsigned int i, x, y; - unsigned int ver; - - for (x = 0; x < NUM_EC; x++) { - /* Setup GPIO's */ - if (!x) { - ver = vpm_in(wc, x, 0x1a0); - printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver); - } - - for (y = 0; y < 4; y++) { - vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ - vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ - vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ - } - - /* Setup TDM path - sets fsync and tdm_clk as inputs */ - reg = vpm_in(wc, x, 0x1a3); /* misc_con */ - vpm_out(wc, x, 0x1a3, reg & ~2); - - /* Setup Echo length (256 taps) */ - vpm_out(wc, x, 0x022, 1); - vpm_out(wc, x, 0x023, 0xff); - - /* Setup timeslots */ - vpm_out(wc, x, 0x02f, 0x00); - mask = 0x02020202 << (x * 4); - - /* Setup the tdm channel masks for all chips */ - for (i = 0; i < 4; i++) - vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff); - - /* Setup convergence rate */ - printk(KERN_DEBUG "VPM: A-law mode\n"); - reg = 0x00 | 0x10 | 0x01; - vpm_out(wc, x, 0x20, reg); - printk(KERN_DEBUG "VPM reg 0x20 is %x\n", reg); - /*vpm_out(wc, x, 0x20, (0x00 | 0x08 | 0x20 | 0x10)); */ - - vpm_out(wc, x, 0x24, 0x02); - reg = vpm_in(wc, x, 0x24); - printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg); - - /* Initialize echo cans */ - for (i = 0; i < MAX_TDM_CHAN; i++) { - if (mask & (0x00000001 << i)) - vpm_out(wc, x, i, 0x00); - } - - /* - * ARM arch at least disallows a udelay of - * more than 2ms... it gives a fake "__bad_udelay" - * reference at link-time. - * long delays in kernel code are pretty sucky anyway - * for now work around it using 5 x 2ms instead of 1 x 10ms - */ - - udelay(2000); - udelay(2000); - udelay(2000); - udelay(2000); - udelay(2000); - - /* Put in bypass mode */ - for (i = 0; i < MAX_TDM_CHAN; i++) { - if (mask & (0x00000001 << i)) - vpm_out(wc, x, i, 0x01); - } - - /* Enable bypass */ - for (i = 0; i < MAX_TDM_CHAN; i++) { - if (mask & (0x00000001 << i)) - vpm_out(wc, x, 0x78 + i, 0x01); - } - - } -} - -#ifdef UNUSED -static void -vpm_check(struct hfc_multi *hctmp) -{ - unsigned char gpi2; - - gpi2 = HFC_inb(hctmp, R_GPI_IN2); - - if ((gpi2 & 0x3) != 0x3) - printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2); -} -#endif /* UNUSED */ - - -/* - * Interface to enable/disable the HW Echocan - * - * these functions are called within a spin_lock_irqsave on - * the channel instance lock, so we are not disturbed by irqs - * - * we can later easily change the interface to make other - * things configurable, for now we configure the taps - * - */ - -static void -vpm_echocan_on(struct hfc_multi *hc, int ch, int taps) -{ - unsigned int timeslot; - unsigned int unit; - struct bchannel *bch = hc->chan[ch].bch; -#ifdef TXADJ - int txadj = -4; - struct sk_buff *skb; -#endif - if (hc->chan[ch].protocol != ISDN_P_B_RAW) - return; - - if (!bch) - return; - -#ifdef TXADJ - skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, - sizeof(int), &txadj, GFP_ATOMIC); - if (skb) - recv_Bchannel_skb(bch, skb); -#endif - - timeslot = ((ch / 4) * 8) + ((ch % 4) * 4) + 1; - unit = ch % 4; - - printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n", - taps, timeslot); - - vpm_out(hc, unit, timeslot, 0x7e); -} - -static void -vpm_echocan_off(struct hfc_multi *hc, int ch) -{ - unsigned int timeslot; - unsigned int unit; - struct bchannel *bch = hc->chan[ch].bch; -#ifdef TXADJ - int txadj = 0; - struct sk_buff *skb; -#endif - - if (hc->chan[ch].protocol != ISDN_P_B_RAW) - return; - - if (!bch) - return; - -#ifdef TXADJ - skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, - sizeof(int), &txadj, GFP_ATOMIC); - if (skb) - recv_Bchannel_skb(bch, skb); -#endif - - timeslot = ((ch / 4) * 8) + ((ch % 4) * 4) + 1; - unit = ch % 4; - - printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n", - timeslot); - /* FILLME */ - vpm_out(hc, unit, timeslot, 0x01); -} - - -/* - * Speech Design resync feature - * NOTE: This is called sometimes outside interrupt handler. - * We must lock irqsave, so no other interrupt (other card) will occur! - * Also multiple interrupts may nest, so must lock each access (lists, card)! - */ -static inline void -hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm) -{ - struct hfc_multi *hc, *next, *pcmmaster = NULL; - void __iomem *plx_acc_32; - u_int pv; - u_long flags; - - spin_lock_irqsave(&HFClock, flags); - spin_lock(&plx_lock); /* must be locked inside other locks */ - - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n", - __func__, syncmaster); - - /* select new master */ - if (newmaster) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "using provided controller\n"); - } else { - list_for_each_entry_safe(hc, next, &HFClist, list) { - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - if (hc->syncronized) { - newmaster = hc; - break; - } - } - } - } - - /* Disable sync of all cards */ - list_for_each_entry_safe(hc, next, &HFClist, list) { - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv &= ~PLX_SYNC_O_EN; - writel(pv, plx_acc_32); - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { - pcmmaster = hc; - if (hc->ctype == HFC_TYPE_E1) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG - "Schedule SYNC_I\n"); - hc->e1_resync |= 1; /* get SYNC_I */ - } - } - } - } - - if (newmaster) { - hc = newmaster; - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "id=%d (0x%p) = synchronized with " - "interface.\n", hc->id, hc); - /* Enable new sync master */ - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv |= PLX_SYNC_O_EN; - writel(pv, plx_acc_32); - /* switch to jatt PLL, if not disabled by RX_SYNC */ - if (hc->ctype == HFC_TYPE_E1 - && !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "Schedule jatt PLL\n"); - hc->e1_resync |= 2; /* switch to jatt */ - } - } else { - if (pcmmaster) { - hc = pcmmaster; - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG - "id=%d (0x%p) = PCM master synchronized " - "with QUARTZ\n", hc->id, hc); - if (hc->ctype == HFC_TYPE_E1) { - /* Use the crystal clock for the PCM - master card */ - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG - "Schedule QUARTZ for HFC-E1\n"); - hc->e1_resync |= 4; /* switch quartz */ - } else { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG - "QUARTZ is automatically " - "enabled by HFC-%dS\n", hc->ctype); - } - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv |= PLX_SYNC_O_EN; - writel(pv, plx_acc_32); - } else - if (!rm) - printk(KERN_ERR "%s no pcm master, this MUST " - "not happen!\n", __func__); - } - syncmaster = newmaster; - - spin_unlock(&plx_lock); - spin_unlock_irqrestore(&HFClock, flags); -} - -/* This must be called AND hc must be locked irqsave!!! */ -static inline void -plxsd_checksync(struct hfc_multi *hc, int rm) -{ - if (hc->syncronized) { - if (syncmaster == NULL) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: GOT sync on card %d" - " (id=%d)\n", __func__, hc->id + 1, - hc->id); - hfcmulti_resync(hc, hc, rm); - } - } else { - if (syncmaster == hc) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: LOST sync on card %d" - " (id=%d)\n", __func__, hc->id + 1, - hc->id); - hfcmulti_resync(hc, NULL, rm); - } - } -} - - -/* - * free hardware resources used by driver - */ -static void -release_io_hfcmulti(struct hfc_multi *hc) -{ - void __iomem *plx_acc_32; - u_int pv; - u_long plx_flags; - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: entered\n", __func__); - - /* soft reset also masks all interrupts */ - hc->hw.r_cirm |= V_SRES; - HFC_outb(hc, R_CIRM, hc->hw.r_cirm); - udelay(1000); - hc->hw.r_cirm &= ~V_SRES; - HFC_outb(hc, R_CIRM, hc->hw.r_cirm); - udelay(1000); /* instead of 'wait' that may cause locking */ - - /* release Speech Design card, if PLX was initialized */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->plx_membase) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: release PLXSD card %d\n", - __func__, hc->id + 1); - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - writel(PLX_GPIOC_INIT, plx_acc_32); - pv = readl(plx_acc_32); - /* Termination off */ - pv &= ~PLX_TERM_ON; - /* Disconnect the PCM */ - pv |= PLX_SLAVE_EN_N; - pv &= ~PLX_MASTER_EN; - pv &= ~PLX_SYNC_O_EN; - /* Put the DSP in Reset */ - pv &= ~PLX_DSP_RES_N; - writel(pv, plx_acc_32); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PCM off: PLX_GPIO=%x\n", - __func__, pv); - spin_unlock_irqrestore(&plx_lock, plx_flags); - } - - /* disable memory mapped ports / io ports */ - test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */ - if (hc->pci_dev) - pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0); - if (hc->pci_membase) - iounmap(hc->pci_membase); - if (hc->plx_membase) - iounmap(hc->plx_membase); - if (hc->pci_iobase) - release_region(hc->pci_iobase, 8); - if (hc->xhfc_membase) - iounmap((void *)hc->xhfc_membase); - - if (hc->pci_dev) { - pci_disable_device(hc->pci_dev); - pci_set_drvdata(hc->pci_dev, NULL); - } - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: done\n", __func__); -} - -/* - * function called to reset the HFC chip. A complete software reset of chip - * and fifos is done. All configuration of the chip is done. - */ - -static int -init_chip(struct hfc_multi *hc) -{ - u_long flags, val, val2 = 0, rev; - int i, err = 0; - u_char r_conf_en, rval; - void __iomem *plx_acc_32; - u_int pv; - u_long plx_flags, hfc_flags; - int plx_count; - struct hfc_multi *pos, *next, *plx_last_hc; - - spin_lock_irqsave(&hc->lock, flags); - /* reset all registers */ - memset(&hc->hw, 0, sizeof(struct hfcm_hw)); - - /* revision check */ - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: entered\n", __func__); - val = HFC_inb(hc, R_CHIP_ID); - if ((val >> 4) != 0x8 && (val >> 4) != 0xc && (val >> 4) != 0xe && - (val >> 1) != 0x31) { - printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n", (u_int)val); - err = -EIO; - goto out; - } - rev = HFC_inb(hc, R_CHIP_RV); - printk(KERN_INFO - "HFC_multi: detected HFC with chip ID=0x%lx revision=%ld%s\n", - val, rev, (rev == 0 && (hc->ctype != HFC_TYPE_XHFC)) ? - " (old FIFO handling)" : ""); - if (hc->ctype != HFC_TYPE_XHFC && rev == 0) { - test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip); - printk(KERN_WARNING - "HFC_multi: NOTE: Your chip is revision 0, " - "ask Cologne Chip for update. Newer chips " - "have a better FIFO handling. Old chips " - "still work but may have slightly lower " - "HDLC transmit performance.\n"); - } - if (rev > 1) { - printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't " - "consider chip revision = %ld. The chip / " - "bridge may not work.\n", rev); - } - - /* set s-ram size */ - hc->Flen = 0x10; - hc->Zmin = 0x80; - hc->Zlen = 384; - hc->DTMFbase = 0x1000; - if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: changing to 128K external RAM\n", - __func__); - hc->hw.r_ctrl |= V_EXT_RAM; - hc->hw.r_ram_sz = 1; - hc->Flen = 0x20; - hc->Zmin = 0xc0; - hc->Zlen = 1856; - hc->DTMFbase = 0x2000; - } - if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: changing to 512K external RAM\n", - __func__); - hc->hw.r_ctrl |= V_EXT_RAM; - hc->hw.r_ram_sz = 2; - hc->Flen = 0x20; - hc->Zmin = 0xc0; - hc->Zlen = 8000; - hc->DTMFbase = 0x2000; - } - if (hc->ctype == HFC_TYPE_XHFC) { - hc->Flen = 0x8; - hc->Zmin = 0x0; - hc->Zlen = 64; - hc->DTMFbase = 0x0; - } - hc->max_trans = poll << 1; - if (hc->max_trans > hc->Zlen) - hc->max_trans = hc->Zlen; - - /* Speech Design PLX bridge */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: initializing PLXSD card %d\n", - __func__, hc->id + 1); - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - writel(PLX_GPIOC_INIT, plx_acc_32); - pv = readl(plx_acc_32); - /* The first and the last cards are terminating the PCM bus */ - pv |= PLX_TERM_ON; /* hc is currently the last */ - /* Disconnect the PCM */ - pv |= PLX_SLAVE_EN_N; - pv &= ~PLX_MASTER_EN; - pv &= ~PLX_SYNC_O_EN; - /* Put the DSP in Reset */ - pv &= ~PLX_DSP_RES_N; - writel(pv, plx_acc_32); - spin_unlock_irqrestore(&plx_lock, plx_flags); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: slave/term: PLX_GPIO=%x\n", - __func__, pv); - /* - * If we are the 3rd PLXSD card or higher, we must turn - * termination of last PLXSD card off. - */ - spin_lock_irqsave(&HFClock, hfc_flags); - plx_count = 0; - plx_last_hc = NULL; - list_for_each_entry_safe(pos, next, &HFClist, list) { - if (test_bit(HFC_CHIP_PLXSD, &pos->chip)) { - plx_count++; - if (pos != hc) - plx_last_hc = pos; - } - } - if (plx_count >= 3) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: card %d is between, so " - "we disable termination\n", - __func__, plx_last_hc->id + 1); - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = plx_last_hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv &= ~PLX_TERM_ON; - writel(pv, plx_acc_32); - spin_unlock_irqrestore(&plx_lock, plx_flags); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: term off: PLX_GPIO=%x\n", - __func__, pv); - } - spin_unlock_irqrestore(&HFClock, hfc_flags); - hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */ - } - - if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) - hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */ - - /* we only want the real Z2 read-pointer for revision > 0 */ - if (!test_bit(HFC_CHIP_REVISION0, &hc->chip)) - hc->hw.r_ram_sz |= V_FZ_MD; - - /* select pcm mode */ - if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: setting PCM into slave mode\n", - __func__); - } else - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) && !plxsd_master) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: setting PCM into master mode\n", - __func__); - hc->hw.r_pcm_md0 |= V_PCM_MD; - } else { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: performing PCM auto detect\n", - __func__); - } - - /* soft reset */ - HFC_outb(hc, R_CTRL, hc->hw.r_ctrl); - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, 0x0C /* R_FIFO_THRES */, - 0x11 /* 16 Bytes TX/RX */); - else - HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); - HFC_outb(hc, R_FIFO_MD, 0); - if (hc->ctype == HFC_TYPE_XHFC) - hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES; - else - hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES - | V_RLD_EPR; - HFC_outb(hc, R_CIRM, hc->hw.r_cirm); - udelay(100); - hc->hw.r_cirm = 0; - HFC_outb(hc, R_CIRM, hc->hw.r_cirm); - udelay(100); - if (hc->ctype != HFC_TYPE_XHFC) - HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); - - /* Speech Design PLX bridge pcm and sync mode */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - /* Connect PCM */ - if (hc->hw.r_pcm_md0 & V_PCM_MD) { - pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; - pv |= PLX_SYNC_O_EN; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: master: PLX_GPIO=%x\n", - __func__, pv); - } else { - pv &= ~(PLX_MASTER_EN | PLX_SLAVE_EN_N); - pv &= ~PLX_SYNC_O_EN; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: slave: PLX_GPIO=%x\n", - __func__, pv); - } - writel(pv, plx_acc_32); - spin_unlock_irqrestore(&plx_lock, plx_flags); - } - - /* PCM setup */ - HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90); - if (hc->slots == 32) - HFC_outb(hc, R_PCM_MD1, 0x00); - if (hc->slots == 64) - HFC_outb(hc, R_PCM_MD1, 0x10); - if (hc->slots == 128) - HFC_outb(hc, R_PCM_MD1, 0x20); - HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0); - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) - HFC_outb(hc, R_PCM_MD2, V_SYNC_SRC); /* sync via SYNC_I / O */ - else if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) - HFC_outb(hc, R_PCM_MD2, 0x10); /* V_C2O_EN */ - else - HFC_outb(hc, R_PCM_MD2, 0x00); /* sync from interface */ - HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); - for (i = 0; i < 256; i++) { - HFC_outb_nodebug(hc, R_SLOT, i); - HFC_outb_nodebug(hc, A_SL_CFG, 0); - if (hc->ctype != HFC_TYPE_XHFC) - HFC_outb_nodebug(hc, A_CONF, 0); - hc->slot_owner[i] = -1; - } - - /* set clock speed */ - if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: setting double clock\n", __func__); - HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); - } - - if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) - HFC_outb(hc, 0x02 /* R_CLK_CFG */, 0x40 /* V_CLKO_OFF */); - - /* B410P GPIO */ - if (test_bit(HFC_CHIP_B410P, &hc->chip)) { - printk(KERN_NOTICE "Setting GPIOs\n"); - HFC_outb(hc, R_GPIO_SEL, 0x30); - HFC_outb(hc, R_GPIO_EN1, 0x3); - udelay(1000); - printk(KERN_NOTICE "calling vpm_init\n"); - vpm_init(hc); - } - - /* check if R_F0_CNT counts (8 kHz frame count) */ - val = HFC_inb(hc, R_F0_CNTL); - val += HFC_inb(hc, R_F0_CNTH) << 8; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "HFC_multi F0_CNT %ld after reset\n", val); - spin_unlock_irqrestore(&hc->lock, flags); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((HZ / 100) ? : 1); /* Timeout minimum 10ms */ - spin_lock_irqsave(&hc->lock, flags); - val2 = HFC_inb(hc, R_F0_CNTL); - val2 += HFC_inb(hc, R_F0_CNTH) << 8; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "HFC_multi F0_CNT %ld after 10 ms (1st try)\n", - val2); - if (val2 >= val + 8) { /* 1 ms */ - /* it counts, so we keep the pcm mode */ - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) - printk(KERN_INFO "controller is PCM bus MASTER\n"); - else - if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) - printk(KERN_INFO "controller is PCM bus SLAVE\n"); - else { - test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); - printk(KERN_INFO "controller is PCM bus SLAVE " - "(auto detected)\n"); - } - } else { - /* does not count */ - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { - controller_fail: - printk(KERN_ERR "HFC_multi ERROR, getting no 125us " - "pulse. Seems that controller fails.\n"); - err = -EIO; - goto out; - } - if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { - printk(KERN_INFO "controller is PCM bus SLAVE " - "(ignoring missing PCM clock)\n"); - } else { - /* only one pcm master */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip) - && plxsd_master) { - printk(KERN_ERR "HFC_multi ERROR, no clock " - "on another Speech Design card found. " - "Please be sure to connect PCM cable.\n"); - err = -EIO; - goto out; - } - /* retry with master clock */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; - pv |= PLX_SYNC_O_EN; - writel(pv, plx_acc_32); - spin_unlock_irqrestore(&plx_lock, plx_flags); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: master: " - "PLX_GPIO=%x\n", __func__, pv); - } - hc->hw.r_pcm_md0 |= V_PCM_MD; - HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); - spin_unlock_irqrestore(&hc->lock, flags); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((HZ / 100) ?: 1); /* Timeout min. 10ms */ - spin_lock_irqsave(&hc->lock, flags); - val2 = HFC_inb(hc, R_F0_CNTL); - val2 += HFC_inb(hc, R_F0_CNTH) << 8; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "HFC_multi F0_CNT %ld after " - "10 ms (2nd try)\n", val2); - if (val2 >= val + 8) { /* 1 ms */ - test_and_set_bit(HFC_CHIP_PCM_MASTER, - &hc->chip); - printk(KERN_INFO "controller is PCM bus MASTER " - "(auto detected)\n"); - } else - goto controller_fail; - } - } - - /* Release the DSP Reset */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) - plxsd_master = 1; - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv |= PLX_DSP_RES_N; - writel(pv, plx_acc_32); - spin_unlock_irqrestore(&plx_lock, plx_flags); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: reset off: PLX_GPIO=%x\n", - __func__, pv); - } - - /* pcm id */ - if (hc->pcm) - printk(KERN_INFO "controller has given PCM BUS ID %d\n", - hc->pcm); - else { - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) - || test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - PCM_cnt++; /* SD has proprietary bridging */ - } - hc->pcm = PCM_cnt; - printk(KERN_INFO "controller has PCM BUS ID %d " - "(auto selected)\n", hc->pcm); - } - - /* set up timer */ - HFC_outb(hc, R_TI_WD, poll_timer); - hc->hw.r_irqmsk_misc |= V_TI_IRQMSK; - - /* set E1 state machine IRQ */ - if (hc->ctype == HFC_TYPE_E1) - hc->hw.r_irqmsk_misc |= V_STA_IRQMSK; - - /* set DTMF detection */ - if (test_bit(HFC_CHIP_DTMF, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: enabling DTMF detection " - "for all B-channel\n", __func__); - hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP; - if (test_bit(HFC_CHIP_ULAW, &hc->chip)) - hc->hw.r_dtmf |= V_ULAW_SEL; - HFC_outb(hc, R_DTMF_N, 102 - 1); - hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK; - } - - /* conference engine */ - if (test_bit(HFC_CHIP_ULAW, &hc->chip)) - r_conf_en = V_CONF_EN | V_ULAW; - else - r_conf_en = V_CONF_EN; - if (hc->ctype != HFC_TYPE_XHFC) - HFC_outb(hc, R_CONF_EN, r_conf_en); - - /* setting leds */ - switch (hc->leds) { - case 1: /* HFC-E1 OEM */ - if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) - HFC_outb(hc, R_GPIO_SEL, 0x32); - else - HFC_outb(hc, R_GPIO_SEL, 0x30); - - HFC_outb(hc, R_GPIO_EN1, 0x0f); - HFC_outb(hc, R_GPIO_OUT1, 0x00); - - HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); - break; - - case 2: /* HFC-4S OEM */ - case 3: - HFC_outb(hc, R_GPIO_SEL, 0xf0); - HFC_outb(hc, R_GPIO_EN1, 0xff); - HFC_outb(hc, R_GPIO_OUT1, 0x00); - break; - } - - if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) { - hc->hw.r_st_sync = 0x10; /* V_AUTO_SYNCI */ - HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); - } - - /* set master clock */ - if (hc->masterclk >= 0) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: setting ST master clock " - "to port %d (0..%d)\n", - __func__, hc->masterclk, hc->ports - 1); - hc->hw.r_st_sync |= (hc->masterclk | V_AUTO_SYNC); - HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); - } - - - - /* setting misc irq */ - HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "r_irqmsk_misc.2: 0x%x\n", - hc->hw.r_irqmsk_misc); - - /* RAM access test */ - HFC_outb(hc, R_RAM_ADDR0, 0); - HFC_outb(hc, R_RAM_ADDR1, 0); - HFC_outb(hc, R_RAM_ADDR2, 0); - for (i = 0; i < 256; i++) { - HFC_outb_nodebug(hc, R_RAM_ADDR0, i); - HFC_outb_nodebug(hc, R_RAM_DATA, ((i * 3) & 0xff)); - } - for (i = 0; i < 256; i++) { - HFC_outb_nodebug(hc, R_RAM_ADDR0, i); - HFC_inb_nodebug(hc, R_RAM_DATA); - rval = HFC_inb_nodebug(hc, R_INT_DATA); - if (rval != ((i * 3) & 0xff)) { - printk(KERN_DEBUG - "addr:%x val:%x should:%x\n", i, rval, - (i * 3) & 0xff); - err++; - } - } - if (err) { - printk(KERN_DEBUG "aborting - %d RAM access errors\n", err); - err = -EIO; - goto out; - } - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: done\n", __func__); -out: - spin_unlock_irqrestore(&hc->lock, flags); - return err; -} - - -/* - * control the watchdog - */ -static void -hfcmulti_watchdog(struct hfc_multi *hc) -{ - hc->wdcount++; - - if (hc->wdcount > 10) { - hc->wdcount = 0; - hc->wdbyte = hc->wdbyte == V_GPIO_OUT2 ? - V_GPIO_OUT3 : V_GPIO_OUT2; - - /* printk("Sending Watchdog Kill %x\n",hc->wdbyte); */ - HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); - HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte); - } -} - - - -/* - * output leds - */ -static void -hfcmulti_leds(struct hfc_multi *hc) -{ - unsigned long lled; - unsigned long leddw; - int i, state, active, leds; - struct dchannel *dch; - int led[4]; - - switch (hc->leds) { - case 1: /* HFC-E1 OEM */ - /* 2 red steady: LOS - * 1 red steady: L1 not active - * 2 green steady: L1 active - * 1st green flashing: activity on TX - * 2nd green flashing: activity on RX - */ - led[0] = 0; - led[1] = 0; - led[2] = 0; - led[3] = 0; - dch = hc->chan[hc->dnum[0]].dch; - if (dch) { - if (hc->chan[hc->dnum[0]].los) - led[1] = 1; - if (hc->e1_state != 1) { - led[0] = 1; - hc->flash[2] = 0; - hc->flash[3] = 0; - } else { - led[2] = 1; - led[3] = 1; - if (!hc->flash[2] && hc->activity_tx) - hc->flash[2] = poll; - if (!hc->flash[3] && hc->activity_rx) - hc->flash[3] = poll; - if (hc->flash[2] && hc->flash[2] < 1024) - led[2] = 0; - if (hc->flash[3] && hc->flash[3] < 1024) - led[3] = 0; - if (hc->flash[2] >= 2048) - hc->flash[2] = 0; - if (hc->flash[3] >= 2048) - hc->flash[3] = 0; - if (hc->flash[2]) - hc->flash[2] += poll; - if (hc->flash[3]) - hc->flash[3] += poll; - } - } - leds = (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xF; - /* leds are inverted */ - if (leds != (int)hc->ledstate) { - HFC_outb_nodebug(hc, R_GPIO_OUT1, leds); - hc->ledstate = leds; - } - break; - - case 2: /* HFC-4S OEM */ - /* red steady: PH_DEACTIVATE - * green steady: PH_ACTIVATE - * green flashing: activity on TX - */ - for (i = 0; i < 4; i++) { - state = 0; - active = -1; - dch = hc->chan[(i << 2) | 2].dch; - if (dch) { - state = dch->state; - if (dch->dev.D.protocol == ISDN_P_NT_S0) - active = 3; - else - active = 7; - } - if (state) { - if (state == active) { - led[i] = 1; /* led green */ - hc->activity_tx |= hc->activity_rx; - if (!hc->flash[i] && - (hc->activity_tx & (1 << i))) - hc->flash[i] = poll; - if (hc->flash[i] && hc->flash[i] < 1024) - led[i] = 0; /* led off */ - if (hc->flash[i] >= 2048) - hc->flash[i] = 0; - if (hc->flash[i]) - hc->flash[i] += poll; - } else { - led[i] = 2; /* led red */ - hc->flash[i] = 0; - } - } else - led[i] = 0; /* led off */ - } - if (test_bit(HFC_CHIP_B410P, &hc->chip)) { - leds = 0; - for (i = 0; i < 4; i++) { - if (led[i] == 1) { - /*green*/ - leds |= (0x2 << (i * 2)); - } else if (led[i] == 2) { - /*red*/ - leds |= (0x1 << (i * 2)); - } - } - if (leds != (int)hc->ledstate) { - vpm_out(hc, 0, 0x1a8 + 3, leds); - hc->ledstate = leds; - } - } else { - leds = ((led[3] > 0) << 0) | ((led[1] > 0) << 1) | - ((led[0] > 0) << 2) | ((led[2] > 0) << 3) | - ((led[3] & 1) << 4) | ((led[1] & 1) << 5) | - ((led[0] & 1) << 6) | ((led[2] & 1) << 7); - if (leds != (int)hc->ledstate) { - HFC_outb_nodebug(hc, R_GPIO_EN1, leds & 0x0F); - HFC_outb_nodebug(hc, R_GPIO_OUT1, leds >> 4); - hc->ledstate = leds; - } - } - break; - - case 3: /* HFC 1S/2S Beronet */ - /* red steady: PH_DEACTIVATE - * green steady: PH_ACTIVATE - * green flashing: activity on TX - */ - for (i = 0; i < 2; i++) { - state = 0; - active = -1; - dch = hc->chan[(i << 2) | 2].dch; - if (dch) { - state = dch->state; - if (dch->dev.D.protocol == ISDN_P_NT_S0) - active = 3; - else - active = 7; - } - if (state) { - if (state == active) { - led[i] = 1; /* led green */ - hc->activity_tx |= hc->activity_rx; - if (!hc->flash[i] && - (hc->activity_tx & (1 << i))) - hc->flash[i] = poll; - if (hc->flash[i] < 1024) - led[i] = 0; /* led off */ - if (hc->flash[i] >= 2048) - hc->flash[i] = 0; - if (hc->flash[i]) - hc->flash[i] += poll; - } else { - led[i] = 2; /* led red */ - hc->flash[i] = 0; - } - } else - led[i] = 0; /* led off */ - } - leds = (led[0] > 0) | ((led[1] > 0) << 1) | ((led[0]&1) << 2) - | ((led[1]&1) << 3); - if (leds != (int)hc->ledstate) { - HFC_outb_nodebug(hc, R_GPIO_EN1, - ((led[0] > 0) << 2) | ((led[1] > 0) << 3)); - HFC_outb_nodebug(hc, R_GPIO_OUT1, - ((led[0] & 1) << 2) | ((led[1] & 1) << 3)); - hc->ledstate = leds; - } - break; - case 8: /* HFC 8S+ Beronet */ - /* off: PH_DEACTIVATE - * steady: PH_ACTIVATE - * flashing: activity on TX - */ - lled = 0xff; /* leds off */ - for (i = 0; i < 8; i++) { - state = 0; - active = -1; - dch = hc->chan[(i << 2) | 2].dch; - if (dch) { - state = dch->state; - if (dch->dev.D.protocol == ISDN_P_NT_S0) - active = 3; - else - active = 7; - } - if (state) { - if (state == active) { - lled &= ~(1 << i); /* led on */ - hc->activity_tx |= hc->activity_rx; - if (!hc->flash[i] && - (hc->activity_tx & (1 << i))) - hc->flash[i] = poll; - if (hc->flash[i] < 1024) - lled |= 1 << i; /* led off */ - if (hc->flash[i] >= 2048) - hc->flash[i] = 0; - if (hc->flash[i]) - hc->flash[i] += poll; - } else - hc->flash[i] = 0; - } - } - leddw = lled << 24 | lled << 16 | lled << 8 | lled; - if (leddw != hc->ledstate) { - /* HFC_outb(hc, R_BRG_PCM_CFG, 1); - HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); */ - /* was _io before */ - HFC_outb_nodebug(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); - outw(0x4000, hc->pci_iobase + 4); - outl(leddw, hc->pci_iobase); - HFC_outb_nodebug(hc, R_BRG_PCM_CFG, V_PCM_CLK); - hc->ledstate = leddw; - } - break; - } - hc->activity_tx = 0; - hc->activity_rx = 0; -} -/* - * read dtmf coefficients - */ - -static void -hfcmulti_dtmf(struct hfc_multi *hc) -{ - s32 *coeff; - u_int mantissa; - int co, ch; - struct bchannel *bch = NULL; - u8 exponent; - int dtmf = 0; - int addr; - u16 w_float; - struct sk_buff *skb; - struct mISDNhead *hh; - - if (debug & DEBUG_HFCMULTI_DTMF) - printk(KERN_DEBUG "%s: dtmf detection irq\n", __func__); - for (ch = 0; ch <= 31; ch++) { - /* only process enabled B-channels */ - bch = hc->chan[ch].bch; - if (!bch) - continue; - if (!hc->created[hc->chan[ch].port]) - continue; - if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) - continue; - if (debug & DEBUG_HFCMULTI_DTMF) - printk(KERN_DEBUG "%s: dtmf channel %d:", - __func__, ch); - coeff = &(hc->chan[ch].coeff[hc->chan[ch].coeff_count * 16]); - dtmf = 1; - for (co = 0; co < 8; co++) { - /* read W(n-1) coefficient */ - addr = hc->DTMFbase + ((co << 7) | (ch << 2)); - HFC_outb_nodebug(hc, R_RAM_ADDR0, addr); - HFC_outb_nodebug(hc, R_RAM_ADDR1, addr >> 8); - HFC_outb_nodebug(hc, R_RAM_ADDR2, (addr >> 16) - | V_ADDR_INC); - w_float = HFC_inb_nodebug(hc, R_RAM_DATA); - w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); - if (debug & DEBUG_HFCMULTI_DTMF) - printk(" %04x", w_float); - - /* decode float (see chip doc) */ - mantissa = w_float & 0x0fff; - if (w_float & 0x8000) - mantissa |= 0xfffff000; - exponent = (w_float >> 12) & 0x7; - if (exponent) { - mantissa ^= 0x1000; - mantissa <<= (exponent - 1); - } - - /* store coefficient */ - coeff[co << 1] = mantissa; - - /* read W(n) coefficient */ - w_float = HFC_inb_nodebug(hc, R_RAM_DATA); - w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); - if (debug & DEBUG_HFCMULTI_DTMF) - printk(" %04x", w_float); - - /* decode float (see chip doc) */ - mantissa = w_float & 0x0fff; - if (w_float & 0x8000) - mantissa |= 0xfffff000; - exponent = (w_float >> 12) & 0x7; - if (exponent) { - mantissa ^= 0x1000; - mantissa <<= (exponent - 1); - } - - /* store coefficient */ - coeff[(co << 1) | 1] = mantissa; - } - if (debug & DEBUG_HFCMULTI_DTMF) - printk(" DTMF ready %08x %08x %08x %08x " - "%08x %08x %08x %08x\n", - coeff[0], coeff[1], coeff[2], coeff[3], - coeff[4], coeff[5], coeff[6], coeff[7]); - hc->chan[ch].coeff_count++; - if (hc->chan[ch].coeff_count == 8) { - hc->chan[ch].coeff_count = 0; - skb = mI_alloc_skb(512, GFP_ATOMIC); - if (!skb) { - printk(KERN_DEBUG "%s: No memory for skb\n", - __func__); - continue; - } - hh = mISDN_HEAD_P(skb); - hh->prim = PH_CONTROL_IND; - hh->id = DTMF_HFC_COEF; - skb_put_data(skb, hc->chan[ch].coeff, 512); - recv_Bchannel_skb(bch, skb); - } - } - - /* restart DTMF processing */ - hc->dtmf = dtmf; - if (dtmf) - HFC_outb_nodebug(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); -} - - -/* - * fill fifo as much as possible - */ - -static void -hfcmulti_tx(struct hfc_multi *hc, int ch) -{ - int i, ii, temp, tmp_len, len = 0; - int Zspace, z1, z2; /* must be int for calculation */ - int Fspace, f1, f2; - u_char *d; - int *txpending, slot_tx; - struct bchannel *bch; - struct dchannel *dch; - struct sk_buff **sp = NULL; - int *idxp; - - bch = hc->chan[ch].bch; - dch = hc->chan[ch].dch; - if ((!dch) && (!bch)) - return; - - txpending = &hc->chan[ch].txpending; - slot_tx = hc->chan[ch].slot_tx; - if (dch) { - if (!test_bit(FLG_ACTIVE, &dch->Flags)) - return; - sp = &dch->tx_skb; - idxp = &dch->tx_idx; - } else { - if (!test_bit(FLG_ACTIVE, &bch->Flags)) - return; - sp = &bch->tx_skb; - idxp = &bch->tx_idx; - } - if (*sp) - len = (*sp)->len; - - if ((!len) && *txpending != 1) - return; /* no data */ - - if (test_bit(HFC_CHIP_B410P, &hc->chip) && - (hc->chan[ch].protocol == ISDN_P_B_RAW) && - (hc->chan[ch].slot_rx < 0) && - (hc->chan[ch].slot_tx < 0)) - HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1)); - else - HFC_outb_nodebug(hc, R_FIFO, ch << 1); - HFC_wait_nodebug(hc); - - if (*txpending == 2) { - /* reset fifo */ - HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait_nodebug(hc); - HFC_outb(hc, A_SUBCH_CFG, 0); - *txpending = 1; - } -next_frame: - if (dch || test_bit(FLG_HDLC, &bch->Flags)) { - f1 = HFC_inb_nodebug(hc, A_F1); - f2 = HFC_inb_nodebug(hc, A_F2); - while (f2 != (temp = HFC_inb_nodebug(hc, A_F2))) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG - "%s(card %d): reread f2 because %d!=%d\n", - __func__, hc->id + 1, temp, f2); - f2 = temp; /* repeat until F2 is equal */ - } - Fspace = f2 - f1 - 1; - if (Fspace < 0) - Fspace += hc->Flen; - /* - * Old FIFO handling doesn't give us the current Z2 read - * pointer, so we cannot send the next frame before the fifo - * is empty. It makes no difference except for a slightly - * lower performance. - */ - if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) { - if (f1 != f2) - Fspace = 0; - else - Fspace = 1; - } - /* one frame only for ST D-channels, to allow resending */ - if (hc->ctype != HFC_TYPE_E1 && dch) { - if (f1 != f2) - Fspace = 0; - } - /* F-counter full condition */ - if (Fspace == 0) - return; - } - z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; - z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; - while (z2 != (temp = (HFC_inw_nodebug(hc, A_Z2) - hc->Zmin))) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG "%s(card %d): reread z2 because " - "%d!=%d\n", __func__, hc->id + 1, temp, z2); - z2 = temp; /* repeat unti Z2 is equal */ - } - hc->chan[ch].Zfill = z1 - z2; - if (hc->chan[ch].Zfill < 0) - hc->chan[ch].Zfill += hc->Zlen; - Zspace = z2 - z1; - if (Zspace <= 0) - Zspace += hc->Zlen; - Zspace -= 4; /* keep not too full, so pointers will not overrun */ - /* fill transparent data only to maximum transparent load (minus 4) */ - if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) - Zspace = Zspace - hc->Zlen + hc->max_trans; - if (Zspace <= 0) /* no space of 4 bytes */ - return; - - /* if no data */ - if (!len) { - if (z1 == z2) { /* empty */ - /* if done with FIFO audio data during PCM connection */ - if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && - *txpending && slot_tx >= 0) { - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG - "%s: reconnecting PCM due to no " - "more FIFO data: channel %d " - "slot_tx %d\n", - __func__, ch, slot_tx); - /* connect slot */ - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, 0xc0 - | 0x07 << 2 | V_HDLC_TRP | V_IFF); - /* Enable FIFO, no interrupt */ - else - HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | - V_HDLC_TRP | V_IFF); - HFC_outb_nodebug(hc, R_FIFO, ch << 1 | 1); - HFC_wait_nodebug(hc); - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, 0xc0 - | 0x07 << 2 | V_HDLC_TRP | V_IFF); - /* Enable FIFO, no interrupt */ - else - HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | - V_HDLC_TRP | V_IFF); - HFC_outb_nodebug(hc, R_FIFO, ch << 1); - HFC_wait_nodebug(hc); - } - *txpending = 0; - } - return; /* no data */ - } - - /* "fill fifo if empty" feature */ - if (bch && test_bit(FLG_FILLEMPTY, &bch->Flags) - && !test_bit(FLG_HDLC, &bch->Flags) && z2 == z1) { - if (debug & DEBUG_HFCMULTI_FILL) - printk(KERN_DEBUG "%s: buffer empty, so we have " - "underrun\n", __func__); - /* fill buffer, to prevent future underrun */ - hc->write_fifo(hc, hc->silence_data, poll >> 1); - Zspace -= (poll >> 1); - } - - /* if audio data and connected slot */ - if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending) - && slot_tx >= 0) { - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG "%s: disconnecting PCM due to " - "FIFO data: channel %d slot_tx %d\n", - __func__, ch, slot_tx); - /* disconnect slot */ - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, 0x80 - | 0x07 << 2 | V_HDLC_TRP | V_IFF); - /* Enable FIFO, no interrupt */ - else - HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | - V_HDLC_TRP | V_IFF); - HFC_outb_nodebug(hc, R_FIFO, ch << 1 | 1); - HFC_wait_nodebug(hc); - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, 0x80 - | 0x07 << 2 | V_HDLC_TRP | V_IFF); - /* Enable FIFO, no interrupt */ - else - HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | - V_HDLC_TRP | V_IFF); - HFC_outb_nodebug(hc, R_FIFO, ch << 1); - HFC_wait_nodebug(hc); - } - *txpending = 1; - - /* show activity */ - if (dch) - hc->activity_tx |= 1 << hc->chan[ch].port; - - /* fill fifo to what we have left */ - ii = len; - if (dch || test_bit(FLG_HDLC, &bch->Flags)) - temp = 1; - else - temp = 0; - i = *idxp; - d = (*sp)->data + i; - if (ii - i > Zspace) - ii = Zspace + i; - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space " - "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n", - __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i, - temp ? "HDLC" : "TRANS"); - - /* Have to prep the audio data */ - hc->write_fifo(hc, d, ii - i); - hc->chan[ch].Zfill += ii - i; - *idxp = ii; - - /* if not all data has been written */ - if (ii != len) { - /* NOTE: fifo is started by the calling function */ - return; - } - - /* if all data has been written, terminate frame */ - if (dch || test_bit(FLG_HDLC, &bch->Flags)) { - /* increment f-counter */ - HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); - HFC_wait_nodebug(hc); - } - - tmp_len = (*sp)->len; - dev_kfree_skb(*sp); - /* check for next frame */ - if (bch && get_next_bframe(bch)) { - len = tmp_len; - goto next_frame; - } - if (dch && get_next_dframe(dch)) { - len = tmp_len; - goto next_frame; - } - - /* - * now we have no more data, so in case of transparent, - * we set the last byte in fifo to 'silence' in case we will get - * no more data at all. this prevents sending an undefined value. - */ - if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); -} - - -/* NOTE: only called if E1 card is in active state */ -static void -hfcmulti_rx(struct hfc_multi *hc, int ch) -{ - int temp; - int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */ - int f1 = 0, f2 = 0; /* = 0, to make GCC happy */ - int again = 0; - struct bchannel *bch; - struct dchannel *dch = NULL; - struct sk_buff *skb, **sp = NULL; - int maxlen; - - bch = hc->chan[ch].bch; - if (bch) { - if (!test_bit(FLG_ACTIVE, &bch->Flags)) - return; - } else if (hc->chan[ch].dch) { - dch = hc->chan[ch].dch; - if (!test_bit(FLG_ACTIVE, &dch->Flags)) - return; - } else { - return; - } -next_frame: - /* on first AND before getting next valid frame, R_FIFO must be written - to. */ - if (test_bit(HFC_CHIP_B410P, &hc->chip) && - (hc->chan[ch].protocol == ISDN_P_B_RAW) && - (hc->chan[ch].slot_rx < 0) && - (hc->chan[ch].slot_tx < 0)) - HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1) | 1); - else - HFC_outb_nodebug(hc, R_FIFO, (ch << 1) | 1); - HFC_wait_nodebug(hc); - - /* ignore if rx is off BUT change fifo (above) to start pending TX */ - if (hc->chan[ch].rx_off) { - if (bch) - bch->dropcnt += poll; /* not exact but fair enough */ - return; - } - - if (dch || test_bit(FLG_HDLC, &bch->Flags)) { - f1 = HFC_inb_nodebug(hc, A_F1); - while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG - "%s(card %d): reread f1 because %d!=%d\n", - __func__, hc->id + 1, temp, f1); - f1 = temp; /* repeat until F1 is equal */ - } - f2 = HFC_inb_nodebug(hc, A_F2); - } - z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; - while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG "%s(card %d): reread z2 because " - "%d!=%d\n", __func__, hc->id + 1, temp, z2); - z1 = temp; /* repeat until Z1 is equal */ - } - z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; - Zsize = z1 - z2; - if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2) - /* complete hdlc frame */ - Zsize++; - if (Zsize < 0) - Zsize += hc->Zlen; - /* if buffer is empty */ - if (Zsize <= 0) - return; - - if (bch) { - maxlen = bchannel_get_rxbuf(bch, Zsize); - if (maxlen < 0) { - pr_warn("card%d.B%d: No bufferspace for %d bytes\n", - hc->id + 1, bch->nr, Zsize); - return; - } - sp = &bch->rx_skb; - maxlen = bch->maxlen; - } else { /* Dchannel */ - sp = &dch->rx_skb; - maxlen = dch->maxlen + 3; - if (*sp == NULL) { - *sp = mI_alloc_skb(maxlen, GFP_ATOMIC); - if (*sp == NULL) { - pr_warn("card%d: No mem for dch rx_skb\n", - hc->id + 1); - return; - } - } - } - /* show activity */ - if (dch) - hc->activity_rx |= 1 << hc->chan[ch].port; - - /* empty fifo with what we have */ - if (dch || test_bit(FLG_HDLC, &bch->Flags)) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d " - "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) " - "got=%d (again %d)\n", __func__, hc->id + 1, ch, - Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE", - f1, f2, Zsize + (*sp)->len, again); - /* HDLC */ - if ((Zsize + (*sp)->len) > maxlen) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG - "%s(card %d): hdlc-frame too large.\n", - __func__, hc->id + 1); - skb_trim(*sp, 0); - HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait_nodebug(hc); - return; - } - - hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); - - if (f1 != f2) { - /* increment Z2,F2-counter */ - HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); - HFC_wait_nodebug(hc); - /* check size */ - if ((*sp)->len < 4) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG - "%s(card %d): Frame below minimum " - "size\n", __func__, hc->id + 1); - skb_trim(*sp, 0); - goto next_frame; - } - /* there is at least one complete frame, check crc */ - if ((*sp)->data[(*sp)->len - 1]) { - if (debug & DEBUG_HFCMULTI_CRC) - printk(KERN_DEBUG - "%s: CRC-error\n", __func__); - skb_trim(*sp, 0); - goto next_frame; - } - skb_trim(*sp, (*sp)->len - 3); - if ((*sp)->len < MISDN_COPY_SIZE) { - skb = *sp; - *sp = mI_alloc_skb(skb->len, GFP_ATOMIC); - if (*sp) { - skb_put_data(*sp, skb->data, skb->len); - skb_trim(skb, 0); - } else { - printk(KERN_DEBUG "%s: No mem\n", - __func__); - *sp = skb; - skb = NULL; - } - } else { - skb = NULL; - } - if (debug & DEBUG_HFCMULTI_FIFO) { - printk(KERN_DEBUG "%s(card %d):", - __func__, hc->id + 1); - temp = 0; - while (temp < (*sp)->len) - printk(" %02x", (*sp)->data[temp++]); - printk("\n"); - } - if (dch) - recv_Dchannel(dch); - else - recv_Bchannel(bch, MISDN_ID_ANY, false); - *sp = skb; - again++; - goto next_frame; - } - /* there is an incomplete frame */ - } else { - /* transparent */ - hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG - "%s(card %d): fifo(%d) reading %d bytes " - "(z1=%04x, z2=%04x) TRANS\n", - __func__, hc->id + 1, ch, Zsize, z1, z2); - /* only bch is transparent */ - recv_Bchannel(bch, hc->chan[ch].Zfill, false); - } -} - - -/* - * Interrupt handler - */ -static void -signal_state_up(struct dchannel *dch, int info, char *msg) -{ - struct sk_buff *skb; - int id, data = info; - - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG "%s: %s\n", __func__, msg); - - id = TEI_SAPI | (GROUP_TEI << 8); /* manager address */ - - skb = _alloc_mISDN_skb(MPH_INFORMATION_IND, id, sizeof(data), &data, - GFP_ATOMIC); - if (!skb) - return; - recv_Dchannel_skb(dch, skb); -} - -static inline void -handle_timer_irq(struct hfc_multi *hc) -{ - int ch, temp; - struct dchannel *dch; - u_long flags; - - /* process queued resync jobs */ - if (hc->e1_resync) { - /* lock, so e1_resync gets not changed */ - spin_lock_irqsave(&HFClock, flags); - if (hc->e1_resync & 1) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "Enable SYNC_I\n"); - HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC); - /* disable JATT, if RX_SYNC is set */ - if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) - HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); - } - if (hc->e1_resync & 2) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "Enable jatt PLL\n"); - HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); - } - if (hc->e1_resync & 4) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG - "Enable QUARTZ for HFC-E1\n"); - /* set jatt to quartz */ - HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC - | V_JATT_OFF); - /* switch to JATT, in case it is not already */ - HFC_outb(hc, R_SYNC_OUT, 0); - } - hc->e1_resync = 0; - spin_unlock_irqrestore(&HFClock, flags); - } - - if (hc->ctype != HFC_TYPE_E1 || hc->e1_state == 1) - for (ch = 0; ch <= 31; ch++) { - if (hc->created[hc->chan[ch].port]) { - hfcmulti_tx(hc, ch); - /* fifo is started when switching to rx-fifo */ - hfcmulti_rx(hc, ch); - if (hc->chan[ch].dch && - hc->chan[ch].nt_timer > -1) { - dch = hc->chan[ch].dch; - if (!(--hc->chan[ch].nt_timer)) { - schedule_event(dch, - FLG_PHCHANGE); - if (debug & - DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: nt_timer at " - "state %x\n", - __func__, - dch->state); - } - } - } - } - if (hc->ctype == HFC_TYPE_E1 && hc->created[0]) { - dch = hc->chan[hc->dnum[0]].dch; - /* LOS */ - temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS; - hc->chan[hc->dnum[0]].los = temp; - if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[0]].cfg)) { - if (!temp && hc->chan[hc->dnum[0]].los) - signal_state_up(dch, L1_SIGNAL_LOS_ON, - "LOS detected"); - if (temp && !hc->chan[hc->dnum[0]].los) - signal_state_up(dch, L1_SIGNAL_LOS_OFF, - "LOS gone"); - } - if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dnum[0]].cfg)) { - /* AIS */ - temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS; - if (!temp && hc->chan[hc->dnum[0]].ais) - signal_state_up(dch, L1_SIGNAL_AIS_ON, - "AIS detected"); - if (temp && !hc->chan[hc->dnum[0]].ais) - signal_state_up(dch, L1_SIGNAL_AIS_OFF, - "AIS gone"); - hc->chan[hc->dnum[0]].ais = temp; - } - if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dnum[0]].cfg)) { - /* SLIP */ - temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX; - if (!temp && hc->chan[hc->dnum[0]].slip_rx) - signal_state_up(dch, L1_SIGNAL_SLIP_RX, - " bit SLIP detected RX"); - hc->chan[hc->dnum[0]].slip_rx = temp; - temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX; - if (!temp && hc->chan[hc->dnum[0]].slip_tx) - signal_state_up(dch, L1_SIGNAL_SLIP_TX, - " bit SLIP detected TX"); - hc->chan[hc->dnum[0]].slip_tx = temp; - } - if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dnum[0]].cfg)) { - /* RDI */ - temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A; - if (!temp && hc->chan[hc->dnum[0]].rdi) - signal_state_up(dch, L1_SIGNAL_RDI_ON, - "RDI detected"); - if (temp && !hc->chan[hc->dnum[0]].rdi) - signal_state_up(dch, L1_SIGNAL_RDI_OFF, - "RDI gone"); - hc->chan[hc->dnum[0]].rdi = temp; - } - temp = HFC_inb_nodebug(hc, R_JATT_DIR); - switch (hc->chan[hc->dnum[0]].sync) { - case 0: - if ((temp & 0x60) == 0x60) { - if (debug & DEBUG_HFCMULTI_SYNC) - printk(KERN_DEBUG - "%s: (id=%d) E1 now " - "in clock sync\n", - __func__, hc->id); - HFC_outb(hc, R_RX_OFF, - hc->chan[hc->dnum[0]].jitter | V_RX_INIT); - HFC_outb(hc, R_TX_OFF, - hc->chan[hc->dnum[0]].jitter | V_RX_INIT); - hc->chan[hc->dnum[0]].sync = 1; - goto check_framesync; - } - break; - case 1: - if ((temp & 0x60) != 0x60) { - if (debug & DEBUG_HFCMULTI_SYNC) - printk(KERN_DEBUG - "%s: (id=%d) E1 " - "lost clock sync\n", - __func__, hc->id); - hc->chan[hc->dnum[0]].sync = 0; - break; - } - check_framesync: - temp = HFC_inb_nodebug(hc, R_SYNC_STA); - if (temp == 0x27) { - if (debug & DEBUG_HFCMULTI_SYNC) - printk(KERN_DEBUG - "%s: (id=%d) E1 " - "now in frame sync\n", - __func__, hc->id); - hc->chan[hc->dnum[0]].sync = 2; - } - break; - case 2: - if ((temp & 0x60) != 0x60) { - if (debug & DEBUG_HFCMULTI_SYNC) - printk(KERN_DEBUG - "%s: (id=%d) E1 lost " - "clock & frame sync\n", - __func__, hc->id); - hc->chan[hc->dnum[0]].sync = 0; - break; - } - temp = HFC_inb_nodebug(hc, R_SYNC_STA); - if (temp != 0x27) { - if (debug & DEBUG_HFCMULTI_SYNC) - printk(KERN_DEBUG - "%s: (id=%d) E1 " - "lost frame sync\n", - __func__, hc->id); - hc->chan[hc->dnum[0]].sync = 1; - } - break; - } - } - - if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) - hfcmulti_watchdog(hc); - - if (hc->leds) - hfcmulti_leds(hc); -} - -static void -ph_state_irq(struct hfc_multi *hc, u_char r_irq_statech) -{ - struct dchannel *dch; - int ch; - int active; - u_char st_status, temp; - - /* state machine */ - for (ch = 0; ch <= 31; ch++) { - if (hc->chan[ch].dch) { - dch = hc->chan[ch].dch; - if (r_irq_statech & 1) { - HFC_outb_nodebug(hc, R_ST_SEL, - hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - /* undocumented: status changes during read */ - st_status = HFC_inb_nodebug(hc, A_ST_RD_STATE); - while (st_status != (temp = - HFC_inb_nodebug(hc, A_ST_RD_STATE))) { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG "%s: reread " - "STATE because %d!=%d\n", - __func__, temp, - st_status); - st_status = temp; /* repeat */ - } - - /* Speech Design TE-sync indication */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && - dch->dev.D.protocol == ISDN_P_TE_S0) { - if (st_status & V_FR_SYNC_ST) - hc->syncronized |= - (1 << hc->chan[ch].port); - else - hc->syncronized &= - ~(1 << hc->chan[ch].port); - } - dch->state = st_status & 0x0f; - if (dch->dev.D.protocol == ISDN_P_NT_S0) - active = 3; - else - active = 7; - if (dch->state == active) { - HFC_outb_nodebug(hc, R_FIFO, - (ch << 1) | 1); - HFC_wait_nodebug(hc); - HFC_outb_nodebug(hc, - R_INC_RES_FIFO, V_RES_F); - HFC_wait_nodebug(hc); - dch->tx_idx = 0; - } - schedule_event(dch, FLG_PHCHANGE); - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: S/T newstate %x port %d\n", - __func__, dch->state, - hc->chan[ch].port); - } - r_irq_statech >>= 1; - } - } - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) - plxsd_checksync(hc, 0); -} - -static void -fifo_irq(struct hfc_multi *hc, int block) -{ - int ch, j; - struct dchannel *dch; - struct bchannel *bch; - u_char r_irq_fifo_bl; - - r_irq_fifo_bl = HFC_inb_nodebug(hc, R_IRQ_FIFO_BL0 + block); - j = 0; - while (j < 8) { - ch = (block << 2) + (j >> 1); - dch = hc->chan[ch].dch; - bch = hc->chan[ch].bch; - if (((!dch) && (!bch)) || (!hc->created[hc->chan[ch].port])) { - j += 2; - continue; - } - if (dch && (r_irq_fifo_bl & (1 << j)) && - test_bit(FLG_ACTIVE, &dch->Flags)) { - hfcmulti_tx(hc, ch); - /* start fifo */ - HFC_outb_nodebug(hc, R_FIFO, 0); - HFC_wait_nodebug(hc); - } - if (bch && (r_irq_fifo_bl & (1 << j)) && - test_bit(FLG_ACTIVE, &bch->Flags)) { - hfcmulti_tx(hc, ch); - /* start fifo */ - HFC_outb_nodebug(hc, R_FIFO, 0); - HFC_wait_nodebug(hc); - } - j++; - if (dch && (r_irq_fifo_bl & (1 << j)) && - test_bit(FLG_ACTIVE, &dch->Flags)) { - hfcmulti_rx(hc, ch); - } - if (bch && (r_irq_fifo_bl & (1 << j)) && - test_bit(FLG_ACTIVE, &bch->Flags)) { - hfcmulti_rx(hc, ch); - } - j++; - } -} - -#ifdef IRQ_DEBUG -int irqsem; -#endif -static irqreturn_t -hfcmulti_interrupt(int intno, void *dev_id) -{ -#ifdef IRQCOUNT_DEBUG - static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0, - iq5 = 0, iq6 = 0, iqcnt = 0; -#endif - struct hfc_multi *hc = dev_id; - struct dchannel *dch; - u_char r_irq_statech, status, r_irq_misc, r_irq_oview; - int i; - void __iomem *plx_acc; - u_short wval; - u_char e1_syncsta, temp, temp2; - u_long flags; - - if (!hc) { - printk(KERN_ERR "HFC-multi: Spurious interrupt!\n"); - return IRQ_NONE; - } - - spin_lock(&hc->lock); - -#ifdef IRQ_DEBUG - if (irqsem) - printk(KERN_ERR "irq for card %d during irq from " - "card %d, this is no bug.\n", hc->id + 1, irqsem); - irqsem = hc->id + 1; -#endif -#ifdef CONFIG_MISDN_HFCMULTI_8xx - if (hc->immap->im_cpm.cp_pbdat & hc->pb_irqmsk) - goto irq_notforus; -#endif - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - spin_lock_irqsave(&plx_lock, flags); - plx_acc = hc->plx_membase + PLX_INTCSR; - wval = readw(plx_acc); - spin_unlock_irqrestore(&plx_lock, flags); - if (!(wval & PLX_INTCSR_LINTI1_STATUS)) - goto irq_notforus; - } - - status = HFC_inb_nodebug(hc, R_STATUS); - r_irq_statech = HFC_inb_nodebug(hc, R_IRQ_STATECH); -#ifdef IRQCOUNT_DEBUG - if (r_irq_statech) - iq1++; - if (status & V_DTMF_STA) - iq2++; - if (status & V_LOST_STA) - iq3++; - if (status & V_EXT_IRQSTA) - iq4++; - if (status & V_MISC_IRQSTA) - iq5++; - if (status & V_FR_IRQSTA) - iq6++; - if (iqcnt++ > 5000) { - printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n", - iq1, iq2, iq3, iq4, iq5, iq6); - iqcnt = 0; - } -#endif - - if (!r_irq_statech && - !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | - V_MISC_IRQSTA | V_FR_IRQSTA))) { - /* irq is not for us */ - goto irq_notforus; - } - hc->irqcnt++; - if (r_irq_statech) { - if (hc->ctype != HFC_TYPE_E1) - ph_state_irq(hc, r_irq_statech); - } - if (status & V_LOST_STA) { - /* LOST IRQ */ - HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */ - } - if (status & V_MISC_IRQSTA) { - /* misc IRQ */ - r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC); - r_irq_misc &= hc->hw.r_irqmsk_misc; /* ignore disabled irqs */ - if (r_irq_misc & V_STA_IRQ) { - if (hc->ctype == HFC_TYPE_E1) { - /* state machine */ - dch = hc->chan[hc->dnum[0]].dch; - e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA); - if (test_bit(HFC_CHIP_PLXSD, &hc->chip) - && hc->e1_getclock) { - if (e1_syncsta & V_FR_SYNC_E1) - hc->syncronized = 1; - else - hc->syncronized = 0; - } - /* undocumented: status changes during read */ - temp = HFC_inb_nodebug(hc, R_E1_RD_STA); - while (temp != (temp2 = - HFC_inb_nodebug(hc, R_E1_RD_STA))) { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG "%s: reread " - "STATE because %d!=%d\n", - __func__, temp, temp2); - temp = temp2; /* repeat */ - } - /* broadcast state change to all fragments */ - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: E1 (id=%d) newstate %x\n", - __func__, hc->id, temp & 0x7); - for (i = 0; i < hc->ports; i++) { - dch = hc->chan[hc->dnum[i]].dch; - dch->state = temp & 0x7; - schedule_event(dch, FLG_PHCHANGE); - } - - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) - plxsd_checksync(hc, 0); - } - } - if (r_irq_misc & V_TI_IRQ) { - if (hc->iclock_on) - mISDN_clock_update(hc->iclock, poll, NULL); - handle_timer_irq(hc); - } - - if (r_irq_misc & V_DTMF_IRQ) - hfcmulti_dtmf(hc); - - if (r_irq_misc & V_IRQ_PROC) { - static int irq_proc_cnt; - if (!irq_proc_cnt++) - printk(KERN_DEBUG "%s: got V_IRQ_PROC -" - " this should not happen\n", __func__); - } - - } - if (status & V_FR_IRQSTA) { - /* FIFO IRQ */ - r_irq_oview = HFC_inb_nodebug(hc, R_IRQ_OVIEW); - for (i = 0; i < 8; i++) { - if (r_irq_oview & (1 << i)) - fifo_irq(hc, i); - } - } - -#ifdef IRQ_DEBUG - irqsem = 0; -#endif - spin_unlock(&hc->lock); - return IRQ_HANDLED; - -irq_notforus: -#ifdef IRQ_DEBUG - irqsem = 0; -#endif - spin_unlock(&hc->lock); - return IRQ_NONE; -} - - -/* - * timer callback for D-chan busy resolution. Currently no function - */ - -static void -hfcmulti_dbusy_timer(struct timer_list *t) -{ -} - - -/* - * activate/deactivate hardware for selected channels and mode - * - * configure B-channel with the given protocol - * ch eqals to the HFC-channel (0-31) - * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31 - * for S/T, 1-31 for E1) - * the hdlc interrupts will be set/unset - */ -static int -mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx, - int bank_tx, int slot_rx, int bank_rx) -{ - int flow_tx = 0, flow_rx = 0, routing = 0; - int oslot_tx, oslot_rx; - int conf; - - if (ch < 0 || ch > 31) - return -EINVAL; - oslot_tx = hc->chan[ch].slot_tx; - oslot_rx = hc->chan[ch].slot_rx; - conf = hc->chan[ch].conf; - - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG - "%s: card %d channel %d protocol %x slot old=%d new=%d " - "bank new=%d (TX) slot old=%d new=%d bank new=%d (RX)\n", - __func__, hc->id, ch, protocol, oslot_tx, slot_tx, - bank_tx, oslot_rx, slot_rx, bank_rx); - - if (oslot_tx >= 0 && slot_tx != oslot_tx) { - /* remove from slot */ - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG "%s: remove from slot %d (TX)\n", - __func__, oslot_tx); - if (hc->slot_owner[oslot_tx << 1] == ch) { - HFC_outb(hc, R_SLOT, oslot_tx << 1); - HFC_outb(hc, A_SL_CFG, 0); - if (hc->ctype != HFC_TYPE_XHFC) - HFC_outb(hc, A_CONF, 0); - hc->slot_owner[oslot_tx << 1] = -1; - } else { - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG - "%s: we are not owner of this tx slot " - "anymore, channel %d is.\n", - __func__, hc->slot_owner[oslot_tx << 1]); - } - } - - if (oslot_rx >= 0 && slot_rx != oslot_rx) { - /* remove from slot */ - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG - "%s: remove from slot %d (RX)\n", - __func__, oslot_rx); - if (hc->slot_owner[(oslot_rx << 1) | 1] == ch) { - HFC_outb(hc, R_SLOT, (oslot_rx << 1) | V_SL_DIR); - HFC_outb(hc, A_SL_CFG, 0); - hc->slot_owner[(oslot_rx << 1) | 1] = -1; - } else { - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG - "%s: we are not owner of this rx slot " - "anymore, channel %d is.\n", - __func__, - hc->slot_owner[(oslot_rx << 1) | 1]); - } - } - - if (slot_tx < 0) { - flow_tx = 0x80; /* FIFO->ST */ - /* disable pcm slot */ - hc->chan[ch].slot_tx = -1; - hc->chan[ch].bank_tx = 0; - } else { - /* set pcm slot */ - if (hc->chan[ch].txpending) - flow_tx = 0x80; /* FIFO->ST */ - else - flow_tx = 0xc0; /* PCM->ST */ - /* put on slot */ - routing = bank_tx ? 0xc0 : 0x80; - if (conf >= 0 || bank_tx > 1) - routing = 0x40; /* loop */ - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG "%s: put channel %d to slot %d bank" - " %d flow %02x routing %02x conf %d (TX)\n", - __func__, ch, slot_tx, bank_tx, - flow_tx, routing, conf); - HFC_outb(hc, R_SLOT, slot_tx << 1); - HFC_outb(hc, A_SL_CFG, (ch << 1) | routing); - if (hc->ctype != HFC_TYPE_XHFC) - HFC_outb(hc, A_CONF, - (conf < 0) ? 0 : (conf | V_CONF_SL)); - hc->slot_owner[slot_tx << 1] = ch; - hc->chan[ch].slot_tx = slot_tx; - hc->chan[ch].bank_tx = bank_tx; - } - if (slot_rx < 0) { - /* disable pcm slot */ - flow_rx = 0x80; /* ST->FIFO */ - hc->chan[ch].slot_rx = -1; - hc->chan[ch].bank_rx = 0; - } else { - /* set pcm slot */ - if (hc->chan[ch].txpending) - flow_rx = 0x80; /* ST->FIFO */ - else - flow_rx = 0xc0; /* ST->(FIFO,PCM) */ - /* put on slot */ - routing = bank_rx ? 0x80 : 0xc0; /* reversed */ - if (conf >= 0 || bank_rx > 1) - routing = 0x40; /* loop */ - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG "%s: put channel %d to slot %d bank" - " %d flow %02x routing %02x conf %d (RX)\n", - __func__, ch, slot_rx, bank_rx, - flow_rx, routing, conf); - HFC_outb(hc, R_SLOT, (slot_rx << 1) | V_SL_DIR); - HFC_outb(hc, A_SL_CFG, (ch << 1) | V_CH_DIR | routing); - hc->slot_owner[(slot_rx << 1) | 1] = ch; - hc->chan[ch].slot_rx = slot_rx; - hc->chan[ch].bank_rx = bank_rx; - } - - switch (protocol) { - case (ISDN_P_NONE): - /* disable TX fifo */ - HFC_outb(hc, R_FIFO, ch << 1); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - /* disable RX fifo */ - HFC_outb(hc, R_FIFO, (ch << 1) | 1); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - if (hc->chan[ch].bch && hc->ctype != HFC_TYPE_E1) { - hc->hw.a_st_ctrl0[hc->chan[ch].port] &= - ((ch & 0x3) == 0) ? ~V_B1_EN : ~V_B2_EN; - HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_CTRL0, - hc->hw.a_st_ctrl0[hc->chan[ch].port]); - } - if (hc->chan[ch].bch) { - test_and_clear_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); - test_and_clear_bit(FLG_TRANSPARENT, - &hc->chan[ch].bch->Flags); - } - break; - case (ISDN_P_B_RAW): /* B-channel */ - - if (test_bit(HFC_CHIP_B410P, &hc->chip) && - (hc->chan[ch].slot_rx < 0) && - (hc->chan[ch].slot_tx < 0)) { - - printk(KERN_DEBUG - "Setting B-channel %d to echo cancelable " - "state on PCM slot %d\n", ch, - ((ch / 4) * 8) + ((ch % 4) * 4) + 1); - printk(KERN_DEBUG - "Enabling pass through for channel\n"); - vpm_out(hc, ch, ((ch / 4) * 8) + - ((ch % 4) * 4) + 1, 0x01); - /* rx path */ - /* S/T -> PCM */ - HFC_outb(hc, R_FIFO, (ch << 1)); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); - HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + - ((ch % 4) * 4) + 1) << 1); - HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1)); - - /* PCM -> FIFO */ - HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - if (hc->chan[ch].protocol != protocol) { - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - } - HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + - ((ch % 4) * 4) + 1) << 1) | 1); - HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1); - - /* tx path */ - /* PCM -> S/T */ - HFC_outb(hc, R_FIFO, (ch << 1) | 1); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); - HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + - ((ch % 4) * 4)) << 1) | 1); - HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1); - - /* FIFO -> PCM */ - HFC_outb(hc, R_FIFO, 0x20 | (ch << 1)); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - if (hc->chan[ch].protocol != protocol) { - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - } - /* tx silence */ - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); - HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + - ((ch % 4) * 4)) << 1); - HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1)); - } else { - /* enable TX fifo */ - HFC_outb(hc, R_FIFO, ch << 1); - HFC_wait(hc); - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, flow_tx | 0x07 << 2 | - V_HDLC_TRP | V_IFF); - /* Enable FIFO, no interrupt */ - else - HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | - V_HDLC_TRP | V_IFF); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - if (hc->chan[ch].protocol != protocol) { - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - } - /* tx silence */ - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); - /* enable RX fifo */ - HFC_outb(hc, R_FIFO, (ch << 1) | 1); - HFC_wait(hc); - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, flow_rx | 0x07 << 2 | - V_HDLC_TRP); - /* Enable FIFO, no interrupt*/ - else - HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | - V_HDLC_TRP); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - if (hc->chan[ch].protocol != protocol) { - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - } - } - if (hc->ctype != HFC_TYPE_E1) { - hc->hw.a_st_ctrl0[hc->chan[ch].port] |= - ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN; - HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_CTRL0, - hc->hw.a_st_ctrl0[hc->chan[ch].port]); - } - if (hc->chan[ch].bch) - test_and_set_bit(FLG_TRANSPARENT, - &hc->chan[ch].bch->Flags); - break; - case (ISDN_P_B_HDLC): /* B-channel */ - case (ISDN_P_TE_S0): /* D-channel */ - case (ISDN_P_NT_S0): - case (ISDN_P_TE_E1): - case (ISDN_P_NT_E1): - /* enable TX fifo */ - HFC_outb(hc, R_FIFO, ch << 1); - HFC_wait(hc); - if (hc->ctype == HFC_TYPE_E1 || hc->chan[ch].bch) { - /* E1 or B-channel */ - HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04); - HFC_outb(hc, A_SUBCH_CFG, 0); - } else { - /* D-Channel without HDLC fill flags */ - HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF); - HFC_outb(hc, A_SUBCH_CFG, 2); - } - HFC_outb(hc, A_IRQ_MSK, V_IRQ); - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - /* enable RX fifo */ - HFC_outb(hc, R_FIFO, (ch << 1) | 1); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04); - if (hc->ctype == HFC_TYPE_E1 || hc->chan[ch].bch) - HFC_outb(hc, A_SUBCH_CFG, 0); /* full 8 bits */ - else - HFC_outb(hc, A_SUBCH_CFG, 2); /* 2 bits dchannel */ - HFC_outb(hc, A_IRQ_MSK, V_IRQ); - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - if (hc->chan[ch].bch) { - test_and_set_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); - if (hc->ctype != HFC_TYPE_E1) { - hc->hw.a_st_ctrl0[hc->chan[ch].port] |= - ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN; - HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_CTRL0, - hc->hw.a_st_ctrl0[hc->chan[ch].port]); - } - } - break; - default: - printk(KERN_DEBUG "%s: protocol not known %x\n", - __func__, protocol); - hc->chan[ch].protocol = ISDN_P_NONE; - return -ENOPROTOOPT; - } - hc->chan[ch].protocol = protocol; - return 0; -} - - -/* - * connect/disconnect PCM - */ - -static void -hfcmulti_pcm(struct hfc_multi *hc, int ch, int slot_tx, int bank_tx, - int slot_rx, int bank_rx) -{ - if (slot_tx < 0 || slot_rx < 0 || bank_tx < 0 || bank_rx < 0) { - /* disable PCM */ - mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0); - return; - } - - /* enable pcm */ - mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx, - slot_rx, bank_rx); -} - -/* - * set/disable conference - */ - -static void -hfcmulti_conf(struct hfc_multi *hc, int ch, int num) -{ - if (num >= 0 && num <= 7) - hc->chan[ch].conf = num; - else - hc->chan[ch].conf = -1; - mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx, - hc->chan[ch].bank_tx, hc->chan[ch].slot_rx, - hc->chan[ch].bank_rx); -} - - -/* - * set/disable sample loop - */ - -/* NOTE: this function is experimental and therefore disabled */ - -/* - * Layer 1 callback function - */ -static int -hfcm_l1callback(struct dchannel *dch, u_int cmd) -{ - struct hfc_multi *hc = dch->hw; - struct sk_buff_head free_queue; - u_long flags; - - switch (cmd) { - case INFO3_P8: - case INFO3_P10: - break; - case HW_RESET_REQ: - /* start activation */ - spin_lock_irqsave(&hc->lock, flags); - if (hc->ctype == HFC_TYPE_E1) { - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: HW_RESET_REQ no BRI\n", - __func__); - } else { - HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* F3 */ - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, A_ST_WR_STATE, 3); - HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT * 3)); - /* activate */ - } - spin_unlock_irqrestore(&hc->lock, flags); - l1_event(dch->l1, HW_POWERUP_IND); - break; - case HW_DEACT_REQ: - __skb_queue_head_init(&free_queue); - /* start deactivation */ - spin_lock_irqsave(&hc->lock, flags); - if (hc->ctype == HFC_TYPE_E1) { - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: HW_DEACT_REQ no BRI\n", - __func__); - } else { - HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2); - /* deactivate */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - hc->syncronized &= - ~(1 << hc->chan[dch->slot].port); - plxsd_checksync(hc, 0); - } - } - skb_queue_splice_init(&dch->squeue, &free_queue); - if (dch->tx_skb) { - __skb_queue_tail(&free_queue, dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - __skb_queue_tail(&free_queue, dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); - spin_unlock_irqrestore(&hc->lock, flags); - __skb_queue_purge(&free_queue); - break; - case HW_POWERUP_REQ: - spin_lock_irqsave(&hc->lock, flags); - if (hc->ctype == HFC_TYPE_E1) { - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: HW_POWERUP_REQ no BRI\n", - __func__); - } else { - HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */ - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */ - } - spin_unlock_irqrestore(&hc->lock, flags); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: unknown command %x\n", - __func__, cmd); - return -1; - } - return 0; -} - -/* - * Layer2 -> Layer 1 Transfer - */ - -static int -handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct hfc_multi *hc = dch->hw; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int ret = -EINVAL; - unsigned int id; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - if (skb->len < 1) - break; - spin_lock_irqsave(&hc->lock, flags); - ret = dchannel_senddata(dch, skb); - if (ret > 0) { /* direct TX */ - id = hh->id; /* skb can be freed */ - hfcmulti_tx(hc, dch->slot); - ret = 0; - /* start fifo */ - HFC_outb(hc, R_FIFO, 0); - HFC_wait(hc); - spin_unlock_irqrestore(&hc->lock, flags); - queue_ch_frame(ch, PH_DATA_CNF, id, NULL); - } else - spin_unlock_irqrestore(&hc->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - if (dch->dev.D.protocol != ISDN_P_TE_S0) { - spin_lock_irqsave(&hc->lock, flags); - ret = 0; - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: PH_ACTIVATE port %d (0..%d)\n", - __func__, hc->chan[dch->slot].port, - hc->ports - 1); - /* start activation */ - if (hc->ctype == HFC_TYPE_E1) { - ph_state_change(dch); - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: E1 report state %x \n", - __func__, dch->state); - } else { - HFC_outb(hc, R_ST_SEL, - hc->chan[dch->slot].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); - /* G1 */ - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, A_ST_WR_STATE, 1); - HFC_outb(hc, A_ST_WR_STATE, 1 | - (V_ST_ACT * 3)); /* activate */ - dch->state = 1; - } - spin_unlock_irqrestore(&hc->lock, flags); - } else - ret = l1_event(dch->l1, hh->prim); - break; - case PH_DEACTIVATE_REQ: - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - if (dch->dev.D.protocol != ISDN_P_TE_S0) { - struct sk_buff_head free_queue; - - __skb_queue_head_init(&free_queue); - spin_lock_irqsave(&hc->lock, flags); - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: PH_DEACTIVATE port %d (0..%d)\n", - __func__, hc->chan[dch->slot].port, - hc->ports - 1); - /* start deactivation */ - if (hc->ctype == HFC_TYPE_E1) { - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: PH_DEACTIVATE no BRI\n", - __func__); - } else { - HFC_outb(hc, R_ST_SEL, - hc->chan[dch->slot].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2); - /* deactivate */ - dch->state = 1; - } - skb_queue_splice_init(&dch->squeue, &free_queue); - if (dch->tx_skb) { - __skb_queue_tail(&free_queue, dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - __skb_queue_tail(&free_queue, dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); -#ifdef FIXME - if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) - dchannel_sched_event(&hc->dch, D_CLEARBUSY); -#endif - ret = 0; - spin_unlock_irqrestore(&hc->lock, flags); - __skb_queue_purge(&free_queue); - } else - ret = l1_event(dch->l1, hh->prim); - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static void -deactivate_bchannel(struct bchannel *bch) -{ - struct hfc_multi *hc = bch->hw; - u_long flags; - - spin_lock_irqsave(&hc->lock, flags); - mISDN_clear_bchannel(bch); - hc->chan[bch->slot].coeff_count = 0; - hc->chan[bch->slot].rx_off = 0; - hc->chan[bch->slot].conf = -1; - mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0); - spin_unlock_irqrestore(&hc->lock, flags); -} - -static int -handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hfc_multi *hc = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - if (!skb->len) - break; - spin_lock_irqsave(&hc->lock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - hfcmulti_tx(hc, bch->slot); - ret = 0; - /* start fifo */ - HFC_outb_nodebug(hc, R_FIFO, 0); - HFC_wait_nodebug(hc); - } - spin_unlock_irqrestore(&hc->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", - __func__, bch->slot); - spin_lock_irqsave(&hc->lock, flags); - /* activate B-channel if not already activated */ - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { - hc->chan[bch->slot].txpending = 0; - ret = mode_hfcmulti(hc, bch->slot, - ch->protocol, - hc->chan[bch->slot].slot_tx, - hc->chan[bch->slot].bank_tx, - hc->chan[bch->slot].slot_rx, - hc->chan[bch->slot].bank_rx); - if (!ret) { - if (ch->protocol == ISDN_P_B_RAW && !hc->dtmf - && test_bit(HFC_CHIP_DTMF, &hc->chip)) { - /* start decoder */ - hc->dtmf = 1; - if (debug & DEBUG_HFCMULTI_DTMF) - printk(KERN_DEBUG - "%s: start dtmf decoder\n", - __func__); - HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | - V_RST_DTMF); - } - } - } else - ret = 0; - spin_unlock_irqrestore(&hc->lock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL, - GFP_KERNEL); - break; - case PH_CONTROL_REQ: - spin_lock_irqsave(&hc->lock, flags); - switch (hh->id) { - case HFC_SPL_LOOP_ON: /* set sample loop */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: HFC_SPL_LOOP_ON (len = %d)\n", - __func__, skb->len); - ret = 0; - break; - case HFC_SPL_LOOP_OFF: /* set silence */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_SPL_LOOP_OFF\n", - __func__); - ret = 0; - break; - default: - printk(KERN_ERR - "%s: unknown PH_CONTROL_REQ info %x\n", - __func__, hh->id); - ret = -EINVAL; - } - spin_unlock_irqrestore(&hc->lock, flags); - break; - case PH_DEACTIVATE_REQ: - deactivate_bchannel(bch); /* locked there */ - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL, - GFP_KERNEL); - ret = 0; - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -/* - * bchannel control function - */ -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - struct dsp_features *features = - (struct dsp_features *)(*((u_long *)&cq->p1)); - struct hfc_multi *hc = bch->hw; - int slot_tx; - int bank_tx; - int slot_rx; - int bank_rx; - int num; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - ret = mISDN_ctrl_bchannel(bch, cq); - cq->op |= MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP; - break; - case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */ - ret = mISDN_ctrl_bchannel(bch, cq); - hc->chan[bch->slot].rx_off = !!cq->p1; - if (!hc->chan[bch->slot].rx_off) { - /* reset fifo on rx on */ - HFC_outb_nodebug(hc, R_FIFO, (bch->slot << 1) | 1); - HFC_wait_nodebug(hc); - HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait_nodebug(hc); - } - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n", - __func__, bch->nr, hc->chan[bch->slot].rx_off); - break; - case MISDN_CTRL_FILL_EMPTY: - ret = mISDN_ctrl_bchannel(bch, cq); - hc->silence = bch->fill[0]; - memset(hc->silence_data, hc->silence, sizeof(hc->silence_data)); - break; - case MISDN_CTRL_HW_FEATURES: /* fill features structure */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HW_FEATURE request\n", - __func__); - /* create confirm */ - features->hfc_id = hc->id; - if (test_bit(HFC_CHIP_DTMF, &hc->chip)) - features->hfc_dtmf = 1; - if (test_bit(HFC_CHIP_CONF, &hc->chip)) - features->hfc_conf = 1; - features->hfc_loops = 0; - if (test_bit(HFC_CHIP_B410P, &hc->chip)) { - features->hfc_echocanhw = 1; - } else { - features->pcm_id = hc->pcm; - features->pcm_slots = hc->slots; - features->pcm_banks = 2; - } - break; - case MISDN_CTRL_HFC_PCM_CONN: /* connect to pcm timeslot (0..N) */ - slot_tx = cq->p1 & 0xff; - bank_tx = cq->p1 >> 8; - slot_rx = cq->p2 & 0xff; - bank_rx = cq->p2 >> 8; - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: HFC_PCM_CONN slot %d bank %d (TX) " - "slot %d bank %d (RX)\n", - __func__, slot_tx, bank_tx, - slot_rx, bank_rx); - if (slot_tx < hc->slots && bank_tx <= 2 && - slot_rx < hc->slots && bank_rx <= 2) - hfcmulti_pcm(hc, bch->slot, - slot_tx, bank_tx, slot_rx, bank_rx); - else { - printk(KERN_WARNING - "%s: HFC_PCM_CONN slot %d bank %d (TX) " - "slot %d bank %d (RX) out of range\n", - __func__, slot_tx, bank_tx, - slot_rx, bank_rx); - ret = -EINVAL; - } - break; - case MISDN_CTRL_HFC_PCM_DISC: /* release interface from pcm timeslot */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_PCM_DISC\n", - __func__); - hfcmulti_pcm(hc, bch->slot, -1, 0, -1, 0); - break; - case MISDN_CTRL_HFC_CONF_JOIN: /* join conference (0..7) */ - num = cq->p1 & 0xff; - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_CONF_JOIN conf %d\n", - __func__, num); - if (num <= 7) - hfcmulti_conf(hc, bch->slot, num); - else { - printk(KERN_WARNING - "%s: HW_CONF_JOIN conf %d out of range\n", - __func__, num); - ret = -EINVAL; - } - break; - case MISDN_CTRL_HFC_CONF_SPLIT: /* split conference */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_CONF_SPLIT\n", __func__); - hfcmulti_conf(hc, bch->slot, -1); - break; - case MISDN_CTRL_HFC_ECHOCAN_ON: - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_ECHOCAN_ON\n", __func__); - if (test_bit(HFC_CHIP_B410P, &hc->chip)) - vpm_echocan_on(hc, bch->slot, cq->p1); - else - ret = -EINVAL; - break; - - case MISDN_CTRL_HFC_ECHOCAN_OFF: - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_ECHOCAN_OFF\n", - __func__); - if (test_bit(HFC_CHIP_B410P, &hc->chip)) - vpm_echocan_off(hc, bch->slot); - else - ret = -EINVAL; - break; - default: - ret = mISDN_ctrl_bchannel(bch, cq); - break; - } - return ret; -} - -static int -hfcm_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hfc_multi *hc = bch->hw; - int err = -EINVAL; - u_long flags; - - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", - __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - deactivate_bchannel(bch); /* locked there */ - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - err = 0; - break; - case CONTROL_CHANNEL: - spin_lock_irqsave(&hc->lock, flags); - err = channel_bctrl(bch, arg); - spin_unlock_irqrestore(&hc->lock, flags); - break; - default: - printk(KERN_WARNING "%s: unknown prim(%x)\n", - __func__, cmd); - } - return err; -} - -/* - * handle D-channel events - * - * handle state change event - */ -static void -ph_state_change(struct dchannel *dch) -{ - struct hfc_multi *hc; - int ch, i; - - if (!dch) { - printk(KERN_WARNING "%s: ERROR given dch is NULL\n", __func__); - return; - } - hc = dch->hw; - ch = dch->slot; - - if (hc->ctype == HFC_TYPE_E1) { - if (dch->dev.D.protocol == ISDN_P_TE_E1) { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: E1 TE (id=%d) newstate %x\n", - __func__, hc->id, dch->state); - } else { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: E1 NT (id=%d) newstate %x\n", - __func__, hc->id, dch->state); - } - switch (dch->state) { - case (1): - if (hc->e1_state != 1) { - for (i = 1; i <= 31; i++) { - /* reset fifos on e1 activation */ - HFC_outb_nodebug(hc, R_FIFO, - (i << 1) | 1); - HFC_wait_nodebug(hc); - HFC_outb_nodebug(hc, R_INC_RES_FIFO, - V_RES_F); - HFC_wait_nodebug(hc); - } - } - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - break; - - default: - if (hc->e1_state != 1) - return; - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - } - hc->e1_state = dch->state; - } else { - if (dch->dev.D.protocol == ISDN_P_TE_S0) { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: S/T TE newstate %x\n", - __func__, dch->state); - switch (dch->state) { - case (0): - l1_event(dch->l1, HW_RESET_IND); - break; - case (3): - l1_event(dch->l1, HW_DEACT_IND); - break; - case (5): - case (8): - l1_event(dch->l1, ANYSIGNAL); - break; - case (6): - l1_event(dch->l1, INFO2); - break; - case (7): - l1_event(dch->l1, INFO4_P8); - break; - } - } else { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG "%s: S/T NT newstate %x\n", - __func__, dch->state); - switch (dch->state) { - case (2): - if (hc->chan[ch].nt_timer == 0) { - hc->chan[ch].nt_timer = -1; - HFC_outb(hc, R_ST_SEL, - hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, 4 | - V_ST_LD_STA); /* G4 */ - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, A_ST_WR_STATE, 4); - dch->state = 4; - } else { - /* one extra count for the next event */ - hc->chan[ch].nt_timer = - nt_t1_count[poll_timer] + 1; - HFC_outb(hc, R_ST_SEL, - hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - /* allow G2 -> G3 transition */ - HFC_outb(hc, A_ST_WR_STATE, 2 | - V_SET_G2_G3); - } - break; - case (1): - hc->chan[ch].nt_timer = -1; - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - break; - case (4): - hc->chan[ch].nt_timer = -1; - break; - case (3): - hc->chan[ch].nt_timer = -1; - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - break; - } - } - } -} - -/* - * called for card mode init message - */ - -static void -hfcmulti_initmode(struct dchannel *dch) -{ - struct hfc_multi *hc = dch->hw; - u_char a_st_wr_state, r_e1_wr_sta; - int i, pt; - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: entered\n", __func__); - - i = dch->slot; - pt = hc->chan[i].port; - if (hc->ctype == HFC_TYPE_E1) { - /* E1 */ - hc->chan[hc->dnum[pt]].slot_tx = -1; - hc->chan[hc->dnum[pt]].slot_rx = -1; - hc->chan[hc->dnum[pt]].conf = -1; - if (hc->dnum[pt]) { - mode_hfcmulti(hc, dch->slot, dch->dev.D.protocol, - -1, 0, -1, 0); - timer_setup(&dch->timer, hfcmulti_dbusy_timer, 0); - } - for (i = 1; i <= 31; i++) { - if (!((1 << i) & hc->bmask[pt])) /* skip unused chan */ - continue; - hc->chan[i].slot_tx = -1; - hc->chan[i].slot_rx = -1; - hc->chan[i].conf = -1; - mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0); - } - } - if (hc->ctype == HFC_TYPE_E1 && pt == 0) { - /* E1, port 0 */ - dch = hc->chan[hc->dnum[0]].dch; - if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[0]].cfg)) { - HFC_outb(hc, R_LOS0, 255); /* 2 ms */ - HFC_outb(hc, R_LOS1, 255); /* 512 ms */ - } - if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dnum[0]].cfg)) { - HFC_outb(hc, R_RX0, 0); - hc->hw.r_tx0 = 0 | V_OUT_EN; - } else { - HFC_outb(hc, R_RX0, 1); - hc->hw.r_tx0 = 1 | V_OUT_EN; - } - hc->hw.r_tx1 = V_ATX | V_NTRI; - HFC_outb(hc, R_TX0, hc->hw.r_tx0); - HFC_outb(hc, R_TX1, hc->hw.r_tx1); - HFC_outb(hc, R_TX_FR0, 0x00); - HFC_outb(hc, R_TX_FR1, 0xf8); - - if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[0]].cfg)) - HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E); - - HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0); - - if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[0]].cfg)) - HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC); - - if (dch->dev.D.protocol == ISDN_P_NT_E1) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: E1 port is NT-mode\n", - __func__); - r_e1_wr_sta = 0; /* G0 */ - hc->e1_getclock = 0; - } else { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: E1 port is TE-mode\n", - __func__); - r_e1_wr_sta = 0; /* F0 */ - hc->e1_getclock = 1; - } - if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) - HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); - else - HFC_outb(hc, R_SYNC_OUT, 0); - if (test_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip)) - hc->e1_getclock = 1; - if (test_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip)) - hc->e1_getclock = 0; - if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { - /* SLAVE (clock master) */ - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: E1 port is clock master " - "(clock from PCM)\n", __func__); - HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC); - } else { - if (hc->e1_getclock) { - /* MASTER (clock slave) */ - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: E1 port is clock slave " - "(clock to PCM)\n", __func__); - HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); - } else { - /* MASTER (clock master) */ - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: E1 port is " - "clock master " - "(clock from QUARTZ)\n", - __func__); - HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | - V_PCM_SYNC | V_JATT_OFF); - HFC_outb(hc, R_SYNC_OUT, 0); - } - } - HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */ - HFC_outb(hc, R_PWM_MD, V_PWM0_MD); - HFC_outb(hc, R_PWM0, 0x50); - HFC_outb(hc, R_PWM1, 0xff); - /* state machine setup */ - HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA); - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta); - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - hc->syncronized = 0; - plxsd_checksync(hc, 0); - } - } - if (hc->ctype != HFC_TYPE_E1) { - /* ST */ - hc->chan[i].slot_tx = -1; - hc->chan[i].slot_rx = -1; - hc->chan[i].conf = -1; - mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0); - timer_setup(&dch->timer, hfcmulti_dbusy_timer, 0); - hc->chan[i - 2].slot_tx = -1; - hc->chan[i - 2].slot_rx = -1; - hc->chan[i - 2].conf = -1; - mode_hfcmulti(hc, i - 2, ISDN_P_NONE, -1, 0, -1, 0); - hc->chan[i - 1].slot_tx = -1; - hc->chan[i - 1].slot_rx = -1; - hc->chan[i - 1].conf = -1; - mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0); - /* select interface */ - HFC_outb(hc, R_ST_SEL, pt); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - if (dch->dev.D.protocol == ISDN_P_NT_S0) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: ST port %d is NT-mode\n", - __func__, pt); - /* clock delay */ - HFC_outb(hc, A_ST_CLK_DLY, clockdelay_nt); - a_st_wr_state = 1; /* G1 */ - hc->hw.a_st_ctrl0[pt] = V_ST_MD; - } else { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: ST port %d is TE-mode\n", - __func__, pt); - /* clock delay */ - HFC_outb(hc, A_ST_CLK_DLY, clockdelay_te); - a_st_wr_state = 2; /* F2 */ - hc->hw.a_st_ctrl0[pt] = 0; - } - if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg)) - hc->hw.a_st_ctrl0[pt] |= V_TX_LI; - if (hc->ctype == HFC_TYPE_XHFC) { - hc->hw.a_st_ctrl0[pt] |= 0x40 /* V_ST_PU_CTRL */; - HFC_outb(hc, 0x35 /* A_ST_CTRL3 */, - 0x7c << 1 /* V_ST_PULSE */); - } - /* line setup */ - HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[pt]); - /* disable E-channel */ - if ((dch->dev.D.protocol == ISDN_P_NT_S0) || - test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg)) - HFC_outb(hc, A_ST_CTRL1, V_E_IGNO); - else - HFC_outb(hc, A_ST_CTRL1, 0); - /* enable B-channel receive */ - HFC_outb(hc, A_ST_CTRL2, V_B1_RX_EN | V_B2_RX_EN); - /* state machine setup */ - HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA); - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state); - hc->hw.r_sci_msk |= 1 << pt; - /* state machine interrupts */ - HFC_outb(hc, R_SCI_MSK, hc->hw.r_sci_msk); - /* unset sync on port */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - hc->syncronized &= - ~(1 << hc->chan[dch->slot].port); - plxsd_checksync(hc, 0); - } - } - if (debug & DEBUG_HFCMULTI_INIT) - printk("%s: done\n", __func__); -} - - -static int -open_dchannel(struct hfc_multi *hc, struct dchannel *dch, - struct channel_req *rq) -{ - int err = 0; - u_long flags; - - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, - dch->dev.id, __builtin_return_address(0)); - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - if ((dch->dev.D.protocol != ISDN_P_NONE) && - (dch->dev.D.protocol != rq->protocol)) { - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG "%s: change protocol %x to %x\n", - __func__, dch->dev.D.protocol, rq->protocol); - } - if ((dch->dev.D.protocol == ISDN_P_TE_S0) && - (rq->protocol != ISDN_P_TE_S0)) - l1_event(dch->l1, CLOSE_CHANNEL); - if (dch->dev.D.protocol != rq->protocol) { - if (rq->protocol == ISDN_P_TE_S0) { - err = create_l1(dch, hfcm_l1callback); - if (err) - return err; - } - dch->dev.D.protocol = rq->protocol; - spin_lock_irqsave(&hc->lock, flags); - hfcmulti_initmode(dch); - spin_unlock_irqrestore(&hc->lock, flags); - } - if (test_bit(FLG_ACTIVE, &dch->Flags)) - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - rq->ch = &dch->dev.D; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -static int -open_bchannel(struct hfc_multi *hc, struct dchannel *dch, - struct channel_req *rq) -{ - struct bchannel *bch; - int ch; - - if (!test_channelmap(rq->adr.channel, dch->dev.channelmap)) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - if (hc->ctype == HFC_TYPE_E1) - ch = rq->adr.channel; - else - ch = (rq->adr.channel - 1) + (dch->slot - 2); - bch = hc->chan[ch].bch; - if (!bch) { - printk(KERN_ERR "%s:internal error ch %d has no bch\n", - __func__, ch); - return -EINVAL; - } - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - hc->chan[ch].rx_off = 0; - rq->ch = &bch->ch; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -/* - * device control function - */ -static int -channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) -{ - struct hfc_multi *hc = dch->hw; - int ret = 0; - int wd_mode, wd_cnt; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_HFC_WD_INIT: /* init the watchdog */ - wd_cnt = cq->p1 & 0xf; - wd_mode = !!(cq->p1 >> 4); - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: MISDN_CTRL_HFC_WD_INIT mode %s" - ", counter 0x%x\n", __func__, - wd_mode ? "AUTO" : "MANUAL", wd_cnt); - /* set the watchdog timer */ - HFC_outb(hc, R_TI_WD, poll_timer | (wd_cnt << 4)); - hc->hw.r_bert_wd_md = (wd_mode ? V_AUTO_WD_RES : 0); - if (hc->ctype == HFC_TYPE_XHFC) - hc->hw.r_bert_wd_md |= 0x40 /* V_WD_EN */; - /* init the watchdog register and reset the counter */ - HFC_outb(hc, R_BERT_WD_MD, hc->hw.r_bert_wd_md | V_WD_RES); - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - /* enable the watchdog output for Speech-Design */ - HFC_outb(hc, R_GPIO_SEL, V_GPIO_SEL7); - HFC_outb(hc, R_GPIO_EN1, V_GPIO_EN15); - HFC_outb(hc, R_GPIO_OUT1, 0); - HFC_outb(hc, R_GPIO_OUT1, V_GPIO_OUT15); - } - break; - case MISDN_CTRL_HFC_WD_RESET: /* reset the watchdog counter */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: MISDN_CTRL_HFC_WD_RESET\n", - __func__); - HFC_outb(hc, R_BERT_WD_MD, hc->hw.r_bert_wd_md | V_WD_RES); - break; - case MISDN_CTRL_L1_TIMER3: - ret = l1_event(dch->l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); - break; - default: - printk(KERN_WARNING "%s: unknown Op %x\n", - __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct hfc_multi *hc = dch->hw; - struct channel_req *rq; - int err = 0; - u_long flags; - - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", - __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - switch (rq->protocol) { - case ISDN_P_TE_S0: - case ISDN_P_NT_S0: - if (hc->ctype == HFC_TYPE_E1) { - err = -EINVAL; - break; - } - err = open_dchannel(hc, dch, rq); /* locked there */ - break; - case ISDN_P_TE_E1: - case ISDN_P_NT_E1: - if (hc->ctype != HFC_TYPE_E1) { - err = -EINVAL; - break; - } - err = open_dchannel(hc, dch, rq); /* locked there */ - break; - default: - spin_lock_irqsave(&hc->lock, flags); - err = open_bchannel(hc, dch, rq); - spin_unlock_irqrestore(&hc->lock, flags); - } - break; - case CLOSE_CHANNEL: - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) close from %p\n", - __func__, dch->dev.id, - __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - spin_lock_irqsave(&hc->lock, flags); - err = channel_dctrl(dch, arg); - spin_unlock_irqrestore(&hc->lock, flags); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: unknown command %x\n", - __func__, cmd); - err = -EINVAL; - } - return err; -} - -static int -clockctl(void *priv, int enable) -{ - struct hfc_multi *hc = priv; - - hc->iclock_on = enable; - return 0; -} - -/* - * initialize the card - */ - -/* - * start timer irq, wait some time and check if we have interrupts. - * if not, reset chip and try again. - */ -static int -init_card(struct hfc_multi *hc) -{ - int err = -EIO; - u_long flags; - void __iomem *plx_acc; - u_long plx_flags; - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: entered\n", __func__); - - spin_lock_irqsave(&hc->lock, flags); - /* set interrupts but leave global interrupt disabled */ - hc->hw.r_irq_ctrl = V_FIFO_IRQ; - disable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - - if (request_irq(hc->irq, hfcmulti_interrupt, IRQF_SHARED, - "HFC-multi", hc)) { - printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", - hc->irq); - hc->irq = 0; - return -EIO; - } - - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc = hc->plx_membase + PLX_INTCSR; - writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE), - plx_acc); /* enable PCI & LINT1 irq */ - spin_unlock_irqrestore(&plx_lock, plx_flags); - } - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: IRQ %d count %d\n", - __func__, hc->irq, hc->irqcnt); - err = init_chip(hc); - if (err) - goto error; - /* - * Finally enable IRQ output - * this is only allowed, if an IRQ routine is already - * established for this HFC, so don't do that earlier - */ - spin_lock_irqsave(&hc->lock, flags); - enable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - /* printk(KERN_DEBUG "no master irq set!!!\n"); */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ - /* turn IRQ off until chip is completely initialized */ - spin_lock_irqsave(&hc->lock, flags); - disable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: IRQ %d count %d\n", - __func__, hc->irq, hc->irqcnt); - if (hc->irqcnt) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: done\n", __func__); - - return 0; - } - if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { - printk(KERN_INFO "ignoring missing interrupts\n"); - return 0; - } - - printk(KERN_ERR "HFC PCI: IRQ(%d) getting no interrupts during init.\n", - hc->irq); - - err = -EIO; - -error: - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc = hc->plx_membase + PLX_INTCSR; - writew(0x00, plx_acc); /*disable IRQs*/ - spin_unlock_irqrestore(&plx_lock, plx_flags); - } - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: free irq %d\n", __func__, hc->irq); - if (hc->irq) { - free_irq(hc->irq, hc); - hc->irq = 0; - } - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: done (err=%d)\n", __func__, err); - return err; -} - -/* - * find pci device and set it up - */ - -static int -setup_pci(struct hfc_multi *hc, struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct hm_map *m = (struct hm_map *)ent->driver_data; - - printk(KERN_INFO - "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", - m->vendor_name, m->card_name, m->clock2 ? "double" : "normal"); - - hc->pci_dev = pdev; - if (m->clock2) - test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); - - if (ent->vendor == PCI_VENDOR_ID_DIGIUM && - ent->device == PCI_DEVICE_ID_DIGIUM_HFC4S) { - test_and_set_bit(HFC_CHIP_B410P, &hc->chip); - test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); - test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); - hc->slots = 32; - } - - if (hc->pci_dev->irq <= 0) { - printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n"); - return -EIO; - } - if (pci_enable_device(hc->pci_dev)) { - printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n"); - return -EIO; - } - hc->leds = m->leds; - hc->ledstate = 0xAFFEAFFE; - hc->opticalsupport = m->opticalsupport; - - hc->pci_iobase = 0; - hc->pci_membase = NULL; - hc->plx_membase = NULL; - - /* set memory access methods */ - if (m->io_mode) /* use mode from card config */ - hc->io_mode = m->io_mode; - switch (hc->io_mode) { - case HFC_IO_MODE_PLXSD: - test_and_set_bit(HFC_CHIP_PLXSD, &hc->chip); - hc->slots = 128; /* required */ - hc->HFC_outb = HFC_outb_pcimem; - hc->HFC_inb = HFC_inb_pcimem; - hc->HFC_inw = HFC_inw_pcimem; - hc->HFC_wait = HFC_wait_pcimem; - hc->read_fifo = read_fifo_pcimem; - hc->write_fifo = write_fifo_pcimem; - hc->plx_origmembase = hc->pci_dev->resource[0].start; - /* MEMBASE 1 is PLX PCI Bridge */ - - if (!hc->plx_origmembase) { - printk(KERN_WARNING - "HFC-multi: No IO-Memory for PCI PLX bridge found\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - hc->plx_membase = ioremap(hc->plx_origmembase, 0x80); - if (!hc->plx_membase) { - printk(KERN_WARNING - "HFC-multi: failed to remap plx address space. " - "(internal error)\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - printk(KERN_INFO - "HFC-multi: plx_membase:%#lx plx_origmembase:%#lx\n", - (u_long)hc->plx_membase, hc->plx_origmembase); - - hc->pci_origmembase = hc->pci_dev->resource[2].start; - /* MEMBASE 1 is PLX PCI Bridge */ - if (!hc->pci_origmembase) { - printk(KERN_WARNING - "HFC-multi: No IO-Memory for PCI card found\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - hc->pci_membase = ioremap(hc->pci_origmembase, 0x400); - if (!hc->pci_membase) { - printk(KERN_WARNING "HFC-multi: failed to remap io " - "address space. (internal error)\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - printk(KERN_INFO - "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d HZ %d " - "leds-type %d\n", - hc->id, (u_long)hc->pci_membase, hc->pci_origmembase, - hc->pci_dev->irq, HZ, hc->leds); - pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); - break; - case HFC_IO_MODE_PCIMEM: - hc->HFC_outb = HFC_outb_pcimem; - hc->HFC_inb = HFC_inb_pcimem; - hc->HFC_inw = HFC_inw_pcimem; - hc->HFC_wait = HFC_wait_pcimem; - hc->read_fifo = read_fifo_pcimem; - hc->write_fifo = write_fifo_pcimem; - hc->pci_origmembase = hc->pci_dev->resource[1].start; - if (!hc->pci_origmembase) { - printk(KERN_WARNING - "HFC-multi: No IO-Memory for PCI card found\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - hc->pci_membase = ioremap(hc->pci_origmembase, 256); - if (!hc->pci_membase) { - printk(KERN_WARNING - "HFC-multi: failed to remap io address space. " - "(internal error)\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - printk(KERN_INFO "card %d: defined at MEMBASE %#lx (%#lx) IRQ " - "%d HZ %d leds-type %d\n", hc->id, (u_long)hc->pci_membase, - hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds); - pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); - break; - case HFC_IO_MODE_REGIO: - hc->HFC_outb = HFC_outb_regio; - hc->HFC_inb = HFC_inb_regio; - hc->HFC_inw = HFC_inw_regio; - hc->HFC_wait = HFC_wait_regio; - hc->read_fifo = read_fifo_regio; - hc->write_fifo = write_fifo_regio; - hc->pci_iobase = (u_int) hc->pci_dev->resource[0].start; - if (!hc->pci_iobase) { - printk(KERN_WARNING - "HFC-multi: No IO for PCI card found\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - if (!request_region(hc->pci_iobase, 8, "hfcmulti")) { - printk(KERN_WARNING "HFC-multi: failed to request " - "address space at 0x%08lx (internal error)\n", - hc->pci_iobase); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - printk(KERN_INFO - "%s %s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n", - m->vendor_name, m->card_name, (u_int) hc->pci_iobase, - hc->pci_dev->irq, HZ, hc->leds); - pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO); - break; - default: - printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - pci_set_drvdata(hc->pci_dev, hc); - - /* At this point the needed PCI config is done */ - /* fifos are still not enabled */ - return 0; -} - - -/* - * remove port - */ - -static void -release_port(struct hfc_multi *hc, struct dchannel *dch) -{ - int pt, ci, i = 0; - u_long flags; - struct bchannel *pb; - - ci = dch->slot; - pt = hc->chan[ci].port; - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: entered for port %d\n", - __func__, pt + 1); - - if (pt >= hc->ports) { - printk(KERN_WARNING "%s: ERROR port out of range (%d).\n", - __func__, pt + 1); - return; - } - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: releasing port=%d\n", - __func__, pt + 1); - - if (dch->dev.D.protocol == ISDN_P_TE_S0) - l1_event(dch->l1, CLOSE_CHANNEL); - - hc->chan[ci].dch = NULL; - - if (hc->created[pt]) { - hc->created[pt] = 0; - mISDN_unregister_device(&dch->dev); - } - - spin_lock_irqsave(&hc->lock, flags); - - if (dch->timer.function) { - timer_delete(&dch->timer); - dch->timer.function = NULL; - } - - if (hc->ctype == HFC_TYPE_E1) { /* E1 */ - /* remove sync */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - hc->syncronized = 0; - plxsd_checksync(hc, 1); - } - /* free channels */ - for (i = 0; i <= 31; i++) { - if (!((1 << i) & hc->bmask[pt])) /* skip unused chan */ - continue; - if (hc->chan[i].bch) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: free port %d channel %d\n", - __func__, hc->chan[i].port + 1, i); - pb = hc->chan[i].bch; - hc->chan[i].bch = NULL; - spin_unlock_irqrestore(&hc->lock, flags); - mISDN_freebchannel(pb); - kfree(pb); - kfree(hc->chan[i].coeff); - spin_lock_irqsave(&hc->lock, flags); - } - } - } else { - /* remove sync */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - hc->syncronized &= - ~(1 << hc->chan[ci].port); - plxsd_checksync(hc, 1); - } - /* free channels */ - if (hc->chan[ci - 2].bch) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: free port %d channel %d\n", - __func__, hc->chan[ci - 2].port + 1, - ci - 2); - pb = hc->chan[ci - 2].bch; - hc->chan[ci - 2].bch = NULL; - spin_unlock_irqrestore(&hc->lock, flags); - mISDN_freebchannel(pb); - kfree(pb); - kfree(hc->chan[ci - 2].coeff); - spin_lock_irqsave(&hc->lock, flags); - } - if (hc->chan[ci - 1].bch) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: free port %d channel %d\n", - __func__, hc->chan[ci - 1].port + 1, - ci - 1); - pb = hc->chan[ci - 1].bch; - hc->chan[ci - 1].bch = NULL; - spin_unlock_irqrestore(&hc->lock, flags); - mISDN_freebchannel(pb); - kfree(pb); - kfree(hc->chan[ci - 1].coeff); - spin_lock_irqsave(&hc->lock, flags); - } - } - - spin_unlock_irqrestore(&hc->lock, flags); - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: free port %d channel D(%d)\n", __func__, - pt+1, ci); - mISDN_freedchannel(dch); - kfree(dch); - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: done!\n", __func__); -} - -static void -release_card(struct hfc_multi *hc) -{ - u_long flags; - int ch; - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: release card (%d) entered\n", - __func__, hc->id); - - /* unregister clock source */ - if (hc->iclock) - mISDN_unregister_clock(hc->iclock); - - /* disable and free irq */ - spin_lock_irqsave(&hc->lock, flags); - disable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - udelay(1000); - if (hc->irq) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: free irq %d (hc=%p)\n", - __func__, hc->irq, hc); - free_irq(hc->irq, hc); - hc->irq = 0; - - } - - /* disable D-channels & B-channels */ - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: disable all channels (d and b)\n", - __func__); - for (ch = 0; ch <= 31; ch++) { - if (hc->chan[ch].dch) - release_port(hc, hc->chan[ch].dch); - } - - /* dimm leds */ - if (hc->leds) - hfcmulti_leds(hc); - - /* release hardware */ - release_io_hfcmulti(hc); - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: remove instance from list\n", - __func__); - list_del(&hc->list); - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: delete instance\n", __func__); - if (hc == syncmaster) - syncmaster = NULL; - kfree(hc); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: card successfully removed\n", - __func__); -} - -static void -init_e1_port_hw(struct hfc_multi *hc, struct hm_map *m) -{ - /* set optical line type */ - if (port[Port_cnt] & 0x001) { - if (!m->opticalsupport) { - printk(KERN_INFO - "This board has no optical " - "support\n"); - } else { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PORT set optical " - "interface: card(%d) " - "port(%d)\n", - __func__, - HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_OPTICAL, - &hc->chan[hc->dnum[0]].cfg); - } - } - /* set LOS report */ - if (port[Port_cnt] & 0x004) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT set " - "LOS report: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_REPORT_LOS, - &hc->chan[hc->dnum[0]].cfg); - } - /* set AIS report */ - if (port[Port_cnt] & 0x008) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT set " - "AIS report: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_REPORT_AIS, - &hc->chan[hc->dnum[0]].cfg); - } - /* set SLIP report */ - if (port[Port_cnt] & 0x010) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PORT set SLIP report: " - "card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_REPORT_SLIP, - &hc->chan[hc->dnum[0]].cfg); - } - /* set RDI report */ - if (port[Port_cnt] & 0x020) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PORT set RDI report: " - "card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_REPORT_RDI, - &hc->chan[hc->dnum[0]].cfg); - } - /* set CRC-4 Mode */ - if (!(port[Port_cnt] & 0x100)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT turn on CRC4 report:" - " card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_CRC4, - &hc->chan[hc->dnum[0]].cfg); - } else { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT turn off CRC4" - " report: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - } - /* set forced clock */ - if (port[Port_cnt] & 0x0200) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT force getting clock from " - "E1: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip); - } else - if (port[Port_cnt] & 0x0400) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT force putting clock to " - "E1: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip); - } - /* set JATT PLL */ - if (port[Port_cnt] & 0x0800) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT disable JATT PLL on " - "E1: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip); - } - /* set elastic jitter buffer */ - if (port[Port_cnt] & 0x3000) { - hc->chan[hc->dnum[0]].jitter = (port[Port_cnt]>>12) & 0x3; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PORT set elastic " - "buffer to %d: card(%d) port(%d)\n", - __func__, hc->chan[hc->dnum[0]].jitter, - HFC_cnt + 1, 1); - } else - hc->chan[hc->dnum[0]].jitter = 2; /* default */ -} - -static int -init_e1_port(struct hfc_multi *hc, struct hm_map *m, int pt) -{ - struct dchannel *dch; - struct bchannel *bch; - int ch, ret = 0; - char name[MISDN_MAX_IDLEN]; - int bcount = 0; - - dch = kzalloc_obj(struct dchannel); - if (!dch) - return -ENOMEM; - dch->debug = debug; - mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); - dch->hw = hc; - dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1); - dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - dch->dev.D.send = handle_dmsg; - dch->dev.D.ctrl = hfcm_dctrl; - dch->slot = hc->dnum[pt]; - hc->chan[hc->dnum[pt]].dch = dch; - hc->chan[hc->dnum[pt]].port = pt; - hc->chan[hc->dnum[pt]].nt_timer = -1; - for (ch = 1; ch <= 31; ch++) { - if (!((1 << ch) & hc->bmask[pt])) /* skip unused channel */ - continue; - bch = kzalloc_obj(struct bchannel); - if (!bch) { - printk(KERN_ERR "%s: no memory for bchannel\n", - __func__); - ret = -ENOMEM; - goto free_chan; - } - hc->chan[ch].coeff = kzalloc(512, GFP_KERNEL); - if (!hc->chan[ch].coeff) { - printk(KERN_ERR "%s: no memory for coeffs\n", - __func__); - ret = -ENOMEM; - kfree(bch); - goto free_chan; - } - bch->nr = ch; - bch->slot = ch; - bch->debug = debug; - mISDN_initbchannel(bch, MAX_DATA_MEM, poll >> 1); - bch->hw = hc; - bch->ch.send = handle_bmsg; - bch->ch.ctrl = hfcm_bctrl; - bch->ch.nr = ch; - list_add(&bch->ch.list, &dch->dev.bchannels); - hc->chan[ch].bch = bch; - hc->chan[ch].port = pt; - set_channelmap(bch->nr, dch->dev.channelmap); - bcount++; - } - dch->dev.nrbchan = bcount; - if (pt == 0) - init_e1_port_hw(hc, m); - if (hc->ports > 1) - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d-%d", - HFC_cnt + 1, pt+1); - else - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); - ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); - if (ret) - goto free_chan; - hc->created[pt] = 1; - return ret; -free_chan: - release_port(hc, dch); - return ret; -} - -static int -init_multi_port(struct hfc_multi *hc, int pt) -{ - struct dchannel *dch; - struct bchannel *bch; - int ch, i, ret = 0; - char name[MISDN_MAX_IDLEN]; - - dch = kzalloc_obj(struct dchannel); - if (!dch) - return -ENOMEM; - dch->debug = debug; - mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); - dch->hw = hc; - dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); - dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - dch->dev.D.send = handle_dmsg; - dch->dev.D.ctrl = hfcm_dctrl; - dch->dev.nrbchan = 2; - i = pt << 2; - dch->slot = i + 2; - hc->chan[i + 2].dch = dch; - hc->chan[i + 2].port = pt; - hc->chan[i + 2].nt_timer = -1; - for (ch = 0; ch < dch->dev.nrbchan; ch++) { - bch = kzalloc_obj(struct bchannel); - if (!bch) { - printk(KERN_ERR "%s: no memory for bchannel\n", - __func__); - ret = -ENOMEM; - goto free_chan; - } - hc->chan[i + ch].coeff = kzalloc(512, GFP_KERNEL); - if (!hc->chan[i + ch].coeff) { - printk(KERN_ERR "%s: no memory for coeffs\n", - __func__); - ret = -ENOMEM; - kfree(bch); - goto free_chan; - } - bch->nr = ch + 1; - bch->slot = i + ch; - bch->debug = debug; - mISDN_initbchannel(bch, MAX_DATA_MEM, poll >> 1); - bch->hw = hc; - bch->ch.send = handle_bmsg; - bch->ch.ctrl = hfcm_bctrl; - bch->ch.nr = ch + 1; - list_add(&bch->ch.list, &dch->dev.bchannels); - hc->chan[i + ch].bch = bch; - hc->chan[i + ch].port = pt; - set_channelmap(bch->nr, dch->dev.channelmap); - } - /* set master clock */ - if (port[Port_cnt] & 0x001) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PROTOCOL set master clock: " - "card(%d) port(%d)\n", - __func__, HFC_cnt + 1, pt + 1); - if (dch->dev.D.protocol != ISDN_P_TE_S0) { - printk(KERN_ERR "Error: Master clock " - "for port(%d) of card(%d) is only" - " possible with TE-mode\n", - pt + 1, HFC_cnt + 1); - ret = -EINVAL; - goto free_chan; - } - if (hc->masterclk >= 0) { - printk(KERN_ERR "Error: Master clock " - "for port(%d) of card(%d) already " - "defined for port(%d)\n", - pt + 1, HFC_cnt + 1, hc->masterclk + 1); - ret = -EINVAL; - goto free_chan; - } - hc->masterclk = pt; - } - /* set transmitter line to non capacitive */ - if (port[Port_cnt] & 0x002) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PROTOCOL set non capacitive " - "transmitter: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, pt + 1); - test_and_set_bit(HFC_CFG_NONCAP_TX, - &hc->chan[i + 2].cfg); - } - /* disable E-channel */ - if (port[Port_cnt] & 0x004) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PROTOCOL disable E-channel: " - "card(%d) port(%d)\n", - __func__, HFC_cnt + 1, pt + 1); - test_and_set_bit(HFC_CFG_DIS_ECHANNEL, - &hc->chan[i + 2].cfg); - } - if (hc->ctype == HFC_TYPE_XHFC) { - snprintf(name, MISDN_MAX_IDLEN - 1, "xhfc.%d-%d", - HFC_cnt + 1, pt + 1); - ret = mISDN_register_device(&dch->dev, NULL, name); - } else { - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d-%d", - hc->ctype, HFC_cnt + 1, pt + 1); - ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); - } - if (ret) - goto free_chan; - hc->created[pt] = 1; - return ret; -free_chan: - release_port(hc, dch); - return ret; -} - -static int -hfcmulti_init(struct hm_map *m, struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int ret_err = 0; - int pt; - struct hfc_multi *hc; - u_long flags; - u_char dips = 0, pmj = 0; /* dip settings, port mode Jumpers */ - int i, ch; - u_int maskcheck; - - if (HFC_cnt >= MAX_CARDS) { - printk(KERN_ERR "too many cards (max=%d).\n", - MAX_CARDS); - return -EINVAL; - } - if ((type[HFC_cnt] & 0xff) && (type[HFC_cnt] & 0xff) != m->type) { - printk(KERN_WARNING "HFC-MULTI: Card '%s:%s' type %d found but " - "type[%d] %d was supplied as module parameter\n", - m->vendor_name, m->card_name, m->type, HFC_cnt, - type[HFC_cnt] & 0xff); - printk(KERN_WARNING "HFC-MULTI: Load module without parameters " - "first, to see cards and their types."); - return -EINVAL; - } - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: Registering %s:%s chip type %d (0x%x)\n", - __func__, m->vendor_name, m->card_name, m->type, - type[HFC_cnt]); - - /* allocate card+fifo structure */ - hc = kzalloc_obj(struct hfc_multi); - if (!hc) { - printk(KERN_ERR "No kmem for HFC-Multi card\n"); - return -ENOMEM; - } - spin_lock_init(&hc->lock); - hc->mtyp = m; - hc->ctype = m->type; - hc->ports = m->ports; - hc->id = HFC_cnt; - hc->pcm = pcm[HFC_cnt]; - hc->io_mode = iomode[HFC_cnt]; - if (hc->ctype == HFC_TYPE_E1 && dmask[E1_cnt]) { - /* fragment card */ - pt = 0; - maskcheck = 0; - for (ch = 0; ch <= 31; ch++) { - if (!((1 << ch) & dmask[E1_cnt])) - continue; - hc->dnum[pt] = ch; - hc->bmask[pt] = bmask[bmask_cnt++]; - if ((maskcheck & hc->bmask[pt]) - || (dmask[E1_cnt] & hc->bmask[pt])) { - printk(KERN_INFO - "HFC-E1 #%d has overlapping B-channels on fragment #%d\n", - E1_cnt + 1, pt); - kfree(hc); - return -EINVAL; - } - maskcheck |= hc->bmask[pt]; - printk(KERN_INFO - "HFC-E1 #%d uses D-channel on slot %d and a B-channel map of 0x%08x\n", - E1_cnt + 1, ch, hc->bmask[pt]); - pt++; - } - hc->ports = pt; - } - if (hc->ctype == HFC_TYPE_E1 && !dmask[E1_cnt]) { - /* default card layout */ - hc->dnum[0] = 16; - hc->bmask[0] = 0xfffefffe; - hc->ports = 1; - } - - /* set chip specific features */ - hc->masterclk = -1; - if (type[HFC_cnt] & 0x100) { - test_and_set_bit(HFC_CHIP_ULAW, &hc->chip); - hc->silence = 0xff; /* ulaw silence */ - } else - hc->silence = 0x2a; /* alaw silence */ - if ((poll >> 1) > sizeof(hc->silence_data)) { - printk(KERN_ERR "HFCMULTI error: silence_data too small, " - "please fix\n"); - kfree(hc); - return -EINVAL; - } - for (i = 0; i < (poll >> 1); i++) - hc->silence_data[i] = hc->silence; - - if (hc->ctype != HFC_TYPE_XHFC) { - if (!(type[HFC_cnt] & 0x200)) - test_and_set_bit(HFC_CHIP_DTMF, &hc->chip); - test_and_set_bit(HFC_CHIP_CONF, &hc->chip); - } - - if (type[HFC_cnt] & 0x800) - test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); - if (type[HFC_cnt] & 0x1000) { - test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); - test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); - } - if (type[HFC_cnt] & 0x4000) - test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip); - if (type[HFC_cnt] & 0x8000) - test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip); - hc->slots = 32; - if (type[HFC_cnt] & 0x10000) - hc->slots = 64; - if (type[HFC_cnt] & 0x20000) - hc->slots = 128; - if (type[HFC_cnt] & 0x80000) { - test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip); - hc->wdcount = 0; - hc->wdbyte = V_GPIO_OUT2; - printk(KERN_NOTICE "Watchdog enabled\n"); - } - - if (pdev && ent) - /* setup pci, hc->slots may change due to PLXSD */ - ret_err = setup_pci(hc, pdev, ent); - else -#ifdef CONFIG_MISDN_HFCMULTI_8xx - ret_err = setup_embedded(hc, m); -#else - { - printk(KERN_WARNING "Embedded IO Mode not selected\n"); - ret_err = -EIO; - } -#endif - if (ret_err) { - if (hc == syncmaster) - syncmaster = NULL; - kfree(hc); - return ret_err; - } - - hc->HFC_outb_nodebug = hc->HFC_outb; - hc->HFC_inb_nodebug = hc->HFC_inb; - hc->HFC_inw_nodebug = hc->HFC_inw; - hc->HFC_wait_nodebug = hc->HFC_wait; -#ifdef HFC_REGISTER_DEBUG - hc->HFC_outb = HFC_outb_debug; - hc->HFC_inb = HFC_inb_debug; - hc->HFC_inw = HFC_inw_debug; - hc->HFC_wait = HFC_wait_debug; -#endif - /* create channels */ - for (pt = 0; pt < hc->ports; pt++) { - if (Port_cnt >= MAX_PORTS) { - printk(KERN_ERR "too many ports (max=%d).\n", - MAX_PORTS); - ret_err = -EINVAL; - goto free_card; - } - if (hc->ctype == HFC_TYPE_E1) - ret_err = init_e1_port(hc, m, pt); - else - ret_err = init_multi_port(hc, pt); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: Registering D-channel, card(%d) port(%d) " - "result %d\n", - __func__, HFC_cnt + 1, pt + 1, ret_err); - - if (ret_err) { - while (pt) { /* release already registered ports */ - pt--; - if (hc->ctype == HFC_TYPE_E1) - release_port(hc, - hc->chan[hc->dnum[pt]].dch); - else - release_port(hc, - hc->chan[(pt << 2) + 2].dch); - } - goto free_card; - } - if (hc->ctype != HFC_TYPE_E1) - Port_cnt++; /* for each S0 port */ - } - if (hc->ctype == HFC_TYPE_E1) { - Port_cnt++; /* for each E1 port */ - E1_cnt++; - } - - /* disp switches */ - switch (m->dip_type) { - case DIP_4S: - /* - * Get DIP setting for beroNet 1S/2S/4S cards - * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) + - * GPI 19/23 (R_GPI_IN2)) - */ - dips = ((~HFC_inb(hc, R_GPIO_IN1) & 0xE0) >> 5) | - ((~HFC_inb(hc, R_GPI_IN2) & 0x80) >> 3) | - (~HFC_inb(hc, R_GPI_IN2) & 0x08); - - /* Port mode (TE/NT) jumpers */ - pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4) & 0xf); - - if (test_bit(HFC_CHIP_B410P, &hc->chip)) - pmj = ~pmj & 0xf; - - printk(KERN_INFO "%s: %s DIPs(0x%x) jumpers(0x%x)\n", - m->vendor_name, m->card_name, dips, pmj); - break; - case DIP_8S: - /* - * Get DIP Setting for beroNet 8S0+ cards - * Enable PCI auxbridge function - */ - HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); - /* prepare access to auxport */ - outw(0x4000, hc->pci_iobase + 4); - /* - * some dummy reads are required to - * read valid DIP switch data - */ - dips = inb(hc->pci_iobase); - dips = inb(hc->pci_iobase); - dips = inb(hc->pci_iobase); - dips = ~inb(hc->pci_iobase) & 0x3F; - outw(0x0, hc->pci_iobase + 4); - /* disable PCI auxbridge function */ - HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); - printk(KERN_INFO "%s: %s DIPs(0x%x)\n", - m->vendor_name, m->card_name, dips); - break; - case DIP_E1: - /* - * get DIP Setting for beroNet E1 cards - * DIP Setting: collect GPI 4/5/6/7 (R_GPI_IN0) - */ - dips = (~HFC_inb(hc, R_GPI_IN0) & 0xF0) >> 4; - printk(KERN_INFO "%s: %s DIPs(0x%x)\n", - m->vendor_name, m->card_name, dips); - break; - } - - /* add to list */ - spin_lock_irqsave(&HFClock, flags); - list_add_tail(&hc->list, &HFClist); - spin_unlock_irqrestore(&HFClock, flags); - - /* use as clock source */ - if (clock == HFC_cnt + 1) - hc->iclock = mISDN_register_clock("HFCMulti", 0, clockctl, hc); - - /* initialize hardware */ - hc->irq = (m->irq) ? : hc->pci_dev->irq; - ret_err = init_card(hc); - if (ret_err) { - printk(KERN_ERR "init card returns %d\n", ret_err); - release_card(hc); - return ret_err; - } - - /* start IRQ and return */ - spin_lock_irqsave(&hc->lock, flags); - enable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - return 0; - -free_card: - release_io_hfcmulti(hc); - if (hc == syncmaster) - syncmaster = NULL; - kfree(hc); - return ret_err; -} - -static void hfc_remove_pci(struct pci_dev *pdev) -{ - struct hfc_multi *card = pci_get_drvdata(pdev); - u_long flags; - - if (debug) - printk(KERN_INFO "removing hfc_multi card vendor:%x " - "device:%x subvendor:%x subdevice:%x\n", - pdev->vendor, pdev->device, - pdev->subsystem_vendor, pdev->subsystem_device); - - if (card) { - spin_lock_irqsave(&HFClock, flags); - release_card(card); - spin_unlock_irqrestore(&HFClock, flags); - } else { - if (debug) - printk(KERN_DEBUG "%s: drvdata already removed\n", - __func__); - } -} - -#define VENDOR_CCD "Cologne Chip AG" -#define VENDOR_BN "beroNet GmbH" -#define VENDOR_DIG "Digium Inc." -#define VENDOR_JH "Junghanns.NET GmbH" -#define VENDOR_PRIM "PrimuX" - -static const struct hm_map hfcm_map[] = { - /*0*/ {VENDOR_BN, "HFC-1S Card (mini PCI)", 4, 1, 1, 3, 0, DIP_4S, 0, 0}, - /*1*/ {VENDOR_BN, "HFC-2S Card", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, - /*2*/ {VENDOR_BN, "HFC-2S Card (mini PCI)", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, - /*3*/ {VENDOR_BN, "HFC-4S Card", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, - /*4*/ {VENDOR_BN, "HFC-4S Card (mini PCI)", 4, 4, 1, 2, 0, 0, 0, 0}, - /*5*/ {VENDOR_CCD, "HFC-4S Eval (old)", 4, 4, 0, 0, 0, 0, 0, 0}, - /*6*/ {VENDOR_CCD, "HFC-4S IOB4ST", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, - /*7*/ {VENDOR_CCD, "HFC-4S", 4, 4, 1, 2, 0, 0, 0, 0}, - /*8*/ {VENDOR_DIG, "HFC-4S Card", 4, 4, 0, 2, 0, 0, HFC_IO_MODE_REGIO, 0}, - /*9*/ {VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 4, 4, 1, 2, 0, 0, 0, 0}, - /*10*/ {VENDOR_JH, "HFC-4S (junghanns 2.0)", 4, 4, 1, 2, 0, 0, 0, 0}, - /*11*/ {VENDOR_PRIM, "HFC-2S Primux Card", 4, 2, 0, 0, 0, 0, 0, 0}, - - /*12*/ {VENDOR_BN, "HFC-8S Card", 8, 8, 1, 0, 0, 0, 0, 0}, - /*13*/ {VENDOR_BN, "HFC-8S Card (+)", 8, 8, 1, 8, 0, DIP_8S, - HFC_IO_MODE_REGIO, 0}, - /*14*/ {VENDOR_CCD, "HFC-8S Eval (old)", 8, 8, 0, 0, 0, 0, 0, 0}, - /*15*/ {VENDOR_CCD, "HFC-8S IOB4ST Recording", 8, 8, 1, 0, 0, 0, 0, 0}, - - /*16*/ {VENDOR_CCD, "HFC-8S IOB8ST", 8, 8, 1, 0, 0, 0, 0, 0}, - /*17*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0, 0}, - /*18*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0, 0}, - - /*19*/ {VENDOR_BN, "HFC-E1 Card", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, - /*20*/ {VENDOR_BN, "HFC-E1 Card (mini PCI)", 1, 1, 0, 1, 0, 0, 0, 0}, - /*21*/ {VENDOR_BN, "HFC-E1+ Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, - /*22*/ {VENDOR_BN, "HFC-E1 Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, - - /*23*/ {VENDOR_CCD, "HFC-E1 Eval (old)", 1, 1, 0, 0, 0, 0, 0, 0}, - /*24*/ {VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0, 0, 0, 0}, - /*25*/ {VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0, 0, 0, 0}, - - /*26*/ {VENDOR_CCD, "HFC-4S Speech Design", 4, 4, 0, 0, 0, 0, - HFC_IO_MODE_PLXSD, 0}, - /*27*/ {VENDOR_CCD, "HFC-E1 Speech Design", 1, 1, 0, 0, 0, 0, - HFC_IO_MODE_PLXSD, 0}, - /*28*/ {VENDOR_CCD, "HFC-4S OpenVox", 4, 4, 1, 0, 0, 0, 0, 0}, - /*29*/ {VENDOR_CCD, "HFC-2S OpenVox", 4, 2, 1, 0, 0, 0, 0, 0}, - /*30*/ {VENDOR_CCD, "HFC-8S OpenVox", 8, 8, 1, 0, 0, 0, 0, 0}, - /*31*/ {VENDOR_CCD, "XHFC-4S Speech Design", 5, 4, 0, 0, 0, 0, - HFC_IO_MODE_EMBSD, XHFC_IRQ}, - /*32*/ {VENDOR_JH, "HFC-8S (junghanns)", 8, 8, 1, 0, 0, 0, 0, 0}, - /*33*/ {VENDOR_BN, "HFC-2S Beronet Card PCIe", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, - /*34*/ {VENDOR_BN, "HFC-4S Beronet Card PCIe", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, -}; - -#undef H -#define H(x) ((unsigned long)&hfcm_map[x]) -static const struct pci_device_id hfmultipci_ids[] = { - - /* Cards with HFC-4S Chip */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN1SM, 0, 0, H(0)}, /* BN1S mini PCI */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN2S, 0, 0, H(1)}, /* BN2S */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN2SM, 0, 0, H(2)}, /* BN2S mini PCI */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN4S, 0, 0, H(3)}, /* BN4S */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN4SM, 0, 0, H(4)}, /* BN4S mini PCI */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_DEVICE_ID_CCD_HFC4S, 0, 0, H(5)}, /* Old Eval */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB4ST, 0, 0, H(6)}, /* IOB4ST */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_HFC4S, 0, 0, H(7)}, /* 4S */ - { PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, - PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, 0, 0, H(8)}, - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_SWYX4S, 0, 0, H(9)}, /* 4S Swyx */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_JH4S20, 0, 0, H(10)}, - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_PMX2S, 0, 0, H(11)}, /* Primux */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - 0xb761, 0, 0, H(33)}, /* BN2S PCIe */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - 0xb762, 0, 0, H(34)}, /* BN4S PCIe */ - - /* Cards with HFC-8S Chip */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN8S, 0, 0, H(12)}, /* BN8S */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN8SP, 0, 0, H(13)}, /* BN8S+ */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, /* IOB8ST Recording */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB8ST_1, 0, 0, H(17)}, /* IOB8ST */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_HFC8S, 0, 0, H(18)}, /* 8S */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_OV8S, 0, 0, H(30)}, /* OpenVox 8 */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_JH8S, 0, 0, H(32)}, /* Junganns 8S */ - - - /* Cards with HFC-E1 Chip */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BNE1, 0, 0, H(19)}, /* BNE1 */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BNE1M, 0, 0, H(20)}, /* BNE1 mini PCI */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BNE1DP, 0, 0, H(21)}, /* BNE1 + (Dual) */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BNE1D, 0, 0, H(22)}, /* BNE1 (Dual) */ - - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_DEVICE_ID_CCD_HFCE1, 0, 0, H(23)}, /* Old Eval */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB1E1, 0, 0, H(24)}, /* IOB1E1 */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_HFCE1, 0, 0, H(25)}, /* E1 */ - - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_SPD4S, 0, 0, H(26)}, /* PLX PCI Bridge */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_SPDE1, 0, 0, H(27)}, /* PLX PCI Bridge */ - - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_JHSE1, 0, 0, H(25)}, /* Junghanns E1 */ - - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFC4S), 0 }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFC8S), 0 }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFCE1), 0 }, - {0, } -}; -#undef H - -MODULE_DEVICE_TABLE(pci, hfmultipci_ids); - -static int -hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - struct hm_map *m = (struct hm_map *)ent->driver_data; - int ret; - - if (m == NULL && ent->vendor == PCI_VENDOR_ID_CCD && ( - ent->device == PCI_DEVICE_ID_CCD_HFC4S || - ent->device == PCI_DEVICE_ID_CCD_HFC8S || - ent->device == PCI_DEVICE_ID_CCD_HFCE1)) { - printk(KERN_ERR - "Unknown HFC multiport controller (vendor:%04x device:%04x " - "subvendor:%04x subdevice:%04x)\n", pdev->vendor, - pdev->device, pdev->subsystem_vendor, - pdev->subsystem_device); - printk(KERN_ERR - "Please contact the driver maintainer for support.\n"); - return -ENODEV; - } - ret = hfcmulti_init(m, pdev, ent); - if (ret) - return ret; - HFC_cnt++; - printk(KERN_INFO "%d devices registered\n", HFC_cnt); - return 0; -} - -static struct pci_driver hfcmultipci_driver = { - .name = "hfc_multi", - .probe = hfcmulti_probe, - .remove = hfc_remove_pci, - .id_table = hfmultipci_ids, -}; - -static void __exit -HFCmulti_cleanup(void) -{ - struct hfc_multi *card, *next; - - /* get rid of all devices of this driver */ - list_for_each_entry_safe(card, next, &HFClist, list) - release_card(card); - pci_unregister_driver(&hfcmultipci_driver); -} - -static int __init -HFCmulti_init(void) -{ - int err; - int i, xhfc = 0; - struct hm_map m; - - printk(KERN_INFO "mISDN: HFC-multi driver %s\n", HFC_MULTI_VERSION); - -#ifdef IRQ_DEBUG - printk(KERN_DEBUG "%s: IRQ_DEBUG IS ENABLED!\n", __func__); -#endif - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: init entered\n", __func__); - - switch (poll) { - case 0: - poll_timer = 6; - poll = 128; - break; - case 8: - poll_timer = 2; - break; - case 16: - poll_timer = 3; - break; - case 32: - poll_timer = 4; - break; - case 64: - poll_timer = 5; - break; - case 128: - poll_timer = 6; - break; - case 256: - poll_timer = 7; - break; - default: - printk(KERN_ERR - "%s: Wrong poll value (%d).\n", __func__, poll); - err = -EINVAL; - return err; - - } - - if (!clock) - clock = 1; - - /* Register the embedded devices. - * This should be done before the PCI cards registration */ - switch (hwid) { - case HWID_MINIP4: - xhfc = 1; - m = hfcm_map[31]; - break; - case HWID_MINIP8: - xhfc = 2; - m = hfcm_map[31]; - break; - case HWID_MINIP16: - xhfc = 4; - m = hfcm_map[31]; - break; - default: - xhfc = 0; - } - - for (i = 0; i < xhfc; ++i) { - err = hfcmulti_init(&m, NULL, NULL); - if (err) { - printk(KERN_ERR "error registering embedded driver: " - "%x\n", err); - return err; - } - HFC_cnt++; - printk(KERN_INFO "%d devices registered\n", HFC_cnt); - } - - /* Register the PCI cards */ - err = pci_register_driver(&hfcmultipci_driver); - if (err < 0) { - printk(KERN_ERR "error registering pci driver: %x\n", err); - return err; - } - - return 0; -} - - -module_init(HFCmulti_init); -module_exit(HFCmulti_cleanup); diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c deleted file mode 100644 index 554a1c640321..000000000000 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ /dev/null @@ -1,2360 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * hfcpci.c low level driver for CCD's hfc-pci based cards - * - * Author Werner Cornelius (werner@isdn4linux.de) - * based on existing driver for CCD hfc ISA cards - * type approval valid for HFC-S PCI A based card - * - * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) - * Copyright 2008 by Karsten Keil - * - * Module options: - * - * debug: - * NOTE: only one poll value must be given for all cards - * See hfc_pci.h for debug flags. - * - * poll: - * NOTE: only one poll value must be given for all cards - * Give the number of samples for each fifo process. - * By default 128 is used. Decrease to reduce delay, increase to - * reduce cpu load. If unsure, don't mess with it! - * A value of 128 will use controller's interrupt. Other values will - * use kernel timer, because the controller will not allow lower values - * than 128. - * Also note that the value depends on the kernel timer frequency. - * If kernel uses a frequency of 1000 Hz, steps of 8 samples are possible. - * If the kernel uses 100 Hz, steps of 80 samples are possible. - * If the kernel uses 300 Hz, steps of about 26 samples are possible. - */ - -#include -#include -#include -#include -#include -#include - -#include "hfc_pci.h" - -static void hfcpci_softirq(struct timer_list *unused); -static const char *hfcpci_revision = "2.0"; - -static int HFC_cnt; -static uint debug; -static uint poll, tics; -static DEFINE_TIMER(hfc_tl, hfcpci_softirq); -static unsigned long hfc_jiffies; - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for CCD's hfc-pci based cards"); -MODULE_LICENSE("GPL"); -module_param(debug, uint, S_IRUGO | S_IWUSR); -module_param(poll, uint, S_IRUGO | S_IWUSR); - -enum { - HFC_CCD_2BD0, - HFC_CCD_B000, - HFC_CCD_B006, - HFC_CCD_B007, - HFC_CCD_B008, - HFC_CCD_B009, - HFC_CCD_B00A, - HFC_CCD_B00B, - HFC_CCD_B00C, - HFC_CCD_B100, - HFC_CCD_B700, - HFC_CCD_B701, - HFC_ASUS_0675, - HFC_BERKOM_A1T, - HFC_BERKOM_TCONCEPT, - HFC_ANIGMA_MC145575, - HFC_ZOLTRIX_2BD0, - HFC_DIGI_DF_M_IOM2_E, - HFC_DIGI_DF_M_E, - HFC_DIGI_DF_M_IOM2_A, - HFC_DIGI_DF_M_A, - HFC_ABOCOM_2BD1, - HFC_SITECOM_DC105V2, -}; - -struct hfcPCI_hw { - unsigned char cirm; - unsigned char ctmt; - unsigned char clkdel; - unsigned char states; - unsigned char conn; - unsigned char mst_m; - unsigned char int_m1; - unsigned char int_m2; - unsigned char sctrl; - unsigned char sctrl_r; - unsigned char sctrl_e; - unsigned char trm; - unsigned char fifo_en; - unsigned char bswapped; - unsigned char protocol; - int nt_timer; - unsigned char __iomem *pci_io; /* start of PCI IO memory */ - dma_addr_t dmahandle; - void *fifos; /* FIFO memory */ - int last_bfifo_cnt[2]; - /* marker saving last b-fifo frame count */ - struct timer_list timer; -}; - -#define HFC_CFG_MASTER 1 -#define HFC_CFG_SLAVE 2 -#define HFC_CFG_PCM 3 -#define HFC_CFG_2HFC 4 -#define HFC_CFG_SLAVEHFC 5 -#define HFC_CFG_NEG_F0 6 -#define HFC_CFG_SW_DD_DU 7 - -#define FLG_HFC_TIMER_T1 16 -#define FLG_HFC_TIMER_T3 17 - -#define NT_T1_COUNT 1120 /* number of 3.125ms interrupts (3.5s) */ -#define NT_T3_COUNT 31 /* number of 3.125ms interrupts (97 ms) */ -#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */ -#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ - - -struct hfc_pci { - u_char subtype; - u_char chanlimit; - u_char initdone; - u_long cfg; - u_int irq; - u_int irqcnt; - struct pci_dev *pdev; - struct hfcPCI_hw hw; - spinlock_t lock; /* card lock */ - struct dchannel dch; - struct bchannel bch[2]; -}; - -/* Interface functions */ -static void -enable_hwirq(struct hfc_pci *hc) -{ - hc->hw.int_m2 |= HFCPCI_IRQ_ENABLE; - Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); -} - -static void -disable_hwirq(struct hfc_pci *hc) -{ - hc->hw.int_m2 &= ~((u_char)HFCPCI_IRQ_ENABLE); - Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); -} - -/* - * free hardware resources used by driver - */ -static void -release_io_hfcpci(struct hfc_pci *hc) -{ - /* disable memory mapped ports + busmaster */ - pci_write_config_word(hc->pdev, PCI_COMMAND, 0); - timer_delete(&hc->hw.timer); - dma_free_coherent(&hc->pdev->dev, 0x8000, hc->hw.fifos, - hc->hw.dmahandle); - iounmap(hc->hw.pci_io); -} - -/* - * set mode (NT or TE) - */ -static void -hfcpci_setmode(struct hfc_pci *hc) -{ - if (hc->hw.protocol == ISDN_P_NT_S0) { - hc->hw.clkdel = CLKDEL_NT; /* ST-Bit delay for NT-Mode */ - hc->hw.sctrl |= SCTRL_MODE_NT; /* NT-MODE */ - hc->hw.states = 1; /* G1 */ - } else { - hc->hw.clkdel = CLKDEL_TE; /* ST-Bit delay for TE-Mode */ - hc->hw.sctrl &= ~SCTRL_MODE_NT; /* TE-MODE */ - hc->hw.states = 2; /* F2 */ - } - Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel); - Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states); - udelay(10); - Write_hfc(hc, HFCPCI_STATES, hc->hw.states | 0x40); /* Deactivate */ - Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); -} - -/* - * function called to reset the HFC PCI chip. A complete software reset of chip - * and fifos is done. - */ -static void -reset_hfcpci(struct hfc_pci *hc) -{ - u_char val; - int cnt = 0; - - printk(KERN_DEBUG "reset_hfcpci: entered\n"); - val = Read_hfc(hc, HFCPCI_CHIP_ID); - printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val); - /* enable memory mapped ports, disable busmaster */ - pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO); - disable_hwirq(hc); - /* enable memory ports + busmaster */ - pci_write_config_word(hc->pdev, PCI_COMMAND, - PCI_ENA_MEMIO + PCI_ENA_MASTER); - val = Read_hfc(hc, HFCPCI_STATUS); - printk(KERN_DEBUG "HFC-PCI status(%x) before reset\n", val); - hc->hw.cirm = HFCPCI_RESET; /* Reset On */ - Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); - set_current_state(TASK_UNINTERRUPTIBLE); - mdelay(10); /* Timeout 10ms */ - hc->hw.cirm = 0; /* Reset Off */ - Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); - val = Read_hfc(hc, HFCPCI_STATUS); - printk(KERN_DEBUG "HFC-PCI status(%x) after reset\n", val); - while (cnt < 50000) { /* max 50000 us */ - udelay(5); - cnt += 5; - val = Read_hfc(hc, HFCPCI_STATUS); - if (!(val & 2)) - break; - } - printk(KERN_DEBUG "HFC-PCI status(%x) after %dus\n", val, cnt); - - hc->hw.fifo_en = 0x30; /* only D fifos enabled */ - - hc->hw.bswapped = 0; /* no exchange */ - hc->hw.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; - hc->hw.trm = HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */ - hc->hw.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */ - hc->hw.sctrl_r = 0; - hc->hw.sctrl_e = HFCPCI_AUTO_AWAKE; /* S/T Auto awake */ - hc->hw.mst_m = 0; - if (test_bit(HFC_CFG_MASTER, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_MASTER; /* HFC Master Mode */ - if (test_bit(HFC_CFG_NEG_F0, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_F0_NEGATIV; - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); - Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); - - hc->hw.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | - HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - - /* Clear already pending ints */ - val = Read_hfc(hc, HFCPCI_INT_S1); - - /* set NT/TE mode */ - hfcpci_setmode(hc); - - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); - - /* - * Init GCI/IOM2 in master mode - * Slots 0 and 1 are set for B-chan 1 and 2 - * D- and monitor/CI channel are not enabled - * STIO1 is used as output for data, B1+B2 from ST->IOM+HFC - * STIO2 is used as data input, B1+B2 from IOM->ST - * ST B-channel send disabled -> continuous 1s - * The IOM slots are always enabled - */ - if (test_bit(HFC_CFG_PCM, &hc->cfg)) { - /* set data flow directions: connect B1,B2: HFC to/from PCM */ - hc->hw.conn = 0x09; - } else { - hc->hw.conn = 0x36; /* set data flow directions */ - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { - Write_hfc(hc, HFCPCI_B1_SSL, 0xC0); - Write_hfc(hc, HFCPCI_B2_SSL, 0xC1); - Write_hfc(hc, HFCPCI_B1_RSL, 0xC0); - Write_hfc(hc, HFCPCI_B2_RSL, 0xC1); - } else { - Write_hfc(hc, HFCPCI_B1_SSL, 0x80); - Write_hfc(hc, HFCPCI_B2_SSL, 0x81); - Write_hfc(hc, HFCPCI_B1_RSL, 0x80); - Write_hfc(hc, HFCPCI_B2_RSL, 0x81); - } - } - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - val = Read_hfc(hc, HFCPCI_INT_S2); -} - -/* - * Timer function called when kernel timer expires - */ -static void -hfcpci_Timer(struct timer_list *t) -{ - struct hfc_pci *hc = timer_container_of(hc, t, hw.timer); - hc->hw.timer.expires = jiffies + 75; - /* WD RESET */ -/* - * WriteReg(hc, HFCD_DATA, HFCD_CTMT, hc->hw.ctmt | 0x80); - * add_timer(&hc->hw.timer); - */ -} - - -/* - * select a b-channel entry matching and active - */ -static struct bchannel * -Sel_BCS(struct hfc_pci *hc, int channel) -{ - if (test_bit(FLG_ACTIVE, &hc->bch[0].Flags) && - (hc->bch[0].nr & channel)) - return &hc->bch[0]; - else if (test_bit(FLG_ACTIVE, &hc->bch[1].Flags) && - (hc->bch[1].nr & channel)) - return &hc->bch[1]; - else - return NULL; -} - -/* - * clear the desired B-channel rx fifo - */ -static void -hfcpci_clear_fifo_rx(struct hfc_pci *hc, int fifo) -{ - u_char fifo_state; - struct bzfifo *bzr; - - if (fifo) { - bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; - fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX; - } else { - bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1; - fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1RX; - } - if (fifo_state) - hc->hw.fifo_en ^= fifo_state; - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - hc->hw.last_bfifo_cnt[fifo] = 0; - bzr->f1 = MAX_B_FRAMES; - bzr->f2 = bzr->f1; /* init F pointers to remain constant */ - bzr->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); - bzr->za[MAX_B_FRAMES].z2 = cpu_to_le16( - le16_to_cpu(bzr->za[MAX_B_FRAMES].z1)); - if (fifo_state) - hc->hw.fifo_en |= fifo_state; - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); -} - -/* - * clear the desired B-channel tx fifo - */ -static void hfcpci_clear_fifo_tx(struct hfc_pci *hc, int fifo) -{ - u_char fifo_state; - struct bzfifo *bzt; - - if (fifo) { - bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; - fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX; - } else { - bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; - fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1TX; - } - if (fifo_state) - hc->hw.fifo_en ^= fifo_state; - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) " - "z1(%x) z2(%x) state(%x)\n", - fifo, bzt->f1, bzt->f2, - le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), - le16_to_cpu(bzt->za[MAX_B_FRAMES].z2), - fifo_state); - bzt->f2 = MAX_B_FRAMES; - bzt->f1 = bzt->f2; /* init F pointers to remain constant */ - bzt->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); - bzt->za[MAX_B_FRAMES].z2 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 2); - if (fifo_state) - hc->hw.fifo_en |= fifo_state; - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)\n", - fifo, bzt->f1, bzt->f2, - le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), - le16_to_cpu(bzt->za[MAX_B_FRAMES].z2)); -} - -/* - * read a complete B-frame out of the buffer - */ -static void -hfcpci_empty_bfifo(struct bchannel *bch, struct bzfifo *bz, - u_char *bdata, int count) -{ - u_char *ptr, *ptr1, new_f2; - int maxlen, new_z2; - struct zt *zp; - - if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO)) - printk(KERN_DEBUG "hfcpci_empty_fifo\n"); - zp = &bz->za[bz->f2]; /* point to Z-Regs */ - new_z2 = le16_to_cpu(zp->z2) + count; /* new position in fifo */ - if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z2 -= B_FIFO_SIZE; /* buffer wrap */ - new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; - if ((count > MAX_DATA_SIZE + 3) || (count < 4) || - (*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) { - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "hfcpci_empty_fifo: incoming packet " - "invalid length %d or crc\n", count); -#ifdef ERROR_STATISTIC - bch->err_inv++; -#endif - bz->za[new_f2].z2 = cpu_to_le16(new_z2); - bz->f2 = new_f2; /* next buffer */ - } else { - bch->rx_skb = mI_alloc_skb(count - 3, GFP_ATOMIC); - if (!bch->rx_skb) { - printk(KERN_WARNING "HFCPCI: receive out of memory\n"); - return; - } - count -= 3; - ptr = skb_put(bch->rx_skb, count); - - if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL) - maxlen = count; /* complete transfer */ - else - maxlen = B_FIFO_SIZE + B_SUB_VAL - - le16_to_cpu(zp->z2); /* maximum */ - - ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL); - /* start of data */ - memcpy(ptr, ptr1, maxlen); /* copy data */ - count -= maxlen; - - if (count) { /* rest remaining */ - ptr += maxlen; - ptr1 = bdata; /* start of buffer */ - memcpy(ptr, ptr1, count); /* rest */ - } - bz->za[new_f2].z2 = cpu_to_le16(new_z2); - bz->f2 = new_f2; /* next buffer */ - recv_Bchannel(bch, MISDN_ID_ANY, false); - } -} - -/* - * D-channel receive procedure - */ -static int -receive_dmsg(struct hfc_pci *hc) -{ - struct dchannel *dch = &hc->dch; - int maxlen; - int rcnt, total; - int count = 5; - u_char *ptr, *ptr1; - struct dfifo *df; - struct zt *zp; - - df = &((union fifo_area *)(hc->hw.fifos))->d_chan.d_rx; - while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) { - zp = &df->za[df->f2 & D_FREG_MASK]; - rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); - if (rcnt < 0) - rcnt += D_FIFO_SIZE; - rcnt++; - if (dch->debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG - "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)\n", - df->f1, df->f2, - le16_to_cpu(zp->z1), - le16_to_cpu(zp->z2), - rcnt); - - if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) || - (df->data[le16_to_cpu(zp->z1)])) { - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG - "empty_fifo hfcpci packet inv. len " - "%d or crc %d\n", - rcnt, - df->data[le16_to_cpu(zp->z1)]); -#ifdef ERROR_STATISTIC - cs->err_rx++; -#endif - df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | - (MAX_D_FRAMES + 1); /* next buffer */ - df->za[df->f2 & D_FREG_MASK].z2 = - cpu_to_le16((le16_to_cpu(zp->z2) + rcnt) & - (D_FIFO_SIZE - 1)); - } else { - dch->rx_skb = mI_alloc_skb(rcnt - 3, GFP_ATOMIC); - if (!dch->rx_skb) { - printk(KERN_WARNING - "HFC-PCI: D receive out of memory\n"); - break; - } - total = rcnt; - rcnt -= 3; - ptr = skb_put(dch->rx_skb, rcnt); - - if (le16_to_cpu(zp->z2) + rcnt <= D_FIFO_SIZE) - maxlen = rcnt; /* complete transfer */ - else - maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2); - /* maximum */ - - ptr1 = df->data + le16_to_cpu(zp->z2); - /* start of data */ - memcpy(ptr, ptr1, maxlen); /* copy data */ - rcnt -= maxlen; - - if (rcnt) { /* rest remaining */ - ptr += maxlen; - ptr1 = df->data; /* start of buffer */ - memcpy(ptr, ptr1, rcnt); /* rest */ - } - df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | - (MAX_D_FRAMES + 1); /* next buffer */ - df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16(( - le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1)); - recv_Dchannel(dch); - } - } - return 1; -} - -/* - * check for transparent receive data and read max one 'poll' size if avail - */ -static void -hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *rxbz, - struct bzfifo *txbz, u_char *bdata) -{ - __le16 *z1r, *z2r, *z1t, *z2t; - int new_z2, fcnt_rx, fcnt_tx, maxlen; - u_char *ptr, *ptr1; - - z1r = &rxbz->za[MAX_B_FRAMES].z1; /* pointer to z reg */ - z2r = z1r + 1; - z1t = &txbz->za[MAX_B_FRAMES].z1; - z2t = z1t + 1; - - fcnt_rx = le16_to_cpu(*z1r) - le16_to_cpu(*z2r); - if (!fcnt_rx) - return; /* no data avail */ - - if (fcnt_rx <= 0) - fcnt_rx += B_FIFO_SIZE; /* bytes actually buffered */ - new_z2 = le16_to_cpu(*z2r) + fcnt_rx; /* new position in fifo */ - if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z2 -= B_FIFO_SIZE; /* buffer wrap */ - - fcnt_tx = le16_to_cpu(*z2t) - le16_to_cpu(*z1t); - if (fcnt_tx <= 0) - fcnt_tx += B_FIFO_SIZE; - /* fcnt_tx contains available bytes in tx-fifo */ - fcnt_tx = B_FIFO_SIZE - fcnt_tx; - /* remaining bytes to send (bytes in tx-fifo) */ - - if (test_bit(FLG_RX_OFF, &bch->Flags)) { - bch->dropcnt += fcnt_rx; - *z2r = cpu_to_le16(new_z2); - return; - } - maxlen = bchannel_get_rxbuf(bch, fcnt_rx); - if (maxlen < 0) { - pr_warn("B%d: No bufferspace for %d bytes\n", bch->nr, fcnt_rx); - } else { - ptr = skb_put(bch->rx_skb, fcnt_rx); - if (le16_to_cpu(*z2r) + fcnt_rx <= B_FIFO_SIZE + B_SUB_VAL) - maxlen = fcnt_rx; /* complete transfer */ - else - maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r); - /* maximum */ - - ptr1 = bdata + (le16_to_cpu(*z2r) - B_SUB_VAL); - /* start of data */ - memcpy(ptr, ptr1, maxlen); /* copy data */ - fcnt_rx -= maxlen; - - if (fcnt_rx) { /* rest remaining */ - ptr += maxlen; - ptr1 = bdata; /* start of buffer */ - memcpy(ptr, ptr1, fcnt_rx); /* rest */ - } - recv_Bchannel(bch, fcnt_tx, false); /* bch, id, !force */ - } - *z2r = cpu_to_le16(new_z2); /* new position */ -} - -/* - * B-channel main receive routine - */ -static void -main_rec_hfcpci(struct bchannel *bch) -{ - struct hfc_pci *hc = bch->hw; - int rcnt, real_fifo; - int receive = 0, count = 5; - struct bzfifo *txbz, *rxbz; - u_char *bdata; - struct zt *zp; - - if ((bch->nr & 2) && (!hc->hw.bswapped)) { - rxbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; - txbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; - bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2; - real_fifo = 1; - } else { - rxbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1; - txbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; - bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b1; - real_fifo = 0; - } -Begin: - count--; - if (rxbz->f1 != rxbz->f2) { - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG "hfcpci rec ch(%x) f1(%d) f2(%d)\n", - bch->nr, rxbz->f1, rxbz->f2); - zp = &rxbz->za[rxbz->f2]; - - rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); - if (rcnt < 0) - rcnt += B_FIFO_SIZE; - rcnt++; - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)\n", - bch->nr, le16_to_cpu(zp->z1), - le16_to_cpu(zp->z2), rcnt); - hfcpci_empty_bfifo(bch, rxbz, bdata, rcnt); - rcnt = rxbz->f1 - rxbz->f2; - if (rcnt < 0) - rcnt += MAX_B_FRAMES + 1; - if (hc->hw.last_bfifo_cnt[real_fifo] > rcnt + 1) { - rcnt = 0; - hfcpci_clear_fifo_rx(hc, real_fifo); - } - hc->hw.last_bfifo_cnt[real_fifo] = rcnt; - if (rcnt > 1) - receive = 1; - else - receive = 0; - } else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { - hfcpci_empty_fifo_trans(bch, rxbz, txbz, bdata); - return; - } else - receive = 0; - if (count && receive) - goto Begin; - -} - -/* - * D-channel send routine - */ -static void -hfcpci_fill_dfifo(struct hfc_pci *hc) -{ - struct dchannel *dch = &hc->dch; - int fcnt; - int count, new_z1, maxlen; - struct dfifo *df; - u_char *src, *dst, new_f1; - - if ((dch->debug & DEBUG_HW_DCHANNEL) && !(dch->debug & DEBUG_HW_DFIFO)) - printk(KERN_DEBUG "%s\n", __func__); - - if (!dch->tx_skb) - return; - count = dch->tx_skb->len - dch->tx_idx; - if (count <= 0) - return; - df = &((union fifo_area *) (hc->hw.fifos))->d_chan.d_tx; - - if (dch->debug & DEBUG_HW_DFIFO) - printk(KERN_DEBUG "%s:f1(%d) f2(%d) z1(f1)(%x)\n", __func__, - df->f1, df->f2, - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1)); - fcnt = df->f1 - df->f2; /* frame count actually buffered */ - if (fcnt < 0) - fcnt += (MAX_D_FRAMES + 1); /* if wrap around */ - if (fcnt > (MAX_D_FRAMES - 1)) { - if (dch->debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG - "hfcpci_fill_Dfifo more as 14 frames\n"); -#ifdef ERROR_STATISTIC - cs->err_tx++; -#endif - return; - } - /* now determine free bytes in FIFO buffer */ - maxlen = le16_to_cpu(df->za[df->f2 & D_FREG_MASK].z2) - - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) - 1; - if (maxlen <= 0) - maxlen += D_FIFO_SIZE; /* count now contains available bytes */ - - if (dch->debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG "hfcpci_fill_Dfifo count(%d/%d)\n", - count, maxlen); - if (count > maxlen) { - if (dch->debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG "hfcpci_fill_Dfifo no fifo mem\n"); - return; - } - new_z1 = (le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) + count) & - (D_FIFO_SIZE - 1); - new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1); - src = dch->tx_skb->data + dch->tx_idx; /* source pointer */ - dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); - maxlen = D_FIFO_SIZE - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); - /* end fifo */ - if (maxlen > count) - maxlen = count; /* limit size */ - memcpy(dst, src, maxlen); /* first copy */ - - count -= maxlen; /* remaining bytes */ - if (count) { - dst = df->data; /* start of buffer */ - src += maxlen; /* new position */ - memcpy(dst, src, count); - } - df->za[new_f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); - /* for next buffer */ - df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); - /* new pos actual buffer */ - df->f1 = new_f1; /* next frame */ - dch->tx_idx = dch->tx_skb->len; -} - -/* - * B-channel send routine - */ -static void -hfcpci_fill_fifo(struct bchannel *bch) -{ - struct hfc_pci *hc = bch->hw; - int maxlen, fcnt; - int count, new_z1; - struct bzfifo *bz; - u_char *bdata; - u_char new_f1, *src, *dst; - __le16 *z1t, *z2t; - - if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO)) - printk(KERN_DEBUG "%s\n", __func__); - if ((!bch->tx_skb) || bch->tx_skb->len == 0) { - if (!test_bit(FLG_FILLEMPTY, &bch->Flags) && - !test_bit(FLG_TRANSPARENT, &bch->Flags)) - return; - count = HFCPCI_FILLEMPTY; - } else { - count = bch->tx_skb->len - bch->tx_idx; - } - if ((bch->nr & 2) && (!hc->hw.bswapped)) { - bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; - bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b2; - } else { - bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; - bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b1; - } - - if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { - z1t = &bz->za[MAX_B_FRAMES].z1; - z2t = z1t + 1; - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG "hfcpci_fill_fifo_trans ch(%x) " - "cnt(%d) z1(%x) z2(%x)\n", bch->nr, count, - le16_to_cpu(*z1t), le16_to_cpu(*z2t)); - fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t); - if (fcnt <= 0) - fcnt += B_FIFO_SIZE; - if (test_bit(FLG_FILLEMPTY, &bch->Flags)) { - /* fcnt contains available bytes in fifo */ - if (count > fcnt) - count = fcnt; - new_z1 = le16_to_cpu(*z1t) + count; - /* new buffer Position */ - if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z1 -= B_FIFO_SIZE; /* buffer wrap */ - dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); - maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); - /* end of fifo */ - if (bch->debug & DEBUG_HW_BFIFO) - printk(KERN_DEBUG "hfcpci_FFt fillempty " - "fcnt(%d) maxl(%d) nz1(%x) dst(%p)\n", - fcnt, maxlen, new_z1, dst); - if (maxlen > count) - maxlen = count; /* limit size */ - memset(dst, bch->fill[0], maxlen); /* first copy */ - count -= maxlen; /* remaining bytes */ - if (count) { - dst = bdata; /* start of buffer */ - memset(dst, bch->fill[0], count); - } - *z1t = cpu_to_le16(new_z1); /* now send data */ - return; - } - /* fcnt contains available bytes in fifo */ - fcnt = B_FIFO_SIZE - fcnt; - /* remaining bytes to send (bytes in fifo) */ - - next_t_frame: - count = bch->tx_skb->len - bch->tx_idx; - /* maximum fill shall be poll*2 */ - if (count > (poll << 1) - fcnt) - count = (poll << 1) - fcnt; - if (count <= 0) - return; - /* data is suitable for fifo */ - new_z1 = le16_to_cpu(*z1t) + count; - /* new buffer Position */ - if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z1 -= B_FIFO_SIZE; /* buffer wrap */ - src = bch->tx_skb->data + bch->tx_idx; - /* source pointer */ - dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); - maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); - /* end of fifo */ - if (bch->debug & DEBUG_HW_BFIFO) - printk(KERN_DEBUG "hfcpci_FFt fcnt(%d) " - "maxl(%d) nz1(%x) dst(%p)\n", - fcnt, maxlen, new_z1, dst); - fcnt += count; - bch->tx_idx += count; - if (maxlen > count) - maxlen = count; /* limit size */ - memcpy(dst, src, maxlen); /* first copy */ - count -= maxlen; /* remaining bytes */ - if (count) { - dst = bdata; /* start of buffer */ - src += maxlen; /* new position */ - memcpy(dst, src, count); - } - *z1t = cpu_to_le16(new_z1); /* now send data */ - if (bch->tx_idx < bch->tx_skb->len) - return; - dev_kfree_skb_any(bch->tx_skb); - if (get_next_bframe(bch)) - goto next_t_frame; - return; - } - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)\n", - __func__, bch->nr, bz->f1, bz->f2, - bz->za[bz->f1].z1); - fcnt = bz->f1 - bz->f2; /* frame count actually buffered */ - if (fcnt < 0) - fcnt += (MAX_B_FRAMES + 1); /* if wrap around */ - if (fcnt > (MAX_B_FRAMES - 1)) { - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "hfcpci_fill_Bfifo more as 14 frames\n"); - return; - } - /* now determine free bytes in FIFO buffer */ - maxlen = le16_to_cpu(bz->za[bz->f2].z2) - - le16_to_cpu(bz->za[bz->f1].z1) - 1; - if (maxlen <= 0) - maxlen += B_FIFO_SIZE; /* count now contains available bytes */ - - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG "hfcpci_fill_fifo ch(%x) count(%d/%d)\n", - bch->nr, count, maxlen); - - if (maxlen < count) { - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG "hfcpci_fill_fifo no fifo mem\n"); - return; - } - new_z1 = le16_to_cpu(bz->za[bz->f1].z1) + count; - /* new buffer Position */ - if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z1 -= B_FIFO_SIZE; /* buffer wrap */ - - new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES); - src = bch->tx_skb->data + bch->tx_idx; /* source pointer */ - dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL); - maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(bz->za[bz->f1].z1); - /* end fifo */ - if (maxlen > count) - maxlen = count; /* limit size */ - memcpy(dst, src, maxlen); /* first copy */ - - count -= maxlen; /* remaining bytes */ - if (count) { - dst = bdata; /* start of buffer */ - src += maxlen; /* new position */ - memcpy(dst, src, count); - } - bz->za[new_f1].z1 = cpu_to_le16(new_z1); /* for next buffer */ - bz->f1 = new_f1; /* next frame */ - dev_kfree_skb_any(bch->tx_skb); - get_next_bframe(bch); -} - - - -/* - * handle L1 state changes TE - */ - -static void -ph_state_te(struct dchannel *dch) -{ - if (dch->debug) - printk(KERN_DEBUG "%s: TE newstate %x\n", - __func__, dch->state); - switch (dch->state) { - case 0: - l1_event(dch->l1, HW_RESET_IND); - break; - case 3: - l1_event(dch->l1, HW_DEACT_IND); - break; - case 5: - case 8: - l1_event(dch->l1, ANYSIGNAL); - break; - case 6: - l1_event(dch->l1, INFO2); - break; - case 7: - l1_event(dch->l1, INFO4_P8); - break; - } -} - -/* - * handle L1 state changes NT - */ - -static void -handle_nt_timer3(struct dchannel *dch) { - struct hfc_pci *hc = dch->hw; - - test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); - hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - hc->hw.nt_timer = 0; - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - if (test_bit(HFC_CFG_MASTER, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); -} - -static void -ph_state_nt(struct dchannel *dch) -{ - struct hfc_pci *hc = dch->hw; - - if (dch->debug) - printk(KERN_DEBUG "%s: NT newstate %x\n", - __func__, dch->state); - switch (dch->state) { - case 2: - if (hc->hw.nt_timer < 0) { - hc->hw.nt_timer = 0; - test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); - test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); - hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - /* Clear already pending ints */ - (void) Read_hfc(hc, HFCPCI_INT_S1); - Write_hfc(hc, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE); - udelay(10); - Write_hfc(hc, HFCPCI_STATES, 4); - dch->state = 4; - } else if (hc->hw.nt_timer == 0) { - hc->hw.int_m1 |= HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - hc->hw.nt_timer = NT_T1_COUNT; - hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; - hc->hw.ctmt |= HFCPCI_TIM3_125; - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | - HFCPCI_CLTIMER); - test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); - test_and_set_bit(FLG_HFC_TIMER_T1, &dch->Flags); - /* allow G2 -> G3 transition */ - Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); - } else { - Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); - } - break; - case 1: - hc->hw.nt_timer = 0; - test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); - test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); - hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - hc->hw.mst_m &= ~HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - break; - case 4: - hc->hw.nt_timer = 0; - test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); - test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); - hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - break; - case 3: - if (!test_and_set_bit(FLG_HFC_TIMER_T3, &dch->Flags)) { - if (!test_and_clear_bit(FLG_L2_ACTIVATED, - &dch->Flags)) { - handle_nt_timer3(dch); - break; - } - test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); - hc->hw.int_m1 |= HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - hc->hw.nt_timer = NT_T3_COUNT; - hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; - hc->hw.ctmt |= HFCPCI_TIM3_125; - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | - HFCPCI_CLTIMER); - } - break; - } -} - -static void -ph_state(struct dchannel *dch) -{ - struct hfc_pci *hc = dch->hw; - - if (hc->hw.protocol == ISDN_P_NT_S0) { - if (test_bit(FLG_HFC_TIMER_T3, &dch->Flags) && - hc->hw.nt_timer < 0) - handle_nt_timer3(dch); - else - ph_state_nt(dch); - } else - ph_state_te(dch); -} - -/* - * Layer 1 callback function - */ -static int -hfc_l1callback(struct dchannel *dch, u_int cmd) -{ - struct hfc_pci *hc = dch->hw; - - switch (cmd) { - case INFO3_P8: - case INFO3_P10: - if (test_bit(HFC_CFG_MASTER, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - break; - case HW_RESET_REQ: - Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); - /* HFC ST 3 */ - udelay(6); - Write_hfc(hc, HFCPCI_STATES, 3); /* HFC ST 2 */ - if (test_bit(HFC_CFG_MASTER, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | - HFCPCI_DO_ACTION); - l1_event(dch->l1, HW_POWERUP_IND); - break; - case HW_DEACT_REQ: - hc->hw.mst_m &= ~HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - skb_queue_purge(&dch->squeue); - if (dch->tx_skb) { - dev_kfree_skb(dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - dev_kfree_skb(dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); - break; - case HW_POWERUP_REQ: - Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: unknown command %x\n", - __func__, cmd); - return -1; - } - return 0; -} - -/* - * Interrupt handler - */ -static inline void -tx_birq(struct bchannel *bch) -{ - if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) - hfcpci_fill_fifo(bch); - else { - dev_kfree_skb_any(bch->tx_skb); - if (get_next_bframe(bch)) - hfcpci_fill_fifo(bch); - } -} - -static inline void -tx_dirq(struct dchannel *dch) -{ - if (dch->tx_skb && dch->tx_idx < dch->tx_skb->len) - hfcpci_fill_dfifo(dch->hw); - else { - dev_kfree_skb(dch->tx_skb); - if (get_next_dframe(dch)) - hfcpci_fill_dfifo(dch->hw); - } -} - -static irqreturn_t -hfcpci_int(int intno, void *dev_id) -{ - struct hfc_pci *hc = dev_id; - u_char exval; - struct bchannel *bch; - u_char val, stat; - - spin_lock(&hc->lock); - if (!(hc->hw.int_m2 & 0x08)) { - spin_unlock(&hc->lock); - return IRQ_NONE; /* not initialised */ - } - stat = Read_hfc(hc, HFCPCI_STATUS); - if (HFCPCI_ANYINT & stat) { - val = Read_hfc(hc, HFCPCI_INT_S1); - if (hc->dch.debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG - "HFC-PCI: stat(%02x) s1(%02x)\n", stat, val); - } else { - /* shared */ - spin_unlock(&hc->lock); - return IRQ_NONE; - } - hc->irqcnt++; - - if (hc->dch.debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG "HFC-PCI irq %x\n", val); - val &= hc->hw.int_m1; - if (val & 0x40) { /* state machine irq */ - exval = Read_hfc(hc, HFCPCI_STATES) & 0xf; - if (hc->dch.debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG "ph_state chg %d->%d\n", - hc->dch.state, exval); - hc->dch.state = exval; - schedule_event(&hc->dch, FLG_PHCHANGE); - val &= ~0x40; - } - if (val & 0x80) { /* timer irq */ - if (hc->hw.protocol == ISDN_P_NT_S0) { - if ((--hc->hw.nt_timer) < 0) - schedule_event(&hc->dch, FLG_PHCHANGE); - } - val &= ~0x80; - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); - } - if (val & 0x08) { /* B1 rx */ - bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); - if (bch) - main_rec_hfcpci(bch); - else if (hc->dch.debug) - printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n"); - } - if (val & 0x10) { /* B2 rx */ - bch = Sel_BCS(hc, 2); - if (bch) - main_rec_hfcpci(bch); - else if (hc->dch.debug) - printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n"); - } - if (val & 0x01) { /* B1 tx */ - bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); - if (bch) - tx_birq(bch); - else if (hc->dch.debug) - printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n"); - } - if (val & 0x02) { /* B2 tx */ - bch = Sel_BCS(hc, 2); - if (bch) - tx_birq(bch); - else if (hc->dch.debug) - printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n"); - } - if (val & 0x20) /* D rx */ - receive_dmsg(hc); - if (val & 0x04) { /* D tx */ - if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags)) - timer_delete(&hc->dch.timer); - tx_dirq(&hc->dch); - } - spin_unlock(&hc->lock); - return IRQ_HANDLED; -} - -/* - * timer callback for D-chan busy resolution. Currently no function - */ -static void -hfcpci_dbusy_timer(struct timer_list *t) -{ -} - -/* - * activate/deactivate hardware for selected channels and mode - */ -static int -mode_hfcpci(struct bchannel *bch, int bc, int protocol) -{ - struct hfc_pci *hc = bch->hw; - int fifo2; - u_char rx_slot = 0, tx_slot = 0, pcm_mode; - - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "HFCPCI bchannel protocol %x-->%x ch %x-->%x\n", - bch->state, protocol, bch->nr, bc); - - fifo2 = bc; - pcm_mode = (bc >> 24) & 0xff; - if (pcm_mode) { /* PCM SLOT USE */ - if (!test_bit(HFC_CFG_PCM, &hc->cfg)) - printk(KERN_WARNING - "%s: pcm channel id without HFC_CFG_PCM\n", - __func__); - rx_slot = (bc >> 8) & 0xff; - tx_slot = (bc >> 16) & 0xff; - bc = bc & 0xff; - } else if (test_bit(HFC_CFG_PCM, &hc->cfg) && (protocol > ISDN_P_NONE)) - printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n", - __func__); - if (hc->chanlimit > 1) { - hc->hw.bswapped = 0; /* B1 and B2 normal mode */ - hc->hw.sctrl_e &= ~0x80; - } else { - if (bc & 2) { - if (protocol != ISDN_P_NONE) { - hc->hw.bswapped = 1; /* B1 and B2 exchanged */ - hc->hw.sctrl_e |= 0x80; - } else { - hc->hw.bswapped = 0; /* B1 and B2 normal mode */ - hc->hw.sctrl_e &= ~0x80; - } - fifo2 = 1; - } else { - hc->hw.bswapped = 0; /* B1 and B2 normal mode */ - hc->hw.sctrl_e &= ~0x80; - } - } - switch (protocol) { - case (-1): /* used for init */ - bch->state = -1; - bch->nr = bc; - fallthrough; - case (ISDN_P_NONE): - if (bch->state == ISDN_P_NONE) - return 0; - if (bc & 2) { - hc->hw.sctrl &= ~SCTRL_B2_ENA; - hc->hw.sctrl_r &= ~SCTRL_B2_ENA; - } else { - hc->hw.sctrl &= ~SCTRL_B1_ENA; - hc->hw.sctrl_r &= ~SCTRL_B1_ENA; - } - if (fifo2 & 2) { - hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2; - hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS | - HFCPCI_INTS_B2REC); - } else { - hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1; - hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS | - HFCPCI_INTS_B1REC); - } -#ifdef REVERSE_BITORDER - if (bch->nr & 2) - hc->hw.cirm &= 0x7f; - else - hc->hw.cirm &= 0xbf; -#endif - bch->state = ISDN_P_NONE; - bch->nr = bc; - test_and_clear_bit(FLG_HDLC, &bch->Flags); - test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case (ISDN_P_B_RAW): - bch->state = protocol; - bch->nr = bc; - hfcpci_clear_fifo_rx(hc, (fifo2 & 2) ? 1 : 0); - hfcpci_clear_fifo_tx(hc, (fifo2 & 2) ? 1 : 0); - if (bc & 2) { - hc->hw.sctrl |= SCTRL_B2_ENA; - hc->hw.sctrl_r |= SCTRL_B2_ENA; -#ifdef REVERSE_BITORDER - hc->hw.cirm |= 0x80; -#endif - } else { - hc->hw.sctrl |= SCTRL_B1_ENA; - hc->hw.sctrl_r |= SCTRL_B1_ENA; -#ifdef REVERSE_BITORDER - hc->hw.cirm |= 0x40; -#endif - } - if (fifo2 & 2) { - hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; - if (!tics) - hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS | - HFCPCI_INTS_B2REC); - hc->hw.ctmt |= 2; - hc->hw.conn &= ~0x18; - } else { - hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; - if (!tics) - hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS | - HFCPCI_INTS_B1REC); - hc->hw.ctmt |= 1; - hc->hw.conn &= ~0x03; - } - test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case (ISDN_P_B_HDLC): - bch->state = protocol; - bch->nr = bc; - hfcpci_clear_fifo_rx(hc, (fifo2 & 2) ? 1 : 0); - hfcpci_clear_fifo_tx(hc, (fifo2 & 2) ? 1 : 0); - if (bc & 2) { - hc->hw.sctrl |= SCTRL_B2_ENA; - hc->hw.sctrl_r |= SCTRL_B2_ENA; - } else { - hc->hw.sctrl |= SCTRL_B1_ENA; - hc->hw.sctrl_r |= SCTRL_B1_ENA; - } - if (fifo2 & 2) { - hc->hw.last_bfifo_cnt[1] = 0; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; - hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS | - HFCPCI_INTS_B2REC); - hc->hw.ctmt &= ~2; - hc->hw.conn &= ~0x18; - } else { - hc->hw.last_bfifo_cnt[0] = 0; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; - hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS | - HFCPCI_INTS_B1REC); - hc->hw.ctmt &= ~1; - hc->hw.conn &= ~0x03; - } - test_and_set_bit(FLG_HDLC, &bch->Flags); - break; - default: - printk(KERN_DEBUG "prot not known %x\n", protocol); - return -ENOPROTOOPT; - } - if (test_bit(HFC_CFG_PCM, &hc->cfg)) { - if ((protocol == ISDN_P_NONE) || - (protocol == -1)) { /* init case */ - rx_slot = 0; - tx_slot = 0; - } else { - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { - rx_slot |= 0xC0; - tx_slot |= 0xC0; - } else { - rx_slot |= 0x80; - tx_slot |= 0x80; - } - } - if (bc & 2) { - hc->hw.conn &= 0xc7; - hc->hw.conn |= 0x08; - printk(KERN_DEBUG "%s: Write_hfc: B2_SSL 0x%x\n", - __func__, tx_slot); - printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n", - __func__, rx_slot); - Write_hfc(hc, HFCPCI_B2_SSL, tx_slot); - Write_hfc(hc, HFCPCI_B2_RSL, rx_slot); - } else { - hc->hw.conn &= 0xf8; - hc->hw.conn |= 0x01; - printk(KERN_DEBUG "%s: Write_hfc: B1_SSL 0x%x\n", - __func__, tx_slot); - printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n", - __func__, rx_slot); - Write_hfc(hc, HFCPCI_B1_SSL, tx_slot); - Write_hfc(hc, HFCPCI_B1_RSL, rx_slot); - } - } - Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); - Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); -#ifdef REVERSE_BITORDER - Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); -#endif - return 0; -} - -static int -set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan) -{ - struct hfc_pci *hc = bch->hw; - - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x\n", - bch->state, protocol, bch->nr, chan); - if (bch->nr != chan) { - printk(KERN_DEBUG - "HFCPCI rxtest wrong channel parameter %x/%x\n", - bch->nr, chan); - return -EINVAL; - } - switch (protocol) { - case (ISDN_P_B_RAW): - bch->state = protocol; - hfcpci_clear_fifo_rx(hc, (chan & 2) ? 1 : 0); - if (chan & 2) { - hc->hw.sctrl_r |= SCTRL_B2_ENA; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; - if (!tics) - hc->hw.int_m1 |= HFCPCI_INTS_B2REC; - hc->hw.ctmt |= 2; - hc->hw.conn &= ~0x18; -#ifdef REVERSE_BITORDER - hc->hw.cirm |= 0x80; -#endif - } else { - hc->hw.sctrl_r |= SCTRL_B1_ENA; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; - if (!tics) - hc->hw.int_m1 |= HFCPCI_INTS_B1REC; - hc->hw.ctmt |= 1; - hc->hw.conn &= ~0x03; -#ifdef REVERSE_BITORDER - hc->hw.cirm |= 0x40; -#endif - } - break; - case (ISDN_P_B_HDLC): - bch->state = protocol; - hfcpci_clear_fifo_rx(hc, (chan & 2) ? 1 : 0); - if (chan & 2) { - hc->hw.sctrl_r |= SCTRL_B2_ENA; - hc->hw.last_bfifo_cnt[1] = 0; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; - hc->hw.int_m1 |= HFCPCI_INTS_B2REC; - hc->hw.ctmt &= ~2; - hc->hw.conn &= ~0x18; - } else { - hc->hw.sctrl_r |= SCTRL_B1_ENA; - hc->hw.last_bfifo_cnt[0] = 0; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; - hc->hw.int_m1 |= HFCPCI_INTS_B1REC; - hc->hw.ctmt &= ~1; - hc->hw.conn &= ~0x03; - } - break; - default: - printk(KERN_DEBUG "prot not known %x\n", protocol); - return -ENOPROTOOPT; - } - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); -#ifdef REVERSE_BITORDER - Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); -#endif - return 0; -} - -static void -deactivate_bchannel(struct bchannel *bch) -{ - struct hfc_pci *hc = bch->hw; - u_long flags; - - spin_lock_irqsave(&hc->lock, flags); - mISDN_clear_bchannel(bch); - mode_hfcpci(bch, bch->nr, ISDN_P_NONE); - spin_unlock_irqrestore(&hc->lock, flags); -} - -/* - * Layer 1 B-channel hardware access - */ -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} -static int -hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hfc_pci *hc = bch->hw; - int ret = -EINVAL; - u_long flags; - - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg); - switch (cmd) { - case HW_TESTRX_RAW: - spin_lock_irqsave(&hc->lock, flags); - ret = set_hfcpci_rxtest(bch, ISDN_P_B_RAW, (int)(long)arg); - spin_unlock_irqrestore(&hc->lock, flags); - break; - case HW_TESTRX_HDLC: - spin_lock_irqsave(&hc->lock, flags); - ret = set_hfcpci_rxtest(bch, ISDN_P_B_HDLC, (int)(long)arg); - spin_unlock_irqrestore(&hc->lock, flags); - break; - case HW_TESTRX_OFF: - spin_lock_irqsave(&hc->lock, flags); - mode_hfcpci(bch, bch->nr, ISDN_P_NONE); - spin_unlock_irqrestore(&hc->lock, flags); - ret = 0; - break; - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - deactivate_bchannel(bch); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - printk(KERN_WARNING "%s: unknown prim(%x)\n", - __func__, cmd); - } - return ret; -} - -/* - * Layer2 -> Layer 1 Dchannel data - */ -static int -hfcpci_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct hfc_pci *hc = dch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned int id; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&hc->lock, flags); - ret = dchannel_senddata(dch, skb); - if (ret > 0) { /* direct TX */ - id = hh->id; /* skb can be freed */ - hfcpci_fill_dfifo(dch->hw); - ret = 0; - spin_unlock_irqrestore(&hc->lock, flags); - queue_ch_frame(ch, PH_DATA_CNF, id, NULL); - } else - spin_unlock_irqrestore(&hc->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(&hc->lock, flags); - if (hc->hw.protocol == ISDN_P_NT_S0) { - ret = 0; - if (test_bit(HFC_CFG_MASTER, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - if (test_bit(FLG_ACTIVE, &dch->Flags)) { - spin_unlock_irqrestore(&hc->lock, flags); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - break; - } - test_and_set_bit(FLG_L2_ACTIVATED, &dch->Flags); - Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | - HFCPCI_DO_ACTION | 1); - } else - ret = l1_event(dch->l1, hh->prim); - spin_unlock_irqrestore(&hc->lock, flags); - break; - case PH_DEACTIVATE_REQ: - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - spin_lock_irqsave(&hc->lock, flags); - if (hc->hw.protocol == ISDN_P_NT_S0) { - struct sk_buff_head free_queue; - - __skb_queue_head_init(&free_queue); - /* prepare deactivation */ - Write_hfc(hc, HFCPCI_STATES, 0x40); - skb_queue_splice_init(&dch->squeue, &free_queue); - if (dch->tx_skb) { - __skb_queue_tail(&free_queue, dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - __skb_queue_tail(&free_queue, dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); -#ifdef FIXME - if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) - dchannel_sched_event(&hc->dch, D_CLEARBUSY); -#endif - hc->hw.mst_m &= ~HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - ret = 0; - spin_unlock_irqrestore(&hc->lock, flags); - __skb_queue_purge(&free_queue); - } else { - ret = l1_event(dch->l1, hh->prim); - spin_unlock_irqrestore(&hc->lock, flags); - } - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -/* - * Layer2 -> Layer 1 Bchannel data - */ -static int -hfcpci_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hfc_pci *hc = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&hc->lock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - hfcpci_fill_fifo(bch); - ret = 0; - } - spin_unlock_irqrestore(&hc->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(&hc->lock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = mode_hfcpci(bch, bch->nr, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(&hc->lock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - deactivate_bchannel(bch); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -/* - * called for card init message - */ - -static void -inithfcpci(struct hfc_pci *hc) -{ - printk(KERN_DEBUG "inithfcpci: entered\n"); - timer_setup(&hc->dch.timer, hfcpci_dbusy_timer, 0); - hc->chanlimit = 2; - mode_hfcpci(&hc->bch[0], 1, -1); - mode_hfcpci(&hc->bch[1], 2, -1); -} - - -static int -init_card(struct hfc_pci *hc) -{ - int cnt = 3; - u_long flags; - - printk(KERN_DEBUG "init_card: entered\n"); - - - spin_lock_irqsave(&hc->lock, flags); - disable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - if (request_irq(hc->irq, hfcpci_int, IRQF_SHARED, "HFC PCI", hc)) { - printk(KERN_WARNING - "mISDN: couldn't get interrupt %d\n", hc->irq); - return -EIO; - } - spin_lock_irqsave(&hc->lock, flags); - reset_hfcpci(hc); - while (cnt) { - inithfcpci(hc); - /* - * Finally enable IRQ output - * this is only allowed, if an IRQ routine is already - * established for this HFC, so don't do that earlier - */ - enable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - /* Timeout 80ms */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((80 * HZ) / 1000); - printk(KERN_INFO "HFC PCI: IRQ %d count %d\n", - hc->irq, hc->irqcnt); - /* now switch timer interrupt off */ - spin_lock_irqsave(&hc->lock, flags); - hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - /* reinit mode reg */ - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - if (!hc->irqcnt) { - printk(KERN_WARNING - "HFC PCI: IRQ(%d) getting no interrupts " - "during init %d\n", hc->irq, 4 - cnt); - if (cnt == 1) - break; - else { - reset_hfcpci(hc); - cnt--; - } - } else { - spin_unlock_irqrestore(&hc->lock, flags); - hc->initdone = 1; - return 0; - } - } - disable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - free_irq(hc->irq, hc); - return -EIO; -} - -static int -channel_ctrl(struct hfc_pci *hc, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - u_char slot; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT | - MISDN_CTRL_DISCONNECT | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_LOOP: - /* channel 0 disabled loop */ - if (cq->channel < 0 || cq->channel > 2) { - ret = -EINVAL; - break; - } - if (cq->channel & 1) { - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) - slot = 0xC0; - else - slot = 0x80; - printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", - __func__, slot); - Write_hfc(hc, HFCPCI_B1_SSL, slot); - Write_hfc(hc, HFCPCI_B1_RSL, slot); - hc->hw.conn = (hc->hw.conn & ~7) | 6; - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - } - if (cq->channel & 2) { - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) - slot = 0xC1; - else - slot = 0x81; - printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", - __func__, slot); - Write_hfc(hc, HFCPCI_B2_SSL, slot); - Write_hfc(hc, HFCPCI_B2_RSL, slot); - hc->hw.conn = (hc->hw.conn & ~0x38) | 0x30; - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - } - if (cq->channel & 3) - hc->hw.trm |= 0x80; /* enable IOM-loop */ - else { - hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09; - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - hc->hw.trm &= 0x7f; /* disable IOM-loop */ - } - Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); - break; - case MISDN_CTRL_CONNECT: - if (cq->channel == cq->p1) { - ret = -EINVAL; - break; - } - if (cq->channel < 1 || cq->channel > 2 || - cq->p1 < 1 || cq->p1 > 2) { - ret = -EINVAL; - break; - } - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) - slot = 0xC0; - else - slot = 0x80; - printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", - __func__, slot); - Write_hfc(hc, HFCPCI_B1_SSL, slot); - Write_hfc(hc, HFCPCI_B2_RSL, slot); - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) - slot = 0xC1; - else - slot = 0x81; - printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", - __func__, slot); - Write_hfc(hc, HFCPCI_B2_SSL, slot); - Write_hfc(hc, HFCPCI_B1_RSL, slot); - hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x36; - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - hc->hw.trm |= 0x80; - Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); - break; - case MISDN_CTRL_DISCONNECT: - hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09; - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - hc->hw.trm &= 0x7f; /* disable IOM-loop */ - break; - case MISDN_CTRL_L1_TIMER3: - ret = l1_event(hc->dch.l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); - break; - default: - printk(KERN_WARNING "%s: unknown Op %x\n", - __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch, - struct channel_req *rq) -{ - int err = 0; - - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, - hc->dch.dev.id, __builtin_return_address(0)); - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - if (rq->adr.channel == 1) { - /* TODO: E-Channel */ - return -EINVAL; - } - if (!hc->initdone) { - if (rq->protocol == ISDN_P_TE_S0) { - err = create_l1(&hc->dch, hfc_l1callback); - if (err) - return err; - } - hc->hw.protocol = rq->protocol; - ch->protocol = rq->protocol; - err = init_card(hc); - if (err) - return err; - } else { - if (rq->protocol != ch->protocol) { - if (hc->hw.protocol == ISDN_P_TE_S0) - l1_event(hc->dch.l1, CLOSE_CHANNEL); - if (rq->protocol == ISDN_P_TE_S0) { - err = create_l1(&hc->dch, hfc_l1callback); - if (err) - return err; - } - hc->hw.protocol = rq->protocol; - ch->protocol = rq->protocol; - hfcpci_setmode(hc); - } - } - - if (((ch->protocol == ISDN_P_NT_S0) && (hc->dch.state == 3)) || - ((ch->protocol == ISDN_P_TE_S0) && (hc->dch.state == 7))) { - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - } - rq->ch = ch; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -static int -open_bchannel(struct hfc_pci *hc, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &hc->bch[rq->adr.channel - 1]; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; /* TODO: E-channel */ - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -/* - * device control function - */ -static int -hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct hfc_pci *hc = dch->hw; - struct channel_req *rq; - int err = 0; - - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", - __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if ((rq->protocol == ISDN_P_TE_S0) || - (rq->protocol == ISDN_P_NT_S0)) - err = open_dchannel(hc, ch, rq); - else - err = open_bchannel(hc, rq); - break; - case CLOSE_CHANNEL: - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) close from %p\n", - __func__, hc->dch.dev.id, - __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(hc, arg); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: unknown command %x\n", - __func__, cmd); - return -EINVAL; - } - return err; -} - -static int -setup_hw(struct hfc_pci *hc) -{ - void *buffer; - - printk(KERN_INFO "mISDN: HFC-PCI driver %s\n", hfcpci_revision); - hc->hw.cirm = 0; - hc->dch.state = 0; - pci_set_master(hc->pdev); - if (!hc->irq) { - printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); - return -EINVAL; - } - hc->hw.pci_io = - (char __iomem *)(unsigned long)hc->pdev->resource[1].start; - - if (!hc->hw.pci_io) { - printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); - return -ENOMEM; - } - /* Allocate memory for FIFOS */ - /* the memory needs to be on a 32k boundary within the first 4G */ - if (dma_set_mask(&hc->pdev->dev, 0xFFFF8000)) { - printk(KERN_WARNING - "HFC-PCI: No usable DMA configuration!\n"); - return -EIO; - } - buffer = dma_alloc_coherent(&hc->pdev->dev, 0x8000, &hc->hw.dmahandle, - GFP_KERNEL); - /* We silently assume the address is okay if nonzero */ - if (!buffer) { - printk(KERN_WARNING - "HFC-PCI: Error allocating memory for FIFO!\n"); - return -ENOMEM; - } - hc->hw.fifos = buffer; - pci_write_config_dword(hc->pdev, 0x80, hc->hw.dmahandle); - hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256); - if (unlikely(!hc->hw.pci_io)) { - printk(KERN_WARNING - "HFC-PCI: Error in ioremap for PCI!\n"); - dma_free_coherent(&hc->pdev->dev, 0x8000, hc->hw.fifos, - hc->hw.dmahandle); - return -ENOMEM; - } - - printk(KERN_INFO - "HFC-PCI: defined at mem %#lx fifo %p(%pad) IRQ %d HZ %d\n", - (u_long) hc->hw.pci_io, hc->hw.fifos, - &hc->hw.dmahandle, hc->irq, HZ); - - /* enable memory mapped ports, disable busmaster */ - pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO); - hc->hw.int_m2 = 0; - disable_hwirq(hc); - hc->hw.int_m1 = 0; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - /* At this point the needed PCI config is done */ - /* fifos are still not enabled */ - timer_setup(&hc->hw.timer, hfcpci_Timer, 0); - /* default PCM master */ - test_and_set_bit(HFC_CFG_MASTER, &hc->cfg); - return 0; -} - -static void -release_card(struct hfc_pci *hc) { - u_long flags; - - spin_lock_irqsave(&hc->lock, flags); - hc->hw.int_m2 = 0; /* interrupt output off ! */ - disable_hwirq(hc); - mode_hfcpci(&hc->bch[0], 1, ISDN_P_NONE); - mode_hfcpci(&hc->bch[1], 2, ISDN_P_NONE); - if (hc->dch.timer.function != NULL) { - timer_delete(&hc->dch.timer); - hc->dch.timer.function = NULL; - } - spin_unlock_irqrestore(&hc->lock, flags); - if (hc->hw.protocol == ISDN_P_TE_S0) - l1_event(hc->dch.l1, CLOSE_CHANNEL); - if (hc->initdone) - free_irq(hc->irq, hc); - release_io_hfcpci(hc); /* must release after free_irq! */ - mISDN_unregister_device(&hc->dch.dev); - mISDN_freebchannel(&hc->bch[1]); - mISDN_freebchannel(&hc->bch[0]); - mISDN_freedchannel(&hc->dch); - pci_set_drvdata(hc->pdev, NULL); - kfree(hc); -} - -static int -setup_card(struct hfc_pci *card) -{ - int err = -EINVAL; - u_int i; - char name[MISDN_MAX_IDLEN]; - - card->dch.debug = debug; - spin_lock_init(&card->lock); - mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state); - card->dch.hw = card; - card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); - card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - card->dch.dev.D.send = hfcpci_l2l1D; - card->dch.dev.D.ctrl = hfc_dctrl; - card->dch.dev.nrbchan = 2; - for (i = 0; i < 2; i++) { - card->bch[i].nr = i + 1; - set_channelmap(i + 1, card->dch.dev.channelmap); - card->bch[i].debug = debug; - mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, poll >> 1); - card->bch[i].hw = card; - card->bch[i].ch.send = hfcpci_l2l1B; - card->bch[i].ch.ctrl = hfc_bctrl; - card->bch[i].ch.nr = i + 1; - list_add(&card->bch[i].ch.list, &card->dch.dev.bchannels); - } - err = setup_hw(card); - if (err) - goto error; - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1); - err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, name); - if (err) - goto error; - HFC_cnt++; - printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt); - return 0; -error: - mISDN_freebchannel(&card->bch[1]); - mISDN_freebchannel(&card->bch[0]); - mISDN_freedchannel(&card->dch); - kfree(card); - return err; -} - -/* private data in the PCI devices list */ -struct _hfc_map { - u_int subtype; - u_int flag; - char *name; -}; - -static const struct _hfc_map hfc_map[] = -{ - {HFC_CCD_2BD0, 0, "CCD/Billion/Asuscom 2BD0"}, - {HFC_CCD_B000, 0, "Billion B000"}, - {HFC_CCD_B006, 0, "Billion B006"}, - {HFC_CCD_B007, 0, "Billion B007"}, - {HFC_CCD_B008, 0, "Billion B008"}, - {HFC_CCD_B009, 0, "Billion B009"}, - {HFC_CCD_B00A, 0, "Billion B00A"}, - {HFC_CCD_B00B, 0, "Billion B00B"}, - {HFC_CCD_B00C, 0, "Billion B00C"}, - {HFC_CCD_B100, 0, "Seyeon B100"}, - {HFC_CCD_B700, 0, "Primux II S0 B700"}, - {HFC_CCD_B701, 0, "Primux II S0 NT B701"}, - {HFC_ABOCOM_2BD1, 0, "Abocom/Magitek 2BD1"}, - {HFC_ASUS_0675, 0, "Asuscom/Askey 675"}, - {HFC_BERKOM_TCONCEPT, 0, "German telekom T-Concept"}, - {HFC_BERKOM_A1T, 0, "German telekom A1T"}, - {HFC_ANIGMA_MC145575, 0, "Motorola MC145575"}, - {HFC_ZOLTRIX_2BD0, 0, "Zoltrix 2BD0"}, - {HFC_DIGI_DF_M_IOM2_E, 0, - "Digi International DataFire Micro V IOM2 (Europe)"}, - {HFC_DIGI_DF_M_E, 0, - "Digi International DataFire Micro V (Europe)"}, - {HFC_DIGI_DF_M_IOM2_A, 0, - "Digi International DataFire Micro V IOM2 (North America)"}, - {HFC_DIGI_DF_M_A, 0, - "Digi International DataFire Micro V (North America)"}, - {HFC_SITECOM_DC105V2, 0, "Sitecom Connectivity DC-105 ISDN TA"}, - {}, -}; - -static const struct pci_device_id hfc_ids[] = -{ - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_2BD0), - (unsigned long) &hfc_map[0] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B000), - (unsigned long) &hfc_map[1] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B006), - (unsigned long) &hfc_map[2] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B007), - (unsigned long) &hfc_map[3] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B008), - (unsigned long) &hfc_map[4] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B009), - (unsigned long) &hfc_map[5] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00A), - (unsigned long) &hfc_map[6] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00B), - (unsigned long) &hfc_map[7] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00C), - (unsigned long) &hfc_map[8] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B100), - (unsigned long) &hfc_map[9] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B700), - (unsigned long) &hfc_map[10] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B701), - (unsigned long) &hfc_map[11] }, - { PCI_VDEVICE(ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1), - (unsigned long) &hfc_map[12] }, - { PCI_VDEVICE(ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675), - (unsigned long) &hfc_map[13] }, - { PCI_VDEVICE(BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT), - (unsigned long) &hfc_map[14] }, - { PCI_VDEVICE(BERKOM, PCI_DEVICE_ID_BERKOM_A1T), - (unsigned long) &hfc_map[15] }, - { PCI_VDEVICE(ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575), - (unsigned long) &hfc_map[16] }, - { PCI_VDEVICE(ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0), - (unsigned long) &hfc_map[17] }, - { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E), - (unsigned long) &hfc_map[18] }, - { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_E), - (unsigned long) &hfc_map[19] }, - { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A), - (unsigned long) &hfc_map[20] }, - { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_A), - (unsigned long) &hfc_map[21] }, - { PCI_VDEVICE(SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2), - (unsigned long) &hfc_map[22] }, - {}, -}; - -static int -hfc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - struct hfc_pci *card; - struct _hfc_map *m = (struct _hfc_map *)ent->driver_data; - - card = kzalloc_obj(struct hfc_pci); - if (!card) { - printk(KERN_ERR "No kmem for HFC card\n"); - return err; - } - card->pdev = pdev; - card->subtype = m->subtype; - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - - printk(KERN_INFO "mISDN_hfcpci: found adapter %s at %s\n", - m->name, pci_name(pdev)); - - card->irq = pdev->irq; - pci_set_drvdata(pdev, card); - err = setup_card(card); - if (err) - pci_set_drvdata(pdev, NULL); - return err; -} - -static void -hfc_remove_pci(struct pci_dev *pdev) -{ - struct hfc_pci *card = pci_get_drvdata(pdev); - - if (card) - release_card(card); - else - if (debug) - printk(KERN_DEBUG "%s: drvdata already removed\n", - __func__); -} - - -static struct pci_driver hfc_driver = { - .name = "hfcpci", - .probe = hfc_probe, - .remove = hfc_remove_pci, - .id_table = hfc_ids, -}; - -static int -_hfcpci_softirq(struct device *dev, void *unused) -{ - struct hfc_pci *hc = dev_get_drvdata(dev); - struct bchannel *bch; - if (hc == NULL) - return 0; - - if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) { - spin_lock_irq(&hc->lock); - bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); - if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */ - main_rec_hfcpci(bch); - tx_birq(bch); - } - bch = Sel_BCS(hc, hc->hw.bswapped ? 1 : 2); - if (bch && bch->state == ISDN_P_B_RAW) { /* B2 rx&tx */ - main_rec_hfcpci(bch); - tx_birq(bch); - } - spin_unlock_irq(&hc->lock); - } - return 0; -} - -static void -hfcpci_softirq(struct timer_list *unused) -{ - WARN_ON_ONCE(driver_for_each_device(&hfc_driver.driver, NULL, NULL, - _hfcpci_softirq) != 0); - - /* if next event would be in the past ... */ - if ((s32)(hfc_jiffies + tics - jiffies) <= 0) - hfc_jiffies = jiffies + 1; - else - hfc_jiffies += tics; - mod_timer(&hfc_tl, hfc_jiffies); -} - -static int __init -HFC_init(void) -{ - int err; - - if (!poll) - poll = HFCPCI_BTRANS_THRESHOLD; - - if (poll != HFCPCI_BTRANS_THRESHOLD) { - tics = (poll * HZ) / 8000; - if (tics < 1) - tics = 1; - poll = (tics * 8000) / HZ; - if (poll > 256 || poll < 8) { - printk(KERN_ERR "%s: Wrong poll value %d not in range " - "of 8..256.\n", __func__, poll); - err = -EINVAL; - return err; - } - } - if (poll != HFCPCI_BTRANS_THRESHOLD) { - printk(KERN_INFO "%s: Using alternative poll value of %d\n", - __func__, poll); - hfc_jiffies = jiffies + tics; - mod_timer(&hfc_tl, hfc_jiffies); - } else - tics = 0; /* indicate the use of controller's timer */ - - err = pci_register_driver(&hfc_driver); - if (err) { - if (timer_pending(&hfc_tl)) - timer_delete(&hfc_tl); - } - - return err; -} - -static void __exit -HFC_cleanup(void) -{ - timer_delete_sync(&hfc_tl); - - pci_unregister_driver(&hfc_driver); -} - -module_init(HFC_init); -module_exit(HFC_cleanup); - -MODULE_DEVICE_TABLE(pci, hfc_ids); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c deleted file mode 100644 index 227babe83879..000000000000 --- a/drivers/isdn/hardware/mISDN/hfcsusb.c +++ /dev/null @@ -1,2157 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* hfcsusb.c - * mISDN driver for Colognechip HFC-S USB chip - * - * Copyright 2001 by Peter Sprenger (sprenger@moving-bytes.de) - * Copyright 2008 by Martin Bachem (info@bachem-it.com) - * - * module params - * debug=, default=0, with n=0xHHHHGGGG - * H - l1 driver flags described in hfcsusb.h - * G - common mISDN debug flags described at mISDNhw.h - * - * poll=, default 128 - * n : burst size of PH_DATA_IND at transparent rx data - * - * Revision: 0.3.3 (socket), 2008-11-05 - */ - -#include -#include -#include -#include -#include -#include "hfcsusb.h" - -static unsigned int debug; -static int poll = DEFAULT_TRANSP_BURST_SZ; - -static LIST_HEAD(HFClist); -static DEFINE_RWLOCK(HFClock); - - -MODULE_AUTHOR("Martin Bachem"); -MODULE_DESCRIPTION("mISDN driver for Colognechip HFC-S USB chip"); -MODULE_LICENSE("GPL"); -module_param(debug, uint, S_IRUGO | S_IWUSR); -module_param(poll, int, 0); - -static int hfcsusb_cnt; - -/* some function prototypes */ -static void hfcsusb_ph_command(struct hfcsusb *hw, u_char command); -static void release_hw(struct hfcsusb *hw); -static void reset_hfcsusb(struct hfcsusb *hw); -static void setPortMode(struct hfcsusb *hw); -static void hfcsusb_start_endpoint(struct hfcsusb *hw, int channel); -static void hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel); -static int hfcsusb_setup_bch(struct bchannel *bch, int protocol); -static void deactivate_bchannel(struct bchannel *bch); -static int hfcsusb_ph_info(struct hfcsusb *hw); - -/* start next background transfer for control channel */ -static void -ctrl_start_transfer(struct hfcsusb *hw) -{ - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - if (hw->ctrl_cnt) { - hw->ctrl_urb->pipe = hw->ctrl_out_pipe; - hw->ctrl_urb->setup_packet = (u_char *)&hw->ctrl_write; - hw->ctrl_urb->transfer_buffer = NULL; - hw->ctrl_urb->transfer_buffer_length = 0; - hw->ctrl_write.wIndex = - cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].hfcs_reg); - hw->ctrl_write.wValue = - cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].reg_val); - - usb_submit_urb(hw->ctrl_urb, GFP_ATOMIC); - } -} - -/* - * queue a control transfer request to write HFC-S USB - * chip register using CTRL resuest queue - */ -static int write_reg(struct hfcsusb *hw, __u8 reg, __u8 val) -{ - struct ctrl_buf *buf; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s reg(0x%02x) val(0x%02x)\n", - hw->name, __func__, reg, val); - - spin_lock(&hw->ctrl_lock); - if (hw->ctrl_cnt >= HFC_CTRL_BUFSIZE) { - spin_unlock(&hw->ctrl_lock); - return 1; - } - buf = &hw->ctrl_buff[hw->ctrl_in_idx]; - buf->hfcs_reg = reg; - buf->reg_val = val; - if (++hw->ctrl_in_idx >= HFC_CTRL_BUFSIZE) - hw->ctrl_in_idx = 0; - if (++hw->ctrl_cnt == 1) - ctrl_start_transfer(hw); - spin_unlock(&hw->ctrl_lock); - - return 0; -} - -/* control completion routine handling background control cmds */ -static void -ctrl_complete(struct urb *urb) -{ - struct hfcsusb *hw = (struct hfcsusb *) urb->context; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - urb->dev = hw->dev; - if (hw->ctrl_cnt) { - hw->ctrl_cnt--; /* decrement actual count */ - if (++hw->ctrl_out_idx >= HFC_CTRL_BUFSIZE) - hw->ctrl_out_idx = 0; /* pointer wrap */ - - ctrl_start_transfer(hw); /* start next transfer */ - } -} - -/* handle LED bits */ -static void -set_led_bit(struct hfcsusb *hw, signed short led_bits, int set_on) -{ - if (set_on) { - if (led_bits < 0) - hw->led_state &= ~abs(led_bits); - else - hw->led_state |= led_bits; - } else { - if (led_bits < 0) - hw->led_state |= abs(led_bits); - else - hw->led_state &= ~led_bits; - } -} - -/* handle LED requests */ -static void -handle_led(struct hfcsusb *hw, int event) -{ - struct hfcsusb_vdata *driver_info = (struct hfcsusb_vdata *) - hfcsusb_idtab[hw->vend_idx].driver_info; - __u8 tmpled; - - if (driver_info->led_scheme == LED_OFF) - return; - tmpled = hw->led_state; - - switch (event) { - case LED_POWER_ON: - set_led_bit(hw, driver_info->led_bits[0], 1); - set_led_bit(hw, driver_info->led_bits[1], 0); - set_led_bit(hw, driver_info->led_bits[2], 0); - set_led_bit(hw, driver_info->led_bits[3], 0); - break; - case LED_POWER_OFF: - set_led_bit(hw, driver_info->led_bits[0], 0); - set_led_bit(hw, driver_info->led_bits[1], 0); - set_led_bit(hw, driver_info->led_bits[2], 0); - set_led_bit(hw, driver_info->led_bits[3], 0); - break; - case LED_S0_ON: - set_led_bit(hw, driver_info->led_bits[1], 1); - break; - case LED_S0_OFF: - set_led_bit(hw, driver_info->led_bits[1], 0); - break; - case LED_B1_ON: - set_led_bit(hw, driver_info->led_bits[2], 1); - break; - case LED_B1_OFF: - set_led_bit(hw, driver_info->led_bits[2], 0); - break; - case LED_B2_ON: - set_led_bit(hw, driver_info->led_bits[3], 1); - break; - case LED_B2_OFF: - set_led_bit(hw, driver_info->led_bits[3], 0); - break; - } - - if (hw->led_state != tmpled) { - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s reg(0x%02x) val(x%02x)\n", - hw->name, __func__, - HFCUSB_P_DATA, hw->led_state); - - write_reg(hw, HFCUSB_P_DATA, hw->led_state); - } -} - -/* - * Layer2 -> Layer 1 Bchannel data - */ -static int -hfcusb_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hfcsusb *hw = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - u_long flags; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&hw->lock, flags); - ret = bchannel_senddata(bch, skb); - spin_unlock_irqrestore(&hw->lock, flags); - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s PH_DATA_REQ ret(%i)\n", - hw->name, __func__, ret); - if (ret > 0) - ret = 0; - return ret; - case PH_ACTIVATE_REQ: - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { - hfcsusb_start_endpoint(hw, bch->nr - 1); - ret = hfcsusb_setup_bch(bch, ch->protocol); - } else - ret = 0; - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - deactivate_bchannel(bch); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - ret = 0; - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -/* - * send full D/B channel status information - * as MPH_INFORMATION_IND - */ -static int -hfcsusb_ph_info(struct hfcsusb *hw) -{ - struct ph_info *phi; - struct dchannel *dch = &hw->dch; - int i; - - phi = kzalloc_flex(*phi, bch, dch->dev.nrbchan, GFP_ATOMIC); - if (!phi) - return -ENOMEM; - - phi->dch.ch.protocol = hw->protocol; - phi->dch.ch.Flags = dch->Flags; - phi->dch.state = dch->state; - phi->dch.num_bch = dch->dev.nrbchan; - for (i = 0; i < dch->dev.nrbchan; i++) { - phi->bch[i].protocol = hw->bch[i].ch.protocol; - phi->bch[i].Flags = hw->bch[i].Flags; - } - _queue_data(&dch->dev.D, MPH_INFORMATION_IND, MISDN_ID_ANY, - struct_size(phi, bch, dch->dev.nrbchan), phi, GFP_ATOMIC); - kfree(phi); - - return 0; -} - -/* - * Layer2 -> Layer 1 Dchannel data - */ -static int -hfcusb_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct mISDNhead *hh = mISDN_HEAD_P(skb); - struct hfcsusb *hw = dch->hw; - int ret = -EINVAL; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s: PH_DATA_REQ\n", - hw->name, __func__); - - spin_lock_irqsave(&hw->lock, flags); - ret = dchannel_senddata(dch, skb); - spin_unlock_irqrestore(&hw->lock, flags); - if (ret > 0) { - ret = 0; - queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL); - } - break; - - case PH_ACTIVATE_REQ: - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s: PH_ACTIVATE_REQ %s\n", - hw->name, __func__, - (hw->protocol == ISDN_P_NT_S0) ? "NT" : "TE"); - - if (hw->protocol == ISDN_P_NT_S0) { - ret = 0; - if (test_bit(FLG_ACTIVE, &dch->Flags)) { - _queue_data(&dch->dev.D, - PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_ATOMIC); - } else { - hfcsusb_ph_command(hw, - HFC_L1_ACTIVATE_NT); - test_and_set_bit(FLG_L2_ACTIVATED, - &dch->Flags); - } - } else { - hfcsusb_ph_command(hw, HFC_L1_ACTIVATE_TE); - ret = l1_event(dch->l1, hh->prim); - } - break; - - case PH_DEACTIVATE_REQ: - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s: PH_DEACTIVATE_REQ\n", - hw->name, __func__); - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - - if (hw->protocol == ISDN_P_NT_S0) { - struct sk_buff_head free_queue; - - __skb_queue_head_init(&free_queue); - hfcsusb_ph_command(hw, HFC_L1_DEACTIVATE_NT); - spin_lock_irqsave(&hw->lock, flags); - skb_queue_splice_init(&dch->squeue, &free_queue); - if (dch->tx_skb) { - __skb_queue_tail(&free_queue, dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - __skb_queue_tail(&free_queue, dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - spin_unlock_irqrestore(&hw->lock, flags); - __skb_queue_purge(&free_queue); -#ifdef FIXME - if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) - dchannel_sched_event(&hc->dch, D_CLEARBUSY); -#endif - ret = 0; - } else - ret = l1_event(dch->l1, hh->prim); - break; - case MPH_INFORMATION_REQ: - ret = hfcsusb_ph_info(hw); - break; - } - - return ret; -} - -/* - * Layer 1 callback function - */ -static int -hfc_l1callback(struct dchannel *dch, u_int cmd) -{ - struct hfcsusb *hw = dch->hw; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s cmd 0x%x\n", - hw->name, __func__, cmd); - - switch (cmd) { - case INFO3_P8: - case INFO3_P10: - case HW_RESET_REQ: - case HW_POWERUP_REQ: - break; - - case HW_DEACT_REQ: - skb_queue_purge(&dch->squeue); - if (dch->tx_skb) { - dev_kfree_skb(dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - dev_kfree_skb(dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: unknown cmd %x\n", - hw->name, __func__, cmd); - return -1; - } - return hfcsusb_ph_info(hw); -} - -static int -open_dchannel(struct hfcsusb *hw, struct mISDNchannel *ch, - struct channel_req *rq) -{ - int err = 0; - - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: %s: dev(%d) open addr(%i) from %p\n", - hw->name, __func__, hw->dch.dev.id, rq->adr.channel, - __builtin_return_address(0)); - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - - test_and_clear_bit(FLG_ACTIVE, &hw->dch.Flags); - test_and_clear_bit(FLG_ACTIVE, &hw->ech.Flags); - hfcsusb_start_endpoint(hw, HFC_CHAN_D); - - /* E-Channel logging */ - if (rq->adr.channel == 1) { - if (hw->fifos[HFCUSB_PCM_RX].pipe) { - hfcsusb_start_endpoint(hw, HFC_CHAN_E); - set_bit(FLG_ACTIVE, &hw->ech.Flags); - _queue_data(&hw->ech.dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - } else - return -EINVAL; - } - - if (!hw->initdone) { - hw->protocol = rq->protocol; - if (rq->protocol == ISDN_P_TE_S0) { - err = create_l1(&hw->dch, hfc_l1callback); - if (err) - return err; - } - setPortMode(hw); - ch->protocol = rq->protocol; - hw->initdone = 1; - } else { - if (rq->protocol != ch->protocol) - return -EPROTONOSUPPORT; - } - - if (((ch->protocol == ISDN_P_NT_S0) && (hw->dch.state == 3)) || - ((ch->protocol == ISDN_P_TE_S0) && (hw->dch.state == 7))) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - rq->ch = ch; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s: %s: cannot get module\n", - hw->name, __func__); - return 0; -} - -static int -open_bchannel(struct hfcsusb *hw, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s B%i\n", - hw->name, __func__, rq->adr.channel); - - bch = &hw->bch[rq->adr.channel - 1]; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s: %s:cannot get module\n", - hw->name, __func__); - return 0; -} - -static int -channel_ctrl(struct hfcsusb *hw, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s op(0x%x) channel(0x%x)\n", - hw->name, __func__, (cq->op), (cq->channel)); - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT | - MISDN_CTRL_DISCONNECT; - break; - default: - printk(KERN_WARNING "%s: %s: unknown Op %x\n", - hw->name, __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -/* - * device control function - */ -static int -hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct hfcsusb *hw = dch->hw; - struct channel_req *rq; - int err = 0; - - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: cmd:%x %p\n", - hw->name, __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if ((rq->protocol == ISDN_P_TE_S0) || - (rq->protocol == ISDN_P_NT_S0)) - err = open_dchannel(hw, ch, rq); - else - err = open_bchannel(hw, rq); - if (!err) - hw->open++; - break; - case CLOSE_CHANNEL: - hw->open--; - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG - "%s: %s: dev(%d) close from %p (open %d)\n", - hw->name, __func__, hw->dch.dev.id, - __builtin_return_address(0), hw->open); - if (!hw->open) { - hfcsusb_stop_endpoint(hw, HFC_CHAN_D); - if (hw->fifos[HFCUSB_PCM_RX].pipe) - hfcsusb_stop_endpoint(hw, HFC_CHAN_E); - handle_led(hw, LED_POWER_ON); - } - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(hw, arg); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: unknown command %x\n", - hw->name, __func__, cmd); - return -EINVAL; - } - return err; -} - -/* - * S0 TE state change event handler - */ -static void -ph_state_te(struct dchannel *dch) -{ - struct hfcsusb *hw = dch->hw; - - if (debug & DEBUG_HW) { - if (dch->state <= HFC_MAX_TE_LAYER1_STATE) - printk(KERN_DEBUG "%s: %s: %s\n", hw->name, __func__, - HFC_TE_LAYER1_STATES[dch->state]); - else - printk(KERN_DEBUG "%s: %s: TE F%d\n", - hw->name, __func__, dch->state); - } - - switch (dch->state) { - case 0: - l1_event(dch->l1, HW_RESET_IND); - break; - case 3: - l1_event(dch->l1, HW_DEACT_IND); - break; - case 5: - case 8: - l1_event(dch->l1, ANYSIGNAL); - break; - case 6: - l1_event(dch->l1, INFO2); - break; - case 7: - l1_event(dch->l1, INFO4_P8); - break; - } - if (dch->state == 7) - handle_led(hw, LED_S0_ON); - else - handle_led(hw, LED_S0_OFF); -} - -/* - * S0 NT state change event handler - */ -static void -ph_state_nt(struct dchannel *dch) -{ - struct hfcsusb *hw = dch->hw; - - if (debug & DEBUG_HW) { - if (dch->state <= HFC_MAX_NT_LAYER1_STATE) - printk(KERN_DEBUG "%s: %s: %s\n", - hw->name, __func__, - HFC_NT_LAYER1_STATES[dch->state]); - - else - printk(KERN_INFO DRIVER_NAME "%s: %s: NT G%d\n", - hw->name, __func__, dch->state); - } - - switch (dch->state) { - case (1): - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - hw->nt_timer = 0; - hw->timers &= ~NT_ACTIVATION_TIMER; - handle_led(hw, LED_S0_OFF); - break; - - case (2): - if (hw->nt_timer < 0) { - hw->nt_timer = 0; - hw->timers &= ~NT_ACTIVATION_TIMER; - hfcsusb_ph_command(dch->hw, HFC_L1_DEACTIVATE_NT); - } else { - hw->timers |= NT_ACTIVATION_TIMER; - hw->nt_timer = NT_T1_COUNT; - /* allow G2 -> G3 transition */ - write_reg(hw, HFCUSB_STATES, 2 | HFCUSB_NT_G2_G3); - } - break; - case (3): - hw->nt_timer = 0; - hw->timers &= ~NT_ACTIVATION_TIMER; - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - handle_led(hw, LED_S0_ON); - break; - case (4): - hw->nt_timer = 0; - hw->timers &= ~NT_ACTIVATION_TIMER; - break; - default: - break; - } - hfcsusb_ph_info(hw); -} - -static void -ph_state(struct dchannel *dch) -{ - struct hfcsusb *hw = dch->hw; - - if (hw->protocol == ISDN_P_NT_S0) - ph_state_nt(dch); - else if (hw->protocol == ISDN_P_TE_S0) - ph_state_te(dch); -} - -/* - * disable/enable BChannel for desired protocol - */ -static int -hfcsusb_setup_bch(struct bchannel *bch, int protocol) -{ - struct hfcsusb *hw = bch->hw; - __u8 conhdlc, sctrl, sctrl_r; - - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: protocol %x-->%x B%d\n", - hw->name, __func__, bch->state, protocol, - bch->nr); - - /* setup val for CON_HDLC */ - conhdlc = 0; - if (protocol > ISDN_P_NONE) - conhdlc = 8; /* enable FIFO */ - - switch (protocol) { - case (-1): /* used for init */ - bch->state = -1; - fallthrough; - case (ISDN_P_NONE): - if (bch->state == ISDN_P_NONE) - return 0; /* already in idle state */ - bch->state = ISDN_P_NONE; - clear_bit(FLG_HDLC, &bch->Flags); - clear_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case (ISDN_P_B_RAW): - conhdlc |= 2; - bch->state = protocol; - set_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case (ISDN_P_B_HDLC): - bch->state = protocol; - set_bit(FLG_HDLC, &bch->Flags); - break; - default: - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: prot not known %x\n", - hw->name, __func__, protocol); - return -ENOPROTOOPT; - } - - if (protocol >= ISDN_P_NONE) { - write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 0 : 2); - write_reg(hw, HFCUSB_CON_HDLC, conhdlc); - write_reg(hw, HFCUSB_INC_RES_F, 2); - write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 1 : 3); - write_reg(hw, HFCUSB_CON_HDLC, conhdlc); - write_reg(hw, HFCUSB_INC_RES_F, 2); - - sctrl = 0x40 + ((hw->protocol == ISDN_P_TE_S0) ? 0x00 : 0x04); - sctrl_r = 0x0; - if (test_bit(FLG_ACTIVE, &hw->bch[0].Flags)) { - sctrl |= 1; - sctrl_r |= 1; - } - if (test_bit(FLG_ACTIVE, &hw->bch[1].Flags)) { - sctrl |= 2; - sctrl_r |= 2; - } - write_reg(hw, HFCUSB_SCTRL, sctrl); - write_reg(hw, HFCUSB_SCTRL_R, sctrl_r); - - if (protocol > ISDN_P_NONE) - handle_led(hw, (bch->nr == 1) ? LED_B1_ON : LED_B2_ON); - else - handle_led(hw, (bch->nr == 1) ? LED_B1_OFF : - LED_B2_OFF); - } - return hfcsusb_ph_info(hw); -} - -static void -hfcsusb_ph_command(struct hfcsusb *hw, u_char command) -{ - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: %x\n", - hw->name, __func__, command); - - switch (command) { - case HFC_L1_ACTIVATE_TE: - /* force sending sending INFO1 */ - write_reg(hw, HFCUSB_STATES, 0x14); - /* start l1 activation */ - write_reg(hw, HFCUSB_STATES, 0x04); - break; - - case HFC_L1_FORCE_DEACTIVATE_TE: - write_reg(hw, HFCUSB_STATES, 0x10); - write_reg(hw, HFCUSB_STATES, 0x03); - break; - - case HFC_L1_ACTIVATE_NT: - if (hw->dch.state == 3) - _queue_data(&hw->dch.dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - else - write_reg(hw, HFCUSB_STATES, HFCUSB_ACTIVATE | - HFCUSB_DO_ACTION | HFCUSB_NT_G2_G3); - break; - - case HFC_L1_DEACTIVATE_NT: - write_reg(hw, HFCUSB_STATES, - HFCUSB_DO_ACTION); - break; - } -} - -/* - * Layer 1 B-channel hardware access - */ -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} - -/* collect data from incoming interrupt or isochron USB data */ -static void -hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len, - int finish) -{ - struct hfcsusb *hw = fifo->hw; - struct sk_buff *rx_skb = NULL; - int maxlen = 0; - int fifon = fifo->fifonum; - int i; - int hdlc = 0; - unsigned long flags; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s: fifo(%i) len(%i) " - "dch(%p) bch(%p) ech(%p)\n", - hw->name, __func__, fifon, len, - fifo->dch, fifo->bch, fifo->ech); - - if (!len) - return; - - if ((!!fifo->dch + !!fifo->bch + !!fifo->ech) != 1) { - printk(KERN_DEBUG "%s: %s: undefined channel\n", - hw->name, __func__); - return; - } - - spin_lock_irqsave(&hw->lock, flags); - if (fifo->dch) { - rx_skb = fifo->dch->rx_skb; - maxlen = fifo->dch->maxlen; - hdlc = 1; - } - if (fifo->bch) { - if (test_bit(FLG_RX_OFF, &fifo->bch->Flags)) { - fifo->bch->dropcnt += len; - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - maxlen = bchannel_get_rxbuf(fifo->bch, len); - rx_skb = fifo->bch->rx_skb; - if (maxlen < 0) { - if (rx_skb) - skb_trim(rx_skb, 0); - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - hw->name, fifo->bch->nr, len); - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - maxlen = fifo->bch->maxlen; - hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); - } - if (fifo->ech) { - rx_skb = fifo->ech->rx_skb; - maxlen = fifo->ech->maxlen; - hdlc = 1; - } - - if (fifo->dch || fifo->ech) { - if (!rx_skb) { - rx_skb = mI_alloc_skb(maxlen, GFP_ATOMIC); - if (rx_skb) { - if (fifo->dch) - fifo->dch->rx_skb = rx_skb; - if (fifo->ech) - fifo->ech->rx_skb = rx_skb; - skb_trim(rx_skb, 0); - } else { - printk(KERN_DEBUG "%s: %s: No mem for rx_skb\n", - hw->name, __func__); - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - } - /* D/E-Channel SKB range check */ - if ((rx_skb->len + len) >= MAX_DFRAME_LEN_L1) { - printk(KERN_DEBUG "%s: %s: sbk mem exceeded " - "for fifo(%d) HFCUSB_D_RX\n", - hw->name, __func__, fifon); - skb_trim(rx_skb, 0); - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - } - - skb_put_data(rx_skb, data, len); - - if (hdlc) { - /* we have a complete hdlc packet */ - if (finish) { - if ((rx_skb->len > 3) && - (!(rx_skb->data[rx_skb->len - 1]))) { - if (debug & DBG_HFC_FIFO_VERBOSE) { - printk(KERN_DEBUG "%s: %s: fifon(%i)" - " new RX len(%i): ", - hw->name, __func__, fifon, - rx_skb->len); - i = 0; - while (i < rx_skb->len) - printk("%02x ", - rx_skb->data[i++]); - printk("\n"); - } - - /* remove CRC & status */ - skb_trim(rx_skb, rx_skb->len - 3); - - if (fifo->dch) - recv_Dchannel(fifo->dch); - if (fifo->bch) - recv_Bchannel(fifo->bch, MISDN_ID_ANY, - 0); - if (fifo->ech) - recv_Echannel(fifo->ech, - &hw->dch); - } else { - if (debug & DBG_HFC_FIFO_VERBOSE) { - printk(KERN_DEBUG - "%s: CRC or minlen ERROR fifon(%i) " - "RX len(%i): ", - hw->name, fifon, rx_skb->len); - i = 0; - while (i < rx_skb->len) - printk("%02x ", - rx_skb->data[i++]); - printk("\n"); - } - skb_trim(rx_skb, 0); - } - } - } else { - /* deliver transparent data to layer2 */ - recv_Bchannel(fifo->bch, MISDN_ID_ANY, false); - } - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void -fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, - void *buf, int num_packets, int packet_size, int interval, - usb_complete_t complete, void *context) -{ - int k; - - usb_fill_bulk_urb(urb, dev, pipe, buf, packet_size * num_packets, - complete, context); - - urb->number_of_packets = num_packets; - urb->transfer_flags = URB_ISO_ASAP; - urb->actual_length = 0; - urb->interval = interval; - - for (k = 0; k < num_packets; k++) { - urb->iso_frame_desc[k].offset = packet_size * k; - urb->iso_frame_desc[k].length = packet_size; - urb->iso_frame_desc[k].actual_length = 0; - } -} - -/* receive completion routine for all ISO tx fifos */ -static void -rx_iso_complete(struct urb *urb) -{ - struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; - struct usb_fifo *fifo = context_iso_urb->owner_fifo; - struct hfcsusb *hw = fifo->hw; - int k, len, errcode, offset, num_isoc_packets, fifon, maxlen, - status, iso_status, i; - __u8 *buf; - static __u8 eof[8]; - __u8 s0_state; - unsigned long flags; - - fifon = fifo->fifonum; - status = urb->status; - - spin_lock_irqsave(&hw->lock, flags); - if (fifo->stop_gracefull) { - fifo->stop_gracefull = 0; - fifo->active = 0; - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - spin_unlock_irqrestore(&hw->lock, flags); - - /* - * ISO transfer only partially completed, - * look at individual frame status for details - */ - if (status == -EXDEV) { - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: with -EXDEV " - "urb->status %d, fifonum %d\n", - hw->name, __func__, status, fifon); - - /* clear status, so go on with ISO transfers */ - status = 0; - } - - s0_state = 0; - if (fifo->active && !status) { - num_isoc_packets = iso_packets[fifon]; - maxlen = fifo->usb_packet_maxlen; - - for (k = 0; k < num_isoc_packets; ++k) { - len = urb->iso_frame_desc[k].actual_length; - offset = urb->iso_frame_desc[k].offset; - buf = context_iso_urb->buffer + offset; - iso_status = urb->iso_frame_desc[k].status; - - if (iso_status && (debug & DBG_HFC_FIFO_VERBOSE)) { - printk(KERN_DEBUG "%s: %s: " - "ISO packet %i, status: %i\n", - hw->name, __func__, k, iso_status); - } - - /* USB data log for every D ISO in */ - if ((fifon == HFCUSB_D_RX) && - (debug & DBG_HFC_USB_VERBOSE)) { - printk(KERN_DEBUG - "%s: %s: %d (%d/%d) len(%d) ", - hw->name, __func__, urb->start_frame, - k, num_isoc_packets - 1, - len); - for (i = 0; i < len; i++) - printk("%x ", buf[i]); - printk("\n"); - } - - if (!iso_status) { - if (fifo->last_urblen != maxlen) { - /* - * save fifo fill-level threshold bits - * to use them later in TX ISO URB - * completions - */ - hw->threshold_mask = buf[1]; - - if (fifon == HFCUSB_D_RX) - s0_state = (buf[0] >> 4); - - eof[fifon] = buf[0] & 1; - if (len > 2) - hfcsusb_rx_frame(fifo, buf + 2, - len - 2, (len < maxlen) - ? eof[fifon] : 0); - } else - hfcsusb_rx_frame(fifo, buf, len, - (len < maxlen) ? - eof[fifon] : 0); - fifo->last_urblen = len; - } - } - - /* signal S0 layer1 state change */ - if ((s0_state) && (hw->initdone) && - (s0_state != hw->dch.state)) { - hw->dch.state = s0_state; - schedule_event(&hw->dch, FLG_PHCHANGE); - } - - fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, - context_iso_urb->buffer, num_isoc_packets, - fifo->usb_packet_maxlen, fifo->intervall, - (usb_complete_t)rx_iso_complete, urb->context); - errcode = usb_submit_urb(urb, GFP_ATOMIC); - if (errcode < 0) { - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: error submitting " - "ISO URB: %d\n", - hw->name, __func__, errcode); - } - } else { - if (status && (debug & DBG_HFC_URB_INFO)) - printk(KERN_DEBUG "%s: %s: rx_iso_complete : " - "urb->status %d, fifonum %d\n", - hw->name, __func__, status, fifon); - } -} - -/* receive completion routine for all interrupt rx fifos */ -static void -rx_int_complete(struct urb *urb) -{ - int len, status, i; - __u8 *buf, maxlen, fifon; - struct usb_fifo *fifo = (struct usb_fifo *) urb->context; - struct hfcsusb *hw = fifo->hw; - static __u8 eof[8]; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - if (fifo->stop_gracefull) { - fifo->stop_gracefull = 0; - fifo->active = 0; - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - spin_unlock_irqrestore(&hw->lock, flags); - - fifon = fifo->fifonum; - if ((!fifo->active) || (urb->status)) { - if (debug & DBG_HFC_URB_ERROR) - printk(KERN_DEBUG - "%s: %s: RX-Fifo %i is going down (%i)\n", - hw->name, __func__, fifon, urb->status); - - fifo->urb->interval = 0; /* cancel automatic rescheduling */ - return; - } - len = urb->actual_length; - buf = fifo->buffer; - maxlen = fifo->usb_packet_maxlen; - - /* USB data log for every D INT in */ - if ((fifon == HFCUSB_D_RX) && (debug & DBG_HFC_USB_VERBOSE)) { - printk(KERN_DEBUG "%s: %s: D RX INT len(%d) ", - hw->name, __func__, len); - for (i = 0; i < len; i++) - printk("%02x ", buf[i]); - printk("\n"); - } - - if (fifo->last_urblen != fifo->usb_packet_maxlen) { - /* the threshold mask is in the 2nd status byte */ - hw->threshold_mask = buf[1]; - - /* signal S0 layer1 state change */ - if (hw->initdone && ((buf[0] >> 4) != hw->dch.state)) { - hw->dch.state = (buf[0] >> 4); - schedule_event(&hw->dch, FLG_PHCHANGE); - } - - eof[fifon] = buf[0] & 1; - /* if we have more than the 2 status bytes -> collect data */ - if (len > 2) - hfcsusb_rx_frame(fifo, buf + 2, - urb->actual_length - 2, - (len < maxlen) ? eof[fifon] : 0); - } else { - hfcsusb_rx_frame(fifo, buf, urb->actual_length, - (len < maxlen) ? eof[fifon] : 0); - } - fifo->last_urblen = urb->actual_length; - - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: error resubmitting USB\n", - hw->name, __func__); - } -} - -/* transmit completion routine for all ISO tx fifos */ -static void -tx_iso_complete(struct urb *urb) -{ - struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; - struct usb_fifo *fifo = context_iso_urb->owner_fifo; - struct hfcsusb *hw = fifo->hw; - struct sk_buff *tx_skb; - int k, tx_offset, num_isoc_packets, sink, remain, current_len, - errcode, hdlc, i; - int *tx_idx; - int frame_complete, fifon, status, fillempty = 0; - __u8 threshbit, *p; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - if (fifo->stop_gracefull) { - fifo->stop_gracefull = 0; - fifo->active = 0; - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - - if (fifo->dch) { - tx_skb = fifo->dch->tx_skb; - tx_idx = &fifo->dch->tx_idx; - hdlc = 1; - } else if (fifo->bch) { - tx_skb = fifo->bch->tx_skb; - tx_idx = &fifo->bch->tx_idx; - hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); - if (!tx_skb && !hdlc && - test_bit(FLG_FILLEMPTY, &fifo->bch->Flags)) - fillempty = 1; - } else { - printk(KERN_DEBUG "%s: %s: neither BCH nor DCH\n", - hw->name, __func__); - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - - fifon = fifo->fifonum; - status = urb->status; - - tx_offset = 0; - - /* - * ISO transfer only partially completed, - * look at individual frame status for details - */ - if (status == -EXDEV) { - if (debug & DBG_HFC_URB_ERROR) - printk(KERN_DEBUG "%s: %s: " - "-EXDEV (%i) fifon (%d)\n", - hw->name, __func__, status, fifon); - - /* clear status, so go on with ISO transfers */ - status = 0; - } - - if (fifo->active && !status) { - /* is FifoFull-threshold set for our channel? */ - threshbit = (hw->threshold_mask & (1 << fifon)); - num_isoc_packets = iso_packets[fifon]; - - /* predict dataflow to avoid fifo overflow */ - if (fifon >= HFCUSB_D_TX) - sink = (threshbit) ? SINK_DMIN : SINK_DMAX; - else - sink = (threshbit) ? SINK_MIN : SINK_MAX; - fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, - context_iso_urb->buffer, num_isoc_packets, - fifo->usb_packet_maxlen, fifo->intervall, - (usb_complete_t)tx_iso_complete, urb->context); - memset(context_iso_urb->buffer, 0, - sizeof(context_iso_urb->buffer)); - frame_complete = 0; - - for (k = 0; k < num_isoc_packets; ++k) { - /* analyze tx success of previous ISO packets */ - if (debug & DBG_HFC_URB_ERROR) { - errcode = urb->iso_frame_desc[k].status; - if (errcode) { - printk(KERN_DEBUG "%s: %s: " - "ISO packet %i, status: %i\n", - hw->name, __func__, k, errcode); - } - } - - /* Generate next ISO Packets */ - if (tx_skb) - remain = tx_skb->len - *tx_idx; - else if (fillempty) - remain = 15; /* > not complete */ - else - remain = 0; - - if (remain > 0) { - fifo->bit_line -= sink; - current_len = (0 - fifo->bit_line) / 8; - if (current_len > 14) - current_len = 14; - if (current_len < 0) - current_len = 0; - if (remain < current_len) - current_len = remain; - - /* how much bit do we put on the line? */ - fifo->bit_line += current_len * 8; - - context_iso_urb->buffer[tx_offset] = 0; - if (current_len == remain) { - if (hdlc) { - /* signal frame completion */ - context_iso_urb-> - buffer[tx_offset] = 1; - /* add 2 byte flags and 16bit - * CRC at end of ISDN frame */ - fifo->bit_line += 32; - } - frame_complete = 1; - } - - /* copy tx data to iso-urb buffer */ - p = context_iso_urb->buffer + tx_offset + 1; - if (fillempty) { - memset(p, fifo->bch->fill[0], - current_len); - } else { - memcpy(p, (tx_skb->data + *tx_idx), - current_len); - *tx_idx += current_len; - } - urb->iso_frame_desc[k].offset = tx_offset; - urb->iso_frame_desc[k].length = current_len + 1; - - /* USB data log for every D ISO out */ - if ((fifon == HFCUSB_D_RX) && !fillempty && - (debug & DBG_HFC_USB_VERBOSE)) { - printk(KERN_DEBUG - "%s: %s (%d/%d) offs(%d) len(%d) ", - hw->name, __func__, - k, num_isoc_packets - 1, - urb->iso_frame_desc[k].offset, - urb->iso_frame_desc[k].length); - - for (i = urb->iso_frame_desc[k].offset; - i < (urb->iso_frame_desc[k].offset - + urb->iso_frame_desc[k].length); - i++) - printk("%x ", - context_iso_urb->buffer[i]); - - printk(" skb->len(%i) tx-idx(%d)\n", - tx_skb->len, *tx_idx); - } - - tx_offset += (current_len + 1); - } else { - urb->iso_frame_desc[k].offset = tx_offset++; - urb->iso_frame_desc[k].length = 1; - /* we lower data margin every msec */ - fifo->bit_line -= sink; - if (fifo->bit_line < BITLINE_INF) - fifo->bit_line = BITLINE_INF; - } - - if (frame_complete) { - frame_complete = 0; - - if (debug & DBG_HFC_FIFO_VERBOSE) { - printk(KERN_DEBUG "%s: %s: " - "fifon(%i) new TX len(%i): ", - hw->name, __func__, - fifon, tx_skb->len); - i = 0; - while (i < tx_skb->len) - printk("%02x ", - tx_skb->data[i++]); - printk("\n"); - } - - dev_consume_skb_irq(tx_skb); - tx_skb = NULL; - if (fifo->dch && get_next_dframe(fifo->dch)) - tx_skb = fifo->dch->tx_skb; - else if (fifo->bch && - get_next_bframe(fifo->bch)) - tx_skb = fifo->bch->tx_skb; - } - } - errcode = usb_submit_urb(urb, GFP_ATOMIC); - if (errcode < 0) { - if (debug & DEBUG_HW) - printk(KERN_DEBUG - "%s: %s: error submitting ISO URB: %d \n", - hw->name, __func__, errcode); - } - - /* - * abuse DChannel tx iso completion to trigger NT mode state - * changes tx_iso_complete is assumed to be called every - * fifo->intervall (ms) - */ - if ((fifon == HFCUSB_D_TX) && (hw->protocol == ISDN_P_NT_S0) - && (hw->timers & NT_ACTIVATION_TIMER)) { - if ((--hw->nt_timer) < 0) - schedule_event(&hw->dch, FLG_PHCHANGE); - } - - } else { - if (status && (debug & DBG_HFC_URB_ERROR)) - printk(KERN_DEBUG "%s: %s: urb->status %s (%i)" - "fifonum=%d\n", - hw->name, __func__, - symbolic(urb_errlist, status), status, fifon); - } - spin_unlock_irqrestore(&hw->lock, flags); -} - -/* - * allocs urbs and start isoc transfer with two pending urbs to avoid - * gaps in the transfer chain - */ -static int -start_isoc_chain(struct usb_fifo *fifo, int num_packets_per_urb, - usb_complete_t complete, int packet_size) -{ - struct hfcsusb *hw = fifo->hw; - int i, k, errcode; - - if (debug) - printk(KERN_DEBUG "%s: %s: fifo %i\n", - hw->name, __func__, fifo->fifonum); - - /* allocate Memory for Iso out Urbs */ - for (i = 0; i < 2; i++) { - if (!(fifo->iso[i].urb)) { - fifo->iso[i].urb = - usb_alloc_urb(num_packets_per_urb, GFP_KERNEL); - if (!(fifo->iso[i].urb)) { - printk(KERN_DEBUG - "%s: %s: alloc urb for fifo %i failed", - hw->name, __func__, fifo->fifonum); - continue; - } - fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo; - fifo->iso[i].indx = i; - - /* Init the first iso */ - if (ISO_BUFFER_SIZE >= - (fifo->usb_packet_maxlen * - num_packets_per_urb)) { - fill_isoc_urb(fifo->iso[i].urb, - fifo->hw->dev, fifo->pipe, - fifo->iso[i].buffer, - num_packets_per_urb, - fifo->usb_packet_maxlen, - fifo->intervall, complete, - &fifo->iso[i]); - memset(fifo->iso[i].buffer, 0, - sizeof(fifo->iso[i].buffer)); - - for (k = 0; k < num_packets_per_urb; k++) { - fifo->iso[i].urb-> - iso_frame_desc[k].offset = - k * packet_size; - fifo->iso[i].urb-> - iso_frame_desc[k].length = - packet_size; - } - } else { - printk(KERN_DEBUG - "%s: %s: ISO Buffer size to small!\n", - hw->name, __func__); - } - } - fifo->bit_line = BITLINE_INF; - - errcode = usb_submit_urb(fifo->iso[i].urb, GFP_KERNEL); - fifo->active = (errcode >= 0) ? 1 : 0; - fifo->stop_gracefull = 0; - if (errcode < 0) { - printk(KERN_DEBUG "%s: %s: %s URB nr:%d\n", - hw->name, __func__, - symbolic(urb_errlist, errcode), i); - } - } - return fifo->active; -} - -static void -stop_iso_gracefull(struct usb_fifo *fifo) -{ - struct hfcsusb *hw = fifo->hw; - int i, timeout; - u_long flags; - - for (i = 0; i < 2; i++) { - spin_lock_irqsave(&hw->lock, flags); - if (debug) - printk(KERN_DEBUG "%s: %s for fifo %i.%i\n", - hw->name, __func__, fifo->fifonum, i); - fifo->stop_gracefull = 1; - spin_unlock_irqrestore(&hw->lock, flags); - } - - for (i = 0; i < 2; i++) { - timeout = 3; - while (fifo->stop_gracefull && timeout--) - schedule_timeout_interruptible((HZ / 1000) * 16); - if (debug && fifo->stop_gracefull) - printk(KERN_DEBUG "%s: ERROR %s for fifo %i.%i\n", - hw->name, __func__, fifo->fifonum, i); - } -} - -static void -stop_int_gracefull(struct usb_fifo *fifo) -{ - struct hfcsusb *hw = fifo->hw; - int timeout; - u_long flags; - - spin_lock_irqsave(&hw->lock, flags); - if (debug) - printk(KERN_DEBUG "%s: %s for fifo %i\n", - hw->name, __func__, fifo->fifonum); - fifo->stop_gracefull = 1; - spin_unlock_irqrestore(&hw->lock, flags); - - timeout = 3; - while (fifo->stop_gracefull && timeout--) - schedule_timeout_interruptible((HZ / 1000) * 3); - if (debug && fifo->stop_gracefull) - printk(KERN_DEBUG "%s: ERROR %s for fifo %i\n", - hw->name, __func__, fifo->fifonum); -} - -/* start the interrupt transfer for the given fifo */ -static void -start_int_fifo(struct usb_fifo *fifo) -{ - struct hfcsusb *hw = fifo->hw; - int errcode; - - if (debug) - printk(KERN_DEBUG "%s: %s: INT IN fifo:%d\n", - hw->name, __func__, fifo->fifonum); - - if (!fifo->urb) { - fifo->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!fifo->urb) - return; - } - usb_fill_int_urb(fifo->urb, fifo->hw->dev, fifo->pipe, - fifo->buffer, fifo->usb_packet_maxlen, - (usb_complete_t)rx_int_complete, fifo, fifo->intervall); - fifo->active = 1; - fifo->stop_gracefull = 0; - errcode = usb_submit_urb(fifo->urb, GFP_KERNEL); - if (errcode) { - printk(KERN_DEBUG "%s: %s: submit URB: status:%i\n", - hw->name, __func__, errcode); - fifo->active = 0; - } -} - -static void -setPortMode(struct hfcsusb *hw) -{ - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s %s\n", hw->name, __func__, - (hw->protocol == ISDN_P_TE_S0) ? "TE" : "NT"); - - if (hw->protocol == ISDN_P_TE_S0) { - write_reg(hw, HFCUSB_SCTRL, 0x40); - write_reg(hw, HFCUSB_SCTRL_E, 0x00); - write_reg(hw, HFCUSB_CLKDEL, CLKDEL_TE); - write_reg(hw, HFCUSB_STATES, 3 | 0x10); - write_reg(hw, HFCUSB_STATES, 3); - } else { - write_reg(hw, HFCUSB_SCTRL, 0x44); - write_reg(hw, HFCUSB_SCTRL_E, 0x09); - write_reg(hw, HFCUSB_CLKDEL, CLKDEL_NT); - write_reg(hw, HFCUSB_STATES, 1 | 0x10); - write_reg(hw, HFCUSB_STATES, 1); - } -} - -static void -reset_hfcsusb(struct hfcsusb *hw) -{ - struct usb_fifo *fifo; - int i; - - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - /* do Chip reset */ - write_reg(hw, HFCUSB_CIRM, 8); - - /* aux = output, reset off */ - write_reg(hw, HFCUSB_CIRM, 0x10); - - /* set USB_SIZE to match the wMaxPacketSize for INT or BULK transfers */ - write_reg(hw, HFCUSB_USB_SIZE, (hw->packet_size / 8) | - ((hw->packet_size / 8) << 4)); - - /* set USB_SIZE_I to match the wMaxPacketSize for ISO transfers */ - write_reg(hw, HFCUSB_USB_SIZE_I, hw->iso_packet_size); - - /* enable PCM/GCI master mode */ - write_reg(hw, HFCUSB_MST_MODE1, 0); /* set default values */ - write_reg(hw, HFCUSB_MST_MODE0, 1); /* enable master mode */ - - /* init the fifos */ - write_reg(hw, HFCUSB_F_THRES, - (HFCUSB_TX_THRESHOLD / 8) | ((HFCUSB_RX_THRESHOLD / 8) << 4)); - - fifo = hw->fifos; - for (i = 0; i < HFCUSB_NUM_FIFOS; i++) { - write_reg(hw, HFCUSB_FIFO, i); /* select the desired fifo */ - fifo[i].max_size = - (i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN; - fifo[i].last_urblen = 0; - - /* set 2 bit for D- & E-channel */ - write_reg(hw, HFCUSB_HDLC_PAR, ((i <= HFCUSB_B2_RX) ? 0 : 2)); - - /* enable all fifos */ - if (i == HFCUSB_D_TX) - write_reg(hw, HFCUSB_CON_HDLC, - (hw->protocol == ISDN_P_NT_S0) ? 0x08 : 0x09); - else - write_reg(hw, HFCUSB_CON_HDLC, 0x08); - write_reg(hw, HFCUSB_INC_RES_F, 2); /* reset the fifo */ - } - - write_reg(hw, HFCUSB_SCTRL_R, 0); /* disable both B receivers */ - handle_led(hw, LED_POWER_ON); -} - -/* start USB data pipes dependand on device's endpoint configuration */ -static void -hfcsusb_start_endpoint(struct hfcsusb *hw, int channel) -{ - /* quick check if endpoint already running */ - if ((channel == HFC_CHAN_D) && (hw->fifos[HFCUSB_D_RX].active)) - return; - if ((channel == HFC_CHAN_B1) && (hw->fifos[HFCUSB_B1_RX].active)) - return; - if ((channel == HFC_CHAN_B2) && (hw->fifos[HFCUSB_B2_RX].active)) - return; - if ((channel == HFC_CHAN_E) && (hw->fifos[HFCUSB_PCM_RX].active)) - return; - - /* start rx endpoints using USB INT IN method */ - if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) - start_int_fifo(hw->fifos + channel * 2 + 1); - - /* start rx endpoints using USB ISO IN method */ - if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) { - switch (channel) { - case HFC_CHAN_D: - start_isoc_chain(hw->fifos + HFCUSB_D_RX, - ISOC_PACKETS_D, - (usb_complete_t)rx_iso_complete, - 16); - break; - case HFC_CHAN_E: - start_isoc_chain(hw->fifos + HFCUSB_PCM_RX, - ISOC_PACKETS_D, - (usb_complete_t)rx_iso_complete, - 16); - break; - case HFC_CHAN_B1: - start_isoc_chain(hw->fifos + HFCUSB_B1_RX, - ISOC_PACKETS_B, - (usb_complete_t)rx_iso_complete, - 16); - break; - case HFC_CHAN_B2: - start_isoc_chain(hw->fifos + HFCUSB_B2_RX, - ISOC_PACKETS_B, - (usb_complete_t)rx_iso_complete, - 16); - break; - } - } - - /* start tx endpoints using USB ISO OUT method */ - switch (channel) { - case HFC_CHAN_D: - start_isoc_chain(hw->fifos + HFCUSB_D_TX, - ISOC_PACKETS_B, - (usb_complete_t)tx_iso_complete, 1); - break; - case HFC_CHAN_B1: - start_isoc_chain(hw->fifos + HFCUSB_B1_TX, - ISOC_PACKETS_D, - (usb_complete_t)tx_iso_complete, 1); - break; - case HFC_CHAN_B2: - start_isoc_chain(hw->fifos + HFCUSB_B2_TX, - ISOC_PACKETS_B, - (usb_complete_t)tx_iso_complete, 1); - break; - } -} - -/* stop USB data pipes dependand on device's endpoint configuration */ -static void -hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel) -{ - /* quick check if endpoint currently running */ - if ((channel == HFC_CHAN_D) && (!hw->fifos[HFCUSB_D_RX].active)) - return; - if ((channel == HFC_CHAN_B1) && (!hw->fifos[HFCUSB_B1_RX].active)) - return; - if ((channel == HFC_CHAN_B2) && (!hw->fifos[HFCUSB_B2_RX].active)) - return; - if ((channel == HFC_CHAN_E) && (!hw->fifos[HFCUSB_PCM_RX].active)) - return; - - /* rx endpoints using USB INT IN method */ - if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) - stop_int_gracefull(hw->fifos + channel * 2 + 1); - - /* rx endpoints using USB ISO IN method */ - if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) - stop_iso_gracefull(hw->fifos + channel * 2 + 1); - - /* tx endpoints using USB ISO OUT method */ - if (channel != HFC_CHAN_E) - stop_iso_gracefull(hw->fifos + channel * 2); -} - - -/* Hardware Initialization */ -static int -setup_hfcsusb(struct hfcsusb *hw) -{ - void *dmabuf = kmalloc_obj(u_char); - u_char b; - int ret; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - if (!dmabuf) - return -ENOMEM; - - ret = read_reg_atomic(hw, HFCUSB_CHIP_ID, dmabuf); - - memcpy(&b, dmabuf, sizeof(u_char)); - kfree(dmabuf); - - /* check the chip id */ - if (ret != 1) { - printk(KERN_DEBUG "%s: %s: cannot read chip id\n", - hw->name, __func__); - return 1; - } - if (b != HFCUSB_CHIPID) { - printk(KERN_DEBUG "%s: %s: Invalid chip id 0x%02x\n", - hw->name, __func__, b); - return 1; - } - - /* first set the needed config, interface and alternate */ - (void) usb_set_interface(hw->dev, hw->if_used, hw->alt_used); - - hw->led_state = 0; - - /* init the background machinery for control requests */ - hw->ctrl_read.bRequestType = 0xc0; - hw->ctrl_read.bRequest = 1; - hw->ctrl_read.wLength = cpu_to_le16(1); - hw->ctrl_write.bRequestType = 0x40; - hw->ctrl_write.bRequest = 0; - hw->ctrl_write.wLength = 0; - usb_fill_control_urb(hw->ctrl_urb, hw->dev, hw->ctrl_out_pipe, - (u_char *)&hw->ctrl_write, NULL, 0, - (usb_complete_t)ctrl_complete, hw); - - reset_hfcsusb(hw); - return 0; -} - -static void -release_hw(struct hfcsusb *hw) -{ - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - /* - * stop all endpoints gracefully - * TODO: mISDN_core should generate CLOSE_CHANNEL - * signals after calling mISDN_unregister_device() - */ - hfcsusb_stop_endpoint(hw, HFC_CHAN_D); - hfcsusb_stop_endpoint(hw, HFC_CHAN_B1); - hfcsusb_stop_endpoint(hw, HFC_CHAN_B2); - if (hw->fifos[HFCUSB_PCM_RX].pipe) - hfcsusb_stop_endpoint(hw, HFC_CHAN_E); - if (hw->protocol == ISDN_P_TE_S0) - l1_event(hw->dch.l1, CLOSE_CHANNEL); - - mISDN_unregister_device(&hw->dch.dev); - mISDN_freebchannel(&hw->bch[1]); - mISDN_freebchannel(&hw->bch[0]); - mISDN_freedchannel(&hw->dch); - - if (hw->ctrl_urb) { - usb_kill_urb(hw->ctrl_urb); - usb_free_urb(hw->ctrl_urb); - hw->ctrl_urb = NULL; - } - - if (hw->intf) - usb_set_intfdata(hw->intf, NULL); - list_del(&hw->list); - kfree(hw); - hw = NULL; -} - -static void -deactivate_bchannel(struct bchannel *bch) -{ - struct hfcsusb *hw = bch->hw; - u_long flags; - - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: bch->nr(%i)\n", - hw->name, __func__, bch->nr); - - spin_lock_irqsave(&hw->lock, flags); - mISDN_clear_bchannel(bch); - spin_unlock_irqrestore(&hw->lock, flags); - hfcsusb_setup_bch(bch, ISDN_P_NONE); - hfcsusb_stop_endpoint(hw, bch->nr - 1); -} - -/* - * Layer 1 B-channel hardware access - */ -static int -hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - int ret = -EINVAL; - - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg); - - switch (cmd) { - case HW_TESTRX_RAW: - case HW_TESTRX_HDLC: - case HW_TESTRX_OFF: - ret = -EINVAL; - break; - - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - deactivate_bchannel(bch); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - printk(KERN_WARNING "%s: unknown prim(%x)\n", - __func__, cmd); - } - return ret; -} - -static int -setup_instance(struct hfcsusb *hw, struct device *parent) -{ - u_long flags; - int err, i; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - spin_lock_init(&hw->ctrl_lock); - spin_lock_init(&hw->lock); - - mISDN_initdchannel(&hw->dch, MAX_DFRAME_LEN_L1, ph_state); - hw->dch.debug = debug & 0xFFFF; - hw->dch.hw = hw; - hw->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); - hw->dch.dev.D.send = hfcusb_l2l1D; - hw->dch.dev.D.ctrl = hfc_dctrl; - - /* enable E-Channel logging */ - if (hw->fifos[HFCUSB_PCM_RX].pipe) - mISDN_initdchannel(&hw->ech, MAX_DFRAME_LEN_L1, NULL); - - hw->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - hw->dch.dev.nrbchan = 2; - for (i = 0; i < 2; i++) { - hw->bch[i].nr = i + 1; - set_channelmap(i + 1, hw->dch.dev.channelmap); - hw->bch[i].debug = debug; - mISDN_initbchannel(&hw->bch[i], MAX_DATA_MEM, poll >> 1); - hw->bch[i].hw = hw; - hw->bch[i].ch.send = hfcusb_l2l1B; - hw->bch[i].ch.ctrl = hfc_bctrl; - hw->bch[i].ch.nr = i + 1; - list_add(&hw->bch[i].ch.list, &hw->dch.dev.bchannels); - } - - hw->fifos[HFCUSB_B1_TX].bch = &hw->bch[0]; - hw->fifos[HFCUSB_B1_RX].bch = &hw->bch[0]; - hw->fifos[HFCUSB_B2_TX].bch = &hw->bch[1]; - hw->fifos[HFCUSB_B2_RX].bch = &hw->bch[1]; - hw->fifos[HFCUSB_D_TX].dch = &hw->dch; - hw->fifos[HFCUSB_D_RX].dch = &hw->dch; - hw->fifos[HFCUSB_PCM_RX].ech = &hw->ech; - hw->fifos[HFCUSB_PCM_TX].ech = &hw->ech; - - err = setup_hfcsusb(hw); - if (err) - goto out; - - snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s.%d", DRIVER_NAME, - hfcsusb_cnt + 1); - printk(KERN_INFO "%s: registered as '%s'\n", - DRIVER_NAME, hw->name); - - err = mISDN_register_device(&hw->dch.dev, parent, hw->name); - if (err) - goto out; - - hfcsusb_cnt++; - write_lock_irqsave(&HFClock, flags); - list_add_tail(&hw->list, &HFClist); - write_unlock_irqrestore(&HFClock, flags); - return 0; - -out: - mISDN_freebchannel(&hw->bch[1]); - mISDN_freebchannel(&hw->bch[0]); - mISDN_freedchannel(&hw->dch); - return err; -} - -static int -hfcsusb_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - int err; - struct hfcsusb *hw; - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_host_interface *iface = intf->cur_altsetting; - struct usb_host_interface *iface_used = NULL; - struct usb_host_endpoint *ep; - struct hfcsusb_vdata *driver_info; - int ifnum = iface->desc.bInterfaceNumber, i, idx, alt_idx, - probe_alt_setting, vend_idx, cfg_used, *vcf, attr, cfg_found, - ep_addr, cmptbl[16], small_match, iso_packet_size, packet_size, - alt_used = 0; - - vend_idx = 0xffff; - for (i = 0; hfcsusb_idtab[i].idVendor; i++) { - if ((le16_to_cpu(dev->descriptor.idVendor) - == hfcsusb_idtab[i].idVendor) && - (le16_to_cpu(dev->descriptor.idProduct) - == hfcsusb_idtab[i].idProduct)) { - vend_idx = i; - continue; - } - } - - printk(KERN_DEBUG - "%s: interface(%d) actalt(%d) minor(%d) vend_idx(%d)\n", - __func__, ifnum, iface->desc.bAlternateSetting, - intf->minor, vend_idx); - - if (vend_idx == 0xffff) { - printk(KERN_WARNING - "%s: no valid vendor found in USB descriptor\n", - __func__); - return -EIO; - } - /* if vendor and product ID is OK, start probing alternate settings */ - alt_idx = 0; - small_match = -1; - - /* default settings */ - iso_packet_size = 16; - packet_size = 64; - - while (alt_idx < intf->num_altsetting) { - iface = intf->altsetting + alt_idx; - probe_alt_setting = iface->desc.bAlternateSetting; - cfg_used = 0; - - while (validconf[cfg_used][0]) { - cfg_found = 1; - vcf = validconf[cfg_used]; - ep = iface->endpoint; - memcpy(cmptbl, vcf, 16 * sizeof(int)); - - /* check for all endpoints in this alternate setting */ - for (i = 0; i < iface->desc.bNumEndpoints; i++) { - ep_addr = ep->desc.bEndpointAddress; - - /* get endpoint base */ - idx = ((ep_addr & 0x7f) - 1) * 2; - if (idx > 15) - return -EIO; - - if (ep_addr & 0x80) - idx++; - attr = ep->desc.bmAttributes; - - if (cmptbl[idx] != EP_NOP) { - if (cmptbl[idx] == EP_NUL) - cfg_found = 0; - if (attr == USB_ENDPOINT_XFER_INT - && cmptbl[idx] == EP_INT) - cmptbl[idx] = EP_NUL; - if (attr == USB_ENDPOINT_XFER_BULK - && cmptbl[idx] == EP_BLK) - cmptbl[idx] = EP_NUL; - if (attr == USB_ENDPOINT_XFER_ISOC - && cmptbl[idx] == EP_ISO) - cmptbl[idx] = EP_NUL; - - if (attr == USB_ENDPOINT_XFER_INT && - ep->desc.bInterval < vcf[17]) { - cfg_found = 0; - } - } - ep++; - } - - for (i = 0; i < 16; i++) - if (cmptbl[i] != EP_NOP && cmptbl[i] != EP_NUL) - cfg_found = 0; - - if (cfg_found) { - if (small_match < cfg_used) { - small_match = cfg_used; - alt_used = probe_alt_setting; - iface_used = iface; - } - } - cfg_used++; - } - alt_idx++; - } /* (alt_idx < intf->num_altsetting) */ - - /* not found a valid USB Ta Endpoint config */ - if (small_match == -1) - return -EIO; - - iface = iface_used; - hw = kzalloc_obj(struct hfcsusb); - if (!hw) - return -ENOMEM; /* got no mem */ - snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s", DRIVER_NAME); - - ep = iface->endpoint; - vcf = validconf[small_match]; - - for (i = 0; i < iface->desc.bNumEndpoints; i++) { - struct usb_fifo *f; - - ep_addr = ep->desc.bEndpointAddress; - /* get endpoint base */ - idx = ((ep_addr & 0x7f) - 1) * 2; - if (ep_addr & 0x80) - idx++; - f = &hw->fifos[idx & 7]; - - /* init Endpoints */ - if (vcf[idx] == EP_NOP || vcf[idx] == EP_NUL) { - ep++; - continue; - } - switch (ep->desc.bmAttributes) { - case USB_ENDPOINT_XFER_INT: - f->pipe = usb_rcvintpipe(dev, - ep->desc.bEndpointAddress); - f->usb_transfer_mode = USB_INT; - packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); - break; - case USB_ENDPOINT_XFER_BULK: - if (ep_addr & 0x80) - f->pipe = usb_rcvbulkpipe(dev, - ep->desc.bEndpointAddress); - else - f->pipe = usb_sndbulkpipe(dev, - ep->desc.bEndpointAddress); - f->usb_transfer_mode = USB_BULK; - packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); - break; - case USB_ENDPOINT_XFER_ISOC: - if (ep_addr & 0x80) - f->pipe = usb_rcvisocpipe(dev, - ep->desc.bEndpointAddress); - else - f->pipe = usb_sndisocpipe(dev, - ep->desc.bEndpointAddress); - f->usb_transfer_mode = USB_ISOC; - iso_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); - break; - default: - f->pipe = 0; - } - - if (f->pipe) { - f->fifonum = idx & 7; - f->hw = hw; - f->usb_packet_maxlen = - le16_to_cpu(ep->desc.wMaxPacketSize); - f->intervall = ep->desc.bInterval; - } - ep++; - } - hw->dev = dev; /* save device */ - hw->if_used = ifnum; /* save used interface */ - hw->alt_used = alt_used; /* and alternate config */ - hw->ctrl_paksize = dev->descriptor.bMaxPacketSize0; /* control size */ - hw->cfg_used = vcf[16]; /* store used config */ - hw->vend_idx = vend_idx; /* store found vendor */ - hw->packet_size = packet_size; - hw->iso_packet_size = iso_packet_size; - - /* create the control pipes needed for register access */ - hw->ctrl_in_pipe = usb_rcvctrlpipe(hw->dev, 0); - hw->ctrl_out_pipe = usb_sndctrlpipe(hw->dev, 0); - - driver_info = (struct hfcsusb_vdata *) - hfcsusb_idtab[vend_idx].driver_info; - - hw->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!hw->ctrl_urb) { - pr_warn("%s: No memory for control urb\n", - driver_info->vend_name); - err = -ENOMEM; - goto err_free_hw; - } - - pr_info("%s: %s: detected \"%s\" (%s, if=%d alt=%d)\n", - hw->name, __func__, driver_info->vend_name, - conf_str[small_match], ifnum, alt_used); - - if (setup_instance(hw, dev->dev.parent)) { - err = -EIO; - goto err_free_urb; - } - - hw->intf = intf; - usb_set_intfdata(hw->intf, hw); - return 0; - -err_free_urb: - usb_free_urb(hw->ctrl_urb); -err_free_hw: - kfree(hw); - return err; -} - -/* function called when an active device is removed */ -static void -hfcsusb_disconnect(struct usb_interface *intf) -{ - struct hfcsusb *hw = usb_get_intfdata(intf); - struct hfcsusb *next; - int cnt = 0; - - printk(KERN_INFO "%s: device disconnected\n", hw->name); - - handle_led(hw, LED_POWER_OFF); - release_hw(hw); - - list_for_each_entry_safe(hw, next, &HFClist, list) - cnt++; - if (!cnt) - hfcsusb_cnt = 0; - - usb_set_intfdata(intf, NULL); -} - -static struct usb_driver hfcsusb_drv = { - .name = DRIVER_NAME, - .id_table = hfcsusb_idtab, - .probe = hfcsusb_probe, - .disconnect = hfcsusb_disconnect, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(hfcsusb_drv); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.h b/drivers/isdn/hardware/mISDN/hfcsusb.h deleted file mode 100644 index 7e2bc5068019..000000000000 --- a/drivers/isdn/hardware/mISDN/hfcsusb.h +++ /dev/null @@ -1,425 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * hfcsusb.h, HFC-S USB mISDN driver - */ - -#ifndef __HFCSUSB_H__ -#define __HFCSUSB_H__ - - -#define DRIVER_NAME "HFC-S_USB" - -#define DBG_HFC_CALL_TRACE 0x00010000 -#define DBG_HFC_FIFO_VERBOSE 0x00020000 -#define DBG_HFC_USB_VERBOSE 0x00100000 -#define DBG_HFC_URB_INFO 0x00200000 -#define DBG_HFC_URB_ERROR 0x00400000 - -#define DEFAULT_TRANSP_BURST_SZ 128 - -#define HFC_CTRL_TIMEOUT 20 /* 5ms timeout writing/reading regs */ -#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ -#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ - -/* hfcsusb Layer1 commands */ -#define HFC_L1_ACTIVATE_TE 1 -#define HFC_L1_ACTIVATE_NT 2 -#define HFC_L1_DEACTIVATE_NT 3 -#define HFC_L1_FORCE_DEACTIVATE_TE 4 - -/* cmd FLAGS in HFCUSB_STATES register */ -#define HFCUSB_LOAD_STATE 0x10 -#define HFCUSB_ACTIVATE 0x20 -#define HFCUSB_DO_ACTION 0x40 -#define HFCUSB_NT_G2_G3 0x80 - -/* timers */ -#define NT_ACTIVATION_TIMER 0x01 /* enables NT mode activation Timer */ -#define NT_T1_COUNT 10 - -#define MAX_BCH_SIZE 2048 /* allowed B-channel packet size */ - -#define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */ -#define HFCUSB_TX_THRESHOLD 96 /* threshold for fifo report bit tx */ - -#define HFCUSB_CHIP_ID 0x16 /* Chip ID register index */ -#define HFCUSB_CIRM 0x00 /* cirm register index */ -#define HFCUSB_USB_SIZE 0x07 /* int length register */ -#define HFCUSB_USB_SIZE_I 0x06 /* iso length register */ -#define HFCUSB_F_CROSS 0x0b /* bit order register */ -#define HFCUSB_CLKDEL 0x37 /* bit delay register */ -#define HFCUSB_CON_HDLC 0xfa /* channel connect register */ -#define HFCUSB_HDLC_PAR 0xfb -#define HFCUSB_SCTRL 0x31 /* S-bus control register (tx) */ -#define HFCUSB_SCTRL_E 0x32 /* same for E and special funcs */ -#define HFCUSB_SCTRL_R 0x33 /* S-bus control register (rx) */ -#define HFCUSB_F_THRES 0x0c /* threshold register */ -#define HFCUSB_FIFO 0x0f /* fifo select register */ -#define HFCUSB_F_USAGE 0x1a /* fifo usage register */ -#define HFCUSB_MST_MODE0 0x14 -#define HFCUSB_MST_MODE1 0x15 -#define HFCUSB_P_DATA 0x1f -#define HFCUSB_INC_RES_F 0x0e -#define HFCUSB_B1_SSL 0x20 -#define HFCUSB_B2_SSL 0x21 -#define HFCUSB_B1_RSL 0x24 -#define HFCUSB_B2_RSL 0x25 -#define HFCUSB_STATES 0x30 - - -#define HFCUSB_CHIPID 0x40 /* ID value of HFC-S USB */ - -/* fifo registers */ -#define HFCUSB_NUM_FIFOS 8 /* maximum number of fifos */ -#define HFCUSB_B1_TX 0 /* index for B1 transmit bulk/int */ -#define HFCUSB_B1_RX 1 /* index for B1 receive bulk/int */ -#define HFCUSB_B2_TX 2 -#define HFCUSB_B2_RX 3 -#define HFCUSB_D_TX 4 -#define HFCUSB_D_RX 5 -#define HFCUSB_PCM_TX 6 -#define HFCUSB_PCM_RX 7 - - -#define USB_INT 0 -#define USB_BULK 1 -#define USB_ISOC 2 - -#define ISOC_PACKETS_D 8 -#define ISOC_PACKETS_B 8 -#define ISO_BUFFER_SIZE 128 - -/* defines how much ISO packets are handled in one URB */ -static int iso_packets[8] = -{ ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, - ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D -}; - - -/* Fifo flow Control for TX ISO */ -#define SINK_MAX 68 -#define SINK_MIN 48 -#define SINK_DMIN 12 -#define SINK_DMAX 18 -#define BITLINE_INF (-96 * 8) - -/* HFC-S USB register access by Control-URSs */ -#define write_reg_atomic(a, b, c) \ - usb_control_msg((a)->dev, (a)->ctrl_out_pipe, 0, 0x40, (c), (b), \ - 0, 0, HFC_CTRL_TIMEOUT) -#define read_reg_atomic(a, b, c) \ - usb_control_msg((a)->dev, (a)->ctrl_in_pipe, 1, 0xC0, 0, (b), (c), \ - 1, HFC_CTRL_TIMEOUT) -#define HFC_CTRL_BUFSIZE 64 - -struct ctrl_buf { - __u8 hfcs_reg; /* register number */ - __u8 reg_val; /* value to be written (or read) */ -}; - -/* - * URB error codes - * Used to represent a list of values and their respective symbolic names - */ -struct hfcusb_symbolic_list { - const int num; - const char *name; -}; - -static struct hfcusb_symbolic_list urb_errlist[] = { - {-ENOMEM, "No memory for allocation of internal structures"}, - {-ENOSPC, "The host controller's bandwidth is already consumed"}, - {-ENOENT, "URB was canceled by unlink_urb"}, - {-EXDEV, "ISO transfer only partially completed"}, - {-EAGAIN, "Too match scheduled for the future"}, - {-ENXIO, "URB already queued"}, - {-EFBIG, "Too much ISO frames requested"}, - {-ENOSR, "Buffer error (overrun)"}, - {-EPIPE, "Specified endpoint is stalled (device not responding)"}, - {-EOVERFLOW, "Babble (bad cable?)"}, - {-EPROTO, "Bit-stuff error (bad cable?)"}, - {-EILSEQ, "CRC/Timeout"}, - {-ETIMEDOUT, "NAK (device does not respond)"}, - {-ESHUTDOWN, "Device unplugged"}, - {-1, NULL} -}; - -static inline const char * -symbolic(struct hfcusb_symbolic_list list[], const int num) -{ - int i; - for (i = 0; list[i].name != NULL; i++) - if (list[i].num == num) - return list[i].name; - return ""; -} - -/* USB descriptor need to contain one of the following EndPoint combination: */ -#define CNF_4INT3ISO 1 /* 4 INT IN, 3 ISO OUT */ -#define CNF_3INT3ISO 2 /* 3 INT IN, 3 ISO OUT */ -#define CNF_4ISO3ISO 3 /* 4 ISO IN, 3 ISO OUT */ -#define CNF_3ISO3ISO 4 /* 3 ISO IN, 3 ISO OUT */ - -#define EP_NUL 1 /* Endpoint at this position not allowed */ -#define EP_NOP 2 /* all type of endpoints allowed at this position */ -#define EP_ISO 3 /* Isochron endpoint mandatory at this position */ -#define EP_BLK 4 /* Bulk endpoint mandatory at this position */ -#define EP_INT 5 /* Interrupt endpoint mandatory at this position */ - -#define HFC_CHAN_B1 0 -#define HFC_CHAN_B2 1 -#define HFC_CHAN_D 2 -#define HFC_CHAN_E 3 - - -/* - * List of all supported endpoint configuration sets, used to find the - * best matching endpoint configuration within a device's USB descriptor. - * We need at least 3 RX endpoints, and 3 TX endpoints, either - * INT-in and ISO-out, or ISO-in and ISO-out) - * with 4 RX endpoints even E-Channel logging is possible - */ -static int -validconf[][19] = { - /* INT in, ISO out config */ - {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT, - EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, - CNF_4INT3ISO, 2, 1}, - {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL, - EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, - CNF_3INT3ISO, 2, 0}, - /* ISO in, ISO out config */ - {EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, - EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO, - CNF_4ISO3ISO, 2, 1}, - {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, - EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL, - CNF_3ISO3ISO, 2, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* EOL element */ -}; - -/* string description of chosen config */ -static char *conf_str[] = { - "4 Interrupt IN + 3 Isochron OUT", - "3 Interrupt IN + 3 Isochron OUT", - "4 Isochron IN + 3 Isochron OUT", - "3 Isochron IN + 3 Isochron OUT" -}; - - -#define LED_OFF 0 /* no LED support */ -#define LED_SCHEME1 1 /* LED standard scheme */ -#define LED_SCHEME2 2 /* not used yet... */ - -#define LED_POWER_ON 1 -#define LED_POWER_OFF 2 -#define LED_S0_ON 3 -#define LED_S0_OFF 4 -#define LED_B1_ON 5 -#define LED_B1_OFF 6 -#define LED_B1_DATA 7 -#define LED_B2_ON 8 -#define LED_B2_OFF 9 -#define LED_B2_DATA 10 - -#define LED_NORMAL 0 /* LEDs are normal */ -#define LED_INVERTED 1 /* LEDs are inverted */ - -/* time in ms to perform a Flashing LED when B-Channel has traffic */ -#define LED_TIME 250 - - - -struct hfcsusb; -struct usb_fifo; - -/* structure defining input+output fifos (interrupt/bulk mode) */ -struct iso_urb { - struct urb *urb; - __u8 buffer[ISO_BUFFER_SIZE]; /* buffer rx/tx USB URB data */ - struct usb_fifo *owner_fifo; /* pointer to owner fifo */ - __u8 indx; /* Fifos's ISO double buffer 0 or 1 ? */ -#ifdef ISO_FRAME_START_DEBUG - int start_frames[ISO_FRAME_START_RING_COUNT]; - __u8 iso_frm_strt_pos; /* index in start_frame[] */ -#endif -}; - -struct usb_fifo { - int fifonum; /* fifo index attached to this structure */ - int active; /* fifo is currently active */ - struct hfcsusb *hw; /* pointer to main structure */ - int pipe; /* address of endpoint */ - __u8 usb_packet_maxlen; /* maximum length for usb transfer */ - unsigned int max_size; /* maximum size of receive/send packet */ - __u8 intervall; /* interrupt interval */ - struct urb *urb; /* transfer structure for usb routines */ - __u8 buffer[128]; /* buffer USB INT OUT URB data */ - int bit_line; /* how much bits are in the fifo? */ - - __u8 usb_transfer_mode; /* switched between ISO and INT */ - struct iso_urb iso[2]; /* two urbs to have one always - one pending */ - - struct dchannel *dch; /* link to hfcsusb_t->dch */ - struct bchannel *bch; /* link to hfcsusb_t->bch */ - struct dchannel *ech; /* link to hfcsusb_t->ech, TODO: E-CHANNEL */ - int last_urblen; /* remember length of last packet */ - __u8 stop_gracefull; /* stops URB retransmission */ -}; - -struct hfcsusb { - struct list_head list; - struct dchannel dch; - struct bchannel bch[2]; - struct dchannel ech; /* TODO : wait for struct echannel ;) */ - - struct usb_device *dev; /* our device */ - struct usb_interface *intf; /* used interface */ - int if_used; /* used interface number */ - int alt_used; /* used alternate config */ - int cfg_used; /* configuration index used */ - int vend_idx; /* index in hfcsusb_idtab */ - int packet_size; - int iso_packet_size; - struct usb_fifo fifos[HFCUSB_NUM_FIFOS]; - - /* control pipe background handling */ - struct ctrl_buf ctrl_buff[HFC_CTRL_BUFSIZE]; - int ctrl_in_idx, ctrl_out_idx, ctrl_cnt; - struct urb *ctrl_urb; - struct usb_ctrlrequest ctrl_write; - struct usb_ctrlrequest ctrl_read; - int ctrl_paksize; - int ctrl_in_pipe, ctrl_out_pipe; - spinlock_t ctrl_lock; /* lock for ctrl */ - spinlock_t lock; - - __u8 threshold_mask; - __u8 led_state; - - __u8 protocol; - int nt_timer; - int open; - __u8 timers; - __u8 initdone; - char name[MISDN_MAX_IDLEN]; -}; - -/* private vendor specific data */ -struct hfcsusb_vdata { - __u8 led_scheme; /* led display scheme */ - signed short led_bits[8]; /* array of 8 possible LED bitmask */ - char *vend_name; /* device name */ -}; - - -#define HFC_MAX_TE_LAYER1_STATE 8 -#define HFC_MAX_NT_LAYER1_STATE 4 - -static const char *HFC_TE_LAYER1_STATES[HFC_MAX_TE_LAYER1_STATE + 1] = { - "TE F0 - Reset", - "TE F1 - Reset", - "TE F2 - Sensing", - "TE F3 - Deactivated", - "TE F4 - Awaiting signal", - "TE F5 - Identifying input", - "TE F6 - Synchronized", - "TE F7 - Activated", - "TE F8 - Lost framing", -}; - -static const char *HFC_NT_LAYER1_STATES[HFC_MAX_NT_LAYER1_STATE + 1] = { - "NT G0 - Reset", - "NT G1 - Deactive", - "NT G2 - Pending activation", - "NT G3 - Active", - "NT G4 - Pending deactivation", -}; - -/* supported devices */ -static const struct usb_device_id hfcsusb_idtab[] = { - { - USB_DEVICE(0x0959, 0x2bd0), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_OFF, {4, 0, 2, 1}, - "ISDN USB TA (Cologne Chip HFC-S USB based)"}), - }, - { - USB_DEVICE(0x0675, 0x1688), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {1, 2, 0, 0}, - "DrayTek miniVigor 128 USB ISDN TA"}), - }, - { - USB_DEVICE(0x07b0, 0x0007), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x80, -64, -32, -16}, - "Billion tiny USB ISDN TA 128"}), - }, - { - USB_DEVICE(0x0742, 0x2008), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {4, 0, 2, 1}, - "Stollmann USB TA"}), - }, - { - USB_DEVICE(0x0742, 0x2009), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {4, 0, 2, 1}, - "Aceex USB ISDN TA"}), - }, - { - USB_DEVICE(0x0742, 0x200A), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {4, 0, 2, 1}, - "OEM USB ISDN TA"}), - }, - { - USB_DEVICE(0x08e3, 0x0301), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {2, 0, 1, 4}, - "Olitec USB RNIS"}), - }, - { - USB_DEVICE(0x07fa, 0x0846), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x80, -64, -32, -16}, - "Bewan Modem RNIS USB"}), - }, - { - USB_DEVICE(0x07fa, 0x0847), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x80, -64, -32, -16}, - "Djinn Numeris USB"}), - }, - { - USB_DEVICE(0x07b0, 0x0006), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x80, -64, -32, -16}, - "Twister ISDN TA"}), - }, - { - USB_DEVICE(0x071d, 0x1005), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x02, 0, 0x01, 0x04}, - "Eicon DIVA USB 4.0"}), - }, - { - USB_DEVICE(0x0586, 0x0102), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x88, -64, -32, -16}, - "ZyXEL OMNI.NET USB II"}), - }, - { - USB_DEVICE(0x1ae7, 0x0525), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x88, -64, -32, -16}, - "X-Tensions USB ISDN TA XC-525"}), - }, - { } -}; - -MODULE_DEVICE_TABLE(usb, hfcsusb_idtab); - -#endif /* __HFCSUSB_H__ */ diff --git a/drivers/isdn/hardware/mISDN/iohelper.h b/drivers/isdn/hardware/mISDN/iohelper.h deleted file mode 100644 index c81f7aba4b57..000000000000 --- a/drivers/isdn/hardware/mISDN/iohelper.h +++ /dev/null @@ -1,96 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * iohelper.h - * helper for define functions to access ISDN hardware - * supported are memory mapped IO - * indirect port IO (one port for address, one for data) - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#ifndef _IOHELPER_H -#define _IOHELPER_H - -typedef u8 (read_reg_func)(void *hwp, u8 offset); -typedef void (write_reg_func)(void *hwp, u8 offset, u8 value); -typedef void (fifo_func)(void *hwp, u8 offset, u8 *datap, int size); - -struct _ioport { - u32 port; - u32 ale; -}; - -#define IOFUNC_IO(name, hws, ap) \ - static u8 Read##name##_IO(void *p, u8 off) { \ - struct hws *hw = p; \ - return inb(hw->ap.port + off); \ - } \ - static void Write##name##_IO(void *p, u8 off, u8 val) { \ - struct hws *hw = p; \ - outb(val, hw->ap.port + off); \ - } \ - static void ReadFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - insb(hw->ap.port + off, dp, size); \ - } \ - static void WriteFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - outsb(hw->ap.port + off, dp, size); \ - } - -#define IOFUNC_IND(name, hws, ap) \ - static u8 Read##name##_IND(void *p, u8 off) { \ - struct hws *hw = p; \ - outb(off, hw->ap.ale); \ - return inb(hw->ap.port); \ - } \ - static void Write##name##_IND(void *p, u8 off, u8 val) { \ - struct hws *hw = p; \ - outb(off, hw->ap.ale); \ - outb(val, hw->ap.port); \ - } \ - static void ReadFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - outb(off, hw->ap.ale); \ - insb(hw->ap.port, dp, size); \ - } \ - static void WriteFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - outb(off, hw->ap.ale); \ - outsb(hw->ap.port, dp, size); \ - } - -#define IOFUNC_MEMIO(name, hws, typ, adr) \ - static u8 Read##name##_MIO(void *p, u8 off) { \ - struct hws *hw = p; \ - return readb(((typ *)hw->adr) + off); \ - } \ - static void Write##name##_MIO(void *p, u8 off, u8 val) { \ - struct hws *hw = p; \ - writeb(val, ((typ *)hw->adr) + off); \ - } \ - static void ReadFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - while (size--) \ - *dp++ = readb(((typ *)hw->adr) + off); \ - } \ - static void WriteFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - while (size--) \ - writeb(*dp++, ((typ *)hw->adr) + off); \ - } - -#define ASSIGN_FUNC(typ, name, dest) do { \ - dest.read_reg = &Read##name##_##typ; \ - dest.write_reg = &Write##name##_##typ; \ - dest.read_fifo = &ReadFiFo##name##_##typ; \ - dest.write_fifo = &WriteFiFo##name##_##typ; \ - } while (0) -#define ASSIGN_FUNC_IPAC(typ, target) do { \ - ASSIGN_FUNC(typ, ISAC, target.isac); \ - ASSIGN_FUNC(typ, IPAC, target); \ - } while (0) - -#endif diff --git a/drivers/isdn/hardware/mISDN/ipac.h b/drivers/isdn/hardware/mISDN/ipac.h deleted file mode 100644 index 2f0c4978a7a5..000000000000 --- a/drivers/isdn/hardware/mISDN/ipac.h +++ /dev/null @@ -1,393 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * - * ipac.h Defines for the Infineon (former Siemens) ISDN - * chip series - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#include "iohelper.h" - -struct isac_hw { - struct dchannel dch; - u32 type; - u32 off; /* offset to isac regs */ - char *name; - spinlock_t *hwlock; /* lock HW access */ - read_reg_func *read_reg; - write_reg_func *write_reg; - fifo_func *read_fifo; - fifo_func *write_fifo; - int (*monitor)(void *, u32, u8 *, int); - void (*release)(struct isac_hw *); - int (*init)(struct isac_hw *); - int (*ctrl)(struct isac_hw *, u32, u_long); - int (*open)(struct isac_hw *, struct channel_req *); - u8 *mon_tx; - u8 *mon_rx; - int mon_txp; - int mon_txc; - int mon_rxp; - struct arcofi_msg *arcofi_list; - struct timer_list arcofitimer; - wait_queue_head_t arcofi_wait; - u8 arcofi_bc; - u8 arcofi_state; - u8 mocr; - u8 adf2; - u8 state; -}; - -struct ipac_hw; - -struct hscx_hw { - struct bchannel bch; - struct ipac_hw *ip; - u8 fifo_size; - u8 off; /* offset to ICA or ICB */ - u8 slot; - char log[64]; -}; - -struct ipac_hw { - struct isac_hw isac; - struct hscx_hw hscx[2]; - char *name; - void *hw; - spinlock_t *hwlock; /* lock HW access */ - struct module *owner; - u32 type; - read_reg_func *read_reg; - write_reg_func *write_reg; - fifo_func *read_fifo; - fifo_func *write_fifo; - void (*release)(struct ipac_hw *); - int (*init)(struct ipac_hw *); - int (*ctrl)(struct ipac_hw *, u32, u_long); - u8 conf; -}; - -#define IPAC_TYPE_ISAC 0x0010 -#define IPAC_TYPE_IPAC 0x0020 -#define IPAC_TYPE_ISACX 0x0040 -#define IPAC_TYPE_IPACX 0x0080 -#define IPAC_TYPE_HSCX 0x0100 - -#define ISAC_USE_ARCOFI 0x1000 - -/* Monitor functions */ -#define MONITOR_RX_0 0x1000 -#define MONITOR_RX_1 0x1001 -#define MONITOR_TX_0 0x2000 -#define MONITOR_TX_1 0x2001 - -/* All registers original Siemens Spec */ -/* IPAC/ISAC registers */ -#define ISAC_ISTA 0x20 -#define ISAC_MASK 0x20 -#define ISAC_CMDR 0x21 -#define ISAC_STAR 0x21 -#define ISAC_MODE 0x22 -#define ISAC_TIMR 0x23 -#define ISAC_EXIR 0x24 -#define ISAC_RBCL 0x25 -#define ISAC_RSTA 0x27 -#define ISAC_RBCH 0x2A -#define ISAC_SPCR 0x30 -#define ISAC_CIR0 0x31 -#define ISAC_CIX0 0x31 -#define ISAC_MOR0 0x32 -#define ISAC_MOX0 0x32 -#define ISAC_CIR1 0x33 -#define ISAC_CIX1 0x33 -#define ISAC_MOR1 0x34 -#define ISAC_MOX1 0x34 -#define ISAC_STCR 0x37 -#define ISAC_ADF1 0x38 -#define ISAC_ADF2 0x39 -#define ISAC_MOCR 0x3a -#define ISAC_MOSR 0x3a -#define ISAC_SQRR 0x3b -#define ISAC_SQXR 0x3b - -#define ISAC_RBCH_XAC 0x80 - -#define IPAC_D_TIN2 0x01 - -/* IPAC/HSCX */ -#define IPAC_ISTAB 0x20 /* RD */ -#define IPAC_MASKB 0x20 /* WR */ -#define IPAC_STARB 0x21 /* RD */ -#define IPAC_CMDRB 0x21 /* WR */ -#define IPAC_MODEB 0x22 /* R/W */ -#define IPAC_EXIRB 0x24 /* RD */ -#define IPAC_RBCLB 0x25 /* RD */ -#define IPAC_RAH1 0x26 /* WR */ -#define IPAC_RAH2 0x27 /* WR */ -#define IPAC_RSTAB 0x27 /* RD */ -#define IPAC_RAL1 0x28 /* R/W */ -#define IPAC_RAL2 0x29 /* WR */ -#define IPAC_RHCRB 0x29 /* RD */ -#define IPAC_XBCL 0x2A /* WR */ -#define IPAC_CCR2 0x2C /* R/W */ -#define IPAC_RBCHB 0x2D /* RD */ -#define IPAC_XBCH 0x2D /* WR */ -#define HSCX_VSTR 0x2E /* RD */ -#define IPAC_RLCR 0x2E /* WR */ -#define IPAC_CCR1 0x2F /* R/W */ -#define IPAC_TSAX 0x30 /* WR */ -#define IPAC_TSAR 0x31 /* WR */ -#define IPAC_XCCR 0x32 /* WR */ -#define IPAC_RCCR 0x33 /* WR */ - -/* IPAC_ISTAB/IPAC_MASKB bits */ -#define IPAC_B_XPR 0x10 -#define IPAC_B_RPF 0x40 -#define IPAC_B_RME 0x80 -#define IPAC_B_ON 0x2F - -/* IPAC_EXIRB bits */ -#define IPAC_B_RFS 0x04 -#define IPAC_B_RFO 0x10 -#define IPAC_B_XDU 0x40 -#define IPAC_B_XMR 0x80 - -/* IPAC special registers */ -#define IPAC_CONF 0xC0 /* R/W */ -#define IPAC_ISTA 0xC1 /* RD */ -#define IPAC_MASK 0xC1 /* WR */ -#define IPAC_ID 0xC2 /* RD */ -#define IPAC_ACFG 0xC3 /* R/W */ -#define IPAC_AOE 0xC4 /* R/W */ -#define IPAC_ARX 0xC5 /* RD */ -#define IPAC_ATX 0xC5 /* WR */ -#define IPAC_PITA1 0xC6 /* R/W */ -#define IPAC_PITA2 0xC7 /* R/W */ -#define IPAC_POTA1 0xC8 /* R/W */ -#define IPAC_POTA2 0xC9 /* R/W */ -#define IPAC_PCFG 0xCA /* R/W */ -#define IPAC_SCFG 0xCB /* R/W */ -#define IPAC_TIMR2 0xCC /* R/W */ - -/* IPAC_ISTA/_MASK bits */ -#define IPAC__EXB 0x01 -#define IPAC__ICB 0x02 -#define IPAC__EXA 0x04 -#define IPAC__ICA 0x08 -#define IPAC__EXD 0x10 -#define IPAC__ICD 0x20 -#define IPAC__INT0 0x40 -#define IPAC__INT1 0x80 -#define IPAC__ON 0xC0 - -/* HSCX ISTA/MASK bits */ -#define HSCX__EXB 0x01 -#define HSCX__EXA 0x02 -#define HSCX__ICA 0x04 - -/* ISAC/ISACX/IPAC/IPACX L1 commands */ -#define ISAC_CMD_TIM 0x0 -#define ISAC_CMD_RS 0x1 -#define ISAC_CMD_SCZ 0x4 -#define ISAC_CMD_SSZ 0x2 -#define ISAC_CMD_AR8 0x8 -#define ISAC_CMD_AR10 0x9 -#define ISAC_CMD_ARL 0xA -#define ISAC_CMD_DUI 0xF - -/* ISAC/ISACX/IPAC/IPACX L1 indications */ -#define ISAC_IND_DR 0x0 -#define ISAC_IND_RS 0x1 -#define ISAC_IND_SD 0x2 -#define ISAC_IND_DIS 0x3 -#define ISAC_IND_RSY 0x4 -#define ISAC_IND_DR6 0x5 -#define ISAC_IND_EI 0x6 -#define ISAC_IND_PU 0x7 -#define ISAC_IND_ARD 0x8 -#define ISAC_IND_TI 0xA -#define ISAC_IND_ATI 0xB -#define ISAC_IND_AI8 0xC -#define ISAC_IND_AI10 0xD -#define ISAC_IND_DID 0xF - -/* the new ISACX / IPACX */ -/* D-channel registers */ -#define ISACX_RFIFOD 0x00 /* RD */ -#define ISACX_XFIFOD 0x00 /* WR */ -#define ISACX_ISTAD 0x20 /* RD */ -#define ISACX_MASKD 0x20 /* WR */ -#define ISACX_STARD 0x21 /* RD */ -#define ISACX_CMDRD 0x21 /* WR */ -#define ISACX_MODED 0x22 /* R/W */ -#define ISACX_EXMD1 0x23 /* R/W */ -#define ISACX_TIMR1 0x24 /* R/W */ -#define ISACX_SAP1 0x25 /* WR */ -#define ISACX_SAP2 0x26 /* WR */ -#define ISACX_RBCLD 0x26 /* RD */ -#define ISACX_RBCHD 0x27 /* RD */ -#define ISACX_TEI1 0x27 /* WR */ -#define ISACX_TEI2 0x28 /* WR */ -#define ISACX_RSTAD 0x28 /* RD */ -#define ISACX_TMD 0x29 /* R/W */ -#define ISACX_CIR0 0x2E /* RD */ -#define ISACX_CIX0 0x2E /* WR */ -#define ISACX_CIR1 0x2F /* RD */ -#define ISACX_CIX1 0x2F /* WR */ - -/* Transceiver registers */ -#define ISACX_TR_CONF0 0x30 /* R/W */ -#define ISACX_TR_CONF1 0x31 /* R/W */ -#define ISACX_TR_CONF2 0x32 /* R/W */ -#define ISACX_TR_STA 0x33 /* RD */ -#define ISACX_TR_CMD 0x34 /* R/W */ -#define ISACX_SQRR1 0x35 /* RD */ -#define ISACX_SQXR1 0x35 /* WR */ -#define ISACX_SQRR2 0x36 /* RD */ -#define ISACX_SQXR2 0x36 /* WR */ -#define ISACX_SQRR3 0x37 /* RD */ -#define ISACX_SQXR3 0x37 /* WR */ -#define ISACX_ISTATR 0x38 /* RD */ -#define ISACX_MASKTR 0x39 /* R/W */ -#define ISACX_TR_MODE 0x3A /* R/W */ -#define ISACX_ACFG1 0x3C /* R/W */ -#define ISACX_ACFG2 0x3D /* R/W */ -#define ISACX_AOE 0x3E /* R/W */ -#define ISACX_ARX 0x3F /* RD */ -#define ISACX_ATX 0x3F /* WR */ - -/* IOM: Timeslot, DPS, CDA */ -#define ISACX_CDA10 0x40 /* R/W */ -#define ISACX_CDA11 0x41 /* R/W */ -#define ISACX_CDA20 0x42 /* R/W */ -#define ISACX_CDA21 0x43 /* R/W */ -#define ISACX_CDA_TSDP10 0x44 /* R/W */ -#define ISACX_CDA_TSDP11 0x45 /* R/W */ -#define ISACX_CDA_TSDP20 0x46 /* R/W */ -#define ISACX_CDA_TSDP21 0x47 /* R/W */ -#define ISACX_BCHA_TSDP_BC1 0x48 /* R/W */ -#define ISACX_BCHA_TSDP_BC2 0x49 /* R/W */ -#define ISACX_BCHB_TSDP_BC1 0x4A /* R/W */ -#define ISACX_BCHB_TSDP_BC2 0x4B /* R/W */ -#define ISACX_TR_TSDP_BC1 0x4C /* R/W */ -#define ISACX_TR_TSDP_BC2 0x4D /* R/W */ -#define ISACX_CDA1_CR 0x4E /* R/W */ -#define ISACX_CDA2_CR 0x4F /* R/W */ - -/* IOM: Contol, Sync transfer, Monitor */ -#define ISACX_TR_CR 0x50 /* R/W */ -#define ISACX_TRC_CR 0x50 /* R/W */ -#define ISACX_BCHA_CR 0x51 /* R/W */ -#define ISACX_BCHB_CR 0x52 /* R/W */ -#define ISACX_DCI_CR 0x53 /* R/W */ -#define ISACX_DCIC_CR 0x53 /* R/W */ -#define ISACX_MON_CR 0x54 /* R/W */ -#define ISACX_SDS1_CR 0x55 /* R/W */ -#define ISACX_SDS2_CR 0x56 /* R/W */ -#define ISACX_IOM_CR 0x57 /* R/W */ -#define ISACX_STI 0x58 /* RD */ -#define ISACX_ASTI 0x58 /* WR */ -#define ISACX_MSTI 0x59 /* R/W */ -#define ISACX_SDS_CONF 0x5A /* R/W */ -#define ISACX_MCDA 0x5B /* RD */ -#define ISACX_MOR 0x5C /* RD */ -#define ISACX_MOX 0x5C /* WR */ -#define ISACX_MOSR 0x5D /* RD */ -#define ISACX_MOCR 0x5E /* R/W */ -#define ISACX_MSTA 0x5F /* RD */ -#define ISACX_MCONF 0x5F /* WR */ - -/* Interrupt and general registers */ -#define ISACX_ISTA 0x60 /* RD */ -#define ISACX_MASK 0x60 /* WR */ -#define ISACX_AUXI 0x61 /* RD */ -#define ISACX_AUXM 0x61 /* WR */ -#define ISACX_MODE1 0x62 /* R/W */ -#define ISACX_MODE2 0x63 /* R/W */ -#define ISACX_ID 0x64 /* RD */ -#define ISACX_SRES 0x64 /* WR */ -#define ISACX_TIMR2 0x65 /* R/W */ - -/* Register Bits */ -/* ISACX/IPACX _ISTAD (R) and _MASKD (W) */ -#define ISACX_D_XDU 0x04 -#define ISACX_D_XMR 0x08 -#define ISACX_D_XPR 0x10 -#define ISACX_D_RFO 0x20 -#define ISACX_D_RPF 0x40 -#define ISACX_D_RME 0x80 - -/* ISACX/IPACX _ISTA (R) and _MASK (W) */ -#define ISACX__ICD 0x01 -#define ISACX__MOS 0x02 -#define ISACX__TRAN 0x04 -#define ISACX__AUX 0x08 -#define ISACX__CIC 0x10 -#define ISACX__ST 0x20 -#define IPACX__ON 0x2C -#define IPACX__ICB 0x40 -#define IPACX__ICA 0x80 - -/* ISACX/IPACX _CMDRD (W) */ -#define ISACX_CMDRD_XRES 0x01 -#define ISACX_CMDRD_XME 0x02 -#define ISACX_CMDRD_XTF 0x08 -#define ISACX_CMDRD_STI 0x10 -#define ISACX_CMDRD_RRES 0x40 -#define ISACX_CMDRD_RMC 0x80 - -/* ISACX/IPACX _RSTAD (R) */ -#define ISACX_RSTAD_TA 0x01 -#define ISACX_RSTAD_CR 0x02 -#define ISACX_RSTAD_SA0 0x04 -#define ISACX_RSTAD_SA1 0x08 -#define ISACX_RSTAD_RAB 0x10 -#define ISACX_RSTAD_CRC 0x20 -#define ISACX_RSTAD_RDO 0x40 -#define ISACX_RSTAD_VFR 0x80 - -/* ISACX/IPACX _CIR0 (R) */ -#define ISACX_CIR0_BAS 0x01 -#define ISACX_CIR0_SG 0x08 -#define ISACX_CIR0_CIC1 0x08 -#define ISACX_CIR0_CIC0 0x08 - -/* B-channel registers */ -#define IPACX_OFF_ICA 0x70 -#define IPACX_OFF_ICB 0x80 - -/* ICA: IPACX_OFF_ICA + Reg ICB: IPACX_OFF_ICB + Reg */ - -#define IPACX_ISTAB 0x00 /* RD */ -#define IPACX_MASKB 0x00 /* WR */ -#define IPACX_STARB 0x01 /* RD */ -#define IPACX_CMDRB 0x01 /* WR */ -#define IPACX_MODEB 0x02 /* R/W */ -#define IPACX_EXMB 0x03 /* R/W */ -#define IPACX_RAH1 0x05 /* WR */ -#define IPACX_RAH2 0x06 /* WR */ -#define IPACX_RBCLB 0x06 /* RD */ -#define IPACX_RBCHB 0x07 /* RD */ -#define IPACX_RAL1 0x07 /* WR */ -#define IPACX_RAL2 0x08 /* WR */ -#define IPACX_RSTAB 0x08 /* RD */ -#define IPACX_TMB 0x09 /* R/W */ -#define IPACX_RFIFOB 0x0A /* RD */ -#define IPACX_XFIFOB 0x0A /* WR */ - -/* IPACX_ISTAB / IPACX_MASKB bits */ -#define IPACX_B_XDU 0x04 -#define IPACX_B_XPR 0x10 -#define IPACX_B_RFO 0x20 -#define IPACX_B_RPF 0x40 -#define IPACX_B_RME 0x80 - -#define IPACX_B_ON 0x0B - -extern int mISDNisac_init(struct isac_hw *, void *); -extern irqreturn_t mISDNisac_irq(struct isac_hw *, u8); -extern u32 mISDNipac_init(struct ipac_hw *, void *); -extern irqreturn_t mISDNipac_irq(struct ipac_hw *, int); diff --git a/drivers/isdn/hardware/mISDN/isar.h b/drivers/isdn/hardware/mISDN/isar.h deleted file mode 100644 index 36a9fa564b17..000000000000 --- a/drivers/isdn/hardware/mISDN/isar.h +++ /dev/null @@ -1,256 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * - * isar.h ISAR (Siemens PSB 7110) specific defines - * - * Author Karsten Keil (keil@isdn4linux.de) - * - * Copyright 2009 by Karsten Keil - */ - -#include "iohelper.h" - -struct isar_hw; - -struct isar_ch { - struct bchannel bch; - struct isar_hw *is; - struct timer_list ftimer; - u8 nr; - u8 dpath; - u8 mml; - u8 state; - u8 cmd; - u8 mod; - u8 newcmd; - u8 newmod; - u8 try_mod; - u8 conmsg[16]; -}; - -struct isar_hw { - struct isar_ch ch[2]; - void *hw; - spinlock_t *hwlock; /* lock HW access */ - char *name; - struct module *owner; - read_reg_func *read_reg; - write_reg_func *write_reg; - fifo_func *read_fifo; - fifo_func *write_fifo; - int (*ctrl)(void *, u32, u_long); - void (*release)(struct isar_hw *); - int (*init)(struct isar_hw *); - int (*open)(struct isar_hw *, struct channel_req *); - int (*firmware)(struct isar_hw *, const u8 *, int); - unsigned long Flags; - int version; - u8 bstat; - u8 iis; - u8 cmsb; - u8 clsb; - u8 buf[256]; - u8 log[256]; -}; - -#define ISAR_IRQMSK 0x04 -#define ISAR_IRQSTA 0x04 -#define ISAR_IRQBIT 0x75 -#define ISAR_CTRL_H 0x61 -#define ISAR_CTRL_L 0x60 -#define ISAR_IIS 0x58 -#define ISAR_IIA 0x58 -#define ISAR_HIS 0x50 -#define ISAR_HIA 0x50 -#define ISAR_MBOX 0x4c -#define ISAR_WADR 0x4a -#define ISAR_RADR 0x48 - -#define ISAR_HIS_VNR 0x14 -#define ISAR_HIS_DKEY 0x02 -#define ISAR_HIS_FIRM 0x1e -#define ISAR_HIS_STDSP 0x08 -#define ISAR_HIS_DIAG 0x05 -#define ISAR_HIS_P0CFG 0x3c -#define ISAR_HIS_P12CFG 0x24 -#define ISAR_HIS_SARTCFG 0x25 -#define ISAR_HIS_PUMPCFG 0x26 -#define ISAR_HIS_PUMPCTRL 0x2a -#define ISAR_HIS_IOM2CFG 0x27 -#define ISAR_HIS_IOM2REQ 0x07 -#define ISAR_HIS_IOM2CTRL 0x2b -#define ISAR_HIS_BSTREQ 0x0c -#define ISAR_HIS_PSTREQ 0x0e -#define ISAR_HIS_SDATA 0x20 -#define ISAR_HIS_DPS1 0x40 -#define ISAR_HIS_DPS2 0x80 -#define SET_DPS(x) ((x << 6) & 0xc0) - -#define ISAR_IIS_MSCMSD 0x3f -#define ISAR_IIS_VNR 0x15 -#define ISAR_IIS_DKEY 0x03 -#define ISAR_IIS_FIRM 0x1f -#define ISAR_IIS_STDSP 0x09 -#define ISAR_IIS_DIAG 0x25 -#define ISAR_IIS_GSTEV 0x00 -#define ISAR_IIS_BSTEV 0x28 -#define ISAR_IIS_BSTRSP 0x2c -#define ISAR_IIS_PSTRSP 0x2e -#define ISAR_IIS_PSTEV 0x2a -#define ISAR_IIS_IOM2RSP 0x27 -#define ISAR_IIS_RDATA 0x20 -#define ISAR_IIS_INVMSG 0x3f - -#define ISAR_CTRL_SWVER 0x10 -#define ISAR_CTRL_STST 0x40 - -#define ISAR_MSG_HWVER 0x20 - -#define ISAR_DP1_USE 1 -#define ISAR_DP2_USE 2 -#define ISAR_RATE_REQ 3 - -#define PMOD_DISABLE 0 -#define PMOD_FAX 1 -#define PMOD_DATAMODEM 2 -#define PMOD_HALFDUPLEX 3 -#define PMOD_V110 4 -#define PMOD_DTMF 5 -#define PMOD_DTMF_TRANS 6 -#define PMOD_BYPASS 7 - -#define PCTRL_ORIG 0x80 -#define PV32P2_V23R 0x40 -#define PV32P2_V22A 0x20 -#define PV32P2_V22B 0x10 -#define PV32P2_V22C 0x08 -#define PV32P2_V21 0x02 -#define PV32P2_BEL 0x01 - -/* LSB MSB in ISAR doc wrong !!! Arghhh */ -#define PV32P3_AMOD 0x80 -#define PV32P3_V32B 0x02 -#define PV32P3_V23B 0x01 -#define PV32P4_48 0x11 -#define PV32P5_48 0x05 -#define PV32P4_UT48 0x11 -#define PV32P5_UT48 0x0d -#define PV32P4_96 0x11 -#define PV32P5_96 0x03 -#define PV32P4_UT96 0x11 -#define PV32P5_UT96 0x0f -#define PV32P4_B96 0x91 -#define PV32P5_B96 0x0b -#define PV32P4_UTB96 0xd1 -#define PV32P5_UTB96 0x0f -#define PV32P4_120 0xb1 -#define PV32P5_120 0x09 -#define PV32P4_UT120 0xf1 -#define PV32P5_UT120 0x0f -#define PV32P4_144 0x99 -#define PV32P5_144 0x09 -#define PV32P4_UT144 0xf9 -#define PV32P5_UT144 0x0f -#define PV32P6_CTN 0x01 -#define PV32P6_ATN 0x02 - -#define PFAXP2_CTN 0x01 -#define PFAXP2_ATN 0x04 - -#define PSEV_10MS_TIMER 0x02 -#define PSEV_CON_ON 0x18 -#define PSEV_CON_OFF 0x19 -#define PSEV_V24_OFF 0x20 -#define PSEV_CTS_ON 0x21 -#define PSEV_CTS_OFF 0x22 -#define PSEV_DCD_ON 0x23 -#define PSEV_DCD_OFF 0x24 -#define PSEV_DSR_ON 0x25 -#define PSEV_DSR_OFF 0x26 -#define PSEV_REM_RET 0xcc -#define PSEV_REM_REN 0xcd -#define PSEV_GSTN_CLR 0xd4 - -#define PSEV_RSP_READY 0xbc -#define PSEV_LINE_TX_H 0xb3 -#define PSEV_LINE_TX_B 0xb2 -#define PSEV_LINE_RX_H 0xb1 -#define PSEV_LINE_RX_B 0xb0 -#define PSEV_RSP_CONN 0xb5 -#define PSEV_RSP_DISC 0xb7 -#define PSEV_RSP_FCERR 0xb9 -#define PSEV_RSP_SILDET 0xbe -#define PSEV_RSP_SILOFF 0xab -#define PSEV_FLAGS_DET 0xba - -#define PCTRL_CMD_TDTMF 0x5a - -#define PCTRL_CMD_FTH 0xa7 -#define PCTRL_CMD_FRH 0xa5 -#define PCTRL_CMD_FTM 0xa8 -#define PCTRL_CMD_FRM 0xa6 -#define PCTRL_CMD_SILON 0xac -#define PCTRL_CMD_CONT 0xa2 -#define PCTRL_CMD_ESC 0xa4 -#define PCTRL_CMD_SILOFF 0xab -#define PCTRL_CMD_HALT 0xa9 - -#define PCTRL_LOC_RET 0xcf -#define PCTRL_LOC_REN 0xce - -#define SMODE_DISABLE 0 -#define SMODE_V14 2 -#define SMODE_HDLC 3 -#define SMODE_BINARY 4 -#define SMODE_FSK_V14 5 - -#define SCTRL_HDMC_BOTH 0x00 -#define SCTRL_HDMC_DTX 0x80 -#define SCTRL_HDMC_DRX 0x40 -#define S_P1_OVSP 0x40 -#define S_P1_SNP 0x20 -#define S_P1_EOP 0x10 -#define S_P1_EDP 0x08 -#define S_P1_NSB 0x04 -#define S_P1_CHS_8 0x03 -#define S_P1_CHS_7 0x02 -#define S_P1_CHS_6 0x01 -#define S_P1_CHS_5 0x00 - -#define S_P2_BFT_DEF 0x10 - -#define IOM_CTRL_ENA 0x80 -#define IOM_CTRL_NOPCM 0x00 -#define IOM_CTRL_ALAW 0x02 -#define IOM_CTRL_ULAW 0x04 -#define IOM_CTRL_RCV 0x01 - -#define IOM_P1_TXD 0x10 - -#define HDLC_FED 0x40 -#define HDLC_FSD 0x20 -#define HDLC_FST 0x20 -#define HDLC_ERROR 0x1c -#define HDLC_ERR_FAD 0x10 -#define HDLC_ERR_RER 0x08 -#define HDLC_ERR_CER 0x04 -#define SART_NMD 0x01 - -#define BSTAT_RDM0 0x1 -#define BSTAT_RDM1 0x2 -#define BSTAT_RDM2 0x4 -#define BSTAT_RDM3 0x8 -#define BSTEV_TBO 0x1f -#define BSTEV_RBO 0x2f - -/* FAX State Machine */ -#define STFAX_NULL 0 -#define STFAX_READY 1 -#define STFAX_LINE 2 -#define STFAX_CONT 3 -#define STFAX_ACTIV 4 -#define STFAX_ESCAPE 5 -#define STFAX_SILDET 6 - -extern u32 mISDNisar_init(struct isar_hw *, void *); -extern void mISDNisar_irq(struct isar_hw *); diff --git a/drivers/isdn/hardware/mISDN/isdnhdlc.c b/drivers/isdn/hardware/mISDN/isdnhdlc.c deleted file mode 100644 index 985367e6711d..000000000000 --- a/drivers/isdn/hardware/mISDN/isdnhdlc.c +++ /dev/null @@ -1,617 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * isdnhdlc.c -- General purpose ISDN HDLC decoder. - * - * Copyright (C) - * 2009 Karsten Keil - * 2002 Wolfgang MĂĽes - * 2001 Frode Isaksen - * 2001 Kai Germaschewski - */ - -#include -#include -#include -#include -#include "isdnhdlc.h" - -/*-------------------------------------------------------------------*/ - -MODULE_AUTHOR("Wolfgang MĂĽes , " - "Frode Isaksen , " - "Kai Germaschewski "); -MODULE_DESCRIPTION("General purpose ISDN HDLC decoder"); -MODULE_LICENSE("GPL"); - -/*-------------------------------------------------------------------*/ - -enum { - HDLC_FAST_IDLE, HDLC_GET_FLAG_B0, HDLC_GETFLAG_B1A6, HDLC_GETFLAG_B7, - HDLC_GET_DATA, HDLC_FAST_FLAG -}; - -enum { - HDLC_SEND_DATA, HDLC_SEND_CRC1, HDLC_SEND_FAST_FLAG, - HDLC_SEND_FIRST_FLAG, HDLC_SEND_CRC2, HDLC_SEND_CLOSING_FLAG, - HDLC_SEND_IDLE1, HDLC_SEND_FAST_IDLE, HDLC_SENDFLAG_B0, - HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED, HDLC_SENDFLAG_ONE -}; - -void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features) -{ - memset(hdlc, 0, sizeof(struct isdnhdlc_vars)); - hdlc->state = HDLC_GET_DATA; - if (features & HDLC_56KBIT) - hdlc->do_adapt56 = 1; - if (features & HDLC_BITREVERSE) - hdlc->do_bitreverse = 1; -} -EXPORT_SYMBOL(isdnhdlc_out_init); - -void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features) -{ - memset(hdlc, 0, sizeof(struct isdnhdlc_vars)); - if (features & HDLC_DCHANNEL) { - hdlc->dchannel = 1; - hdlc->state = HDLC_SEND_FIRST_FLAG; - } else { - hdlc->dchannel = 0; - hdlc->state = HDLC_SEND_FAST_FLAG; - hdlc->ffvalue = 0x7e; - } - hdlc->cbin = 0x7e; - if (features & HDLC_56KBIT) { - hdlc->do_adapt56 = 1; - hdlc->state = HDLC_SENDFLAG_B0; - } else - hdlc->data_bits = 8; - if (features & HDLC_BITREVERSE) - hdlc->do_bitreverse = 1; -} -EXPORT_SYMBOL(isdnhdlc_rcv_init); - -static int -check_frame(struct isdnhdlc_vars *hdlc) -{ - int status; - - if (hdlc->dstpos < 2) /* too small - framing error */ - status = -HDLC_FRAMING_ERROR; - else if (hdlc->crc != 0xf0b8) /* crc error */ - status = -HDLC_CRC_ERROR; - else { - /* remove CRC */ - hdlc->dstpos -= 2; - /* good frame */ - status = hdlc->dstpos; - } - return status; -} - -/* - isdnhdlc_decode - decodes HDLC frames from a transparent bit stream. - - The source buffer is scanned for valid HDLC frames looking for - flags (01111110) to indicate the start of a frame. If the start of - the frame is found, the bit stuffing is removed (0 after 5 1's). - When a new flag is found, the complete frame has been received - and the CRC is checked. - If a valid frame is found, the function returns the frame length - excluding the CRC with the bit HDLC_END_OF_FRAME set. - If the beginning of a valid frame is found, the function returns - the length. - If a framing error is found (too many 1s and not a flag) the function - returns the length with the bit HDLC_FRAMING_ERROR set. - If a CRC error is found the function returns the length with the - bit HDLC_CRC_ERROR set. - If the frame length exceeds the destination buffer size, the function - returns the length with the bit HDLC_LENGTH_ERROR set. - - src - source buffer - slen - source buffer length - count - number of bytes removed (decoded) from the source buffer - dst _ destination buffer - dsize - destination buffer size - returns - number of decoded bytes in the destination buffer and status - flag. -*/ -int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, int slen, - int *count, u8 *dst, int dsize) -{ - int status = 0; - - static const unsigned char fast_flag[] = { - 0x00, 0x00, 0x00, 0x20, 0x30, 0x38, 0x3c, 0x3e, 0x3f - }; - - static const unsigned char fast_flag_value[] = { - 0x00, 0x7e, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3f - }; - - static const unsigned char fast_abort[] = { - 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff - }; - -#define handle_fast_flag(h) \ - do { \ - if (h->cbin == fast_flag[h->bit_shift]) { \ - h->ffvalue = fast_flag_value[h->bit_shift]; \ - h->state = HDLC_FAST_FLAG; \ - h->ffbit_shift = h->bit_shift; \ - h->bit_shift = 1; \ - } else { \ - h->state = HDLC_GET_DATA; \ - h->data_received = 0; \ - } \ - } while (0) - -#define handle_abort(h) \ - do { \ - h->shift_reg = fast_abort[h->ffbit_shift - 1]; \ - h->hdlc_bits1 = h->ffbit_shift - 2; \ - if (h->hdlc_bits1 < 0) \ - h->hdlc_bits1 = 0; \ - h->data_bits = h->ffbit_shift - 1; \ - h->state = HDLC_GET_DATA; \ - h->data_received = 0; \ - } while (0) - - *count = slen; - - while (slen > 0) { - if (hdlc->bit_shift == 0) { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - hdlc->cbin = bitrev8(*src++); - else - hdlc->cbin = *src++; - slen--; - hdlc->bit_shift = 8; - if (hdlc->do_adapt56) - hdlc->bit_shift--; - } - - switch (hdlc->state) { - case STOPPED: - return 0; - case HDLC_FAST_IDLE: - if (hdlc->cbin == 0xff) { - hdlc->bit_shift = 0; - break; - } - hdlc->state = HDLC_GET_FLAG_B0; - hdlc->hdlc_bits1 = 0; - hdlc->bit_shift = 8; - break; - case HDLC_GET_FLAG_B0: - if (!(hdlc->cbin & 0x80)) { - hdlc->state = HDLC_GETFLAG_B1A6; - hdlc->hdlc_bits1 = 0; - } else { - if ((!hdlc->do_adapt56) && - (++hdlc->hdlc_bits1 >= 8) && - (hdlc->bit_shift == 1)) - hdlc->state = HDLC_FAST_IDLE; - } - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_GETFLAG_B1A6: - if (hdlc->cbin & 0x80) { - hdlc->hdlc_bits1++; - if (hdlc->hdlc_bits1 == 6) - hdlc->state = HDLC_GETFLAG_B7; - } else - hdlc->hdlc_bits1 = 0; - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_GETFLAG_B7: - if (hdlc->cbin & 0x80) { - hdlc->state = HDLC_GET_FLAG_B0; - } else { - hdlc->state = HDLC_GET_DATA; - hdlc->crc = 0xffff; - hdlc->shift_reg = 0; - hdlc->hdlc_bits1 = 0; - hdlc->data_bits = 0; - hdlc->data_received = 0; - } - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_GET_DATA: - if (hdlc->cbin & 0x80) { - hdlc->hdlc_bits1++; - switch (hdlc->hdlc_bits1) { - case 6: - break; - case 7: - if (hdlc->data_received) - /* bad frame */ - status = -HDLC_FRAMING_ERROR; - if (!hdlc->do_adapt56) { - if (hdlc->cbin == fast_abort - [hdlc->bit_shift + 1]) { - hdlc->state = - HDLC_FAST_IDLE; - hdlc->bit_shift = 1; - break; - } - } else - hdlc->state = HDLC_GET_FLAG_B0; - break; - default: - hdlc->shift_reg >>= 1; - hdlc->shift_reg |= 0x80; - hdlc->data_bits++; - break; - } - } else { - switch (hdlc->hdlc_bits1) { - case 5: - break; - case 6: - if (hdlc->data_received) - status = check_frame(hdlc); - hdlc->crc = 0xffff; - hdlc->shift_reg = 0; - hdlc->data_bits = 0; - if (!hdlc->do_adapt56) - handle_fast_flag(hdlc); - else { - hdlc->state = HDLC_GET_DATA; - hdlc->data_received = 0; - } - break; - default: - hdlc->shift_reg >>= 1; - hdlc->data_bits++; - break; - } - hdlc->hdlc_bits1 = 0; - } - if (status) { - hdlc->dstpos = 0; - *count -= slen; - hdlc->cbin <<= 1; - hdlc->bit_shift--; - return status; - } - if (hdlc->data_bits == 8) { - hdlc->data_bits = 0; - hdlc->data_received = 1; - hdlc->crc = crc_ccitt_byte(hdlc->crc, - hdlc->shift_reg); - - /* good byte received */ - if (hdlc->dstpos < dsize) - dst[hdlc->dstpos++] = hdlc->shift_reg; - else { - /* frame too long */ - status = -HDLC_LENGTH_ERROR; - hdlc->dstpos = 0; - } - } - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_FAST_FLAG: - if (hdlc->cbin == hdlc->ffvalue) { - hdlc->bit_shift = 0; - break; - } else { - if (hdlc->cbin == 0xff) { - hdlc->state = HDLC_FAST_IDLE; - hdlc->bit_shift = 0; - } else if (hdlc->ffbit_shift == 8) { - hdlc->state = HDLC_GETFLAG_B7; - break; - } else - handle_abort(hdlc); - } - break; - default: - break; - } - } - *count -= slen; - return 0; -} -EXPORT_SYMBOL(isdnhdlc_decode); -/* - isdnhdlc_encode - encodes HDLC frames to a transparent bit stream. - - The bit stream starts with a beginning flag (01111110). After - that each byte is added to the bit stream with bit stuffing added - (0 after 5 1's). - When the last byte has been removed from the source buffer, the - CRC (2 bytes is added) and the frame terminates with the ending flag. - For the dchannel, the idle character (all 1's) is also added at the end. - If this function is called with empty source buffer (slen=0), flags or - idle character will be generated. - - src - source buffer - slen - source buffer length - count - number of bytes removed (encoded) from source buffer - dst _ destination buffer - dsize - destination buffer size - returns - number of encoded bytes in the destination buffer -*/ -int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, u16 slen, - int *count, u8 *dst, int dsize) -{ - static const unsigned char xfast_flag_value[] = { - 0x7e, 0x3f, 0x9f, 0xcf, 0xe7, 0xf3, 0xf9, 0xfc, 0x7e - }; - - int len = 0; - - *count = slen; - - /* special handling for one byte frames */ - if ((slen == 1) && (hdlc->state == HDLC_SEND_FAST_FLAG)) - hdlc->state = HDLC_SENDFLAG_ONE; - while (dsize > 0) { - if (hdlc->bit_shift == 0) { - if (slen && !hdlc->do_closing) { - hdlc->shift_reg = *src++; - slen--; - if (slen == 0) - /* closing sequence, CRC + flag(s) */ - hdlc->do_closing = 1; - hdlc->bit_shift = 8; - } else { - if (hdlc->state == HDLC_SEND_DATA) { - if (hdlc->data_received) { - hdlc->state = HDLC_SEND_CRC1; - hdlc->crc ^= 0xffff; - hdlc->bit_shift = 8; - hdlc->shift_reg = - hdlc->crc & 0xff; - } else if (!hdlc->do_adapt56) - hdlc->state = - HDLC_SEND_FAST_FLAG; - else - hdlc->state = - HDLC_SENDFLAG_B0; - } - - } - } - - switch (hdlc->state) { - case STOPPED: - while (dsize--) - *dst++ = 0xff; - return dsize; - case HDLC_SEND_FAST_FLAG: - hdlc->do_closing = 0; - if (slen == 0) { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - *dst++ = bitrev8(hdlc->ffvalue); - else - *dst++ = hdlc->ffvalue; - len++; - dsize--; - break; - } - fallthrough; - case HDLC_SENDFLAG_ONE: - if (hdlc->bit_shift == 8) { - hdlc->cbin = hdlc->ffvalue >> - (8 - hdlc->data_bits); - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - hdlc->data_received = 1; - } - break; - case HDLC_SENDFLAG_B0: - hdlc->do_closing = 0; - hdlc->cbin <<= 1; - hdlc->data_bits++; - hdlc->hdlc_bits1 = 0; - hdlc->state = HDLC_SENDFLAG_B1A6; - break; - case HDLC_SENDFLAG_B1A6: - hdlc->cbin <<= 1; - hdlc->data_bits++; - hdlc->cbin++; - if (++hdlc->hdlc_bits1 == 6) - hdlc->state = HDLC_SENDFLAG_B7; - break; - case HDLC_SENDFLAG_B7: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (slen == 0) { - hdlc->state = HDLC_SENDFLAG_B0; - break; - } - if (hdlc->bit_shift == 8) { - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - hdlc->data_received = 1; - } - break; - case HDLC_SEND_FIRST_FLAG: - hdlc->data_received = 1; - if (hdlc->data_bits == 8) { - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - break; - } - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->shift_reg & 0x01) - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - if (hdlc->bit_shift == 0) { - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - } - break; - case HDLC_SEND_DATA: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->bit_shift == 8) - hdlc->crc = crc_ccitt_byte(hdlc->crc, - hdlc->shift_reg); - if (hdlc->shift_reg & 0x01) { - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - break; - case HDLC_SEND_CRC1: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->shift_reg & 0x01) { - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - if (hdlc->bit_shift == 0) { - hdlc->shift_reg = (hdlc->crc >> 8); - hdlc->state = HDLC_SEND_CRC2; - hdlc->bit_shift = 8; - } - break; - case HDLC_SEND_CRC2: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->shift_reg & 0x01) { - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - if (hdlc->bit_shift == 0) { - hdlc->shift_reg = 0x7e; - hdlc->state = HDLC_SEND_CLOSING_FLAG; - hdlc->bit_shift = 8; - } - break; - case HDLC_SEND_CLOSING_FLAG: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->shift_reg & 0x01) - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - if (hdlc->bit_shift == 0) { - hdlc->ffvalue = - xfast_flag_value[hdlc->data_bits]; - if (hdlc->dchannel) { - hdlc->ffvalue = 0x7e; - hdlc->state = HDLC_SEND_IDLE1; - hdlc->bit_shift = 8-hdlc->data_bits; - if (hdlc->bit_shift == 0) - hdlc->state = - HDLC_SEND_FAST_IDLE; - } else { - if (!hdlc->do_adapt56) { - hdlc->state = - HDLC_SEND_FAST_FLAG; - hdlc->data_received = 0; - } else { - hdlc->state = HDLC_SENDFLAG_B0; - hdlc->data_received = 0; - } - /* Finished this frame, send flags */ - if (dsize > 1) - dsize = 1; - } - } - break; - case HDLC_SEND_IDLE1: - hdlc->do_closing = 0; - hdlc->cbin <<= 1; - hdlc->cbin++; - hdlc->data_bits++; - hdlc->bit_shift--; - if (hdlc->bit_shift == 0) { - hdlc->state = HDLC_SEND_FAST_IDLE; - hdlc->bit_shift = 0; - } - break; - case HDLC_SEND_FAST_IDLE: - hdlc->do_closing = 0; - hdlc->cbin = 0xff; - hdlc->data_bits = 8; - if (hdlc->bit_shift == 8) { - hdlc->cbin = 0x7e; - hdlc->state = HDLC_SEND_FIRST_FLAG; - } else { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - *dst++ = bitrev8(hdlc->cbin); - else - *dst++ = hdlc->cbin; - hdlc->bit_shift = 0; - hdlc->data_bits = 0; - len++; - dsize = 0; - } - break; - default: - break; - } - if (hdlc->do_adapt56) { - if (hdlc->data_bits == 7) { - hdlc->cbin <<= 1; - hdlc->cbin++; - hdlc->data_bits++; - } - } - if (hdlc->data_bits == 8) { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - *dst++ = bitrev8(hdlc->cbin); - else - *dst++ = hdlc->cbin; - hdlc->data_bits = 0; - len++; - dsize--; - } - } - *count -= slen; - - return len; -} -EXPORT_SYMBOL(isdnhdlc_encode); diff --git a/drivers/isdn/hardware/mISDN/isdnhdlc.h b/drivers/isdn/hardware/mISDN/isdnhdlc.h deleted file mode 100644 index fe2c1279c139..000000000000 --- a/drivers/isdn/hardware/mISDN/isdnhdlc.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * hdlc.h -- General purpose ISDN HDLC decoder. - * - * Implementation of a HDLC decoder/encoder in software. - * Necessary because some ISDN devices don't have HDLC - * controllers. - * - * Copyright (C) - * 2009 Karsten Keil - * 2002 Wolfgang MĂĽes - * 2001 Frode Isaksen - * 2001 Kai Germaschewski - */ - -#ifndef __ISDNHDLC_H__ -#define __ISDNHDLC_H__ - -struct isdnhdlc_vars { - int bit_shift; - int hdlc_bits1; - int data_bits; - int ffbit_shift; /* encoding only */ - int state; - int dstpos; - - u16 crc; - - u8 cbin; - u8 shift_reg; - u8 ffvalue; - - /* set if transferring data */ - u32 data_received:1; - /* set if D channel (send idle instead of flags) */ - u32 dchannel:1; - /* set if 56K adaptation */ - u32 do_adapt56:1; - /* set if in closing phase (need to send CRC + flag) */ - u32 do_closing:1; - /* set if data is bitreverse */ - u32 do_bitreverse:1; -}; - -/* Feature Flags */ -#define HDLC_56KBIT 0x01 -#define HDLC_DCHANNEL 0x02 -#define HDLC_BITREVERSE 0x04 - -/* - The return value from isdnhdlc_decode is - the frame length, 0 if no complete frame was decoded, - or a negative error number -*/ -#define HDLC_FRAMING_ERROR 1 -#define HDLC_CRC_ERROR 2 -#define HDLC_LENGTH_ERROR 3 - -extern void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features); - -extern int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, - int slen, int *count, u8 *dst, int dsize); - -extern void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features); - -extern int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, - u16 slen, int *count, u8 *dst, int dsize); - -#endif /* __ISDNHDLC_H__ */ diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c deleted file mode 100644 index aaa639ad5526..000000000000 --- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c +++ /dev/null @@ -1,1168 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * mISDNinfineon.c - * Support for cards based on following Infineon ISDN chipsets - * - ISAC + HSCX - * - IPAC and IPAC-X - * - ISAC-SX + HSCX - * - * Supported cards: - * - Dialogic Diva 2.0 - * - Dialogic Diva 2.0U - * - Dialogic Diva 2.01 - * - Dialogic Diva 2.02 - * - Sedlbauer Speedwin - * - HST Saphir3 - * - Develo (former ELSA) Microlink PCI (Quickstep 1000) - * - Develo (former ELSA) Quickstep 3000 - * - Berkom Scitel BRIX Quadro - * - Dr.Neuhaus (Sagem) Niccy - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include "ipac.h" - -#define INFINEON_REV "1.0" - -static int inf_cnt; -static u32 debug; -static u32 irqloops = 4; - -enum inf_types { - INF_NONE, - INF_DIVA20, - INF_DIVA20U, - INF_DIVA201, - INF_DIVA202, - INF_SPEEDWIN, - INF_SAPHIR3, - INF_QS1000, - INF_QS3000, - INF_NICCY, - INF_SCT_1, - INF_SCT_2, - INF_SCT_3, - INF_SCT_4, - INF_GAZEL_R685, - INF_GAZEL_R753 -}; - -enum addr_mode { - AM_NONE = 0, - AM_IO, - AM_MEMIO, - AM_IND_IO, -}; - -struct inf_cinfo { - enum inf_types typ; - const char *full; - const char *name; - enum addr_mode cfg_mode; - enum addr_mode addr_mode; - u8 cfg_bar; - u8 addr_bar; - void *irqfunc; -}; - -struct _ioaddr { - enum addr_mode mode; - union { - void __iomem *p; - struct _ioport io; - } a; -}; - -struct _iohandle { - enum addr_mode mode; - resource_size_t size; - resource_size_t start; - void __iomem *p; -}; - -struct inf_hw { - struct list_head list; - struct pci_dev *pdev; - const struct inf_cinfo *ci; - char name[MISDN_MAX_IDLEN]; - u32 irq; - u32 irqcnt; - struct _iohandle cfg; - struct _iohandle addr; - struct _ioaddr isac; - struct _ioaddr hscx; - spinlock_t lock; /* HW access lock */ - struct ipac_hw ipac; - struct inf_hw *sc[3]; /* slave cards */ -}; - - -#define PCI_SUBVENDOR_HST_SAPHIR3 0x52 -#define PCI_SUBVENDOR_SEDLBAUER_PCI 0x53 -#define PCI_SUB_ID_SEDLBAUER 0x01 - -static struct pci_device_id infineon_ids[] = { - { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20), INF_DIVA20 }, - { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20_U), INF_DIVA20U }, - { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA201), INF_DIVA201 }, - { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA202), INF_DIVA202 }, - { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, - PCI_SUBVENDOR_SEDLBAUER_PCI, PCI_SUB_ID_SEDLBAUER, 0, 0, - INF_SPEEDWIN }, - { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, - PCI_SUBVENDOR_HST_SAPHIR3, PCI_SUB_ID_SEDLBAUER, 0, 0, INF_SAPHIR3 }, - { PCI_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_MICROLINK), INF_QS1000 }, - { PCI_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_QS3000), INF_QS3000 }, - { PCI_VDEVICE(SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY), INF_NICCY }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO, 0, 0, - INF_SCT_1 }, - { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R685), INF_GAZEL_R685 }, - { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R753), INF_GAZEL_R753 }, - { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_DJINN_ITOO), INF_GAZEL_R753 }, - { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_OLITEC), INF_GAZEL_R753 }, - { } -}; -MODULE_DEVICE_TABLE(pci, infineon_ids); - -/* PCI interface specific defines */ -/* Diva 2.0/2.0U */ -#define DIVA_HSCX_PORT 0x00 -#define DIVA_HSCX_ALE 0x04 -#define DIVA_ISAC_PORT 0x08 -#define DIVA_ISAC_ALE 0x0C -#define DIVA_PCI_CTRL 0x10 - -/* DIVA_PCI_CTRL bits */ -#define DIVA_IRQ_BIT 0x01 -#define DIVA_RESET_BIT 0x08 -#define DIVA_EEPROM_CLK 0x40 -#define DIVA_LED_A 0x10 -#define DIVA_LED_B 0x20 -#define DIVA_IRQ_CLR 0x80 - -/* Diva 2.01/2.02 */ -/* Siemens PITA */ -#define PITA_ICR_REG 0x00 -#define PITA_INT0_STATUS 0x02 - -#define PITA_MISC_REG 0x1c -#define PITA_PARA_SOFTRESET 0x01000000 -#define PITA_SER_SOFTRESET 0x02000000 -#define PITA_PARA_MPX_MODE 0x04000000 -#define PITA_INT0_ENABLE 0x00020000 - -/* TIGER 100 Registers */ -#define TIGER_RESET_ADDR 0x00 -#define TIGER_EXTERN_RESET 0x01 -#define TIGER_AUX_CTRL 0x02 -#define TIGER_AUX_DATA 0x03 -#define TIGER_AUX_IRQMASK 0x05 -#define TIGER_AUX_STATUS 0x07 - -/* Tiger AUX BITs */ -#define TIGER_IOMASK 0xdd /* 1 and 5 are inputs */ -#define TIGER_IRQ_BIT 0x02 - -#define TIGER_IPAC_ALE 0xC0 -#define TIGER_IPAC_PORT 0xC8 - -/* ELSA (now Develo) PCI cards */ -#define ELSA_IRQ_ADDR 0x4c -#define ELSA_IRQ_MASK 0x04 -#define QS1000_IRQ_OFF 0x01 -#define QS3000_IRQ_OFF 0x03 -#define QS1000_IRQ_ON 0x41 -#define QS3000_IRQ_ON 0x43 - -/* Dr Neuhaus/Sagem Niccy */ -#define NICCY_ISAC_PORT 0x00 -#define NICCY_HSCX_PORT 0x01 -#define NICCY_ISAC_ALE 0x02 -#define NICCY_HSCX_ALE 0x03 - -#define NICCY_IRQ_CTRL_REG 0x38 -#define NICCY_IRQ_ENABLE 0x001f00 -#define NICCY_IRQ_DISABLE 0xff0000 -#define NICCY_IRQ_BIT 0x800000 - - -/* Scitel PLX */ -#define SCT_PLX_IRQ_ADDR 0x4c -#define SCT_PLX_RESET_ADDR 0x50 -#define SCT_PLX_IRQ_ENABLE 0x41 -#define SCT_PLX_RESET_BIT 0x04 - -/* Gazel */ -#define GAZEL_IPAC_DATA_PORT 0x04 -/* Gazel PLX */ -#define GAZEL_CNTRL 0x50 -#define GAZEL_RESET 0x04 -#define GAZEL_RESET_9050 0x40000000 -#define GAZEL_INCSR 0x4C -#define GAZEL_ISAC_EN 0x08 -#define GAZEL_INT_ISAC 0x20 -#define GAZEL_HSCX_EN 0x01 -#define GAZEL_INT_HSCX 0x04 -#define GAZEL_PCI_EN 0x40 -#define GAZEL_IPAC_EN 0x03 - - -static LIST_HEAD(Cards); -static DEFINE_RWLOCK(card_lock); /* protect Cards */ - -static void -_set_debug(struct inf_hw *card) -{ - card->ipac.isac.dch.debug = debug; - card->ipac.hscx[0].bch.debug = debug; - card->ipac.hscx[1].bch.debug = debug; -} - -static int -set_debug(const char *val, const struct kernel_param *kp) -{ - int ret; - struct inf_hw *card; - - ret = param_set_uint(val, kp); - if (!ret) { - read_lock(&card_lock); - list_for_each_entry(card, &Cards, list) - _set_debug(card); - read_unlock(&card_lock); - } - return ret; -} - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for cards based on Infineon ISDN chipsets"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(INFINEON_REV); -module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "infineon debug mask"); -module_param(irqloops, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(irqloops, "infineon maximal irqloops (default 4)"); - -/* Interface functions */ - -IOFUNC_IO(ISAC, inf_hw, isac.a.io) -IOFUNC_IO(IPAC, inf_hw, hscx.a.io) -IOFUNC_IND(ISAC, inf_hw, isac.a.io) -IOFUNC_IND(IPAC, inf_hw, hscx.a.io) -IOFUNC_MEMIO(ISAC, inf_hw, u32, isac.a.p) -IOFUNC_MEMIO(IPAC, inf_hw, u32, hscx.a.p) - -static irqreturn_t -diva_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u8 val; - - spin_lock(&hw->lock); - val = inb((u32)hw->cfg.start + DIVA_PCI_CTRL); - if (!(val & DIVA_IRQ_BIT)) { /* for us or shared ? */ - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -diva20x_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u8 val; - - spin_lock(&hw->lock); - val = readb(hw->cfg.p); - if (!(val & PITA_INT0_STATUS)) { /* for us or shared ? */ - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - writeb(PITA_INT0_STATUS, hw->cfg.p); /* ACK PITA INT0 */ - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -tiger_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u8 val; - - spin_lock(&hw->lock); - val = inb((u32)hw->cfg.start + TIGER_AUX_STATUS); - if (val & TIGER_IRQ_BIT) { /* for us or shared ? */ - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -elsa_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u8 val; - - spin_lock(&hw->lock); - val = inb((u32)hw->cfg.start + ELSA_IRQ_ADDR); - if (!(val & ELSA_IRQ_MASK)) { - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -niccy_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u32 val; - - spin_lock(&hw->lock); - val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - if (!(val & NICCY_IRQ_BIT)) { /* for us or shared ? */ - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -gazel_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - irqreturn_t ret; - - spin_lock(&hw->lock); - ret = mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return ret; -} - -static irqreturn_t -ipac_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u8 val; - - spin_lock(&hw->lock); - val = hw->ipac.read_reg(hw, IPAC_ISTA); - if (!(val & 0x3f)) { - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static void -enable_hwirq(struct inf_hw *hw) -{ - u16 w; - u32 val; - - switch (hw->ci->typ) { - case INF_DIVA201: - case INF_DIVA202: - writel(PITA_INT0_ENABLE, hw->cfg.p); - break; - case INF_SPEEDWIN: - case INF_SAPHIR3: - outb(TIGER_IRQ_BIT, (u32)hw->cfg.start + TIGER_AUX_IRQMASK); - break; - case INF_QS1000: - outb(QS1000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR); - break; - case INF_QS3000: - outb(QS3000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR); - break; - case INF_NICCY: - val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - val |= NICCY_IRQ_ENABLE; - outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - break; - case INF_SCT_1: - w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); - w |= SCT_PLX_IRQ_ENABLE; - outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); - break; - case INF_GAZEL_R685: - outb(GAZEL_ISAC_EN + GAZEL_HSCX_EN + GAZEL_PCI_EN, - (u32)hw->cfg.start + GAZEL_INCSR); - break; - case INF_GAZEL_R753: - outb(GAZEL_IPAC_EN + GAZEL_PCI_EN, - (u32)hw->cfg.start + GAZEL_INCSR); - break; - default: - break; - } -} - -static void -disable_hwirq(struct inf_hw *hw) -{ - u16 w; - u32 val; - - switch (hw->ci->typ) { - case INF_DIVA201: - case INF_DIVA202: - writel(0, hw->cfg.p); - break; - case INF_SPEEDWIN: - case INF_SAPHIR3: - outb(0, (u32)hw->cfg.start + TIGER_AUX_IRQMASK); - break; - case INF_QS1000: - outb(QS1000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR); - break; - case INF_QS3000: - outb(QS3000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR); - break; - case INF_NICCY: - val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - val &= NICCY_IRQ_DISABLE; - outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - break; - case INF_SCT_1: - w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); - w &= (~SCT_PLX_IRQ_ENABLE); - outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); - break; - case INF_GAZEL_R685: - case INF_GAZEL_R753: - outb(0, (u32)hw->cfg.start + GAZEL_INCSR); - break; - default: - break; - } -} - -static void -ipac_chip_reset(struct inf_hw *hw) -{ - hw->ipac.write_reg(hw, IPAC_POTA2, 0x20); - mdelay(5); - hw->ipac.write_reg(hw, IPAC_POTA2, 0x00); - mdelay(5); - hw->ipac.write_reg(hw, IPAC_CONF, hw->ipac.conf); - hw->ipac.write_reg(hw, IPAC_MASK, 0xc0); -} - -static void -reset_inf(struct inf_hw *hw) -{ - u16 w; - u32 val; - - if (debug & DEBUG_HW) - pr_notice("%s: resetting card\n", hw->name); - switch (hw->ci->typ) { - case INF_DIVA20: - case INF_DIVA20U: - outb(0, (u32)hw->cfg.start + DIVA_PCI_CTRL); - mdelay(10); - outb(DIVA_RESET_BIT, (u32)hw->cfg.start + DIVA_PCI_CTRL); - mdelay(10); - /* Workaround PCI9060 */ - outb(9, (u32)hw->cfg.start + 0x69); - outb(DIVA_RESET_BIT | DIVA_LED_A, - (u32)hw->cfg.start + DIVA_PCI_CTRL); - break; - case INF_DIVA201: - writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE, - hw->cfg.p + PITA_MISC_REG); - mdelay(1); - writel(PITA_PARA_MPX_MODE, hw->cfg.p + PITA_MISC_REG); - mdelay(10); - break; - case INF_DIVA202: - writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE, - hw->cfg.p + PITA_MISC_REG); - mdelay(1); - writel(PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET, - hw->cfg.p + PITA_MISC_REG); - mdelay(10); - break; - case INF_SPEEDWIN: - case INF_SAPHIR3: - ipac_chip_reset(hw); - hw->ipac.write_reg(hw, IPAC_ACFG, 0xff); - hw->ipac.write_reg(hw, IPAC_AOE, 0x00); - hw->ipac.write_reg(hw, IPAC_PCFG, 0x12); - break; - case INF_QS1000: - case INF_QS3000: - ipac_chip_reset(hw); - hw->ipac.write_reg(hw, IPAC_ACFG, 0x00); - hw->ipac.write_reg(hw, IPAC_AOE, 0x3c); - hw->ipac.write_reg(hw, IPAC_ATX, 0xff); - break; - case INF_NICCY: - break; - case INF_SCT_1: - w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR); - w &= (~SCT_PLX_RESET_BIT); - outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); - mdelay(10); - w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR); - w |= SCT_PLX_RESET_BIT; - outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); - mdelay(10); - break; - case INF_GAZEL_R685: - val = inl((u32)hw->cfg.start + GAZEL_CNTRL); - val |= (GAZEL_RESET_9050 + GAZEL_RESET); - outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); - val &= ~(GAZEL_RESET_9050 + GAZEL_RESET); - mdelay(4); - outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); - mdelay(10); - hw->ipac.isac.adf2 = 0x87; - hw->ipac.hscx[0].slot = 0x1f; - hw->ipac.hscx[1].slot = 0x23; - break; - case INF_GAZEL_R753: - val = inl((u32)hw->cfg.start + GAZEL_CNTRL); - val |= (GAZEL_RESET_9050 + GAZEL_RESET); - outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); - val &= ~(GAZEL_RESET_9050 + GAZEL_RESET); - mdelay(4); - outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); - mdelay(10); - ipac_chip_reset(hw); - hw->ipac.write_reg(hw, IPAC_ACFG, 0xff); - hw->ipac.write_reg(hw, IPAC_AOE, 0x00); - hw->ipac.conf = 0x01; /* IOM off */ - break; - default: - return; - } - enable_hwirq(hw); -} - -static int -inf_ctrl(struct inf_hw *hw, u32 cmd, u_long arg) -{ - int ret = 0; - - switch (cmd) { - case HW_RESET_REQ: - reset_inf(hw); - break; - default: - pr_info("%s: %s unknown command %x %lx\n", - hw->name, __func__, cmd, arg); - ret = -EINVAL; - break; - } - return ret; -} - -static int -init_irq(struct inf_hw *hw) -{ - int ret, cnt = 3; - u_long flags; - - if (!hw->ci->irqfunc) - return -EINVAL; - ret = request_irq(hw->irq, hw->ci->irqfunc, IRQF_SHARED, hw->name, hw); - if (ret) { - pr_info("%s: couldn't get interrupt %d\n", hw->name, hw->irq); - return ret; - } - while (cnt--) { - spin_lock_irqsave(&hw->lock, flags); - reset_inf(hw); - ret = hw->ipac.init(&hw->ipac); - if (ret) { - spin_unlock_irqrestore(&hw->lock, flags); - pr_info("%s: ISAC init failed with %d\n", - hw->name, ret); - break; - } - spin_unlock_irqrestore(&hw->lock, flags); - msleep_interruptible(10); - if (debug & DEBUG_HW) - pr_notice("%s: IRQ %d count %d\n", hw->name, - hw->irq, hw->irqcnt); - if (!hw->irqcnt) { - pr_info("%s: IRQ(%d) got no requests during init %d\n", - hw->name, hw->irq, 3 - cnt); - } else - return 0; - } - free_irq(hw->irq, hw); - return -EIO; -} - -static void -release_io(struct inf_hw *hw) -{ - if (hw->cfg.mode) { - if (hw->cfg.mode == AM_MEMIO) { - release_mem_region(hw->cfg.start, hw->cfg.size); - if (hw->cfg.p) - iounmap(hw->cfg.p); - } else - release_region(hw->cfg.start, hw->cfg.size); - hw->cfg.mode = AM_NONE; - } - if (hw->addr.mode) { - if (hw->addr.mode == AM_MEMIO) { - release_mem_region(hw->addr.start, hw->addr.size); - if (hw->addr.p) - iounmap(hw->addr.p); - } else - release_region(hw->addr.start, hw->addr.size); - hw->addr.mode = AM_NONE; - } -} - -static int -setup_io(struct inf_hw *hw) -{ - int err = 0; - - if (hw->ci->cfg_mode) { - hw->cfg.start = pci_resource_start(hw->pdev, hw->ci->cfg_bar); - hw->cfg.size = pci_resource_len(hw->pdev, hw->ci->cfg_bar); - if (hw->ci->cfg_mode == AM_MEMIO) { - if (!request_mem_region(hw->cfg.start, hw->cfg.size, - hw->name)) - err = -EBUSY; - } else { - if (!request_region(hw->cfg.start, hw->cfg.size, - hw->name)) - err = -EBUSY; - } - if (err) { - pr_info("mISDN: %s config port %lx (%lu bytes)" - "already in use\n", hw->name, - (ulong)hw->cfg.start, (ulong)hw->cfg.size); - return err; - } - hw->cfg.mode = hw->ci->cfg_mode; - if (hw->ci->cfg_mode == AM_MEMIO) { - hw->cfg.p = ioremap(hw->cfg.start, hw->cfg.size); - if (!hw->cfg.p) - return -ENOMEM; - } - if (debug & DEBUG_HW) - pr_notice("%s: IO cfg %lx (%lu bytes) mode%d\n", - hw->name, (ulong)hw->cfg.start, - (ulong)hw->cfg.size, hw->ci->cfg_mode); - - } - if (hw->ci->addr_mode) { - hw->addr.start = pci_resource_start(hw->pdev, hw->ci->addr_bar); - hw->addr.size = pci_resource_len(hw->pdev, hw->ci->addr_bar); - if (hw->ci->addr_mode == AM_MEMIO) { - if (!request_mem_region(hw->addr.start, hw->addr.size, - hw->name)) - err = -EBUSY; - } else { - if (!request_region(hw->addr.start, hw->addr.size, - hw->name)) - err = -EBUSY; - } - if (err) { - pr_info("mISDN: %s address port %lx (%lu bytes)" - "already in use\n", hw->name, - (ulong)hw->addr.start, (ulong)hw->addr.size); - return err; - } - hw->addr.mode = hw->ci->addr_mode; - if (hw->ci->addr_mode == AM_MEMIO) { - hw->addr.p = ioremap(hw->addr.start, hw->addr.size); - if (!hw->addr.p) - return -ENOMEM; - } - if (debug & DEBUG_HW) - pr_notice("%s: IO addr %lx (%lu bytes) mode%d\n", - hw->name, (ulong)hw->addr.start, - (ulong)hw->addr.size, hw->ci->addr_mode); - - } - - switch (hw->ci->typ) { - case INF_DIVA20: - case INF_DIVA20U: - hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; - hw->isac.mode = hw->cfg.mode; - hw->isac.a.io.ale = (u32)hw->cfg.start + DIVA_ISAC_ALE; - hw->isac.a.io.port = (u32)hw->cfg.start + DIVA_ISAC_PORT; - hw->hscx.mode = hw->cfg.mode; - hw->hscx.a.io.ale = (u32)hw->cfg.start + DIVA_HSCX_ALE; - hw->hscx.a.io.port = (u32)hw->cfg.start + DIVA_HSCX_PORT; - break; - case INF_DIVA201: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.mode = hw->addr.mode; - hw->isac.a.p = hw->addr.p; - hw->hscx.mode = hw->addr.mode; - hw->hscx.a.p = hw->addr.p; - break; - case INF_DIVA202: - hw->ipac.type = IPAC_TYPE_IPACX; - hw->isac.mode = hw->addr.mode; - hw->isac.a.p = hw->addr.p; - hw->hscx.mode = hw->addr.mode; - hw->hscx.a.p = hw->addr.p; - break; - case INF_SPEEDWIN: - case INF_SAPHIR3: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.mode = hw->cfg.mode; - hw->isac.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE; - hw->isac.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT; - hw->hscx.mode = hw->cfg.mode; - hw->hscx.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE; - hw->hscx.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT; - outb(0xff, (ulong)hw->cfg.start); - mdelay(1); - outb(0x00, (ulong)hw->cfg.start); - mdelay(1); - outb(TIGER_IOMASK, (ulong)hw->cfg.start + TIGER_AUX_CTRL); - break; - case INF_QS1000: - case INF_QS3000: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.a.io.ale = (u32)hw->addr.start; - hw->isac.a.io.port = (u32)hw->addr.start + 1; - hw->isac.mode = hw->addr.mode; - hw->hscx.a.io.ale = (u32)hw->addr.start; - hw->hscx.a.io.port = (u32)hw->addr.start + 1; - hw->hscx.mode = hw->addr.mode; - break; - case INF_NICCY: - hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; - hw->isac.mode = hw->addr.mode; - hw->isac.a.io.ale = (u32)hw->addr.start + NICCY_ISAC_ALE; - hw->isac.a.io.port = (u32)hw->addr.start + NICCY_ISAC_PORT; - hw->hscx.mode = hw->addr.mode; - hw->hscx.a.io.ale = (u32)hw->addr.start + NICCY_HSCX_ALE; - hw->hscx.a.io.port = (u32)hw->addr.start + NICCY_HSCX_PORT; - break; - case INF_SCT_1: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.a.io.ale = (u32)hw->addr.start; - hw->isac.a.io.port = hw->isac.a.io.ale + 4; - hw->isac.mode = hw->addr.mode; - hw->hscx.a.io.ale = hw->isac.a.io.ale; - hw->hscx.a.io.port = hw->isac.a.io.port; - hw->hscx.mode = hw->addr.mode; - break; - case INF_SCT_2: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.a.io.ale = (u32)hw->addr.start + 0x08; - hw->isac.a.io.port = hw->isac.a.io.ale + 4; - hw->isac.mode = hw->addr.mode; - hw->hscx.a.io.ale = hw->isac.a.io.ale; - hw->hscx.a.io.port = hw->isac.a.io.port; - hw->hscx.mode = hw->addr.mode; - break; - case INF_SCT_3: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.a.io.ale = (u32)hw->addr.start + 0x10; - hw->isac.a.io.port = hw->isac.a.io.ale + 4; - hw->isac.mode = hw->addr.mode; - hw->hscx.a.io.ale = hw->isac.a.io.ale; - hw->hscx.a.io.port = hw->isac.a.io.port; - hw->hscx.mode = hw->addr.mode; - break; - case INF_SCT_4: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.a.io.ale = (u32)hw->addr.start + 0x20; - hw->isac.a.io.port = hw->isac.a.io.ale + 4; - hw->isac.mode = hw->addr.mode; - hw->hscx.a.io.ale = hw->isac.a.io.ale; - hw->hscx.a.io.port = hw->isac.a.io.port; - hw->hscx.mode = hw->addr.mode; - break; - case INF_GAZEL_R685: - hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; - hw->ipac.isac.off = 0x80; - hw->isac.mode = hw->addr.mode; - hw->isac.a.io.port = (u32)hw->addr.start; - hw->hscx.mode = hw->addr.mode; - hw->hscx.a.io.port = hw->isac.a.io.port; - break; - case INF_GAZEL_R753: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.mode = hw->addr.mode; - hw->isac.a.io.ale = (u32)hw->addr.start; - hw->isac.a.io.port = (u32)hw->addr.start + GAZEL_IPAC_DATA_PORT; - hw->hscx.mode = hw->addr.mode; - hw->hscx.a.io.ale = hw->isac.a.io.ale; - hw->hscx.a.io.port = hw->isac.a.io.port; - break; - default: - return -EINVAL; - } - switch (hw->isac.mode) { - case AM_MEMIO: - ASSIGN_FUNC_IPAC(MIO, hw->ipac); - break; - case AM_IND_IO: - ASSIGN_FUNC_IPAC(IND, hw->ipac); - break; - case AM_IO: - ASSIGN_FUNC_IPAC(IO, hw->ipac); - break; - default: - return -EINVAL; - } - return 0; -} - -static void -release_card(struct inf_hw *card) { - ulong flags; - int i; - - spin_lock_irqsave(&card->lock, flags); - disable_hwirq(card); - spin_unlock_irqrestore(&card->lock, flags); - card->ipac.isac.release(&card->ipac.isac); - free_irq(card->irq, card); - mISDN_unregister_device(&card->ipac.isac.dch.dev); - release_io(card); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - switch (card->ci->typ) { - case INF_SCT_2: - case INF_SCT_3: - case INF_SCT_4: - break; - case INF_SCT_1: - for (i = 0; i < 3; i++) { - if (card->sc[i]) - release_card(card->sc[i]); - card->sc[i] = NULL; - } - fallthrough; - default: - pci_disable_device(card->pdev); - pci_set_drvdata(card->pdev, NULL); - break; - } - kfree(card); - inf_cnt--; -} - -static int -setup_instance(struct inf_hw *card) -{ - int err; - ulong flags; - - snprintf(card->name, MISDN_MAX_IDLEN - 1, "%s.%d", card->ci->name, - inf_cnt + 1); - write_lock_irqsave(&card_lock, flags); - list_add_tail(&card->list, &Cards); - write_unlock_irqrestore(&card_lock, flags); - - _set_debug(card); - card->ipac.isac.name = card->name; - card->ipac.name = card->name; - card->ipac.owner = THIS_MODULE; - spin_lock_init(&card->lock); - card->ipac.isac.hwlock = &card->lock; - card->ipac.hwlock = &card->lock; - card->ipac.ctrl = (void *)&inf_ctrl; - - err = setup_io(card); - if (err) - goto error_setup; - - card->ipac.isac.dch.dev.Bprotocols = - mISDNipac_init(&card->ipac, card); - - if (card->ipac.isac.dch.dev.Bprotocols == 0) - goto error_setup; - - err = mISDN_register_device(&card->ipac.isac.dch.dev, - &card->pdev->dev, card->name); - if (err) - goto error; - - err = init_irq(card); - if (!err) { - inf_cnt++; - pr_notice("Infineon %d cards installed\n", inf_cnt); - return 0; - } - mISDN_unregister_device(&card->ipac.isac.dch.dev); -error: - card->ipac.release(&card->ipac); -error_setup: - release_io(card); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - return err; -} - -static const struct inf_cinfo inf_card_info[] = { - { - INF_DIVA20, - "Dialogic Diva 2.0", - "diva20", - AM_IND_IO, AM_NONE, 2, 0, - &diva_irq - }, - { - INF_DIVA20U, - "Dialogic Diva 2.0U", - "diva20U", - AM_IND_IO, AM_NONE, 2, 0, - &diva_irq - }, - { - INF_DIVA201, - "Dialogic Diva 2.01", - "diva201", - AM_MEMIO, AM_MEMIO, 0, 1, - &diva20x_irq - }, - { - INF_DIVA202, - "Dialogic Diva 2.02", - "diva202", - AM_MEMIO, AM_MEMIO, 0, 1, - &diva20x_irq - }, - { - INF_SPEEDWIN, - "Sedlbauer SpeedWin PCI", - "speedwin", - AM_IND_IO, AM_NONE, 0, 0, - &tiger_irq - }, - { - INF_SAPHIR3, - "HST Saphir 3", - "saphir", - AM_IND_IO, AM_NONE, 0, 0, - &tiger_irq - }, - { - INF_QS1000, - "Develo Microlink PCI", - "qs1000", - AM_IO, AM_IND_IO, 1, 3, - &elsa_irq - }, - { - INF_QS3000, - "Develo QuickStep 3000", - "qs3000", - AM_IO, AM_IND_IO, 1, 3, - &elsa_irq - }, - { - INF_NICCY, - "Sagem NICCY", - "niccy", - AM_IO, AM_IND_IO, 0, 1, - &niccy_irq - }, - { - INF_SCT_1, - "SciTel Quadro", - "p1_scitel", - AM_IO, AM_IND_IO, 1, 5, - &ipac_irq - }, - { - INF_SCT_2, - "SciTel Quadro", - "p2_scitel", - AM_NONE, AM_IND_IO, 0, 4, - &ipac_irq - }, - { - INF_SCT_3, - "SciTel Quadro", - "p3_scitel", - AM_NONE, AM_IND_IO, 0, 3, - &ipac_irq - }, - { - INF_SCT_4, - "SciTel Quadro", - "p4_scitel", - AM_NONE, AM_IND_IO, 0, 2, - &ipac_irq - }, - { - INF_GAZEL_R685, - "Gazel R685", - "gazel685", - AM_IO, AM_IO, 1, 2, - &gazel_irq - }, - { - INF_GAZEL_R753, - "Gazel R753", - "gazel753", - AM_IO, AM_IND_IO, 1, 2, - &ipac_irq - }, - { - INF_NONE, - } -}; - -static const struct inf_cinfo * -get_card_info(enum inf_types typ) -{ - const struct inf_cinfo *ci = inf_card_info; - - while (ci->typ != INF_NONE) { - if (ci->typ == typ) - return ci; - ci++; - } - return NULL; -} - -static int -inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - struct inf_hw *card; - - card = kzalloc_obj(struct inf_hw); - if (!card) { - pr_info("No memory for Infineon ISDN card\n"); - return err; - } - card->pdev = pdev; - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - card->ci = get_card_info(ent->driver_data); - if (!card->ci) { - pr_info("mISDN: do not have information about adapter at %s\n", - pci_name(pdev)); - kfree(card); - pci_disable_device(pdev); - return -EINVAL; - } else - pr_notice("mISDN: found adapter %s at %s\n", - card->ci->full, pci_name(pdev)); - - card->irq = pdev->irq; - pci_set_drvdata(pdev, card); - err = setup_instance(card); - if (err) { - pci_disable_device(pdev); - kfree(card); - pci_set_drvdata(pdev, NULL); - } else if (ent->driver_data == INF_SCT_1) { - int i; - struct inf_hw *sc; - - for (i = 1; i < 4; i++) { - sc = kzalloc_obj(struct inf_hw); - if (!sc) { - release_card(card); - pci_disable_device(pdev); - return -ENOMEM; - } - sc->irq = card->irq; - sc->pdev = card->pdev; - sc->ci = card->ci + i; - err = setup_instance(sc); - if (err) { - pci_disable_device(pdev); - kfree(sc); - release_card(card); - break; - } else - card->sc[i - 1] = sc; - } - } - return err; -} - -static void -inf_remove(struct pci_dev *pdev) -{ - struct inf_hw *card = pci_get_drvdata(pdev); - - if (card) - release_card(card); - else - pr_debug("%s: drvdata already removed\n", __func__); -} - -static struct pci_driver infineon_driver = { - .name = "ISDN Infineon pci", - .probe = inf_probe, - .remove = inf_remove, - .id_table = infineon_ids, -}; - -static int __init -infineon_init(void) -{ - int err; - - pr_notice("Infineon ISDN Driver Rev. %s\n", INFINEON_REV); - err = pci_register_driver(&infineon_driver); - return err; -} - -static void __exit -infineon_cleanup(void) -{ - pci_unregister_driver(&infineon_driver); -} - -module_init(infineon_init); -module_exit(infineon_cleanup); diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c deleted file mode 100644 index a34ea6058960..000000000000 --- a/drivers/isdn/hardware/mISDN/mISDNipac.c +++ /dev/null @@ -1,1636 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * isac.c ISAC specific routines - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#include -#include -#include -#include -#include "ipac.h" - - -#define DBUSY_TIMER_VALUE 80 -#define ARCOFI_USE 1 - -#define ISAC_REV "2.0" - -MODULE_AUTHOR("Karsten Keil"); -MODULE_VERSION(ISAC_REV); -MODULE_DESCRIPTION("mISDN driver for ISAC specific functions"); -MODULE_LICENSE("GPL v2"); - -#define ReadISAC(is, o) (is->read_reg(is->dch.hw, o + is->off)) -#define WriteISAC(is, o, v) (is->write_reg(is->dch.hw, o + is->off, v)) -#define ReadHSCX(h, o) (h->ip->read_reg(h->ip->hw, h->off + o)) -#define WriteHSCX(h, o, v) (h->ip->write_reg(h->ip->hw, h->off + o, v)) -#define ReadIPAC(ip, o) (ip->read_reg(ip->hw, o)) -#define WriteIPAC(ip, o, v) (ip->write_reg(ip->hw, o, v)) - -static inline void -ph_command(struct isac_hw *isac, u8 command) -{ - pr_debug("%s: ph_command %x\n", isac->name, command); - if (isac->type & IPAC_TYPE_ISACX) - WriteISAC(isac, ISACX_CIX0, (command << 4) | 0xE); - else - WriteISAC(isac, ISAC_CIX0, (command << 2) | 3); -} - -static void -isac_ph_state_change(struct isac_hw *isac) -{ - switch (isac->state) { - case (ISAC_IND_RS): - case (ISAC_IND_EI): - ph_command(isac, ISAC_CMD_DUI); - } - schedule_event(&isac->dch, FLG_PHCHANGE); -} - -static void -isac_ph_state_bh(struct dchannel *dch) -{ - struct isac_hw *isac = container_of(dch, struct isac_hw, dch); - - switch (isac->state) { - case ISAC_IND_RS: - case ISAC_IND_EI: - dch->state = 0; - l1_event(dch->l1, HW_RESET_IND); - break; - case ISAC_IND_DID: - dch->state = 3; - l1_event(dch->l1, HW_DEACT_CNF); - break; - case ISAC_IND_DR: - case ISAC_IND_DR6: - dch->state = 3; - l1_event(dch->l1, HW_DEACT_IND); - break; - case ISAC_IND_PU: - dch->state = 4; - l1_event(dch->l1, HW_POWERUP_IND); - break; - case ISAC_IND_RSY: - if (dch->state <= 5) { - dch->state = 5; - l1_event(dch->l1, ANYSIGNAL); - } else { - dch->state = 8; - l1_event(dch->l1, LOSTFRAMING); - } - break; - case ISAC_IND_ARD: - dch->state = 6; - l1_event(dch->l1, INFO2); - break; - case ISAC_IND_AI8: - dch->state = 7; - l1_event(dch->l1, INFO4_P8); - break; - case ISAC_IND_AI10: - dch->state = 7; - l1_event(dch->l1, INFO4_P10); - break; - } - pr_debug("%s: TE newstate %x\n", isac->name, dch->state); -} - -static void -isac_empty_fifo(struct isac_hw *isac, int count) -{ - u8 *ptr; - - pr_debug("%s: %s %d\n", isac->name, __func__, count); - - if (!isac->dch.rx_skb) { - isac->dch.rx_skb = mI_alloc_skb(isac->dch.maxlen, GFP_ATOMIC); - if (!isac->dch.rx_skb) { - pr_info("%s: D receive out of memory\n", isac->name); - WriteISAC(isac, ISAC_CMDR, 0x80); - return; - } - } - if ((isac->dch.rx_skb->len + count) >= isac->dch.maxlen) { - pr_debug("%s: %s overrun %d\n", isac->name, __func__, - isac->dch.rx_skb->len + count); - WriteISAC(isac, ISAC_CMDR, 0x80); - return; - } - ptr = skb_put(isac->dch.rx_skb, count); - isac->read_fifo(isac->dch.hw, isac->off, ptr, count); - WriteISAC(isac, ISAC_CMDR, 0x80); - if (isac->dch.debug & DEBUG_HW_DFIFO) { - char pfx[MISDN_MAX_IDLEN + 16]; - - snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-recv %s %d ", - isac->name, count); - print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -static void -isac_fill_fifo(struct isac_hw *isac) -{ - int count, more; - u8 *ptr; - - if (!isac->dch.tx_skb) - return; - count = isac->dch.tx_skb->len - isac->dch.tx_idx; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - pr_debug("%s: %s %d\n", isac->name, __func__, count); - ptr = isac->dch.tx_skb->data + isac->dch.tx_idx; - isac->dch.tx_idx += count; - isac->write_fifo(isac->dch.hw, isac->off, ptr, count); - WriteISAC(isac, ISAC_CMDR, more ? 0x8 : 0xa); - if (test_and_set_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) { - pr_debug("%s: %s dbusytimer running\n", isac->name, __func__); - timer_delete(&isac->dch.timer); - } - isac->dch.timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); - add_timer(&isac->dch.timer); - if (isac->dch.debug & DEBUG_HW_DFIFO) { - char pfx[MISDN_MAX_IDLEN + 16]; - - snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-send %s %d ", - isac->name, count); - print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -static void -isac_rme_irq(struct isac_hw *isac) -{ - u8 val, count; - - val = ReadISAC(isac, ISAC_RSTA); - if ((val & 0x70) != 0x20) { - if (val & 0x40) { - pr_debug("%s: ISAC RDO\n", isac->name); -#ifdef ERROR_STATISTIC - isac->dch.err_rx++; -#endif - } - if (!(val & 0x20)) { - pr_debug("%s: ISAC CRC error\n", isac->name); -#ifdef ERROR_STATISTIC - isac->dch.err_crc++; -#endif - } - WriteISAC(isac, ISAC_CMDR, 0x80); - dev_kfree_skb(isac->dch.rx_skb); - isac->dch.rx_skb = NULL; - } else { - count = ReadISAC(isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(isac, count); - recv_Dchannel(&isac->dch); - } -} - -static void -isac_xpr_irq(struct isac_hw *isac) -{ - if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) - timer_delete(&isac->dch.timer); - if (isac->dch.tx_skb && isac->dch.tx_idx < isac->dch.tx_skb->len) { - isac_fill_fifo(isac); - } else { - dev_kfree_skb(isac->dch.tx_skb); - if (get_next_dframe(&isac->dch)) - isac_fill_fifo(isac); - } -} - -static void -isac_retransmit(struct isac_hw *isac) -{ - if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) - timer_delete(&isac->dch.timer); - if (test_bit(FLG_TX_BUSY, &isac->dch.Flags)) { - /* Restart frame */ - isac->dch.tx_idx = 0; - isac_fill_fifo(isac); - } else if (isac->dch.tx_skb) { /* should not happen */ - pr_info("%s: tx_skb exist but not busy\n", isac->name); - test_and_set_bit(FLG_TX_BUSY, &isac->dch.Flags); - isac->dch.tx_idx = 0; - isac_fill_fifo(isac); - } else { - pr_info("%s: ISAC XDU no TX_BUSY\n", isac->name); - if (get_next_dframe(&isac->dch)) - isac_fill_fifo(isac); - } -} - -static void -isac_mos_irq(struct isac_hw *isac) -{ - u8 val; - int ret; - - val = ReadISAC(isac, ISAC_MOSR); - pr_debug("%s: ISAC MOSR %02x\n", isac->name, val); -#if ARCOFI_USE - if (val & 0x08) { - if (!isac->mon_rx) { - isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC); - if (!isac->mon_rx) { - pr_info("%s: ISAC MON RX out of memory!\n", - isac->name); - isac->mocr &= 0xf0; - isac->mocr |= 0x0a; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - goto afterMONR0; - } else - isac->mon_rxp = 0; - } - if (isac->mon_rxp >= MAX_MON_FRAME) { - isac->mocr &= 0xf0; - isac->mocr |= 0x0a; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mon_rxp = 0; - pr_debug("%s: ISAC MON RX overflow!\n", isac->name); - goto afterMONR0; - } - isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR0); - pr_debug("%s: ISAC MOR0 %02x\n", isac->name, - isac->mon_rx[isac->mon_rxp - 1]); - if (isac->mon_rxp == 1) { - isac->mocr |= 0x04; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - } - } -afterMONR0: - if (val & 0x80) { - if (!isac->mon_rx) { - isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC); - if (!isac->mon_rx) { - pr_info("%s: ISAC MON RX out of memory!\n", - isac->name); - isac->mocr &= 0x0f; - isac->mocr |= 0xa0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - goto afterMONR1; - } else - isac->mon_rxp = 0; - } - if (isac->mon_rxp >= MAX_MON_FRAME) { - isac->mocr &= 0x0f; - isac->mocr |= 0xa0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mon_rxp = 0; - pr_debug("%s: ISAC MON RX overflow!\n", isac->name); - goto afterMONR1; - } - isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR1); - pr_debug("%s: ISAC MOR1 %02x\n", isac->name, - isac->mon_rx[isac->mon_rxp - 1]); - isac->mocr |= 0x40; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - } -afterMONR1: - if (val & 0x04) { - isac->mocr &= 0xf0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mocr |= 0x0a; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - if (isac->monitor) { - ret = isac->monitor(isac->dch.hw, MONITOR_RX_0, - isac->mon_rx, isac->mon_rxp); - if (ret) - kfree(isac->mon_rx); - } else { - pr_info("%s: MONITOR 0 received %d but no user\n", - isac->name, isac->mon_rxp); - kfree(isac->mon_rx); - } - isac->mon_rx = NULL; - isac->mon_rxp = 0; - } - if (val & 0x40) { - isac->mocr &= 0x0f; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mocr |= 0xa0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - if (isac->monitor) { - ret = isac->monitor(isac->dch.hw, MONITOR_RX_1, - isac->mon_rx, isac->mon_rxp); - if (ret) - kfree(isac->mon_rx); - } else { - pr_info("%s: MONITOR 1 received %d but no user\n", - isac->name, isac->mon_rxp); - kfree(isac->mon_rx); - } - isac->mon_rx = NULL; - isac->mon_rxp = 0; - } - if (val & 0x02) { - if ((!isac->mon_tx) || (isac->mon_txc && - (isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) { - isac->mocr &= 0xf0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mocr |= 0x0a; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { - if (isac->monitor) - isac->monitor(isac->dch.hw, - MONITOR_TX_0, NULL, 0); - } - kfree(isac->mon_tx); - isac->mon_tx = NULL; - isac->mon_txc = 0; - isac->mon_txp = 0; - goto AfterMOX0; - } - if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { - if (isac->monitor) - isac->monitor(isac->dch.hw, - MONITOR_TX_0, NULL, 0); - kfree(isac->mon_tx); - isac->mon_tx = NULL; - isac->mon_txc = 0; - isac->mon_txp = 0; - goto AfterMOX0; - } - WriteISAC(isac, ISAC_MOX0, isac->mon_tx[isac->mon_txp++]); - pr_debug("%s: ISAC %02x -> MOX0\n", isac->name, - isac->mon_tx[isac->mon_txp - 1]); - } -AfterMOX0: - if (val & 0x20) { - if ((!isac->mon_tx) || (isac->mon_txc && - (isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) { - isac->mocr &= 0x0f; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mocr |= 0xa0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { - if (isac->monitor) - isac->monitor(isac->dch.hw, - MONITOR_TX_1, NULL, 0); - } - kfree(isac->mon_tx); - isac->mon_tx = NULL; - isac->mon_txc = 0; - isac->mon_txp = 0; - goto AfterMOX1; - } - if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { - if (isac->monitor) - isac->monitor(isac->dch.hw, - MONITOR_TX_1, NULL, 0); - kfree(isac->mon_tx); - isac->mon_tx = NULL; - isac->mon_txc = 0; - isac->mon_txp = 0; - goto AfterMOX1; - } - WriteISAC(isac, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]); - pr_debug("%s: ISAC %02x -> MOX1\n", isac->name, - isac->mon_tx[isac->mon_txp - 1]); - } -AfterMOX1: - val = 0; /* dummy to avoid warning */ -#endif -} - -static void -isac_cisq_irq(struct isac_hw *isac) { - u8 val; - - val = ReadISAC(isac, ISAC_CIR0); - pr_debug("%s: ISAC CIR0 %02X\n", isac->name, val); - if (val & 2) { - pr_debug("%s: ph_state change %x->%x\n", isac->name, - isac->state, (val >> 2) & 0xf); - isac->state = (val >> 2) & 0xf; - isac_ph_state_change(isac); - } - if (val & 1) { - val = ReadISAC(isac, ISAC_CIR1); - pr_debug("%s: ISAC CIR1 %02X\n", isac->name, val); - } -} - -static void -isacsx_cic_irq(struct isac_hw *isac) -{ - u8 val; - - val = ReadISAC(isac, ISACX_CIR0); - pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val); - if (val & ISACX_CIR0_CIC0) { - pr_debug("%s: ph_state change %x->%x\n", isac->name, - isac->state, val >> 4); - isac->state = val >> 4; - isac_ph_state_change(isac); - } -} - -static void -isacsx_rme_irq(struct isac_hw *isac) -{ - int count; - u8 val; - - val = ReadISAC(isac, ISACX_RSTAD); - if ((val & (ISACX_RSTAD_VFR | - ISACX_RSTAD_RDO | - ISACX_RSTAD_CRC | - ISACX_RSTAD_RAB)) - != (ISACX_RSTAD_VFR | ISACX_RSTAD_CRC)) { - pr_debug("%s: RSTAD %#x, dropped\n", isac->name, val); -#ifdef ERROR_STATISTIC - if (val & ISACX_RSTAD_CRC) - isac->dch.err_rx++; - else - isac->dch.err_crc++; -#endif - WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC); - dev_kfree_skb(isac->dch.rx_skb); - isac->dch.rx_skb = NULL; - } else { - count = ReadISAC(isac, ISACX_RBCLD) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(isac, count); - if (isac->dch.rx_skb) { - skb_trim(isac->dch.rx_skb, isac->dch.rx_skb->len - 1); - pr_debug("%s: dchannel received %d\n", isac->name, - isac->dch.rx_skb->len); - recv_Dchannel(&isac->dch); - } - } -} - -irqreturn_t -mISDNisac_irq(struct isac_hw *isac, u8 val) -{ - if (unlikely(!val)) - return IRQ_NONE; - pr_debug("%s: ISAC interrupt %02x\n", isac->name, val); - if (isac->type & IPAC_TYPE_ISACX) { - if (val & ISACX__CIC) - isacsx_cic_irq(isac); - if (val & ISACX__ICD) { - val = ReadISAC(isac, ISACX_ISTAD); - pr_debug("%s: ISTAD %02x\n", isac->name, val); - if (val & ISACX_D_XDU) { - pr_debug("%s: ISAC XDU\n", isac->name); -#ifdef ERROR_STATISTIC - isac->dch.err_tx++; -#endif - isac_retransmit(isac); - } - if (val & ISACX_D_XMR) { - pr_debug("%s: ISAC XMR\n", isac->name); -#ifdef ERROR_STATISTIC - isac->dch.err_tx++; -#endif - isac_retransmit(isac); - } - if (val & ISACX_D_XPR) - isac_xpr_irq(isac); - if (val & ISACX_D_RFO) { - pr_debug("%s: ISAC RFO\n", isac->name); - WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC); - } - if (val & ISACX_D_RME) - isacsx_rme_irq(isac); - if (val & ISACX_D_RPF) - isac_empty_fifo(isac, 0x20); - } - } else { - if (val & 0x80) /* RME */ - isac_rme_irq(isac); - if (val & 0x40) /* RPF */ - isac_empty_fifo(isac, 32); - if (val & 0x10) /* XPR */ - isac_xpr_irq(isac); - if (val & 0x04) /* CISQ */ - isac_cisq_irq(isac); - if (val & 0x20) /* RSC - never */ - pr_debug("%s: ISAC RSC interrupt\n", isac->name); - if (val & 0x02) /* SIN - never */ - pr_debug("%s: ISAC SIN interrupt\n", isac->name); - if (val & 0x01) { /* EXI */ - val = ReadISAC(isac, ISAC_EXIR); - pr_debug("%s: ISAC EXIR %02x\n", isac->name, val); - if (val & 0x80) /* XMR */ - pr_debug("%s: ISAC XMR\n", isac->name); - if (val & 0x40) { /* XDU */ - pr_debug("%s: ISAC XDU\n", isac->name); -#ifdef ERROR_STATISTIC - isac->dch.err_tx++; -#endif - isac_retransmit(isac); - } - if (val & 0x04) /* MOS */ - isac_mos_irq(isac); - } - } - return IRQ_HANDLED; -} -EXPORT_SYMBOL(mISDNisac_irq); - -static int -isac_l1hw(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct isac_hw *isac = container_of(dch, struct isac_hw, dch); - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - u32 id; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(isac->hwlock, flags); - ret = dchannel_senddata(dch, skb); - if (ret > 0) { /* direct TX */ - id = hh->id; /* skb can be freed */ - isac_fill_fifo(isac); - ret = 0; - spin_unlock_irqrestore(isac->hwlock, flags); - queue_ch_frame(ch, PH_DATA_CNF, id, NULL); - } else - spin_unlock_irqrestore(isac->hwlock, flags); - return ret; - case PH_ACTIVATE_REQ: - ret = l1_event(dch->l1, hh->prim); - break; - case PH_DEACTIVATE_REQ: - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - ret = l1_event(dch->l1, hh->prim); - break; - } - - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -isac_ctrl(struct isac_hw *isac, u32 cmd, unsigned long para) -{ - u8 tl = 0; - unsigned long flags; - int ret = 0; - - switch (cmd) { - case HW_TESTLOOP: - spin_lock_irqsave(isac->hwlock, flags); - if (!(isac->type & IPAC_TYPE_ISACX)) { - /* TODO: implement for IPAC_TYPE_ISACX */ - if (para & 1) /* B1 */ - tl |= 0x0c; - else if (para & 2) /* B2 */ - tl |= 0x3; - /* we only support IOM2 mode */ - WriteISAC(isac, ISAC_SPCR, tl); - if (tl) - WriteISAC(isac, ISAC_ADF1, 0x8); - else - WriteISAC(isac, ISAC_ADF1, 0x0); - } - spin_unlock_irqrestore(isac->hwlock, flags); - break; - case HW_TIMER3_VALUE: - ret = l1_event(isac->dch.l1, HW_TIMER3_VALUE | (para & 0xff)); - break; - default: - pr_debug("%s: %s unknown command %x %lx\n", isac->name, - __func__, cmd, para); - ret = -1; - } - return ret; -} - -static int -isac_l1cmd(struct dchannel *dch, u32 cmd) -{ - struct isac_hw *isac = container_of(dch, struct isac_hw, dch); - u_long flags; - - pr_debug("%s: cmd(%x) state(%02x)\n", isac->name, cmd, isac->state); - switch (cmd) { - case INFO3_P8: - spin_lock_irqsave(isac->hwlock, flags); - ph_command(isac, ISAC_CMD_AR8); - spin_unlock_irqrestore(isac->hwlock, flags); - break; - case INFO3_P10: - spin_lock_irqsave(isac->hwlock, flags); - ph_command(isac, ISAC_CMD_AR10); - spin_unlock_irqrestore(isac->hwlock, flags); - break; - case HW_RESET_REQ: - spin_lock_irqsave(isac->hwlock, flags); - if ((isac->state == ISAC_IND_EI) || - (isac->state == ISAC_IND_DR) || - (isac->state == ISAC_IND_DR6) || - (isac->state == ISAC_IND_RS)) - ph_command(isac, ISAC_CMD_TIM); - else - ph_command(isac, ISAC_CMD_RS); - spin_unlock_irqrestore(isac->hwlock, flags); - break; - case HW_DEACT_REQ: - skb_queue_purge(&dch->squeue); - if (dch->tx_skb) { - dev_kfree_skb(dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - dev_kfree_skb(dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); - break; - case HW_POWERUP_REQ: - spin_lock_irqsave(isac->hwlock, flags); - ph_command(isac, ISAC_CMD_TIM); - spin_unlock_irqrestore(isac->hwlock, flags); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - default: - pr_debug("%s: %s unknown command %x\n", isac->name, - __func__, cmd); - return -1; - } - return 0; -} - -static void -isac_release(struct isac_hw *isac) -{ - if (isac->type & IPAC_TYPE_ISACX) - WriteISAC(isac, ISACX_MASK, 0xff); - else if (isac->type != 0) - WriteISAC(isac, ISAC_MASK, 0xff); - if (isac->dch.timer.function != NULL) { - timer_delete(&isac->dch.timer); - isac->dch.timer.function = NULL; - } - kfree(isac->mon_rx); - isac->mon_rx = NULL; - kfree(isac->mon_tx); - isac->mon_tx = NULL; - if (isac->dch.l1) - l1_event(isac->dch.l1, CLOSE_CHANNEL); - mISDN_freedchannel(&isac->dch); -} - -static void -dbusy_timer_handler(struct timer_list *t) -{ - struct isac_hw *isac = timer_container_of(isac, t, dch.timer); - int rbch, star; - u_long flags; - - if (test_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) { - spin_lock_irqsave(isac->hwlock, flags); - rbch = ReadISAC(isac, ISAC_RBCH); - star = ReadISAC(isac, ISAC_STAR); - pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n", - isac->name, rbch, star); - if (rbch & ISAC_RBCH_XAC) /* D-Channel Busy */ - test_and_set_bit(FLG_L1_BUSY, &isac->dch.Flags); - else { - /* discard frame; reset transceiver */ - test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags); - if (isac->dch.tx_idx) - isac->dch.tx_idx = 0; - else - pr_info("%s: ISAC D-Channel Busy no tx_idx\n", - isac->name); - /* Transmitter reset */ - WriteISAC(isac, ISAC_CMDR, 0x01); - } - spin_unlock_irqrestore(isac->hwlock, flags); - } -} - -static int -open_dchannel_caller(struct isac_hw *isac, struct channel_req *rq, void *caller) -{ - pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__, - isac->dch.dev.id, caller); - if (rq->protocol != ISDN_P_TE_S0) - return -EINVAL; - if (rq->adr.channel == 1) - /* E-Channel not supported */ - return -EINVAL; - rq->ch = &isac->dch.dev.D; - rq->ch->protocol = rq->protocol; - if (isac->dch.state == 7) - _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - return 0; -} - -static int -open_dchannel(struct isac_hw *isac, struct channel_req *rq) -{ - return open_dchannel_caller(isac, rq, __builtin_return_address(0)); -} - -static const char *ISACVer[] = -{"2086/2186 V1.1", "2085 B1", "2085 B2", - "2085 V2.3"}; - -static int -isac_init(struct isac_hw *isac) -{ - u8 val; - int err = 0; - - if (!isac->dch.l1) { - err = create_l1(&isac->dch, isac_l1cmd); - if (err) - return err; - } - isac->mon_tx = NULL; - isac->mon_rx = NULL; - timer_setup(&isac->dch.timer, dbusy_timer_handler, 0); - isac->mocr = 0xaa; - if (isac->type & IPAC_TYPE_ISACX) { - /* Disable all IRQ */ - WriteISAC(isac, ISACX_MASK, 0xff); - val = ReadISAC(isac, ISACX_STARD); - pr_debug("%s: ISACX STARD %x\n", isac->name, val); - val = ReadISAC(isac, ISACX_ISTAD); - pr_debug("%s: ISACX ISTAD %x\n", isac->name, val); - val = ReadISAC(isac, ISACX_ISTA); - pr_debug("%s: ISACX ISTA %x\n", isac->name, val); - /* clear LDD */ - WriteISAC(isac, ISACX_TR_CONF0, 0x00); - /* enable transmitter */ - WriteISAC(isac, ISACX_TR_CONF2, 0x00); - /* transparent mode 0, RAC, stop/go */ - WriteISAC(isac, ISACX_MODED, 0xc9); - /* all HDLC IRQ unmasked */ - val = ReadISAC(isac, ISACX_ID); - if (isac->dch.debug & DEBUG_HW) - pr_notice("%s: ISACX Design ID %x\n", - isac->name, val & 0x3f); - val = ReadISAC(isac, ISACX_CIR0); - pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val); - isac->state = val >> 4; - isac_ph_state_change(isac); - ph_command(isac, ISAC_CMD_RS); - WriteISAC(isac, ISACX_MASK, IPACX__ON); - WriteISAC(isac, ISACX_MASKD, 0x00); - } else { /* old isac */ - WriteISAC(isac, ISAC_MASK, 0xff); - val = ReadISAC(isac, ISAC_STAR); - pr_debug("%s: ISAC STAR %x\n", isac->name, val); - val = ReadISAC(isac, ISAC_MODE); - pr_debug("%s: ISAC MODE %x\n", isac->name, val); - val = ReadISAC(isac, ISAC_ADF2); - pr_debug("%s: ISAC ADF2 %x\n", isac->name, val); - val = ReadISAC(isac, ISAC_ISTA); - pr_debug("%s: ISAC ISTA %x\n", isac->name, val); - if (val & 0x01) { - val = ReadISAC(isac, ISAC_EXIR); - pr_debug("%s: ISAC EXIR %x\n", isac->name, val); - } - val = ReadISAC(isac, ISAC_RBCH); - if (isac->dch.debug & DEBUG_HW) - pr_notice("%s: ISAC version (%x): %s\n", isac->name, - val, ISACVer[(val >> 5) & 3]); - isac->type |= ((val >> 5) & 3); - if (!isac->adf2) - isac->adf2 = 0x80; - if (!(isac->adf2 & 0x80)) { /* only IOM 2 Mode */ - pr_info("%s: only support IOM2 mode but adf2=%02x\n", - isac->name, isac->adf2); - isac_release(isac); - return -EINVAL; - } - WriteISAC(isac, ISAC_ADF2, isac->adf2); - WriteISAC(isac, ISAC_SQXR, 0x2f); - WriteISAC(isac, ISAC_SPCR, 0x00); - WriteISAC(isac, ISAC_STCR, 0x70); - WriteISAC(isac, ISAC_MODE, 0xc9); - WriteISAC(isac, ISAC_TIMR, 0x00); - WriteISAC(isac, ISAC_ADF1, 0x00); - val = ReadISAC(isac, ISAC_CIR0); - pr_debug("%s: ISAC CIR0 %x\n", isac->name, val); - isac->state = (val >> 2) & 0xf; - isac_ph_state_change(isac); - ph_command(isac, ISAC_CMD_RS); - WriteISAC(isac, ISAC_MASK, 0); - } - return err; -} - -int -mISDNisac_init(struct isac_hw *isac, void *hw) -{ - mISDN_initdchannel(&isac->dch, MAX_DFRAME_LEN_L1, isac_ph_state_bh); - isac->dch.hw = hw; - isac->dch.dev.D.send = isac_l1hw; - isac->init = isac_init; - isac->release = isac_release; - isac->ctrl = isac_ctrl; - isac->open = open_dchannel; - isac->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0); - isac->dch.dev.nrbchan = 2; - return 0; -} -EXPORT_SYMBOL(mISDNisac_init); - -static void -waitforCEC(struct hscx_hw *hx) -{ - u8 starb, to = 50; - - while (to) { - starb = ReadHSCX(hx, IPAC_STARB); - if (!(starb & 0x04)) - break; - udelay(1); - to--; - } - if (to < 50) - pr_debug("%s: B%1d CEC %d us\n", hx->ip->name, hx->bch.nr, - 50 - to); - if (!to) - pr_info("%s: B%1d CEC timeout\n", hx->ip->name, hx->bch.nr); -} - - -static void -waitforXFW(struct hscx_hw *hx) -{ - u8 starb, to = 50; - - while (to) { - starb = ReadHSCX(hx, IPAC_STARB); - if ((starb & 0x44) == 0x40) - break; - udelay(1); - to--; - } - if (to < 50) - pr_debug("%s: B%1d XFW %d us\n", hx->ip->name, hx->bch.nr, - 50 - to); - if (!to) - pr_info("%s: B%1d XFW timeout\n", hx->ip->name, hx->bch.nr); -} - -static void -hscx_cmdr(struct hscx_hw *hx, u8 cmd) -{ - if (hx->ip->type & IPAC_TYPE_IPACX) - WriteHSCX(hx, IPACX_CMDRB, cmd); - else { - waitforCEC(hx); - WriteHSCX(hx, IPAC_CMDRB, cmd); - } -} - -static void -hscx_empty_fifo(struct hscx_hw *hscx, u8 count) -{ - u8 *p; - int maxlen; - - pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count); - if (test_bit(FLG_RX_OFF, &hscx->bch.Flags)) { - hscx->bch.dropcnt += count; - hscx_cmdr(hscx, 0x80); /* RMC */ - return; - } - maxlen = bchannel_get_rxbuf(&hscx->bch, count); - if (maxlen < 0) { - hscx_cmdr(hscx, 0x80); /* RMC */ - if (hscx->bch.rx_skb) - skb_trim(hscx->bch.rx_skb, 0); - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - hscx->ip->name, hscx->bch.nr, count); - return; - } - p = skb_put(hscx->bch.rx_skb, count); - - if (hscx->ip->type & IPAC_TYPE_IPACX) - hscx->ip->read_fifo(hscx->ip->hw, - hscx->off + IPACX_RFIFOB, p, count); - else - hscx->ip->read_fifo(hscx->ip->hw, - hscx->off, p, count); - - hscx_cmdr(hscx, 0x80); /* RMC */ - - if (hscx->bch.debug & DEBUG_HW_BFIFO) { - snprintf(hscx->log, 64, "B%1d-recv %s %d ", - hscx->bch.nr, hscx->ip->name, count); - print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count); - } -} - -static void -hscx_fill_fifo(struct hscx_hw *hscx) -{ - int count, more; - u8 *p; - - if (!hscx->bch.tx_skb) { - if (!test_bit(FLG_TX_EMPTY, &hscx->bch.Flags)) - return; - count = hscx->fifo_size; - more = 1; - p = hscx->log; - memset(p, hscx->bch.fill[0], count); - } else { - count = hscx->bch.tx_skb->len - hscx->bch.tx_idx; - if (count <= 0) - return; - p = hscx->bch.tx_skb->data + hscx->bch.tx_idx; - - more = test_bit(FLG_TRANSPARENT, &hscx->bch.Flags) ? 1 : 0; - if (count > hscx->fifo_size) { - count = hscx->fifo_size; - more = 1; - } - pr_debug("%s: B%1d %d/%d/%d\n", hscx->ip->name, hscx->bch.nr, - count, hscx->bch.tx_idx, hscx->bch.tx_skb->len); - hscx->bch.tx_idx += count; - } - if (hscx->ip->type & IPAC_TYPE_IPACX) - hscx->ip->write_fifo(hscx->ip->hw, - hscx->off + IPACX_XFIFOB, p, count); - else { - waitforXFW(hscx); - hscx->ip->write_fifo(hscx->ip->hw, - hscx->off, p, count); - } - hscx_cmdr(hscx, more ? 0x08 : 0x0a); - - if (hscx->bch.tx_skb && (hscx->bch.debug & DEBUG_HW_BFIFO)) { - snprintf(hscx->log, 64, "B%1d-send %s %d ", - hscx->bch.nr, hscx->ip->name, count); - print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count); - } -} - -static void -hscx_xpr(struct hscx_hw *hx) -{ - if (hx->bch.tx_skb && hx->bch.tx_idx < hx->bch.tx_skb->len) { - hscx_fill_fifo(hx); - } else { - dev_kfree_skb(hx->bch.tx_skb); - if (get_next_bframe(&hx->bch)) { - hscx_fill_fifo(hx); - test_and_clear_bit(FLG_TX_EMPTY, &hx->bch.Flags); - } else if (test_bit(FLG_TX_EMPTY, &hx->bch.Flags)) { - hscx_fill_fifo(hx); - } - } -} - -static void -ipac_rme(struct hscx_hw *hx) -{ - int count; - u8 rstab; - - if (hx->ip->type & IPAC_TYPE_IPACX) - rstab = ReadHSCX(hx, IPACX_RSTAB); - else - rstab = ReadHSCX(hx, IPAC_RSTAB); - pr_debug("%s: B%1d RSTAB %02x\n", hx->ip->name, hx->bch.nr, rstab); - if ((rstab & 0xf0) != 0xa0) { - /* !(VFR && !RDO && CRC && !RAB) */ - if (!(rstab & 0x80)) { - if (hx->bch.debug & DEBUG_HW_BCHANNEL) - pr_notice("%s: B%1d invalid frame\n", - hx->ip->name, hx->bch.nr); - } - if (rstab & 0x40) { - if (hx->bch.debug & DEBUG_HW_BCHANNEL) - pr_notice("%s: B%1d RDO proto=%x\n", - hx->ip->name, hx->bch.nr, - hx->bch.state); - } - if (!(rstab & 0x20)) { - if (hx->bch.debug & DEBUG_HW_BCHANNEL) - pr_notice("%s: B%1d CRC error\n", - hx->ip->name, hx->bch.nr); - } - hscx_cmdr(hx, 0x80); /* Do RMC */ - return; - } - if (hx->ip->type & IPAC_TYPE_IPACX) - count = ReadHSCX(hx, IPACX_RBCLB); - else - count = ReadHSCX(hx, IPAC_RBCLB); - count &= (hx->fifo_size - 1); - if (count == 0) - count = hx->fifo_size; - hscx_empty_fifo(hx, count); - if (!hx->bch.rx_skb) - return; - if (hx->bch.rx_skb->len < 2) { - pr_debug("%s: B%1d frame too short %d\n", - hx->ip->name, hx->bch.nr, hx->bch.rx_skb->len); - skb_trim(hx->bch.rx_skb, 0); - } else { - skb_trim(hx->bch.rx_skb, hx->bch.rx_skb->len - 1); - recv_Bchannel(&hx->bch, 0, false); - } -} - -static void -ipac_irq(struct hscx_hw *hx, u8 ista) -{ - u8 istab, m, exirb = 0; - - if (hx->ip->type & IPAC_TYPE_IPACX) - istab = ReadHSCX(hx, IPACX_ISTAB); - else if (hx->ip->type & IPAC_TYPE_IPAC) { - istab = ReadHSCX(hx, IPAC_ISTAB); - m = (hx->bch.nr & 1) ? IPAC__EXA : IPAC__EXB; - if (m & ista) { - exirb = ReadHSCX(hx, IPAC_EXIRB); - pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name, - hx->bch.nr, exirb); - } - } else if (hx->bch.nr & 2) { /* HSCX B */ - if (ista & (HSCX__EXA | HSCX__ICA)) - ipac_irq(&hx->ip->hscx[0], ista); - if (ista & HSCX__EXB) { - exirb = ReadHSCX(hx, IPAC_EXIRB); - pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name, - hx->bch.nr, exirb); - } - istab = ista & 0xF8; - } else { /* HSCX A */ - istab = ReadHSCX(hx, IPAC_ISTAB); - if (ista & HSCX__EXA) { - exirb = ReadHSCX(hx, IPAC_EXIRB); - pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name, - hx->bch.nr, exirb); - } - istab = istab & 0xF8; - } - if (exirb & IPAC_B_XDU) - istab |= IPACX_B_XDU; - if (exirb & IPAC_B_RFO) - istab |= IPACX_B_RFO; - pr_debug("%s: B%1d ISTAB %02x\n", hx->ip->name, hx->bch.nr, istab); - - if (!test_bit(FLG_ACTIVE, &hx->bch.Flags)) - return; - - if (istab & IPACX_B_RME) - ipac_rme(hx); - - if (istab & IPACX_B_RPF) { - hscx_empty_fifo(hx, hx->fifo_size); - if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) - recv_Bchannel(&hx->bch, 0, false); - } - - if (istab & IPACX_B_RFO) { - pr_debug("%s: B%1d RFO error\n", hx->ip->name, hx->bch.nr); - hscx_cmdr(hx, 0x40); /* RRES */ - } - - if (istab & IPACX_B_XPR) - hscx_xpr(hx); - - if (istab & IPACX_B_XDU) { - if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) { - if (test_bit(FLG_FILLEMPTY, &hx->bch.Flags)) - test_and_set_bit(FLG_TX_EMPTY, &hx->bch.Flags); - hscx_xpr(hx); - return; - } - pr_debug("%s: B%1d XDU error at len %d\n", hx->ip->name, - hx->bch.nr, hx->bch.tx_idx); - hx->bch.tx_idx = 0; - hscx_cmdr(hx, 0x01); /* XRES */ - } -} - -irqreturn_t -mISDNipac_irq(struct ipac_hw *ipac, int maxloop) -{ - int cnt = maxloop + 1; - u8 ista, istad; - struct isac_hw *isac = &ipac->isac; - - if (ipac->type & IPAC_TYPE_IPACX) { - ista = ReadIPAC(ipac, ISACX_ISTA); - while (ista && --cnt) { - pr_debug("%s: ISTA %02x\n", ipac->name, ista); - if (ista & IPACX__ICA) - ipac_irq(&ipac->hscx[0], ista); - if (ista & IPACX__ICB) - ipac_irq(&ipac->hscx[1], ista); - if (ista & (ISACX__ICD | ISACX__CIC)) - mISDNisac_irq(&ipac->isac, ista); - ista = ReadIPAC(ipac, ISACX_ISTA); - } - } else if (ipac->type & IPAC_TYPE_IPAC) { - ista = ReadIPAC(ipac, IPAC_ISTA); - while (ista && --cnt) { - pr_debug("%s: ISTA %02x\n", ipac->name, ista); - if (ista & (IPAC__ICD | IPAC__EXD)) { - istad = ReadISAC(isac, ISAC_ISTA); - pr_debug("%s: ISTAD %02x\n", ipac->name, istad); - if (istad & IPAC_D_TIN2) - pr_debug("%s TIN2 irq\n", ipac->name); - if (ista & IPAC__EXD) - istad |= 1; /* ISAC EXI */ - mISDNisac_irq(isac, istad); - } - if (ista & (IPAC__ICA | IPAC__EXA)) - ipac_irq(&ipac->hscx[0], ista); - if (ista & (IPAC__ICB | IPAC__EXB)) - ipac_irq(&ipac->hscx[1], ista); - ista = ReadIPAC(ipac, IPAC_ISTA); - } - } else if (ipac->type & IPAC_TYPE_HSCX) { - while (--cnt) { - ista = ReadIPAC(ipac, IPAC_ISTAB + ipac->hscx[1].off); - pr_debug("%s: B2 ISTA %02x\n", ipac->name, ista); - if (ista) - ipac_irq(&ipac->hscx[1], ista); - istad = ReadISAC(isac, ISAC_ISTA); - pr_debug("%s: ISTAD %02x\n", ipac->name, istad); - if (istad) - mISDNisac_irq(isac, istad); - if (0 == (ista | istad)) - break; - } - } - if (cnt > maxloop) /* only for ISAC/HSCX without PCI IRQ test */ - return IRQ_NONE; - if (cnt < maxloop) - pr_debug("%s: %d irqloops cpu%d\n", ipac->name, - maxloop - cnt, smp_processor_id()); - if (maxloop && !cnt) - pr_notice("%s: %d IRQ LOOP cpu%d\n", ipac->name, - maxloop, smp_processor_id()); - return IRQ_HANDLED; -} -EXPORT_SYMBOL(mISDNipac_irq); - -static int -hscx_mode(struct hscx_hw *hscx, u32 bprotocol) -{ - pr_debug("%s: HSCX %c protocol %x-->%x ch %d\n", hscx->ip->name, - '@' + hscx->bch.nr, hscx->bch.state, bprotocol, hscx->bch.nr); - if (hscx->ip->type & IPAC_TYPE_IPACX) { - if (hscx->bch.nr & 1) { /* B1 and ICA */ - WriteIPAC(hscx->ip, ISACX_BCHA_TSDP_BC1, 0x80); - WriteIPAC(hscx->ip, ISACX_BCHA_CR, 0x88); - } else { /* B2 and ICB */ - WriteIPAC(hscx->ip, ISACX_BCHB_TSDP_BC1, 0x81); - WriteIPAC(hscx->ip, ISACX_BCHB_CR, 0x88); - } - switch (bprotocol) { - case ISDN_P_NONE: /* init */ - WriteHSCX(hscx, IPACX_MODEB, 0xC0); /* rec off */ - WriteHSCX(hscx, IPACX_EXMB, 0x30); /* std adj. */ - WriteHSCX(hscx, IPACX_MASKB, 0xFF); /* ints off */ - hscx_cmdr(hscx, 0x41); - test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_RAW: - WriteHSCX(hscx, IPACX_MODEB, 0x88); /* ex trans */ - WriteHSCX(hscx, IPACX_EXMB, 0x00); /* trans */ - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON); - test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_HDLC: - WriteHSCX(hscx, IPACX_MODEB, 0xC0); /* trans */ - WriteHSCX(hscx, IPACX_EXMB, 0x00); /* hdlc,crc */ - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON); - test_and_set_bit(FLG_HDLC, &hscx->bch.Flags); - break; - default: - pr_info("%s: protocol not known %x\n", hscx->ip->name, - bprotocol); - return -ENOPROTOOPT; - } - } else if (hscx->ip->type & IPAC_TYPE_IPAC) { /* IPAC */ - WriteHSCX(hscx, IPAC_CCR1, 0x82); - WriteHSCX(hscx, IPAC_CCR2, 0x30); - WriteHSCX(hscx, IPAC_XCCR, 0x07); - WriteHSCX(hscx, IPAC_RCCR, 0x07); - WriteHSCX(hscx, IPAC_TSAX, hscx->slot); - WriteHSCX(hscx, IPAC_TSAR, hscx->slot); - switch (bprotocol) { - case ISDN_P_NONE: - WriteHSCX(hscx, IPAC_TSAX, 0x1F); - WriteHSCX(hscx, IPAC_TSAR, 0x1F); - WriteHSCX(hscx, IPAC_MODEB, 0x84); - WriteHSCX(hscx, IPAC_CCR1, 0x82); - WriteHSCX(hscx, IPAC_MASKB, 0xFF); /* ints off */ - test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_RAW: - WriteHSCX(hscx, IPAC_MODEB, 0xe4); /* ex trans */ - WriteHSCX(hscx, IPAC_CCR1, 0x82); - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPAC_MASKB, 0); - test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_HDLC: - WriteHSCX(hscx, IPAC_MODEB, 0x8c); - WriteHSCX(hscx, IPAC_CCR1, 0x8a); - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPAC_MASKB, 0); - test_and_set_bit(FLG_HDLC, &hscx->bch.Flags); - break; - default: - pr_info("%s: protocol not known %x\n", hscx->ip->name, - bprotocol); - return -ENOPROTOOPT; - } - } else if (hscx->ip->type & IPAC_TYPE_HSCX) { /* HSCX */ - WriteHSCX(hscx, IPAC_CCR1, 0x85); - WriteHSCX(hscx, IPAC_CCR2, 0x30); - WriteHSCX(hscx, IPAC_XCCR, 0x07); - WriteHSCX(hscx, IPAC_RCCR, 0x07); - WriteHSCX(hscx, IPAC_TSAX, hscx->slot); - WriteHSCX(hscx, IPAC_TSAR, hscx->slot); - switch (bprotocol) { - case ISDN_P_NONE: - WriteHSCX(hscx, IPAC_TSAX, 0x1F); - WriteHSCX(hscx, IPAC_TSAR, 0x1F); - WriteHSCX(hscx, IPAC_MODEB, 0x84); - WriteHSCX(hscx, IPAC_CCR1, 0x85); - WriteHSCX(hscx, IPAC_MASKB, 0xFF); /* ints off */ - test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_RAW: - WriteHSCX(hscx, IPAC_MODEB, 0xe4); /* ex trans */ - WriteHSCX(hscx, IPAC_CCR1, 0x85); - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPAC_MASKB, 0); - test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_HDLC: - WriteHSCX(hscx, IPAC_MODEB, 0x8c); - WriteHSCX(hscx, IPAC_CCR1, 0x8d); - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPAC_MASKB, 0); - test_and_set_bit(FLG_HDLC, &hscx->bch.Flags); - break; - default: - pr_info("%s: protocol not known %x\n", hscx->ip->name, - bprotocol); - return -ENOPROTOOPT; - } - } else - return -EINVAL; - hscx->bch.state = bprotocol; - return 0; -} - -static int -hscx_l2l1(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hscx_hw *hx = container_of(bch, struct hscx_hw, bch); - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(hx->ip->hwlock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - ret = 0; - hscx_fill_fifo(hx); - } - spin_unlock_irqrestore(hx->ip->hwlock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(hx->ip->hwlock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = hscx_mode(hx, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(hx->ip->hwlock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - spin_lock_irqsave(hx->ip->hwlock, flags); - mISDN_clear_bchannel(bch); - hscx_mode(hx, ISDN_P_NONE); - spin_unlock_irqrestore(hx->ip->hwlock, flags); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - default: - pr_info("%s: %s unknown prim(%x,%x)\n", - hx->ip->name, __func__, hh->prim, hh->id); - ret = -EINVAL; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} - -static int -hscx_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hscx_hw *hx = container_of(bch, struct hscx_hw, bch); - int ret = -EINVAL; - u_long flags; - - pr_debug("%s: %s cmd:%x %p\n", hx->ip->name, __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - cancel_work_sync(&bch->workq); - spin_lock_irqsave(hx->ip->hwlock, flags); - mISDN_clear_bchannel(bch); - hscx_mode(hx, ISDN_P_NONE); - spin_unlock_irqrestore(hx->ip->hwlock, flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(hx->ip->owner); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - pr_info("%s: %s unknown prim(%x)\n", - hx->ip->name, __func__, cmd); - } - return ret; -} - -static void -free_ipac(struct ipac_hw *ipac) -{ - isac_release(&ipac->isac); -} - -static const char *HSCXVer[] = -{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7", - "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"}; - - - -static void -hscx_init(struct hscx_hw *hx) -{ - u8 val; - - WriteHSCX(hx, IPAC_RAH2, 0xFF); - WriteHSCX(hx, IPAC_XBCH, 0x00); - WriteHSCX(hx, IPAC_RLCR, 0x00); - - if (hx->ip->type & IPAC_TYPE_HSCX) { - WriteHSCX(hx, IPAC_CCR1, 0x85); - val = ReadHSCX(hx, HSCX_VSTR); - pr_debug("%s: HSCX VSTR %02x\n", hx->ip->name, val); - if (hx->bch.debug & DEBUG_HW) - pr_notice("%s: HSCX version %s\n", hx->ip->name, - HSCXVer[val & 0x0f]); - } else - WriteHSCX(hx, IPAC_CCR1, 0x82); - WriteHSCX(hx, IPAC_CCR2, 0x30); - WriteHSCX(hx, IPAC_XCCR, 0x07); - WriteHSCX(hx, IPAC_RCCR, 0x07); -} - -static int -ipac_init(struct ipac_hw *ipac) -{ - u8 val; - - if (ipac->type & IPAC_TYPE_HSCX) { - hscx_init(&ipac->hscx[0]); - hscx_init(&ipac->hscx[1]); - val = ReadIPAC(ipac, IPAC_ID); - } else if (ipac->type & IPAC_TYPE_IPAC) { - hscx_init(&ipac->hscx[0]); - hscx_init(&ipac->hscx[1]); - WriteIPAC(ipac, IPAC_MASK, IPAC__ON); - val = ReadIPAC(ipac, IPAC_CONF); - /* conf is default 0, but can be overwritten by card setup */ - pr_debug("%s: IPAC CONF %02x/%02x\n", ipac->name, - val, ipac->conf); - WriteIPAC(ipac, IPAC_CONF, ipac->conf); - val = ReadIPAC(ipac, IPAC_ID); - if (ipac->hscx[0].bch.debug & DEBUG_HW) - pr_notice("%s: IPAC Design ID %02x\n", ipac->name, val); - } - /* nothing special for IPACX to do here */ - return isac_init(&ipac->isac); -} - -static int -open_bchannel(struct ipac_hw *ipac, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &ipac->hscx[rq->adr.channel - 1].bch; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - return 0; -} - -static int -channel_ctrl(struct ipac_hw *ipac, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_LOOP: - /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ - if (cq->channel < 0 || cq->channel > 3) { - ret = -EINVAL; - break; - } - ret = ipac->ctrl(ipac, HW_TESTLOOP, cq->channel); - break; - case MISDN_CTRL_L1_TIMER3: - ret = ipac->isac.ctrl(&ipac->isac, HW_TIMER3_VALUE, cq->p1); - break; - default: - pr_info("%s: unknown CTRL OP %x\n", ipac->name, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -ipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct isac_hw *isac = container_of(dch, struct isac_hw, dch); - struct ipac_hw *ipac = container_of(isac, struct ipac_hw, isac); - struct channel_req *rq; - int err = 0; - - pr_debug("%s: DCTRL: %x %p\n", ipac->name, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if (rq->protocol == ISDN_P_TE_S0) - err = open_dchannel_caller(isac, rq, __builtin_return_address(0)); - else - err = open_bchannel(ipac, rq); - if (err) - break; - if (!try_module_get(ipac->owner)) - pr_info("%s: cannot get module\n", ipac->name); - break; - case CLOSE_CHANNEL: - pr_debug("%s: dev(%d) close from %p\n", ipac->name, - dch->dev.id, __builtin_return_address(0)); - module_put(ipac->owner); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(ipac, arg); - break; - default: - pr_debug("%s: unknown DCTRL command %x\n", ipac->name, cmd); - return -EINVAL; - } - return err; -} - -u32 -mISDNipac_init(struct ipac_hw *ipac, void *hw) -{ - u32 ret; - u8 i; - - ipac->hw = hw; - if (ipac->isac.dch.debug & DEBUG_HW) - pr_notice("%s: ipac type %x\n", ipac->name, ipac->type); - if (ipac->type & IPAC_TYPE_HSCX) { - ipac->isac.type = IPAC_TYPE_ISAC; - ipac->hscx[0].off = 0; - ipac->hscx[1].off = 0x40; - ipac->hscx[0].fifo_size = 32; - ipac->hscx[1].fifo_size = 32; - } else if (ipac->type & IPAC_TYPE_IPAC) { - ipac->isac.type = IPAC_TYPE_IPAC | IPAC_TYPE_ISAC; - ipac->hscx[0].off = 0; - ipac->hscx[1].off = 0x40; - ipac->hscx[0].fifo_size = 64; - ipac->hscx[1].fifo_size = 64; - } else if (ipac->type & IPAC_TYPE_IPACX) { - ipac->isac.type = IPAC_TYPE_IPACX | IPAC_TYPE_ISACX; - ipac->hscx[0].off = IPACX_OFF_ICA; - ipac->hscx[1].off = IPACX_OFF_ICB; - ipac->hscx[0].fifo_size = 64; - ipac->hscx[1].fifo_size = 64; - } else - return 0; - - mISDNisac_init(&ipac->isac, hw); - - ipac->isac.dch.dev.D.ctrl = ipac_dctrl; - - for (i = 0; i < 2; i++) { - ipac->hscx[i].bch.nr = i + 1; - set_channelmap(i + 1, ipac->isac.dch.dev.channelmap); - list_add(&ipac->hscx[i].bch.ch.list, - &ipac->isac.dch.dev.bchannels); - mISDN_initbchannel(&ipac->hscx[i].bch, MAX_DATA_MEM, - ipac->hscx[i].fifo_size); - ipac->hscx[i].bch.ch.nr = i + 1; - ipac->hscx[i].bch.ch.send = &hscx_l2l1; - ipac->hscx[i].bch.ch.ctrl = hscx_bctrl; - ipac->hscx[i].bch.hw = hw; - ipac->hscx[i].ip = ipac; - /* default values for IOM time slots - * can be overwritten by card */ - ipac->hscx[i].slot = (i == 0) ? 0x2f : 0x03; - } - - ipac->init = ipac_init; - ipac->release = free_ipac; - - ret = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - return ret; -} -EXPORT_SYMBOL(mISDNipac_init); - -static int __init -isac_mod_init(void) -{ - pr_notice("mISDNipac module version %s\n", ISAC_REV); - return 0; -} - -static void __exit -isac_mod_cleanup(void) -{ - pr_notice("mISDNipac module unloaded\n"); -} -module_init(isac_mod_init); -module_exit(isac_mod_cleanup); diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c deleted file mode 100644 index dace91ba412b..000000000000 --- a/drivers/isdn/hardware/mISDN/mISDNisar.c +++ /dev/null @@ -1,1694 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * mISDNisar.c ISAR (Siemens PSB 7110) specific functions - * - * Author Karsten Keil (keil@isdn4linux.de) - * - * Copyright 2009 by Karsten Keil - */ - -/* define this to enable static debug messages, if you kernel supports - * dynamic debugging, you should use debugfs for this - */ -/* #define DEBUG */ - -#include -#include -#include -#include -#include -#include "isar.h" - -#define ISAR_REV "2.1" - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for ISAR (Siemens PSB 7110) specific functions"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(ISAR_REV); - -#define DEBUG_HW_FIRMWARE_FIFO 0x10000 - -static const u8 faxmodulation[] = {3, 24, 48, 72, 73, 74, 96, 97, 98, 121, - 122, 145, 146}; -#define FAXMODCNT 13 - -static void isar_setup(struct isar_hw *); - -static inline int -waitforHIA(struct isar_hw *isar, int timeout) -{ - int t = timeout; - u8 val = isar->read_reg(isar->hw, ISAR_HIA); - - while ((val & 1) && t) { - udelay(1); - t--; - val = isar->read_reg(isar->hw, ISAR_HIA); - } - pr_debug("%s: HIA after %dus\n", isar->name, timeout - t); - return timeout; -} - -/* - * send msg to ISAR mailbox - * if msg is NULL use isar->buf - */ -static int -send_mbox(struct isar_hw *isar, u8 his, u8 creg, u8 len, u8 *msg) -{ - if (!waitforHIA(isar, 1000)) - return 0; - pr_debug("send_mbox(%02x,%02x,%d)\n", his, creg, len); - isar->write_reg(isar->hw, ISAR_CTRL_H, creg); - isar->write_reg(isar->hw, ISAR_CTRL_L, len); - isar->write_reg(isar->hw, ISAR_WADR, 0); - if (!msg) - msg = isar->buf; - if (msg && len) { - isar->write_fifo(isar->hw, ISAR_MBOX, msg, len); - if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) { - int l = 0; - - while (l < (int)len) { - hex_dump_to_buffer(msg + l, len - l, 32, 1, - isar->log, 256, 1); - pr_debug("%s: %s %02x: %s\n", isar->name, - __func__, l, isar->log); - l += 32; - } - } - } - isar->write_reg(isar->hw, ISAR_HIS, his); - waitforHIA(isar, 1000); - return 1; -} - -/* - * receive message from ISAR mailbox - * if msg is NULL use isar->buf - */ -static void -rcv_mbox(struct isar_hw *isar, u8 *msg) -{ - if (!msg) - msg = isar->buf; - isar->write_reg(isar->hw, ISAR_RADR, 0); - if (msg && isar->clsb) { - isar->read_fifo(isar->hw, ISAR_MBOX, msg, isar->clsb); - if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) { - int l = 0; - - while (l < (int)isar->clsb) { - hex_dump_to_buffer(msg + l, isar->clsb - l, 32, - 1, isar->log, 256, 1); - pr_debug("%s: %s %02x: %s\n", isar->name, - __func__, l, isar->log); - l += 32; - } - } - } - isar->write_reg(isar->hw, ISAR_IIA, 0); -} - -static inline void -get_irq_infos(struct isar_hw *isar) -{ - isar->iis = isar->read_reg(isar->hw, ISAR_IIS); - isar->cmsb = isar->read_reg(isar->hw, ISAR_CTRL_H); - isar->clsb = isar->read_reg(isar->hw, ISAR_CTRL_L); - pr_debug("%s: rcv_mbox(%02x,%02x,%d)\n", isar->name, - isar->iis, isar->cmsb, isar->clsb); -} - -/* - * poll answer message from ISAR mailbox - * should be used only with ISAR IRQs disabled before DSP was started - * - */ -static int -poll_mbox(struct isar_hw *isar, int maxdelay) -{ - int t = maxdelay; - u8 irq; - - irq = isar->read_reg(isar->hw, ISAR_IRQBIT); - while (t && !(irq & ISAR_IRQSTA)) { - udelay(1); - t--; - } - if (t) { - get_irq_infos(isar); - rcv_mbox(isar, NULL); - } - pr_debug("%s: pulled %d bytes after %d us\n", - isar->name, isar->clsb, maxdelay - t); - return t; -} - -static int -ISARVersion(struct isar_hw *isar) -{ - int ver; - - /* disable ISAR IRQ */ - isar->write_reg(isar->hw, ISAR_IRQBIT, 0); - isar->buf[0] = ISAR_MSG_HWVER; - isar->buf[1] = 0; - isar->buf[2] = 1; - if (!send_mbox(isar, ISAR_HIS_VNR, 0, 3, NULL)) - return -1; - if (!poll_mbox(isar, 1000)) - return -2; - if (isar->iis == ISAR_IIS_VNR) { - if (isar->clsb == 1) { - ver = isar->buf[0] & 0xf; - return ver; - } - return -3; - } - return -4; -} - -static int -load_firmware(struct isar_hw *isar, const u8 *buf, int size) -{ - u32 saved_debug = isar->ch[0].bch.debug; - int ret, cnt; - u8 nom, noc; - u16 left, val, *sp = (u16 *)buf; - u8 *mp; - u_long flags; - - struct { - u16 sadr; - u16 len; - u16 d_key; - } blk_head; - - if (1 != isar->version) { - pr_err("%s: ISAR wrong version %d firmware download aborted\n", - isar->name, isar->version); - return -EINVAL; - } - if (!(saved_debug & DEBUG_HW_FIRMWARE_FIFO)) - isar->ch[0].bch.debug &= ~DEBUG_HW_BFIFO; - pr_debug("%s: load firmware %d words (%d bytes)\n", - isar->name, size / 2, size); - cnt = 0; - size /= 2; - /* disable ISAR IRQ */ - spin_lock_irqsave(isar->hwlock, flags); - isar->write_reg(isar->hw, ISAR_IRQBIT, 0); - spin_unlock_irqrestore(isar->hwlock, flags); - while (cnt < size) { - blk_head.sadr = le16_to_cpu(*sp++); - blk_head.len = le16_to_cpu(*sp++); - blk_head.d_key = le16_to_cpu(*sp++); - cnt += 3; - pr_debug("ISAR firmware block (%#x,%d,%#x)\n", - blk_head.sadr, blk_head.len, blk_head.d_key & 0xff); - left = blk_head.len; - if (cnt + left > size) { - pr_info("%s: firmware error have %d need %d words\n", - isar->name, size, cnt + left); - ret = -EINVAL; - goto reterrflg; - } - spin_lock_irqsave(isar->hwlock, flags); - if (!send_mbox(isar, ISAR_HIS_DKEY, blk_head.d_key & 0xff, - 0, NULL)) { - pr_info("ISAR send_mbox dkey failed\n"); - ret = -ETIME; - goto reterror; - } - if (!poll_mbox(isar, 1000)) { - pr_warn("ISAR poll_mbox dkey failed\n"); - ret = -ETIME; - goto reterror; - } - spin_unlock_irqrestore(isar->hwlock, flags); - if ((isar->iis != ISAR_IIS_DKEY) || isar->cmsb || isar->clsb) { - pr_info("ISAR wrong dkey response (%x,%x,%x)\n", - isar->iis, isar->cmsb, isar->clsb); - ret = 1; - goto reterrflg; - } - while (left > 0) { - if (left > 126) - noc = 126; - else - noc = left; - nom = (2 * noc) + 3; - mp = isar->buf; - /* the ISAR is big endian */ - *mp++ = blk_head.sadr >> 8; - *mp++ = blk_head.sadr & 0xFF; - left -= noc; - cnt += noc; - *mp++ = noc; - pr_debug("%s: load %3d words at %04x\n", isar->name, - noc, blk_head.sadr); - blk_head.sadr += noc; - while (noc) { - val = le16_to_cpu(*sp++); - *mp++ = val >> 8; - *mp++ = val & 0xFF; - noc--; - } - spin_lock_irqsave(isar->hwlock, flags); - if (!send_mbox(isar, ISAR_HIS_FIRM, 0, nom, NULL)) { - pr_info("ISAR send_mbox prog failed\n"); - ret = -ETIME; - goto reterror; - } - if (!poll_mbox(isar, 1000)) { - pr_info("ISAR poll_mbox prog failed\n"); - ret = -ETIME; - goto reterror; - } - spin_unlock_irqrestore(isar->hwlock, flags); - if ((isar->iis != ISAR_IIS_FIRM) || - isar->cmsb || isar->clsb) { - pr_info("ISAR wrong prog response (%x,%x,%x)\n", - isar->iis, isar->cmsb, isar->clsb); - ret = -EIO; - goto reterrflg; - } - } - pr_debug("%s: ISAR firmware block %d words loaded\n", - isar->name, blk_head.len); - } - isar->ch[0].bch.debug = saved_debug; - /* 10ms delay */ - cnt = 10; - while (cnt--) - mdelay(1); - isar->buf[0] = 0xff; - isar->buf[1] = 0xfe; - isar->bstat = 0; - spin_lock_irqsave(isar->hwlock, flags); - if (!send_mbox(isar, ISAR_HIS_STDSP, 0, 2, NULL)) { - pr_info("ISAR send_mbox start dsp failed\n"); - ret = -ETIME; - goto reterror; - } - if (!poll_mbox(isar, 1000)) { - pr_info("ISAR poll_mbox start dsp failed\n"); - ret = -ETIME; - goto reterror; - } - if ((isar->iis != ISAR_IIS_STDSP) || isar->cmsb || isar->clsb) { - pr_info("ISAR wrong start dsp response (%x,%x,%x)\n", - isar->iis, isar->cmsb, isar->clsb); - ret = -EIO; - goto reterror; - } else - pr_debug("%s: ISAR start dsp success\n", isar->name); - - /* NORMAL mode entered */ - /* Enable IRQs of ISAR */ - isar->write_reg(isar->hw, ISAR_IRQBIT, ISAR_IRQSTA); - spin_unlock_irqrestore(isar->hwlock, flags); - cnt = 1000; /* max 1s */ - while ((!isar->bstat) && cnt) { - mdelay(1); - cnt--; - } - if (!cnt) { - pr_info("ISAR no general status event received\n"); - ret = -ETIME; - goto reterrflg; - } else - pr_debug("%s: ISAR general status event %x\n", - isar->name, isar->bstat); - /* 10ms delay */ - cnt = 10; - while (cnt--) - mdelay(1); - isar->iis = 0; - spin_lock_irqsave(isar->hwlock, flags); - if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) { - pr_info("ISAR send_mbox self tst failed\n"); - ret = -ETIME; - goto reterror; - } - spin_unlock_irqrestore(isar->hwlock, flags); - cnt = 10000; /* max 100 ms */ - while ((isar->iis != ISAR_IIS_DIAG) && cnt) { - udelay(10); - cnt--; - } - mdelay(1); - if (!cnt) { - pr_info("ISAR no self tst response\n"); - ret = -ETIME; - goto reterrflg; - } - if ((isar->cmsb == ISAR_CTRL_STST) && (isar->clsb == 1) - && (isar->buf[0] == 0)) - pr_debug("%s: ISAR selftest OK\n", isar->name); - else { - pr_info("ISAR selftest not OK %x/%x/%x\n", - isar->cmsb, isar->clsb, isar->buf[0]); - ret = -EIO; - goto reterrflg; - } - spin_lock_irqsave(isar->hwlock, flags); - isar->iis = 0; - if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) { - pr_info("ISAR RQST SVN failed\n"); - ret = -ETIME; - goto reterror; - } - spin_unlock_irqrestore(isar->hwlock, flags); - cnt = 30000; /* max 300 ms */ - while ((isar->iis != ISAR_IIS_DIAG) && cnt) { - udelay(10); - cnt--; - } - mdelay(1); - if (!cnt) { - pr_info("ISAR no SVN response\n"); - ret = -ETIME; - goto reterrflg; - } else { - if ((isar->cmsb == ISAR_CTRL_SWVER) && (isar->clsb == 1)) { - pr_notice("%s: ISAR software version %#x\n", - isar->name, isar->buf[0]); - } else { - pr_info("%s: ISAR wrong swver response (%x,%x)" - " cnt(%d)\n", isar->name, isar->cmsb, - isar->clsb, cnt); - ret = -EIO; - goto reterrflg; - } - } - spin_lock_irqsave(isar->hwlock, flags); - isar_setup(isar); - spin_unlock_irqrestore(isar->hwlock, flags); - ret = 0; -reterrflg: - spin_lock_irqsave(isar->hwlock, flags); -reterror: - isar->ch[0].bch.debug = saved_debug; - if (ret) - /* disable ISAR IRQ */ - isar->write_reg(isar->hw, ISAR_IRQBIT, 0); - spin_unlock_irqrestore(isar->hwlock, flags); - return ret; -} - -static inline void -deliver_status(struct isar_ch *ch, int status) -{ - pr_debug("%s: HL->LL FAXIND %x\n", ch->is->name, status); - _queue_data(&ch->bch.ch, PH_CONTROL_IND, status, 0, NULL, GFP_ATOMIC); -} - -static inline void -isar_rcv_frame(struct isar_ch *ch) -{ - u8 *ptr; - int maxlen; - - if (!ch->is->clsb) { - pr_debug("%s; ISAR zero len frame\n", ch->is->name); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - return; - } - if (test_bit(FLG_RX_OFF, &ch->bch.Flags)) { - ch->bch.dropcnt += ch->is->clsb; - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - return; - } - switch (ch->bch.state) { - case ISDN_P_NONE: - pr_debug("%s: ISAR protocol 0 spurious IIS_RDATA %x/%x/%x\n", - ch->is->name, ch->is->iis, ch->is->cmsb, ch->is->clsb); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - case ISDN_P_B_RAW: - case ISDN_P_B_L2DTMF: - case ISDN_P_B_MODEM_ASYNC: - maxlen = bchannel_get_rxbuf(&ch->bch, ch->is->clsb); - if (maxlen < 0) { - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - ch->is->name, ch->bch.nr, ch->is->clsb); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } - rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb)); - recv_Bchannel(&ch->bch, 0, false); - break; - case ISDN_P_B_HDLC: - maxlen = bchannel_get_rxbuf(&ch->bch, ch->is->clsb); - if (maxlen < 0) { - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - ch->is->name, ch->bch.nr, ch->is->clsb); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } - if (ch->is->cmsb & HDLC_ERROR) { - pr_debug("%s: ISAR frame error %x len %d\n", - ch->is->name, ch->is->cmsb, ch->is->clsb); -#ifdef ERROR_STATISTIC - if (ch->is->cmsb & HDLC_ERR_RER) - ch->bch.err_inv++; - if (ch->is->cmsb & HDLC_ERR_CER) - ch->bch.err_crc++; -#endif - skb_trim(ch->bch.rx_skb, 0); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } - if (ch->is->cmsb & HDLC_FSD) - skb_trim(ch->bch.rx_skb, 0); - ptr = skb_put(ch->bch.rx_skb, ch->is->clsb); - rcv_mbox(ch->is, ptr); - if (ch->is->cmsb & HDLC_FED) { - if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */ - pr_debug("%s: ISAR frame too short %d\n", - ch->is->name, ch->bch.rx_skb->len); - skb_trim(ch->bch.rx_skb, 0); - break; - } - skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2); - recv_Bchannel(&ch->bch, 0, false); - } - break; - case ISDN_P_B_T30_FAX: - if (ch->state != STFAX_ACTIV) { - pr_debug("%s: isar_rcv_frame: not ACTIV\n", - ch->is->name); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - if (ch->bch.rx_skb) - skb_trim(ch->bch.rx_skb, 0); - break; - } - if (!ch->bch.rx_skb) { - ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen, - GFP_ATOMIC); - if (unlikely(!ch->bch.rx_skb)) { - pr_info("%s: B receive out of memory\n", - __func__); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } - } - if (ch->cmd == PCTRL_CMD_FRM) { - rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb)); - pr_debug("%s: isar_rcv_frame: %d\n", - ch->is->name, ch->bch.rx_skb->len); - if (ch->is->cmsb & SART_NMD) { /* ABORT */ - pr_debug("%s: isar_rcv_frame: no more data\n", - ch->is->name); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - send_mbox(ch->is, SET_DPS(ch->dpath) | - ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, - 0, NULL); - ch->state = STFAX_ESCAPE; - /* set_skb_flag(skb, DF_NOMOREDATA); */ - } - recv_Bchannel(&ch->bch, 0, false); - if (ch->is->cmsb & SART_NMD) - deliver_status(ch, HW_MOD_NOCARR); - break; - } - if (ch->cmd != PCTRL_CMD_FRH) { - pr_debug("%s: isar_rcv_frame: unknown fax mode %x\n", - ch->is->name, ch->cmd); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - if (ch->bch.rx_skb) - skb_trim(ch->bch.rx_skb, 0); - break; - } - /* PCTRL_CMD_FRH */ - if ((ch->bch.rx_skb->len + ch->is->clsb) > - (ch->bch.maxlen + 2)) { - pr_info("%s: %s incoming packet too large\n", - ch->is->name, __func__); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - skb_trim(ch->bch.rx_skb, 0); - break; - } else if (ch->is->cmsb & HDLC_ERROR) { - pr_info("%s: ISAR frame error %x len %d\n", - ch->is->name, ch->is->cmsb, ch->is->clsb); - skb_trim(ch->bch.rx_skb, 0); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } - if (ch->is->cmsb & HDLC_FSD) - skb_trim(ch->bch.rx_skb, 0); - ptr = skb_put(ch->bch.rx_skb, ch->is->clsb); - rcv_mbox(ch->is, ptr); - if (ch->is->cmsb & HDLC_FED) { - if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */ - pr_info("%s: ISAR frame too short %d\n", - ch->is->name, ch->bch.rx_skb->len); - skb_trim(ch->bch.rx_skb, 0); - break; - } - skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2); - recv_Bchannel(&ch->bch, 0, false); - } - if (ch->is->cmsb & SART_NMD) { /* ABORT */ - pr_debug("%s: isar_rcv_frame: no more data\n", - ch->is->name); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - if (ch->bch.rx_skb) - skb_trim(ch->bch.rx_skb, 0); - send_mbox(ch->is, SET_DPS(ch->dpath) | - ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); - ch->state = STFAX_ESCAPE; - deliver_status(ch, HW_MOD_NOCARR); - } - break; - default: - pr_info("isar_rcv_frame protocol (%x)error\n", ch->bch.state); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } -} - -static void -isar_fill_fifo(struct isar_ch *ch) -{ - int count; - u8 msb; - u8 *ptr; - - pr_debug("%s: ch%d tx_skb %d tx_idx %d\n", ch->is->name, ch->bch.nr, - ch->bch.tx_skb ? ch->bch.tx_skb->len : -1, ch->bch.tx_idx); - if (!(ch->is->bstat & - (ch->dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2))) - return; - if (!ch->bch.tx_skb) { - if (!test_bit(FLG_TX_EMPTY, &ch->bch.Flags) || - (ch->bch.state != ISDN_P_B_RAW)) - return; - count = ch->mml; - /* use the card buffer */ - memset(ch->is->buf, ch->bch.fill[0], count); - send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, - 0, count, ch->is->buf); - return; - } - count = ch->bch.tx_skb->len - ch->bch.tx_idx; - if (count <= 0) - return; - if (count > ch->mml) { - msb = 0; - count = ch->mml; - } else { - msb = HDLC_FED; - } - ptr = ch->bch.tx_skb->data + ch->bch.tx_idx; - if (!ch->bch.tx_idx) { - pr_debug("%s: frame start\n", ch->is->name); - if ((ch->bch.state == ISDN_P_B_T30_FAX) && - (ch->cmd == PCTRL_CMD_FTH)) { - if (count > 1) { - if ((ptr[0] == 0xff) && (ptr[1] == 0x13)) { - /* last frame */ - test_and_set_bit(FLG_LASTDATA, - &ch->bch.Flags); - pr_debug("%s: set LASTDATA\n", - ch->is->name); - if (msb == HDLC_FED) - test_and_set_bit(FLG_DLEETX, - &ch->bch.Flags); - } - } - } - msb |= HDLC_FST; - } - ch->bch.tx_idx += count; - switch (ch->bch.state) { - case ISDN_P_NONE: - pr_info("%s: wrong protocol 0\n", __func__); - break; - case ISDN_P_B_RAW: - case ISDN_P_B_L2DTMF: - case ISDN_P_B_MODEM_ASYNC: - send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, - 0, count, ptr); - break; - case ISDN_P_B_HDLC: - send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, - msb, count, ptr); - break; - case ISDN_P_B_T30_FAX: - if (ch->state != STFAX_ACTIV) - pr_debug("%s: not ACTIV\n", ch->is->name); - else if (ch->cmd == PCTRL_CMD_FTH) - send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, - msb, count, ptr); - else if (ch->cmd == PCTRL_CMD_FTM) - send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, - 0, count, ptr); - else - pr_debug("%s: not FTH/FTM\n", ch->is->name); - break; - default: - pr_info("%s: protocol(%x) error\n", - __func__, ch->bch.state); - break; - } -} - -static inline struct isar_ch * -sel_bch_isar(struct isar_hw *isar, u8 dpath) -{ - struct isar_ch *base = &isar->ch[0]; - - if ((!dpath) || (dpath > 2)) - return NULL; - if (base->dpath == dpath) - return base; - base++; - if (base->dpath == dpath) - return base; - return NULL; -} - -static void -send_next(struct isar_ch *ch) -{ - pr_debug("%s: %s ch%d tx_skb %d tx_idx %d\n", ch->is->name, __func__, - ch->bch.nr, ch->bch.tx_skb ? ch->bch.tx_skb->len : -1, - ch->bch.tx_idx); - if (ch->bch.state == ISDN_P_B_T30_FAX) { - if (ch->cmd == PCTRL_CMD_FTH) { - if (test_bit(FLG_LASTDATA, &ch->bch.Flags)) { - pr_debug("set NMD_DATA\n"); - test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags); - } - } else if (ch->cmd == PCTRL_CMD_FTM) { - if (test_bit(FLG_DLEETX, &ch->bch.Flags)) { - test_and_set_bit(FLG_LASTDATA, &ch->bch.Flags); - test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags); - } - } - } - dev_kfree_skb(ch->bch.tx_skb); - if (get_next_bframe(&ch->bch)) { - isar_fill_fifo(ch); - test_and_clear_bit(FLG_TX_EMPTY, &ch->bch.Flags); - } else if (test_bit(FLG_TX_EMPTY, &ch->bch.Flags)) { - isar_fill_fifo(ch); - } else { - if (test_and_clear_bit(FLG_DLEETX, &ch->bch.Flags)) { - if (test_and_clear_bit(FLG_LASTDATA, - &ch->bch.Flags)) { - if (test_and_clear_bit(FLG_NMD_DATA, - &ch->bch.Flags)) { - u8 zd = 0; - send_mbox(ch->is, SET_DPS(ch->dpath) | - ISAR_HIS_SDATA, 0x01, 1, &zd); - } - test_and_set_bit(FLG_LL_OK, &ch->bch.Flags); - } else { - deliver_status(ch, HW_MOD_CONNECT); - } - } else if (test_bit(FLG_FILLEMPTY, &ch->bch.Flags)) { - test_and_set_bit(FLG_TX_EMPTY, &ch->bch.Flags); - } - } -} - -static void -check_send(struct isar_hw *isar, u8 rdm) -{ - struct isar_ch *ch; - - pr_debug("%s: rdm %x\n", isar->name, rdm); - if (rdm & BSTAT_RDM1) { - ch = sel_bch_isar(isar, 1); - if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) { - if (ch->bch.tx_skb && (ch->bch.tx_skb->len > - ch->bch.tx_idx)) - isar_fill_fifo(ch); - else - send_next(ch); - } - } - if (rdm & BSTAT_RDM2) { - ch = sel_bch_isar(isar, 2); - if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) { - if (ch->bch.tx_skb && (ch->bch.tx_skb->len > - ch->bch.tx_idx)) - isar_fill_fifo(ch); - else - send_next(ch); - } - } -} - -static const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4", - "300", "600", "1200", "2400", "4800", "7200", - "9600nt", "9600t", "12000", "14400", "WRONG"}; -static const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21", - "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"}; - -static void -isar_pump_status_rsp(struct isar_ch *ch) { - u8 ril = ch->is->buf[0]; - u8 rim; - - if (!test_and_clear_bit(ISAR_RATE_REQ, &ch->is->Flags)) - return; - if (ril > 14) { - pr_info("%s: wrong pstrsp ril=%d\n", ch->is->name, ril); - ril = 15; - } - switch (ch->is->buf[1]) { - case 0: - rim = 0; - break; - case 0x20: - rim = 2; - break; - case 0x40: - rim = 3; - break; - case 0x41: - rim = 4; - break; - case 0x51: - rim = 5; - break; - case 0x61: - rim = 6; - break; - case 0x71: - rim = 7; - break; - case 0x82: - rim = 8; - break; - case 0x92: - rim = 9; - break; - case 0xa2: - rim = 10; - break; - default: - rim = 1; - break; - } - sprintf(ch->conmsg, "%s %s", dmril[ril], dmrim[rim]); - pr_debug("%s: pump strsp %s\n", ch->is->name, ch->conmsg); -} - -static void -isar_pump_statev_modem(struct isar_ch *ch, u8 devt) { - u8 dps = SET_DPS(ch->dpath); - - switch (devt) { - case PSEV_10MS_TIMER: - pr_debug("%s: pump stev TIMER\n", ch->is->name); - break; - case PSEV_CON_ON: - pr_debug("%s: pump stev CONNECT\n", ch->is->name); - deliver_status(ch, HW_MOD_CONNECT); - break; - case PSEV_CON_OFF: - pr_debug("%s: pump stev NO CONNECT\n", ch->is->name); - send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); - deliver_status(ch, HW_MOD_NOCARR); - break; - case PSEV_V24_OFF: - pr_debug("%s: pump stev V24 OFF\n", ch->is->name); - break; - case PSEV_CTS_ON: - pr_debug("%s: pump stev CTS ON\n", ch->is->name); - break; - case PSEV_CTS_OFF: - pr_debug("%s pump stev CTS OFF\n", ch->is->name); - break; - case PSEV_DCD_ON: - pr_debug("%s: pump stev CARRIER ON\n", ch->is->name); - test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags); - send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); - break; - case PSEV_DCD_OFF: - pr_debug("%s: pump stev CARRIER OFF\n", ch->is->name); - break; - case PSEV_DSR_ON: - pr_debug("%s: pump stev DSR ON\n", ch->is->name); - break; - case PSEV_DSR_OFF: - pr_debug("%s: pump stev DSR_OFF\n", ch->is->name); - break; - case PSEV_REM_RET: - pr_debug("%s: pump stev REMOTE RETRAIN\n", ch->is->name); - break; - case PSEV_REM_REN: - pr_debug("%s: pump stev REMOTE RENEGOTIATE\n", ch->is->name); - break; - case PSEV_GSTN_CLR: - pr_debug("%s: pump stev GSTN CLEAR\n", ch->is->name); - break; - default: - pr_info("u%s: unknown pump stev %x\n", ch->is->name, devt); - break; - } -} - -static void -isar_pump_statev_fax(struct isar_ch *ch, u8 devt) { - u8 dps = SET_DPS(ch->dpath); - u8 p1; - - switch (devt) { - case PSEV_10MS_TIMER: - pr_debug("%s: pump stev TIMER\n", ch->is->name); - break; - case PSEV_RSP_READY: - pr_debug("%s: pump stev RSP_READY\n", ch->is->name); - ch->state = STFAX_READY; - deliver_status(ch, HW_MOD_READY); -#ifdef AUTOCON - if (test_bit(BC_FLG_ORIG, &ch->bch.Flags)) - isar_pump_cmd(bch, HW_MOD_FRH, 3); - else - isar_pump_cmd(bch, HW_MOD_FTH, 3); -#endif - break; - case PSEV_LINE_TX_H: - if (ch->state == STFAX_LINE) { - pr_debug("%s: pump stev LINE_TX_H\n", ch->is->name); - ch->state = STFAX_CONT; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - PCTRL_CMD_CONT, 0, NULL); - } else { - pr_debug("%s: pump stev LINE_TX_H wrong st %x\n", - ch->is->name, ch->state); - } - break; - case PSEV_LINE_RX_H: - if (ch->state == STFAX_LINE) { - pr_debug("%s: pump stev LINE_RX_H\n", ch->is->name); - ch->state = STFAX_CONT; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - PCTRL_CMD_CONT, 0, NULL); - } else { - pr_debug("%s: pump stev LINE_RX_H wrong st %x\n", - ch->is->name, ch->state); - } - break; - case PSEV_LINE_TX_B: - if (ch->state == STFAX_LINE) { - pr_debug("%s: pump stev LINE_TX_B\n", ch->is->name); - ch->state = STFAX_CONT; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - PCTRL_CMD_CONT, 0, NULL); - } else { - pr_debug("%s: pump stev LINE_TX_B wrong st %x\n", - ch->is->name, ch->state); - } - break; - case PSEV_LINE_RX_B: - if (ch->state == STFAX_LINE) { - pr_debug("%s: pump stev LINE_RX_B\n", ch->is->name); - ch->state = STFAX_CONT; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - PCTRL_CMD_CONT, 0, NULL); - } else { - pr_debug("%s: pump stev LINE_RX_B wrong st %x\n", - ch->is->name, ch->state); - } - break; - case PSEV_RSP_CONN: - if (ch->state == STFAX_CONT) { - pr_debug("%s: pump stev RSP_CONN\n", ch->is->name); - ch->state = STFAX_ACTIV; - test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags); - send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); - if (ch->cmd == PCTRL_CMD_FTH) { - int delay = (ch->mod == 3) ? 1000 : 200; - /* 1s (200 ms) Flags before data */ - if (test_and_set_bit(FLG_FTI_RUN, - &ch->bch.Flags)) - timer_delete(&ch->ftimer); - ch->ftimer.expires = - jiffies + ((delay * HZ) / 1000); - test_and_set_bit(FLG_LL_CONN, - &ch->bch.Flags); - add_timer(&ch->ftimer); - } else { - deliver_status(ch, HW_MOD_CONNECT); - } - } else { - pr_debug("%s: pump stev RSP_CONN wrong st %x\n", - ch->is->name, ch->state); - } - break; - case PSEV_FLAGS_DET: - pr_debug("%s: pump stev FLAGS_DET\n", ch->is->name); - break; - case PSEV_RSP_DISC: - pr_debug("%s: pump stev RSP_DISC state(%d)\n", - ch->is->name, ch->state); - if (ch->state == STFAX_ESCAPE) { - p1 = 5; - switch (ch->newcmd) { - case 0: - ch->state = STFAX_READY; - break; - case PCTRL_CMD_FTM: - p1 = 2; - fallthrough; - case PCTRL_CMD_FTH: - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - PCTRL_CMD_SILON, 1, &p1); - ch->state = STFAX_SILDET; - break; - case PCTRL_CMD_FRH: - case PCTRL_CMD_FRM: - ch->mod = ch->newmod; - p1 = ch->newmod; - ch->newmod = 0; - ch->cmd = ch->newcmd; - ch->newcmd = 0; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - ch->cmd, 1, &p1); - ch->state = STFAX_LINE; - ch->try_mod = 3; - break; - default: - pr_debug("%s: RSP_DISC unknown newcmd %x\n", - ch->is->name, ch->newcmd); - break; - } - } else if (ch->state == STFAX_ACTIV) { - if (test_and_clear_bit(FLG_LL_OK, &ch->bch.Flags)) - deliver_status(ch, HW_MOD_OK); - else if (ch->cmd == PCTRL_CMD_FRM) - deliver_status(ch, HW_MOD_NOCARR); - else - deliver_status(ch, HW_MOD_FCERROR); - ch->state = STFAX_READY; - } else if (ch->state != STFAX_SILDET) { - /* ignore in STFAX_SILDET */ - ch->state = STFAX_READY; - deliver_status(ch, HW_MOD_FCERROR); - } - break; - case PSEV_RSP_SILDET: - pr_debug("%s: pump stev RSP_SILDET\n", ch->is->name); - if (ch->state == STFAX_SILDET) { - ch->mod = ch->newmod; - p1 = ch->newmod; - ch->newmod = 0; - ch->cmd = ch->newcmd; - ch->newcmd = 0; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - ch->cmd, 1, &p1); - ch->state = STFAX_LINE; - ch->try_mod = 3; - } - break; - case PSEV_RSP_SILOFF: - pr_debug("%s: pump stev RSP_SILOFF\n", ch->is->name); - break; - case PSEV_RSP_FCERR: - if (ch->state == STFAX_LINE) { - pr_debug("%s: pump stev RSP_FCERR try %d\n", - ch->is->name, ch->try_mod); - if (ch->try_mod--) { - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - ch->cmd, 1, &ch->mod); - break; - } - } - pr_debug("%s: pump stev RSP_FCERR\n", ch->is->name); - ch->state = STFAX_ESCAPE; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, - 0, NULL); - deliver_status(ch, HW_MOD_FCERROR); - break; - default: - break; - } -} - -void -mISDNisar_irq(struct isar_hw *isar) -{ - struct isar_ch *ch; - - get_irq_infos(isar); - switch (isar->iis & ISAR_IIS_MSCMSD) { - case ISAR_IIS_RDATA: - ch = sel_bch_isar(isar, isar->iis >> 6); - if (ch) - isar_rcv_frame(ch); - else { - pr_debug("%s: ISAR spurious IIS_RDATA %x/%x/%x\n", - isar->name, isar->iis, isar->cmsb, - isar->clsb); - isar->write_reg(isar->hw, ISAR_IIA, 0); - } - break; - case ISAR_IIS_GSTEV: - isar->write_reg(isar->hw, ISAR_IIA, 0); - isar->bstat |= isar->cmsb; - check_send(isar, isar->cmsb); - break; - case ISAR_IIS_BSTEV: -#ifdef ERROR_STATISTIC - ch = sel_bch_isar(isar, isar->iis >> 6); - if (ch) { - if (isar->cmsb == BSTEV_TBO) - ch->bch.err_tx++; - if (isar->cmsb == BSTEV_RBO) - ch->bch.err_rdo++; - } -#endif - pr_debug("%s: Buffer STEV dpath%d msb(%x)\n", - isar->name, isar->iis >> 6, isar->cmsb); - isar->write_reg(isar->hw, ISAR_IIA, 0); - break; - case ISAR_IIS_PSTEV: - ch = sel_bch_isar(isar, isar->iis >> 6); - if (ch) { - rcv_mbox(isar, NULL); - if (ch->bch.state == ISDN_P_B_MODEM_ASYNC) - isar_pump_statev_modem(ch, isar->cmsb); - else if (ch->bch.state == ISDN_P_B_T30_FAX) - isar_pump_statev_fax(ch, isar->cmsb); - else if (ch->bch.state == ISDN_P_B_RAW) { - int tt; - tt = isar->cmsb | 0x30; - if (tt == 0x3e) - tt = '*'; - else if (tt == 0x3f) - tt = '#'; - else if (tt > '9') - tt += 7; - tt |= DTMF_TONE_VAL; - _queue_data(&ch->bch.ch, PH_CONTROL_IND, - MISDN_ID_ANY, sizeof(tt), &tt, - GFP_ATOMIC); - } else - pr_debug("%s: ISAR IIS_PSTEV pm %d sta %x\n", - isar->name, ch->bch.state, - isar->cmsb); - } else { - pr_debug("%s: ISAR spurious IIS_PSTEV %x/%x/%x\n", - isar->name, isar->iis, isar->cmsb, - isar->clsb); - isar->write_reg(isar->hw, ISAR_IIA, 0); - } - break; - case ISAR_IIS_PSTRSP: - ch = sel_bch_isar(isar, isar->iis >> 6); - if (ch) { - rcv_mbox(isar, NULL); - isar_pump_status_rsp(ch); - } else { - pr_debug("%s: ISAR spurious IIS_PSTRSP %x/%x/%x\n", - isar->name, isar->iis, isar->cmsb, - isar->clsb); - isar->write_reg(isar->hw, ISAR_IIA, 0); - } - break; - case ISAR_IIS_DIAG: - case ISAR_IIS_BSTRSP: - case ISAR_IIS_IOM2RSP: - rcv_mbox(isar, NULL); - break; - case ISAR_IIS_INVMSG: - rcv_mbox(isar, NULL); - pr_debug("%s: invalid msg his:%x\n", isar->name, isar->cmsb); - break; - default: - rcv_mbox(isar, NULL); - pr_debug("%s: unhandled msg iis(%x) ctrl(%x/%x)\n", - isar->name, isar->iis, isar->cmsb, isar->clsb); - break; - } -} -EXPORT_SYMBOL(mISDNisar_irq); - -static void -ftimer_handler(struct timer_list *t) -{ - struct isar_ch *ch = timer_container_of(ch, t, ftimer); - - pr_debug("%s: ftimer flags %lx\n", ch->is->name, ch->bch.Flags); - test_and_clear_bit(FLG_FTI_RUN, &ch->bch.Flags); - if (test_and_clear_bit(FLG_LL_CONN, &ch->bch.Flags)) - deliver_status(ch, HW_MOD_CONNECT); -} - -static void -setup_pump(struct isar_ch *ch) { - u8 dps = SET_DPS(ch->dpath); - u8 ctrl, param[6]; - - switch (ch->bch.state) { - case ISDN_P_NONE: - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL); - break; - case ISDN_P_B_L2DTMF: - if (test_bit(FLG_DTMFSEND, &ch->bch.Flags)) { - param[0] = 5; /* TOA 5 db */ - send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, - PMOD_DTMF_TRANS, 1, param); - } else { - param[0] = 40; /* REL -46 dbm */ - send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, - PMOD_DTMF, 1, param); - } - fallthrough; - case ISDN_P_B_MODEM_ASYNC: - ctrl = PMOD_DATAMODEM; - if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) { - ctrl |= PCTRL_ORIG; - param[5] = PV32P6_CTN; - } else { - param[5] = PV32P6_ATN; - } - param[0] = 6; /* 6 db */ - param[1] = PV32P2_V23R | PV32P2_V22A | PV32P2_V22B | - PV32P2_V22C | PV32P2_V21 | PV32P2_BEL; - param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B; - param[3] = PV32P4_UT144; - param[4] = PV32P5_UT144; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param); - break; - case ISDN_P_B_T30_FAX: - ctrl = PMOD_FAX; - if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) { - ctrl |= PCTRL_ORIG; - param[1] = PFAXP2_CTN; - } else { - param[1] = PFAXP2_ATN; - } - param[0] = 6; /* 6 db */ - send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param); - ch->state = STFAX_NULL; - ch->newcmd = 0; - ch->newmod = 0; - test_and_set_bit(FLG_FTI_RUN, &ch->bch.Flags); - break; - } - udelay(1000); - send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); - udelay(1000); -} - -static void -setup_sart(struct isar_ch *ch) { - u8 dps = SET_DPS(ch->dpath); - u8 ctrl, param[2] = {0, 0}; - - switch (ch->bch.state) { - case ISDN_P_NONE: - send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, - 0, NULL); - break; - case ISDN_P_B_RAW: - case ISDN_P_B_L2DTMF: - send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, - 2, param); - break; - case ISDN_P_B_HDLC: - case ISDN_P_B_T30_FAX: - send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, - 1, param); - break; - case ISDN_P_B_MODEM_ASYNC: - ctrl = SMODE_V14 | SCTRL_HDMC_BOTH; - param[0] = S_P1_CHS_8; - param[1] = S_P2_BFT_DEF; - send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, ctrl, 2, param); - break; - } - udelay(1000); - send_mbox(ch->is, dps | ISAR_HIS_BSTREQ, 0, 0, NULL); - udelay(1000); -} - -static void -setup_iom2(struct isar_ch *ch) { - u8 dps = SET_DPS(ch->dpath); - u8 cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD, 0, 0, 0, 0}; - - if (ch->bch.nr == 2) { - msg[1] = 1; - msg[3] = 1; - } - switch (ch->bch.state) { - case ISDN_P_NONE: - cmsb = 0; - /* dummy slot */ - msg[1] = ch->dpath + 2; - msg[3] = ch->dpath + 2; - break; - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - break; - case ISDN_P_B_MODEM_ASYNC: - case ISDN_P_B_T30_FAX: - cmsb |= IOM_CTRL_RCV; - fallthrough; - case ISDN_P_B_L2DTMF: - if (test_bit(FLG_DTMFSEND, &ch->bch.Flags)) - cmsb |= IOM_CTRL_RCV; - cmsb |= IOM_CTRL_ALAW; - break; - } - send_mbox(ch->is, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg); - udelay(1000); - send_mbox(ch->is, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL); - udelay(1000); -} - -static int -modeisar(struct isar_ch *ch, u32 bprotocol) -{ - /* Here we are selecting the best datapath for requested protocol */ - if (ch->bch.state == ISDN_P_NONE) { /* New Setup */ - switch (bprotocol) { - case ISDN_P_NONE: /* init */ - if (!ch->dpath) - /* no init for dpath 0 */ - return 0; - test_and_clear_bit(FLG_HDLC, &ch->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &ch->bch.Flags); - break; - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - /* best is datapath 2 */ - if (!test_and_set_bit(ISAR_DP2_USE, &ch->is->Flags)) - ch->dpath = 2; - else if (!test_and_set_bit(ISAR_DP1_USE, - &ch->is->Flags)) - ch->dpath = 1; - else { - pr_info("modeisar both paths in use\n"); - return -EBUSY; - } - if (bprotocol == ISDN_P_B_HDLC) - test_and_set_bit(FLG_HDLC, &ch->bch.Flags); - else - test_and_set_bit(FLG_TRANSPARENT, - &ch->bch.Flags); - break; - case ISDN_P_B_MODEM_ASYNC: - case ISDN_P_B_T30_FAX: - case ISDN_P_B_L2DTMF: - /* only datapath 1 */ - if (!test_and_set_bit(ISAR_DP1_USE, &ch->is->Flags)) - ch->dpath = 1; - else { - pr_info("%s: ISAR modeisar analog functions" - "only with DP1\n", ch->is->name); - return -EBUSY; - } - break; - default: - pr_info("%s: protocol not known %x\n", ch->is->name, - bprotocol); - return -ENOPROTOOPT; - } - } - pr_debug("%s: ISAR ch%d dp%d protocol %x->%x\n", ch->is->name, - ch->bch.nr, ch->dpath, ch->bch.state, bprotocol); - ch->bch.state = bprotocol; - setup_pump(ch); - setup_iom2(ch); - setup_sart(ch); - if (ch->bch.state == ISDN_P_NONE) { - /* Clear resources */ - if (ch->dpath == 1) - test_and_clear_bit(ISAR_DP1_USE, &ch->is->Flags); - else if (ch->dpath == 2) - test_and_clear_bit(ISAR_DP2_USE, &ch->is->Flags); - ch->dpath = 0; - ch->is->ctrl(ch->is->hw, HW_DEACT_IND, ch->bch.nr); - } else - ch->is->ctrl(ch->is->hw, HW_ACTIVATE_IND, ch->bch.nr); - return 0; -} - -static void -isar_pump_cmd(struct isar_ch *ch, u32 cmd, u8 para) -{ - u8 dps = SET_DPS(ch->dpath); - u8 ctrl = 0, nom = 0, p1 = 0; - - pr_debug("%s: isar_pump_cmd %x/%x state(%x)\n", - ch->is->name, cmd, para, ch->bch.state); - switch (cmd) { - case HW_MOD_FTM: - if (ch->state == STFAX_READY) { - p1 = para; - ctrl = PCTRL_CMD_FTM; - nom = 1; - ch->state = STFAX_LINE; - ch->cmd = ctrl; - ch->mod = para; - ch->newmod = 0; - ch->newcmd = 0; - ch->try_mod = 3; - } else if ((ch->state == STFAX_ACTIV) && - (ch->cmd == PCTRL_CMD_FTM) && (ch->mod == para)) - deliver_status(ch, HW_MOD_CONNECT); - else { - ch->newmod = para; - ch->newcmd = PCTRL_CMD_FTM; - nom = 0; - ctrl = PCTRL_CMD_ESC; - ch->state = STFAX_ESCAPE; - } - break; - case HW_MOD_FTH: - if (ch->state == STFAX_READY) { - p1 = para; - ctrl = PCTRL_CMD_FTH; - nom = 1; - ch->state = STFAX_LINE; - ch->cmd = ctrl; - ch->mod = para; - ch->newmod = 0; - ch->newcmd = 0; - ch->try_mod = 3; - } else if ((ch->state == STFAX_ACTIV) && - (ch->cmd == PCTRL_CMD_FTH) && (ch->mod == para)) - deliver_status(ch, HW_MOD_CONNECT); - else { - ch->newmod = para; - ch->newcmd = PCTRL_CMD_FTH; - nom = 0; - ctrl = PCTRL_CMD_ESC; - ch->state = STFAX_ESCAPE; - } - break; - case HW_MOD_FRM: - if (ch->state == STFAX_READY) { - p1 = para; - ctrl = PCTRL_CMD_FRM; - nom = 1; - ch->state = STFAX_LINE; - ch->cmd = ctrl; - ch->mod = para; - ch->newmod = 0; - ch->newcmd = 0; - ch->try_mod = 3; - } else if ((ch->state == STFAX_ACTIV) && - (ch->cmd == PCTRL_CMD_FRM) && (ch->mod == para)) - deliver_status(ch, HW_MOD_CONNECT); - else { - ch->newmod = para; - ch->newcmd = PCTRL_CMD_FRM; - nom = 0; - ctrl = PCTRL_CMD_ESC; - ch->state = STFAX_ESCAPE; - } - break; - case HW_MOD_FRH: - if (ch->state == STFAX_READY) { - p1 = para; - ctrl = PCTRL_CMD_FRH; - nom = 1; - ch->state = STFAX_LINE; - ch->cmd = ctrl; - ch->mod = para; - ch->newmod = 0; - ch->newcmd = 0; - ch->try_mod = 3; - } else if ((ch->state == STFAX_ACTIV) && - (ch->cmd == PCTRL_CMD_FRH) && (ch->mod == para)) - deliver_status(ch, HW_MOD_CONNECT); - else { - ch->newmod = para; - ch->newcmd = PCTRL_CMD_FRH; - nom = 0; - ctrl = PCTRL_CMD_ESC; - ch->state = STFAX_ESCAPE; - } - break; - case PCTRL_CMD_TDTMF: - p1 = para; - nom = 1; - ctrl = PCTRL_CMD_TDTMF; - break; - } - if (ctrl) - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1); -} - -static void -isar_setup(struct isar_hw *isar) -{ - u8 msg; - int i; - - /* Dpath 1, 2 */ - msg = 61; - for (i = 0; i < 2; i++) { - /* Buffer Config */ - send_mbox(isar, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) | - ISAR_HIS_P12CFG, 4, 1, &msg); - isar->ch[i].mml = msg; - isar->ch[i].bch.state = 0; - isar->ch[i].dpath = i + 1; - modeisar(&isar->ch[i], ISDN_P_NONE); - } -} - -static int -isar_l2l1(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct isar_ch *ich = container_of(bch, struct isar_ch, bch); - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - u32 id, *val; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(ich->is->hwlock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - ret = 0; - isar_fill_fifo(ich); - } - spin_unlock_irqrestore(ich->is->hwlock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(ich->is->hwlock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = modeisar(ich, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(ich->is->hwlock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - spin_lock_irqsave(ich->is->hwlock, flags); - mISDN_clear_bchannel(bch); - modeisar(ich, ISDN_P_NONE); - spin_unlock_irqrestore(ich->is->hwlock, flags); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - case PH_CONTROL_REQ: - val = (u32 *)skb->data; - pr_debug("%s: PH_CONTROL | REQUEST %x/%x\n", ich->is->name, - hh->id, *val); - if ((hh->id == 0) && ((*val & ~DTMF_TONE_MASK) == - DTMF_TONE_VAL)) { - if (bch->state == ISDN_P_B_L2DTMF) { - char tt = *val & DTMF_TONE_MASK; - - if (tt == '*') - tt = 0x1e; - else if (tt == '#') - tt = 0x1f; - else if (tt > '9') - tt -= 7; - tt &= 0x1f; - spin_lock_irqsave(ich->is->hwlock, flags); - isar_pump_cmd(ich, PCTRL_CMD_TDTMF, tt); - spin_unlock_irqrestore(ich->is->hwlock, flags); - } else { - pr_info("%s: DTMF send wrong protocol %x\n", - __func__, bch->state); - return -EINVAL; - } - } else if ((hh->id == HW_MOD_FRM) || (hh->id == HW_MOD_FRH) || - (hh->id == HW_MOD_FTM) || (hh->id == HW_MOD_FTH)) { - for (id = 0; id < FAXMODCNT; id++) - if (faxmodulation[id] == *val) - break; - if ((FAXMODCNT > id) && - test_bit(FLG_INITIALIZED, &bch->Flags)) { - pr_debug("%s: isar: new mod\n", ich->is->name); - isar_pump_cmd(ich, hh->id, *val); - ret = 0; - } else { - pr_info("%s: wrong modulation\n", - ich->is->name); - ret = -EINVAL; - } - } else if (hh->id == HW_MOD_LASTDATA) - test_and_set_bit(FLG_DLEETX, &bch->Flags); - else { - pr_info("%s: unknown PH_CONTROL_REQ %x\n", - ich->is->name, hh->id); - ret = -EINVAL; - } - fallthrough; - default: - pr_info("%s: %s unknown prim(%x,%x)\n", - ich->is->name, __func__, hh->prim, hh->id); - ret = -EINVAL; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} - -static int -isar_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct isar_ch *ich = container_of(bch, struct isar_ch, bch); - int ret = -EINVAL; - u_long flags; - - pr_debug("%s: %s cmd:%x %p\n", ich->is->name, __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - cancel_work_sync(&bch->workq); - spin_lock_irqsave(ich->is->hwlock, flags); - mISDN_clear_bchannel(bch); - modeisar(ich, ISDN_P_NONE); - spin_unlock_irqrestore(ich->is->hwlock, flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(ich->is->owner); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - pr_info("%s: %s unknown prim(%x)\n", - ich->is->name, __func__, cmd); - } - return ret; -} - -static void -free_isar(struct isar_hw *isar) -{ - modeisar(&isar->ch[0], ISDN_P_NONE); - modeisar(&isar->ch[1], ISDN_P_NONE); - timer_delete(&isar->ch[0].ftimer); - timer_delete(&isar->ch[1].ftimer); - test_and_clear_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags); - test_and_clear_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags); -} - -static int -init_isar(struct isar_hw *isar) -{ - int cnt = 3; - - while (cnt--) { - isar->version = ISARVersion(isar); - if (isar->ch[0].bch.debug & DEBUG_HW) - pr_notice("%s: Testing version %d (%d time)\n", - isar->name, isar->version, 3 - cnt); - if (isar->version == 1) - break; - isar->ctrl(isar->hw, HW_RESET_REQ, 0); - } - if (isar->version != 1) - return -EINVAL; - timer_setup(&isar->ch[0].ftimer, ftimer_handler, 0); - test_and_set_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags); - timer_setup(&isar->ch[1].ftimer, ftimer_handler, 0); - test_and_set_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags); - return 0; -} - -static int -isar_open(struct isar_hw *isar, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &isar->ch[rq->adr.channel - 1].bch; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - return 0; -} - -u32 -mISDNisar_init(struct isar_hw *isar, void *hw) -{ - u32 ret, i; - - isar->hw = hw; - for (i = 0; i < 2; i++) { - isar->ch[i].bch.nr = i + 1; - mISDN_initbchannel(&isar->ch[i].bch, MAX_DATA_MEM, 32); - isar->ch[i].bch.ch.nr = i + 1; - isar->ch[i].bch.ch.send = &isar_l2l1; - isar->ch[i].bch.ch.ctrl = isar_bctrl; - isar->ch[i].bch.hw = hw; - isar->ch[i].is = isar; - } - - isar->init = &init_isar; - isar->release = &free_isar; - isar->firmware = &load_firmware; - isar->open = &isar_open; - - ret = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_L2DTMF & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_MODEM_ASYNC & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_T30_FAX & ISDN_P_B_MASK)); - - return ret; -} -EXPORT_SYMBOL(mISDNisar_init); - -static int __init isar_mod_init(void) -{ - pr_notice("mISDN: ISAR driver Rev. %s\n", ISAR_REV); - return 0; -} - -static void __exit isar_mod_cleanup(void) -{ - pr_notice("mISDN: ISAR module unloaded\n"); -} -module_init(isar_mod_init); -module_exit(isar_mod_cleanup); diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c deleted file mode 100644 index 8d740d8eacec..000000000000 --- a/drivers/isdn/hardware/mISDN/netjet.c +++ /dev/null @@ -1,1154 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * NETJet mISDN driver - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include "ipac.h" -#include "iohelper.h" -#include "netjet.h" -#include "isdnhdlc.h" - -#define NETJET_REV "2.0" - -enum nj_types { - NETJET_S_TJ300, - NETJET_S_TJ320, - ENTERNOW__TJ320, -}; - -struct tiger_dma { - size_t size; - u32 *start; - int idx; - u32 dmastart; - u32 dmairq; - u32 dmaend; - u32 dmacur; -}; - -struct tiger_hw; - -struct tiger_ch { - struct bchannel bch; - struct tiger_hw *nj; - int idx; - int free; - int lastrx; - u16 rxstate; - u16 txstate; - struct isdnhdlc_vars hsend; - struct isdnhdlc_vars hrecv; - u8 *hsbuf; - u8 *hrbuf; -}; - -#define TX_INIT 0x0001 -#define TX_IDLE 0x0002 -#define TX_RUN 0x0004 -#define TX_UNDERRUN 0x0100 -#define RX_OVERRUN 0x0100 - -#define LOG_SIZE 64 - -struct tiger_hw { - struct list_head list; - struct pci_dev *pdev; - char name[MISDN_MAX_IDLEN]; - enum nj_types typ; - int irq; - u32 irqcnt; - u32 base; - size_t base_s; - dma_addr_t dma; - void *dma_p; - spinlock_t lock; /* lock HW */ - struct isac_hw isac; - struct tiger_dma send; - struct tiger_dma recv; - struct tiger_ch bc[2]; - u8 ctrlreg; - u8 dmactrl; - u8 auxd; - u8 last_is0; - u8 irqmask0; - char log[LOG_SIZE]; -}; - -static LIST_HEAD(Cards); -static DEFINE_RWLOCK(card_lock); /* protect Cards */ -static u32 debug; -static int nj_cnt; - -static void -_set_debug(struct tiger_hw *card) -{ - card->isac.dch.debug = debug; - card->bc[0].bch.debug = debug; - card->bc[1].bch.debug = debug; -} - -static int -set_debug(const char *val, const struct kernel_param *kp) -{ - int ret; - struct tiger_hw *card; - - ret = param_set_uint(val, kp); - if (!ret) { - read_lock(&card_lock); - list_for_each_entry(card, &Cards, list) - _set_debug(card); - read_unlock(&card_lock); - } - return ret; -} - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for NETJet cards"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(NETJET_REV); -module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Netjet debug mask"); - -static void -nj_disable_hwirq(struct tiger_hw *card) -{ - outb(0, card->base + NJ_IRQMASK0); - outb(0, card->base + NJ_IRQMASK1); -} - - -static u8 -ReadISAC_nj(void *p, u8 offset) -{ - struct tiger_hw *card = p; - u8 ret; - - card->auxd &= 0xfc; - card->auxd |= (offset >> 4) & 3; - outb(card->auxd, card->base + NJ_AUXDATA); - ret = inb(card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2)); - return ret; -} - -static void -WriteISAC_nj(void *p, u8 offset, u8 value) -{ - struct tiger_hw *card = p; - - card->auxd &= 0xfc; - card->auxd |= (offset >> 4) & 3; - outb(card->auxd, card->base + NJ_AUXDATA); - outb(value, card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2)); -} - -static void -ReadFiFoISAC_nj(void *p, u8 offset, u8 *data, int size) -{ - struct tiger_hw *card = p; - - card->auxd &= 0xfc; - outb(card->auxd, card->base + NJ_AUXDATA); - insb(card->base + NJ_ISAC_OFF, data, size); -} - -static void -WriteFiFoISAC_nj(void *p, u8 offset, u8 *data, int size) -{ - struct tiger_hw *card = p; - - card->auxd &= 0xfc; - outb(card->auxd, card->base + NJ_AUXDATA); - outsb(card->base + NJ_ISAC_OFF, data, size); -} - -static void -fill_mem(struct tiger_ch *bc, u32 idx, u32 cnt, u32 fill) -{ - struct tiger_hw *card = bc->bch.hw; - u32 mask = 0xff, val; - - pr_debug("%s: B%1d fill %02x len %d idx %d/%d\n", card->name, - bc->bch.nr, fill, cnt, idx, card->send.idx); - if (bc->bch.nr & 2) { - fill <<= 8; - mask <<= 8; - } - mask ^= 0xffffffff; - while (cnt--) { - val = card->send.start[idx]; - val &= mask; - val |= fill; - card->send.start[idx++] = val; - if (idx >= card->send.size) - idx = 0; - } -} - -static int -mode_tiger(struct tiger_ch *bc, u32 protocol) -{ - struct tiger_hw *card = bc->bch.hw; - - pr_debug("%s: B%1d protocol %x-->%x\n", card->name, - bc->bch.nr, bc->bch.state, protocol); - switch (protocol) { - case ISDN_P_NONE: - if (bc->bch.state == ISDN_P_NONE) - break; - fill_mem(bc, 0, card->send.size, 0xff); - bc->bch.state = protocol; - /* only stop dma and interrupts if both channels NULL */ - if ((card->bc[0].bch.state == ISDN_P_NONE) && - (card->bc[1].bch.state == ISDN_P_NONE)) { - card->dmactrl = 0; - outb(card->dmactrl, card->base + NJ_DMACTRL); - outb(0, card->base + NJ_IRQMASK0); - } - test_and_clear_bit(FLG_HDLC, &bc->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &bc->bch.Flags); - bc->txstate = 0; - bc->rxstate = 0; - bc->lastrx = -1; - break; - case ISDN_P_B_RAW: - test_and_set_bit(FLG_TRANSPARENT, &bc->bch.Flags); - bc->bch.state = protocol; - bc->idx = 0; - bc->free = card->send.size / 2; - bc->rxstate = 0; - bc->txstate = TX_INIT | TX_IDLE; - bc->lastrx = -1; - if (!card->dmactrl) { - card->dmactrl = 1; - outb(card->dmactrl, card->base + NJ_DMACTRL); - outb(0x0f, card->base + NJ_IRQMASK0); - } - break; - case ISDN_P_B_HDLC: - test_and_set_bit(FLG_HDLC, &bc->bch.Flags); - bc->bch.state = protocol; - bc->idx = 0; - bc->free = card->send.size / 2; - bc->rxstate = 0; - bc->txstate = TX_INIT | TX_IDLE; - isdnhdlc_rcv_init(&bc->hrecv, 0); - isdnhdlc_out_init(&bc->hsend, 0); - bc->lastrx = -1; - if (!card->dmactrl) { - card->dmactrl = 1; - outb(card->dmactrl, card->base + NJ_DMACTRL); - outb(0x0f, card->base + NJ_IRQMASK0); - } - break; - default: - pr_info("%s: %s protocol %x not handled\n", card->name, - __func__, protocol); - return -ENOPROTOOPT; - } - card->send.dmacur = inl(card->base + NJ_DMA_READ_ADR); - card->recv.dmacur = inl(card->base + NJ_DMA_WRITE_ADR); - card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2; - card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2; - pr_debug("%s: %s ctrl %x irq %02x/%02x idx %d/%d\n", - card->name, __func__, - inb(card->base + NJ_DMACTRL), - inb(card->base + NJ_IRQMASK0), - inb(card->base + NJ_IRQSTAT0), - card->send.idx, - card->recv.idx); - return 0; -} - -static void -nj_reset(struct tiger_hw *card) -{ - outb(0xff, card->base + NJ_CTRL); /* Reset On */ - mdelay(1); - - /* now edge triggered for TJ320 GE 13/07/00 */ - /* see comment in IRQ function */ - if (card->typ == NETJET_S_TJ320) /* TJ320 */ - card->ctrlreg = 0x40; /* Reset Off and status read clear */ - else - card->ctrlreg = 0x00; /* Reset Off and status read clear */ - outb(card->ctrlreg, card->base + NJ_CTRL); - mdelay(10); - - /* configure AUX pins (all output except ISAC IRQ pin) */ - card->auxd = 0; - card->dmactrl = 0; - outb(~NJ_ISACIRQ, card->base + NJ_AUXCTRL); - outb(NJ_ISACIRQ, card->base + NJ_IRQMASK1); - outb(card->auxd, card->base + NJ_AUXDATA); -} - -static int -inittiger(struct tiger_hw *card) -{ - int i; - - card->dma_p = dma_alloc_coherent(&card->pdev->dev, NJ_DMA_SIZE, - &card->dma, GFP_ATOMIC); - if (!card->dma_p) { - pr_info("%s: No DMA memory\n", card->name); - return -ENOMEM; - } - if ((u64)card->dma > 0xffffffff) { - pr_info("%s: DMA outside 32 bit\n", card->name); - return -ENOMEM; - } - for (i = 0; i < 2; i++) { - card->bc[i].hsbuf = kmalloc(NJ_DMA_TXSIZE, GFP_ATOMIC); - if (!card->bc[i].hsbuf) { - pr_info("%s: no B%d send buffer\n", card->name, i + 1); - return -ENOMEM; - } - card->bc[i].hrbuf = kmalloc(NJ_DMA_RXSIZE, GFP_ATOMIC); - if (!card->bc[i].hrbuf) { - pr_info("%s: no B%d recv buffer\n", card->name, i + 1); - return -ENOMEM; - } - } - memset(card->dma_p, 0xff, NJ_DMA_SIZE); - - card->send.start = card->dma_p; - card->send.dmastart = (u32)card->dma; - card->send.dmaend = card->send.dmastart + - (4 * (NJ_DMA_TXSIZE - 1)); - card->send.dmairq = card->send.dmastart + - (4 * ((NJ_DMA_TXSIZE / 2) - 1)); - card->send.size = NJ_DMA_TXSIZE; - - if (debug & DEBUG_HW) - pr_notice("%s: send buffer phy %#x - %#x - %#x virt %p" - " size %zu u32\n", card->name, - card->send.dmastart, card->send.dmairq, - card->send.dmaend, card->send.start, card->send.size); - - outl(card->send.dmastart, card->base + NJ_DMA_READ_START); - outl(card->send.dmairq, card->base + NJ_DMA_READ_IRQ); - outl(card->send.dmaend, card->base + NJ_DMA_READ_END); - - card->recv.start = card->dma_p + (NJ_DMA_SIZE / 2); - card->recv.dmastart = (u32)card->dma + (NJ_DMA_SIZE / 2); - card->recv.dmaend = card->recv.dmastart + - (4 * (NJ_DMA_RXSIZE - 1)); - card->recv.dmairq = card->recv.dmastart + - (4 * ((NJ_DMA_RXSIZE / 2) - 1)); - card->recv.size = NJ_DMA_RXSIZE; - - if (debug & DEBUG_HW) - pr_notice("%s: recv buffer phy %#x - %#x - %#x virt %p" - " size %zu u32\n", card->name, - card->recv.dmastart, card->recv.dmairq, - card->recv.dmaend, card->recv.start, card->recv.size); - - outl(card->recv.dmastart, card->base + NJ_DMA_WRITE_START); - outl(card->recv.dmairq, card->base + NJ_DMA_WRITE_IRQ); - outl(card->recv.dmaend, card->base + NJ_DMA_WRITE_END); - return 0; -} - -static void -read_dma(struct tiger_ch *bc, u32 idx, int cnt) -{ - struct tiger_hw *card = bc->bch.hw; - int i, stat; - u32 val; - u8 *p, *pn; - - if (bc->lastrx == idx) { - bc->rxstate |= RX_OVERRUN; - pr_info("%s: B%1d overrun at idx %d\n", card->name, - bc->bch.nr, idx); - } - bc->lastrx = idx; - if (test_bit(FLG_RX_OFF, &bc->bch.Flags)) { - bc->bch.dropcnt += cnt; - return; - } - stat = bchannel_get_rxbuf(&bc->bch, cnt); - /* only transparent use the count here, HDLC overun is detected later */ - if (stat == -ENOMEM) { - pr_warn("%s.B%d: No memory for %d bytes\n", - card->name, bc->bch.nr, cnt); - return; - } - if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) - p = skb_put(bc->bch.rx_skb, cnt); - else - p = bc->hrbuf; - - for (i = 0; i < cnt; i++) { - val = card->recv.start[idx++]; - if (bc->bch.nr & 2) - val >>= 8; - if (idx >= card->recv.size) - idx = 0; - p[i] = val & 0xff; - } - - if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) { - recv_Bchannel(&bc->bch, 0, false); - return; - } - - pn = bc->hrbuf; - while (cnt > 0) { - stat = isdnhdlc_decode(&bc->hrecv, pn, cnt, &i, - bc->bch.rx_skb->data, bc->bch.maxlen); - if (stat > 0) { /* valid frame received */ - p = skb_put(bc->bch.rx_skb, stat); - if (debug & DEBUG_HW_BFIFO) { - snprintf(card->log, LOG_SIZE, - "B%1d-recv %s %d ", bc->bch.nr, - card->name, stat); - print_hex_dump_bytes(card->log, - DUMP_PREFIX_OFFSET, p, - stat); - } - recv_Bchannel(&bc->bch, 0, false); - stat = bchannel_get_rxbuf(&bc->bch, bc->bch.maxlen); - if (stat < 0) { - pr_warn("%s.B%d: No memory for %d bytes\n", - card->name, bc->bch.nr, cnt); - return; - } - } else if (stat == -HDLC_CRC_ERROR) { - pr_info("%s: B%1d receive frame CRC error\n", - card->name, bc->bch.nr); - } else if (stat == -HDLC_FRAMING_ERROR) { - pr_info("%s: B%1d receive framing error\n", - card->name, bc->bch.nr); - } else if (stat == -HDLC_LENGTH_ERROR) { - pr_info("%s: B%1d receive frame too long (> %d)\n", - card->name, bc->bch.nr, bc->bch.maxlen); - } - pn += i; - cnt -= i; - } -} - -static void -recv_tiger(struct tiger_hw *card, u8 irq_stat) -{ - u32 idx; - int cnt = card->recv.size / 2; - - /* Note receive is via the WRITE DMA channel */ - card->last_is0 &= ~NJ_IRQM0_WR_MASK; - card->last_is0 |= (irq_stat & NJ_IRQM0_WR_MASK); - - if (irq_stat & NJ_IRQM0_WR_END) - idx = cnt - 1; - else - idx = card->recv.size - 1; - - if (test_bit(FLG_ACTIVE, &card->bc[0].bch.Flags)) - read_dma(&card->bc[0], idx, cnt); - if (test_bit(FLG_ACTIVE, &card->bc[1].bch.Flags)) - read_dma(&card->bc[1], idx, cnt); -} - -/* sync with current DMA address at start or after exception */ -static void -resync(struct tiger_ch *bc, struct tiger_hw *card) -{ - card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR); - card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2; - if (bc->free > card->send.size / 2) - bc->free = card->send.size / 2; - /* currently we simple sync to the next complete free area - * this hast the advantage that we have always maximum time to - * handle TX irq - */ - if (card->send.idx < ((card->send.size / 2) - 1)) - bc->idx = (card->recv.size / 2) - 1; - else - bc->idx = card->recv.size - 1; - bc->txstate = TX_RUN; - pr_debug("%s: %s B%1d free %d idx %d/%d\n", card->name, - __func__, bc->bch.nr, bc->free, bc->idx, card->send.idx); -} - -static int bc_next_frame(struct tiger_ch *); - -static void -fill_hdlc_flag(struct tiger_ch *bc) -{ - struct tiger_hw *card = bc->bch.hw; - int count, i; - u32 m, v; - u8 *p; - - if (bc->free == 0) - return; - pr_debug("%s: %s B%1d %d state %x idx %d/%d\n", card->name, - __func__, bc->bch.nr, bc->free, bc->txstate, - bc->idx, card->send.idx); - if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN)) - resync(bc, card); - count = isdnhdlc_encode(&bc->hsend, NULL, 0, &i, - bc->hsbuf, bc->free); - pr_debug("%s: B%1d hdlc encoded %d flags\n", card->name, - bc->bch.nr, count); - bc->free -= count; - p = bc->hsbuf; - m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff; - for (i = 0; i < count; i++) { - if (bc->idx >= card->send.size) - bc->idx = 0; - v = card->send.start[bc->idx]; - v &= m; - v |= (bc->bch.nr & 1) ? (u32)(p[i]) : ((u32)(p[i])) << 8; - card->send.start[bc->idx++] = v; - } - if (debug & DEBUG_HW_BFIFO) { - snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ", - bc->bch.nr, card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count); - } -} - -static void -fill_dma(struct tiger_ch *bc) -{ - struct tiger_hw *card = bc->bch.hw; - int count, i, fillempty = 0; - u32 m, v, n = 0; - u8 *p; - - if (bc->free == 0) - return; - if (!bc->bch.tx_skb) { - if (!test_bit(FLG_TX_EMPTY, &bc->bch.Flags)) - return; - fillempty = 1; - count = card->send.size >> 1; - p = bc->bch.fill; - } else { - count = bc->bch.tx_skb->len - bc->bch.tx_idx; - if (count <= 0) - return; - pr_debug("%s: %s B%1d %d/%d/%d/%d state %x idx %d/%d\n", - card->name, __func__, bc->bch.nr, count, bc->free, - bc->bch.tx_idx, bc->bch.tx_skb->len, bc->txstate, - bc->idx, card->send.idx); - p = bc->bch.tx_skb->data + bc->bch.tx_idx; - } - if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN)) - resync(bc, card); - if (test_bit(FLG_HDLC, &bc->bch.Flags) && !fillempty) { - count = isdnhdlc_encode(&bc->hsend, p, count, &i, - bc->hsbuf, bc->free); - pr_debug("%s: B%1d hdlc encoded %d in %d\n", card->name, - bc->bch.nr, i, count); - bc->bch.tx_idx += i; - bc->free -= count; - p = bc->hsbuf; - } else { - if (count > bc->free) - count = bc->free; - if (!fillempty) - bc->bch.tx_idx += count; - bc->free -= count; - } - m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff; - if (fillempty) { - n = p[0]; - if (!(bc->bch.nr & 1)) - n <<= 8; - for (i = 0; i < count; i++) { - if (bc->idx >= card->send.size) - bc->idx = 0; - v = card->send.start[bc->idx]; - v &= m; - v |= n; - card->send.start[bc->idx++] = v; - } - } else { - for (i = 0; i < count; i++) { - if (bc->idx >= card->send.size) - bc->idx = 0; - v = card->send.start[bc->idx]; - v &= m; - n = p[i]; - v |= (bc->bch.nr & 1) ? n : n << 8; - card->send.start[bc->idx++] = v; - } - } - if (debug & DEBUG_HW_BFIFO) { - snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ", - bc->bch.nr, card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count); - } - if (bc->free) - bc_next_frame(bc); -} - - -static int -bc_next_frame(struct tiger_ch *bc) -{ - int ret = 1; - - if (bc->bch.tx_skb && bc->bch.tx_idx < bc->bch.tx_skb->len) { - fill_dma(bc); - } else { - dev_kfree_skb(bc->bch.tx_skb); - if (get_next_bframe(&bc->bch)) { - fill_dma(bc); - test_and_clear_bit(FLG_TX_EMPTY, &bc->bch.Flags); - } else if (test_bit(FLG_TX_EMPTY, &bc->bch.Flags)) { - fill_dma(bc); - } else if (test_bit(FLG_FILLEMPTY, &bc->bch.Flags)) { - test_and_set_bit(FLG_TX_EMPTY, &bc->bch.Flags); - ret = 0; - } else { - ret = 0; - } - } - return ret; -} - -static void -send_tiger_bc(struct tiger_hw *card, struct tiger_ch *bc) -{ - int ret; - - bc->free += card->send.size / 2; - if (bc->free >= card->send.size) { - if (!(bc->txstate & (TX_UNDERRUN | TX_INIT))) { - pr_info("%s: B%1d TX underrun state %x\n", card->name, - bc->bch.nr, bc->txstate); - bc->txstate |= TX_UNDERRUN; - } - bc->free = card->send.size; - } - ret = bc_next_frame(bc); - if (!ret) { - if (test_bit(FLG_HDLC, &bc->bch.Flags)) { - fill_hdlc_flag(bc); - return; - } - pr_debug("%s: B%1d TX no data free %d idx %d/%d\n", card->name, - bc->bch.nr, bc->free, bc->idx, card->send.idx); - if (!(bc->txstate & (TX_IDLE | TX_INIT))) { - fill_mem(bc, bc->idx, bc->free, 0xff); - if (bc->free == card->send.size) - bc->txstate |= TX_IDLE; - } - } -} - -static void -send_tiger(struct tiger_hw *card, u8 irq_stat) -{ - int i; - - /* Note send is via the READ DMA channel */ - if ((irq_stat & card->last_is0) & NJ_IRQM0_RD_MASK) { - pr_info("%s: tiger warn write double dma %x/%x\n", - card->name, irq_stat, card->last_is0); - return; - } else { - card->last_is0 &= ~NJ_IRQM0_RD_MASK; - card->last_is0 |= (irq_stat & NJ_IRQM0_RD_MASK); - } - for (i = 0; i < 2; i++) { - if (test_bit(FLG_ACTIVE, &card->bc[i].bch.Flags)) - send_tiger_bc(card, &card->bc[i]); - } -} - -static irqreturn_t -nj_irq(int intno, void *dev_id) -{ - struct tiger_hw *card = dev_id; - u8 val, s1val, s0val; - - spin_lock(&card->lock); - s0val = inb(card->base | NJ_IRQSTAT0); - s1val = inb(card->base | NJ_IRQSTAT1); - if ((s1val & NJ_ISACIRQ) && (s0val == 0)) { - /* shared IRQ */ - spin_unlock(&card->lock); - return IRQ_NONE; - } - pr_debug("%s: IRQSTAT0 %02x IRQSTAT1 %02x\n", card->name, s0val, s1val); - card->irqcnt++; - if (!(s1val & NJ_ISACIRQ)) { - val = ReadISAC_nj(card, ISAC_ISTA); - if (val) - mISDNisac_irq(&card->isac, val); - } - - if (s0val) - /* write to clear */ - outb(s0val, card->base | NJ_IRQSTAT0); - else - goto end; - s1val = s0val; - /* set bits in sval to indicate which page is free */ - card->recv.dmacur = inl(card->base | NJ_DMA_WRITE_ADR); - card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2; - if (card->recv.dmacur < card->recv.dmairq) - s0val = 0x08; /* the 2nd write area is free */ - else - s0val = 0x04; /* the 1st write area is free */ - - card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR); - card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2; - if (card->send.dmacur < card->send.dmairq) - s0val |= 0x02; /* the 2nd read area is free */ - else - s0val |= 0x01; /* the 1st read area is free */ - - pr_debug("%s: DMA Status %02x/%02x/%02x %d/%d\n", card->name, - s1val, s0val, card->last_is0, - card->recv.idx, card->send.idx); - /* test if we have a DMA interrupt */ - if (s0val != card->last_is0) { - if ((s0val & NJ_IRQM0_RD_MASK) != - (card->last_is0 & NJ_IRQM0_RD_MASK)) - /* got a write dma int */ - send_tiger(card, s0val); - if ((s0val & NJ_IRQM0_WR_MASK) != - (card->last_is0 & NJ_IRQM0_WR_MASK)) - /* got a read dma int */ - recv_tiger(card, s0val); - } -end: - spin_unlock(&card->lock); - return IRQ_HANDLED; -} - -static int -nj_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) -{ - int ret = -EINVAL; - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch); - struct tiger_hw *card = bch->hw; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&card->lock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - fill_dma(bc); - ret = 0; - } - spin_unlock_irqrestore(&card->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(&card->lock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = mode_tiger(bc, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(&card->lock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - spin_lock_irqsave(&card->lock, flags); - mISDN_clear_bchannel(bch); - mode_tiger(bc, ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_bctrl(struct tiger_ch *bc, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(&bc->bch, cq); -} - -static int -nj_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch); - struct tiger_hw *card = bch->hw; - int ret = -EINVAL; - u_long flags; - - pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - cancel_work_sync(&bch->workq); - spin_lock_irqsave(&card->lock, flags); - mISDN_clear_bchannel(bch); - mode_tiger(bc, ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bc, arg); - break; - default: - pr_info("%s: %s unknown prim(%x)\n", card->name, __func__, cmd); - } - return ret; -} - -static int -channel_ctrl(struct tiger_hw *card, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_LOOP: - /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ - if (cq->channel < 0 || cq->channel > 3) { - ret = -EINVAL; - break; - } - ret = card->isac.ctrl(&card->isac, HW_TESTLOOP, cq->channel); - break; - case MISDN_CTRL_L1_TIMER3: - ret = card->isac.ctrl(&card->isac, HW_TIMER3_VALUE, cq->p1); - break; - default: - pr_info("%s: %s unknown Op %x\n", card->name, __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -open_bchannel(struct tiger_hw *card, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &card->bc[rq->adr.channel - 1].bch; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - return 0; -} - -/* - * device control function - */ -static int -nj_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct tiger_hw *card = dch->hw; - struct channel_req *rq; - int err = 0; - - pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if (rq->protocol == ISDN_P_TE_S0) - err = card->isac.open(&card->isac, rq); - else - err = open_bchannel(card, rq); - if (err) - break; - if (!try_module_get(THIS_MODULE)) - pr_info("%s: cannot get module\n", card->name); - break; - case CLOSE_CHANNEL: - pr_debug("%s: dev(%d) close from %p\n", card->name, dch->dev.id, - __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(card, arg); - break; - default: - pr_debug("%s: %s unknown command %x\n", - card->name, __func__, cmd); - return -EINVAL; - } - return err; -} - -static int -nj_init_card(struct tiger_hw *card) -{ - u_long flags; - int ret; - - spin_lock_irqsave(&card->lock, flags); - nj_disable_hwirq(card); - spin_unlock_irqrestore(&card->lock, flags); - - card->irq = card->pdev->irq; - if (request_irq(card->irq, nj_irq, IRQF_SHARED, card->name, card)) { - pr_info("%s: couldn't get interrupt %d\n", - card->name, card->irq); - card->irq = -1; - return -EIO; - } - - spin_lock_irqsave(&card->lock, flags); - nj_reset(card); - ret = card->isac.init(&card->isac); - if (ret) - goto error; - ret = inittiger(card); - if (ret) - goto error; - mode_tiger(&card->bc[0], ISDN_P_NONE); - mode_tiger(&card->bc[1], ISDN_P_NONE); -error: - spin_unlock_irqrestore(&card->lock, flags); - return ret; -} - - -static void -nj_release(struct tiger_hw *card) -{ - u_long flags; - int i; - - if (card->base_s) { - spin_lock_irqsave(&card->lock, flags); - nj_disable_hwirq(card); - mode_tiger(&card->bc[0], ISDN_P_NONE); - mode_tiger(&card->bc[1], ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - card->isac.release(&card->isac); - release_region(card->base, card->base_s); - card->base_s = 0; - } - if (card->irq > 0) - free_irq(card->irq, card); - if (device_is_registered(&card->isac.dch.dev.dev)) - mISDN_unregister_device(&card->isac.dch.dev); - - for (i = 0; i < 2; i++) { - mISDN_freebchannel(&card->bc[i].bch); - kfree(card->bc[i].hsbuf); - kfree(card->bc[i].hrbuf); - } - if (card->dma_p) - dma_free_coherent(&card->pdev->dev, NJ_DMA_SIZE, card->dma_p, - card->dma); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - pci_disable_device(card->pdev); - pci_set_drvdata(card->pdev, NULL); - kfree(card); -} - - -static int -nj_setup(struct tiger_hw *card) -{ - card->base = pci_resource_start(card->pdev, 0); - card->base_s = pci_resource_len(card->pdev, 0); - if (!request_region(card->base, card->base_s, card->name)) { - pr_info("%s: NETjet config port %#x-%#x already in use\n", - card->name, card->base, - (u32)(card->base + card->base_s - 1)); - card->base_s = 0; - return -EIO; - } - ASSIGN_FUNC(nj, ISAC, card->isac); - return 0; -} - - -static int -setup_instance(struct tiger_hw *card) -{ - int i, err; - u_long flags; - - snprintf(card->name, MISDN_MAX_IDLEN - 1, "netjet.%d", nj_cnt + 1); - write_lock_irqsave(&card_lock, flags); - list_add_tail(&card->list, &Cards); - write_unlock_irqrestore(&card_lock, flags); - - _set_debug(card); - card->isac.name = card->name; - spin_lock_init(&card->lock); - card->isac.hwlock = &card->lock; - mISDNisac_init(&card->isac, card); - - card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - card->isac.dch.dev.D.ctrl = nj_dctrl; - for (i = 0; i < 2; i++) { - card->bc[i].bch.nr = i + 1; - set_channelmap(i + 1, card->isac.dch.dev.channelmap); - mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM, - NJ_DMA_RXSIZE >> 1); - card->bc[i].bch.hw = card; - card->bc[i].bch.ch.send = nj_l2l1B; - card->bc[i].bch.ch.ctrl = nj_bctrl; - card->bc[i].bch.ch.nr = i + 1; - list_add(&card->bc[i].bch.ch.list, - &card->isac.dch.dev.bchannels); - card->bc[i].bch.hw = card; - } - err = nj_setup(card); - if (err) - goto error; - err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev, - card->name); - if (err) - goto error; - err = nj_init_card(card); - if (!err) { - nj_cnt++; - pr_notice("Netjet %d cards installed\n", nj_cnt); - return 0; - } -error: - nj_release(card); - return err; -} - -static int -nj_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - int cfg; - struct tiger_hw *card; - - if (pdev->subsystem_vendor == 0x8086 && - pdev->subsystem_device == 0x0003) { - pr_notice("Netjet: Digium X100P/X101P not handled\n"); - return -ENODEV; - } - - if (pdev->subsystem_vendor == 0x55 && - pdev->subsystem_device == 0x02) { - pr_notice("Netjet: Enter!Now not handled yet\n"); - return -ENODEV; - } - - if (pdev->subsystem_vendor == 0xb100 && - pdev->subsystem_device == 0x0003) { - pr_notice("Netjet: Digium TDM400P not handled yet\n"); - return -ENODEV; - } - - card = kzalloc_obj(struct tiger_hw); - if (!card) { - pr_info("No kmem for Netjet\n"); - return err; - } - - card->pdev = pdev; - - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - - printk(KERN_INFO "nj_probe(mISDN): found adapter at %s\n", - pci_name(pdev)); - - pci_set_master(pdev); - - /* the TJ300 and TJ320 must be detected, the IRQ handling is different - * unfortunately the chips use the same device ID, but the TJ320 has - * the bit20 in status PCI cfg register set - */ - pci_read_config_dword(pdev, 0x04, &cfg); - if (cfg & 0x00100000) - card->typ = NETJET_S_TJ320; - else - card->typ = NETJET_S_TJ300; - - card->base = pci_resource_start(pdev, 0); - pci_set_drvdata(pdev, card); - err = setup_instance(card); - if (err) - pci_set_drvdata(pdev, NULL); - - return err; -} - - -static void nj_remove(struct pci_dev *pdev) -{ - struct tiger_hw *card = pci_get_drvdata(pdev); - - if (card) - nj_release(card); - else - pr_info("%s drvdata already removed\n", __func__); -} - -/* We cannot select cards with PCI_SUB... IDs, since here are cards with - * SUB IDs set to PCI_ANY_ID, so we need to match all and reject - * known other cards which not work with this driver - see probe function */ -static const struct pci_device_id nj_pci_ids[] = { - { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { } -}; -MODULE_DEVICE_TABLE(pci, nj_pci_ids); - -static struct pci_driver nj_driver = { - .name = "netjet", - .probe = nj_probe, - .remove = nj_remove, - .id_table = nj_pci_ids, -}; - -static int __init nj_init(void) -{ - int err; - - pr_notice("Netjet PCI driver Rev. %s\n", NETJET_REV); - err = pci_register_driver(&nj_driver); - return err; -} - -static void __exit nj_cleanup(void) -{ - pci_unregister_driver(&nj_driver); -} - -module_init(nj_init); -module_exit(nj_cleanup); diff --git a/drivers/isdn/hardware/mISDN/netjet.h b/drivers/isdn/hardware/mISDN/netjet.h deleted file mode 100644 index b23ad9f6d4d0..000000000000 --- a/drivers/isdn/hardware/mISDN/netjet.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * NETjet common header file - * - * Author Karsten Keil - * based on work of Matt Henderson and Daniel Potts, - * Traverse Technologies P/L www.traverse.com.au - * - * Copyright 2009 by Karsten Keil - */ - -#define NJ_CTRL 0x00 -#define NJ_DMACTRL 0x01 -#define NJ_AUXCTRL 0x02 -#define NJ_AUXDATA 0x03 -#define NJ_IRQMASK0 0x04 -#define NJ_IRQMASK1 0x05 -#define NJ_IRQSTAT0 0x06 -#define NJ_IRQSTAT1 0x07 -#define NJ_DMA_READ_START 0x08 -#define NJ_DMA_READ_IRQ 0x0c -#define NJ_DMA_READ_END 0x10 -#define NJ_DMA_READ_ADR 0x14 -#define NJ_DMA_WRITE_START 0x18 -#define NJ_DMA_WRITE_IRQ 0x1c -#define NJ_DMA_WRITE_END 0x20 -#define NJ_DMA_WRITE_ADR 0x24 -#define NJ_PULSE_CNT 0x28 - -#define NJ_ISAC_OFF 0xc0 -#define NJ_ISACIRQ 0x10 - -#define NJ_IRQM0_RD_MASK 0x03 -#define NJ_IRQM0_RD_IRQ 0x01 -#define NJ_IRQM0_RD_END 0x02 -#define NJ_IRQM0_WR_MASK 0x0c -#define NJ_IRQM0_WR_IRQ 0x04 -#define NJ_IRQM0_WR_END 0x08 - -/* one page here is no need to be smaller */ -#define NJ_DMA_SIZE 4096 -/* 2 * 64 byte is a compromise between IRQ count and latency */ -#define NJ_DMA_RXSIZE 128 /* 2 * 64 */ -#define NJ_DMA_TXSIZE 128 /* 2 * 64 */ diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c deleted file mode 100644 index ab24c3c460e6..000000000000 --- a/drivers/isdn/hardware/mISDN/speedfax.c +++ /dev/null @@ -1,520 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * speedfax.c low level stuff for Sedlbauer Speedfax+ cards - * based on the ISAR DSP - * Thanks to Sedlbauer AG for informations and HW - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include -#include "ipac.h" -#include "isar.h" - -#define SPEEDFAX_REV "2.0" - -#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51 -#define PCI_SUBVENDOR_SPEEDFAX_PCI 0x54 -#define PCI_SUB_ID_SEDLBAUER 0x01 - -#define SFAX_PCI_ADDR 0xc8 -#define SFAX_PCI_ISAC 0xd0 -#define SFAX_PCI_ISAR 0xe0 - -/* TIGER 100 Registers */ - -#define TIGER_RESET_ADDR 0x00 -#define TIGER_EXTERN_RESET_ON 0x01 -#define TIGER_EXTERN_RESET_OFF 0x00 -#define TIGER_AUX_CTRL 0x02 -#define TIGER_AUX_DATA 0x03 -#define TIGER_AUX_IRQMASK 0x05 -#define TIGER_AUX_STATUS 0x07 - -/* Tiger AUX BITs */ -#define SFAX_AUX_IOMASK 0xdd /* 1 and 5 are inputs */ -#define SFAX_ISAR_RESET_BIT_OFF 0x00 -#define SFAX_ISAR_RESET_BIT_ON 0x01 -#define SFAX_TIGER_IRQ_BIT 0x02 -#define SFAX_LED1_BIT 0x08 -#define SFAX_LED2_BIT 0x10 - -#define SFAX_PCI_RESET_ON (SFAX_ISAR_RESET_BIT_ON) -#define SFAX_PCI_RESET_OFF (SFAX_LED1_BIT | SFAX_LED2_BIT) - -static int sfax_cnt; -static u32 debug; -static u32 irqloops = 4; - -struct sfax_hw { - struct list_head list; - struct pci_dev *pdev; - char name[MISDN_MAX_IDLEN]; - u32 irq; - u32 irqcnt; - u32 cfg; - struct _ioport p_isac; - struct _ioport p_isar; - u8 aux_data; - spinlock_t lock; /* HW access lock */ - struct isac_hw isac; - struct isar_hw isar; -}; - -static LIST_HEAD(Cards); -static DEFINE_RWLOCK(card_lock); /* protect Cards */ - -static void -_set_debug(struct sfax_hw *card) -{ - card->isac.dch.debug = debug; - card->isar.ch[0].bch.debug = debug; - card->isar.ch[1].bch.debug = debug; -} - -static int -set_debug(const char *val, const struct kernel_param *kp) -{ - int ret; - struct sfax_hw *card; - - ret = param_set_uint(val, kp); - if (!ret) { - read_lock(&card_lock); - list_for_each_entry(card, &Cards, list) - _set_debug(card); - read_unlock(&card_lock); - } - return ret; -} - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for Sedlbauer Speedfax+ cards"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(SPEEDFAX_REV); -MODULE_FIRMWARE("isdn/ISAR.BIN"); -module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Speedfax debug mask"); -module_param(irqloops, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(irqloops, "Speedfax maximal irqloops (default 4)"); - -IOFUNC_IND(ISAC, sfax_hw, p_isac) -IOFUNC_IND(ISAR, sfax_hw, p_isar) - -static irqreturn_t -speedfax_irq(int intno, void *dev_id) -{ - struct sfax_hw *sf = dev_id; - u8 val; - int cnt = irqloops; - - spin_lock(&sf->lock); - val = inb(sf->cfg + TIGER_AUX_STATUS); - if (val & SFAX_TIGER_IRQ_BIT) { /* for us or shared ? */ - spin_unlock(&sf->lock); - return IRQ_NONE; /* shared */ - } - sf->irqcnt++; - val = ReadISAR_IND(sf, ISAR_IRQBIT); -Start_ISAR: - if (val & ISAR_IRQSTA) - mISDNisar_irq(&sf->isar); - val = ReadISAC_IND(sf, ISAC_ISTA); - if (val) - mISDNisac_irq(&sf->isac, val); - val = ReadISAR_IND(sf, ISAR_IRQBIT); - if ((val & ISAR_IRQSTA) && cnt--) - goto Start_ISAR; - if (cnt < irqloops) - pr_debug("%s: %d irqloops cpu%d\n", sf->name, - irqloops - cnt, smp_processor_id()); - if (irqloops && !cnt) - pr_notice("%s: %d IRQ LOOP cpu%d\n", sf->name, - irqloops, smp_processor_id()); - spin_unlock(&sf->lock); - return IRQ_HANDLED; -} - -static void -enable_hwirq(struct sfax_hw *sf) -{ - WriteISAC_IND(sf, ISAC_MASK, 0); - WriteISAR_IND(sf, ISAR_IRQBIT, ISAR_IRQMSK); - outb(SFAX_TIGER_IRQ_BIT, sf->cfg + TIGER_AUX_IRQMASK); -} - -static void -disable_hwirq(struct sfax_hw *sf) -{ - WriteISAC_IND(sf, ISAC_MASK, 0xFF); - WriteISAR_IND(sf, ISAR_IRQBIT, 0); - outb(0, sf->cfg + TIGER_AUX_IRQMASK); -} - -static void -reset_speedfax(struct sfax_hw *sf) -{ - - pr_debug("%s: resetting card\n", sf->name); - outb(TIGER_EXTERN_RESET_ON, sf->cfg + TIGER_RESET_ADDR); - outb(SFAX_PCI_RESET_ON, sf->cfg + TIGER_AUX_DATA); - mdelay(1); - outb(TIGER_EXTERN_RESET_OFF, sf->cfg + TIGER_RESET_ADDR); - sf->aux_data = SFAX_PCI_RESET_OFF; - outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); - mdelay(1); -} - -static int -sfax_ctrl(struct sfax_hw *sf, u32 cmd, u_long arg) -{ - int ret = 0; - - switch (cmd) { - case HW_RESET_REQ: - reset_speedfax(sf); - break; - case HW_ACTIVATE_IND: - if (arg & 1) - sf->aux_data &= ~SFAX_LED1_BIT; - if (arg & 2) - sf->aux_data &= ~SFAX_LED2_BIT; - outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); - break; - case HW_DEACT_IND: - if (arg & 1) - sf->aux_data |= SFAX_LED1_BIT; - if (arg & 2) - sf->aux_data |= SFAX_LED2_BIT; - outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); - break; - default: - pr_info("%s: %s unknown command %x %lx\n", - sf->name, __func__, cmd, arg); - ret = -EINVAL; - break; - } - return ret; -} - -static int -channel_ctrl(struct sfax_hw *sf, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_LOOP: - /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ - if (cq->channel < 0 || cq->channel > 3) { - ret = -EINVAL; - break; - } - ret = sf->isac.ctrl(&sf->isac, HW_TESTLOOP, cq->channel); - break; - case MISDN_CTRL_L1_TIMER3: - ret = sf->isac.ctrl(&sf->isac, HW_TIMER3_VALUE, cq->p1); - break; - default: - pr_info("%s: unknown Op %x\n", sf->name, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -sfax_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct sfax_hw *sf = dch->hw; - struct channel_req *rq; - int err = 0; - - pr_debug("%s: cmd:%x %p\n", sf->name, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if (rq->protocol == ISDN_P_TE_S0) - err = sf->isac.open(&sf->isac, rq); - else - err = sf->isar.open(&sf->isar, rq); - if (err) - break; - if (!try_module_get(THIS_MODULE)) - pr_info("%s: cannot get module\n", sf->name); - break; - case CLOSE_CHANNEL: - pr_debug("%s: dev(%d) close from %p\n", sf->name, - dch->dev.id, __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(sf, arg); - break; - default: - pr_debug("%s: unknown command %x\n", sf->name, cmd); - return -EINVAL; - } - return err; -} - -static int -init_card(struct sfax_hw *sf) -{ - int ret, cnt = 3; - u_long flags; - - ret = request_irq(sf->irq, speedfax_irq, IRQF_SHARED, sf->name, sf); - if (ret) { - pr_info("%s: couldn't get interrupt %d\n", sf->name, sf->irq); - return ret; - } - while (cnt--) { - spin_lock_irqsave(&sf->lock, flags); - ret = sf->isac.init(&sf->isac); - if (ret) { - spin_unlock_irqrestore(&sf->lock, flags); - pr_info("%s: ISAC init failed with %d\n", - sf->name, ret); - break; - } - enable_hwirq(sf); - /* RESET Receiver and Transmitter */ - WriteISAC_IND(sf, ISAC_CMDR, 0x41); - spin_unlock_irqrestore(&sf->lock, flags); - msleep_interruptible(10); - if (debug & DEBUG_HW) - pr_notice("%s: IRQ %d count %d\n", sf->name, - sf->irq, sf->irqcnt); - if (!sf->irqcnt) { - pr_info("%s: IRQ(%d) got no requests during init %d\n", - sf->name, sf->irq, 3 - cnt); - } else - return 0; - } - free_irq(sf->irq, sf); - return -EIO; -} - - -static int -setup_speedfax(struct sfax_hw *sf) -{ - u_long flags; - - if (!request_region(sf->cfg, 256, sf->name)) { - pr_info("mISDN: %s config port %x-%x already in use\n", - sf->name, sf->cfg, sf->cfg + 255); - return -EIO; - } - outb(0xff, sf->cfg); - outb(0, sf->cfg); - outb(0xdd, sf->cfg + TIGER_AUX_CTRL); - outb(0, sf->cfg + TIGER_AUX_IRQMASK); - - sf->isac.type = IPAC_TYPE_ISAC; - sf->p_isac.ale = sf->cfg + SFAX_PCI_ADDR; - sf->p_isac.port = sf->cfg + SFAX_PCI_ISAC; - sf->p_isar.ale = sf->cfg + SFAX_PCI_ADDR; - sf->p_isar.port = sf->cfg + SFAX_PCI_ISAR; - ASSIGN_FUNC(IND, ISAC, sf->isac); - ASSIGN_FUNC(IND, ISAR, sf->isar); - spin_lock_irqsave(&sf->lock, flags); - reset_speedfax(sf); - disable_hwirq(sf); - spin_unlock_irqrestore(&sf->lock, flags); - return 0; -} - -static void -release_card(struct sfax_hw *card) { - u_long flags; - - spin_lock_irqsave(&card->lock, flags); - disable_hwirq(card); - spin_unlock_irqrestore(&card->lock, flags); - card->isac.release(&card->isac); - free_irq(card->irq, card); - card->isar.release(&card->isar); - mISDN_unregister_device(&card->isac.dch.dev); - release_region(card->cfg, 256); - pci_disable_device(card->pdev); - pci_set_drvdata(card->pdev, NULL); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - kfree(card); - sfax_cnt--; -} - -static int -setup_instance(struct sfax_hw *card) -{ - const struct firmware *firmware; - int i, err; - u_long flags; - - snprintf(card->name, MISDN_MAX_IDLEN - 1, "Speedfax.%d", sfax_cnt + 1); - write_lock_irqsave(&card_lock, flags); - list_add_tail(&card->list, &Cards); - write_unlock_irqrestore(&card_lock, flags); - _set_debug(card); - spin_lock_init(&card->lock); - card->isac.hwlock = &card->lock; - card->isar.hwlock = &card->lock; - card->isar.ctrl = (void *)&sfax_ctrl; - card->isac.name = card->name; - card->isar.name = card->name; - card->isar.owner = THIS_MODULE; - - err = request_firmware(&firmware, "isdn/ISAR.BIN", &card->pdev->dev); - if (err < 0) { - pr_info("%s: firmware request failed %d\n", - card->name, err); - goto error_fw; - } - if (debug & DEBUG_HW) - pr_notice("%s: got firmware %zu bytes\n", - card->name, firmware->size); - - mISDNisac_init(&card->isac, card); - - card->isac.dch.dev.D.ctrl = sfax_dctrl; - card->isac.dch.dev.Bprotocols = - mISDNisar_init(&card->isar, card); - for (i = 0; i < 2; i++) { - set_channelmap(i + 1, card->isac.dch.dev.channelmap); - list_add(&card->isar.ch[i].bch.ch.list, - &card->isac.dch.dev.bchannels); - } - - err = setup_speedfax(card); - if (err) - goto error_setup; - err = card->isar.init(&card->isar); - if (err) - goto error; - err = mISDN_register_device(&card->isac.dch.dev, - &card->pdev->dev, card->name); - if (err) - goto error; - err = init_card(card); - if (err) - goto error_init; - err = card->isar.firmware(&card->isar, firmware->data, firmware->size); - if (!err) { - release_firmware(firmware); - sfax_cnt++; - pr_notice("SpeedFax %d cards installed\n", sfax_cnt); - return 0; - } - disable_hwirq(card); - free_irq(card->irq, card); -error_init: - mISDN_unregister_device(&card->isac.dch.dev); -error: - release_region(card->cfg, 256); -error_setup: - card->isac.release(&card->isac); - card->isar.release(&card->isar); - release_firmware(firmware); -error_fw: - pci_disable_device(card->pdev); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - kfree(card); - return err; -} - -static int -sfaxpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - struct sfax_hw *card = kzalloc_obj(struct sfax_hw); - - if (!card) { - pr_info("No memory for Speedfax+ PCI\n"); - return err; - } - card->pdev = pdev; - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - - pr_notice("mISDN: Speedfax found adapter %s at %s\n", - (char *)ent->driver_data, pci_name(pdev)); - - card->cfg = pci_resource_start(pdev, 0); - card->irq = pdev->irq; - pci_set_drvdata(pdev, card); - err = setup_instance(card); - if (err) - pci_set_drvdata(pdev, NULL); - return err; -} - -static void -sfax_remove_pci(struct pci_dev *pdev) -{ - struct sfax_hw *card = pci_get_drvdata(pdev); - - if (card) - release_card(card); - else - pr_debug("%s: drvdata already removed\n", __func__); -} - -static struct pci_device_id sfaxpci_ids[] = { - { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, - PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER, - 0, 0, (unsigned long) "Pyramid Speedfax + PCI" - }, - { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, - PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER, - 0, 0, (unsigned long) "Sedlbauer Speedfax + PCI" - }, - { } -}; -MODULE_DEVICE_TABLE(pci, sfaxpci_ids); - -static struct pci_driver sfaxpci_driver = { - .name = "speedfax+ pci", - .probe = sfaxpci_probe, - .remove = sfax_remove_pci, - .id_table = sfaxpci_ids, -}; - -static int __init -Speedfax_init(void) -{ - int err; - - pr_notice("Sedlbauer Speedfax+ Driver Rev. %s\n", - SPEEDFAX_REV); - err = pci_register_driver(&sfaxpci_driver); - return err; -} - -static void __exit -Speedfax_cleanup(void) -{ - pci_unregister_driver(&sfaxpci_driver); -} - -module_init(Speedfax_init); -module_exit(Speedfax_cleanup); diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c deleted file mode 100644 index a341470c042f..000000000000 --- a/drivers/isdn/hardware/mISDN/w6692.c +++ /dev/null @@ -1,1417 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * w6692.c mISDN driver for Winbond w6692 based cards - * - * Author Karsten Keil - * based on the w6692 I4L driver from Petr Novak - * - * Copyright 2009 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include "w6692.h" - -#define W6692_REV "2.0" - -#define DBUSY_TIMER_VALUE 80 - -enum { - W6692_ASUS, - W6692_WINBOND, - W6692_USR -}; - -/* private data in the PCI devices list */ -struct w6692map { - u_int subtype; - char *name; -}; - -static const struct w6692map w6692_map[] = -{ - {W6692_ASUS, "Dynalink/AsusCom IS64PH"}, - {W6692_WINBOND, "Winbond W6692"}, - {W6692_USR, "USR W6692"} -}; - -#define PCI_DEVICE_ID_USR_6692 0x3409 - -struct w6692_ch { - struct bchannel bch; - u32 addr; - struct timer_list timer; - u8 b_mode; -}; - -struct w6692_hw { - struct list_head list; - struct pci_dev *pdev; - char name[MISDN_MAX_IDLEN]; - u32 irq; - u32 irqcnt; - u32 addr; - u32 fmask; /* feature mask - bit set per card nr */ - int subtype; - spinlock_t lock; /* hw lock */ - u8 imask; - u8 pctl; - u8 xaddr; - u8 xdata; - u8 state; - struct w6692_ch bc[2]; - struct dchannel dch; - char log[64]; -}; - -static LIST_HEAD(Cards); -static DEFINE_RWLOCK(card_lock); /* protect Cards */ - -static int w6692_cnt; -static int debug; -static u32 led; -static u32 pots; - -static void -_set_debug(struct w6692_hw *card) -{ - card->dch.debug = debug; - card->bc[0].bch.debug = debug; - card->bc[1].bch.debug = debug; -} - -static int -set_debug(const char *val, const struct kernel_param *kp) -{ - int ret; - struct w6692_hw *card; - - ret = param_set_uint(val, kp); - if (!ret) { - read_lock(&card_lock); - list_for_each_entry(card, &Cards, list) - _set_debug(card); - read_unlock(&card_lock); - } - return ret; -} - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for Winbond w6692 based cards"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(W6692_REV); -module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "W6692 debug mask"); -module_param(led, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(led, "W6692 LED support bitmask (one bit per card)"); -module_param(pots, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(pots, "W6692 POTS support bitmask (one bit per card)"); - -static inline u8 -ReadW6692(struct w6692_hw *card, u8 offset) -{ - return inb(card->addr + offset); -} - -static inline void -WriteW6692(struct w6692_hw *card, u8 offset, u8 value) -{ - outb(value, card->addr + offset); -} - -static inline u8 -ReadW6692B(struct w6692_ch *bc, u8 offset) -{ - return inb(bc->addr + offset); -} - -static inline void -WriteW6692B(struct w6692_ch *bc, u8 offset, u8 value) -{ - outb(value, bc->addr + offset); -} - -static void -enable_hwirq(struct w6692_hw *card) -{ - WriteW6692(card, W_IMASK, card->imask); -} - -static void -disable_hwirq(struct w6692_hw *card) -{ - WriteW6692(card, W_IMASK, 0xff); -} - -static const char *W6692Ver[] = {"V00", "V01", "V10", "V11"}; - -static void -W6692Version(struct w6692_hw *card) -{ - int val; - - val = ReadW6692(card, W_D_RBCH); - pr_notice("%s: Winbond W6692 version: %s\n", card->name, - W6692Ver[(val >> 6) & 3]); -} - -static void -w6692_led_handler(struct w6692_hw *card, int on) -{ - if ((!(card->fmask & led)) || card->subtype == W6692_USR) - return; - if (on) { - card->xdata &= 0xfb; /* LED ON */ - WriteW6692(card, W_XDATA, card->xdata); - } else { - card->xdata |= 0x04; /* LED OFF */ - WriteW6692(card, W_XDATA, card->xdata); - } -} - -static void -ph_command(struct w6692_hw *card, u8 cmd) -{ - pr_debug("%s: ph_command %x\n", card->name, cmd); - WriteW6692(card, W_CIX, cmd); -} - -static void -W6692_new_ph(struct w6692_hw *card) -{ - if (card->state == W_L1CMD_RST) - ph_command(card, W_L1CMD_DRC); - schedule_event(&card->dch, FLG_PHCHANGE); -} - -static void -W6692_ph_bh(struct dchannel *dch) -{ - struct w6692_hw *card = dch->hw; - - switch (card->state) { - case W_L1CMD_RST: - dch->state = 0; - l1_event(dch->l1, HW_RESET_IND); - break; - case W_L1IND_CD: - dch->state = 3; - l1_event(dch->l1, HW_DEACT_CNF); - break; - case W_L1IND_DRD: - dch->state = 3; - l1_event(dch->l1, HW_DEACT_IND); - break; - case W_L1IND_CE: - dch->state = 4; - l1_event(dch->l1, HW_POWERUP_IND); - break; - case W_L1IND_LD: - if (dch->state <= 5) { - dch->state = 5; - l1_event(dch->l1, ANYSIGNAL); - } else { - dch->state = 8; - l1_event(dch->l1, LOSTFRAMING); - } - break; - case W_L1IND_ARD: - dch->state = 6; - l1_event(dch->l1, INFO2); - break; - case W_L1IND_AI8: - dch->state = 7; - l1_event(dch->l1, INFO4_P8); - break; - case W_L1IND_AI10: - dch->state = 7; - l1_event(dch->l1, INFO4_P10); - break; - default: - pr_debug("%s: TE unknown state %02x dch state %02x\n", - card->name, card->state, dch->state); - break; - } - pr_debug("%s: TE newstate %02x\n", card->name, dch->state); -} - -static void -W6692_empty_Dfifo(struct w6692_hw *card, int count) -{ - struct dchannel *dch = &card->dch; - u8 *ptr; - - pr_debug("%s: empty_Dfifo %d\n", card->name, count); - if (!dch->rx_skb) { - dch->rx_skb = mI_alloc_skb(card->dch.maxlen, GFP_ATOMIC); - if (!dch->rx_skb) { - pr_info("%s: D receive out of memory\n", card->name); - WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); - return; - } - } - if ((dch->rx_skb->len + count) >= dch->maxlen) { - pr_debug("%s: empty_Dfifo overrun %d\n", card->name, - dch->rx_skb->len + count); - WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); - return; - } - ptr = skb_put(dch->rx_skb, count); - insb(card->addr + W_D_RFIFO, ptr, count); - WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); - if (debug & DEBUG_HW_DFIFO) { - snprintf(card->log, 63, "D-recv %s %d ", - card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -static void -W6692_fill_Dfifo(struct w6692_hw *card) -{ - struct dchannel *dch = &card->dch; - int count; - u8 *ptr; - u8 cmd = W_D_CMDR_XMS; - - pr_debug("%s: fill_Dfifo\n", card->name); - if (!dch->tx_skb) - return; - count = dch->tx_skb->len - dch->tx_idx; - if (count <= 0) - return; - if (count > W_D_FIFO_THRESH) - count = W_D_FIFO_THRESH; - else - cmd |= W_D_CMDR_XME; - ptr = dch->tx_skb->data + dch->tx_idx; - dch->tx_idx += count; - outsb(card->addr + W_D_XFIFO, ptr, count); - WriteW6692(card, W_D_CMDR, cmd); - if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) { - pr_debug("%s: fill_Dfifo dbusytimer running\n", card->name); - timer_delete(&dch->timer); - } - dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000); - add_timer(&dch->timer); - if (debug & DEBUG_HW_DFIFO) { - snprintf(card->log, 63, "D-send %s %d ", - card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -static void -d_retransmit(struct w6692_hw *card) -{ - struct dchannel *dch = &card->dch; - - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); -#ifdef FIXME - if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) - dchannel_sched_event(dch, D_CLEARBUSY); -#endif - if (test_bit(FLG_TX_BUSY, &dch->Flags)) { - /* Restart frame */ - dch->tx_idx = 0; - W6692_fill_Dfifo(card); - } else if (dch->tx_skb) { /* should not happen */ - pr_info("%s: %s without TX_BUSY\n", card->name, __func__); - test_and_set_bit(FLG_TX_BUSY, &dch->Flags); - dch->tx_idx = 0; - W6692_fill_Dfifo(card); - } else { - pr_info("%s: XDU no TX_BUSY\n", card->name); - if (get_next_dframe(dch)) - W6692_fill_Dfifo(card); - } -} - -static void -handle_rxD(struct w6692_hw *card) { - u8 stat; - int count; - - stat = ReadW6692(card, W_D_RSTA); - if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) { - if (stat & W_D_RSTA_RDOV) { - pr_debug("%s: D-channel RDOV\n", card->name); -#ifdef ERROR_STATISTIC - card->dch.err_rx++; -#endif - } - if (stat & W_D_RSTA_CRCE) { - pr_debug("%s: D-channel CRC error\n", card->name); -#ifdef ERROR_STATISTIC - card->dch.err_crc++; -#endif - } - if (stat & W_D_RSTA_RMB) { - pr_debug("%s: D-channel ABORT\n", card->name); -#ifdef ERROR_STATISTIC - card->dch.err_rx++; -#endif - } - dev_kfree_skb(card->dch.rx_skb); - card->dch.rx_skb = NULL; - WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST); - } else { - count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1); - if (count == 0) - count = W_D_FIFO_THRESH; - W6692_empty_Dfifo(card, count); - recv_Dchannel(&card->dch); - } -} - -static void -handle_txD(struct w6692_hw *card) { - if (test_and_clear_bit(FLG_BUSY_TIMER, &card->dch.Flags)) - timer_delete(&card->dch.timer); - if (card->dch.tx_skb && card->dch.tx_idx < card->dch.tx_skb->len) { - W6692_fill_Dfifo(card); - } else { - dev_kfree_skb(card->dch.tx_skb); - if (get_next_dframe(&card->dch)) - W6692_fill_Dfifo(card); - } -} - -static void -handle_statusD(struct w6692_hw *card) -{ - struct dchannel *dch = &card->dch; - u8 exval, v1, cir; - - exval = ReadW6692(card, W_D_EXIR); - - pr_debug("%s: D_EXIR %02x\n", card->name, exval); - if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) { - /* Transmit underrun/collision */ - pr_debug("%s: D-channel underrun/collision\n", card->name); -#ifdef ERROR_STATISTIC - dch->err_tx++; -#endif - d_retransmit(card); - } - if (exval & W_D_EXI_RDOV) { /* RDOV */ - pr_debug("%s: D-channel RDOV\n", card->name); - WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST); - } - if (exval & W_D_EXI_TIN2) /* TIN2 - never */ - pr_debug("%s: spurious TIN2 interrupt\n", card->name); - if (exval & W_D_EXI_MOC) { /* MOC - not supported */ - v1 = ReadW6692(card, W_MOSR); - pr_debug("%s: spurious MOC interrupt MOSR %02x\n", - card->name, v1); - } - if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */ - cir = ReadW6692(card, W_CIR); - pr_debug("%s: ISC CIR %02X\n", card->name, cir); - if (cir & W_CIR_ICC) { - v1 = cir & W_CIR_COD_MASK; - pr_debug("%s: ph_state_change %x -> %x\n", card->name, - dch->state, v1); - card->state = v1; - if (card->fmask & led) { - switch (v1) { - case W_L1IND_AI8: - case W_L1IND_AI10: - w6692_led_handler(card, 1); - break; - default: - w6692_led_handler(card, 0); - break; - } - } - W6692_new_ph(card); - } - if (cir & W_CIR_SCC) { - v1 = ReadW6692(card, W_SQR); - pr_debug("%s: SCC SQR %02X\n", card->name, v1); - } - } - if (exval & W_D_EXI_WEXP) - pr_debug("%s: spurious WEXP interrupt!\n", card->name); - if (exval & W_D_EXI_TEXP) - pr_debug("%s: spurious TEXP interrupt!\n", card->name); -} - -static void -W6692_empty_Bfifo(struct w6692_ch *wch, int count) -{ - struct w6692_hw *card = wch->bch.hw; - u8 *ptr; - int maxlen; - - pr_debug("%s: empty_Bfifo %d\n", card->name, count); - if (unlikely(wch->bch.state == ISDN_P_NONE)) { - pr_debug("%s: empty_Bfifo ISDN_P_NONE\n", card->name); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); - if (wch->bch.rx_skb) - skb_trim(wch->bch.rx_skb, 0); - return; - } - if (test_bit(FLG_RX_OFF, &wch->bch.Flags)) { - wch->bch.dropcnt += count; - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); - return; - } - maxlen = bchannel_get_rxbuf(&wch->bch, count); - if (maxlen < 0) { - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); - if (wch->bch.rx_skb) - skb_trim(wch->bch.rx_skb, 0); - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - card->name, wch->bch.nr, count); - return; - } - ptr = skb_put(wch->bch.rx_skb, count); - insb(wch->addr + W_B_RFIFO, ptr, count); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); - if (debug & DEBUG_HW_DFIFO) { - snprintf(card->log, 63, "B%1d-recv %s %d ", - wch->bch.nr, card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -static void -W6692_fill_Bfifo(struct w6692_ch *wch) -{ - struct w6692_hw *card = wch->bch.hw; - int count, fillempty = 0; - u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS; - - pr_debug("%s: fill Bfifo\n", card->name); - if (!wch->bch.tx_skb) { - if (!test_bit(FLG_TX_EMPTY, &wch->bch.Flags)) - return; - ptr = wch->bch.fill; - count = W_B_FIFO_THRESH; - fillempty = 1; - } else { - count = wch->bch.tx_skb->len - wch->bch.tx_idx; - if (count <= 0) - return; - ptr = wch->bch.tx_skb->data + wch->bch.tx_idx; - } - if (count > W_B_FIFO_THRESH) - count = W_B_FIFO_THRESH; - else if (test_bit(FLG_HDLC, &wch->bch.Flags)) - cmd |= W_B_CMDR_XME; - - pr_debug("%s: fill Bfifo%d/%d\n", card->name, - count, wch->bch.tx_idx); - wch->bch.tx_idx += count; - if (fillempty) { - while (count > 0) { - outsb(wch->addr + W_B_XFIFO, ptr, MISDN_BCH_FILL_SIZE); - count -= MISDN_BCH_FILL_SIZE; - } - } else { - outsb(wch->addr + W_B_XFIFO, ptr, count); - } - WriteW6692B(wch, W_B_CMDR, cmd); - if ((debug & DEBUG_HW_BFIFO) && !fillempty) { - snprintf(card->log, 63, "B%1d-send %s %d ", - wch->bch.nr, card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -#if 0 -static int -setvolume(struct w6692_ch *wch, int mic, struct sk_buff *skb) -{ - struct w6692_hw *card = wch->bch.hw; - u16 *vol = (u16 *)skb->data; - u8 val; - - if ((!(card->fmask & pots)) || - !test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) - return -ENODEV; - if (skb->len < 2) - return -EINVAL; - if (*vol > 7) - return -EINVAL; - val = *vol & 7; - val = 7 - val; - if (mic) { - val <<= 3; - card->xaddr &= 0xc7; - } else { - card->xaddr &= 0xf8; - } - card->xaddr |= val; - WriteW6692(card, W_XADDR, card->xaddr); - return 0; -} - -static int -enable_pots(struct w6692_ch *wch) -{ - struct w6692_hw *card = wch->bch.hw; - - if ((!(card->fmask & pots)) || - !test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) - return -ENODEV; - wch->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0; - WriteW6692B(wch, W_B_MODE, wch->b_mode); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); - card->pctl |= ((wch->bch.nr & 2) ? W_PCTL_PCX : 0); - WriteW6692(card, W_PCTL, card->pctl); - return 0; -} -#endif - -static int -disable_pots(struct w6692_ch *wch) -{ - struct w6692_hw *card = wch->bch.hw; - - if (!(card->fmask & pots)) - return -ENODEV; - wch->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0); - WriteW6692B(wch, W_B_MODE, wch->b_mode); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | - W_B_CMDR_XRST); - return 0; -} - -static int -w6692_mode(struct w6692_ch *wch, u32 pr) -{ - struct w6692_hw *card; - - card = wch->bch.hw; - pr_debug("%s: B%d protocol %x-->%x\n", card->name, - wch->bch.nr, wch->bch.state, pr); - switch (pr) { - case ISDN_P_NONE: - if ((card->fmask & pots) && (wch->b_mode & W_B_MODE_EPCM)) - disable_pots(wch); - wch->b_mode = 0; - mISDN_clear_bchannel(&wch->bch); - WriteW6692B(wch, W_B_MODE, wch->b_mode); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); - test_and_clear_bit(FLG_HDLC, &wch->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &wch->bch.Flags); - break; - case ISDN_P_B_RAW: - wch->b_mode = W_B_MODE_MMS; - WriteW6692B(wch, W_B_MODE, wch->b_mode); - WriteW6692B(wch, W_B_EXIM, 0); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | - W_B_CMDR_XRST); - test_and_set_bit(FLG_TRANSPARENT, &wch->bch.Flags); - break; - case ISDN_P_B_HDLC: - wch->b_mode = W_B_MODE_ITF; - WriteW6692B(wch, W_B_MODE, wch->b_mode); - WriteW6692B(wch, W_B_ADM1, 0xff); - WriteW6692B(wch, W_B_ADM2, 0xff); - WriteW6692B(wch, W_B_EXIM, 0); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | - W_B_CMDR_XRST); - test_and_set_bit(FLG_HDLC, &wch->bch.Flags); - break; - default: - pr_info("%s: protocol %x not known\n", card->name, pr); - return -ENOPROTOOPT; - } - wch->bch.state = pr; - return 0; -} - -static void -send_next(struct w6692_ch *wch) -{ - if (wch->bch.tx_skb && wch->bch.tx_idx < wch->bch.tx_skb->len) { - W6692_fill_Bfifo(wch); - } else { - dev_kfree_skb(wch->bch.tx_skb); - if (get_next_bframe(&wch->bch)) { - W6692_fill_Bfifo(wch); - test_and_clear_bit(FLG_TX_EMPTY, &wch->bch.Flags); - } else if (test_bit(FLG_TX_EMPTY, &wch->bch.Flags)) { - W6692_fill_Bfifo(wch); - } - } -} - -static void -W6692B_interrupt(struct w6692_hw *card, int ch) -{ - struct w6692_ch *wch = &card->bc[ch]; - int count; - u8 stat, star = 0; - - stat = ReadW6692B(wch, W_B_EXIR); - pr_debug("%s: B%d EXIR %02x\n", card->name, wch->bch.nr, stat); - if (stat & W_B_EXI_RME) { - star = ReadW6692B(wch, W_B_STAR); - if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) { - if ((star & W_B_STAR_RDOV) && - test_bit(FLG_ACTIVE, &wch->bch.Flags)) { - pr_debug("%s: B%d RDOV proto=%x\n", card->name, - wch->bch.nr, wch->bch.state); -#ifdef ERROR_STATISTIC - wch->bch.err_rdo++; -#endif - } - if (test_bit(FLG_HDLC, &wch->bch.Flags)) { - if (star & W_B_STAR_CRCE) { - pr_debug("%s: B%d CRC error\n", - card->name, wch->bch.nr); -#ifdef ERROR_STATISTIC - wch->bch.err_crc++; -#endif - } - if (star & W_B_STAR_RMB) { - pr_debug("%s: B%d message abort\n", - card->name, wch->bch.nr); -#ifdef ERROR_STATISTIC - wch->bch.err_inv++; -#endif - } - } - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | - W_B_CMDR_RRST | W_B_CMDR_RACT); - if (wch->bch.rx_skb) - skb_trim(wch->bch.rx_skb, 0); - } else { - count = ReadW6692B(wch, W_B_RBCL) & - (W_B_FIFO_THRESH - 1); - if (count == 0) - count = W_B_FIFO_THRESH; - W6692_empty_Bfifo(wch, count); - recv_Bchannel(&wch->bch, 0, false); - } - } - if (stat & W_B_EXI_RMR) { - if (!(stat & W_B_EXI_RME)) - star = ReadW6692B(wch, W_B_STAR); - if (star & W_B_STAR_RDOV) { - pr_debug("%s: B%d RDOV proto=%x\n", card->name, - wch->bch.nr, wch->bch.state); -#ifdef ERROR_STATISTIC - wch->bch.err_rdo++; -#endif - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | - W_B_CMDR_RRST | W_B_CMDR_RACT); - } else { - W6692_empty_Bfifo(wch, W_B_FIFO_THRESH); - if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) - recv_Bchannel(&wch->bch, 0, false); - } - } - if (stat & W_B_EXI_RDOV) { - /* only if it is not handled yet */ - if (!(star & W_B_STAR_RDOV)) { - pr_debug("%s: B%d RDOV IRQ proto=%x\n", card->name, - wch->bch.nr, wch->bch.state); -#ifdef ERROR_STATISTIC - wch->bch.err_rdo++; -#endif - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | - W_B_CMDR_RRST | W_B_CMDR_RACT); - } - } - if (stat & W_B_EXI_XFR) { - if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) { - star = ReadW6692B(wch, W_B_STAR); - pr_debug("%s: B%d star %02x\n", card->name, - wch->bch.nr, star); - } - if (star & W_B_STAR_XDOW) { - pr_warn("%s: B%d XDOW proto=%x\n", card->name, - wch->bch.nr, wch->bch.state); -#ifdef ERROR_STATISTIC - wch->bch.err_xdu++; -#endif - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST | - W_B_CMDR_RACT); - /* resend */ - if (wch->bch.tx_skb) { - if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) - wch->bch.tx_idx = 0; - } - } - send_next(wch); - if (star & W_B_STAR_XDOW) - return; /* handle XDOW only once */ - } - if (stat & W_B_EXI_XDUN) { - pr_warn("%s: B%d XDUN proto=%x\n", card->name, - wch->bch.nr, wch->bch.state); -#ifdef ERROR_STATISTIC - wch->bch.err_xdu++; -#endif - /* resend - no XRST needed */ - if (wch->bch.tx_skb) { - if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) - wch->bch.tx_idx = 0; - } else if (test_bit(FLG_FILLEMPTY, &wch->bch.Flags)) { - test_and_set_bit(FLG_TX_EMPTY, &wch->bch.Flags); - } - send_next(wch); - } -} - -static irqreturn_t -w6692_irq(int intno, void *dev_id) -{ - struct w6692_hw *card = dev_id; - u8 ista; - - spin_lock(&card->lock); - ista = ReadW6692(card, W_ISTA); - if ((ista | card->imask) == card->imask) { - /* possible a shared IRQ reqest */ - spin_unlock(&card->lock); - return IRQ_NONE; - } - card->irqcnt++; - pr_debug("%s: ista %02x\n", card->name, ista); - ista &= ~card->imask; - if (ista & W_INT_B1_EXI) - W6692B_interrupt(card, 0); - if (ista & W_INT_B2_EXI) - W6692B_interrupt(card, 1); - if (ista & W_INT_D_RME) - handle_rxD(card); - if (ista & W_INT_D_RMR) - W6692_empty_Dfifo(card, W_D_FIFO_THRESH); - if (ista & W_INT_D_XFR) - handle_txD(card); - if (ista & W_INT_D_EXI) - handle_statusD(card); - if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */ - pr_debug("%s: W6692 spurious XINT!\n", card->name); -/* End IRQ Handler */ - spin_unlock(&card->lock); - return IRQ_HANDLED; -} - -static void -dbusy_timer_handler(struct timer_list *t) -{ - struct dchannel *dch = timer_container_of(dch, t, timer); - struct w6692_hw *card = dch->hw; - int rbch, star; - u_long flags; - - if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) { - spin_lock_irqsave(&card->lock, flags); - rbch = ReadW6692(card, W_D_RBCH); - star = ReadW6692(card, W_D_STAR); - pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n", - card->name, rbch, star); - if (star & W_D_STAR_XBZ) /* D-Channel Busy */ - test_and_set_bit(FLG_L1_BUSY, &dch->Flags); - else { - /* discard frame; reset transceiver */ - test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags); - if (dch->tx_idx) - dch->tx_idx = 0; - else - pr_info("%s: W6692 D-Channel Busy no tx_idx\n", - card->name); - /* Transmitter reset */ - WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST); - } - spin_unlock_irqrestore(&card->lock, flags); - } -} - -static void initW6692(struct w6692_hw *card) -{ - u8 val; - - timer_setup(&card->dch.timer, dbusy_timer_handler, 0); - w6692_mode(&card->bc[0], ISDN_P_NONE); - w6692_mode(&card->bc[1], ISDN_P_NONE); - WriteW6692(card, W_D_CTL, 0x00); - disable_hwirq(card); - WriteW6692(card, W_D_SAM, 0xff); - WriteW6692(card, W_D_TAM, 0xff); - WriteW6692(card, W_D_MODE, W_D_MODE_RACT); - card->state = W_L1CMD_RST; - ph_command(card, W_L1CMD_RST); - ph_command(card, W_L1CMD_ECK); - /* enable all IRQ but extern */ - card->imask = 0x18; - WriteW6692(card, W_D_EXIM, 0x00); - WriteW6692B(&card->bc[0], W_B_EXIM, 0); - WriteW6692B(&card->bc[1], W_B_EXIM, 0); - /* Reset D-chan receiver and transmitter */ - WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST); - /* Reset B-chan receiver and transmitter */ - WriteW6692B(&card->bc[0], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); - WriteW6692B(&card->bc[1], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); - /* enable peripheral */ - if (card->subtype == W6692_USR) { - /* seems that USR implemented some power control features - * Pin 79 is connected to the oscilator circuit so we - * have to handle it here - */ - card->pctl = 0x80; - card->xdata = 0; - WriteW6692(card, W_PCTL, card->pctl); - WriteW6692(card, W_XDATA, card->xdata); - } else { - card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 | - W_PCTL_OE1 | W_PCTL_OE0; - card->xaddr = 0x00;/* all sw off */ - if (card->fmask & pots) - card->xdata |= 0x06; /* POWER UP/ LED OFF / ALAW */ - if (card->fmask & led) - card->xdata |= 0x04; /* LED OFF */ - if ((card->fmask & pots) || (card->fmask & led)) { - WriteW6692(card, W_PCTL, card->pctl); - WriteW6692(card, W_XADDR, card->xaddr); - WriteW6692(card, W_XDATA, card->xdata); - val = ReadW6692(card, W_XADDR); - if (debug & DEBUG_HW) - pr_notice("%s: W_XADDR=%02x\n", - card->name, val); - } - } -} - -static void -reset_w6692(struct w6692_hw *card) -{ - WriteW6692(card, W_D_CTL, W_D_CTL_SRST); - mdelay(10); - WriteW6692(card, W_D_CTL, 0); -} - -static int -init_card(struct w6692_hw *card) -{ - int cnt = 3; - u_long flags; - - spin_lock_irqsave(&card->lock, flags); - disable_hwirq(card); - spin_unlock_irqrestore(&card->lock, flags); - if (request_irq(card->irq, w6692_irq, IRQF_SHARED, card->name, card)) { - pr_info("%s: couldn't get interrupt %d\n", card->name, - card->irq); - return -EIO; - } - while (cnt--) { - spin_lock_irqsave(&card->lock, flags); - initW6692(card); - enable_hwirq(card); - spin_unlock_irqrestore(&card->lock, flags); - /* Timeout 10ms */ - msleep_interruptible(10); - if (debug & DEBUG_HW) - pr_notice("%s: IRQ %d count %d\n", card->name, - card->irq, card->irqcnt); - if (!card->irqcnt) { - pr_info("%s: IRQ(%d) getting no IRQs during init %d\n", - card->name, card->irq, 3 - cnt); - reset_w6692(card); - } else - return 0; - } - free_irq(card->irq, card); - return -EIO; -} - -static int -w6692_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch); - struct w6692_hw *card = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&card->lock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - ret = 0; - W6692_fill_Bfifo(bc); - } - spin_unlock_irqrestore(&card->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(&card->lock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = w6692_mode(bc, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(&card->lock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - spin_lock_irqsave(&card->lock, flags); - mISDN_clear_bchannel(bch); - w6692_mode(bc, ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - default: - pr_info("%s: %s unknown prim(%x,%x)\n", - card->name, __func__, hh->prim, hh->id); - ret = -EINVAL; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} - -static int -open_bchannel(struct w6692_hw *card, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &card->bc[rq->adr.channel - 1].bch; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - return 0; -} - -static int -channel_ctrl(struct w6692_hw *card, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_L1_TIMER3: - ret = l1_event(card->dch.l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); - break; - default: - pr_info("%s: unknown CTRL OP %x\n", card->name, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -w6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch); - struct w6692_hw *card = bch->hw; - int ret = -EINVAL; - u_long flags; - - pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - cancel_work_sync(&bch->workq); - spin_lock_irqsave(&card->lock, flags); - mISDN_clear_bchannel(bch); - w6692_mode(bc, ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - pr_info("%s: %s unknown prim(%x)\n", - card->name, __func__, cmd); - } - return ret; -} - -static int -w6692_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - u32 id; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&card->lock, flags); - ret = dchannel_senddata(dch, skb); - if (ret > 0) { /* direct TX */ - id = hh->id; /* skb can be freed */ - W6692_fill_Dfifo(card); - ret = 0; - spin_unlock_irqrestore(&card->lock, flags); - queue_ch_frame(ch, PH_DATA_CNF, id, NULL); - } else - spin_unlock_irqrestore(&card->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - ret = l1_event(dch->l1, hh->prim); - break; - case PH_DEACTIVATE_REQ: - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - ret = l1_event(dch->l1, hh->prim); - break; - } - - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -w6692_l1callback(struct dchannel *dch, u32 cmd) -{ - struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); - u_long flags; - - pr_debug("%s: cmd(%x) state(%02x)\n", card->name, cmd, card->state); - switch (cmd) { - case INFO3_P8: - spin_lock_irqsave(&card->lock, flags); - ph_command(card, W_L1CMD_AR8); - spin_unlock_irqrestore(&card->lock, flags); - break; - case INFO3_P10: - spin_lock_irqsave(&card->lock, flags); - ph_command(card, W_L1CMD_AR10); - spin_unlock_irqrestore(&card->lock, flags); - break; - case HW_RESET_REQ: - spin_lock_irqsave(&card->lock, flags); - if (card->state != W_L1IND_DRD) - ph_command(card, W_L1CMD_RST); - ph_command(card, W_L1CMD_ECK); - spin_unlock_irqrestore(&card->lock, flags); - break; - case HW_DEACT_REQ: - skb_queue_purge(&dch->squeue); - if (dch->tx_skb) { - dev_kfree_skb(dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - dev_kfree_skb(dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); - break; - case HW_POWERUP_REQ: - spin_lock_irqsave(&card->lock, flags); - ph_command(card, W_L1CMD_ECK); - spin_unlock_irqrestore(&card->lock, flags); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - default: - pr_debug("%s: %s unknown command %x\n", card->name, - __func__, cmd); - return -1; - } - return 0; -} - -static int -open_dchannel(struct w6692_hw *card, struct channel_req *rq, void *caller) -{ - pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__, - card->dch.dev.id, caller); - if (rq->protocol != ISDN_P_TE_S0) - return -EINVAL; - if (rq->adr.channel == 1) - /* E-Channel not supported */ - return -EINVAL; - rq->ch = &card->dch.dev.D; - rq->ch->protocol = rq->protocol; - if (card->dch.state == 7) - _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - return 0; -} - -static int -w6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); - struct channel_req *rq; - int err = 0; - - pr_debug("%s: DCTRL: %x %p\n", card->name, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if (rq->protocol == ISDN_P_TE_S0) - err = open_dchannel(card, rq, __builtin_return_address(0)); - else - err = open_bchannel(card, rq); - if (err) - break; - if (!try_module_get(THIS_MODULE)) - pr_info("%s: cannot get module\n", card->name); - break; - case CLOSE_CHANNEL: - pr_debug("%s: dev(%d) close from %p\n", card->name, - dch->dev.id, __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(card, arg); - break; - default: - pr_debug("%s: unknown DCTRL command %x\n", card->name, cmd); - return -EINVAL; - } - return err; -} - -static int -setup_w6692(struct w6692_hw *card) -{ - u32 val; - - if (!request_region(card->addr, 256, card->name)) { - pr_info("%s: config port %x-%x already in use\n", card->name, - card->addr, card->addr + 255); - return -EIO; - } - W6692Version(card); - card->bc[0].addr = card->addr; - card->bc[1].addr = card->addr + 0x40; - val = ReadW6692(card, W_ISTA); - if (debug & DEBUG_HW) - pr_notice("%s ISTA=%02x\n", card->name, val); - val = ReadW6692(card, W_IMASK); - if (debug & DEBUG_HW) - pr_notice("%s IMASK=%02x\n", card->name, val); - val = ReadW6692(card, W_D_EXIR); - if (debug & DEBUG_HW) - pr_notice("%s D_EXIR=%02x\n", card->name, val); - val = ReadW6692(card, W_D_EXIM); - if (debug & DEBUG_HW) - pr_notice("%s D_EXIM=%02x\n", card->name, val); - val = ReadW6692(card, W_D_RSTA); - if (debug & DEBUG_HW) - pr_notice("%s D_RSTA=%02x\n", card->name, val); - return 0; -} - -static void -release_card(struct w6692_hw *card) -{ - u_long flags; - - spin_lock_irqsave(&card->lock, flags); - disable_hwirq(card); - w6692_mode(&card->bc[0], ISDN_P_NONE); - w6692_mode(&card->bc[1], ISDN_P_NONE); - if ((card->fmask & led) || card->subtype == W6692_USR) { - card->xdata |= 0x04; /* LED OFF */ - WriteW6692(card, W_XDATA, card->xdata); - } - spin_unlock_irqrestore(&card->lock, flags); - free_irq(card->irq, card); - l1_event(card->dch.l1, CLOSE_CHANNEL); - mISDN_unregister_device(&card->dch.dev); - release_region(card->addr, 256); - mISDN_freebchannel(&card->bc[1].bch); - mISDN_freebchannel(&card->bc[0].bch); - mISDN_freedchannel(&card->dch); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - pci_disable_device(card->pdev); - pci_set_drvdata(card->pdev, NULL); - kfree(card); -} - -static int -setup_instance(struct w6692_hw *card) -{ - int i, err; - u_long flags; - - snprintf(card->name, MISDN_MAX_IDLEN - 1, "w6692.%d", w6692_cnt + 1); - write_lock_irqsave(&card_lock, flags); - list_add_tail(&card->list, &Cards); - write_unlock_irqrestore(&card_lock, flags); - card->fmask = (1 << w6692_cnt); - _set_debug(card); - spin_lock_init(&card->lock); - mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, W6692_ph_bh); - card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0); - card->dch.dev.D.send = w6692_l2l1D; - card->dch.dev.D.ctrl = w6692_dctrl; - card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - card->dch.hw = card; - card->dch.dev.nrbchan = 2; - for (i = 0; i < 2; i++) { - mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM, - W_B_FIFO_THRESH); - card->bc[i].bch.hw = card; - card->bc[i].bch.nr = i + 1; - card->bc[i].bch.ch.nr = i + 1; - card->bc[i].bch.ch.send = w6692_l2l1B; - card->bc[i].bch.ch.ctrl = w6692_bctrl; - set_channelmap(i + 1, card->dch.dev.channelmap); - list_add(&card->bc[i].bch.ch.list, &card->dch.dev.bchannels); - } - err = setup_w6692(card); - if (err) - goto error_setup; - err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, - card->name); - if (err) - goto error_reg; - err = init_card(card); - if (err) - goto error_init; - err = create_l1(&card->dch, w6692_l1callback); - if (!err) { - w6692_cnt++; - pr_notice("W6692 %d cards installed\n", w6692_cnt); - return 0; - } - - free_irq(card->irq, card); -error_init: - mISDN_unregister_device(&card->dch.dev); -error_reg: - release_region(card->addr, 256); -error_setup: - mISDN_freebchannel(&card->bc[1].bch); - mISDN_freebchannel(&card->bc[0].bch); - mISDN_freedchannel(&card->dch); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - kfree(card); - return err; -} - -static int -w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - struct w6692_hw *card; - struct w6692map *m = (struct w6692map *)ent->driver_data; - - card = kzalloc_obj(struct w6692_hw); - if (!card) { - pr_info("No kmem for w6692 card\n"); - return err; - } - card->pdev = pdev; - card->subtype = m->subtype; - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - - printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n", - m->name, pci_name(pdev)); - - card->addr = pci_resource_start(pdev, 1); - card->irq = pdev->irq; - pci_set_drvdata(pdev, card); - err = setup_instance(card); - if (err) - pci_set_drvdata(pdev, NULL); - return err; -} - -static void -w6692_remove_pci(struct pci_dev *pdev) -{ - struct w6692_hw *card = pci_get_drvdata(pdev); - - if (card) - release_card(card); - else - if (debug) - pr_notice("%s: drvdata already removed\n", __func__); -} - -static const struct pci_device_id w6692_ids[] = { - { PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]}, - { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, - PCI_VENDOR_ID_USR, PCI_DEVICE_ID_USR_6692, 0, 0, - (ulong)&w6692_map[2]}, - { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[1]}, - { } -}; -MODULE_DEVICE_TABLE(pci, w6692_ids); - -static struct pci_driver w6692_driver = { - .name = "w6692", - .probe = w6692_probe, - .remove = w6692_remove_pci, - .id_table = w6692_ids, -}; - -static int __init w6692_init(void) -{ - int err; - - pr_notice("Winbond W6692 PCI driver Rev. %s\n", W6692_REV); - - err = pci_register_driver(&w6692_driver); - return err; -} - -static void __exit w6692_cleanup(void) -{ - pci_unregister_driver(&w6692_driver); -} - -module_init(w6692_init); -module_exit(w6692_cleanup); diff --git a/drivers/isdn/hardware/mISDN/w6692.h b/drivers/isdn/hardware/mISDN/w6692.h deleted file mode 100644 index 45e1dc5d6c2d..000000000000 --- a/drivers/isdn/hardware/mISDN/w6692.h +++ /dev/null @@ -1,177 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Winbond W6692 specific defines - * - * Author Karsten Keil - * based on the w6692 I4L driver from Petr Novak - * - * Copyright 2009 by Karsten Keil - */ - -/* Specifications of W6692 registers */ - -#define W_D_RFIFO 0x00 /* R */ -#define W_D_XFIFO 0x04 /* W */ -#define W_D_CMDR 0x08 /* W */ -#define W_D_MODE 0x0c /* R/W */ -#define W_D_TIMR 0x10 /* R/W */ -#define W_ISTA 0x14 /* R_clr */ -#define W_IMASK 0x18 /* R/W */ -#define W_D_EXIR 0x1c /* R_clr */ -#define W_D_EXIM 0x20 /* R/W */ -#define W_D_STAR 0x24 /* R */ -#define W_D_RSTA 0x28 /* R */ -#define W_D_SAM 0x2c /* R/W */ -#define W_D_SAP1 0x30 /* R/W */ -#define W_D_SAP2 0x34 /* R/W */ -#define W_D_TAM 0x38 /* R/W */ -#define W_D_TEI1 0x3c /* R/W */ -#define W_D_TEI2 0x40 /* R/W */ -#define W_D_RBCH 0x44 /* R */ -#define W_D_RBCL 0x48 /* R */ -#define W_TIMR2 0x4c /* W */ -#define W_L1_RC 0x50 /* R/W */ -#define W_D_CTL 0x54 /* R/W */ -#define W_CIR 0x58 /* R */ -#define W_CIX 0x5c /* W */ -#define W_SQR 0x60 /* R */ -#define W_SQX 0x64 /* W */ -#define W_PCTL 0x68 /* R/W */ -#define W_MOR 0x6c /* R */ -#define W_MOX 0x70 /* R/W */ -#define W_MOSR 0x74 /* R_clr */ -#define W_MOCR 0x78 /* R/W */ -#define W_GCR 0x7c /* R/W */ - -#define W_B_RFIFO 0x80 /* R */ -#define W_B_XFIFO 0x84 /* W */ -#define W_B_CMDR 0x88 /* W */ -#define W_B_MODE 0x8c /* R/W */ -#define W_B_EXIR 0x90 /* R_clr */ -#define W_B_EXIM 0x94 /* R/W */ -#define W_B_STAR 0x98 /* R */ -#define W_B_ADM1 0x9c /* R/W */ -#define W_B_ADM2 0xa0 /* R/W */ -#define W_B_ADR1 0xa4 /* R/W */ -#define W_B_ADR2 0xa8 /* R/W */ -#define W_B_RBCL 0xac /* R */ -#define W_B_RBCH 0xb0 /* R */ - -#define W_XADDR 0xf4 /* R/W */ -#define W_XDATA 0xf8 /* R/W */ -#define W_EPCTL 0xfc /* W */ - -/* W6692 register bits */ - -#define W_D_CMDR_XRST 0x01 -#define W_D_CMDR_XME 0x02 -#define W_D_CMDR_XMS 0x08 -#define W_D_CMDR_STT 0x10 -#define W_D_CMDR_RRST 0x40 -#define W_D_CMDR_RACK 0x80 - -#define W_D_MODE_RLP 0x01 -#define W_D_MODE_DLP 0x02 -#define W_D_MODE_MFD 0x04 -#define W_D_MODE_TEE 0x08 -#define W_D_MODE_TMS 0x10 -#define W_D_MODE_RACT 0x40 -#define W_D_MODE_MMS 0x80 - -#define W_INT_B2_EXI 0x01 -#define W_INT_B1_EXI 0x02 -#define W_INT_D_EXI 0x04 -#define W_INT_XINT0 0x08 -#define W_INT_XINT1 0x10 -#define W_INT_D_XFR 0x20 -#define W_INT_D_RME 0x40 -#define W_INT_D_RMR 0x80 - -#define W_D_EXI_WEXP 0x01 -#define W_D_EXI_TEXP 0x02 -#define W_D_EXI_ISC 0x04 -#define W_D_EXI_MOC 0x08 -#define W_D_EXI_TIN2 0x10 -#define W_D_EXI_XCOL 0x20 -#define W_D_EXI_XDUN 0x40 -#define W_D_EXI_RDOV 0x80 - -#define W_D_STAR_DRDY 0x10 -#define W_D_STAR_XBZ 0x20 -#define W_D_STAR_XDOW 0x80 - -#define W_D_RSTA_RMB 0x10 -#define W_D_RSTA_CRCE 0x20 -#define W_D_RSTA_RDOV 0x40 - -#define W_D_CTL_SRST 0x20 - -#define W_CIR_SCC 0x80 -#define W_CIR_ICC 0x40 -#define W_CIR_COD_MASK 0x0f - -#define W_PCTL_PCX 0x01 -#define W_PCTL_XMODE 0x02 -#define W_PCTL_OE0 0x04 -#define W_PCTL_OE1 0x08 -#define W_PCTL_OE2 0x10 -#define W_PCTL_OE3 0x20 -#define W_PCTL_OE4 0x40 -#define W_PCTL_OE5 0x80 - -#define W_B_CMDR_XRST 0x01 -#define W_B_CMDR_XME 0x02 -#define W_B_CMDR_XMS 0x04 -#define W_B_CMDR_RACT 0x20 -#define W_B_CMDR_RRST 0x40 -#define W_B_CMDR_RACK 0x80 - -#define W_B_MODE_FTS0 0x01 -#define W_B_MODE_FTS1 0x02 -#define W_B_MODE_SW56 0x04 -#define W_B_MODE_BSW0 0x08 -#define W_B_MODE_BSW1 0x10 -#define W_B_MODE_EPCM 0x20 -#define W_B_MODE_ITF 0x40 -#define W_B_MODE_MMS 0x80 - -#define W_B_EXI_XDUN 0x01 -#define W_B_EXI_XFR 0x02 -#define W_B_EXI_RDOV 0x10 -#define W_B_EXI_RME 0x20 -#define W_B_EXI_RMR 0x40 - -#define W_B_STAR_XBZ 0x01 -#define W_B_STAR_XDOW 0x04 -#define W_B_STAR_RMB 0x10 -#define W_B_STAR_CRCE 0x20 -#define W_B_STAR_RDOV 0x40 - -#define W_B_RBCH_LOV 0x20 - -/* W6692 Layer1 commands */ - -#define W_L1CMD_ECK 0x00 -#define W_L1CMD_RST 0x01 -#define W_L1CMD_SCP 0x04 -#define W_L1CMD_SSP 0x02 -#define W_L1CMD_AR8 0x08 -#define W_L1CMD_AR10 0x09 -#define W_L1CMD_EAL 0x0a -#define W_L1CMD_DRC 0x0f - -/* W6692 Layer1 indications */ - -#define W_L1IND_CE 0x07 -#define W_L1IND_DRD 0x00 -#define W_L1IND_LD 0x04 -#define W_L1IND_ARD 0x08 -#define W_L1IND_TI 0x0a -#define W_L1IND_ATI 0x0b -#define W_L1IND_AI8 0x0c -#define W_L1IND_AI10 0x0d -#define W_L1IND_CD 0x0f - -/* FIFO thresholds */ -#define W_D_FIFO_THRESH 64 -#define W_B_FIFO_THRESH 64 diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig deleted file mode 100644 index c9a53c222472..000000000000 --- a/drivers/isdn/mISDN/Kconfig +++ /dev/null @@ -1,48 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# modularer ISDN driver -# - -menuconfig MISDN - tristate "Modular ISDN driver" - help - Enable support for the modular ISDN driver. - -if MISDN != n - -config MISDN_DSP - tristate "Digital Audio Processing of transparent data" - depends on MISDN - select BITREVERSE - help - Enable support for digital audio processing capability. - - This module may be used for special applications that require - cross connecting of bchannels, conferencing, dtmf decoding, - echo cancellation, tone generation, and Blowfish encryption and - decryption. It may use hardware features if available. - - E.g. it is required for PBX4Linux. Go to http://isdn.eversberg.eu - and get more information about this module and its usage. - - If unsure, say 'N'. - -config MISDN_L1OIP - tristate "ISDN over IP tunnel" - depends on MISDN - help - Enable support for ISDN over IP tunnel. - - It features: - - dynamic IP exchange, if one or both peers have dynamic IPs - - BRI (S0) and PRI (S2M) interface - - layer 1 control via network keepalive frames - - direct tunneling of physical interface via IP - - NOTE: This protocol is called 'Layer 1 over IP' and is not - compatible with ISDNoIP (Agfeo) or TDMoIP. Protocol description is - provided in the source code. - -source "drivers/isdn/hardware/mISDN/Kconfig" - -endif #MISDN diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile deleted file mode 100644 index f3b4b7fa85f8..000000000000 --- a/drivers/isdn/mISDN/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the modular ISDN driver -# - -obj-$(CONFIG_MISDN) += mISDN_core.o -obj-$(CONFIG_MISDN_DSP) += mISDN_dsp.o -obj-$(CONFIG_MISDN_L1OIP) += l1oip.o - -# multi objects - -mISDN_core-objs := core.o fsm.o socket.o clock.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o -mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o -l1oip-objs := l1oip_core.o l1oip_codec.o diff --git a/drivers/isdn/mISDN/clock.c b/drivers/isdn/mISDN/clock.c deleted file mode 100644 index 2668be9de20a..000000000000 --- a/drivers/isdn/mISDN/clock.c +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright 2008 by Andreas Eversberg - * - * Quick API description: - * - * A clock source registers using mISDN_register_clock: - * name = text string to name clock source - * priority = value to priorize clock sources (0 = default) - * ctl = callback function to enable/disable clock source - * priv = private pointer of clock source - * return = pointer to clock source structure; - * - * Note: Callback 'ctl' can be called before mISDN_register_clock returns! - * Also it can be called during mISDN_unregister_clock. - * - * A clock source calls mISDN_clock_update with given samples elapsed, if - * enabled. If function call is delayed, tv must be set with the timestamp - * of the actual event. - * - * A clock source unregisters using mISDN_unregister_clock. - * - * To get current clock, call mISDN_clock_get. The signed short value - * counts the number of samples since. Time since last clock event is added. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "core.h" - -static u_int *debug; -static LIST_HEAD(iclock_list); -static DEFINE_RWLOCK(iclock_lock); -static u16 iclock_count; /* counter of last clock */ -static ktime_t iclock_timestamp; /* time stamp of last clock */ -static int iclock_timestamp_valid; /* already received one timestamp */ -static struct mISDNclock *iclock_current; - -void -mISDN_init_clock(u_int *dp) -{ - debug = dp; - iclock_timestamp = ktime_get(); -} - -static void -select_iclock(void) -{ - struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL; - int pri = -128; - - list_for_each_entry(iclock, &iclock_list, list) { - if (iclock->pri > pri) { - pri = iclock->pri; - bestclock = iclock; - } - if (iclock_current == iclock) - lastclock = iclock; - } - if (lastclock && bestclock != lastclock) { - /* last used clock source still exists but changes, disable */ - if (*debug & DEBUG_CLOCK) - printk(KERN_DEBUG "Old clock source '%s' disable.\n", - lastclock->name); - lastclock->ctl(lastclock->priv, 0); - } - if (bestclock && bestclock != iclock_current) { - /* new clock source selected, enable */ - if (*debug & DEBUG_CLOCK) - printk(KERN_DEBUG "New clock source '%s' enable.\n", - bestclock->name); - bestclock->ctl(bestclock->priv, 1); - } - if (bestclock != iclock_current) { - /* no clock received yet */ - iclock_timestamp_valid = 0; - } - iclock_current = bestclock; -} - -struct mISDNclock -*mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv) -{ - u_long flags; - struct mISDNclock *iclock; - - if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) - printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri); - iclock = kzalloc_obj(struct mISDNclock, GFP_ATOMIC); - if (!iclock) { - printk(KERN_ERR "%s: No memory for clock entry.\n", __func__); - return NULL; - } - strscpy(iclock->name, name, sizeof(iclock->name)); - iclock->pri = pri; - iclock->priv = priv; - iclock->ctl = ctl; - write_lock_irqsave(&iclock_lock, flags); - list_add_tail(&iclock->list, &iclock_list); - select_iclock(); - write_unlock_irqrestore(&iclock_lock, flags); - return iclock; -} -EXPORT_SYMBOL(mISDN_register_clock); - -void -mISDN_unregister_clock(struct mISDNclock *iclock) -{ - u_long flags; - - if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) - printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name, - iclock->pri); - write_lock_irqsave(&iclock_lock, flags); - if (iclock_current == iclock) { - if (*debug & DEBUG_CLOCK) - printk(KERN_DEBUG - "Current clock source '%s' unregisters.\n", - iclock->name); - iclock->ctl(iclock->priv, 0); - } - list_del(&iclock->list); - select_iclock(); - write_unlock_irqrestore(&iclock_lock, flags); -} -EXPORT_SYMBOL(mISDN_unregister_clock); - -void -mISDN_clock_update(struct mISDNclock *iclock, int samples, ktime_t *timestamp) -{ - u_long flags; - ktime_t timestamp_now; - u16 delta; - - write_lock_irqsave(&iclock_lock, flags); - if (iclock_current != iclock) { - printk(KERN_ERR "%s: '%s' sends us clock updates, but we do " - "listen to '%s'. This is a bug!\n", __func__, - iclock->name, - iclock_current ? iclock_current->name : "nothing"); - iclock->ctl(iclock->priv, 0); - write_unlock_irqrestore(&iclock_lock, flags); - return; - } - if (iclock_timestamp_valid) { - /* increment sample counter by given samples */ - iclock_count += samples; - if (timestamp) { /* timestamp must be set, if function call is delayed */ - iclock_timestamp = *timestamp; - } else { - iclock_timestamp = ktime_get(); - } - } else { - /* calc elapsed time by system clock */ - if (timestamp) { /* timestamp must be set, if function call is delayed */ - timestamp_now = *timestamp; - } else { - timestamp_now = ktime_get(); - } - delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp), - (NSEC_PER_SEC / 8000)); - /* add elapsed time to counter and set new timestamp */ - iclock_count += delta; - iclock_timestamp = timestamp_now; - iclock_timestamp_valid = 1; - if (*debug & DEBUG_CLOCK) - printk("Received first clock from source '%s'.\n", - iclock_current ? iclock_current->name : "nothing"); - } - write_unlock_irqrestore(&iclock_lock, flags); -} -EXPORT_SYMBOL(mISDN_clock_update); - -unsigned short -mISDN_clock_get(void) -{ - u_long flags; - ktime_t timestamp_now; - u16 delta; - u16 count; - - read_lock_irqsave(&iclock_lock, flags); - /* calc elapsed time by system clock */ - timestamp_now = ktime_get(); - delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp), - (NSEC_PER_SEC / 8000)); - /* add elapsed time to counter */ - count = iclock_count + delta; - read_unlock_irqrestore(&iclock_lock, flags); - return count; -} -EXPORT_SYMBOL(mISDN_clock_get); diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c deleted file mode 100644 index 8ec2d4d4f135..000000000000 --- a/drivers/isdn/mISDN/core.c +++ /dev/null @@ -1,400 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include "core.h" - -static u_int debug; - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("Modular ISDN core driver"); -MODULE_LICENSE("GPL"); -module_param(debug, uint, S_IRUGO | S_IWUSR); - -static u64 device_ids; -#define MAX_DEVICE_ID 63 - -static LIST_HEAD(Bprotocols); -static DEFINE_RWLOCK(bp_lock); - -static void mISDN_dev_release(struct device *dev) -{ - /* nothing to do: the device is part of its parent's data structure */ -} - -static ssize_t id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return -ENODEV; - return sprintf(buf, "%d\n", mdev->id); -} -static DEVICE_ATTR_RO(id); - -static ssize_t nrbchan_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return -ENODEV; - return sprintf(buf, "%d\n", mdev->nrbchan); -} -static DEVICE_ATTR_RO(nrbchan); - -static ssize_t d_protocols_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return -ENODEV; - return sprintf(buf, "%d\n", mdev->Dprotocols); -} -static DEVICE_ATTR_RO(d_protocols); - -static ssize_t b_protocols_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return -ENODEV; - return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols()); -} -static DEVICE_ATTR_RO(b_protocols); - -static ssize_t protocol_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return -ENODEV; - return sprintf(buf, "%d\n", mdev->D.protocol); -} -static DEVICE_ATTR_RO(protocol); - -static ssize_t name_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - strcpy(buf, dev_name(dev)); - return strlen(buf); -} -static DEVICE_ATTR_RO(name); - -#if 0 /* hangs */ -static ssize_t name_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - int err = 0; - char *out = kmalloc(count + 1, GFP_KERNEL); - - if (!out) - return -ENOMEM; - - memcpy(out, buf, count); - if (count && out[count - 1] == '\n') - out[--count] = 0; - if (count) - err = device_rename(dev, out); - kfree(out); - - return (err < 0) ? err : count; -} -static DEVICE_ATTR_RW(name); -#endif - -static ssize_t channelmap_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - char *bp = buf; - int i; - - for (i = 0; i <= mdev->nrbchan; i++) - *bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0'; - - return bp - buf; -} -static DEVICE_ATTR_RO(channelmap); - -static struct attribute *mISDN_attrs[] = { - &dev_attr_id.attr, - &dev_attr_d_protocols.attr, - &dev_attr_b_protocols.attr, - &dev_attr_protocol.attr, - &dev_attr_channelmap.attr, - &dev_attr_nrbchan.attr, - &dev_attr_name.attr, - NULL, -}; -ATTRIBUTE_GROUPS(mISDN); - -static int mISDN_uevent(const struct device *dev, struct kobj_uevent_env *env) -{ - const struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return 0; - - if (add_uevent_var(env, "nchans=%d", mdev->nrbchan)) - return -ENOMEM; - - return 0; -} - -static struct class mISDN_class = { - .name = "mISDN", - .dev_uevent = mISDN_uevent, - .dev_groups = mISDN_groups, - .dev_release = mISDN_dev_release, -}; - -static int -_get_mdevice(struct device *dev, const void *id) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return 0; - if (mdev->id != *(const u_int *)id) - return 0; - return 1; -} - -struct mISDNdevice -*get_mdevice(u_int id) -{ - return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id, - _get_mdevice)); -} - -static int -_get_mdevice_count(struct device *dev, void *cnt) -{ - *(int *)cnt += 1; - return 0; -} - -int -get_mdevice_count(void) -{ - int cnt = 0; - - class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count); - return cnt; -} - -static int -get_free_devid(void) -{ - u_int i; - - for (i = 0; i <= MAX_DEVICE_ID; i++) - if (!test_and_set_bit(i, (u_long *)&device_ids)) - break; - if (i > MAX_DEVICE_ID) - return -EBUSY; - return i; -} - -int -mISDN_register_device(struct mISDNdevice *dev, - struct device *parent, char *name) -{ - int err; - - err = get_free_devid(); - if (err < 0) - return err; - dev->id = err; - - device_initialize(&dev->dev); - if (name && name[0]) - dev_set_name(&dev->dev, "%s", name); - else - dev_set_name(&dev->dev, "mISDN%d", dev->id); - if (debug & DEBUG_CORE) - printk(KERN_DEBUG "mISDN_register %s %d\n", - dev_name(&dev->dev), dev->id); - dev->dev.class = &mISDN_class; - - err = create_stack(dev); - if (err) - goto error1; - - dev->dev.platform_data = dev; - dev->dev.parent = parent; - dev_set_drvdata(&dev->dev, dev); - - err = device_add(&dev->dev); - if (err) - goto error3; - return 0; - -error3: - delete_stack(dev); -error1: - put_device(&dev->dev); - return err; - -} -EXPORT_SYMBOL(mISDN_register_device); - -void -mISDN_unregister_device(struct mISDNdevice *dev) { - if (debug & DEBUG_CORE) - printk(KERN_DEBUG "mISDN_unregister %s %d\n", - dev_name(&dev->dev), dev->id); - /* sysfs_remove_link(&dev->dev.kobj, "device"); */ - device_del(&dev->dev); - dev_set_drvdata(&dev->dev, NULL); - - test_and_clear_bit(dev->id, (u_long *)&device_ids); - delete_stack(dev); - put_device(&dev->dev); -} -EXPORT_SYMBOL(mISDN_unregister_device); - -u_int -get_all_Bprotocols(void) -{ - struct Bprotocol *bp; - u_int m = 0; - - read_lock(&bp_lock); - list_for_each_entry(bp, &Bprotocols, list) - m |= bp->Bprotocols; - read_unlock(&bp_lock); - return m; -} - -struct Bprotocol * -get_Bprotocol4mask(u_int m) -{ - struct Bprotocol *bp; - - read_lock(&bp_lock); - list_for_each_entry(bp, &Bprotocols, list) - if (bp->Bprotocols & m) { - read_unlock(&bp_lock); - return bp; - } - read_unlock(&bp_lock); - return NULL; -} - -int -mISDN_register_Bprotocol(struct Bprotocol *bp) -{ - u_long flags; - struct Bprotocol *old; - - if (debug & DEBUG_CORE) - printk(KERN_DEBUG "%s: %s/%x\n", __func__, - bp->name, bp->Bprotocols); - old = get_Bprotocol4mask(bp->Bprotocols); - if (old) { - printk(KERN_WARNING - "register duplicate protocol old %s/%x new %s/%x\n", - old->name, old->Bprotocols, bp->name, bp->Bprotocols); - return -EBUSY; - } - write_lock_irqsave(&bp_lock, flags); - list_add_tail(&bp->list, &Bprotocols); - write_unlock_irqrestore(&bp_lock, flags); - return 0; -} -EXPORT_SYMBOL(mISDN_register_Bprotocol); - -void -mISDN_unregister_Bprotocol(struct Bprotocol *bp) -{ - u_long flags; - - if (debug & DEBUG_CORE) - printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name, - bp->Bprotocols); - write_lock_irqsave(&bp_lock, flags); - list_del(&bp->list); - write_unlock_irqrestore(&bp_lock, flags); -} -EXPORT_SYMBOL(mISDN_unregister_Bprotocol); - -static const char *msg_no_channel = ""; -static const char *msg_no_stack = ""; -static const char *msg_no_stackdev = ""; - -const char *mISDNDevName4ch(struct mISDNchannel *ch) -{ - if (!ch) - return msg_no_channel; - if (!ch->st) - return msg_no_stack; - if (!ch->st->dev) - return msg_no_stackdev; - return dev_name(&ch->st->dev->dev); -}; -EXPORT_SYMBOL(mISDNDevName4ch); - -static int -mISDNInit(void) -{ - int err; - - printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n", - MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); - mISDN_init_clock(&debug); - mISDN_initstack(&debug); - err = class_register(&mISDN_class); - if (err) - goto error1; - err = mISDN_inittimer(&debug); - if (err) - goto error2; - err = Isdnl1_Init(&debug); - if (err) - goto error3; - err = Isdnl2_Init(&debug); - if (err) - goto error4; - err = misdn_sock_init(&debug); - if (err) - goto error5; - return 0; - -error5: - Isdnl2_cleanup(); -error4: - Isdnl1_cleanup(); -error3: - mISDN_timer_cleanup(); -error2: - class_unregister(&mISDN_class); -error1: - return err; -} - -static void mISDN_cleanup(void) -{ - misdn_sock_cleanup(); - Isdnl2_cleanup(); - Isdnl1_cleanup(); - mISDN_timer_cleanup(); - class_unregister(&mISDN_class); - - printk(KERN_DEBUG "mISDNcore unloaded\n"); -} - -module_init(mISDNInit); -module_exit(mISDN_cleanup); diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h deleted file mode 100644 index 5617c06de8e4..000000000000 --- a/drivers/isdn/mISDN/core.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright 2008 by Karsten Keil - */ - -#ifndef mISDN_CORE_H -#define mISDN_CORE_H - -extern struct mISDNdevice *get_mdevice(u_int); -extern int get_mdevice_count(void); - -/* stack status flag */ -#define mISDN_STACK_ACTION_MASK 0x0000ffff -#define mISDN_STACK_COMMAND_MASK 0x000f0000 -#define mISDN_STACK_STATUS_MASK 0xfff00000 -/* action bits 0-15 */ -#define mISDN_STACK_WORK 0 -#define mISDN_STACK_SETUP 1 -#define mISDN_STACK_CLEARING 2 -#define mISDN_STACK_RESTART 3 -#define mISDN_STACK_WAKEUP 4 -#define mISDN_STACK_ABORT 15 -/* command bits 16-19 */ -#define mISDN_STACK_STOPPED 16 -#define mISDN_STACK_INIT 17 -#define mISDN_STACK_THREADSTART 18 -/* status bits 20-31 */ -#define mISDN_STACK_BCHANNEL 20 -#define mISDN_STACK_ACTIVE 29 -#define mISDN_STACK_RUNNING 30 -#define mISDN_STACK_KILLED 31 - - -/* manager options */ -#define MGR_OPT_USER 24 -#define MGR_OPT_NETWORK 25 - -extern int connect_Bstack(struct mISDNdevice *, struct mISDNchannel *, - u_int, struct sockaddr_mISDN *); -extern int connect_layer1(struct mISDNdevice *, struct mISDNchannel *, - u_int, struct sockaddr_mISDN *); -extern int create_l2entity(struct mISDNdevice *, struct mISDNchannel *, - u_int, struct sockaddr_mISDN *); - -extern int create_stack(struct mISDNdevice *); -extern int create_teimanager(struct mISDNdevice *); -extern void delete_teimanager(struct mISDNchannel *); -extern void delete_channel(struct mISDNchannel *); -extern void delete_stack(struct mISDNdevice *); -extern void mISDN_initstack(u_int *); -extern int misdn_sock_init(u_int *); -extern void misdn_sock_cleanup(void); -extern void add_layer2(struct mISDNchannel *, struct mISDNstack *); -extern void __add_layer2(struct mISDNchannel *, struct mISDNstack *); - -extern u_int get_all_Bprotocols(void); -struct Bprotocol *get_Bprotocol4mask(u_int); - -extern int mISDN_inittimer(u_int *); -extern void mISDN_timer_cleanup(void); - -extern int Isdnl1_Init(u_int *); -extern void Isdnl1_cleanup(void); -extern int Isdnl2_Init(u_int *); -extern void Isdnl2_cleanup(void); - -extern void mISDN_init_clock(u_int *); - -#endif diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h deleted file mode 100644 index baf31258f5c9..000000000000 --- a/drivers/isdn/mISDN/dsp.h +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Audio support data for ISDN4Linux. - * - * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#define DEBUG_DSP_CTRL 0x0001 -#define DEBUG_DSP_CORE 0x0002 -#define DEBUG_DSP_DTMF 0x0004 -#define DEBUG_DSP_CMX 0x0010 -#define DEBUG_DSP_TONE 0x0020 -#define DEBUG_DSP_BLOWFISH 0x0040 -#define DEBUG_DSP_DELAY 0x0100 -#define DEBUG_DSP_CLOCK 0x0200 -#define DEBUG_DSP_DTMFCOEFF 0x8000 /* heavy output */ - -/* options may be: - * - * bit 0 = use ulaw instead of alaw - * bit 1 = enable hfc hardware acceleration for all channels - * - */ -#define DSP_OPT_ULAW (1 << 0) -#define DSP_OPT_NOHARDWARE (1 << 1) - -#include -#include - -#include "dsp_ecdis.h" - -extern int dsp_options; -extern int dsp_debug; -extern int dsp_poll; -extern int dsp_tics; -extern spinlock_t dsp_lock; -extern struct work_struct dsp_workq; -extern u32 dsp_poll_diff; /* calculated fix-comma corrected poll value */ - -/*************** - * audio stuff * - ***************/ - -extern s32 dsp_audio_alaw_to_s32[256]; -extern s32 dsp_audio_ulaw_to_s32[256]; -extern s32 *dsp_audio_law_to_s32; -extern u8 dsp_audio_s16_to_law[65536]; -extern u8 dsp_audio_alaw_to_ulaw[256]; -extern u8 dsp_audio_mix_law[65536]; -extern u8 dsp_audio_seven2law[128]; -extern u8 dsp_audio_law2seven[256]; -extern void dsp_audio_generate_law_tables(void); -extern void dsp_audio_generate_s2law_table(void); -extern void dsp_audio_generate_seven(void); -extern void dsp_audio_generate_mix_table(void); -extern void dsp_audio_generate_ulaw_samples(void); -extern void dsp_audio_generate_volume_changes(void); -extern u8 dsp_silence; - - -/************* - * cmx stuff * - *************/ - -#define MAX_POLL 256 /* maximum number of send-chunks */ - -#define CMX_BUFF_SIZE 0x8000 /* must be 2**n (0x1000 about 1/2 second) */ -#define CMX_BUFF_HALF 0x4000 /* CMX_BUFF_SIZE / 2 */ -#define CMX_BUFF_MASK 0x7fff /* CMX_BUFF_SIZE - 1 */ - -/* how many seconds will we check the lowest delay until the jitter buffer - is reduced by that delay */ -#define MAX_SECONDS_JITTER_CHECK 5 - -extern struct timer_list dsp_spl_tl; - -/* the datatype need to match jiffies datatype */ -extern unsigned long dsp_spl_jiffies; - -/* the structure of conferences: - * - * each conference has a unique number, given by user space. - * the conferences are linked in a chain. - * each conference has members linked in a chain. - * each dsplayer points to a member, each member points to a dsplayer. - */ - -/* all members within a conference (this is linked 1:1 with the dsp) */ -struct dsp; -struct dsp_conf_member { - struct list_head list; - struct dsp *dsp; -}; - -/* the list of all conferences */ -struct dsp_conf { - struct list_head list; - u32 id; - /* all cmx stacks with the same ID are - connected */ - struct list_head mlist; - int software; /* conf is processed by software */ - int hardware; /* conf is processed by hardware */ - /* note: if both unset, has only one member */ -}; - - -/************** - * DTMF stuff * - **************/ - -#define DSP_DTMF_NPOINTS 102 - -#define ECHOCAN_BUFF_SIZE 0x400 /* must be 2**n */ -#define ECHOCAN_BUFF_MASK 0x3ff /* -1 */ - -struct dsp_dtmf { - int enable; /* dtmf is enabled */ - int treshold; /* above this is dtmf (square of) */ - int software; /* dtmf uses software decoding */ - int hardware; /* dtmf uses hardware decoding */ - int size; /* number of bytes in buffer */ - signed short buffer[DSP_DTMF_NPOINTS]; - /* buffers one full dtmf frame */ - u8 lastwhat, lastdigit; - int count; - u8 digits[16]; /* dtmf result */ -}; - - -/****************** - * pipeline stuff * - ******************/ -struct dsp_pipeline { - rwlock_t lock; - struct list_head list; - int inuse; -}; - -/*************** - * tones stuff * - ***************/ - -struct dsp_tone { - int software; /* tones are generated by software */ - int hardware; /* tones are generated by hardware */ - int tone; - void *pattern; - int count; - int index; - struct timer_list tl; -}; - -/*************** - * echo stuff * - ***************/ - -struct dsp_echo { - int software; /* echo is generated by software */ - int hardware; /* echo is generated by hardware */ -}; - -/***************** - * general stuff * - *****************/ - -struct dsp { - struct list_head list; - struct mISDNchannel ch; - struct mISDNchannel *up; - unsigned char name[64]; - int b_active; - struct dsp_echo echo; - int rx_disabled; /* what the user wants */ - int rx_is_off; /* what the card is */ - int tx_mix; - struct dsp_tone tone; - struct dsp_dtmf dtmf; - int tx_volume, rx_volume; - - /* queue for sending frames */ - struct work_struct workq; - struct sk_buff_head sendq; - int hdlc; /* if mode is hdlc */ - int data_pending; /* currently an unconfirmed frame */ - - /* conference stuff */ - u32 conf_id; - struct dsp_conf *conf; - struct dsp_conf_member - *member; - - /* buffer stuff */ - int rx_W; /* current write pos for data without timestamp */ - int rx_R; /* current read pos for transmit clock */ - int rx_init; /* if set, pointers will be adjusted first */ - int tx_W; /* current write pos for transmit data */ - int tx_R; /* current read pos for transmit clock */ - int rx_delay[MAX_SECONDS_JITTER_CHECK]; - int tx_delay[MAX_SECONDS_JITTER_CHECK]; - u8 tx_buff[CMX_BUFF_SIZE]; - u8 rx_buff[CMX_BUFF_SIZE]; - int last_tx; /* if set, we transmitted last poll interval */ - int cmx_delay; /* initial delay of buffers, - or 0 for dynamic jitter buffer */ - int tx_dejitter; /* if set, dejitter tx buffer */ - int tx_data; /* enables tx-data of CMX to upper layer */ - - /* hardware stuff */ - struct dsp_features features; - int features_rx_off; /* set if rx_off is featured */ - int features_fill_empty; /* set if fill_empty is featured */ - int pcm_slot_rx; /* current PCM slot (or -1) */ - int pcm_bank_rx; - int pcm_slot_tx; - int pcm_bank_tx; - int hfc_conf; /* unique id of current conference (or -1) */ - - /* encryption stuff */ - int bf_enable; - u32 bf_p[18]; - u32 bf_s[1024]; - int bf_crypt_pos; - u8 bf_data_in[9]; - u8 bf_crypt_out[9]; - int bf_decrypt_in_pos; - int bf_decrypt_out_pos; - u8 bf_crypt_inring[16]; - u8 bf_data_out[9]; - int bf_sync; - - struct dsp_pipeline - pipeline; -}; - -/* functions */ - -extern void dsp_change_volume(struct sk_buff *skb, int volume); - -extern struct list_head dsp_ilist; -extern struct list_head conf_ilist; -extern void dsp_cmx_debug(struct dsp *dsp); -extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp); -extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id); -extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb); -extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb); -extern void dsp_cmx_send(struct timer_list *arg); -extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb); -extern int dsp_cmx_del_conf_member(struct dsp *dsp); -extern int dsp_cmx_del_conf(struct dsp_conf *conf); - -extern void dsp_dtmf_goertzel_init(struct dsp *dsp); -extern void dsp_dtmf_hardware(struct dsp *dsp); -extern u8 *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, - int fmt); - -extern int dsp_tone(struct dsp *dsp, int tone); -extern void dsp_tone_copy(struct dsp *dsp, u8 *data, int len); -extern void dsp_tone_timeout(struct timer_list *t); - -extern void dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len); -extern void dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len); -extern int dsp_bf_init(struct dsp *dsp, const u8 *key, unsigned int keylen); -extern void dsp_bf_cleanup(struct dsp *dsp); - -extern int dsp_pipeline_module_init(void); -extern void dsp_pipeline_module_exit(void); -extern int dsp_pipeline_init(struct dsp_pipeline *pipeline); -extern void dsp_pipeline_destroy(struct dsp_pipeline *pipeline); -extern int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg); -extern void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, - int len); -extern void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, - int len, unsigned int txlen); diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c deleted file mode 100644 index bbef98e7a16e..000000000000 --- a/drivers/isdn/mISDN/dsp_audio.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Audio support data for mISDN_dsp. - * - * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) - * Rewritten by Peter - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include -#include -#include -#include "core.h" -#include "dsp.h" - -/* ulaw[unsigned char] -> signed 16-bit */ -s32 dsp_audio_ulaw_to_s32[256]; -/* alaw[unsigned char] -> signed 16-bit */ -s32 dsp_audio_alaw_to_s32[256]; - -s32 *dsp_audio_law_to_s32; -EXPORT_SYMBOL(dsp_audio_law_to_s32); - -/* signed 16-bit -> law */ -u8 dsp_audio_s16_to_law[65536]; -EXPORT_SYMBOL(dsp_audio_s16_to_law); - -/* alaw -> ulaw */ -u8 dsp_audio_alaw_to_ulaw[256]; -/* ulaw -> alaw */ -static u8 dsp_audio_ulaw_to_alaw[256]; -u8 dsp_silence; - - -/***************************************************** - * generate table for conversion of s16 to alaw/ulaw * - *****************************************************/ - -#define AMI_MASK 0x55 - -static inline unsigned char linear2alaw(short int linear) -{ - int mask; - int seg; - int pcm_val; - static int seg_end[8] = { - 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF - }; - - pcm_val = linear; - if (pcm_val >= 0) { - /* Sign (7th) bit = 1 */ - mask = AMI_MASK | 0x80; - } else { - /* Sign bit = 0 */ - mask = AMI_MASK; - pcm_val = -pcm_val; - } - - /* Convert the scaled magnitude to segment number. */ - for (seg = 0; seg < 8; seg++) { - if (pcm_val <= seg_end[seg]) - break; - } - /* Combine the sign, segment, and quantization bits. */ - return ((seg << 4) | - ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask; -} - - -static inline short int alaw2linear(unsigned char alaw) -{ - int i; - int seg; - - alaw ^= AMI_MASK; - i = ((alaw & 0x0F) << 4) + 8 /* rounding error */; - seg = (((int) alaw & 0x70) >> 4); - if (seg) - i = (i + 0x100) << (seg - 1); - return (short int) ((alaw & 0x80) ? i : -i); -} - -static inline short int ulaw2linear(unsigned char ulaw) -{ - short mu, e, f, y; - static short etab[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764}; - - mu = 255 - ulaw; - e = (mu & 0x70) / 16; - f = mu & 0x0f; - y = f * (1 << (e + 3)); - y += etab[e]; - if (mu & 0x80) - y = -y; - return y; -} - -#define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */ - -static unsigned char linear2ulaw(short sample) -{ - static int exp_lut[256] = { - 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; - int sign, exponent, mantissa; - unsigned char ulawbyte; - - /* Get the sample into sign-magnitude. */ - sign = (sample >> 8) & 0x80; /* set aside the sign */ - if (sign != 0) - sample = -sample; /* get magnitude */ - - /* Convert from 16 bit linear to ulaw. */ - sample = sample + BIAS; - exponent = exp_lut[(sample >> 7) & 0xFF]; - mantissa = (sample >> (exponent + 3)) & 0x0F; - ulawbyte = ~(sign | (exponent << 4) | mantissa); - - return ulawbyte; -} - -void dsp_audio_generate_law_tables(void) -{ - int i; - for (i = 0; i < 256; i++) - dsp_audio_alaw_to_s32[i] = alaw2linear(bitrev8((u8)i)); - - for (i = 0; i < 256; i++) - dsp_audio_ulaw_to_s32[i] = ulaw2linear(bitrev8((u8)i)); - - for (i = 0; i < 256; i++) { - dsp_audio_alaw_to_ulaw[i] = - linear2ulaw(dsp_audio_alaw_to_s32[i]); - dsp_audio_ulaw_to_alaw[i] = - linear2alaw(dsp_audio_ulaw_to_s32[i]); - } -} - -void -dsp_audio_generate_s2law_table(void) -{ - int i; - - if (dsp_options & DSP_OPT_ULAW) { - /* generating ulaw-table */ - for (i = -32768; i < 32768; i++) { - dsp_audio_s16_to_law[i & 0xffff] = - bitrev8(linear2ulaw(i)); - } - } else { - /* generating alaw-table */ - for (i = -32768; i < 32768; i++) { - dsp_audio_s16_to_law[i & 0xffff] = - bitrev8(linear2alaw(i)); - } - } -} - - -/* - * the seven bit sample is the number of every second alaw-sample ordered by - * aplitude. 0x00 is negative, 0x7f is positive amplitude. - */ -u8 dsp_audio_seven2law[128]; -u8 dsp_audio_law2seven[256]; - -/******************************************************************** - * generate table for conversion law from/to 7-bit alaw-like sample * - ********************************************************************/ - -void -dsp_audio_generate_seven(void) -{ - int i, j, k; - u8 spl; - u8 sorted_alaw[256]; - - /* generate alaw table, sorted by the linear value */ - for (i = 0; i < 256; i++) { - j = 0; - for (k = 0; k < 256; k++) { - if (dsp_audio_alaw_to_s32[k] - < dsp_audio_alaw_to_s32[i]) - j++; - } - sorted_alaw[j] = i; - } - - /* generate tabels */ - for (i = 0; i < 256; i++) { - /* spl is the source: the law-sample (converted to alaw) */ - spl = i; - if (dsp_options & DSP_OPT_ULAW) - spl = dsp_audio_ulaw_to_alaw[i]; - /* find the 7-bit-sample */ - for (j = 0; j < 256; j++) { - if (sorted_alaw[j] == spl) - break; - } - /* write 7-bit audio value */ - dsp_audio_law2seven[i] = j >> 1; - } - for (i = 0; i < 128; i++) { - spl = sorted_alaw[i << 1]; - if (dsp_options & DSP_OPT_ULAW) - spl = dsp_audio_alaw_to_ulaw[spl]; - dsp_audio_seven2law[i] = spl; - } -} - - -/* mix 2*law -> law */ -u8 dsp_audio_mix_law[65536]; - -/****************************************************** - * generate mix table to mix two law samples into one * - ******************************************************/ - -void -dsp_audio_generate_mix_table(void) -{ - int i, j; - s32 sample; - - i = 0; - while (i < 256) { - j = 0; - while (j < 256) { - sample = dsp_audio_law_to_s32[i]; - sample += dsp_audio_law_to_s32[j]; - if (sample > 32767) - sample = 32767; - if (sample < -32768) - sample = -32768; - dsp_audio_mix_law[(i << 8) | j] = - dsp_audio_s16_to_law[sample & 0xffff]; - j++; - } - i++; - } -} - - -/************************************* - * generate different volume changes * - *************************************/ - -static u8 dsp_audio_reduce8[256]; -static u8 dsp_audio_reduce7[256]; -static u8 dsp_audio_reduce6[256]; -static u8 dsp_audio_reduce5[256]; -static u8 dsp_audio_reduce4[256]; -static u8 dsp_audio_reduce3[256]; -static u8 dsp_audio_reduce2[256]; -static u8 dsp_audio_reduce1[256]; -static u8 dsp_audio_increase1[256]; -static u8 dsp_audio_increase2[256]; -static u8 dsp_audio_increase3[256]; -static u8 dsp_audio_increase4[256]; -static u8 dsp_audio_increase5[256]; -static u8 dsp_audio_increase6[256]; -static u8 dsp_audio_increase7[256]; -static u8 dsp_audio_increase8[256]; - -static u8 *dsp_audio_volume_change[16] = { - dsp_audio_reduce8, - dsp_audio_reduce7, - dsp_audio_reduce6, - dsp_audio_reduce5, - dsp_audio_reduce4, - dsp_audio_reduce3, - dsp_audio_reduce2, - dsp_audio_reduce1, - dsp_audio_increase1, - dsp_audio_increase2, - dsp_audio_increase3, - dsp_audio_increase4, - dsp_audio_increase5, - dsp_audio_increase6, - dsp_audio_increase7, - dsp_audio_increase8, -}; - -void -dsp_audio_generate_volume_changes(void) -{ - register s32 sample; - int i; - int num[] = { 110, 125, 150, 175, 200, 300, 400, 500 }; - int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 }; - - i = 0; - while (i < 256) { - dsp_audio_reduce8[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[7] / num[7]) & 0xffff]; - dsp_audio_reduce7[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff]; - dsp_audio_reduce6[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff]; - dsp_audio_reduce5[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff]; - dsp_audio_reduce4[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff]; - dsp_audio_reduce3[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff]; - dsp_audio_reduce2[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff]; - dsp_audio_reduce1[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[0] / denum[0]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[1] / denum[1]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[2] / denum[2]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[3] / denum[3]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[4] / denum[4]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[5] / denum[5]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[6] / denum[6]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[7] / denum[7]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff]; - - i++; - } -} - - -/************************************** - * change the volume of the given skb * - **************************************/ - -/* this is a helper function for changing volume of skb. the range may be - * -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8 - */ -void -dsp_change_volume(struct sk_buff *skb, int volume) -{ - u8 *volume_change; - int i, ii; - u8 *p; - int shift; - - if (volume == 0) - return; - - /* get correct conversion table */ - if (volume < 0) { - shift = volume + 8; - if (shift < 0) - shift = 0; - } else { - shift = volume + 7; - if (shift > 15) - shift = 15; - } - volume_change = dsp_audio_volume_change[shift]; - i = 0; - ii = skb->len; - p = skb->data; - /* change volume */ - while (i < ii) { - *p = volume_change[*p]; - p++; - i++; - } -} diff --git a/drivers/isdn/mISDN/dsp_biquad.h b/drivers/isdn/mISDN/dsp_biquad.h deleted file mode 100644 index f40d52a4c4ee..000000000000 --- a/drivers/isdn/mISDN/dsp_biquad.h +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * SpanDSP - a series of DSP components for telephony - * - * biquad.h - General telephony bi-quad section routines (currently this just - * handles canonic/type 2 form) - * - * Written by Steve Underwood - * - * Copyright (C) 2001 Steve Underwood - * - * All rights reserved. - */ - -struct biquad2_state { - int32_t gain; - int32_t a1; - int32_t a2; - int32_t b1; - int32_t b2; - - int32_t z1; - int32_t z2; -}; - -static inline void biquad2_init(struct biquad2_state *bq, - int32_t gain, int32_t a1, int32_t a2, int32_t b1, int32_t b2) -{ - bq->gain = gain; - bq->a1 = a1; - bq->a2 = a2; - bq->b1 = b1; - bq->b2 = b2; - - bq->z1 = 0; - bq->z2 = 0; -} - -static inline int16_t biquad2(struct biquad2_state *bq, int16_t sample) -{ - int32_t y; - int32_t z0; - - z0 = sample * bq->gain + bq->z1 * bq->a1 + bq->z2 * bq->a2; - y = z0 + bq->z1 * bq->b1 + bq->z2 * bq->b2; - - bq->z2 = bq->z1; - bq->z1 = z0 >> 15; - y >>= 15; - return y; -} diff --git a/drivers/isdn/mISDN/dsp_blowfish.c b/drivers/isdn/mISDN/dsp_blowfish.c deleted file mode 100644 index 0e77c282c862..000000000000 --- a/drivers/isdn/mISDN/dsp_blowfish.c +++ /dev/null @@ -1,667 +0,0 @@ -/* - * Blowfish encryption/decryption for mISDN_dsp. - * - * Copyright Andreas Eversberg (jolly@eversberg.eu) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include "core.h" -#include "dsp.h" - -/* - * how to encode a sample stream to 64-bit blocks that will be encryped - * - * first of all, data is collected until a block of 9 samples are received. - * of course, a packet may have much more than 9 sample, but is may have - * not excacly the multiple of 9 samples. if there is a rest, the next - * received data will complete the block. - * - * the block is then converted to 9 uLAW samples without the least sigificant - * bit. the result is a 7-bit encoded sample. - * - * the samples will be reoganised to form 8 bytes of data: - * (5(6) means: encoded sample no. 5, bit 6) - * - * 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6) - * 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5) - * 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4) - * 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3) - * 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2) - * 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) - * 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) - * 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0) - * - * the missing bit 0 of the last byte is filled with some - * random noise, to fill all 8 bytes. - * - * the 8 bytes will be encrypted using blowfish. - * - * the result will be converted into 9 bytes. the bit 7 is used for - * checksumme (CS) for sync (0, 1) and for the last bit: - * (5(6) means: crypted byte 5, bit 6) - * - * 1 0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) - * 0 0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2) - * 0 1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3) - * 0 2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4) - * 0 3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5) - * CS 4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6) - * CS 5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7) - * CS 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0) - * 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) - * - * the checksum is used to detect transmission errors and frame drops. - * - * synchronisation of received block is done by shifting the upper bit of each - * byte (bit 7) to a shift register. if the rigister has the first five bits - * (10000), this is used to find the sync. only if sync has been found, the - * current block of 9 received bytes are decrypted. before that the check - * sum is calculated. if it is incorrect the block is dropped. - * this will avoid loud noise due to corrupt encrypted data. - * - * if the last block is corrupt, the current decoded block is repeated - * until a valid block has been received. - */ - -/* - * some blowfish parts are taken from the - * crypto-api for faster implementation - */ - -static const u32 bf_pbox[16 + 2] = { - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, - 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, - 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, - 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, - 0x9216d5d9, 0x8979fb1b, -}; - -static const u32 bf_sbox[256 * 4] = { - 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, - 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, - 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, - 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, - 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, - 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, - 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, - 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, - 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, - 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, - 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, - 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, - 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, - 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, - 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, - 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, - 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, - 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, - 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, - 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, - 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, - 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, - 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, - 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, - 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, - 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, - 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, - 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, - 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, - 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, - 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, - 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, - 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, - 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, - 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, - 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, - 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, - 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, - 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, - 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, - 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, - 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, - 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, - 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, - 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, - 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, - 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, - 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, - 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, - 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, - 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, - 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, - 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, - 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, - 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, - 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, - 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, - 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, - 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, - 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, - 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, - 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, - 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, - 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, - 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, - 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, - 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, - 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, - 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, - 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, - 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, - 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, - 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, - 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, - 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, - 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, - 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, - 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, - 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, - 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, - 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, - 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, - 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, - 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, - 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, - 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, - 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, - 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, - 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, - 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, - 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, - 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, - 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, - 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, - 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, - 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, - 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, - 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, - 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, - 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, - 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, - 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, - 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, - 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, - 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, - 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, - 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, - 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, - 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, - 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, - 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, - 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, - 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, - 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, - 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, - 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, - 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, - 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, - 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, - 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, - 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, - 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, - 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, - 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, - 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, - 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, - 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, - 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, - 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, - 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, - 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, - 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, - 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, - 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, - 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, - 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, - 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, - 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, - 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, - 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, - 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, - 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, - 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, - 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, - 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, - 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, - 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, - 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, - 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, - 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, - 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, - 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, - 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, - 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, - 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, - 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, - 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, - 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, - 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, - 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, - 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, - 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, - 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, - 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, - 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, - 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, - 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, - 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, - 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, - 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, - 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, - 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, - 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, - 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, - 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, - 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, - 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, - 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, - 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, - 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, - 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, - 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, - 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, - 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, - 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, - 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, - 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, - 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, - 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, - 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, - 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, - 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, - 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, - 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, - 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, - 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, - 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, - 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, - 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, - 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, - 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, - 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, - 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, - 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, - 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, - 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, - 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, - 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, - 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, - 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, - 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, - 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, - 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, - 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, - 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, - 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, - 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, - 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, - 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, - 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, - 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, - 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, - 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, - 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, - 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, - 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, - 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, - 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, - 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, - 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, - 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, - 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, - 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, - 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, - 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, - 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, - 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, - 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, - 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, - 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, - 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, - 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, - 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, - 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, - 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, - 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, - 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, - 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, - 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, - 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, - 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, - 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, - 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, - 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, - 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, - 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, -}; - -/* - * Round loop unrolling macros, S is a pointer to a S-Box array - * organized in 4 unsigned longs at a row. - */ -#define GET32_3(x) (((x) & 0xff)) -#define GET32_2(x) (((x) >> (8)) & (0xff)) -#define GET32_1(x) (((x) >> (16)) & (0xff)) -#define GET32_0(x) (((x) >> (24)) & (0xff)) - -#define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \ - S[512 + GET32_2(x)]) + S[768 + GET32_3(x)]) - -#define EROUND(a, b, n) do { b ^= P[n]; a ^= bf_F(b); } while (0) -#define DROUND(a, b, n) do { a ^= bf_F(b); b ^= P[n]; } while (0) - - -/* - * encrypt isdn data frame - * every block with 9 samples is encrypted - */ -void -dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len) -{ - int i = 0, j = dsp->bf_crypt_pos; - u8 *bf_data_in = dsp->bf_data_in; - u8 *bf_crypt_out = dsp->bf_crypt_out; - u32 *P = dsp->bf_p; - u32 *S = dsp->bf_s; - u32 yl, yr; - u32 cs; - u8 nibble; - - while (i < len) { - /* collect a block of 9 samples */ - if (j < 9) { - bf_data_in[j] = *data; - *data++ = bf_crypt_out[j++]; - i++; - continue; - } - j = 0; - /* transcode 9 samples xlaw to 8 bytes */ - yl = dsp_audio_law2seven[bf_data_in[0]]; - yl = (yl << 7) | dsp_audio_law2seven[bf_data_in[1]]; - yl = (yl << 7) | dsp_audio_law2seven[bf_data_in[2]]; - yl = (yl << 7) | dsp_audio_law2seven[bf_data_in[3]]; - nibble = dsp_audio_law2seven[bf_data_in[4]]; - yr = nibble; - yl = (yl << 4) | (nibble >> 3); - yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[5]]; - yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[6]]; - yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[7]]; - yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[8]]; - yr = (yr << 1) | (bf_data_in[0] & 1); - - /* fill unused bit with random noise of audio input */ - /* encrypt */ - - EROUND(yr, yl, 0); - EROUND(yl, yr, 1); - EROUND(yr, yl, 2); - EROUND(yl, yr, 3); - EROUND(yr, yl, 4); - EROUND(yl, yr, 5); - EROUND(yr, yl, 6); - EROUND(yl, yr, 7); - EROUND(yr, yl, 8); - EROUND(yl, yr, 9); - EROUND(yr, yl, 10); - EROUND(yl, yr, 11); - EROUND(yr, yl, 12); - EROUND(yl, yr, 13); - EROUND(yr, yl, 14); - EROUND(yl, yr, 15); - yl ^= P[16]; - yr ^= P[17]; - - /* calculate 3-bit checksumme */ - cs = yl ^ (yl >> 3) ^ (yl >> 6) ^ (yl >> 9) ^ (yl >> 12) ^ (yl >> 15) - ^ (yl >> 18) ^ (yl >> 21) ^ (yl >> 24) ^ (yl >> 27) ^ (yl >> 30) - ^ (yr << 2) ^ (yr >> 1) ^ (yr >> 4) ^ (yr >> 7) ^ (yr >> 10) - ^ (yr >> 13) ^ (yr >> 16) ^ (yr >> 19) ^ (yr >> 22) ^ (yr >> 25) - ^ (yr >> 28) ^ (yr >> 31); - - /* - * transcode 8 crypted bytes to 9 data bytes with sync - * and checksum information - */ - bf_crypt_out[0] = (yl >> 25) | 0x80; - bf_crypt_out[1] = (yl >> 18) & 0x7f; - bf_crypt_out[2] = (yl >> 11) & 0x7f; - bf_crypt_out[3] = (yl >> 4) & 0x7f; - bf_crypt_out[4] = ((yl << 3) & 0x78) | ((yr >> 29) & 0x07); - bf_crypt_out[5] = ((yr >> 22) & 0x7f) | ((cs << 5) & 0x80); - bf_crypt_out[6] = ((yr >> 15) & 0x7f) | ((cs << 6) & 0x80); - bf_crypt_out[7] = ((yr >> 8) & 0x7f) | (cs << 7); - bf_crypt_out[8] = yr; - } - - /* write current count */ - dsp->bf_crypt_pos = j; - -} - - -/* - * decrypt isdn data frame - * every block with 9 bytes is decrypted - */ -void -dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len) -{ - int i = 0; - u8 j = dsp->bf_decrypt_in_pos; - u8 k = dsp->bf_decrypt_out_pos; - u8 *bf_crypt_inring = dsp->bf_crypt_inring; - u8 *bf_data_out = dsp->bf_data_out; - u16 sync = dsp->bf_sync; - u32 *P = dsp->bf_p; - u32 *S = dsp->bf_s; - u32 yl, yr; - u8 nibble; - u8 cs, cs0, cs1, cs2; - - while (i < len) { - /* - * shift upper bit and rotate data to buffer ring - * send current decrypted data - */ - sync = (sync << 1) | ((*data) >> 7); - bf_crypt_inring[j++ & 15] = *data; - *data++ = bf_data_out[k++]; - i++; - if (k == 9) - k = 0; /* repeat if no sync has been found */ - /* check if not in sync */ - if ((sync & 0x1f0) != 0x100) - continue; - j -= 9; - /* transcode receive data to 64 bit block of encrypted data */ - yl = bf_crypt_inring[j++ & 15]; - yl = (yl << 7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ - yl = (yl << 7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ - yl = (yl << 7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ - nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ - yr = nibble; - yl = (yl << 4) | (nibble >> 3); - cs2 = bf_crypt_inring[j++ & 15]; - yr = (yr << 7) | (cs2 & 0x7f); - cs1 = bf_crypt_inring[j++ & 15]; - yr = (yr << 7) | (cs1 & 0x7f); - cs0 = bf_crypt_inring[j++ & 15]; - yr = (yr << 7) | (cs0 & 0x7f); - yr = (yr << 8) | bf_crypt_inring[j++ & 15]; - - /* calculate 3-bit checksumme */ - cs = yl ^ (yl >> 3) ^ (yl >> 6) ^ (yl >> 9) ^ (yl >> 12) ^ (yl >> 15) - ^ (yl >> 18) ^ (yl >> 21) ^ (yl >> 24) ^ (yl >> 27) ^ (yl >> 30) - ^ (yr << 2) ^ (yr >> 1) ^ (yr >> 4) ^ (yr >> 7) ^ (yr >> 10) - ^ (yr >> 13) ^ (yr >> 16) ^ (yr >> 19) ^ (yr >> 22) ^ (yr >> 25) - ^ (yr >> 28) ^ (yr >> 31); - - /* check if frame is valid */ - if ((cs & 0x7) != (((cs2 >> 5) & 4) | ((cs1 >> 6) & 2) | (cs0 >> 7))) { - if (dsp_debug & DEBUG_DSP_BLOWFISH) - printk(KERN_DEBUG - "DSP BLOWFISH: received corrupt frame, " - "checksumme is not correct\n"); - continue; - } - - /* decrypt */ - yr ^= P[17]; - yl ^= P[16]; - DROUND(yl, yr, 15); - DROUND(yr, yl, 14); - DROUND(yl, yr, 13); - DROUND(yr, yl, 12); - DROUND(yl, yr, 11); - DROUND(yr, yl, 10); - DROUND(yl, yr, 9); - DROUND(yr, yl, 8); - DROUND(yl, yr, 7); - DROUND(yr, yl, 6); - DROUND(yl, yr, 5); - DROUND(yr, yl, 4); - DROUND(yl, yr, 3); - DROUND(yr, yl, 2); - DROUND(yl, yr, 1); - DROUND(yr, yl, 0); - - /* transcode 8 crypted bytes to 9 sample bytes */ - bf_data_out[0] = dsp_audio_seven2law[(yl >> 25) & 0x7f]; - bf_data_out[1] = dsp_audio_seven2law[(yl >> 18) & 0x7f]; - bf_data_out[2] = dsp_audio_seven2law[(yl >> 11) & 0x7f]; - bf_data_out[3] = dsp_audio_seven2law[(yl >> 4) & 0x7f]; - bf_data_out[4] = dsp_audio_seven2law[((yl << 3) & 0x78) | - ((yr >> 29) & 0x07)]; - - bf_data_out[5] = dsp_audio_seven2law[(yr >> 22) & 0x7f]; - bf_data_out[6] = dsp_audio_seven2law[(yr >> 15) & 0x7f]; - bf_data_out[7] = dsp_audio_seven2law[(yr >> 8) & 0x7f]; - bf_data_out[8] = dsp_audio_seven2law[(yr >> 1) & 0x7f]; - k = 0; /* start with new decoded frame */ - } - - /* write current count and sync */ - dsp->bf_decrypt_in_pos = j; - dsp->bf_decrypt_out_pos = k; - dsp->bf_sync = sync; -} - - -/* used to encrypt S and P boxes */ -static inline void -encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src) -{ - u32 yl = src[0]; - u32 yr = src[1]; - - EROUND(yr, yl, 0); - EROUND(yl, yr, 1); - EROUND(yr, yl, 2); - EROUND(yl, yr, 3); - EROUND(yr, yl, 4); - EROUND(yl, yr, 5); - EROUND(yr, yl, 6); - EROUND(yl, yr, 7); - EROUND(yr, yl, 8); - EROUND(yl, yr, 9); - EROUND(yr, yl, 10); - EROUND(yl, yr, 11); - EROUND(yr, yl, 12); - EROUND(yl, yr, 13); - EROUND(yr, yl, 14); - EROUND(yl, yr, 15); - - yl ^= P[16]; - yr ^= P[17]; - - dst[0] = yr; - dst[1] = yl; -} - -/* - * initialize the dsp for encryption and decryption using the same key - * Calculates the blowfish S and P boxes for encryption and decryption. - * The margin of keylen must be 4-56 bytes. - * returns 0 if ok. - */ -int -dsp_bf_init(struct dsp *dsp, const u8 *key, uint keylen) -{ - short i, j, count; - u32 data[2], temp; - u32 *P = (u32 *)dsp->bf_p; - u32 *S = (u32 *)dsp->bf_s; - - if (keylen < 4 || keylen > 56) - return 1; - - /* Set dsp states */ - i = 0; - while (i < 9) { - dsp->bf_crypt_out[i] = 0xff; - dsp->bf_data_out[i] = dsp_silence; - i++; - } - dsp->bf_crypt_pos = 0; - dsp->bf_decrypt_in_pos = 0; - dsp->bf_decrypt_out_pos = 0; - dsp->bf_sync = 0x1ff; - dsp->bf_enable = 1; - - /* Copy the initialization s-boxes */ - for (i = 0, count = 0; i < 256; i++) - for (j = 0; j < 4; j++, count++) - S[count] = bf_sbox[count]; - - /* Set the p-boxes */ - for (i = 0; i < 16 + 2; i++) - P[i] = bf_pbox[i]; - - /* Actual subkey generation */ - for (j = 0, i = 0; i < 16 + 2; i++) { - temp = (((u32)key[j] << 24) | - ((u32)key[(j + 1) % keylen] << 16) | - ((u32)key[(j + 2) % keylen] << 8) | - ((u32)key[(j + 3) % keylen])); - - P[i] = P[i] ^ temp; - j = (j + 4) % keylen; - } - - data[0] = 0x00000000; - data[1] = 0x00000000; - - for (i = 0; i < 16 + 2; i += 2) { - encrypt_block(P, S, data, data); - - P[i] = data[0]; - P[i + 1] = data[1]; - } - - for (i = 0; i < 4; i++) { - for (j = 0, count = i * 256; j < 256; j += 2, count += 2) { - encrypt_block(P, S, data, data); - - S[count] = data[0]; - S[count + 1] = data[1]; - } - } - - return 0; -} - - -/* - * turn encryption off - */ -void -dsp_bf_cleanup(struct dsp *dsp) -{ - dsp->bf_enable = 0; -} diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c deleted file mode 100644 index d5eb2349c414..000000000000 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ /dev/null @@ -1,1949 +0,0 @@ -/* - * Audio crossconnecting/conferrencing (hardware level). - * - * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -/* - * The process of adding and removing parties to/from a conference: - * - * There is a chain of struct dsp_conf which has one or more members in a chain - * of struct dsp_conf_member. - * - * After a party is added, the conference is checked for hardware capability. - * Also if a party is removed, the conference is checked again. - * - * There are 3 different solutions: -1 = software, 0 = hardware-crossconnect - * 1-n = hardware-conference. The n will give the conference number. - * - * Depending on the change after removal or insertion of a party, hardware - * commands are given. - * - * The current solution is stored within the struct dsp_conf entry. - */ - -/* - * HOW THE CMX WORKS: - * - * There are 3 types of interaction: One member is alone, in this case only - * data flow from upper to lower layer is done. - * Two members will also exchange their data so they are crossconnected. - * Three or more members will be added in a conference and will hear each - * other but will not receive their own speech (echo) if not enabled. - * - * Features of CMX are: - * - Crossconnecting or even conference, if more than two members are together. - * - Force mixing of transmit data with other crossconnect/conference members. - * - Echo generation to benchmark the delay of audio processing. - * - Use hardware to minimize cpu load, disable FIFO load and minimize delay. - * - Dejittering and clock generation. - * - * There are 2 buffers: - * - * - * RX-Buffer - * R W - * | | - * ----------------+-------------+------------------- - * - * The rx-buffer is a ring buffer used to store the received data for each - * individual member. This is only the case if data needs to be dejittered - * or in case of a conference where different clocks require reclocking. - * The transmit-clock (R) will read the buffer. - * If the clock overruns the write-pointer, we will have a buffer underrun. - * If the write pointer always has a certain distance from the transmit- - * clock, we will have a delay. The delay will dynamically be increased and - * reduced. - * - * - * TX-Buffer - * R W - * | | - * -----------------+--------+----------------------- - * - * The tx-buffer is a ring buffer to queue the transmit data from user space - * until it will be mixed or sent. There are two pointers, R and W. If the write - * pointer W would reach or overrun R, the buffer would overrun. In this case - * (some) data is dropped so that it will not overrun. - * Additionally a dynamic dejittering can be enabled. this allows data from - * user space that have jitter and different clock source. - * - * - * Clock: - * - * A Clock is not required, if the data source has exactly one clock. In this - * case the data source is forwarded to the destination. - * - * A Clock is required, because the data source - * - has multiple clocks. - * - has no usable clock due to jitter or packet loss (VoIP). - * In this case the system's clock is used. The clock resolution depends on - * the jiffy resolution. - * - * If a member joins a conference: - * - * - If a member joins, its rx_buff is set to silence and change read pointer - * to transmit clock. - * - * The procedure of received data from card is explained in cmx_receive. - * The procedure of received data from user space is explained in cmx_transmit. - * The procedure of transmit data to card is cmx_send. - * - * - * Interaction with other features: - * - * DTMF: - * DTMF decoding is done before the data is crossconnected. - * - * Volume change: - * Changing rx-volume is done before the data is crossconnected. The tx-volume - * must be changed whenever data is transmitted to the card by the cmx. - * - * Tones: - * If a tone is enabled, it will be processed whenever data is transmitted to - * the card. It will replace the tx-data from the user space. - * If tones are generated by hardware, this conference member is removed for - * this time. - * - * Disable rx-data: - * If cmx is realized in hardware, rx data will be disabled if requested by - * the upper layer. If dtmf decoding is done by software and enabled, rx data - * will not be disabled but blocked to the upper layer. - * - * HFC conference engine: - * If it is possible to realize all features using hardware, hardware will be - * used if not forbidden by control command. Disabling rx-data provides - * absolutely traffic free audio processing. (except for the quick 1-frame - * upload of a tone loop, only once for a new tone) - * - */ - -/* delay.h is required for hw_lock.h */ - -#include -#include -#include -#include -#include "core.h" -#include "dsp.h" -/* - * debugging of multi party conference, - * by using conference even with two members - */ - -/* #define CMX_CONF_DEBUG */ - -/*#define CMX_DEBUG * massive read/write pointer output */ -/*#define CMX_DELAY_DEBUG * gives rx-buffer delay overview */ -/*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */ - -/* - * debug cmx memory structure - */ -void -dsp_cmx_debug(struct dsp *dsp) -{ - struct dsp_conf *conf; - struct dsp_conf_member *member; - struct dsp *odsp; - - printk(KERN_DEBUG "-----Current DSP\n"); - list_for_each_entry(odsp, &dsp_ilist, list) { - printk(KERN_DEBUG "* %s hardecho=%d softecho=%d txmix=%d", - odsp->name, odsp->echo.hardware, odsp->echo.software, - odsp->tx_mix); - if (odsp->conf) - printk(" (Conf %d)", odsp->conf->id); - if (dsp == odsp) - printk(" *this*"); - printk("\n"); - } - printk(KERN_DEBUG "-----Current Conf:\n"); - list_for_each_entry(conf, &conf_ilist, list) { - printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf); - list_for_each_entry(member, &conf->mlist, list) { - printk(KERN_DEBUG - " - member = %s (slot_tx %d, bank_tx %d, " - "slot_rx %d, bank_rx %d hfc_conf %d " - "tx_data %d rx_is_off %d)%s\n", - member->dsp->name, member->dsp->pcm_slot_tx, - member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, - member->dsp->pcm_bank_rx, member->dsp->hfc_conf, - member->dsp->tx_data, member->dsp->rx_is_off, - (member->dsp == dsp) ? " *this*" : ""); - } - } - printk(KERN_DEBUG "-----end\n"); -} - -/* - * search conference - */ -static struct dsp_conf * -dsp_cmx_search_conf(u32 id) -{ - struct dsp_conf *conf; - - if (!id) { - printk(KERN_WARNING "%s: conference ID is 0.\n", __func__); - return NULL; - } - - /* search conference */ - list_for_each_entry(conf, &conf_ilist, list) - if (conf->id == id) - return conf; - - return NULL; -} - - -/* - * add member to conference - */ -static int -dsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf) -{ - struct dsp_conf_member *member; - - if (!conf || !dsp) { - printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__); - return -EINVAL; - } - if (dsp->member) { - printk(KERN_WARNING "%s: dsp is already member in a conf.\n", - __func__); - return -EINVAL; - } - - if (dsp->conf) { - printk(KERN_WARNING "%s: dsp is already in a conf.\n", - __func__); - return -EINVAL; - } - - member = kzalloc_obj(struct dsp_conf_member, GFP_ATOMIC); - if (!member) { - printk(KERN_ERR "kzalloc struct dsp_conf_member failed\n"); - return -ENOMEM; - } - member->dsp = dsp; - /* clear rx buffer */ - memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); - dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */ - dsp->rx_W = 0; - dsp->rx_R = 0; - - list_add_tail(&member->list, &conf->mlist); - - dsp->conf = conf; - dsp->member = member; - - return 0; -} - - -/* - * del member from conference - */ -int -dsp_cmx_del_conf_member(struct dsp *dsp) -{ - struct dsp_conf_member *member; - - if (!dsp) { - printk(KERN_WARNING "%s: dsp is 0.\n", - __func__); - return -EINVAL; - } - - if (!dsp->conf) { - printk(KERN_WARNING "%s: dsp is not in a conf.\n", - __func__); - return -EINVAL; - } - - if (list_empty(&dsp->conf->mlist)) { - printk(KERN_WARNING "%s: dsp has linked an empty conf.\n", - __func__); - return -EINVAL; - } - - /* find us in conf */ - list_for_each_entry(member, &dsp->conf->mlist, list) { - if (member->dsp == dsp) { - list_del(&member->list); - dsp->conf = NULL; - dsp->member = NULL; - kfree(member); - return 0; - } - } - printk(KERN_WARNING - "%s: dsp is not present in its own conf_member list.\n", - __func__); - - return -EINVAL; -} - - -/* - * new conference - */ -static struct dsp_conf -*dsp_cmx_new_conf(u32 id) -{ - struct dsp_conf *conf; - - if (!id) { - printk(KERN_WARNING "%s: id is 0.\n", - __func__); - return NULL; - } - - conf = kzalloc_obj(struct dsp_conf, GFP_ATOMIC); - if (!conf) { - printk(KERN_ERR "kzalloc struct dsp_conf failed\n"); - return NULL; - } - INIT_LIST_HEAD(&conf->mlist); - conf->id = id; - - list_add_tail(&conf->list, &conf_ilist); - - return conf; -} - - -/* - * del conference - */ -int -dsp_cmx_del_conf(struct dsp_conf *conf) -{ - if (!conf) { - printk(KERN_WARNING "%s: conf is null.\n", - __func__); - return -EINVAL; - } - - if (!list_empty(&conf->mlist)) { - printk(KERN_WARNING "%s: conf not empty.\n", - __func__); - return -EINVAL; - } - list_del(&conf->list); - kfree(conf); - - return 0; -} - - -/* - * send HW message to hfc card - */ -static void -dsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2, - u32 param3, u32 param4) -{ - struct mISDN_ctrl_req cq; - - memset(&cq, 0, sizeof(cq)); - cq.op = message; - cq.p1 = param1 | (param2 << 8); - cq.p2 = param3 | (param4 << 8); - if (dsp->ch.peer) - dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq); -} - - -/* - * do hardware update and set the software/hardware flag - * - * either a conference or a dsp instance can be given - * if only dsp instance is given, the instance is not associated with a conf - * and therefore removed. if a conference is given, the dsp is expected to - * be member of that conference. - */ -void -dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp) -{ - struct dsp_conf_member *member, *nextm; - struct dsp *finddsp; - int memb = 0, i, ii, i1, i2; - int freeunits[8]; - u_char freeslots[256]; - int same_hfc = -1, same_pcm = -1, current_conf = -1, - all_conf = 1, tx_data = 0; - - /* dsp gets updated (no conf) */ - if (!conf) { - if (!dsp) - return; - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "%s checking dsp %s\n", - __func__, dsp->name); - one_member: - /* remove HFC conference if enabled */ - if (dsp->hfc_conf >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s removing %s from HFC conf %d " - "because dsp is split\n", __func__, - dsp->name, dsp->hfc_conf); - dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT, - 0, 0, 0, 0); - dsp->hfc_conf = -1; - } - /* process hw echo */ - if (dsp->features.pcm_banks < 1) - return; - if (!dsp->echo.software && !dsp->echo.hardware) { - /* NO ECHO: remove PCM slot if assigned */ - if (dsp->pcm_slot_tx >= 0 || dsp->pcm_slot_rx >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "%s removing %s from" - " PCM slot %d (TX) %d (RX) because" - " dsp is split (no echo)\n", - __func__, dsp->name, - dsp->pcm_slot_tx, dsp->pcm_slot_rx); - dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_DISC, - 0, 0, 0, 0); - dsp->pcm_slot_tx = -1; - dsp->pcm_bank_tx = -1; - dsp->pcm_slot_rx = -1; - dsp->pcm_bank_rx = -1; - } - return; - } - /* echo is enabled, find out if we use soft or hardware */ - dsp->echo.software = dsp->tx_data; - dsp->echo.hardware = 0; - /* ECHO: already echo */ - if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_rx < 0 && - dsp->pcm_bank_tx == 2 && dsp->pcm_bank_rx == 2) { - dsp->echo.hardware = 1; - return; - } - /* ECHO: if slot already assigned */ - if (dsp->pcm_slot_tx >= 0) { - dsp->pcm_slot_rx = dsp->pcm_slot_tx; - dsp->pcm_bank_tx = 2; /* 2 means loop */ - dsp->pcm_bank_rx = 2; - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s refresh %s for echo using slot %d\n", - __func__, dsp->name, - dsp->pcm_slot_tx); - dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, - dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); - dsp->echo.hardware = 1; - return; - } - /* ECHO: find slot */ - dsp->pcm_slot_tx = -1; - dsp->pcm_slot_rx = -1; - memset(freeslots, 1, sizeof(freeslots)); - list_for_each_entry(finddsp, &dsp_ilist, list) { - if (finddsp->features.pcm_id == dsp->features.pcm_id) { - if (finddsp->pcm_slot_rx >= 0 && - finddsp->pcm_slot_rx < sizeof(freeslots)) - freeslots[finddsp->pcm_slot_rx] = 0; - if (finddsp->pcm_slot_tx >= 0 && - finddsp->pcm_slot_tx < sizeof(freeslots)) - freeslots[finddsp->pcm_slot_tx] = 0; - } - } - i = 0; - ii = dsp->features.pcm_slots; - while (i < ii) { - if (freeslots[i]) - break; - i++; - } - if (i == ii) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s no slot available for echo\n", - __func__); - /* no more slots available */ - dsp->echo.software = 1; - return; - } - /* assign free slot */ - dsp->pcm_slot_tx = i; - dsp->pcm_slot_rx = i; - dsp->pcm_bank_tx = 2; /* loop */ - dsp->pcm_bank_rx = 2; - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s assign echo for %s using slot %d\n", - __func__, dsp->name, dsp->pcm_slot_tx); - dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, - dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); - dsp->echo.hardware = 1; - return; - } - - /* conf gets updated (all members) */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "%s checking conference %d\n", - __func__, conf->id); - - if (list_empty(&conf->mlist)) { - printk(KERN_ERR "%s: conference without members\n", - __func__); - return; - } - member = list_entry(conf->mlist.next, struct dsp_conf_member, list); - same_hfc = member->dsp->features.hfc_id; - same_pcm = member->dsp->features.pcm_id; - /* check all members in our conference */ - list_for_each_entry(member, &conf->mlist, list) { - /* check if member uses mixing */ - if (member->dsp->tx_mix) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "tx_mix is turned on\n", __func__, - member->dsp->name); - conf_software: - list_for_each_entry(member, &conf->mlist, list) { - dsp = member->dsp; - /* remove HFC conference if enabled */ - if (dsp->hfc_conf >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s removing %s from HFC " - "conf %d because not " - "possible with hardware\n", - __func__, - dsp->name, - dsp->hfc_conf); - dsp_cmx_hw_message(dsp, - MISDN_CTRL_HFC_CONF_SPLIT, - 0, 0, 0, 0); - dsp->hfc_conf = -1; - } - /* remove PCM slot if assigned */ - if (dsp->pcm_slot_tx >= 0 || - dsp->pcm_slot_rx >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "%s removing " - "%s from PCM slot %d (TX)" - " slot %d (RX) because not" - " possible with hardware\n", - __func__, - dsp->name, - dsp->pcm_slot_tx, - dsp->pcm_slot_rx); - dsp_cmx_hw_message(dsp, - MISDN_CTRL_HFC_PCM_DISC, - 0, 0, 0, 0); - dsp->pcm_slot_tx = -1; - dsp->pcm_bank_tx = -1; - dsp->pcm_slot_rx = -1; - dsp->pcm_bank_rx = -1; - } - } - conf->hardware = 0; - conf->software = 1; - return; - } - /* check if member has echo turned on */ - if (member->dsp->echo.hardware || member->dsp->echo.software) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "echo is turned on\n", __func__, - member->dsp->name); - goto conf_software; - } - /* check if member has tx_mix turned on */ - if (member->dsp->tx_mix) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "tx_mix is turned on\n", - __func__, member->dsp->name); - goto conf_software; - } - /* check if member changes volume at an not suppoted level */ - if (member->dsp->tx_volume) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "tx_volume is changed\n", - __func__, member->dsp->name); - goto conf_software; - } - if (member->dsp->rx_volume) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "rx_volume is changed\n", - __func__, member->dsp->name); - goto conf_software; - } - /* check if tx-data turned on */ - if (member->dsp->tx_data) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s tx_data is turned on\n", - __func__, member->dsp->name); - tx_data = 1; - } - /* check if pipeline exists */ - if (member->dsp->pipeline.inuse) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "pipeline exists\n", __func__, - member->dsp->name); - goto conf_software; - } - /* check if encryption is enabled */ - if (member->dsp->bf_enable) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "%s dsp %s cannot form a " - "conf, because encryption is enabled\n", - __func__, member->dsp->name); - goto conf_software; - } - /* check if member is on a card with PCM support */ - if (member->dsp->features.pcm_id < 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "dsp has no PCM bus\n", - __func__, member->dsp->name); - goto conf_software; - } - /* check if relations are on the same PCM bus */ - if (member->dsp->features.pcm_id != same_pcm) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "dsp is on a different PCM bus than the " - "first dsp\n", - __func__, member->dsp->name); - goto conf_software; - } - /* determine if members are on the same hfc chip */ - if (same_hfc != member->dsp->features.hfc_id) - same_hfc = -1; - /* if there are members already in a conference */ - if (current_conf < 0 && member->dsp->hfc_conf >= 0) - current_conf = member->dsp->hfc_conf; - /* if any member is not in a conference */ - if (member->dsp->hfc_conf < 0) - all_conf = 0; - - memb++; - } - - /* if no member, this is an error */ - if (memb < 1) - return; - - /* one member */ - if (memb == 1) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s conf %d cannot form a HW conference, " - "because dsp is alone\n", __func__, conf->id); - conf->hardware = 0; - conf->software = 0; - member = list_entry(conf->mlist.next, struct dsp_conf_member, - list); - dsp = member->dsp; - goto one_member; - } - - /* - * ok, now we are sure that all members are on the same pcm. - * now we will see if we have only two members, so we can do - * crossconnections, which don't have any limitations. - */ - - /* if we have only two members */ - if (memb == 2) { - member = list_entry(conf->mlist.next, struct dsp_conf_member, - list); - nextm = list_entry(member->list.next, struct dsp_conf_member, - list); - /* remove HFC conference if enabled */ - if (member->dsp->hfc_conf >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s removing %s from HFC conf %d because " - "two parties require only a PCM slot\n", - __func__, member->dsp->name, - member->dsp->hfc_conf); - dsp_cmx_hw_message(member->dsp, - MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); - member->dsp->hfc_conf = -1; - } - if (nextm->dsp->hfc_conf >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s removing %s from HFC conf %d because " - "two parties require only a PCM slot\n", - __func__, nextm->dsp->name, - nextm->dsp->hfc_conf); - dsp_cmx_hw_message(nextm->dsp, - MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); - nextm->dsp->hfc_conf = -1; - } - /* if members have two banks (and not on the same chip) */ - if (member->dsp->features.pcm_banks > 1 && - nextm->dsp->features.pcm_banks > 1 && - member->dsp->features.hfc_id != - nextm->dsp->features.hfc_id) { - /* if both members have same slots with crossed banks */ - if (member->dsp->pcm_slot_tx >= 0 && - member->dsp->pcm_slot_rx >= 0 && - nextm->dsp->pcm_slot_tx >= 0 && - nextm->dsp->pcm_slot_rx >= 0 && - nextm->dsp->pcm_slot_tx == - member->dsp->pcm_slot_rx && - nextm->dsp->pcm_slot_rx == - member->dsp->pcm_slot_tx && - nextm->dsp->pcm_slot_tx == - member->dsp->pcm_slot_tx && - member->dsp->pcm_bank_tx != - member->dsp->pcm_bank_rx && - nextm->dsp->pcm_bank_tx != - nextm->dsp->pcm_bank_rx) { - /* all members have same slot */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s & %s stay joined on " - "PCM slot %d bank %d (TX) bank %d " - "(RX) (on different chips)\n", - __func__, - member->dsp->name, - nextm->dsp->name, - member->dsp->pcm_slot_tx, - member->dsp->pcm_bank_tx, - member->dsp->pcm_bank_rx); - conf->hardware = 1; - conf->software = tx_data; - return; - } - /* find a new slot */ - memset(freeslots, 1, sizeof(freeslots)); - list_for_each_entry(dsp, &dsp_ilist, list) { - if (dsp != member->dsp && - dsp != nextm->dsp && - member->dsp->features.pcm_id == - dsp->features.pcm_id) { - if (dsp->pcm_slot_rx >= 0 && - dsp->pcm_slot_rx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_rx] = 0; - if (dsp->pcm_slot_tx >= 0 && - dsp->pcm_slot_tx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_tx] = 0; - } - } - i = 0; - ii = member->dsp->features.pcm_slots; - while (i < ii) { - if (freeslots[i]) - break; - i++; - } - if (i == ii) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s no slot available for " - "%s & %s\n", __func__, - member->dsp->name, - nextm->dsp->name); - /* no more slots available */ - goto conf_software; - } - /* assign free slot */ - member->dsp->pcm_slot_tx = i; - member->dsp->pcm_slot_rx = i; - nextm->dsp->pcm_slot_tx = i; - nextm->dsp->pcm_slot_rx = i; - member->dsp->pcm_bank_rx = 0; - member->dsp->pcm_bank_tx = 1; - nextm->dsp->pcm_bank_rx = 1; - nextm->dsp->pcm_bank_tx = 0; - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s adding %s & %s to new PCM slot %d " - "(TX and RX on different chips) because " - "both members have not same slots\n", - __func__, - member->dsp->name, - nextm->dsp->name, - member->dsp->pcm_slot_tx); - dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, - member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, - member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); - dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, - nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, - nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); - conf->hardware = 1; - conf->software = tx_data; - return; - /* if members have one bank (or on the same chip) */ - } else { - /* if both members have different crossed slots */ - if (member->dsp->pcm_slot_tx >= 0 && - member->dsp->pcm_slot_rx >= 0 && - nextm->dsp->pcm_slot_tx >= 0 && - nextm->dsp->pcm_slot_rx >= 0 && - nextm->dsp->pcm_slot_tx == - member->dsp->pcm_slot_rx && - nextm->dsp->pcm_slot_rx == - member->dsp->pcm_slot_tx && - member->dsp->pcm_slot_tx != - member->dsp->pcm_slot_rx && - member->dsp->pcm_bank_tx == 0 && - member->dsp->pcm_bank_rx == 0 && - nextm->dsp->pcm_bank_tx == 0 && - nextm->dsp->pcm_bank_rx == 0) { - /* all members have same slot */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s & %s stay joined on PCM " - "slot %d (TX) %d (RX) on same chip " - "or one bank PCM)\n", __func__, - member->dsp->name, - nextm->dsp->name, - member->dsp->pcm_slot_tx, - member->dsp->pcm_slot_rx); - conf->hardware = 1; - conf->software = tx_data; - return; - } - /* find two new slot */ - memset(freeslots, 1, sizeof(freeslots)); - list_for_each_entry(dsp, &dsp_ilist, list) { - if (dsp != member->dsp && - dsp != nextm->dsp && - member->dsp->features.pcm_id == - dsp->features.pcm_id) { - if (dsp->pcm_slot_rx >= 0 && - dsp->pcm_slot_rx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_rx] = 0; - if (dsp->pcm_slot_tx >= 0 && - dsp->pcm_slot_tx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_tx] = 0; - } - } - i1 = 0; - ii = member->dsp->features.pcm_slots; - while (i1 < ii) { - if (freeslots[i1]) - break; - i1++; - } - if (i1 == ii) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s no slot available " - "for %s & %s\n", __func__, - member->dsp->name, - nextm->dsp->name); - /* no more slots available */ - goto conf_software; - } - i2 = i1 + 1; - while (i2 < ii) { - if (freeslots[i2]) - break; - i2++; - } - if (i2 == ii) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s no slot available " - "for %s & %s\n", - __func__, - member->dsp->name, - nextm->dsp->name); - /* no more slots available */ - goto conf_software; - } - /* assign free slots */ - member->dsp->pcm_slot_tx = i1; - member->dsp->pcm_slot_rx = i2; - nextm->dsp->pcm_slot_tx = i2; - nextm->dsp->pcm_slot_rx = i1; - member->dsp->pcm_bank_rx = 0; - member->dsp->pcm_bank_tx = 0; - nextm->dsp->pcm_bank_rx = 0; - nextm->dsp->pcm_bank_tx = 0; - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s adding %s & %s to new PCM slot %d " - "(TX) %d (RX) on same chip or one bank " - "PCM, because both members have not " - "crossed slots\n", __func__, - member->dsp->name, - nextm->dsp->name, - member->dsp->pcm_slot_tx, - member->dsp->pcm_slot_rx); - dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, - member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, - member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); - dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, - nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, - nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); - conf->hardware = 1; - conf->software = tx_data; - return; - } - } - - /* - * if we have more than two, we may check if we have a conference - * unit available on the chip. also all members must be on the same - */ - - /* if not the same HFC chip */ - if (same_hfc < 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s conference %d cannot be formed, because " - "members are on different chips or not " - "on HFC chip\n", - __func__, conf->id); - goto conf_software; - } - - /* for more than two members.. */ - - /* if all members already have the same conference */ - if (all_conf) { - conf->hardware = 1; - conf->software = tx_data; - return; - } - - /* - * if there is an existing conference, but not all members have joined - */ - if (current_conf >= 0) { - join_members: - list_for_each_entry(member, &conf->mlist, list) { - /* if no conference engine on our chip, change to - * software */ - if (!member->dsp->features.hfc_conf) - goto conf_software; - /* in case of hdlc, change to software */ - if (member->dsp->hdlc) - goto conf_software; - /* join to current conference */ - if (member->dsp->hfc_conf == current_conf) - continue; - /* get a free timeslot first */ - memset(freeslots, 1, sizeof(freeslots)); - list_for_each_entry(dsp, &dsp_ilist, list) { - /* - * not checking current member, because - * slot will be overwritten. - */ - if ( - dsp != member->dsp && - /* dsp must be on the same PCM */ - member->dsp->features.pcm_id == - dsp->features.pcm_id) { - /* dsp must be on a slot */ - if (dsp->pcm_slot_tx >= 0 && - dsp->pcm_slot_tx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_tx] = 0; - if (dsp->pcm_slot_rx >= 0 && - dsp->pcm_slot_rx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_rx] = 0; - } - } - i = 0; - ii = member->dsp->features.pcm_slots; - while (i < ii) { - if (freeslots[i]) - break; - i++; - } - if (i == ii) { - /* no more slots available */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s conference %d cannot be formed," - " because no slot free\n", - __func__, conf->id); - goto conf_software; - } - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s changing dsp %s to HW conference " - "%d slot %d\n", __func__, - member->dsp->name, current_conf, i); - /* assign free slot & set PCM & join conf */ - member->dsp->pcm_slot_tx = i; - member->dsp->pcm_slot_rx = i; - member->dsp->pcm_bank_tx = 2; /* loop */ - member->dsp->pcm_bank_rx = 2; - member->dsp->hfc_conf = current_conf; - dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, - i, 2, i, 2); - dsp_cmx_hw_message(member->dsp, - MISDN_CTRL_HFC_CONF_JOIN, current_conf, 0, 0, 0); - } - conf->hardware = 1; - conf->software = tx_data; - return; - } - - /* - * no member is in a conference yet, so we find a free one - */ - memset(freeunits, 1, sizeof(freeunits)); - list_for_each_entry(dsp, &dsp_ilist, list) { - /* dsp must be on the same chip */ - if (dsp->features.hfc_id == same_hfc && - /* dsp must have joined a HW conference */ - dsp->hfc_conf >= 0 && - /* slot must be within range */ - dsp->hfc_conf < 8) - freeunits[dsp->hfc_conf] = 0; - } - i = 0; - ii = 8; - while (i < ii) { - if (freeunits[i]) - break; - i++; - } - if (i == ii) { - /* no more conferences available */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s conference %d cannot be formed, because " - "no conference number free\n", - __func__, conf->id); - goto conf_software; - } - /* join all members */ - current_conf = i; - goto join_members; -} - - -/* - * conf_id != 0: join or change conference - * conf_id == 0: split from conference if not already - */ -int -dsp_cmx_conf(struct dsp *dsp, u32 conf_id) -{ - int err; - struct dsp_conf *conf; - struct dsp_conf_member *member; - - /* if conference doesn't change */ - if (dsp->conf_id == conf_id) - return 0; - - /* first remove us from current conf */ - if (dsp->conf_id) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "removing us from conference %d\n", - dsp->conf->id); - /* remove us from conf */ - conf = dsp->conf; - err = dsp_cmx_del_conf_member(dsp); - if (err) - return err; - dsp->conf_id = 0; - - /* update hardware */ - dsp_cmx_hardware(NULL, dsp); - - /* conf now empty? */ - if (list_empty(&conf->mlist)) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "conference is empty, so we remove it.\n"); - err = dsp_cmx_del_conf(conf); - if (err) - return err; - } else { - /* update members left on conf */ - dsp_cmx_hardware(conf, NULL); - } - } - - /* if split */ - if (!conf_id) - return 0; - - /* now add us to conf */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "searching conference %d\n", - conf_id); - conf = dsp_cmx_search_conf(conf_id); - if (!conf) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "conference doesn't exist yet, creating.\n"); - /* the conference doesn't exist, so we create */ - conf = dsp_cmx_new_conf(conf_id); - if (!conf) - return -EINVAL; - } else if (!list_empty(&conf->mlist)) { - member = list_entry(conf->mlist.next, struct dsp_conf_member, - list); - if (dsp->hdlc && !member->dsp->hdlc) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "cannot join transparent conference.\n"); - return -EINVAL; - } - if (!dsp->hdlc && member->dsp->hdlc) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "cannot join hdlc conference.\n"); - return -EINVAL; - } - } - /* add conference member */ - err = dsp_cmx_add_conf_member(dsp, conf); - if (err) - return err; - dsp->conf_id = conf_id; - - /* if we are alone, we do nothing! */ - if (list_empty(&conf->mlist)) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "we are alone in this conference, so exit.\n"); - /* update hardware */ - dsp_cmx_hardware(NULL, dsp); - return 0; - } - - /* update members on conf */ - dsp_cmx_hardware(conf, NULL); - - return 0; -} - -#ifdef CMX_DELAY_DEBUG -int delaycount; -static void -showdelay(struct dsp *dsp, int samples, int delay) -{ - char bar[] = "--------------------------------------------------|"; - int sdelay; - - delaycount += samples; - if (delaycount < 8000) - return; - delaycount = 0; - - sdelay = delay * 50 / (dsp_poll << 2); - - printk(KERN_DEBUG "DELAY (%s) %3d >%s\n", dsp->name, delay, - sdelay > 50 ? "..." : bar + 50 - sdelay); -} -#endif - -/* - * audio data is received from card - */ -void -dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) -{ - u8 *d, *p; - int len = skb->len; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int w, i, ii; - - /* check if we have sompen */ - if (len < 1) - return; - - /* half of the buffer should be larger than maximum packet size */ - if (len >= CMX_BUFF_HALF) { - printk(KERN_ERR - "%s line %d: packet from card is too large (%d bytes). " - "please make card send smaller packets OR increase " - "CMX_BUFF_SIZE\n", __FILE__, __LINE__, len); - return; - } - - /* - * initialize pointers if not already - - * also add delay if requested by PH_SIGNAL - */ - if (dsp->rx_init) { - dsp->rx_init = 0; - if (dsp->features.unordered) { - dsp->rx_R = (hh->id & CMX_BUFF_MASK); - if (dsp->cmx_delay) - dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) - & CMX_BUFF_MASK; - else - dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1)) - & CMX_BUFF_MASK; - } else { - dsp->rx_R = 0; - if (dsp->cmx_delay) - dsp->rx_W = dsp->cmx_delay; - else - dsp->rx_W = dsp_poll >> 1; - } - } - /* if frame contains time code, write directly */ - if (dsp->features.unordered) { - dsp->rx_W = (hh->id & CMX_BUFF_MASK); - /* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */ - } - /* - * if we underrun (or maybe overrun), - * we set our new read pointer, and write silence to buffer - */ - if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) { - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG - "cmx_receive(dsp=%lx): UNDERRUN (or overrun the " - "maximum delay), adjusting read pointer! " - "(inst %s)\n", (u_long)dsp, dsp->name); - /* flush rx buffer and set delay to dsp_poll / 2 */ - if (dsp->features.unordered) { - dsp->rx_R = (hh->id & CMX_BUFF_MASK); - if (dsp->cmx_delay) - dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) - & CMX_BUFF_MASK; - else - dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1)) - & CMX_BUFF_MASK; - } else { - dsp->rx_R = 0; - if (dsp->cmx_delay) - dsp->rx_W = dsp->cmx_delay; - else - dsp->rx_W = dsp_poll >> 1; - } - memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); - } - /* if we have reached double delay, jump back to middle */ - if (dsp->cmx_delay) - if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >= - (dsp->cmx_delay << 1)) { - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG - "cmx_receive(dsp=%lx): OVERRUN (because " - "twice the delay is reached), adjusting " - "read pointer! (inst %s)\n", - (u_long)dsp, dsp->name); - /* flush buffer */ - if (dsp->features.unordered) { - dsp->rx_R = (hh->id & CMX_BUFF_MASK); - dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) - & CMX_BUFF_MASK; - } else { - dsp->rx_R = 0; - dsp->rx_W = dsp->cmx_delay; - } - memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); - } - - /* show where to write */ -#ifdef CMX_DEBUG - printk(KERN_DEBUG - "cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n", - (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name); -#endif - - /* write data into rx_buffer */ - p = skb->data; - d = dsp->rx_buff; - w = dsp->rx_W; - i = 0; - ii = len; - while (i < ii) { - d[w++ & CMX_BUFF_MASK] = *p++; - i++; - } - - /* increase write-pointer */ - dsp->rx_W = ((dsp->rx_W + len) & CMX_BUFF_MASK); -#ifdef CMX_DELAY_DEBUG - showdelay(dsp, len, (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK); -#endif -} - - -/* - * send (mixed) audio data to card and control jitter - */ -static void -dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members) -{ - struct dsp_conf *conf = dsp->conf; - struct dsp *member, *other; - register s32 sample; - u8 *d, *p, *q, *o_q; - struct sk_buff *nskb, *txskb; - int r, rr, t, tt, o_r, o_rr; - int preload = 0; - struct mISDNhead *hh, *thh; - int tx_data_only = 0; - - /* don't process if: */ - if (!dsp->b_active) { /* if not active */ - dsp->last_tx = 0; - return; - } - if (((dsp->conf && dsp->conf->hardware) || /* hardware conf */ - dsp->echo.hardware) && /* OR hardware echo */ - dsp->tx_R == dsp->tx_W && /* AND no tx-data */ - !(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */ - if (!dsp->tx_data) { /* no tx_data for user space required */ - dsp->last_tx = 0; - return; - } - if (dsp->conf && dsp->conf->software && dsp->conf->hardware) - tx_data_only = 1; - if (dsp->echo.software && dsp->echo.hardware) - tx_data_only = 1; - } - -#ifdef CMX_DEBUG - printk(KERN_DEBUG - "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n", - members, dsp->name, conf, dsp->rx_R, dsp->rx_W); -#endif - - /* preload if we have delay set */ - if (dsp->cmx_delay && !dsp->last_tx) { - preload = len; - if (preload < 128) - preload = 128; - } - - /* PREPARE RESULT */ - nskb = mI_alloc_skb(len + preload, GFP_ATOMIC); - if (!nskb) { - printk(KERN_ERR - "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n", - len + preload); - return; - } - hh = mISDN_HEAD_P(nskb); - hh->prim = PH_DATA_REQ; - hh->id = 0; - dsp->last_tx = 1; - - /* set pointers, indexes and stuff */ - member = dsp; - p = dsp->tx_buff; /* transmit data */ - q = dsp->rx_buff; /* received data */ - d = skb_put(nskb, preload + len); /* result */ - t = dsp->tx_R; /* tx-pointers */ - tt = dsp->tx_W; - r = dsp->rx_R; /* rx-pointers */ - rr = (r + len) & CMX_BUFF_MASK; - - /* preload with silence, if required */ - if (preload) { - memset(d, dsp_silence, preload); - d += preload; - } - - /* PROCESS TONES/TX-DATA ONLY */ - if (dsp->tone.tone && dsp->tone.software) { - /* -> copy tone */ - dsp_tone_copy(dsp, d, len); - dsp->tx_R = 0; /* clear tx buffer */ - dsp->tx_W = 0; - goto send_packet; - } - /* if we have tx-data but do not use mixing */ - if (!dsp->tx_mix && t != tt) { - /* -> send tx-data and continue when not enough */ -#ifdef CMX_TX_DEBUG - sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p); -#endif - while (r != rr && t != tt) { -#ifdef CMX_TX_DEBUG - if (strlen(debugbuf) < 48) - sprintf(debugbuf + strlen(debugbuf), " %02x", - p[t]); -#endif - *d++ = p[t]; /* write tx_buff */ - t = (t + 1) & CMX_BUFF_MASK; - r = (r + 1) & CMX_BUFF_MASK; - } - if (r == rr) { - dsp->tx_R = t; -#ifdef CMX_TX_DEBUG - printk(KERN_DEBUG "%s\n", debugbuf); -#endif - goto send_packet; - } - } -#ifdef CMX_TX_DEBUG - printk(KERN_DEBUG "%s\n", debugbuf); -#endif - - /* PROCESS DATA (one member / no conf) */ - if (!conf || members <= 1) { - /* -> if echo is NOT enabled */ - if (!dsp->echo.software) { - /* -> send tx-data if available or use 0-volume */ - while (r != rr && t != tt) { - *d++ = p[t]; /* write tx_buff */ - t = (t + 1) & CMX_BUFF_MASK; - r = (r + 1) & CMX_BUFF_MASK; - } - if (r != rr) { - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG "%s: RX empty\n", - __func__); - memset(d, dsp_silence, (rr - r) & CMX_BUFF_MASK); - } - /* -> if echo is enabled */ - } else { - /* - * -> mix tx-data with echo if available, - * or use echo only - */ - while (r != rr && t != tt) { - *d++ = dsp_audio_mix_law[(p[t] << 8) | q[r]]; - t = (t + 1) & CMX_BUFF_MASK; - r = (r + 1) & CMX_BUFF_MASK; - } - while (r != rr) { - *d++ = q[r]; /* echo */ - r = (r + 1) & CMX_BUFF_MASK; - } - } - dsp->tx_R = t; - goto send_packet; - } - /* PROCESS DATA (two members) */ -#ifdef CMX_CONF_DEBUG - if (0) { -#else - if (members == 2) { -#endif - /* "other" becomes other party */ - other = (list_entry(conf->mlist.next, - struct dsp_conf_member, list))->dsp; - if (other == member) - other = (list_entry(conf->mlist.prev, - struct dsp_conf_member, list))->dsp; - o_q = other->rx_buff; /* received data */ - o_rr = (other->rx_R + len) & CMX_BUFF_MASK; - /* end of rx-pointer */ - o_r = (o_rr - rr + r) & CMX_BUFF_MASK; - /* start rx-pointer at current read position*/ - /* -> if echo is NOT enabled */ - if (!dsp->echo.software) { - /* - * -> copy other member's rx-data, - * if tx-data is available, mix - */ - while (o_r != o_rr && t != tt) { - *d++ = dsp_audio_mix_law[(p[t] << 8) | o_q[o_r]]; - t = (t + 1) & CMX_BUFF_MASK; - o_r = (o_r + 1) & CMX_BUFF_MASK; - } - while (o_r != o_rr) { - *d++ = o_q[o_r]; - o_r = (o_r + 1) & CMX_BUFF_MASK; - } - /* -> if echo is enabled */ - } else { - /* - * -> mix other member's rx-data with echo, - * if tx-data is available, mix - */ - while (r != rr && t != tt) { - sample = dsp_audio_law_to_s32[p[t]] + - dsp_audio_law_to_s32[q[r]] + - dsp_audio_law_to_s32[o_q[o_r]]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - *d++ = dsp_audio_s16_to_law[sample & 0xffff]; - /* tx-data + rx_data + echo */ - t = (t + 1) & CMX_BUFF_MASK; - r = (r + 1) & CMX_BUFF_MASK; - o_r = (o_r + 1) & CMX_BUFF_MASK; - } - while (r != rr) { - *d++ = dsp_audio_mix_law[(q[r] << 8) | o_q[o_r]]; - r = (r + 1) & CMX_BUFF_MASK; - o_r = (o_r + 1) & CMX_BUFF_MASK; - } - } - dsp->tx_R = t; - goto send_packet; - } - /* PROCESS DATA (three or more members) */ - /* -> if echo is NOT enabled */ - if (!dsp->echo.software) { - /* - * -> subtract rx-data from conf-data, - * if tx-data is available, mix - */ - while (r != rr && t != tt) { - sample = dsp_audio_law_to_s32[p[t]] + *c++ - - dsp_audio_law_to_s32[q[r]]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - *d++ = dsp_audio_s16_to_law[sample & 0xffff]; - /* conf-rx+tx */ - r = (r + 1) & CMX_BUFF_MASK; - t = (t + 1) & CMX_BUFF_MASK; - } - while (r != rr) { - sample = *c++ - dsp_audio_law_to_s32[q[r]]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - *d++ = dsp_audio_s16_to_law[sample & 0xffff]; - /* conf-rx */ - r = (r + 1) & CMX_BUFF_MASK; - } - /* -> if echo is enabled */ - } else { - /* - * -> encode conf-data, if tx-data - * is available, mix - */ - while (r != rr && t != tt) { - sample = dsp_audio_law_to_s32[p[t]] + *c++; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - *d++ = dsp_audio_s16_to_law[sample & 0xffff]; - /* conf(echo)+tx */ - t = (t + 1) & CMX_BUFF_MASK; - r = (r + 1) & CMX_BUFF_MASK; - } - while (r != rr) { - sample = *c++; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - *d++ = dsp_audio_s16_to_law[sample & 0xffff]; - /* conf(echo) */ - r = (r + 1) & CMX_BUFF_MASK; - } - } - dsp->tx_R = t; - goto send_packet; - -send_packet: - /* - * send tx-data if enabled - don't filter, - * because we want what we send, not what we filtered - */ - if (dsp->tx_data) { - if (tx_data_only) { - hh->prim = DL_DATA_REQ; - hh->id = 0; - /* queue and trigger */ - skb_queue_tail(&dsp->sendq, nskb); - schedule_work(&dsp->workq); - /* exit because only tx_data is used */ - return; - } else { - txskb = mI_alloc_skb(len, GFP_ATOMIC); - if (!txskb) { - printk(KERN_ERR - "FATAL ERROR in mISDN_dsp.o: " - "cannot alloc %d bytes\n", len); - } else { - thh = mISDN_HEAD_P(txskb); - thh->prim = DL_DATA_REQ; - thh->id = 0; - skb_put_data(txskb, nskb->data + preload, len); - /* queue (trigger later) */ - skb_queue_tail(&dsp->sendq, txskb); - } - } - } - - /* send data only to card, if we don't just calculated tx_data */ - /* adjust volume */ - if (dsp->tx_volume) - dsp_change_volume(nskb, dsp->tx_volume); - /* pipeline */ - if (dsp->pipeline.inuse) - dsp_pipeline_process_tx(&dsp->pipeline, nskb->data, - nskb->len); - /* crypt */ - if (dsp->bf_enable) - dsp_bf_encrypt(dsp, nskb->data, nskb->len); - /* queue and trigger */ - skb_queue_tail(&dsp->sendq, nskb); - schedule_work(&dsp->workq); -} - -static u32 jittercount; /* counter for jitter check */ -struct timer_list dsp_spl_tl; -unsigned long dsp_spl_jiffies; /* calculate the next time to fire */ -static u16 dsp_count; /* last sample count */ -static int dsp_count_valid; /* if we have last sample count */ - -void -dsp_cmx_send(struct timer_list *arg) -{ - struct dsp_conf *conf; - struct dsp_conf_member *member; - struct dsp *dsp; - int mustmix, members; - static s32 mixbuffer[MAX_POLL + 100]; - s32 *c; - u8 *p, *q; - int r, rr; - int jittercheck = 0, delay, i; - u_long flags; - u16 length, count; - - /* lock */ - spin_lock_irqsave(&dsp_lock, flags); - - if (!dsp_count_valid) { - dsp_count = mISDN_clock_get(); - length = dsp_poll; - dsp_count_valid = 1; - } else { - count = mISDN_clock_get(); - length = count - dsp_count; - dsp_count = count; - } - if (length > MAX_POLL + 100) - length = MAX_POLL + 100; - /* printk(KERN_DEBUG "len=%d dsp_count=0x%x\n", length, dsp_count); */ - - /* - * check if jitter needs to be checked (this is every second) - */ - jittercount += length; - if (jittercount >= 8000) { - jittercount -= 8000; - jittercheck = 1; - } - - /* loop all members that do not require conference mixing */ - list_for_each_entry(dsp, &dsp_ilist, list) { - if (dsp->hdlc) - continue; - conf = dsp->conf; - mustmix = 0; - members = 0; - if (conf) { - members = list_count_nodes(&conf->mlist); -#ifdef CMX_CONF_DEBUG - if (conf->software && members > 1) -#else - if (conf->software && members > 2) -#endif - mustmix = 1; - } - - /* transmission required */ - if (!mustmix) { - dsp_cmx_send_member(dsp, length, mixbuffer, members); - - /* - * unused mixbuffer is given to prevent a - * potential null-pointer-bug - */ - } - } - - /* loop all members that require conference mixing */ - list_for_each_entry(conf, &conf_ilist, list) { - /* count members and check hardware */ - members = list_count_nodes(&conf->mlist); -#ifdef CMX_CONF_DEBUG - if (conf->software && members > 1) { -#else - if (conf->software && members > 2) { -#endif - /* check for hdlc conf */ - member = list_entry(conf->mlist.next, - struct dsp_conf_member, list); - if (member->dsp->hdlc) - continue; - /* mix all data */ - memset(mixbuffer, 0, length * sizeof(s32)); - list_for_each_entry(member, &conf->mlist, list) { - dsp = member->dsp; - /* get range of data to mix */ - c = mixbuffer; - q = dsp->rx_buff; - r = dsp->rx_R; - rr = (r + length) & CMX_BUFF_MASK; - /* add member's data */ - while (r != rr) { - *c++ += dsp_audio_law_to_s32[q[r]]; - r = (r + 1) & CMX_BUFF_MASK; - } - } - - /* process each member */ - list_for_each_entry(member, &conf->mlist, list) { - /* transmission */ - dsp_cmx_send_member(member->dsp, length, - mixbuffer, members); - } - } - } - - /* delete rx-data, increment buffers, change pointers */ - list_for_each_entry(dsp, &dsp_ilist, list) { - if (dsp->hdlc) - continue; - p = dsp->rx_buff; - q = dsp->tx_buff; - r = dsp->rx_R; - /* move receive pointer when receiving */ - if (!dsp->rx_is_off) { - rr = (r + length) & CMX_BUFF_MASK; - /* delete rx-data */ - while (r != rr) { - p[r] = dsp_silence; - r = (r + 1) & CMX_BUFF_MASK; - } - /* increment rx-buffer pointer */ - dsp->rx_R = r; /* write incremented read pointer */ - } - - /* check current rx_delay */ - delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK; - if (delay >= CMX_BUFF_HALF) - delay = 0; /* will be the delay before next write */ - /* check for lower delay */ - if (delay < dsp->rx_delay[0]) - dsp->rx_delay[0] = delay; - /* check current tx_delay */ - delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK; - if (delay >= CMX_BUFF_HALF) - delay = 0; /* will be the delay before next write */ - /* check for lower delay */ - if (delay < dsp->tx_delay[0]) - dsp->tx_delay[0] = delay; - if (jittercheck) { - /* find the lowest of all rx_delays */ - delay = dsp->rx_delay[0]; - i = 1; - while (i < MAX_SECONDS_JITTER_CHECK) { - if (delay > dsp->rx_delay[i]) - delay = dsp->rx_delay[i]; - i++; - } - /* - * remove rx_delay only if we have delay AND we - * have not preset cmx_delay AND - * the delay is greater dsp_poll - */ - if (delay > dsp_poll && !dsp->cmx_delay) { - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG - "%s lowest rx_delay of %d bytes for" - " dsp %s are now removed.\n", - __func__, delay, - dsp->name); - r = dsp->rx_R; - rr = (r + delay - (dsp_poll >> 1)) - & CMX_BUFF_MASK; - /* delete rx-data */ - while (r != rr) { - p[r] = dsp_silence; - r = (r + 1) & CMX_BUFF_MASK; - } - /* increment rx-buffer pointer */ - dsp->rx_R = r; - /* write incremented read pointer */ - } - /* find the lowest of all tx_delays */ - delay = dsp->tx_delay[0]; - i = 1; - while (i < MAX_SECONDS_JITTER_CHECK) { - if (delay > dsp->tx_delay[i]) - delay = dsp->tx_delay[i]; - i++; - } - /* - * remove delay only if we have delay AND we - * have enabled tx_dejitter - */ - if (delay > dsp_poll && dsp->tx_dejitter) { - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG - "%s lowest tx_delay of %d bytes for" - " dsp %s are now removed.\n", - __func__, delay, - dsp->name); - r = dsp->tx_R; - rr = (r + delay - (dsp_poll >> 1)) - & CMX_BUFF_MASK; - /* delete tx-data */ - while (r != rr) { - q[r] = dsp_silence; - r = (r + 1) & CMX_BUFF_MASK; - } - /* increment rx-buffer pointer */ - dsp->tx_R = r; - /* write incremented read pointer */ - } - /* scroll up delays */ - i = MAX_SECONDS_JITTER_CHECK - 1; - while (i) { - dsp->rx_delay[i] = dsp->rx_delay[i - 1]; - dsp->tx_delay[i] = dsp->tx_delay[i - 1]; - i--; - } - dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ - dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ - } - } - - /* if next event would be in the past ... */ - if ((s32)(dsp_spl_jiffies + dsp_tics-jiffies) <= 0) - dsp_spl_jiffies = jiffies + 1; - else - dsp_spl_jiffies += dsp_tics; - - dsp_spl_tl.expires = dsp_spl_jiffies; - add_timer(&dsp_spl_tl); - - /* unlock */ - spin_unlock_irqrestore(&dsp_lock, flags); -} - -/* - * audio data is transmitted from upper layer to the dsp - */ -void -dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb) -{ - u_int w, ww; - u8 *d, *p; - int space; /* todo: , l = skb->len; */ -#ifdef CMX_TX_DEBUG - char debugbuf[256] = ""; -#endif - - /* check if there is enough space, and then copy */ - w = dsp->tx_W; - ww = dsp->tx_R; - p = dsp->tx_buff; - d = skb->data; - space = (ww - w - 1) & CMX_BUFF_MASK; - /* write-pointer should not overrun nor reach read pointer */ - if (space < skb->len) { - /* write to the space we have left */ - ww = (ww - 1) & CMX_BUFF_MASK; /* end one byte prior tx_R */ - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG "%s: TX overflow space=%d skb->len=" - "%d, w=0x%04x, ww=0x%04x\n", __func__, space, - skb->len, w, ww); - } else - /* write until all byte are copied */ - ww = (w + skb->len) & CMX_BUFF_MASK; - dsp->tx_W = ww; - /* show current buffer */ -#ifdef CMX_DEBUG - printk(KERN_DEBUG - "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", - (u_long)dsp, (ww - w) & CMX_BUFF_MASK, w, ww, dsp->name); -#endif - - /* copy transmit data to tx-buffer */ -#ifdef CMX_TX_DEBUG - sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p); -#endif - while (w != ww) { -#ifdef CMX_TX_DEBUG - if (strlen(debugbuf) < 48) - sprintf(debugbuf + strlen(debugbuf), " %02x", *d); -#endif - p[w] = *d++; - w = (w + 1) & CMX_BUFF_MASK; - } -#ifdef CMX_TX_DEBUG - printk(KERN_DEBUG "%s\n", debugbuf); -#endif - -} - -/* - * hdlc data is received from card and sent to all members. - */ -void -dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb) -{ - struct sk_buff *nskb = NULL; - struct dsp_conf_member *member; - struct mISDNhead *hh; - - /* not if not active */ - if (!dsp->b_active) - return; - - /* check if we have sompen */ - if (skb->len < 1) - return; - - /* no conf */ - if (!dsp->conf) { - /* in case of software echo */ - if (dsp->echo.software) { - nskb = skb_clone(skb, GFP_ATOMIC); - if (nskb) { - hh = mISDN_HEAD_P(nskb); - hh->prim = PH_DATA_REQ; - hh->id = 0; - skb_queue_tail(&dsp->sendq, nskb); - schedule_work(&dsp->workq); - } - } - return; - } - /* in case of hardware conference */ - if (dsp->conf->hardware) - return; - list_for_each_entry(member, &dsp->conf->mlist, list) { - if (dsp->echo.software || member->dsp != dsp) { - nskb = skb_clone(skb, GFP_ATOMIC); - if (nskb) { - hh = mISDN_HEAD_P(nskb); - hh->prim = PH_DATA_REQ; - hh->id = 0; - skb_queue_tail(&member->dsp->sendq, nskb); - schedule_work(&member->dsp->workq); - } - } - } -} diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c deleted file mode 100644 index d0aa415a6b09..000000000000 --- a/drivers/isdn/mISDN/dsp_core.c +++ /dev/null @@ -1,1227 +0,0 @@ -/* - * Author Andreas Eversberg (jolly@eversberg.eu) - * Based on source code structure by - * Karsten Keil (keil@isdn4linux.de) - * - * This file is (c) under GNU PUBLIC LICENSE - * - * Thanks to Karsten Keil (great drivers) - * Cologne Chip (great chips) - * - * This module does: - * Real-time tone generation - * DTMF detection - * Real-time cross-connection and conferrence - * Compensate jitter due to system load and hardware fault. - * All features are done in kernel space and will be realized - * using hardware, if available and supported by chip set. - * Blowfish encryption/decryption - */ - -/* STRUCTURE: - * - * The dsp module provides layer 2 for b-channels (64kbit). It provides - * transparent audio forwarding with special digital signal processing: - * - * - (1) generation of tones - * - (2) detection of dtmf tones - * - (3) crossconnecting and conferences (clocking) - * - (4) echo generation for delay test - * - (5) volume control - * - (6) disable receive data - * - (7) pipeline - * - (8) encryption/decryption - * - * Look: - * TX RX - * ------upper layer------ - * | ^ - * | |(6) - * v | - * +-----+-------------+-----+ - * |(3)(4) | - * | CMX | - * | | - * | +-------------+ - * | | ^ - * | | | - * |+---------+| +----+----+ - * ||(1) || |(2) | - * || || | | - * || Tones || | DTMF | - * || || | | - * || || | | - * |+----+----+| +----+----+ - * +-----+-----+ ^ - * | | - * v | - * +----+----+ +----+----+ - * |(5) | |(5) | - * | | | | - * |TX Volume| |RX Volume| - * | | | | - * | | | | - * +----+----+ +----+----+ - * | ^ - * | | - * v | - * +----+-------------+----+ - * |(7) | - * | | - * | Pipeline Processing | - * | | - * | | - * +----+-------------+----+ - * | ^ - * | | - * v | - * +----+----+ +----+----+ - * |(8) | |(8) | - * | | | | - * | Encrypt | | Decrypt | - * | | | | - * | | | | - * +----+----+ +----+----+ - * | ^ - * | | - * v | - * ------card layer------ - * TX RX - * - * Above you can see the logical data flow. If software is used to do the - * process, it is actually the real data flow. If hardware is used, data - * may not flow, but hardware commands to the card, to provide the data flow - * as shown. - * - * NOTE: The channel must be activated in order to make dsp work, even if - * no data flow to the upper layer is intended. Activation can be done - * after and before controlling the setting using PH_CONTROL requests. - * - * DTMF: Will be detected by hardware if possible. It is done before CMX - * processing. - * - * Tones: Will be generated via software if endless looped audio fifos are - * not supported by hardware. Tones will override all data from CMX. - * It is not required to join a conference to use tones at any time. - * - * CMX: Is transparent when not used. When it is used, it will do - * crossconnections and conferences via software if not possible through - * hardware. If hardware capability is available, hardware is used. - * - * Echo: Is generated by CMX and is used to check performance of hard and - * software CMX. - * - * The CMX has special functions for conferences with one, two and more - * members. It will allow different types of data flow. Receive and transmit - * data to/form upper layer may be switched on/off individually without losing - * features of CMX, Tones and DTMF. - * - * Echo Cancellation: Sometimes we like to cancel echo from the interface. - * Note that a VoIP call may not have echo caused by the IP phone. The echo - * is generated by the telephone line connected to it. Because the delay - * is high, it becomes an echo. RESULT: Echo Cachelation is required if - * both echo AND delay is applied to an interface. - * Remember that software CMX always generates a more or less delay. - * - * If all used features can be realized in hardware, and if transmit and/or - * receive data ist disabled, the card may not send/receive any data at all. - * Not receiving is useful if only announcements are played. Not sending is - * useful if an answering machine records audio. Not sending and receiving is - * useful during most states of the call. If supported by hardware, tones - * will be played without cpu load. Small PBXs and NT-Mode applications will - * not need expensive hardware when processing calls. - * - * - * LOCKING: - * - * When data is received from upper or lower layer (card), the complete dsp - * module is locked by a global lock. This lock MUST lock irq, because it - * must lock timer events by DSP poll timer. - * When data is ready to be transmitted down, the data is queued and sent - * outside lock and timer event. - * PH_CONTROL must not change any settings, join or split conference members - * during process of data. - * - * HDLC: - * - * It works quite the same as transparent, except that HDLC data is forwarded - * to all other conference members if no hardware bridging is possible. - * Send data will be writte to sendq. Sendq will be sent if confirm is received. - * Conference cannot join, if one member is not hdlc. - * - */ - -#include -#include -#include -#include -#include -#include -#include "core.h" -#include "dsp.h" - -static const char *mISDN_dsp_revision = "2.0"; - -static int debug; -static int options; -static int poll; -static int dtmfthreshold = 100; - -MODULE_AUTHOR("Andreas Eversberg"); -module_param(debug, uint, S_IRUGO | S_IWUSR); -module_param(options, uint, S_IRUGO | S_IWUSR); -module_param(poll, uint, S_IRUGO | S_IWUSR); -module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR); -MODULE_DESCRIPTION("mISDN driver for Digital Audio Processing of transparent data"); -MODULE_LICENSE("GPL"); - -/*int spinnest = 0;*/ - -DEFINE_SPINLOCK(dsp_lock); /* global dsp lock */ -LIST_HEAD(dsp_ilist); -LIST_HEAD(conf_ilist); -int dsp_debug; -int dsp_options; -int dsp_poll, dsp_tics; - -/* check if rx may be turned off or must be turned on */ -static void -dsp_rx_off_member(struct dsp *dsp) -{ - struct mISDN_ctrl_req cq; - int rx_off = 1; - - memset(&cq, 0, sizeof(cq)); - - if (!dsp->features_rx_off) - return; - - /* not disabled */ - if (!dsp->rx_disabled) - rx_off = 0; - /* software dtmf */ - else if (dsp->dtmf.software) - rx_off = 0; - /* echo in software */ - else if (dsp->echo.software) - rx_off = 0; - /* bridge in software */ - else if (dsp->conf && dsp->conf->software) - rx_off = 0; - /* data is not required by user space and not required - * for echo dtmf detection, soft-echo, soft-bridging */ - - if (rx_off == dsp->rx_is_off) - return; - - if (!dsp->ch.peer) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: no peer, no rx_off\n", - __func__); - return; - } - cq.op = MISDN_CTRL_RX_OFF; - cq.p1 = rx_off; - if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) { - printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", - __func__); - return; - } - dsp->rx_is_off = rx_off; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: %s set rx_off = %d\n", - __func__, dsp->name, rx_off); -} -static void -dsp_rx_off(struct dsp *dsp) -{ - struct dsp_conf_member *member; - - if (dsp_options & DSP_OPT_NOHARDWARE) - return; - - /* no conf */ - if (!dsp->conf) { - dsp_rx_off_member(dsp); - return; - } - /* check all members in conf */ - list_for_each_entry(member, &dsp->conf->mlist, list) { - dsp_rx_off_member(member->dsp); - } -} - -/* enable "fill empty" feature */ -static void -dsp_fill_empty(struct dsp *dsp) -{ - struct mISDN_ctrl_req cq; - - memset(&cq, 0, sizeof(cq)); - - if (!dsp->ch.peer) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: no peer, no fill_empty\n", - __func__); - return; - } - cq.op = MISDN_CTRL_FILL_EMPTY; - cq.p1 = 1; - cq.p2 = dsp_silence; - if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) { - printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", - __func__); - return; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: %s set fill_empty = 1\n", - __func__, dsp->name); -} - -static int -dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) -{ - struct sk_buff *nskb; - int ret = 0; - int cont; - u8 *data; - int len; - - if (skb->len < sizeof(int)) { - printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__); - return -EINVAL; - } - cont = *((int *)skb->data); - len = skb->len - sizeof(int); - data = skb->data + sizeof(int); - - switch (cont) { - case DTMF_TONE_START: /* turn on DTMF */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: start dtmf\n", __func__); - if (len == sizeof(int)) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_NOTICE "changing DTMF Threshold " - "to %d\n", *((int *)data)); - dsp->dtmf.treshold = (*(int *)data) * 10000; - } - dsp->dtmf.enable = 1; - /* init goertzel */ - dsp_dtmf_goertzel_init(dsp); - - /* check dtmf hardware */ - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - break; - case DTMF_TONE_STOP: /* turn off DTMF */ - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: stop dtmf\n", __func__); - dsp->dtmf.enable = 0; - dsp->dtmf.hardware = 0; - dsp->dtmf.software = 0; - break; - case DSP_CONF_JOIN: /* join / update conference */ - if (len < sizeof(int)) { - ret = -EINVAL; - break; - } - if (*((u32 *)data) == 0) - goto conf_split; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: join conference %d\n", - __func__, *((u32 *)data)); - ret = dsp_cmx_conf(dsp, *((u32 *)data)); - /* dsp_cmx_hardware will also be called here */ - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_CONF_SPLIT: /* remove from conference */ - conf_split: - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: release conference\n", __func__); - ret = dsp_cmx_conf(dsp, 0); - /* dsp_cmx_hardware will also be called here */ - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - dsp_rx_off(dsp); - break; - case DSP_TONE_PATT_ON: /* play tone */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len < sizeof(int)) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: turn tone 0x%x on\n", - __func__, *((int *)skb->data)); - ret = dsp_tone(dsp, *((int *)data)); - if (!ret) { - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - } - if (!dsp->tone.tone) - goto tone_off; - break; - case DSP_TONE_PATT_OFF: /* stop tone */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: turn tone off\n", __func__); - dsp_tone(dsp, 0); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - /* reset tx buffers (user space data) */ - tone_off: - dsp->rx_W = 0; - dsp->rx_R = 0; - break; - case DSP_VOL_CHANGE_TX: /* change volume */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len < sizeof(int)) { - ret = -EINVAL; - break; - } - dsp->tx_volume = *((int *)data); - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: change tx vol to %d\n", - __func__, dsp->tx_volume); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - break; - case DSP_VOL_CHANGE_RX: /* change volume */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len < sizeof(int)) { - ret = -EINVAL; - break; - } - dsp->rx_volume = *((int *)data); - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: change rx vol to %d\n", - __func__, dsp->tx_volume); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - break; - case DSP_ECHO_ON: /* enable echo */ - dsp->echo.software = 1; /* soft echo */ - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: enable cmx-echo\n", __func__); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_ECHO_OFF: /* disable echo */ - dsp->echo.software = 0; - dsp->echo.hardware = 0; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: disable cmx-echo\n", __func__); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_RECEIVE_ON: /* enable receive to user space */ - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: enable receive to user " - "space\n", __func__); - dsp->rx_disabled = 0; - dsp_rx_off(dsp); - break; - case DSP_RECEIVE_OFF: /* disable receive to user space */ - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: disable receive to " - "user space\n", __func__); - dsp->rx_disabled = 1; - dsp_rx_off(dsp); - break; - case DSP_MIX_ON: /* enable mixing of tx data */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: enable mixing of " - "tx-data with conf members\n", __func__); - dsp->tx_mix = 1; - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_MIX_OFF: /* disable mixing of tx data */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: disable mixing of " - "tx-data with conf members\n", __func__); - dsp->tx_mix = 0; - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_TXDATA_ON: /* enable txdata */ - dsp->tx_data = 1; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: enable tx-data\n", __func__); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_TXDATA_OFF: /* disable txdata */ - dsp->tx_data = 0; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: disable tx-data\n", __func__); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_DELAY: /* use delay algorithm instead of dynamic - jitter algorithm */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len < sizeof(int)) { - ret = -EINVAL; - break; - } - dsp->cmx_delay = (*((int *)data)) << 3; - /* milliseconds to samples */ - if (dsp->cmx_delay >= (CMX_BUFF_HALF >> 1)) - /* clip to half of maximum usable buffer - (half of half buffer) */ - dsp->cmx_delay = (CMX_BUFF_HALF >> 1) - 1; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: use delay algorithm to " - "compensate jitter (%d samples)\n", - __func__, dsp->cmx_delay); - break; - case DSP_JITTER: /* use dynamic jitter algorithm instead of - delay algorithm */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - dsp->cmx_delay = 0; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: use jitter algorithm to " - "compensate jitter\n", __func__); - break; - case DSP_TX_DEJITTER: /* use dynamic jitter algorithm for tx-buffer */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - dsp->tx_dejitter = 1; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: use dejitter on TX " - "buffer\n", __func__); - break; - case DSP_TX_DEJ_OFF: /* use tx-buffer without dejittering*/ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - dsp->tx_dejitter = 0; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: use TX buffer without " - "dejittering\n", __func__); - break; - case DSP_PIPELINE_CFG: - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len > 0 && ((char *)data)[len - 1]) { - printk(KERN_DEBUG "%s: pipeline config string " - "is not NULL terminated!\n", __func__); - ret = -EINVAL; - } else { - dsp->pipeline.inuse = 1; - dsp_cmx_hardware(dsp->conf, dsp); - ret = dsp_pipeline_build(&dsp->pipeline, - len > 0 ? data : NULL); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - } - break; - case DSP_BF_ENABLE_KEY: /* turn blowfish on */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len < 4 || len > 56) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: turn blowfish on (key " - "not shown)\n", __func__); - ret = dsp_bf_init(dsp, (u8 *)data, len); - /* set new cont */ - if (!ret) - cont = DSP_BF_ACCEPT; - else - cont = DSP_BF_REJECT; - /* send indication if it worked to set it */ - nskb = _alloc_mISDN_skb(PH_CONTROL_IND, MISDN_ID_ANY, - sizeof(int), &cont, GFP_ATOMIC); - if (nskb) { - if (dsp->up) { - if (dsp->up->send(dsp->up, nskb)) - dev_kfree_skb(nskb); - } else - dev_kfree_skb(nskb); - } - if (!ret) { - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - } - break; - case DSP_BF_DISABLE: /* turn blowfish off */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: turn blowfish off\n", __func__); - dsp_bf_cleanup(dsp); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - break; - default: - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: ctrl req %x unhandled\n", - __func__, cont); - ret = -EINVAL; - } - return ret; -} - -static void -get_features(struct mISDNchannel *ch) -{ - struct dsp *dsp = container_of(ch, struct dsp, ch); - struct mISDN_ctrl_req cq; - - if (!ch->peer) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: no peer, no features\n", - __func__); - return; - } - memset(&cq, 0, sizeof(cq)); - cq.op = MISDN_CTRL_GETOP; - if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq) < 0) { - printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", - __func__); - return; - } - if (cq.op & MISDN_CTRL_RX_OFF) - dsp->features_rx_off = 1; - if (cq.op & MISDN_CTRL_FILL_EMPTY) - dsp->features_fill_empty = 1; - if (dsp_options & DSP_OPT_NOHARDWARE) - return; - if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) { - cq.op = MISDN_CTRL_HW_FEATURES; - *((u_long *)&cq.p1) = (u_long)&dsp->features; - if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq)) { - printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", - __func__); - } - } else - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: features not supported for %s\n", - __func__, dsp->name); -} - -static int -dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct dsp *dsp = container_of(ch, struct dsp, ch); - struct mISDNhead *hh; - int ret = 0; - u8 *digits = NULL; - u_long flags; - - hh = mISDN_HEAD_P(skb); - switch (hh->prim) { - /* FROM DOWN */ - case (PH_DATA_CNF): - dsp->data_pending = 0; - /* trigger next hdlc frame, if any */ - if (dsp->hdlc) { - spin_lock_irqsave(&dsp_lock, flags); - if (dsp->b_active) - schedule_work(&dsp->workq); - spin_unlock_irqrestore(&dsp_lock, flags); - } - break; - case (PH_DATA_IND): - case (DL_DATA_IND): - if (skb->len < 1) { - ret = -EINVAL; - break; - } - if (dsp->rx_is_off) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: rx-data during rx_off" - " for %s\n", - __func__, dsp->name); - } - if (dsp->hdlc) { - /* hdlc */ - spin_lock_irqsave(&dsp_lock, flags); - dsp_cmx_hdlc(dsp, skb); - spin_unlock_irqrestore(&dsp_lock, flags); - if (dsp->rx_disabled) { - /* if receive is not allowed */ - break; - } - hh->prim = DL_DATA_IND; - if (dsp->up) - return dsp->up->send(dsp->up, skb); - break; - } - - spin_lock_irqsave(&dsp_lock, flags); - - /* decrypt if enabled */ - if (dsp->bf_enable) - dsp_bf_decrypt(dsp, skb->data, skb->len); - /* pipeline */ - if (dsp->pipeline.inuse) - dsp_pipeline_process_rx(&dsp->pipeline, skb->data, - skb->len, hh->id); - /* change volume if requested */ - if (dsp->rx_volume) - dsp_change_volume(skb, dsp->rx_volume); - /* check if dtmf soft decoding is turned on */ - if (dsp->dtmf.software) { - digits = dsp_dtmf_goertzel_decode(dsp, skb->data, - skb->len, (dsp_options & DSP_OPT_ULAW) ? 1 : 0); - } - /* we need to process receive data if software */ - if (dsp->conf && dsp->conf->software) { - /* process data from card at cmx */ - dsp_cmx_receive(dsp, skb); - } - - spin_unlock_irqrestore(&dsp_lock, flags); - - /* send dtmf result, if any */ - if (digits) { - while (*digits) { - int k; - struct sk_buff *nskb; - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s: digit" - "(%c) to layer %s\n", - __func__, *digits, dsp->name); - k = *digits | DTMF_TONE_VAL; - nskb = _alloc_mISDN_skb(PH_CONTROL_IND, - MISDN_ID_ANY, sizeof(int), &k, - GFP_ATOMIC); - if (nskb) { - if (dsp->up) { - if (dsp->up->send( - dsp->up, nskb)) - dev_kfree_skb(nskb); - } else - dev_kfree_skb(nskb); - } - digits++; - } - } - if (dsp->rx_disabled) { - /* if receive is not allowed */ - break; - } - hh->prim = DL_DATA_IND; - if (dsp->up) - return dsp->up->send(dsp->up, skb); - break; - case (PH_CONTROL_IND): - if (dsp_debug & DEBUG_DSP_DTMFCOEFF) - printk(KERN_DEBUG "%s: PH_CONTROL INDICATION " - "received: %x (len %d) %s\n", __func__, - hh->id, skb->len, dsp->name); - switch (hh->id) { - case (DTMF_HFC_COEF): /* getting coefficients */ - if (!dsp->dtmf.hardware) { - if (dsp_debug & DEBUG_DSP_DTMFCOEFF) - printk(KERN_DEBUG "%s: ignoring DTMF " - "coefficients from HFC\n", - __func__); - break; - } - digits = dsp_dtmf_goertzel_decode(dsp, skb->data, - skb->len, 2); - while (*digits) { - int k; - struct sk_buff *nskb; - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s: digit" - "(%c) to layer %s\n", - __func__, *digits, dsp->name); - k = *digits | DTMF_TONE_VAL; - nskb = _alloc_mISDN_skb(PH_CONTROL_IND, - MISDN_ID_ANY, sizeof(int), &k, - GFP_ATOMIC); - if (nskb) { - if (dsp->up) { - if (dsp->up->send( - dsp->up, nskb)) - dev_kfree_skb(nskb); - } else - dev_kfree_skb(nskb); - } - digits++; - } - break; - case (HFC_VOL_CHANGE_TX): /* change volume */ - if (skb->len != sizeof(int)) { - ret = -EINVAL; - break; - } - spin_lock_irqsave(&dsp_lock, flags); - dsp->tx_volume = *((int *)skb->data); - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: change tx volume to " - "%d\n", __func__, dsp->tx_volume); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - spin_unlock_irqrestore(&dsp_lock, flags); - break; - default: - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: ctrl ind %x unhandled " - "%s\n", __func__, hh->id, dsp->name); - ret = -EINVAL; - } - break; - case (PH_ACTIVATE_IND): - case (PH_ACTIVATE_CNF): - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: b_channel is now active %s\n", - __func__, dsp->name); - /* bchannel now active */ - spin_lock_irqsave(&dsp_lock, flags); - dsp->b_active = 1; - dsp->data_pending = 0; - dsp->rx_init = 1; - /* rx_W and rx_R will be adjusted on first frame */ - dsp->rx_W = 0; - dsp->rx_R = 0; - memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff)); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - spin_unlock_irqrestore(&dsp_lock, flags); - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: done with activation, sending " - "confirm to user space. %s\n", __func__, - dsp->name); - /* send activation to upper layer */ - hh->prim = DL_ESTABLISH_CNF; - if (dsp->up) - return dsp->up->send(dsp->up, skb); - break; - case (PH_DEACTIVATE_IND): - case (PH_DEACTIVATE_CNF): - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: b_channel is now inactive %s\n", - __func__, dsp->name); - /* bchannel now inactive */ - spin_lock_irqsave(&dsp_lock, flags); - dsp->b_active = 0; - dsp->data_pending = 0; - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - spin_unlock_irqrestore(&dsp_lock, flags); - hh->prim = DL_RELEASE_CNF; - if (dsp->up) - return dsp->up->send(dsp->up, skb); - break; - /* FROM UP */ - case (DL_DATA_REQ): - case (PH_DATA_REQ): - if (skb->len < 1) { - ret = -EINVAL; - break; - } - if (dsp->hdlc) { - /* hdlc */ - if (!dsp->b_active) { - ret = -EIO; - break; - } - hh->prim = PH_DATA_REQ; - spin_lock_irqsave(&dsp_lock, flags); - skb_queue_tail(&dsp->sendq, skb); - schedule_work(&dsp->workq); - spin_unlock_irqrestore(&dsp_lock, flags); - return 0; - } - /* send data to tx-buffer (if no tone is played) */ - if (!dsp->tone.tone) { - spin_lock_irqsave(&dsp_lock, flags); - dsp_cmx_transmit(dsp, skb); - spin_unlock_irqrestore(&dsp_lock, flags); - } - break; - case (PH_CONTROL_REQ): - spin_lock_irqsave(&dsp_lock, flags); - ret = dsp_control_req(dsp, hh, skb); - spin_unlock_irqrestore(&dsp_lock, flags); - break; - case (DL_ESTABLISH_REQ): - case (PH_ACTIVATE_REQ): - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: activating b_channel %s\n", - __func__, dsp->name); - if (dsp->dtmf.hardware || dsp->dtmf.software) - dsp_dtmf_goertzel_init(dsp); - get_features(ch); - /* enable fill_empty feature */ - if (dsp->features_fill_empty) - dsp_fill_empty(dsp); - /* send ph_activate */ - hh->prim = PH_ACTIVATE_REQ; - if (ch->peer) - return ch->recv(ch->peer, skb); - break; - case (DL_RELEASE_REQ): - case (PH_DEACTIVATE_REQ): - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: releasing b_channel %s\n", - __func__, dsp->name); - spin_lock_irqsave(&dsp_lock, flags); - dsp->tone.tone = 0; - dsp->tone.hardware = 0; - dsp->tone.software = 0; - if (timer_pending(&dsp->tone.tl)) - timer_delete(&dsp->tone.tl); - if (dsp->conf) - dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be - called here */ - skb_queue_purge(&dsp->sendq); - spin_unlock_irqrestore(&dsp_lock, flags); - hh->prim = PH_DEACTIVATE_REQ; - if (ch->peer) - return ch->recv(ch->peer, skb); - break; - default: - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: msg %x unhandled %s\n", - __func__, hh->prim, dsp->name); - ret = -EINVAL; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -dsp_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct dsp *dsp = container_of(ch, struct dsp, ch); - u_long flags; - - if (debug & DEBUG_DSP_CTRL) - printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd); - - switch (cmd) { - case OPEN_CHANNEL: - break; - case CLOSE_CHANNEL: - if (dsp->ch.peer) - dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL); - - /* wait until workqueue has finished, - * must lock here, or we may hit send-process currently - * queueing. */ - spin_lock_irqsave(&dsp_lock, flags); - dsp->b_active = 0; - spin_unlock_irqrestore(&dsp_lock, flags); - /* MUST not be locked, because it waits until queue is done. */ - cancel_work_sync(&dsp->workq); - spin_lock_irqsave(&dsp_lock, flags); - if (timer_pending(&dsp->tone.tl)) - timer_delete(&dsp->tone.tl); - skb_queue_purge(&dsp->sendq); - if (dsp_debug & DEBUG_DSP_CTRL) - printk(KERN_DEBUG "%s: releasing member %s\n", - __func__, dsp->name); - dsp->b_active = 0; - dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called - here */ - dsp_pipeline_destroy(&dsp->pipeline); - - if (dsp_debug & DEBUG_DSP_CTRL) - printk(KERN_DEBUG "%s: remove & destroy object %s\n", - __func__, dsp->name); - list_del(&dsp->list); - spin_unlock_irqrestore(&dsp_lock, flags); - - if (dsp_debug & DEBUG_DSP_CTRL) - printk(KERN_DEBUG "%s: dsp instance released\n", - __func__); - vfree(dsp); - module_put(THIS_MODULE); - break; - } - return 0; -} - -static void -dsp_send_bh(struct work_struct *work) -{ - struct dsp *dsp = container_of(work, struct dsp, workq); - struct sk_buff *skb; - struct mISDNhead *hh; - - if (dsp->hdlc && dsp->data_pending) - return; /* wait until data has been acknowledged */ - - /* send queued data */ - while ((skb = skb_dequeue(&dsp->sendq))) { - /* in locked date, we must have still data in queue */ - if (dsp->data_pending) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: fifo full %s, this is " - "no bug!\n", __func__, dsp->name); - /* flush transparent data, if not acked */ - dev_kfree_skb(skb); - continue; - } - hh = mISDN_HEAD_P(skb); - if (hh->prim == DL_DATA_REQ) { - /* send packet up */ - if (dsp->up) { - if (dsp->up->send(dsp->up, skb)) - dev_kfree_skb(skb); - } else - dev_kfree_skb(skb); - } else { - /* send packet down */ - if (dsp->ch.peer) { - dsp->data_pending = 1; - if (dsp->ch.recv(dsp->ch.peer, skb)) { - dev_kfree_skb(skb); - dsp->data_pending = 0; - } - } else - dev_kfree_skb(skb); - } - } -} - -static int -dspcreate(struct channel_req *crq) -{ - struct dsp *ndsp; - u_long flags; - - if (crq->protocol != ISDN_P_B_L2DSP - && crq->protocol != ISDN_P_B_L2DSPHDLC) - return -EPROTONOSUPPORT; - ndsp = vzalloc(sizeof(struct dsp)); - if (!ndsp) { - printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__); - return -ENOMEM; - } - if (dsp_debug & DEBUG_DSP_CTRL) - printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__); - - /* default enabled */ - INIT_WORK(&ndsp->workq, (void *)dsp_send_bh); - skb_queue_head_init(&ndsp->sendq); - ndsp->ch.send = dsp_function; - ndsp->ch.ctrl = dsp_ctrl; - ndsp->up = crq->ch; - crq->ch = &ndsp->ch; - if (crq->protocol == ISDN_P_B_L2DSP) { - crq->protocol = ISDN_P_B_RAW; - ndsp->hdlc = 0; - } else { - crq->protocol = ISDN_P_B_HDLC; - ndsp->hdlc = 1; - } - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", - __func__); - - sprintf(ndsp->name, "DSP_C%x(0x%p)", - ndsp->up->st->dev->id + 1, ndsp); - /* set frame size to start */ - ndsp->features.hfc_id = -1; /* current PCM id */ - ndsp->features.pcm_id = -1; /* current PCM id */ - ndsp->pcm_slot_rx = -1; /* current CPM slot */ - ndsp->pcm_slot_tx = -1; - ndsp->pcm_bank_rx = -1; - ndsp->pcm_bank_tx = -1; - ndsp->hfc_conf = -1; /* current conference number */ - /* set tone timer */ - timer_setup(&ndsp->tone.tl, dsp_tone_timeout, 0); - - if (dtmfthreshold < 20 || dtmfthreshold > 500) - dtmfthreshold = 200; - ndsp->dtmf.treshold = dtmfthreshold * 10000; - - /* init pipeline append to list */ - spin_lock_irqsave(&dsp_lock, flags); - dsp_pipeline_init(&ndsp->pipeline); - list_add_tail(&ndsp->list, &dsp_ilist); - spin_unlock_irqrestore(&dsp_lock, flags); - - return 0; -} - - -static struct Bprotocol DSP = { - .Bprotocols = (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK)) - | (1 << (ISDN_P_B_L2DSPHDLC & ISDN_P_B_MASK)), - .name = "dsp", - .create = dspcreate -}; - -static int __init dsp_init(void) -{ - int err; - int tics; - - printk(KERN_INFO "DSP module %s\n", mISDN_dsp_revision); - - dsp_options = options; - dsp_debug = debug; - - /* set packet size */ - dsp_poll = poll; - if (dsp_poll) { - if (dsp_poll > MAX_POLL) { - printk(KERN_ERR "%s: Wrong poll value (%d), use %d " - "maximum.\n", __func__, poll, MAX_POLL); - err = -EINVAL; - return err; - } - if (dsp_poll < 8) { - printk(KERN_ERR "%s: Wrong poll value (%d), use 8 " - "minimum.\n", __func__, dsp_poll); - err = -EINVAL; - return err; - } - dsp_tics = poll * HZ / 8000; - if (dsp_tics * 8000 != poll * HZ) { - printk(KERN_INFO "mISDN_dsp: Cannot clock every %d " - "samples (0,125 ms). It is not a multiple of " - "%d HZ.\n", poll, HZ); - err = -EINVAL; - return err; - } - } else { - poll = 8; - while (poll <= MAX_POLL) { - tics = (poll * HZ) / 8000; - if (tics * 8000 == poll * HZ) { - dsp_tics = tics; - dsp_poll = poll; - if (poll >= 64) - break; - } - poll++; - } - } - if (dsp_poll == 0) { - printk(KERN_INFO "mISDN_dsp: There is no multiple of kernel " - "clock that equals exactly the duration of 8-256 " - "samples. (Choose kernel clock speed like 100, 250, " - "300, 1000)\n"); - err = -EINVAL; - return err; - } - printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals " - "%d jiffies.\n", dsp_poll, dsp_tics); - - /* init conversion tables */ - dsp_audio_generate_law_tables(); - dsp_silence = (dsp_options & DSP_OPT_ULAW) ? 0xff : 0x2a; - dsp_audio_law_to_s32 = (dsp_options & DSP_OPT_ULAW) ? - dsp_audio_ulaw_to_s32 : dsp_audio_alaw_to_s32; - dsp_audio_generate_s2law_table(); - dsp_audio_generate_seven(); - dsp_audio_generate_mix_table(); - if (dsp_options & DSP_OPT_ULAW) - dsp_audio_generate_ulaw_samples(); - dsp_audio_generate_volume_changes(); - - err = dsp_pipeline_module_init(); - if (err) { - printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, " - "error(%d)\n", err); - return err; - } - - err = mISDN_register_Bprotocol(&DSP); - if (err) { - printk(KERN_ERR "Can't register %s error(%d)\n", DSP.name, err); - return err; - } - - /* set sample timer */ - timer_setup(&dsp_spl_tl, dsp_cmx_send, 0); - dsp_spl_tl.expires = jiffies + dsp_tics; - dsp_spl_jiffies = dsp_spl_tl.expires; - add_timer(&dsp_spl_tl); - - return 0; -} - - -static void __exit dsp_cleanup(void) -{ - mISDN_unregister_Bprotocol(&DSP); - - timer_delete_sync(&dsp_spl_tl); - - if (!list_empty(&dsp_ilist)) { - printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not " - "empty.\n"); - } - if (!list_empty(&conf_ilist)) { - printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not " - "all memory freed.\n"); - } - - dsp_pipeline_module_exit(); -} - -module_init(dsp_init); -module_exit(dsp_cleanup); diff --git a/drivers/isdn/mISDN/dsp_dtmf.c b/drivers/isdn/mISDN/dsp_dtmf.c deleted file mode 100644 index 642f30be5ce2..000000000000 --- a/drivers/isdn/mISDN/dsp_dtmf.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * DTMF decoder. - * - * Copyright by Andreas Eversberg (jolly@eversberg.eu) - * based on different decoders such as ISDN4Linux - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include "core.h" -#include "dsp.h" - -#define NCOEFF 8 /* number of frequencies to be analyzed */ - -/* For DTMF recognition: - * 2 * cos(2 * PI * k / N) precalculated for all k - */ -static u64 cos2pik[NCOEFF] = -{ - /* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */ - 55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630 -}; - -/* digit matrix */ -static char dtmf_matrix[4][4] = -{ - {'1', '2', '3', 'A'}, - {'4', '5', '6', 'B'}, - {'7', '8', '9', 'C'}, - {'*', '0', '#', 'D'} -}; - -/* dtmf detection using goertzel algorithm - * init function - */ -void dsp_dtmf_goertzel_init(struct dsp *dsp) -{ - dsp->dtmf.size = 0; - dsp->dtmf.lastwhat = '\0'; - dsp->dtmf.lastdigit = '\0'; - dsp->dtmf.count = 0; -} - -/* check for hardware or software features - */ -void dsp_dtmf_hardware(struct dsp *dsp) -{ - int hardware = 1; - - if (!dsp->dtmf.enable) - return; - - if (!dsp->features.hfc_dtmf) - hardware = 0; - - /* check for volume change */ - if (dsp->tx_volume) { - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " - "because tx_volume is changed\n", - __func__, dsp->name); - hardware = 0; - } - if (dsp->rx_volume) { - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " - "because rx_volume is changed\n", - __func__, dsp->name); - hardware = 0; - } - /* check if encryption is enabled */ - if (dsp->bf_enable) { - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " - "because encryption is enabled\n", - __func__, dsp->name); - hardware = 0; - } - /* check if pipeline exists */ - if (dsp->pipeline.inuse) { - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " - "because pipeline exists.\n", - __func__, dsp->name); - hardware = 0; - } - - dsp->dtmf.hardware = hardware; - dsp->dtmf.software = !hardware; -} - - -/************************************************************* - * calculate the coefficients of the given sample and decode * - *************************************************************/ - -/* the given sample is decoded. if the sample is not long enough for a - * complete frame, the decoding is finished and continued with the next - * call of this function. - * - * the algorithm is very good for detection with a minimum of errors. i - * tested it allot. it even works with very short tones (40ms). the only - * disadvantage is, that it doesn't work good with different volumes of both - * tones. this will happen, if accoustically coupled dialers are used. - * it sometimes detects tones during speech, which is normal for decoders. - * use sequences to given commands during calls. - * - * dtmf - points to a structure of the current dtmf state - * spl and len - the sample - * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder - */ - -u8 -*dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, int fmt) -{ - u8 what; - int size; - signed short *buf; - s32 sk, sk1, sk2; - int k, n, i; - s32 *hfccoeff; - s32 result[NCOEFF], tresh, treshl; - int lowgroup, highgroup; - s64 cos2pik_; - - dsp->dtmf.digits[0] = '\0'; - - /* Note: The function will loop until the buffer has not enough samples - * left to decode a full frame. - */ -again: - /* convert samples */ - size = dsp->dtmf.size; - buf = dsp->dtmf.buffer; - switch (fmt) { - case 0: /* alaw */ - case 1: /* ulaw */ - while (size < DSP_DTMF_NPOINTS && len) { - buf[size++] = dsp_audio_law_to_s32[*data++]; - len--; - } - break; - - case 2: /* HFC coefficients */ - default: - if (len < 64) { - if (len > 0) - printk(KERN_ERR "%s: coefficients have invalid " - "size. (is=%d < must=%d)\n", - __func__, len, 64); - return dsp->dtmf.digits; - } - hfccoeff = (s32 *)data; - for (k = 0; k < NCOEFF; k++) { - sk2 = (*hfccoeff++) >> 4; - sk = (*hfccoeff++) >> 4; - if (sk > 32767 || sk < -32767 || sk2 > 32767 - || sk2 < -32767) - printk(KERN_WARNING - "DTMF-Detection overflow\n"); - /* compute |X(k)|**2 */ - result[k] = - (sk * sk) - - (((cos2pik[k] * sk) >> 15) * sk2) + - (sk2 * sk2); - } - data += 64; - len -= 64; - goto coefficients; - break; - } - dsp->dtmf.size = size; - - if (size < DSP_DTMF_NPOINTS) - return dsp->dtmf.digits; - - dsp->dtmf.size = 0; - - /* now we have a full buffer of signed long samples - we do goertzel */ - for (k = 0; k < NCOEFF; k++) { - sk = 0; - sk1 = 0; - sk2 = 0; - buf = dsp->dtmf.buffer; - cos2pik_ = cos2pik[k]; - for (n = 0; n < DSP_DTMF_NPOINTS; n++) { - sk = ((cos2pik_ * sk1) >> 15) - sk2 + (*buf++); - sk2 = sk1; - sk1 = sk; - } - sk >>= 8; - sk2 >>= 8; - if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767) - printk(KERN_WARNING "DTMF-Detection overflow\n"); - /* compute |X(k)|**2 */ - result[k] = - (sk * sk) - - (((cos2pik[k] * sk) >> 15) * sk2) + - (sk2 * sk2); - } - - /* our (squared) coefficients have been calculated, we need to process - * them. - */ -coefficients: - tresh = 0; - for (i = 0; i < NCOEFF; i++) { - if (result[i] < 0) - result[i] = 0; - if (result[i] > dsp->dtmf.treshold) { - if (result[i] > tresh) - tresh = result[i]; - } - } - - if (tresh == 0) { - what = 0; - goto storedigit; - } - - if (dsp_debug & DEBUG_DSP_DTMFCOEFF) { - s32 tresh_100 = tresh/100; - - if (tresh_100 == 0) { - tresh_100 = 1; - printk(KERN_DEBUG - "tresh(%d) too small set tresh/100 to 1\n", - tresh); - } - printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d" - " tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n", - result[0] / 10000, result[1] / 10000, result[2] / 10000, - result[3] / 10000, result[4] / 10000, result[5] / 10000, - result[6] / 10000, result[7] / 10000, tresh / 10000, - result[0] / (tresh_100), result[1] / (tresh_100), - result[2] / (tresh_100), result[3] / (tresh_100), - result[4] / (tresh_100), result[5] / (tresh_100), - result[6] / (tresh_100), result[7] / (tresh_100)); - } - - /* calc digit (lowgroup/highgroup) */ - lowgroup = -1; - highgroup = -1; - treshl = tresh >> 3; /* tones which are not on, must be below 9 dB */ - tresh = tresh >> 2; /* touchtones must match within 6 dB */ - for (i = 0; i < NCOEFF; i++) { - if (result[i] < treshl) - continue; /* ignore */ - if (result[i] < tresh) { - lowgroup = -1; - highgroup = -1; - break; /* noise in between */ - } - /* good level found. This is allowed only one time per group */ - if (i < NCOEFF / 2) { - /* lowgroup */ - if (lowgroup >= 0) { - /* Bad. Another tone found. */ - lowgroup = -1; - break; - } else - lowgroup = i; - } else { - /* higroup */ - if (highgroup >= 0) { - /* Bad. Another tone found. */ - highgroup = -1; - break; - } else - highgroup = i - (NCOEFF / 2); - } - } - - /* get digit or null */ - what = 0; - if (lowgroup >= 0 && highgroup >= 0) - what = dtmf_matrix[lowgroup][highgroup]; - -storedigit: - if (what && (dsp_debug & DEBUG_DSP_DTMF)) - printk(KERN_DEBUG "DTMF what: %c\n", what); - - if (dsp->dtmf.lastwhat != what) - dsp->dtmf.count = 0; - - /* the tone (or no tone) must remain 3 times without change */ - if (dsp->dtmf.count == 2) { - if (dsp->dtmf.lastdigit != what) { - dsp->dtmf.lastdigit = what; - if (what) { - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "DTMF digit: %c\n", - what); - if ((strlen(dsp->dtmf.digits) + 1) - < sizeof(dsp->dtmf.digits)) { - dsp->dtmf.digits[strlen( - dsp->dtmf.digits) + 1] = '\0'; - dsp->dtmf.digits[strlen( - dsp->dtmf.digits)] = what; - } - } - } - } else - dsp->dtmf.count++; - - dsp->dtmf.lastwhat = what; - - goto again; -} diff --git a/drivers/isdn/mISDN/dsp_ecdis.h b/drivers/isdn/mISDN/dsp_ecdis.h deleted file mode 100644 index 4bcdf321875d..000000000000 --- a/drivers/isdn/mISDN/dsp_ecdis.h +++ /dev/null @@ -1,96 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * SpanDSP - a series of DSP components for telephony - * - * ec_disable_detector.h - A detector which should eventually meet the - * G.164/G.165 requirements for detecting the - * 2100Hz echo cancellor disable tone. - * - * Written by Steve Underwood - * - * Copyright (C) 2001 Steve Underwood - * - * All rights reserved. - */ - -#include "dsp_biquad.h" - -struct ec_disable_detector_state { - struct biquad2_state notch; - int notch_level; - int channel_level; - int tone_present; - int tone_cycle_duration; - int good_cycles; - int hit; -}; - - -#define FALSE 0 -#define TRUE (!FALSE) - -static inline void -echo_can_disable_detector_init(struct ec_disable_detector_state *det) -{ - /* Elliptic notch */ - /* This is actually centred at 2095Hz, but gets the balance we want, due - to the asymmetric walls of the notch */ - biquad2_init(&det->notch, - (int32_t)(-0.7600000 * 32768.0), - (int32_t)(-0.1183852 * 32768.0), - (int32_t)(-0.5104039 * 32768.0), - (int32_t)(0.1567596 * 32768.0), - (int32_t)(1.0000000 * 32768.0)); - - det->channel_level = 0; - det->notch_level = 0; - det->tone_present = FALSE; - det->tone_cycle_duration = 0; - det->good_cycles = 0; - det->hit = 0; -} -/*- End of function --------------------------------------------------------*/ - -static inline int -echo_can_disable_detector_update(struct ec_disable_detector_state *det, - int16_t amp) -{ - int16_t notched; - - notched = biquad2(&det->notch, amp); - /* Estimate the overall energy in the channel, and the energy in - the notch (i.e. overall channel energy - tone energy => noise). - Use abs instead of multiply for speed (is it really faster?). - Damp the overall energy a little more for a stable result. - Damp the notch energy a little less, so we don't damp out the - blip every time the phase reverses */ - det->channel_level += ((abs(amp) - det->channel_level) >> 5); - det->notch_level += ((abs(notched) - det->notch_level) >> 4); - if (det->channel_level > 280) { - /* There is adequate energy in the channel. - Is it mostly at 2100Hz? */ - if (det->notch_level * 6 < det->channel_level) { - /* The notch says yes, so we have the tone. */ - if (!det->tone_present) { - /* Do we get a kick every 450+-25ms? */ - if (det->tone_cycle_duration >= 425 * 8 - && det->tone_cycle_duration <= 475 * 8) { - det->good_cycles++; - if (det->good_cycles > 2) - det->hit = TRUE; - } - det->tone_cycle_duration = 0; - } - det->tone_present = TRUE; - } else - det->tone_present = FALSE; - det->tone_cycle_duration++; - } else { - det->tone_present = FALSE; - det->tone_cycle_duration = 0; - det->good_cycles = 0; - } - return det->hit; -} -/*- End of function --------------------------------------------------------*/ -/*- End of file ------------------------------------------------------------*/ diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c deleted file mode 100644 index 0cd216e28f00..000000000000 --- a/drivers/isdn/mISDN/dsp_hwec.c +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * dsp_hwec.c: - * builtin mISDN dsp pipeline element for enabling the hw echocanceller - * - * Copyright (C) 2007, Nadi Sarrar - * - * Nadi Sarrar - */ - -#include -#include -#include -#include -#include "core.h" -#include "dsp.h" -#include "dsp_hwec.h" - -static struct mISDN_dsp_element_arg args[] = { - { "deftaps", "128", "Set the number of taps of cancellation." }, -}; - -static struct mISDN_dsp_element dsp_hwec_p = { - .name = "hwec", - .new = NULL, - .free = NULL, - .process_tx = NULL, - .process_rx = NULL, - .num_args = ARRAY_SIZE(args), - .args = args, -}; -struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p; - -void dsp_hwec_enable(struct dsp *dsp, const char *arg) -{ - int deftaps = 128, - len; - struct mISDN_ctrl_req cq; - - if (!dsp) { - printk(KERN_ERR "%s: failed to enable hwec: dsp is NULL\n", - __func__); - return; - } - - if (!arg) - goto _do; - - len = strlen(arg); - if (!len) - goto _do; - - { - char *dup, *next, *tok, *name, *val; - int tmp; - - dup = next = kstrdup(arg, GFP_ATOMIC); - if (!dup) - return; - - while ((tok = strsep(&next, ","))) { - if (!strlen(tok)) - continue; - name = strsep(&tok, "="); - val = tok; - - if (!val) - continue; - - if (!strcmp(name, "deftaps")) { - if (sscanf(val, "%d", &tmp) == 1) - deftaps = tmp; - } - } - - kfree(dup); - } - -_do: - printk(KERN_DEBUG "%s: enabling hwec with deftaps=%d\n", - __func__, deftaps); - memset(&cq, 0, sizeof(cq)); - cq.op = MISDN_CTRL_HFC_ECHOCAN_ON; - cq.p1 = deftaps; - if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { - printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", - __func__); - return; - } -} - -void dsp_hwec_disable(struct dsp *dsp) -{ - struct mISDN_ctrl_req cq; - - if (!dsp) { - printk(KERN_ERR "%s: failed to disable hwec: dsp is NULL\n", - __func__); - return; - } - - printk(KERN_DEBUG "%s: disabling hwec\n", __func__); - memset(&cq, 0, sizeof(cq)); - cq.op = MISDN_CTRL_HFC_ECHOCAN_OFF; - if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { - printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", - __func__); - return; - } -} - -int dsp_hwec_init(void) -{ - mISDN_dsp_element_register(dsp_hwec); - - return 0; -} - -void dsp_hwec_exit(void) -{ - mISDN_dsp_element_unregister(dsp_hwec); -} diff --git a/drivers/isdn/mISDN/dsp_hwec.h b/drivers/isdn/mISDN/dsp_hwec.h deleted file mode 100644 index c9cb0ea249da..000000000000 --- a/drivers/isdn/mISDN/dsp_hwec.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * dsp_hwec.h - */ - -extern struct mISDN_dsp_element *dsp_hwec; -extern void dsp_hwec_enable(struct dsp *dsp, const char *arg); -extern void dsp_hwec_disable(struct dsp *dsp); -extern int dsp_hwec_init(void); -extern void dsp_hwec_exit(void); diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c deleted file mode 100644 index 55693dc7206b..000000000000 --- a/drivers/isdn/mISDN/dsp_pipeline.c +++ /dev/null @@ -1,300 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * dsp_pipeline.c: pipelined audio processing - * - * Copyright (C) 2007, Nadi Sarrar - * - * Nadi Sarrar - */ - -#include -#include -#include -#include -#include -#include -#include -#include "dsp.h" -#include "dsp_hwec.h" - -struct dsp_pipeline_entry { - struct mISDN_dsp_element *elem; - void *p; - struct list_head list; -}; -struct dsp_element_entry { - struct mISDN_dsp_element *elem; - struct device dev; - struct list_head list; -}; - -static LIST_HEAD(dsp_elements); - -/* sysfs */ -static const struct class elements_class = { - .name = "dsp_pipeline", -}; - -static ssize_t -attr_show_args(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct mISDN_dsp_element *elem = dev_get_drvdata(dev); - int i; - char *p = buf; - - *buf = 0; - for (i = 0; i < elem->num_args; i++) - p += sprintf(p, "Name: %s\n%s%s%sDescription: %s\n\n", - elem->args[i].name, - elem->args[i].def ? "Default: " : "", - elem->args[i].def ? elem->args[i].def : "", - elem->args[i].def ? "\n" : "", - elem->args[i].desc); - - return p - buf; -} - -static struct device_attribute element_attributes[] = { - __ATTR(args, 0444, attr_show_args, NULL), -}; - -static void -mISDN_dsp_dev_release(struct device *dev) -{ - struct dsp_element_entry *entry = - container_of(dev, struct dsp_element_entry, dev); - list_del(&entry->list); - kfree(entry); -} - -int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) -{ - struct dsp_element_entry *entry; - int ret, i; - - if (!elem) - return -EINVAL; - - entry = kzalloc_obj(struct dsp_element_entry, GFP_ATOMIC); - if (!entry) - return -ENOMEM; - - INIT_LIST_HEAD(&entry->list); - entry->elem = elem; - - entry->dev.class = &elements_class; - entry->dev.release = mISDN_dsp_dev_release; - dev_set_drvdata(&entry->dev, elem); - dev_set_name(&entry->dev, "%s", elem->name); - ret = device_register(&entry->dev); - if (ret) { - printk(KERN_ERR "%s: failed to register %s\n", - __func__, elem->name); - goto err1; - } - list_add_tail(&entry->list, &dsp_elements); - - for (i = 0; i < ARRAY_SIZE(element_attributes); ++i) { - ret = device_create_file(&entry->dev, - &element_attributes[i]); - if (ret) { - printk(KERN_ERR "%s: failed to create device file\n", - __func__); - goto err2; - } - } - - return 0; - -err2: - device_unregister(&entry->dev); - return ret; -err1: - put_device(&entry->dev); - return ret; -} -EXPORT_SYMBOL(mISDN_dsp_element_register); - -void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem) -{ - struct dsp_element_entry *entry, *n; - - if (!elem) - return; - - list_for_each_entry_safe(entry, n, &dsp_elements, list) - if (entry->elem == elem) { - device_unregister(&entry->dev); - return; - } - printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name); -} -EXPORT_SYMBOL(mISDN_dsp_element_unregister); - -int dsp_pipeline_module_init(void) -{ - int err; - - err = class_register(&elements_class); - if (err) - return err; - - dsp_hwec_init(); - - return 0; -} - -void dsp_pipeline_module_exit(void) -{ - struct dsp_element_entry *entry, *n; - - dsp_hwec_exit(); - - class_unregister(&elements_class); - - list_for_each_entry_safe(entry, n, &dsp_elements, list) { - list_del(&entry->list); - printk(KERN_WARNING "%s: element was still registered: %s\n", - __func__, entry->elem->name); - kfree(entry); - } -} - -int dsp_pipeline_init(struct dsp_pipeline *pipeline) -{ - if (!pipeline) - return -EINVAL; - - INIT_LIST_HEAD(&pipeline->list); - - return 0; -} - -static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline) -{ - struct dsp_pipeline_entry *entry, *n; - - list_for_each_entry_safe(entry, n, &pipeline->list, list) { - list_del(&entry->list); - if (entry->elem == dsp_hwec) - dsp_hwec_disable(container_of(pipeline, struct dsp, - pipeline)); - else - entry->elem->free(entry->p); - kfree(entry); - } -} - -void dsp_pipeline_destroy(struct dsp_pipeline *pipeline) -{ - - if (!pipeline) - return; - - _dsp_pipeline_destroy(pipeline); -} - -int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) -{ - int found = 0; - char *dup, *next, *tok, *name, *args; - struct dsp_element_entry *entry, *n; - struct dsp_pipeline_entry *pipeline_entry; - struct mISDN_dsp_element *elem; - - if (!pipeline) - return -EINVAL; - - if (!list_empty(&pipeline->list)) - _dsp_pipeline_destroy(pipeline); - - dup = next = kstrdup(cfg, GFP_ATOMIC); - if (!dup) - return 0; - while ((tok = strsep(&next, "|"))) { - if (!strlen(tok)) - continue; - name = strsep(&tok, "("); - args = strsep(&tok, ")"); - if (args && !*args) - args = NULL; - - list_for_each_entry_safe(entry, n, &dsp_elements, list) - if (!strcmp(entry->elem->name, name)) { - elem = entry->elem; - - pipeline_entry = kmalloc_obj(struct dsp_pipeline_entry, - GFP_ATOMIC); - if (!pipeline_entry) { - printk(KERN_ERR "%s: failed to add " - "entry to pipeline: %s (out of " - "memory)\n", __func__, elem->name); - goto _out; - } - pipeline_entry->elem = elem; - - if (elem == dsp_hwec) { - /* This is a hack to make the hwec - available as a pipeline module */ - dsp_hwec_enable(container_of(pipeline, - struct dsp, pipeline), args); - list_add_tail(&pipeline_entry->list, - &pipeline->list); - } else { - pipeline_entry->p = elem->new(args); - if (pipeline_entry->p) { - list_add_tail(&pipeline_entry-> - list, &pipeline->list); - } else { - printk(KERN_ERR "%s: failed " - "to add entry to pipeline: " - "%s (new() returned NULL)\n", - __func__, elem->name); - kfree(pipeline_entry); - } - } - found = 1; - break; - } - - if (found) - found = 0; - else - printk(KERN_ERR "%s: element not found, skipping: " - "%s\n", __func__, name); - } - -_out: - if (!list_empty(&pipeline->list)) - pipeline->inuse = 1; - else - pipeline->inuse = 0; - - kfree(dup); - return 0; -} - -void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len) -{ - struct dsp_pipeline_entry *entry; - - if (!pipeline) - return; - - list_for_each_entry(entry, &pipeline->list, list) - if (entry->elem->process_tx) - entry->elem->process_tx(entry->p, data, len); -} - -void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len, - unsigned int txlen) -{ - struct dsp_pipeline_entry *entry; - - if (!pipeline) - return; - - list_for_each_entry_reverse(entry, &pipeline->list, list) - if (entry->elem->process_rx) - entry->elem->process_rx(entry->p, data, len, txlen); -} diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c deleted file mode 100644 index fa7813ae8d97..000000000000 --- a/drivers/isdn/mISDN/dsp_tones.c +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Audio support data for ISDN4Linux. - * - * Copyright Andreas Eversberg (jolly@eversberg.eu) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include -#include "core.h" -#include "dsp.h" - - -#define DATA_S sample_silence -#define SIZE_S (&sizeof_silence) -#define DATA_GA sample_german_all -#define SIZE_GA (&sizeof_german_all) -#define DATA_GO sample_german_old -#define SIZE_GO (&sizeof_german_old) -#define DATA_DT sample_american_dialtone -#define SIZE_DT (&sizeof_american_dialtone) -#define DATA_RI sample_american_ringing -#define SIZE_RI (&sizeof_american_ringing) -#define DATA_BU sample_american_busy -#define SIZE_BU (&sizeof_american_busy) -#define DATA_S1 sample_special1 -#define SIZE_S1 (&sizeof_special1) -#define DATA_S2 sample_special2 -#define SIZE_S2 (&sizeof_special2) -#define DATA_S3 sample_special3 -#define SIZE_S3 (&sizeof_special3) - -/***************/ -/* tones loops */ -/***************/ - -/* all tones are alaw encoded */ -/* the last sample+1 is in phase with the first sample. the error is low */ - -static u8 sample_german_all[] = { - 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, - 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, - 0xdc, 0xfc, 0x6c, - 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, - 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, - 0xdc, 0xfc, 0x6c, - 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, - 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, - 0xdc, 0xfc, 0x6c, - 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, - 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, - 0xdc, 0xfc, 0x6c, -}; -static u32 sizeof_german_all = sizeof(sample_german_all); - -static u8 sample_german_old[] = { - 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, - 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, - 0x8c, - 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, - 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, - 0x8c, - 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, - 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, - 0x8c, - 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, - 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, - 0x8c, -}; -static u32 sizeof_german_old = sizeof(sample_german_old); - -static u8 sample_american_dialtone[] = { - 0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c, - 0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d, - 0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0, - 0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67, - 0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67, - 0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef, - 0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8, - 0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61, - 0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e, - 0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30, - 0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d, - 0x6d, 0x91, 0x19, -}; -static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone); - -static u8 sample_american_ringing[] = { - 0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90, - 0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed, - 0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c, - 0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d, - 0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec, - 0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11, - 0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00, - 0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39, - 0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6, - 0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3, - 0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b, - 0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f, - 0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56, - 0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59, - 0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30, - 0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d, - 0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c, - 0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd, - 0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc, - 0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d, - 0x4d, 0xbd, 0x0d, 0xad, 0xe1, -}; -static u32 sizeof_american_ringing = sizeof(sample_american_ringing); - -static u8 sample_american_busy[] = { - 0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66, - 0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96, - 0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57, - 0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f, - 0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40, - 0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d, - 0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c, - 0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d, - 0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40, - 0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7, - 0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a, - 0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7, - 0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40, - 0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d, - 0x4d, 0x4d, 0x6d, 0x01, -}; -static u32 sizeof_american_busy = sizeof(sample_american_busy); - -static u8 sample_special1[] = { - 0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d, - 0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd, - 0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd, - 0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd, - 0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed, - 0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41, - 0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7, - 0x6d, 0xbd, 0x2d, -}; -static u32 sizeof_special1 = sizeof(sample_special1); - -static u8 sample_special2[] = { - 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, - 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, - 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, - 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, - 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, - 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, - 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, - 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, - 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, - 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, -}; -static u32 sizeof_special2 = sizeof(sample_special2); - -static u8 sample_special3[] = { - 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, - 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, - 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, - 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, - 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, - 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, - 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, - 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, - 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, - 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, -}; -static u32 sizeof_special3 = sizeof(sample_special3); - -static u8 sample_silence[] = { - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, -}; -static u32 sizeof_silence = sizeof(sample_silence); - -struct tones_samples { - u32 *len; - u8 *data; -}; -static struct -tones_samples samples[] = { - {&sizeof_german_all, sample_german_all}, - {&sizeof_german_old, sample_german_old}, - {&sizeof_american_dialtone, sample_american_dialtone}, - {&sizeof_american_ringing, sample_american_ringing}, - {&sizeof_american_busy, sample_american_busy}, - {&sizeof_special1, sample_special1}, - {&sizeof_special2, sample_special2}, - {&sizeof_special3, sample_special3}, - {NULL, NULL}, -}; - -/*********************************** - * generate ulaw from alaw samples * - ***********************************/ - -void -dsp_audio_generate_ulaw_samples(void) -{ - int i, j; - - i = 0; - while (samples[i].len) { - j = 0; - while (j < (*samples[i].len)) { - samples[i].data[j] = - dsp_audio_alaw_to_ulaw[samples[i].data[j]]; - j++; - } - i++; - } -} - - -/**************************** - * tone sequence definition * - ****************************/ - -static struct pattern { - int tone; - u8 *data[10]; - u32 *siz[10]; - u32 seq[10]; -} pattern[] = { - {TONE_GERMAN_DIALTONE, - {DATA_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDDIALTONE, - {DATA_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_AMERICAN_DIALTONE, - {DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_DIALPBX, - {DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, - NULL}, - {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, - NULL}, - {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDDIALPBX, - {DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, - NULL}, - {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, - NULL}, - {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, - - {TONE_AMERICAN_DIALPBX, - {DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, NULL, NULL, NULL, - NULL}, - {SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, NULL, NULL, NULL, - NULL}, - {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, - - {TONE_GERMAN_RINGING, - {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDRINGING, - {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_AMERICAN_RINGING, - {DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_RINGPBX, - {DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDRINGPBX, - {DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, - - {TONE_AMERICAN_RINGPBX, - {DATA_RI, DATA_S, DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_BUSY, - {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDBUSY, - {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_AMERICAN_BUSY, - {DATA_BU, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_BU, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_HANGUP, - {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDHANGUP, - {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_AMERICAN_HANGUP, - {DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_SPECIAL_INFO, - {DATA_S1, DATA_S2, DATA_S3, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_GASSENBESETZT, - {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_AUFSCHALTTON, - {DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} }, - - {0, - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, -}; - -/****************** - * copy tone data * - ******************/ - -/* an sk_buff is generated from the number of samples needed. - * the count will be changed and may begin from 0 each pattern period. - * the clue is to precalculate the pointers and legths to use only one - * memcpy per function call, or two memcpy if the tone sequence changes. - * - * pattern - the type of the pattern - * count - the sample from the beginning of the pattern (phase) - * len - the number of bytes - * - * return - the sk_buff with the sample - * - * if tones has finished (e.g. knocking tone), dsp->tones is turned off - */ -void dsp_tone_copy(struct dsp *dsp, u8 *data, int len) -{ - int index, count, start, num; - struct pattern *pat; - struct dsp_tone *tone = &dsp->tone; - - /* if we have no tone, we copy silence */ - if (!tone->tone) { - memset(data, dsp_silence, len); - return; - } - - /* process pattern */ - pat = (struct pattern *)tone->pattern; - /* points to the current pattern */ - index = tone->index; /* gives current sequence index */ - count = tone->count; /* gives current sample */ - - /* copy sample */ - while (len) { - /* find sample to start with */ - while (42) { - /* wrap around */ - if (!pat->seq[index]) { - count = 0; - index = 0; - } - /* check if we are currently playing this tone */ - if (count < pat->seq[index]) - break; - if (dsp_debug & DEBUG_DSP_TONE) - printk(KERN_DEBUG "%s: reaching next sequence " - "(index=%d)\n", __func__, index); - count -= pat->seq[index]; - index++; - } - /* calculate start and number of samples */ - start = count % (*(pat->siz[index])); - num = len; - if (num + count > pat->seq[index]) - num = pat->seq[index] - count; - if (num + start > (*(pat->siz[index]))) - num = (*(pat->siz[index])) - start; - /* copy memory */ - memcpy(data, pat->data[index] + start, num); - /* reduce length */ - data += num; - count += num; - len -= num; - } - tone->index = index; - tone->count = count; - - /* return sk_buff */ - return; -} - - -/******************************* - * send HW message to hfc card * - *******************************/ - -static void -dsp_tone_hw_message(struct dsp *dsp, u8 *sample, int len) -{ - struct sk_buff *nskb; - - /* unlocking is not required, because we don't expect a response */ - nskb = _alloc_mISDN_skb(PH_CONTROL_REQ, - (len) ? HFC_SPL_LOOP_ON : HFC_SPL_LOOP_OFF, len, sample, - GFP_ATOMIC); - if (nskb) { - if (dsp->ch.peer) { - if (dsp->ch.recv(dsp->ch.peer, nskb)) - dev_kfree_skb(nskb); - } else - dev_kfree_skb(nskb); - } -} - - -/***************** - * timer expires * - *****************/ -void -dsp_tone_timeout(struct timer_list *t) -{ - struct dsp *dsp = timer_container_of(dsp, t, tone.tl); - struct dsp_tone *tone = &dsp->tone; - struct pattern *pat = (struct pattern *)tone->pattern; - int index = tone->index; - - if (!tone->tone) - return; - - index++; - if (!pat->seq[index]) - index = 0; - tone->index = index; - - /* set next tone */ - if (pat->data[index] == DATA_S) - dsp_tone_hw_message(dsp, NULL, 0); - else - dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index])); - /* set timer */ - tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000; - add_timer(&tone->tl); -} - - -/******************** - * set/release tone * - ********************/ - -/* - * tones are relaized by streaming or by special loop commands if supported - * by hardware. when hardware is used, the patterns will be controlled by - * timers. - */ -int -dsp_tone(struct dsp *dsp, int tone) -{ - struct pattern *pat; - int i; - struct dsp_tone *tonet = &dsp->tone; - - tonet->software = 0; - tonet->hardware = 0; - - /* we turn off the tone */ - if (!tone) { - if (dsp->features.hfc_loops && timer_pending(&tonet->tl)) - timer_delete(&tonet->tl); - if (dsp->features.hfc_loops) - dsp_tone_hw_message(dsp, NULL, 0); - tonet->tone = 0; - return 0; - } - - pat = NULL; - i = 0; - while (pattern[i].tone) { - if (pattern[i].tone == tone) { - pat = &pattern[i]; - break; - } - i++; - } - if (!pat) { - printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone); - return -EINVAL; - } - if (dsp_debug & DEBUG_DSP_TONE) - printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n", - __func__, tone, 0); - tonet->tone = tone; - tonet->pattern = pat; - tonet->index = 0; - tonet->count = 0; - - if (dsp->features.hfc_loops) { - tonet->hardware = 1; - /* set first tone */ - dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0])); - /* set timer */ - if (timer_pending(&tonet->tl)) - timer_delete(&tonet->tl); - tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000; - add_timer(&tonet->tl); - } else { - tonet->software = 1; - } - - return 0; -} diff --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c deleted file mode 100644 index 825b686496d2..000000000000 --- a/drivers/isdn/mISDN/fsm.c +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * finite state machine implementation - * - * Author Karsten Keil - * - * Thanks to Jan den Ouden - * Fritz Elfert - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include -#include -#include "fsm.h" - -#define FSM_TIMER_DEBUG 0 - -int -mISDN_FsmNew(struct Fsm *fsm, - struct FsmNode *fnlist, int fncount) -{ - int i; - - fsm->jumpmatrix = - kzalloc(array3_size(sizeof(FSMFNPTR), fsm->state_count, - fsm->event_count), - GFP_KERNEL); - if (fsm->jumpmatrix == NULL) - return -ENOMEM; - - for (i = 0; i < fncount; i++) - if ((fnlist[i].state >= fsm->state_count) || - (fnlist[i].event >= fsm->event_count)) { - printk(KERN_ERR - "mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n", - i, (long)fnlist[i].state, (long)fsm->state_count, - (long)fnlist[i].event, (long)fsm->event_count); - } else - fsm->jumpmatrix[fsm->state_count * fnlist[i].event + - fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; - return 0; -} -EXPORT_SYMBOL(mISDN_FsmNew); - -void -mISDN_FsmFree(struct Fsm *fsm) -{ - kfree((void *) fsm->jumpmatrix); -} -EXPORT_SYMBOL(mISDN_FsmFree); - -int -mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg) -{ - FSMFNPTR r; - - if ((fi->state >= fi->fsm->state_count) || - (event >= fi->fsm->event_count)) { - printk(KERN_ERR - "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n", - (long)fi->state, (long)fi->fsm->state_count, event, - (long)fi->fsm->event_count); - return 1; - } - r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; - if (r) { - if (fi->debug) - fi->printdebug(fi, "State %s Event %s", - fi->fsm->strState[fi->state], - fi->fsm->strEvent[event]); - r(fi, event, arg); - return 0; - } else { - if (fi->debug) - fi->printdebug(fi, "State %s Event %s no action", - fi->fsm->strState[fi->state], - fi->fsm->strEvent[event]); - return 1; - } -} -EXPORT_SYMBOL(mISDN_FsmEvent); - -void -mISDN_FsmChangeState(struct FsmInst *fi, int newstate) -{ - fi->state = newstate; - if (fi->debug) - fi->printdebug(fi, "ChangeState %s", - fi->fsm->strState[newstate]); -} -EXPORT_SYMBOL(mISDN_FsmChangeState); - -static void -FsmExpireTimer(struct timer_list *t) -{ - struct FsmTimer *ft = timer_container_of(ft, t, tl); -#if FSM_TIMER_DEBUG - if (ft->fi->debug) - ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft); -#endif - mISDN_FsmEvent(ft->fi, ft->event, ft->arg); -} - -void -mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) -{ - ft->fi = fi; -#if FSM_TIMER_DEBUG - if (ft->fi->debug) - ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft); -#endif - timer_setup(&ft->tl, FsmExpireTimer, 0); -} -EXPORT_SYMBOL(mISDN_FsmInitTimer); - -void -mISDN_FsmDelTimer(struct FsmTimer *ft, int where) -{ -#if FSM_TIMER_DEBUG - if (ft->fi->debug) - ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d", - (long) ft, where); -#endif - timer_delete(&ft->tl); -} -EXPORT_SYMBOL(mISDN_FsmDelTimer); - -int -mISDN_FsmAddTimer(struct FsmTimer *ft, - int millisec, int event, void *arg, int where) -{ - -#if FSM_TIMER_DEBUG - if (ft->fi->debug) - ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d", - (long) ft, millisec, where); -#endif - - if (timer_pending(&ft->tl)) { - if (ft->fi->debug) { - printk(KERN_WARNING - "mISDN_FsmAddTimer: timer already active!\n"); - ft->fi->printdebug(ft->fi, - "mISDN_FsmAddTimer already active!"); - } - return -1; - } - ft->event = event; - ft->arg = arg; - ft->tl.expires = jiffies + (millisec * HZ) / 1000; - add_timer(&ft->tl); - return 0; -} -EXPORT_SYMBOL(mISDN_FsmAddTimer); - -void -mISDN_FsmRestartTimer(struct FsmTimer *ft, - int millisec, int event, void *arg, int where) -{ - -#if FSM_TIMER_DEBUG - if (ft->fi->debug) - ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d", - (long) ft, millisec, where); -#endif - - if (timer_pending(&ft->tl)) - timer_delete(&ft->tl); - ft->event = event; - ft->arg = arg; - ft->tl.expires = jiffies + (millisec * HZ) / 1000; - add_timer(&ft->tl); -} -EXPORT_SYMBOL(mISDN_FsmRestartTimer); diff --git a/drivers/isdn/mISDN/fsm.h b/drivers/isdn/mISDN/fsm.h deleted file mode 100644 index 211554b997f8..000000000000 --- a/drivers/isdn/mISDN/fsm.h +++ /dev/null @@ -1,58 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * - * Author Karsten Keil - * - * Thanks to Jan den Ouden - * Fritz Elfert - * Copyright 2008 by Karsten Keil - */ - -#ifndef _MISDN_FSM_H -#define _MISDN_FSM_H - -#include - -/* Statemachine */ - -struct FsmInst; - -typedef void (*FSMFNPTR)(struct FsmInst *, int, void *); - -struct Fsm { - FSMFNPTR *jumpmatrix; - int state_count, event_count; - char **strEvent, **strState; -}; - -struct FsmInst { - struct Fsm *fsm; - int state; - int debug; - void *userdata; - int userint; - void (*printdebug) (struct FsmInst *, char *, ...); -}; - -struct FsmNode { - int state, event; - void (*routine) (struct FsmInst *, int, void *); -}; - -struct FsmTimer { - struct FsmInst *fi; - struct timer_list tl; - int event; - void *arg; -}; - -extern int mISDN_FsmNew(struct Fsm *, struct FsmNode *, int); -extern void mISDN_FsmFree(struct Fsm *); -extern int mISDN_FsmEvent(struct FsmInst *, int , void *); -extern void mISDN_FsmChangeState(struct FsmInst *, int); -extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *); -extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int); -extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int); -extern void mISDN_FsmDelTimer(struct FsmTimer *, int); - -#endif diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c deleted file mode 100644 index 8c93af06ed02..000000000000 --- a/drivers/isdn/mISDN/hwchannel.c +++ /dev/null @@ -1,516 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include - -static void -dchannel_bh(struct work_struct *ws) -{ - struct dchannel *dch = container_of(ws, struct dchannel, workq); - struct sk_buff *skb; - int err; - - if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) { - while ((skb = skb_dequeue(&dch->rqueue))) { - if (likely(dch->dev.D.peer)) { - err = dch->dev.D.recv(dch->dev.D.peer, skb); - if (err) - dev_kfree_skb(skb); - } else - dev_kfree_skb(skb); - } - } - if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) { - if (dch->phfunc) - dch->phfunc(dch); - } -} - -static void -bchannel_bh(struct work_struct *ws) -{ - struct bchannel *bch = container_of(ws, struct bchannel, workq); - struct sk_buff *skb; - int err; - - if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) { - while ((skb = skb_dequeue(&bch->rqueue))) { - bch->rcount--; - if (likely(bch->ch.peer)) { - err = bch->ch.recv(bch->ch.peer, skb); - if (err) - dev_kfree_skb(skb); - } else - dev_kfree_skb(skb); - } - } -} - -int -mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf) -{ - test_and_set_bit(FLG_HDLC, &ch->Flags); - ch->maxlen = maxlen; - ch->hw = NULL; - ch->rx_skb = NULL; - ch->tx_skb = NULL; - ch->tx_idx = 0; - ch->phfunc = phf; - skb_queue_head_init(&ch->squeue); - skb_queue_head_init(&ch->rqueue); - INIT_LIST_HEAD(&ch->dev.bchannels); - INIT_WORK(&ch->workq, dchannel_bh); - return 0; -} -EXPORT_SYMBOL(mISDN_initdchannel); - -int -mISDN_initbchannel(struct bchannel *ch, unsigned short maxlen, - unsigned short minlen) -{ - ch->Flags = 0; - ch->minlen = minlen; - ch->next_minlen = minlen; - ch->init_minlen = minlen; - ch->maxlen = maxlen; - ch->next_maxlen = maxlen; - ch->init_maxlen = maxlen; - ch->hw = NULL; - ch->rx_skb = NULL; - ch->tx_skb = NULL; - ch->tx_idx = 0; - skb_queue_head_init(&ch->rqueue); - ch->rcount = 0; - ch->next_skb = NULL; - INIT_WORK(&ch->workq, bchannel_bh); - return 0; -} -EXPORT_SYMBOL(mISDN_initbchannel); - -int -mISDN_freedchannel(struct dchannel *ch) -{ - if (ch->tx_skb) { - dev_kfree_skb(ch->tx_skb); - ch->tx_skb = NULL; - } - if (ch->rx_skb) { - dev_kfree_skb(ch->rx_skb); - ch->rx_skb = NULL; - } - skb_queue_purge(&ch->squeue); - skb_queue_purge(&ch->rqueue); - flush_work(&ch->workq); - return 0; -} -EXPORT_SYMBOL(mISDN_freedchannel); - -void -mISDN_clear_bchannel(struct bchannel *ch) -{ - if (ch->tx_skb) { - dev_kfree_skb(ch->tx_skb); - ch->tx_skb = NULL; - } - ch->tx_idx = 0; - if (ch->rx_skb) { - dev_kfree_skb(ch->rx_skb); - ch->rx_skb = NULL; - } - if (ch->next_skb) { - dev_kfree_skb(ch->next_skb); - ch->next_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); - test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); - test_and_clear_bit(FLG_ACTIVE, &ch->Flags); - test_and_clear_bit(FLG_FILLEMPTY, &ch->Flags); - test_and_clear_bit(FLG_TX_EMPTY, &ch->Flags); - test_and_clear_bit(FLG_RX_OFF, &ch->Flags); - ch->dropcnt = 0; - ch->minlen = ch->init_minlen; - ch->next_minlen = ch->init_minlen; - ch->maxlen = ch->init_maxlen; - ch->next_maxlen = ch->init_maxlen; - skb_queue_purge(&ch->rqueue); - ch->rcount = 0; -} -EXPORT_SYMBOL(mISDN_clear_bchannel); - -void -mISDN_freebchannel(struct bchannel *ch) -{ - cancel_work_sync(&ch->workq); - mISDN_clear_bchannel(ch); -} -EXPORT_SYMBOL(mISDN_freebchannel); - -int -mISDN_ctrl_bchannel(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_RX_BUFFER | MISDN_CTRL_FILL_EMPTY | - MISDN_CTRL_RX_OFF; - break; - case MISDN_CTRL_FILL_EMPTY: - if (cq->p1) { - memset(bch->fill, cq->p2 & 0xff, MISDN_BCH_FILL_SIZE); - test_and_set_bit(FLG_FILLEMPTY, &bch->Flags); - } else { - test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); - } - break; - case MISDN_CTRL_RX_OFF: - /* read back dropped byte count */ - cq->p2 = bch->dropcnt; - if (cq->p1) - test_and_set_bit(FLG_RX_OFF, &bch->Flags); - else - test_and_clear_bit(FLG_RX_OFF, &bch->Flags); - bch->dropcnt = 0; - break; - case MISDN_CTRL_RX_BUFFER: - if (cq->p2 > MISDN_CTRL_RX_SIZE_IGNORE) - bch->next_maxlen = cq->p2; - if (cq->p1 > MISDN_CTRL_RX_SIZE_IGNORE) - bch->next_minlen = cq->p1; - /* we return the old values */ - cq->p1 = bch->minlen; - cq->p2 = bch->maxlen; - break; - default: - pr_info("mISDN unhandled control %x operation\n", cq->op); - ret = -EINVAL; - break; - } - return ret; -} -EXPORT_SYMBOL(mISDN_ctrl_bchannel); - -static inline u_int -get_sapi_tei(u_char *p) -{ - u_int sapi, tei; - - sapi = *p >> 2; - tei = p[1] >> 1; - return sapi | (tei << 8); -} - -void -recv_Dchannel(struct dchannel *dch) -{ - struct mISDNhead *hh; - - if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */ - dev_kfree_skb(dch->rx_skb); - dch->rx_skb = NULL; - return; - } - hh = mISDN_HEAD_P(dch->rx_skb); - hh->prim = PH_DATA_IND; - hh->id = get_sapi_tei(dch->rx_skb->data); - skb_queue_tail(&dch->rqueue, dch->rx_skb); - dch->rx_skb = NULL; - schedule_event(dch, FLG_RECVQUEUE); -} -EXPORT_SYMBOL(recv_Dchannel); - -void -recv_Echannel(struct dchannel *ech, struct dchannel *dch) -{ - struct mISDNhead *hh; - - if (ech->rx_skb->len < 2) { /* at least 2 for sapi / tei */ - dev_kfree_skb(ech->rx_skb); - ech->rx_skb = NULL; - return; - } - hh = mISDN_HEAD_P(ech->rx_skb); - hh->prim = PH_DATA_E_IND; - hh->id = get_sapi_tei(ech->rx_skb->data); - skb_queue_tail(&dch->rqueue, ech->rx_skb); - ech->rx_skb = NULL; - schedule_event(dch, FLG_RECVQUEUE); -} -EXPORT_SYMBOL(recv_Echannel); - -void -recv_Bchannel(struct bchannel *bch, unsigned int id, bool force) -{ - struct mISDNhead *hh; - - /* if allocation did fail upper functions still may call us */ - if (unlikely(!bch->rx_skb)) - return; - if (unlikely(!bch->rx_skb->len)) { - /* we have no data to send - this may happen after recovery - * from overflow or too small allocation. - * We need to free the buffer here */ - dev_kfree_skb(bch->rx_skb); - bch->rx_skb = NULL; - } else { - if (test_bit(FLG_TRANSPARENT, &bch->Flags) && - (bch->rx_skb->len < bch->minlen) && !force) - return; - hh = mISDN_HEAD_P(bch->rx_skb); - hh->prim = PH_DATA_IND; - hh->id = id; - if (bch->rcount >= 64) { - printk(KERN_WARNING - "B%d receive queue overflow - flushing!\n", - bch->nr); - skb_queue_purge(&bch->rqueue); - } - bch->rcount++; - skb_queue_tail(&bch->rqueue, bch->rx_skb); - bch->rx_skb = NULL; - schedule_event(bch, FLG_RECVQUEUE); - } -} -EXPORT_SYMBOL(recv_Bchannel); - -void -recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb) -{ - skb_queue_tail(&dch->rqueue, skb); - schedule_event(dch, FLG_RECVQUEUE); -} -EXPORT_SYMBOL(recv_Dchannel_skb); - -void -recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb) -{ - if (bch->rcount >= 64) { - printk(KERN_WARNING "B-channel %p receive queue overflow, " - "flushing!\n", bch); - skb_queue_purge(&bch->rqueue); - bch->rcount = 0; - } - bch->rcount++; - skb_queue_tail(&bch->rqueue, skb); - schedule_event(bch, FLG_RECVQUEUE); -} -EXPORT_SYMBOL(recv_Bchannel_skb); - -static void -confirm_Dsend(struct dchannel *dch) -{ - struct sk_buff *skb; - - skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb), - 0, NULL, GFP_ATOMIC); - if (!skb) { - printk(KERN_ERR "%s: no skb id %x\n", __func__, - mISDN_HEAD_ID(dch->tx_skb)); - return; - } - skb_queue_tail(&dch->rqueue, skb); - schedule_event(dch, FLG_RECVQUEUE); -} - -int -get_next_dframe(struct dchannel *dch) -{ - dch->tx_idx = 0; - dch->tx_skb = skb_dequeue(&dch->squeue); - if (dch->tx_skb) { - confirm_Dsend(dch); - return 1; - } - dch->tx_skb = NULL; - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - return 0; -} -EXPORT_SYMBOL(get_next_dframe); - -static void -confirm_Bsend(struct bchannel *bch) -{ - struct sk_buff *skb; - - if (bch->rcount >= 64) { - printk(KERN_WARNING "B-channel %p receive queue overflow, " - "flushing!\n", bch); - skb_queue_purge(&bch->rqueue); - bch->rcount = 0; - } - skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb), - 0, NULL, GFP_ATOMIC); - if (!skb) { - printk(KERN_ERR "%s: no skb id %x\n", __func__, - mISDN_HEAD_ID(bch->tx_skb)); - return; - } - bch->rcount++; - skb_queue_tail(&bch->rqueue, skb); - schedule_event(bch, FLG_RECVQUEUE); -} - -int -get_next_bframe(struct bchannel *bch) -{ - bch->tx_idx = 0; - if (test_bit(FLG_TX_NEXT, &bch->Flags)) { - bch->tx_skb = bch->next_skb; - if (bch->tx_skb) { - bch->next_skb = NULL; - test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); - /* confirm imediately to allow next data */ - confirm_Bsend(bch); - return 1; - } else { - test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); - printk(KERN_WARNING "B TX_NEXT without skb\n"); - } - } - bch->tx_skb = NULL; - test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); - return 0; -} -EXPORT_SYMBOL(get_next_bframe); - -void -queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb) -{ - struct mISDNhead *hh; - - if (!skb) { - _queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC); - } else { - if (ch->peer) { - hh = mISDN_HEAD_P(skb); - hh->prim = pr; - hh->id = id; - if (!ch->recv(ch->peer, skb)) - return; - } - dev_kfree_skb(skb); - } -} -EXPORT_SYMBOL(queue_ch_frame); - -int -dchannel_senddata(struct dchannel *ch, struct sk_buff *skb) -{ - /* check oversize */ - if (skb->len <= 0) { - printk(KERN_WARNING "%s: skb too small\n", __func__); - return -EINVAL; - } - if (skb->len > ch->maxlen) { - printk(KERN_WARNING "%s: skb too large(%d/%d)\n", - __func__, skb->len, ch->maxlen); - return -EINVAL; - } - /* HW lock must be obtained */ - if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { - skb_queue_tail(&ch->squeue, skb); - return 0; - } else { - /* write to fifo */ - ch->tx_skb = skb; - ch->tx_idx = 0; - return 1; - } -} -EXPORT_SYMBOL(dchannel_senddata); - -int -bchannel_senddata(struct bchannel *ch, struct sk_buff *skb) -{ - - /* check oversize */ - if (skb->len <= 0) { - printk(KERN_WARNING "%s: skb too small\n", __func__); - return -EINVAL; - } - if (skb->len > ch->maxlen) { - printk(KERN_WARNING "%s: skb too large(%d/%d)\n", - __func__, skb->len, ch->maxlen); - return -EINVAL; - } - /* HW lock must be obtained */ - /* check for pending next_skb */ - if (ch->next_skb) { - printk(KERN_WARNING - "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", - __func__, skb->len, ch->next_skb->len); - return -EBUSY; - } - if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { - test_and_set_bit(FLG_TX_NEXT, &ch->Flags); - ch->next_skb = skb; - return 0; - } else { - /* write to fifo */ - ch->tx_skb = skb; - ch->tx_idx = 0; - confirm_Bsend(ch); - return 1; - } -} -EXPORT_SYMBOL(bchannel_senddata); - -/* The function allocates a new receive skb on demand with a size for the - * requirements of the current protocol. It returns the tailroom of the - * receive skb or an error. - */ -int -bchannel_get_rxbuf(struct bchannel *bch, int reqlen) -{ - int len; - - if (bch->rx_skb) { - len = skb_tailroom(bch->rx_skb); - if (len < reqlen) { - pr_warn("B%d no space for %d (only %d) bytes\n", - bch->nr, reqlen, len); - if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { - /* send what we have now and try a new buffer */ - recv_Bchannel(bch, 0, true); - } else { - /* on HDLC we have to drop too big frames */ - return -EMSGSIZE; - } - } else { - return len; - } - } - /* update current min/max length first */ - if (unlikely(bch->maxlen != bch->next_maxlen)) - bch->maxlen = bch->next_maxlen; - if (unlikely(bch->minlen != bch->next_minlen)) - bch->minlen = bch->next_minlen; - if (unlikely(reqlen > bch->maxlen)) - return -EMSGSIZE; - if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { - if (reqlen >= bch->minlen) { - len = reqlen; - } else { - len = 2 * bch->minlen; - if (len > bch->maxlen) - len = bch->maxlen; - } - } else { - /* with HDLC we do not know the length yet */ - len = bch->maxlen; - } - bch->rx_skb = mI_alloc_skb(len, GFP_ATOMIC); - if (!bch->rx_skb) { - pr_warn("B%d receive no memory for %d bytes\n", bch->nr, len); - len = -ENOMEM; - } - return len; -} -EXPORT_SYMBOL(bchannel_get_rxbuf); diff --git a/drivers/isdn/mISDN/l1oip.h b/drivers/isdn/mISDN/l1oip.h deleted file mode 100644 index 48133d022812..000000000000 --- a/drivers/isdn/mISDN/l1oip.h +++ /dev/null @@ -1,92 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * see notice in l1oip.c - */ - -/* debugging */ -#define DEBUG_L1OIP_INIT 0x00010000 -#define DEBUG_L1OIP_SOCKET 0x00020000 -#define DEBUG_L1OIP_MGR 0x00040000 -#define DEBUG_L1OIP_MSG 0x00080000 - -/* enable to disorder received bchannels by sequence 2143658798... */ -/* - #define REORDER_DEBUG -*/ - -/* frames */ -#define L1OIP_MAX_LEN 2048 /* max packet size form l2 */ -#define L1OIP_MAX_PERFRAME 1400 /* max data size in one frame */ - - -/* timers */ -#define L1OIP_KEEPALIVE 15 -#define L1OIP_TIMEOUT 65 - - -/* socket */ -#define L1OIP_DEFAULTPORT 931 - - -/* channel structure */ -struct l1oip_chan { - struct dchannel *dch; - struct bchannel *bch; - u32 tx_counter; /* counts xmit bytes/packets */ - u32 rx_counter; /* counts recv bytes/packets */ - u32 codecstate; /* used by codec to save data */ -#ifdef REORDER_DEBUG - int disorder_flag; - struct sk_buff *disorder_skb; - u32 disorder_cnt; -#endif -}; - - -/* card structure */ -struct l1oip { - struct list_head list; - - /* card */ - int registered; /* if registered with mISDN */ - char name[MISDN_MAX_IDLEN]; - int idx; /* card index */ - int pri; /* 1=pri, 0=bri */ - int d_idx; /* current dchannel number */ - int b_num; /* number of bchannels */ - u32 id; /* id of connection */ - int ondemand; /* if transmis. is on demand */ - int bundle; /* bundle channels in one frm */ - int codec; /* codec to use for transmis. */ - int limit; /* limit number of bchannels */ - bool shutdown; /* if card is released */ - - /* timer */ - struct timer_list keep_tl; - struct timer_list timeout_tl; - int timeout_on; - struct work_struct workq; - - /* socket */ - struct socket *socket; /* if set, socket is created */ - struct completion socket_complete;/* completion of sock thread */ - struct task_struct *socket_thread; - spinlock_t socket_lock; /* access sock outside thread */ - u32 remoteip; /* if all set, ip is assigned */ - u16 localport; /* must always be set */ - u16 remoteport; /* must always be set */ - struct sockaddr_in sin_local; /* local socket name */ - struct sockaddr_in sin_remote; /* remote socket name */ - struct msghdr sendmsg; /* ip message to send */ - struct kvec sendiov; /* iov for message */ - - /* frame */ - struct l1oip_chan chan[128]; /* channel instances */ -}; - -extern int l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state); -extern int l1oip_4bit_to_law(u8 *data, int len, u8 *result); -extern int l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result); -extern int l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result); -extern void l1oip_4bit_free(void); -extern int l1oip_4bit_alloc(int ulaw); diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c deleted file mode 100644 index 1059234fbc67..000000000000 --- a/drivers/isdn/mISDN/l1oip_codec.c +++ /dev/null @@ -1,358 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - - * l1oip_codec.c generic codec using lookup table - * -> conversion from a-Law to u-Law - * -> conversion from u-Law to a-Law - * -> compression by reducing the number of sample resolution to 4 - * - * NOTE: It is not compatible with any standard codec like ADPCM. - * - * Author Andreas Eversberg (jolly@eversberg.eu) - * - - */ - -/* - - How the codec works: - -------------------- - - The volume is increased to increase the dynamic range of the audio signal. - Each sample is converted to a-LAW with only 16 steps of level resolution. - A pair of two samples are stored in one byte. - - The first byte is stored in the upper bits, the second byte is stored in the - lower bits. - - To speed up compression and decompression, two lookup tables are formed: - - - 16 bits index for two samples (law encoded) with 8 bit compressed result. - - 8 bits index for one compressed data with 16 bits decompressed result. - - NOTE: The bytes are handled as they are law-encoded. - -*/ - -#include -#include -#include -#include "core.h" -#include "l1oip.h" - -/* definitions of codec. don't use calculations, code may run slower. */ - -static u8 *table_com; -static u16 *table_dec; - - -/* alaw -> ulaw */ -static u8 alaw_to_ulaw[256] = -{ - 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49, - 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57, - 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41, - 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f, - 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d, - 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b, - 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45, - 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53, - 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47, - 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55, - 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f, - 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e, - 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b, - 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59, - 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43, - 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51, - 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a, - 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58, - 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42, - 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50, - 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e, - 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c, - 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46, - 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54, - 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48, - 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56, - 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40, - 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f, - 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c, - 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a, - 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44, - 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52 -}; - -/* ulaw -> alaw */ -static u8 ulaw_to_alaw[256] = -{ - 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, - 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25, - 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, - 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d, - 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, - 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21, - 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, - 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9, - 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, - 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf, - 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, - 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33, - 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, - 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b, - 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, - 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b, - 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, - 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24, - 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, - 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c, - 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, - 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20, - 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, - 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8, - 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, - 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde, - 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, - 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32, - 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, - 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a, - 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a, - 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a -}; - -/* alaw -> 4bit compression */ -static u8 alaw_to_4bit[256] = { - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0d, 0x02, - 0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x01, 0x0a, 0x05, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, -}; - -/* 4bit -> alaw decompression */ -static u8 _4bit_to_alaw[16] = { - 0x5d, 0x51, 0xd9, 0xd7, 0x5f, 0x53, 0xa3, 0x4b, - 0x2a, 0x3a, 0x22, 0x2e, 0x26, 0x56, 0x20, 0x2c, -}; - -/* ulaw -> 4bit compression */ -static u8 ulaw_to_4bit[256] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, -}; - -/* 4bit -> ulaw decompression */ -static u8 _4bit_to_ulaw[16] = { - 0x11, 0x21, 0x31, 0x40, 0x4e, 0x5c, 0x68, 0x71, - 0xfe, 0xef, 0xe7, 0xdb, 0xcd, 0xbf, 0xaf, 0x9f, -}; - - -/* - * Compresses data to the result buffer - * The result size must be at least half of the input buffer. - * The number of samples also must be even! - */ -int -l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state) -{ - int ii, i = 0, o = 0; - - if (!len) - return 0; - - /* send saved byte and first input byte */ - if (*state) { - *result++ = table_com[(((*state) << 8) & 0xff00) | (*data++)]; - len--; - o++; - } - - ii = len >> 1; - - while (i < ii) { - *result++ = table_com[(data[0]<<8) | (data[1])]; - data += 2; - i++; - o++; - } - - /* if len has an odd number, we save byte for next call */ - if (len & 1) - *state = 0x100 + *data; - else - *state = 0; - - return o; -} - -/* Decompress data to the result buffer - * The result size must be the number of sample in packet. (2 * input data) - * The number of samples in the result are even! - */ -int -l1oip_4bit_to_law(u8 *data, int len, u8 *result) -{ - int i = 0; - u16 r; - - while (i < len) { - r = table_dec[*data++]; - *result++ = r >> 8; - *result++ = r; - i++; - } - - return len << 1; -} - - -/* - * law conversion - */ -int -l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result) -{ - int i = 0; - - while (i < len) { - *result++ = alaw_to_ulaw[*data++]; - i++; - } - - return len; -} - -int -l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result) -{ - int i = 0; - - while (i < len) { - *result++ = ulaw_to_alaw[*data++]; - i++; - } - - return len; -} - - -/* - * generate/free compression and decompression table - */ -void -l1oip_4bit_free(void) -{ - vfree(table_dec); - vfree(table_com); - table_com = NULL; - table_dec = NULL; -} - -int -l1oip_4bit_alloc(int ulaw) -{ - int i1, i2, c, sample; - - /* in case, it is called again */ - if (table_dec) - return 0; - - /* alloc conversion tables */ - table_com = vzalloc(65536); - table_dec = vzalloc(512); - if (!table_com || !table_dec) { - l1oip_4bit_free(); - return -ENOMEM; - } - /* generate compression table */ - i1 = 0; - while (i1 < 256) { - if (ulaw) - c = ulaw_to_4bit[i1]; - else - c = alaw_to_4bit[i1]; - i2 = 0; - while (i2 < 256) { - table_com[(i1 << 8) | i2] |= (c << 4); - table_com[(i2 << 8) | i1] |= c; - i2++; - } - i1++; - } - - /* generate decompression table */ - i1 = 0; - while (i1 < 16) { - if (ulaw) - sample = _4bit_to_ulaw[i1]; - else - sample = _4bit_to_alaw[i1]; - i2 = 0; - while (i2 < 16) { - table_dec[(i1 << 4) | i2] |= (sample << 8); - table_dec[(i2 << 4) | i1] |= sample; - i2++; - } - i1++; - } - - return 0; -} diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c deleted file mode 100644 index 6866a0d6b382..000000000000 --- a/drivers/isdn/mISDN/l1oip_core.c +++ /dev/null @@ -1,1505 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - - * l1oip.c low level driver for tunneling layer 1 over IP - * - * NOTE: It is not compatible with TDMoIP nor "ISDN over IP". - * - * Author Andreas Eversberg (jolly@eversberg.eu) - */ - -/* module parameters: - * type: - Value 1 = BRI - Value 2 = PRI - Value 3 = BRI (multi channel frame, not supported yet) - Value 4 = PRI (multi channel frame, not supported yet) - A multi channel frame reduces overhead to a single frame for all - b-channels, but increases delay. - (NOTE: Multi channel frames are not implemented yet.) - - * codec: - Value 0 = transparent (default) - Value 1 = transfer ALAW - Value 2 = transfer ULAW - Value 3 = transfer generic 4 bit compression. - - * ulaw: - 0 = we use a-Law (default) - 1 = we use u-Law - - * limit: - limitation of B-channels to control bandwidth (1...126) - BRI: 1 or 2 - PRI: 1-30, 31-126 (126, because dchannel ist not counted here) - Also limited ressources are used for stack, resulting in less channels. - It is possible to have more channels than 30 in PRI mode, this must - be supported by the application. - - * ip: - byte representation of remote ip address (127.0.0.1 -> 127,0,0,1) - If not given or four 0, no remote address is set. - For multiple interfaces, concat ip addresses. (127,0,0,1,127,0,0,1) - - * port: - port number (local interface) - If not given or 0, port 931 is used for fist instance, 932 for next... - For multiple interfaces, different ports must be given. - - * remoteport: - port number (remote interface) - If not given or 0, remote port equals local port - For multiple interfaces on equal sites, different ports must be given. - - * ondemand: - 0 = fixed (always transmit packets, even when remote side timed out) - 1 = on demand (only transmit packets, when remote side is detected) - the default is 0 - NOTE: ID must also be set for on demand. - - * id: - optional value to identify frames. This value must be equal on both - peers and should be random. If omitted or 0, no ID is transmitted. - - * debug: - NOTE: only one debug value must be given for all cards - enable debugging (see l1oip.h for debug options) - - - Special mISDN controls: - - op = MISDN_CTRL_SETPEER* - p1 = bytes 0-3 : remote IP address in network order (left element first) - p2 = bytes 1-2 : remote port in network order (high byte first) - optional: - p2 = bytes 3-4 : local port in network order (high byte first) - - op = MISDN_CTRL_UNSETPEER* - - * Use l1oipctrl for comfortable setting or removing ip address. - (Layer 1 Over IP CTRL) - - - L1oIP-Protocol - -------------- - - Frame Header: - - 7 6 5 4 3 2 1 0 - +---------------+ - |Ver|T|I|Coding | - +---------------+ - | ID byte 3 * | - +---------------+ - | ID byte 2 * | - +---------------+ - | ID byte 1 * | - +---------------+ - | ID byte 0 * | - +---------------+ - |M| Channel | - +---------------+ - | Length * | - +---------------+ - | Time Base MSB | - +---------------+ - | Time Base LSB | - +---------------+ - | Data.... | - - ... - - | | - +---------------+ - |M| Channel | - +---------------+ - | Length * | - +---------------+ - | Time Base MSB | - +---------------+ - | Time Base LSB | - +---------------+ - | Data.... | - - ... - - - * Only included in some cases. - - - Ver = Version - If version is missmatch, the frame must be ignored. - - - T = Type of interface - Must be 0 for S0 or 1 for E1. - - - I = Id present - If bit is set, four ID bytes are included in frame. - - - ID = Connection ID - Additional ID to prevent Denial of Service attacs. Also it prevents hijacking - connections with dynamic IP. The ID should be random and must not be 0. - - - Coding = Type of codec - Must be 0 for no transcoding. Also for D-channel and other HDLC frames. - 1 and 2 are reserved for explicitly use of a-LAW or u-LAW codec. - 3 is used for generic table compressor. - - - M = More channels to come. If this flag is 1, the following byte contains - the length of the channel data. After the data block, the next channel will - be defined. The flag for the last channel block (or if only one channel is - transmitted), must be 0 and no length is given. - - - Channel = Channel number - 0 reserved - 1-3 channel data for S0 (3 is D-channel) - 1-31 channel data for E1 (16 is D-channel) - 32-127 channel data for extended E1 (16 is D-channel) - - - The length is used if the M-flag is 1. It is used to find the next channel - inside frame. - NOTE: A value of 0 equals 256 bytes of data. - -> For larger data blocks, a single frame must be used. - -> For larger streams, a single frame or multiple blocks with same channel ID - must be used. - - - Time Base = Timestamp of first sample in frame - The "Time Base" is used to rearange packets and to detect packet loss. - The 16 bits are sent in network order (MSB first) and count 1/8000 th of a - second. This causes a wrap around each 8,192 seconds. There is no requirement - for the initial "Time Base", but 0 should be used for the first packet. - In case of HDLC data, this timestamp counts the packet or byte number. - - - Two Timers: - - After initialisation, a timer of 15 seconds is started. Whenever a packet is - transmitted, the timer is reset to 15 seconds again. If the timer expires, an - empty packet is transmitted. This keep the connection alive. - - When a valid packet is received, a timer 65 seconds is started. The interface - become ACTIVE. If the timer expires, the interface becomes INACTIVE. - - - Dynamic IP handling: - - To allow dynamic IP, the ID must be non 0. In this case, any packet with the - correct port number and ID will be accepted. If the remote side changes its IP - the new IP is used for all transmitted packets until it changes again. - - - On Demand: - - If the ondemand parameter is given, the remote IP is set to 0 on timeout. - This will stop keepalive traffic to remote. If the remote is online again, - traffic will continue to the remote address. This is useful for road warriors. - This feature only works with ID set, otherwhise it is highly unsecure. - - - Socket and Thread - ----------------- - - The complete socket opening and closing is done by a thread. - When the thread opened a socket, the hc->socket descriptor is set. Whenever a - packet shall be sent to the socket, the hc->socket must be checked whether not - NULL. To prevent change in socket descriptor, the hc->socket_lock must be used. - To change the socket, a recall of l1oip_socket_open() will safely kill the - socket process and create a new one. - -*/ - -#define L1OIP_VERSION 0 /* 0...3 */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "core.h" -#include "l1oip.h" - -static const char *l1oip_revision = "2.00"; - -static int l1oip_cnt; -static DEFINE_SPINLOCK(l1oip_lock); -static LIST_HEAD(l1oip_ilist); - -#define MAX_CARDS 16 -static u_int type[MAX_CARDS]; -static u_int codec[MAX_CARDS]; -static u_int ip[MAX_CARDS * 4]; -static u_int port[MAX_CARDS]; -static u_int remoteport[MAX_CARDS]; -static u_int ondemand[MAX_CARDS]; -static u_int limit[MAX_CARDS]; -static u_int id[MAX_CARDS]; -static int debug; -static int ulaw; - -MODULE_AUTHOR("Andreas Eversberg"); -MODULE_DESCRIPTION("mISDN driver for tunneling layer 1 over IP"); -MODULE_LICENSE("GPL"); -module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(codec, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(ip, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(remoteport, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(ondemand, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(limit, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(id, uint, NULL, S_IRUGO | S_IWUSR); -module_param(ulaw, uint, S_IRUGO | S_IWUSR); -module_param(debug, uint, S_IRUGO | S_IWUSR); - -/* - * send a frame via socket, if open and restart timer - */ -static int -l1oip_socket_send(struct l1oip *hc, u8 localcodec, u8 channel, u32 chanmask, - u16 timebase, u8 *buf, int len) -{ - u8 *p; - u8 frame[MAX_DFRAME_LEN_L1 + 32]; - struct socket *socket = NULL; - - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: sending data to socket (len = %d)\n", - __func__, len); - - p = frame; - - /* restart timer */ - if (time_before(hc->keep_tl.expires, jiffies + 5 * HZ) && !hc->shutdown) - mod_timer(&hc->keep_tl, jiffies + L1OIP_KEEPALIVE * HZ); - else - hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE * HZ; - - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: resetting timer\n", __func__); - - /* drop if we have no remote ip or port */ - if (!hc->sin_remote.sin_addr.s_addr || !hc->sin_remote.sin_port) { - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: dropping frame, because remote " - "IP is not set.\n", __func__); - return len; - } - - /* assemble frame */ - *p++ = (L1OIP_VERSION << 6) /* version and coding */ - | (hc->pri ? 0x20 : 0x00) /* type */ - | (hc->id ? 0x10 : 0x00) /* id */ - | localcodec; - if (hc->id) { - *p++ = hc->id >> 24; /* id */ - *p++ = hc->id >> 16; - *p++ = hc->id >> 8; - *p++ = hc->id; - } - *p++ = 0x00 + channel; /* m-flag, channel */ - *p++ = timebase >> 8; /* time base */ - *p++ = timebase; - - if (buf && len) { /* add data to frame */ - if (localcodec == 1 && ulaw) - l1oip_ulaw_to_alaw(buf, len, p); - else if (localcodec == 2 && !ulaw) - l1oip_alaw_to_ulaw(buf, len, p); - else if (localcodec == 3) - len = l1oip_law_to_4bit(buf, len, p, - &hc->chan[channel].codecstate); - else - memcpy(p, buf, len); - } - len += p - frame; - - /* check for socket in safe condition */ - spin_lock(&hc->socket_lock); - if (!hc->socket) { - spin_unlock(&hc->socket_lock); - return 0; - } - /* seize socket */ - socket = hc->socket; - hc->socket = NULL; - spin_unlock(&hc->socket_lock); - /* send packet */ - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: sending packet to socket (len " - "= %d)\n", __func__, len); - hc->sendiov.iov_base = frame; - hc->sendiov.iov_len = len; - len = kernel_sendmsg(socket, &hc->sendmsg, &hc->sendiov, 1, len); - /* give socket back */ - hc->socket = socket; /* no locking required */ - - return len; -} - - -/* - * receive channel data from socket - */ -static void -l1oip_socket_recv(struct l1oip *hc, u8 remotecodec, u8 channel, u16 timebase, - u8 *buf, int len) -{ - struct sk_buff *nskb; - struct bchannel *bch; - struct dchannel *dch; - u8 *p; - u32 rx_counter; - - if (len == 0) { - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: received empty keepalive data, " - "ignoring\n", __func__); - return; - } - - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: received data, sending to mISDN (%d)\n", - __func__, len); - - if (channel < 1 || channel > 127) { - printk(KERN_WARNING "%s: packet error - channel %d out of " - "range\n", __func__, channel); - return; - } - dch = hc->chan[channel].dch; - bch = hc->chan[channel].bch; - if (!dch && !bch) { - printk(KERN_WARNING "%s: packet error - channel %d not in " - "stack\n", __func__, channel); - return; - } - - /* prepare message */ - nskb = mI_alloc_skb((remotecodec == 3) ? (len << 1) : len, GFP_ATOMIC); - if (!nskb) { - printk(KERN_ERR "%s: No mem for skb.\n", __func__); - return; - } - p = skb_put(nskb, (remotecodec == 3) ? (len << 1) : len); - - if (remotecodec == 1 && ulaw) - l1oip_alaw_to_ulaw(buf, len, p); - else if (remotecodec == 2 && !ulaw) - l1oip_ulaw_to_alaw(buf, len, p); - else if (remotecodec == 3) - len = l1oip_4bit_to_law(buf, len, p); - else - memcpy(p, buf, len); - - /* send message up */ - if (dch && len >= 2) { - dch->rx_skb = nskb; - recv_Dchannel(dch); - } - if (bch) { - /* expand 16 bit sequence number to 32 bit sequence number */ - rx_counter = hc->chan[channel].rx_counter; - if (((s16)(timebase - rx_counter)) >= 0) { - /* time has changed forward */ - if (timebase >= (rx_counter & 0xffff)) - rx_counter = - (rx_counter & 0xffff0000) | timebase; - else - rx_counter = ((rx_counter & 0xffff0000) + 0x10000) - | timebase; - } else { - /* time has changed backwards */ - if (timebase < (rx_counter & 0xffff)) - rx_counter = - (rx_counter & 0xffff0000) | timebase; - else - rx_counter = ((rx_counter & 0xffff0000) - 0x10000) - | timebase; - } - hc->chan[channel].rx_counter = rx_counter; - -#ifdef REORDER_DEBUG - if (hc->chan[channel].disorder_flag) { - swap(hc->chan[channel].disorder_skb, nskb); - swap(hc->chan[channel].disorder_cnt, rx_counter); - } - hc->chan[channel].disorder_flag ^= 1; - if (nskb) -#endif - queue_ch_frame(&bch->ch, PH_DATA_IND, rx_counter, nskb); - } -} - - -/* - * parse frame and extract channel data - */ -static void -l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len) -{ - u32 packet_id; - u8 channel; - u8 remotecodec; - u16 timebase; - int m, mlen; - int len_start = len; /* initial frame length */ - struct dchannel *dch = hc->chan[hc->d_idx].dch; - - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: received frame, parsing... (%d)\n", - __func__, len); - - /* check length */ - if (len < 1 + 1 + 2) { - printk(KERN_WARNING "%s: packet error - length %d below " - "4 bytes\n", __func__, len); - return; - } - - /* check version */ - if (((*buf) >> 6) != L1OIP_VERSION) { - printk(KERN_WARNING "%s: packet error - unknown version %d\n", - __func__, buf[0]>>6); - return; - } - - /* check type */ - if (((*buf) & 0x20) && !hc->pri) { - printk(KERN_WARNING "%s: packet error - received E1 packet " - "on S0 interface\n", __func__); - return; - } - if (!((*buf) & 0x20) && hc->pri) { - printk(KERN_WARNING "%s: packet error - received S0 packet " - "on E1 interface\n", __func__); - return; - } - - /* get id flag */ - packet_id = (*buf >> 4) & 1; - - /* check coding */ - remotecodec = (*buf) & 0x0f; - if (remotecodec > 3) { - printk(KERN_WARNING "%s: packet error - remotecodec %d " - "unsupported\n", __func__, remotecodec); - return; - } - buf++; - len--; - - /* check packet_id */ - if (packet_id) { - if (!hc->id) { - printk(KERN_WARNING "%s: packet error - packet has id " - "0x%x, but we have not\n", __func__, packet_id); - return; - } - if (len < 4) { - printk(KERN_WARNING "%s: packet error - packet too " - "short for ID value\n", __func__); - return; - } - packet_id = (*buf++) << 24; - packet_id += (*buf++) << 16; - packet_id += (*buf++) << 8; - packet_id += (*buf++); - len -= 4; - - if (packet_id != hc->id) { - printk(KERN_WARNING "%s: packet error - ID mismatch, " - "got 0x%x, we 0x%x\n", - __func__, packet_id, hc->id); - return; - } - } else { - if (hc->id) { - printk(KERN_WARNING "%s: packet error - packet has no " - "ID, but we have\n", __func__); - return; - } - } - -multiframe: - if (len < 1) { - printk(KERN_WARNING "%s: packet error - packet too short, " - "channel expected at position %d.\n", - __func__, len-len_start + 1); - return; - } - - /* get channel and multiframe flag */ - channel = *buf & 0x7f; - m = *buf >> 7; - buf++; - len--; - - /* check length on multiframe */ - if (m) { - if (len < 1) { - printk(KERN_WARNING "%s: packet error - packet too " - "short, length expected at position %d.\n", - __func__, len_start - len - 1); - return; - } - - mlen = *buf++; - len--; - if (mlen == 0) - mlen = 256; - if (len < mlen + 3) { - printk(KERN_WARNING "%s: packet error - length %d at " - "position %d exceeds total length %d.\n", - __func__, mlen, len_start-len - 1, len_start); - return; - } - if (len == mlen + 3) { - printk(KERN_WARNING "%s: packet error - length %d at " - "position %d will not allow additional " - "packet.\n", - __func__, mlen, len_start-len + 1); - return; - } - } else - mlen = len - 2; /* single frame, subtract timebase */ - - if (len < 2) { - printk(KERN_WARNING "%s: packet error - packet too short, time " - "base expected at position %d.\n", - __func__, len-len_start + 1); - return; - } - - /* get time base */ - timebase = (*buf++) << 8; - timebase |= (*buf++); - len -= 2; - - /* if inactive, we send up a PH_ACTIVATE and activate */ - if (!test_bit(FLG_ACTIVE, &dch->Flags)) { - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: interface become active due to " - "received packet\n", __func__); - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_ATOMIC); - } - - /* distribute packet */ - l1oip_socket_recv(hc, remotecodec, channel, timebase, buf, mlen); - buf += mlen; - len -= mlen; - - /* multiframe */ - if (m) - goto multiframe; - - /* restart timer */ - if ((time_before(hc->timeout_tl.expires, jiffies + 5 * HZ) || - !hc->timeout_on) && - !hc->shutdown) { - hc->timeout_on = 1; - mod_timer(&hc->timeout_tl, jiffies + L1OIP_TIMEOUT * HZ); - } else /* only adjust timer */ - hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT * HZ; - - /* if ip or source port changes */ - if ((hc->sin_remote.sin_addr.s_addr != sin->sin_addr.s_addr) - || (hc->sin_remote.sin_port != sin->sin_port)) { - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: remote address changes from " - "0x%08x to 0x%08x (port %d to %d)\n", __func__, - ntohl(hc->sin_remote.sin_addr.s_addr), - ntohl(sin->sin_addr.s_addr), - ntohs(hc->sin_remote.sin_port), - ntohs(sin->sin_port)); - hc->sin_remote.sin_addr.s_addr = sin->sin_addr.s_addr; - hc->sin_remote.sin_port = sin->sin_port; - } -} - - -/* - * socket stuff - */ -static int -l1oip_socket_thread(void *data) -{ - struct l1oip *hc = (struct l1oip *)data; - int ret = 0; - struct sockaddr_in sin_rx; - struct kvec iov; - struct msghdr msg = {.msg_name = &sin_rx, - .msg_namelen = sizeof(sin_rx)}; - unsigned char *recvbuf; - size_t recvbuf_size = 1500; - int recvlen; - struct socket *socket = NULL; - DECLARE_COMPLETION_ONSTACK(wait); - - /* allocate buffer memory */ - recvbuf = kmalloc(recvbuf_size, GFP_KERNEL); - if (!recvbuf) { - printk(KERN_ERR "%s: Failed to alloc recvbuf.\n", __func__); - ret = -ENOMEM; - goto fail; - } - - iov.iov_base = recvbuf; - iov.iov_len = recvbuf_size; - - /* make daemon */ - allow_signal(SIGTERM); - - /* create socket */ - if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &socket)) { - printk(KERN_ERR "%s: Failed to create socket.\n", __func__); - ret = -EIO; - goto fail; - } - - /* set incoming address */ - hc->sin_local.sin_family = AF_INET; - hc->sin_local.sin_addr.s_addr = INADDR_ANY; - hc->sin_local.sin_port = htons((unsigned short)hc->localport); - - /* set outgoing address */ - hc->sin_remote.sin_family = AF_INET; - hc->sin_remote.sin_addr.s_addr = htonl(hc->remoteip); - hc->sin_remote.sin_port = htons((unsigned short)hc->remoteport); - - /* bind to incoming port */ - if (socket->ops->bind(socket, (struct sockaddr_unsized *)&hc->sin_local, - sizeof(hc->sin_local))) { - printk(KERN_ERR "%s: Failed to bind socket to port %d.\n", - __func__, hc->localport); - ret = -EINVAL; - goto fail; - } - - /* check sk */ - if (socket->sk == NULL) { - printk(KERN_ERR "%s: socket->sk == NULL\n", __func__); - ret = -EIO; - goto fail; - } - - /* build send message */ - hc->sendmsg.msg_name = &hc->sin_remote; - hc->sendmsg.msg_namelen = sizeof(hc->sin_remote); - hc->sendmsg.msg_control = NULL; - hc->sendmsg.msg_controllen = 0; - - /* give away socket */ - spin_lock(&hc->socket_lock); - hc->socket = socket; - spin_unlock(&hc->socket_lock); - - /* read loop */ - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: socket created and open\n", - __func__); - while (!signal_pending(current)) { - iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, recvbuf_size); - recvlen = sock_recvmsg(socket, &msg, 0); - if (recvlen > 0) { - l1oip_socket_parse(hc, &sin_rx, recvbuf, recvlen); - } else { - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_WARNING - "%s: broken pipe on socket\n", __func__); - } - } - - /* get socket back, check first if in use, maybe by send function */ - spin_lock(&hc->socket_lock); - /* if hc->socket is NULL, it is in use until it is given back */ - while (!hc->socket) { - spin_unlock(&hc->socket_lock); - schedule_timeout(HZ / 10); - spin_lock(&hc->socket_lock); - } - hc->socket = NULL; - spin_unlock(&hc->socket_lock); - - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: socket thread terminating\n", - __func__); - -fail: - /* free recvbuf */ - kfree(recvbuf); - - /* close socket */ - if (socket) - sock_release(socket); - - /* if we got killed, signal completion */ - complete(&hc->socket_complete); - hc->socket_thread = NULL; /* show termination of thread */ - - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: socket thread terminated\n", - __func__); - return ret; -} - -static void -l1oip_socket_close(struct l1oip *hc) -{ - struct dchannel *dch = hc->chan[hc->d_idx].dch; - - /* kill thread */ - if (hc->socket_thread) { - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: socket thread exists, " - "killing...\n", __func__); - send_sig(SIGTERM, hc->socket_thread, 0); - wait_for_completion(&hc->socket_complete); - } - - /* if active, we send up a PH_DEACTIVATE and deactivate */ - if (test_bit(FLG_ACTIVE, &dch->Flags)) { - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: interface become deactivated " - "due to timeout\n", __func__); - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_ATOMIC); - } -} - -static int -l1oip_socket_open(struct l1oip *hc) -{ - /* in case of reopen, we need to close first */ - l1oip_socket_close(hc); - - init_completion(&hc->socket_complete); - - /* create receive process */ - hc->socket_thread = kthread_run(l1oip_socket_thread, hc, "l1oip_%s", - hc->name); - if (IS_ERR(hc->socket_thread)) { - int err = PTR_ERR(hc->socket_thread); - printk(KERN_ERR "%s: Failed (%d) to create socket process.\n", - __func__, err); - hc->socket_thread = NULL; - sock_release(hc->socket); - return err; - } - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: socket thread created\n", __func__); - - return 0; -} - - -static void -l1oip_send_bh(struct work_struct *work) -{ - struct l1oip *hc = container_of(work, struct l1oip, workq); - - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: keepalive timer expired, sending empty " - "frame on dchannel\n", __func__); - - /* send an empty l1oip frame at D-channel */ - l1oip_socket_send(hc, 0, hc->d_idx, 0, 0, NULL, 0); -} - - -/* - * timer stuff - */ -static void -l1oip_keepalive(struct timer_list *t) -{ - struct l1oip *hc = timer_container_of(hc, t, keep_tl); - - schedule_work(&hc->workq); -} - -static void -l1oip_timeout(struct timer_list *t) -{ - struct l1oip *hc = timer_container_of(hc, t, - timeout_tl); - struct dchannel *dch = hc->chan[hc->d_idx].dch; - - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: timeout timer expired, turn layer one " - "down.\n", __func__); - - hc->timeout_on = 0; /* state that timer must be initialized next time */ - - /* if timeout, we send up a PH_DEACTIVATE and deactivate */ - if (test_bit(FLG_ACTIVE, &dch->Flags)) { - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: interface become deactivated " - "due to timeout\n", __func__); - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_ATOMIC); - } - - /* if we have ondemand set, we remove ip address */ - if (hc->ondemand) { - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: on demand causes ip address to " - "be removed\n", __func__); - hc->sin_remote.sin_addr.s_addr = 0; - } -} - - -/* - * message handling - */ -static int -handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct l1oip *hc = dch->hw; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int ret = -EINVAL; - int l, ll; - unsigned char *p; - - switch (hh->prim) { - case PH_DATA_REQ: - if (skb->len < 1) { - printk(KERN_WARNING "%s: skb too small\n", - __func__); - break; - } - if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) { - printk(KERN_WARNING "%s: skb too large\n", - __func__); - break; - } - /* send frame */ - p = skb->data; - l = skb->len; - while (l) { - /* - * This is technically bounded by L1OIP_MAX_PERFRAME but - * MAX_DFRAME_LEN_L1 < L1OIP_MAX_PERFRAME - */ - ll = (l < MAX_DFRAME_LEN_L1) ? l : MAX_DFRAME_LEN_L1; - l1oip_socket_send(hc, 0, dch->slot, 0, - hc->chan[dch->slot].tx_counter++, p, ll); - p += ll; - l -= ll; - } - skb_trim(skb, 0); - queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); - return 0; - case PH_ACTIVATE_REQ: - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n" - , __func__, dch->slot, hc->b_num + 1); - skb_trim(skb, 0); - if (test_bit(FLG_ACTIVE, &dch->Flags)) - queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb); - else - queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb); - return 0; - case PH_DEACTIVATE_REQ: - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d " - "(1..%d)\n", __func__, dch->slot, - hc->b_num + 1); - skb_trim(skb, 0); - if (test_bit(FLG_ACTIVE, &dch->Flags)) - queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb); - else - queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb); - return 0; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - struct l1oip *hc = dch->hw; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER - | MISDN_CTRL_GETPEER; - break; - case MISDN_CTRL_SETPEER: - hc->remoteip = (u32)cq->p1; - hc->remoteport = cq->p2 & 0xffff; - hc->localport = cq->p2 >> 16; - if (!hc->remoteport) - hc->remoteport = hc->localport; - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: got new ip address from user " - "space.\n", __func__); - l1oip_socket_open(hc); - break; - case MISDN_CTRL_UNSETPEER: - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: removing ip address.\n", - __func__); - hc->remoteip = 0; - l1oip_socket_open(hc); - break; - case MISDN_CTRL_GETPEER: - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: getting ip address.\n", - __func__); - cq->p1 = hc->remoteip; - cq->p2 = hc->remoteport | (hc->localport << 16); - break; - default: - printk(KERN_WARNING "%s: unknown Op %x\n", - __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -open_dchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq) -{ - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, - dch->dev.id, __builtin_return_address(0)); - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - if ((dch->dev.D.protocol != ISDN_P_NONE) && - (dch->dev.D.protocol != rq->protocol)) { - if (debug & DEBUG_HW_OPEN) - printk(KERN_WARNING "%s: change protocol %x to %x\n", - __func__, dch->dev.D.protocol, rq->protocol); - } - if (dch->dev.D.protocol != rq->protocol) - dch->dev.D.protocol = rq->protocol; - - if (test_bit(FLG_ACTIVE, &dch->Flags)) { - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - } - rq->ch = &dch->dev.D; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -static int -open_bchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq) -{ - struct bchannel *bch; - int ch; - - if (!test_channelmap(rq->adr.channel, dch->dev.channelmap)) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - ch = rq->adr.channel; /* BRI: 1=B1 2=B2 PRI: 1..15,17.. */ - bch = hc->chan[ch].bch; - if (!bch) { - printk(KERN_ERR "%s:internal error ch %d has no bch\n", - __func__, ch); - return -EINVAL; - } - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -static int -l1oip_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct l1oip *hc = dch->hw; - struct channel_req *rq; - int err = 0; - - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", - __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - switch (rq->protocol) { - case ISDN_P_TE_S0: - case ISDN_P_NT_S0: - if (hc->pri) { - err = -EINVAL; - break; - } - err = open_dchannel(hc, dch, rq); - break; - case ISDN_P_TE_E1: - case ISDN_P_NT_E1: - if (!hc->pri) { - err = -EINVAL; - break; - } - err = open_dchannel(hc, dch, rq); - break; - default: - err = open_bchannel(hc, dch, rq); - } - break; - case CLOSE_CHANNEL: - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) close from %p\n", - __func__, dch->dev.id, - __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_dctrl(dch, arg); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: unknown command %x\n", - __func__, cmd); - err = -EINVAL; - } - return err; -} - -static int -handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct l1oip *hc = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int l, ll; - unsigned char *p; - - switch (hh->prim) { - case PH_DATA_REQ: - if (skb->len <= 0) { - printk(KERN_WARNING "%s: skb too small\n", - __func__); - break; - } - if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) { - printk(KERN_WARNING "%s: skb too large\n", - __func__); - break; - } - /* check for AIS / ulaw-silence */ - l = skb->len; - if (!memchr_inv(skb->data, 0xff, l)) { - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: got AIS, not sending, " - "but counting\n", __func__); - hc->chan[bch->slot].tx_counter += l; - skb_trim(skb, 0); - queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); - return 0; - } - /* check for silence */ - l = skb->len; - if (!memchr_inv(skb->data, 0x2a, l)) { - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: got silence, not sending" - ", but counting\n", __func__); - hc->chan[bch->slot].tx_counter += l; - skb_trim(skb, 0); - queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); - return 0; - } - - /* send frame */ - p = skb->data; - l = skb->len; - while (l) { - /* - * This is technically bounded by L1OIP_MAX_PERFRAME but - * MAX_DFRAME_LEN_L1 < L1OIP_MAX_PERFRAME - */ - ll = (l < MAX_DFRAME_LEN_L1) ? l : MAX_DFRAME_LEN_L1; - l1oip_socket_send(hc, hc->codec, bch->slot, 0, - hc->chan[bch->slot].tx_counter, p, ll); - hc->chan[bch->slot].tx_counter += ll; - p += ll; - l -= ll; - } - skb_trim(skb, 0); - queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); - return 0; - case PH_ACTIVATE_REQ: - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n" - , __func__, bch->slot, hc->b_num + 1); - hc->chan[bch->slot].codecstate = 0; - test_and_set_bit(FLG_ACTIVE, &bch->Flags); - skb_trim(skb, 0); - queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb); - return 0; - case PH_DEACTIVATE_REQ: - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d " - "(1..%d)\n", __func__, bch->slot, - hc->b_num + 1); - test_and_clear_bit(FLG_ACTIVE, &bch->Flags); - skb_trim(skb, 0); - queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb); - return 0; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - struct dsp_features *features = - (struct dsp_features *)(*((u_long *)&cq->p1)); - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_HW_FEATURES_OP; - break; - case MISDN_CTRL_HW_FEATURES: /* fill features structure */ - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: HW_FEATURE request\n", - __func__); - /* create confirm */ - features->unclocked = 1; - features->unordered = 1; - break; - default: - printk(KERN_WARNING "%s: unknown Op %x\n", - __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -l1oip_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - int err = -EINVAL; - - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", - __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - test_and_clear_bit(FLG_ACTIVE, &bch->Flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - err = 0; - break; - case CONTROL_CHANNEL: - err = channel_bctrl(bch, arg); - break; - default: - printk(KERN_WARNING "%s: unknown prim(%x)\n", - __func__, cmd); - } - return err; -} - - -/* - * cleanup module and stack - */ -static void -release_card(struct l1oip *hc) -{ - int ch; - - hc->shutdown = true; - - timer_shutdown_sync(&hc->keep_tl); - timer_shutdown_sync(&hc->timeout_tl); - - cancel_work_sync(&hc->workq); - - if (hc->socket_thread) - l1oip_socket_close(hc); - - if (hc->registered && hc->chan[hc->d_idx].dch) - mISDN_unregister_device(&hc->chan[hc->d_idx].dch->dev); - for (ch = 0; ch < 128; ch++) { - if (hc->chan[ch].dch) { - mISDN_freedchannel(hc->chan[ch].dch); - kfree(hc->chan[ch].dch); - } - if (hc->chan[ch].bch) { - mISDN_freebchannel(hc->chan[ch].bch); - kfree(hc->chan[ch].bch); -#ifdef REORDER_DEBUG - dev_kfree_skb(hc->chan[ch].disorder_skb); -#endif - } - } - - spin_lock(&l1oip_lock); - list_del(&hc->list); - spin_unlock(&l1oip_lock); - - kfree(hc); -} - -static void -l1oip_cleanup(void) -{ - struct l1oip *hc, *next; - - list_for_each_entry_safe(hc, next, &l1oip_ilist, list) - release_card(hc); - - l1oip_4bit_free(); -} - - -/* - * module and stack init - */ -static int -init_card(struct l1oip *hc, int pri, int bundle) -{ - struct dchannel *dch; - struct bchannel *bch; - int ret; - int i, ch; - - spin_lock_init(&hc->socket_lock); - hc->idx = l1oip_cnt; - hc->pri = pri; - hc->d_idx = pri ? 16 : 3; - hc->b_num = pri ? 30 : 2; - hc->bundle = bundle; - if (hc->pri) - sprintf(hc->name, "l1oip-e1.%d", l1oip_cnt + 1); - else - sprintf(hc->name, "l1oip-s0.%d", l1oip_cnt + 1); - - switch (codec[l1oip_cnt]) { - case 0: /* as is */ - case 1: /* alaw */ - case 2: /* ulaw */ - case 3: /* 4bit */ - break; - default: - printk(KERN_ERR "Codec(%d) not supported.\n", - codec[l1oip_cnt]); - return -EINVAL; - } - hc->codec = codec[l1oip_cnt]; - if (debug & DEBUG_L1OIP_INIT) - printk(KERN_DEBUG "%s: using codec %d\n", - __func__, hc->codec); - - if (id[l1oip_cnt] == 0) { - printk(KERN_WARNING "Warning: No 'id' value given or " - "0, this is highly unsecure. Please use 32 " - "bit random number 0x...\n"); - } - hc->id = id[l1oip_cnt]; - if (debug & DEBUG_L1OIP_INIT) - printk(KERN_DEBUG "%s: using id 0x%x\n", __func__, hc->id); - - hc->ondemand = ondemand[l1oip_cnt]; - if (hc->ondemand && !hc->id) { - printk(KERN_ERR "%s: ondemand option only allowed in " - "conjunction with non 0 ID\n", __func__); - return -EINVAL; - } - - if (limit[l1oip_cnt]) - hc->b_num = limit[l1oip_cnt]; - if (!pri && hc->b_num > 2) { - printk(KERN_ERR "Maximum limit for BRI interface is 2 " - "channels.\n"); - return -EINVAL; - } - if (pri && hc->b_num > 126) { - printk(KERN_ERR "Maximum limit for PRI interface is 126 " - "channels.\n"); - return -EINVAL; - } - if (pri && hc->b_num > 30) { - printk(KERN_WARNING "Maximum limit for BRI interface is 30 " - "channels.\n"); - printk(KERN_WARNING "Your selection of %d channels must be " - "supported by application.\n", hc->limit); - } - - hc->remoteip = ip[l1oip_cnt << 2] << 24 - | ip[(l1oip_cnt << 2) + 1] << 16 - | ip[(l1oip_cnt << 2) + 2] << 8 - | ip[(l1oip_cnt << 2) + 3]; - hc->localport = port[l1oip_cnt]?:(L1OIP_DEFAULTPORT + l1oip_cnt); - if (remoteport[l1oip_cnt]) - hc->remoteport = remoteport[l1oip_cnt]; - else - hc->remoteport = hc->localport; - if (debug & DEBUG_L1OIP_INIT) - printk(KERN_DEBUG "%s: using local port %d remote ip " - "%d.%d.%d.%d port %d ondemand %d\n", __func__, - hc->localport, hc->remoteip >> 24, - (hc->remoteip >> 16) & 0xff, - (hc->remoteip >> 8) & 0xff, hc->remoteip & 0xff, - hc->remoteport, hc->ondemand); - - dch = kzalloc_obj(struct dchannel); - if (!dch) - return -ENOMEM; - dch->debug = debug; - mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, NULL); - dch->hw = hc; - if (pri) - dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1); - else - dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); - dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - dch->dev.D.send = handle_dmsg; - dch->dev.D.ctrl = l1oip_dctrl; - dch->dev.nrbchan = hc->b_num; - dch->slot = hc->d_idx; - hc->chan[hc->d_idx].dch = dch; - i = 1; - for (ch = 0; ch < dch->dev.nrbchan; ch++) { - if (ch == 15) - i++; - bch = kzalloc_obj(struct bchannel); - if (!bch) { - printk(KERN_ERR "%s: no memory for bchannel\n", - __func__); - return -ENOMEM; - } - bch->nr = i + ch; - bch->slot = i + ch; - bch->debug = debug; - mISDN_initbchannel(bch, MAX_DATA_MEM, 0); - bch->hw = hc; - bch->ch.send = handle_bmsg; - bch->ch.ctrl = l1oip_bctrl; - bch->ch.nr = i + ch; - list_add(&bch->ch.list, &dch->dev.bchannels); - hc->chan[i + ch].bch = bch; - set_channelmap(bch->nr, dch->dev.channelmap); - } - /* TODO: create a parent device for this driver */ - ret = mISDN_register_device(&dch->dev, NULL, hc->name); - if (ret) - return ret; - hc->registered = 1; - - if (debug & DEBUG_L1OIP_INIT) - printk(KERN_DEBUG "%s: Setting up network card(%d)\n", - __func__, l1oip_cnt + 1); - ret = l1oip_socket_open(hc); - if (ret) - return ret; - - timer_setup(&hc->keep_tl, l1oip_keepalive, 0); - hc->keep_tl.expires = jiffies + 2 * HZ; /* two seconds first time */ - add_timer(&hc->keep_tl); - - timer_setup(&hc->timeout_tl, l1oip_timeout, 0); - hc->timeout_on = 0; /* state that we have timer off */ - - return 0; -} - -static int __init -l1oip_init(void) -{ - int pri, bundle; - struct l1oip *hc; - int ret; - - printk(KERN_INFO "mISDN: Layer-1-over-IP driver Rev. %s\n", - l1oip_revision); - - if (l1oip_4bit_alloc(ulaw)) - return -ENOMEM; - - l1oip_cnt = 0; - while (l1oip_cnt < MAX_CARDS && type[l1oip_cnt]) { - switch (type[l1oip_cnt] & 0xff) { - case 1: - pri = 0; - bundle = 0; - break; - case 2: - pri = 1; - bundle = 0; - break; - case 3: - pri = 0; - bundle = 1; - break; - case 4: - pri = 1; - bundle = 1; - break; - default: - printk(KERN_ERR "Card type(%d) not supported.\n", - type[l1oip_cnt] & 0xff); - l1oip_cleanup(); - return -EINVAL; - } - - if (debug & DEBUG_L1OIP_INIT) - printk(KERN_DEBUG "%s: interface %d is %s with %s.\n", - __func__, l1oip_cnt, pri ? "PRI" : "BRI", - bundle ? "bundled IP packet for all B-channels" : - "separate IP packets for every B-channel"); - - hc = kzalloc_obj(struct l1oip, GFP_ATOMIC); - if (!hc) { - printk(KERN_ERR "No kmem for L1-over-IP driver.\n"); - l1oip_cleanup(); - return -ENOMEM; - } - INIT_WORK(&hc->workq, (void *)l1oip_send_bh); - - spin_lock(&l1oip_lock); - list_add_tail(&hc->list, &l1oip_ilist); - spin_unlock(&l1oip_lock); - - ret = init_card(hc, pri, bundle); - if (ret) { - l1oip_cleanup(); - return ret; - } - - l1oip_cnt++; - } - printk(KERN_INFO "%d virtual devices registered\n", l1oip_cnt); - return 0; -} - -module_init(l1oip_init); -module_exit(l1oip_cleanup); diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c deleted file mode 100644 index 3fbc170acf9a..000000000000 --- a/drivers/isdn/mISDN/layer1.c +++ /dev/null @@ -1,415 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - - -#include -#include -#include -#include "core.h" -#include "layer1.h" -#include "fsm.h" - -static u_int *debug; - -struct layer1 { - u_long Flags; - struct FsmInst l1m; - struct FsmTimer timer3; - struct FsmTimer timerX; - int delay; - int t3_value; - struct dchannel *dch; - dchannel_l1callback *dcb; -}; - -#define TIMER3_DEFAULT_VALUE 7000 - -static -struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL}; - -enum { - ST_L1_F2, - ST_L1_F3, - ST_L1_F4, - ST_L1_F5, - ST_L1_F6, - ST_L1_F7, - ST_L1_F8, -}; - -#define L1S_STATE_COUNT (ST_L1_F8 + 1) - -static char *strL1SState[] = -{ - "ST_L1_F2", - "ST_L1_F3", - "ST_L1_F4", - "ST_L1_F5", - "ST_L1_F6", - "ST_L1_F7", - "ST_L1_F8", -}; - -enum { - EV_PH_ACTIVATE, - EV_PH_DEACTIVATE, - EV_RESET_IND, - EV_DEACT_CNF, - EV_DEACT_IND, - EV_POWER_UP, - EV_ANYSIG_IND, - EV_INFO2_IND, - EV_INFO4_IND, - EV_TIMER_DEACT, - EV_TIMER_ACT, - EV_TIMER3, -}; - -#define L1_EVENT_COUNT (EV_TIMER3 + 1) - -static char *strL1Event[] = -{ - "EV_PH_ACTIVATE", - "EV_PH_DEACTIVATE", - "EV_RESET_IND", - "EV_DEACT_CNF", - "EV_DEACT_IND", - "EV_POWER_UP", - "EV_ANYSIG_IND", - "EV_INFO2_IND", - "EV_INFO4_IND", - "EV_TIMER_DEACT", - "EV_TIMER_ACT", - "EV_TIMER3", -}; - -static void -l1m_debug(struct FsmInst *fi, char *fmt, ...) -{ - struct layer1 *l1 = fi->userdata; - struct va_format vaf; - va_list va; - - va_start(va, fmt); - - vaf.fmt = fmt; - vaf.va = &va; - - printk(KERN_DEBUG "%s: %pV\n", dev_name(&l1->dch->dev.dev), &vaf); - - va_end(va); -} - -static void -l1_reset(struct FsmInst *fi, int event, void *arg) -{ - mISDN_FsmChangeState(fi, ST_L1_F3); -} - -static void -l1_deact_cnf(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - mISDN_FsmChangeState(fi, ST_L1_F3); - if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) - l1->dcb(l1->dch, HW_POWERUP_REQ); -} - -static void -l1_deact_req_s(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - mISDN_FsmChangeState(fi, ST_L1_F3); - mISDN_FsmRestartTimer(&l1->timerX, 550, EV_TIMER_DEACT, NULL, 2); - test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags); -} - -static void -l1_power_up_s(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) { - mISDN_FsmChangeState(fi, ST_L1_F4); - l1->dcb(l1->dch, INFO3_P8); - } else - mISDN_FsmChangeState(fi, ST_L1_F3); -} - -static void -l1_go_F5(struct FsmInst *fi, int event, void *arg) -{ - mISDN_FsmChangeState(fi, ST_L1_F5); -} - -static void -l1_go_F8(struct FsmInst *fi, int event, void *arg) -{ - mISDN_FsmChangeState(fi, ST_L1_F8); -} - -static void -l1_info2_ind(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - mISDN_FsmChangeState(fi, ST_L1_F6); - l1->dcb(l1->dch, INFO3_P8); -} - -static void -l1_info4_ind(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - mISDN_FsmChangeState(fi, ST_L1_F7); - l1->dcb(l1->dch, INFO3_P8); - if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags)) - mISDN_FsmDelTimer(&l1->timerX, 4); - if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) { - if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags)) - mISDN_FsmDelTimer(&l1->timer3, 3); - mISDN_FsmRestartTimer(&l1->timerX, 110, EV_TIMER_ACT, NULL, 2); - test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags); - } -} - -static void -l1_timer3(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags); - if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) { - if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) - l1->dcb(l1->dch, HW_D_NOBLOCKED); - l1->dcb(l1->dch, PH_DEACTIVATE_IND); - } - if (l1->l1m.state != ST_L1_F6) { - mISDN_FsmChangeState(fi, ST_L1_F3); - /* do not force anything here, we need send INFO 0 */ - } -} - -static void -l1_timer_act(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags); - test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags); - l1->dcb(l1->dch, PH_ACTIVATE_IND); -} - -static void -l1_timer_deact(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags); - test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags); - if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) - l1->dcb(l1->dch, HW_D_NOBLOCKED); - l1->dcb(l1->dch, PH_DEACTIVATE_IND); - l1->dcb(l1->dch, HW_DEACT_REQ); -} - -static void -l1_activate_s(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - mISDN_FsmRestartTimer(&l1->timer3, l1->t3_value, EV_TIMER3, NULL, 2); - test_and_set_bit(FLG_L1_T3RUN, &l1->Flags); - /* Tell HW to send INFO 1 */ - l1->dcb(l1->dch, HW_RESET_REQ); -} - -static void -l1_activate_no(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) && - (!test_bit(FLG_L1_T3RUN, &l1->Flags))) { - test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags); - if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) - l1->dcb(l1->dch, HW_D_NOBLOCKED); - l1->dcb(l1->dch, PH_DEACTIVATE_IND); - } -} - -static struct FsmNode L1SFnList[] = -{ - {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s}, - {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no}, - {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no}, - {ST_L1_F3, EV_RESET_IND, l1_reset}, - {ST_L1_F4, EV_RESET_IND, l1_reset}, - {ST_L1_F5, EV_RESET_IND, l1_reset}, - {ST_L1_F6, EV_RESET_IND, l1_reset}, - {ST_L1_F7, EV_RESET_IND, l1_reset}, - {ST_L1_F8, EV_RESET_IND, l1_reset}, - {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s}, - {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s}, - {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s}, - {ST_L1_F3, EV_POWER_UP, l1_power_up_s}, - {ST_L1_F4, EV_ANYSIG_IND, l1_go_F5}, - {ST_L1_F6, EV_ANYSIG_IND, l1_go_F8}, - {ST_L1_F7, EV_ANYSIG_IND, l1_go_F8}, - {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, - {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, - {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, - {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, - {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, - {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, - {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, - {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, - {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, - {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, - {ST_L1_F3, EV_TIMER3, l1_timer3}, - {ST_L1_F4, EV_TIMER3, l1_timer3}, - {ST_L1_F5, EV_TIMER3, l1_timer3}, - {ST_L1_F6, EV_TIMER3, l1_timer3}, - {ST_L1_F8, EV_TIMER3, l1_timer3}, - {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, - {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, - {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, - {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, - {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, - {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, - {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, -}; - -static void -release_l1(struct layer1 *l1) { - mISDN_FsmDelTimer(&l1->timerX, 0); - mISDN_FsmDelTimer(&l1->timer3, 0); - if (l1->dch) - l1->dch->l1 = NULL; - module_put(THIS_MODULE); - kfree(l1); -} - -int -l1_event(struct layer1 *l1, u_int event) -{ - int err = 0; - - if (!l1) - return -EINVAL; - switch (event) { - case HW_RESET_IND: - mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL); - break; - case HW_DEACT_IND: - mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL); - break; - case HW_POWERUP_IND: - mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL); - break; - case HW_DEACT_CNF: - mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL); - break; - case ANYSIGNAL: - mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); - break; - case LOSTFRAMING: - mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); - break; - case INFO2: - mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL); - break; - case INFO4_P8: - mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); - break; - case INFO4_P10: - mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); - break; - case PH_ACTIVATE_REQ: - if (test_bit(FLG_L1_ACTIVATED, &l1->Flags)) - l1->dcb(l1->dch, PH_ACTIVATE_IND); - else { - test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags); - mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL); - } - break; - case CLOSE_CHANNEL: - release_l1(l1); - break; - default: - if ((event & ~HW_TIMER3_VMASK) == HW_TIMER3_VALUE) { - int val = event & HW_TIMER3_VMASK; - - if (val < 5) - val = 5; - if (val > 30) - val = 30; - l1->t3_value = val; - break; - } - if (*debug & DEBUG_L1) - printk(KERN_DEBUG "%s %x unhandled\n", - __func__, event); - err = -EINVAL; - } - return err; -} -EXPORT_SYMBOL(l1_event); - -int -create_l1(struct dchannel *dch, dchannel_l1callback *dcb) { - struct layer1 *nl1; - - nl1 = kzalloc_obj(struct layer1, GFP_ATOMIC); - if (!nl1) { - printk(KERN_ERR "kmalloc struct layer1 failed\n"); - return -ENOMEM; - } - nl1->l1m.fsm = &l1fsm_s; - nl1->l1m.state = ST_L1_F3; - nl1->Flags = 0; - nl1->t3_value = TIMER3_DEFAULT_VALUE; - nl1->l1m.debug = *debug & DEBUG_L1_FSM; - nl1->l1m.userdata = nl1; - nl1->l1m.userint = 0; - nl1->l1m.printdebug = l1m_debug; - nl1->dch = dch; - nl1->dcb = dcb; - mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer3); - mISDN_FsmInitTimer(&nl1->l1m, &nl1->timerX); - __module_get(THIS_MODULE); - dch->l1 = nl1; - return 0; -} -EXPORT_SYMBOL(create_l1); - -int -Isdnl1_Init(u_int *deb) -{ - debug = deb; - l1fsm_s.state_count = L1S_STATE_COUNT; - l1fsm_s.event_count = L1_EVENT_COUNT; - l1fsm_s.strEvent = strL1Event; - l1fsm_s.strState = strL1SState; - return mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList)); -} - -void -Isdnl1_cleanup(void) -{ - mISDN_FsmFree(&l1fsm_s); -} diff --git a/drivers/isdn/mISDN/layer1.h b/drivers/isdn/mISDN/layer1.h deleted file mode 100644 index f03e86450daf..000000000000 --- a/drivers/isdn/mISDN/layer1.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * - * Layer 1 defines - * - * Copyright 2008 by Karsten Keil - */ - -#define FLG_L1_ACTIVATING 1 -#define FLG_L1_ACTIVATED 2 -#define FLG_L1_DEACTTIMER 3 -#define FLG_L1_ACTTIMER 4 -#define FLG_L1_T3RUN 5 -#define FLG_L1_PULL_REQ 6 -#define FLG_L1_UINT 7 -#define FLG_L1_DBLOCKED 8 diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c deleted file mode 100644 index b75869c9f78f..000000000000 --- a/drivers/isdn/mISDN/layer2.c +++ /dev/null @@ -1,2266 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include "core.h" -#include "fsm.h" -#include "layer2.h" - -static u_int *debug; - -static -struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL}; - -static char *strL2State[] = -{ - "ST_L2_1", - "ST_L2_2", - "ST_L2_3", - "ST_L2_4", - "ST_L2_5", - "ST_L2_6", - "ST_L2_7", - "ST_L2_8", -}; - -enum { - EV_L2_UI, - EV_L2_SABME, - EV_L2_DISC, - EV_L2_DM, - EV_L2_UA, - EV_L2_FRMR, - EV_L2_SUPER, - EV_L2_I, - EV_L2_DL_DATA, - EV_L2_ACK_PULL, - EV_L2_DL_UNITDATA, - EV_L2_DL_ESTABLISH_REQ, - EV_L2_DL_RELEASE_REQ, - EV_L2_MDL_ASSIGN, - EV_L2_MDL_REMOVE, - EV_L2_MDL_ERROR, - EV_L1_DEACTIVATE, - EV_L2_T200, - EV_L2_T203, - EV_L2_T200I, - EV_L2_T203I, - EV_L2_SET_OWN_BUSY, - EV_L2_CLEAR_OWN_BUSY, - EV_L2_FRAME_ERROR, -}; - -#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR + 1) - -static char *strL2Event[] = -{ - "EV_L2_UI", - "EV_L2_SABME", - "EV_L2_DISC", - "EV_L2_DM", - "EV_L2_UA", - "EV_L2_FRMR", - "EV_L2_SUPER", - "EV_L2_I", - "EV_L2_DL_DATA", - "EV_L2_ACK_PULL", - "EV_L2_DL_UNITDATA", - "EV_L2_DL_ESTABLISH_REQ", - "EV_L2_DL_RELEASE_REQ", - "EV_L2_MDL_ASSIGN", - "EV_L2_MDL_REMOVE", - "EV_L2_MDL_ERROR", - "EV_L1_DEACTIVATE", - "EV_L2_T200", - "EV_L2_T203", - "EV_L2_T200I", - "EV_L2_T203I", - "EV_L2_SET_OWN_BUSY", - "EV_L2_CLEAR_OWN_BUSY", - "EV_L2_FRAME_ERROR", -}; - -static void -l2m_debug(struct FsmInst *fi, char *fmt, ...) -{ - struct layer2 *l2 = fi->userdata; - struct va_format vaf; - va_list va; - - if (!(*debug & DEBUG_L2_FSM)) - return; - - va_start(va, fmt); - - vaf.fmt = fmt; - vaf.va = &va; - - printk(KERN_DEBUG "%s l2 (sapi %d tei %d): %pV\n", - mISDNDevName4ch(&l2->ch), l2->sapi, l2->tei, &vaf); - - va_end(va); -} - -inline u_int -l2headersize(struct layer2 *l2, int ui) -{ - return ((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + - (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); -} - -inline u_int -l2addrsize(struct layer2 *l2) -{ - return test_bit(FLG_LAPD, &l2->flag) ? 2 : 1; -} - -static u_int -l2_newid(struct layer2 *l2) -{ - u_int id; - - id = l2->next_id++; - if (id == 0x7fff) - l2->next_id = 1; - id <<= 16; - id |= l2->tei << 8; - id |= l2->sapi; - return id; -} - -static void -l2up(struct layer2 *l2, u_int prim, struct sk_buff *skb) -{ - int err; - - if (!l2->up) - return; - mISDN_HEAD_PRIM(skb) = prim; - mISDN_HEAD_ID(skb) = (l2->ch.nr << 16) | l2->ch.addr; - err = l2->up->send(l2->up, skb); - if (err) { - printk(KERN_WARNING "%s: dev %s err=%d\n", __func__, - mISDNDevName4ch(&l2->ch), err); - dev_kfree_skb(skb); - } -} - -static void -l2up_create(struct layer2 *l2, u_int prim, int len, void *arg) -{ - struct sk_buff *skb; - struct mISDNhead *hh; - int err; - - if (!l2->up) - return; - skb = mI_alloc_skb(len, GFP_ATOMIC); - if (!skb) - return; - hh = mISDN_HEAD_P(skb); - hh->prim = prim; - hh->id = (l2->ch.nr << 16) | l2->ch.addr; - if (len) - skb_put_data(skb, arg, len); - err = l2->up->send(l2->up, skb); - if (err) { - printk(KERN_WARNING "%s: dev %s err=%d\n", __func__, - mISDNDevName4ch(&l2->ch), err); - dev_kfree_skb(skb); - } -} - -static int -l2down_skb(struct layer2 *l2, struct sk_buff *skb) { - int ret; - - ret = l2->ch.recv(l2->ch.peer, skb); - if (ret && (*debug & DEBUG_L2_RECV)) - printk(KERN_DEBUG "l2down_skb: dev %s ret(%d)\n", - mISDNDevName4ch(&l2->ch), ret); - return ret; -} - -static int -l2down_raw(struct layer2 *l2, struct sk_buff *skb) -{ - struct mISDNhead *hh = mISDN_HEAD_P(skb); - - if (hh->prim == PH_DATA_REQ) { - if (test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) { - skb_queue_tail(&l2->down_queue, skb); - return 0; - } - l2->down_id = mISDN_HEAD_ID(skb); - } - return l2down_skb(l2, skb); -} - -static int -l2down(struct layer2 *l2, u_int prim, u_int id, struct sk_buff *skb) -{ - struct mISDNhead *hh = mISDN_HEAD_P(skb); - - hh->prim = prim; - hh->id = id; - return l2down_raw(l2, skb); -} - -static int -l2down_create(struct layer2 *l2, u_int prim, u_int id, int len, void *arg) -{ - struct sk_buff *skb; - int err; - struct mISDNhead *hh; - - skb = mI_alloc_skb(len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - hh = mISDN_HEAD_P(skb); - hh->prim = prim; - hh->id = id; - if (len) - skb_put_data(skb, arg, len); - err = l2down_raw(l2, skb); - if (err) - dev_kfree_skb(skb); - return err; -} - -static int -ph_data_confirm(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) { - struct sk_buff *nskb = skb; - int ret = -EAGAIN; - - if (test_bit(FLG_L1_NOTREADY, &l2->flag)) { - if (hh->id == l2->down_id) { - nskb = skb_dequeue(&l2->down_queue); - if (nskb) { - l2->down_id = mISDN_HEAD_ID(nskb); - if (l2down_skb(l2, nskb)) { - dev_kfree_skb(nskb); - l2->down_id = MISDN_ID_NONE; - } - } else - l2->down_id = MISDN_ID_NONE; - if (ret) { - dev_kfree_skb(skb); - ret = 0; - } - if (l2->down_id == MISDN_ID_NONE) { - test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); - mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); - } - } - } - if (!test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) { - nskb = skb_dequeue(&l2->down_queue); - if (nskb) { - l2->down_id = mISDN_HEAD_ID(nskb); - if (l2down_skb(l2, nskb)) { - dev_kfree_skb(nskb); - l2->down_id = MISDN_ID_NONE; - test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); - } - } else - test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); - } - return ret; -} - -static void -l2_timeout(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb; - struct mISDNhead *hh; - - skb = mI_alloc_skb(0, GFP_ATOMIC); - if (!skb) { - printk(KERN_WARNING "%s: L2(%d,%d) nr:%x timer %s no skb\n", - mISDNDevName4ch(&l2->ch), l2->sapi, l2->tei, - l2->ch.nr, event == EV_L2_T200 ? "T200" : "T203"); - return; - } - hh = mISDN_HEAD_P(skb); - hh->prim = event == EV_L2_T200 ? DL_TIMER200_IND : DL_TIMER203_IND; - hh->id = l2->ch.nr; - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s: L2(%d,%d) nr:%x timer %s expired\n", - mISDNDevName4ch(&l2->ch), l2->sapi, l2->tei, - l2->ch.nr, event == EV_L2_T200 ? "T200" : "T203"); - if (l2->ch.st) - l2->ch.st->own.recv(&l2->ch.st->own, skb); -} - -static int -l2mgr(struct layer2 *l2, u_int prim, void *arg) { - long c = (long)arg; - - printk(KERN_WARNING "l2mgr: dev %s addr:%x prim %x %c\n", - mISDNDevName4ch(&l2->ch), l2->id, prim, (char)c); - if (test_bit(FLG_LAPD, &l2->flag) && - !test_bit(FLG_FIXED_TEI, &l2->flag)) { - switch (c) { - case 'C': - case 'D': - case 'G': - case 'H': - l2_tei(l2, prim, (u_long)arg); - break; - } - } - return 0; -} - -static void -set_peer_busy(struct layer2 *l2) { - test_and_set_bit(FLG_PEER_BUSY, &l2->flag); - if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue)) - test_and_set_bit(FLG_L2BLOCK, &l2->flag); -} - -static void -clear_peer_busy(struct layer2 *l2) { - if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag)) - test_and_clear_bit(FLG_L2BLOCK, &l2->flag); -} - -static void -InitWin(struct layer2 *l2) -{ - int i; - - for (i = 0; i < MAX_WINDOW; i++) - l2->windowar[i] = NULL; -} - -static int -freewin(struct layer2 *l2) -{ - int i, cnt = 0; - - for (i = 0; i < MAX_WINDOW; i++) { - if (l2->windowar[i]) { - cnt++; - dev_kfree_skb(l2->windowar[i]); - l2->windowar[i] = NULL; - } - } - return cnt; -} - -static void -ReleaseWin(struct layer2 *l2) -{ - int cnt = freewin(l2); - - if (cnt) - printk(KERN_WARNING - "isdnl2 freed %d skbuffs in release\n", cnt); -} - -inline unsigned int -cansend(struct layer2 *l2) -{ - unsigned int p1; - - if (test_bit(FLG_MOD128, &l2->flag)) - p1 = (l2->vs - l2->va) % 128; - else - p1 = (l2->vs - l2->va) % 8; - return (p1 < l2->window) && !test_bit(FLG_PEER_BUSY, &l2->flag); -} - -inline void -clear_exception(struct layer2 *l2) -{ - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - test_and_clear_bit(FLG_REJEXC, &l2->flag); - test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); - clear_peer_busy(l2); -} - -static int -sethdraddr(struct layer2 *l2, u_char *header, int rsp) -{ - u_char *ptr = header; - int crbit = rsp; - - if (test_bit(FLG_LAPD, &l2->flag)) { - if (test_bit(FLG_LAPD_NET, &l2->flag)) - crbit = !crbit; - *ptr++ = (l2->sapi << 2) | (crbit ? 2 : 0); - *ptr++ = (l2->tei << 1) | 1; - return 2; - } else { - if (test_bit(FLG_ORIG, &l2->flag)) - crbit = !crbit; - if (crbit) - *ptr++ = l2->addr.B; - else - *ptr++ = l2->addr.A; - return 1; - } -} - -static inline void -enqueue_super(struct layer2 *l2, struct sk_buff *skb) -{ - if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb)) - dev_kfree_skb(skb); -} - -static inline void -enqueue_ui(struct layer2 *l2, struct sk_buff *skb) -{ - if (l2->tm) - l2_tei(l2, MDL_STATUS_UI_IND, 0); - if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb)) - dev_kfree_skb(skb); -} - -inline int -IsUI(u_char *data) -{ - return (data[0] & 0xef) == UI; -} - -inline int -IsUA(u_char *data) -{ - return (data[0] & 0xef) == UA; -} - -inline int -IsDM(u_char *data) -{ - return (data[0] & 0xef) == DM; -} - -inline int -IsDISC(u_char *data) -{ - return (data[0] & 0xef) == DISC; -} - -inline int -IsRR(u_char *data, struct layer2 *l2) -{ - if (test_bit(FLG_MOD128, &l2->flag)) - return data[0] == RR; - else - return (data[0] & 0xf) == 1; -} - -inline int -IsSFrame(u_char *data, struct layer2 *l2) -{ - register u_char d = *data; - - if (!test_bit(FLG_MOD128, &l2->flag)) - d &= 0xf; - return ((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c); -} - -inline int -IsSABME(u_char *data, struct layer2 *l2) -{ - u_char d = data[0] & ~0x10; - - return test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM; -} - -inline int -IsREJ(u_char *data, struct layer2 *l2) -{ - return test_bit(FLG_MOD128, &l2->flag) ? - data[0] == REJ : (data[0] & 0xf) == REJ; -} - -inline int -IsFRMR(u_char *data) -{ - return (data[0] & 0xef) == FRMR; -} - -inline int -IsRNR(u_char *data, struct layer2 *l2) -{ - return test_bit(FLG_MOD128, &l2->flag) ? - data[0] == RNR : (data[0] & 0xf) == RNR; -} - -static int -iframe_error(struct layer2 *l2, struct sk_buff *skb) -{ - u_int i; - int rsp = *skb->data & 0x2; - - i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1); - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - if (rsp) - return 'L'; - if (skb->len < i) - return 'N'; - if ((skb->len - i) > l2->maxlen) - return 'O'; - return 0; -} - -static int -super_error(struct layer2 *l2, struct sk_buff *skb) -{ - if (skb->len != l2addrsize(l2) + - (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1)) - return 'N'; - return 0; -} - -static int -unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp) -{ - int rsp = (*skb->data & 0x2) >> 1; - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - if (rsp != wantrsp) - return 'L'; - if (skb->len != l2addrsize(l2) + 1) - return 'N'; - return 0; -} - -static int -UI_error(struct layer2 *l2, struct sk_buff *skb) -{ - int rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - if (rsp) - return 'L'; - if (skb->len > l2->maxlen + l2addrsize(l2) + 1) - return 'O'; - return 0; -} - -static int -FRMR_error(struct layer2 *l2, struct sk_buff *skb) -{ - u_int headers = l2addrsize(l2) + 1; - u_char *datap = skb->data + headers; - int rsp = *skb->data & 0x2; - - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - if (!rsp) - return 'L'; - if (test_bit(FLG_MOD128, &l2->flag)) { - if (skb->len < headers + 5) - return 'N'; - else if (*debug & DEBUG_L2) - l2m_debug(&l2->l2m, - "FRMR information %2x %2x %2x %2x %2x", - datap[0], datap[1], datap[2], datap[3], datap[4]); - } else { - if (skb->len < headers + 3) - return 'N'; - else if (*debug & DEBUG_L2) - l2m_debug(&l2->l2m, - "FRMR information %2x %2x %2x", - datap[0], datap[1], datap[2]); - } - return 0; -} - -static unsigned int -legalnr(struct layer2 *l2, unsigned int nr) -{ - if (test_bit(FLG_MOD128, &l2->flag)) - return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128); - else - return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8); -} - -static void -setva(struct layer2 *l2, unsigned int nr) -{ - struct sk_buff *skb; - - while (l2->va != nr) { - l2->va++; - if (test_bit(FLG_MOD128, &l2->flag)) - l2->va %= 128; - else - l2->va %= 8; - if (l2->windowar[l2->sow]) { - skb_trim(l2->windowar[l2->sow], 0); - skb_queue_tail(&l2->tmp_queue, l2->windowar[l2->sow]); - l2->windowar[l2->sow] = NULL; - } - l2->sow = (l2->sow + 1) % l2->window; - } - skb = skb_dequeue(&l2->tmp_queue); - while (skb) { - dev_kfree_skb(skb); - skb = skb_dequeue(&l2->tmp_queue); - } -} - -static void -send_uframe(struct layer2 *l2, struct sk_buff *skb, u_char cmd, u_char cr) -{ - u_char tmp[MAX_L2HEADER_LEN]; - int i; - - i = sethdraddr(l2, tmp, cr); - tmp[i++] = cmd; - if (skb) - skb_trim(skb, 0); - else { - skb = mI_alloc_skb(i, GFP_ATOMIC); - if (!skb) { - printk(KERN_WARNING "%s: can't alloc skbuff in %s\n", - mISDNDevName4ch(&l2->ch), __func__); - return; - } - } - skb_put_data(skb, tmp, i); - enqueue_super(l2, skb); -} - - -inline u_char -get_PollFlag(struct layer2 *l2, struct sk_buff *skb) -{ - return skb->data[l2addrsize(l2)] & 0x10; -} - -inline u_char -get_PollFlagFree(struct layer2 *l2, struct sk_buff *skb) -{ - u_char PF; - - PF = get_PollFlag(l2, skb); - dev_kfree_skb(skb); - return PF; -} - -inline void -start_t200(struct layer2 *l2, int i) -{ - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); - test_and_set_bit(FLG_T200_RUN, &l2->flag); -} - -inline void -restart_t200(struct layer2 *l2, int i) -{ - mISDN_FsmRestartTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); - test_and_set_bit(FLG_T200_RUN, &l2->flag); -} - -inline void -stop_t200(struct layer2 *l2, int i) -{ - if (test_and_clear_bit(FLG_T200_RUN, &l2->flag)) - mISDN_FsmDelTimer(&l2->t200, i); -} - -inline void -st5_dl_release_l2l3(struct layer2 *l2) -{ - int pr; - - if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) - pr = DL_RELEASE_CNF; - else - pr = DL_RELEASE_IND; - l2up_create(l2, pr, 0, NULL); -} - -inline void -lapb_dl_release_l2l3(struct layer2 *l2, int f) -{ - if (test_bit(FLG_LAPB, &l2->flag)) - l2down_create(l2, PH_DEACTIVATE_REQ, l2_newid(l2), 0, NULL); - l2up_create(l2, f, 0, NULL); -} - -static void -establishlink(struct FsmInst *fi) -{ - struct layer2 *l2 = fi->userdata; - u_char cmd; - - clear_exception(l2); - l2->rc = 0; - cmd = (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10; - send_uframe(l2, NULL, cmd, CMD); - mISDN_FsmDelTimer(&l2->t203, 1); - restart_t200(l2, 1); - test_and_clear_bit(FLG_PEND_REL, &l2->flag); - freewin(l2); - mISDN_FsmChangeState(fi, ST_L2_5); -} - -static void -l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - if (get_PollFlagFree(l2, skb)) - l2mgr(l2, MDL_ERROR_IND, (void *) 'C'); - else - l2mgr(l2, MDL_ERROR_IND, (void *) 'D'); - -} - -static void -l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - if (get_PollFlagFree(l2, skb)) - l2mgr(l2, MDL_ERROR_IND, (void *) 'B'); - else { - l2mgr(l2, MDL_ERROR_IND, (void *) 'E'); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); - } -} - -static void -l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - if (get_PollFlagFree(l2, skb)) - l2mgr(l2, MDL_ERROR_IND, (void *) 'B'); - else - l2mgr(l2, MDL_ERROR_IND, (void *) 'E'); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); -} - -static void -l2_go_st3(struct FsmInst *fi, int event, void *arg) -{ - dev_kfree_skb((struct sk_buff *)arg); - mISDN_FsmChangeState(fi, ST_L2_3); -} - -static void -l2_mdl_assign(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - mISDN_FsmChangeState(fi, ST_L2_3); - dev_kfree_skb((struct sk_buff *)arg); - l2_tei(l2, MDL_ASSIGN_IND, 0); -} - -static void -l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_tail(&l2->ui_queue, skb); - mISDN_FsmChangeState(fi, ST_L2_2); - l2_tei(l2, MDL_ASSIGN_IND, 0); -} - -static void -l2_queue_ui(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_tail(&l2->ui_queue, skb); -} - -static void -tx_ui(struct layer2 *l2) -{ - struct sk_buff *skb; - u_char header[MAX_L2HEADER_LEN]; - int i; - - i = sethdraddr(l2, header, CMD); - if (test_bit(FLG_LAPD_NET, &l2->flag)) - header[1] = 0xff; /* tei 127 */ - header[i++] = UI; - while ((skb = skb_dequeue(&l2->ui_queue))) { - memcpy(skb_push(skb, i), header, i); - enqueue_ui(l2, skb); - } -} - -static void -l2_send_ui(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_tail(&l2->ui_queue, skb); - tx_ui(l2); -} - -static void -l2_got_ui(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_pull(skb, l2headersize(l2, 1)); -/* - * in states 1-3 for broadcast - */ - - if (l2->tm) - l2_tei(l2, MDL_STATUS_UI_IND, 0); - l2up(l2, DL_UNITDATA_IND, skb); -} - -static void -l2_establish(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - establishlink(fi); - test_and_set_bit(FLG_L3_INIT, &l2->flag); - dev_kfree_skb(skb); -} - -static void -l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->i_queue); - test_and_set_bit(FLG_L3_INIT, &l2->flag); - test_and_clear_bit(FLG_PEND_REL, &l2->flag); - dev_kfree_skb(skb); -} - -static void -l2_l3_reestablish(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->i_queue); - establishlink(fi); - test_and_set_bit(FLG_L3_INIT, &l2->flag); - dev_kfree_skb(skb); -} - -static void -l2_release(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_trim(skb, 0); - l2up(l2, DL_RELEASE_CNF, skb); -} - -static void -l2_pend_rel(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - test_and_set_bit(FLG_PEND_REL, &l2->flag); - dev_kfree_skb(skb); -} - -static void -l2_disconnect(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_purge(&l2->i_queue); - freewin(l2); - mISDN_FsmChangeState(fi, ST_L2_6); - l2->rc = 0; - send_uframe(l2, NULL, DISC | 0x10, CMD); - mISDN_FsmDelTimer(&l2->t203, 1); - restart_t200(l2, 2); - dev_kfree_skb(skb); -} - -static void -l2_start_multi(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - l2->vs = 0; - l2->va = 0; - l2->vr = 0; - l2->sow = 0; - clear_exception(l2); - send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP); - mISDN_FsmChangeState(fi, ST_L2_7); - mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); - skb_trim(skb, 0); - l2up(l2, DL_ESTABLISH_IND, skb); - if (l2->tm) - l2_tei(l2, MDL_STATUS_UP_IND, 0); -} - -static void -l2_send_UA(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); -} - -static void -l2_send_DM(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - send_uframe(l2, skb, DM | get_PollFlag(l2, skb), RSP); -} - -static void -l2_restart_multi(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - int est = 0; - - send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); - - l2mgr(l2, MDL_ERROR_IND, (void *) 'F'); - - if (l2->vs != l2->va) { - skb_queue_purge(&l2->i_queue); - est = 1; - } - - clear_exception(l2); - l2->vs = 0; - l2->va = 0; - l2->vr = 0; - l2->sow = 0; - mISDN_FsmChangeState(fi, ST_L2_7); - stop_t200(l2, 3); - mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); - - if (est) - l2up_create(l2, DL_ESTABLISH_IND, 0, NULL); -/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST, - * MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED, - * 0, NULL, 0); - */ - if (skb_queue_len(&l2->i_queue) && cansend(l2)) - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); -} - -static void -l2_stop_multi(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - mISDN_FsmChangeState(fi, ST_L2_4); - mISDN_FsmDelTimer(&l2->t203, 3); - stop_t200(l2, 4); - - send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); - skb_queue_purge(&l2->i_queue); - freewin(l2); - lapb_dl_release_l2l3(l2, DL_RELEASE_IND); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); -} - -static void -l2_connected(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - int pr = -1; - - if (!get_PollFlag(l2, skb)) { - l2_mdl_error_ua(fi, event, arg); - return; - } - dev_kfree_skb(skb); - if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) - l2_disconnect(fi, event, NULL); - if (test_and_clear_bit(FLG_L3_INIT, &l2->flag)) { - pr = DL_ESTABLISH_CNF; - } else if (l2->vs != l2->va) { - skb_queue_purge(&l2->i_queue); - pr = DL_ESTABLISH_IND; - } - stop_t200(l2, 5); - l2->vr = 0; - l2->vs = 0; - l2->va = 0; - l2->sow = 0; - mISDN_FsmChangeState(fi, ST_L2_7); - mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4); - if (pr != -1) - l2up_create(l2, pr, 0, NULL); - - if (skb_queue_len(&l2->i_queue) && cansend(l2)) - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); - - if (l2->tm) - l2_tei(l2, MDL_STATUS_UP_IND, 0); -} - -static void -l2_released(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (!get_PollFlag(l2, skb)) { - l2_mdl_error_ua(fi, event, arg); - return; - } - dev_kfree_skb(skb); - stop_t200(l2, 6); - lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); -} - -static void -l2_reestablish(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (!get_PollFlagFree(l2, skb)) { - establishlink(fi); - test_and_set_bit(FLG_L3_INIT, &l2->flag); - } -} - -static void -l2_st5_dm_release(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (get_PollFlagFree(l2, skb)) { - stop_t200(l2, 7); - if (!test_bit(FLG_L3_INIT, &l2->flag)) - skb_queue_purge(&l2->i_queue); - if (test_bit(FLG_LAPB, &l2->flag)) - l2down_create(l2, PH_DEACTIVATE_REQ, - l2_newid(l2), 0, NULL); - st5_dl_release_l2l3(l2); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); - } -} - -static void -l2_st6_dm_release(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (get_PollFlagFree(l2, skb)) { - stop_t200(l2, 8); - lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); - } -} - -static void -enquiry_cr(struct layer2 *l2, u_char typ, u_char cr, u_char pf) -{ - struct sk_buff *skb; - u_char tmp[MAX_L2HEADER_LEN]; - int i; - - i = sethdraddr(l2, tmp, cr); - if (test_bit(FLG_MOD128, &l2->flag)) { - tmp[i++] = typ; - tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); - } else - tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0); - skb = mI_alloc_skb(i, GFP_ATOMIC); - if (!skb) { - printk(KERN_WARNING "%s: isdnl2 can't alloc sbbuff in %s\n", - mISDNDevName4ch(&l2->ch), __func__); - return; - } - skb_put_data(skb, tmp, i); - enqueue_super(l2, skb); -} - -inline void -enquiry_response(struct layer2 *l2) -{ - if (test_bit(FLG_OWN_BUSY, &l2->flag)) - enquiry_cr(l2, RNR, RSP, 1); - else - enquiry_cr(l2, RR, RSP, 1); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); -} - -inline void -transmit_enquiry(struct layer2 *l2) -{ - if (test_bit(FLG_OWN_BUSY, &l2->flag)) - enquiry_cr(l2, RNR, CMD, 1); - else - enquiry_cr(l2, RR, CMD, 1); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - start_t200(l2, 9); -} - - -static void -nrerrorrecovery(struct FsmInst *fi) -{ - struct layer2 *l2 = fi->userdata; - - l2mgr(l2, MDL_ERROR_IND, (void *) 'J'); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); -} - -static void -invoke_retransmission(struct layer2 *l2, unsigned int nr) -{ - u_int p1; - - if (l2->vs != nr) { - while (l2->vs != nr) { - (l2->vs)--; - if (test_bit(FLG_MOD128, &l2->flag)) { - l2->vs %= 128; - p1 = (l2->vs - l2->va) % 128; - } else { - l2->vs %= 8; - p1 = (l2->vs - l2->va) % 8; - } - p1 = (p1 + l2->sow) % l2->window; - if (l2->windowar[p1]) - skb_queue_head(&l2->i_queue, l2->windowar[p1]); - else - printk(KERN_WARNING - "%s: windowar[%d] is NULL\n", - mISDNDevName4ch(&l2->ch), p1); - l2->windowar[p1] = NULL; - } - mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); - } -} - -static void -l2_st7_got_super(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - int PollFlag, rsp, typ = RR; - unsigned int nr; - - rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - - skb_pull(skb, l2addrsize(l2)); - if (IsRNR(skb->data, l2)) { - set_peer_busy(l2); - typ = RNR; - } else - clear_peer_busy(l2); - if (IsREJ(skb->data, l2)) - typ = REJ; - - if (test_bit(FLG_MOD128, &l2->flag)) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - nr = skb->data[1] >> 1; - } else { - PollFlag = (skb->data[0] & 0x10); - nr = (skb->data[0] >> 5) & 0x7; - } - dev_kfree_skb(skb); - - if (PollFlag) { - if (rsp) - l2mgr(l2, MDL_ERROR_IND, (void *) 'A'); - else - enquiry_response(l2); - } - if (legalnr(l2, nr)) { - if (typ == REJ) { - setva(l2, nr); - invoke_retransmission(l2, nr); - stop_t200(l2, 10); - if (mISDN_FsmAddTimer(&l2->t203, l2->T203, - EV_L2_T203, NULL, 6)) - l2m_debug(&l2->l2m, "Restart T203 ST7 REJ"); - } else if ((nr == l2->vs) && (typ == RR)) { - setva(l2, nr); - stop_t200(l2, 11); - mISDN_FsmRestartTimer(&l2->t203, l2->T203, - EV_L2_T203, NULL, 7); - } else if ((l2->va != nr) || (typ == RNR)) { - setva(l2, nr); - if (typ != RR) - mISDN_FsmDelTimer(&l2->t203, 9); - restart_t200(l2, 12); - } - if (skb_queue_len(&l2->i_queue) && (typ == RR)) - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); - } else - nrerrorrecovery(fi); -} - -static void -l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (!test_bit(FLG_L3_INIT, &l2->flag)) - skb_queue_tail(&l2->i_queue, skb); - else - dev_kfree_skb(skb); -} - -static void -l2_feed_i_pull(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_tail(&l2->i_queue, skb); - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); -} - -static void -l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_tail(&l2->i_queue, skb); -} - -static void -l2_got_iframe(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - int PollFlag, i; - u_int ns, nr; - - i = l2addrsize(l2); - if (test_bit(FLG_MOD128, &l2->flag)) { - PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); - ns = skb->data[i] >> 1; - nr = (skb->data[i + 1] >> 1) & 0x7f; - } else { - PollFlag = (skb->data[i] & 0x10); - ns = (skb->data[i] >> 1) & 0x7; - nr = (skb->data[i] >> 5) & 0x7; - } - if (test_bit(FLG_OWN_BUSY, &l2->flag)) { - dev_kfree_skb(skb); - if (PollFlag) - enquiry_response(l2); - } else { - if (l2->vr == ns) { - l2->vr++; - if (test_bit(FLG_MOD128, &l2->flag)) - l2->vr %= 128; - else - l2->vr %= 8; - test_and_clear_bit(FLG_REJEXC, &l2->flag); - if (PollFlag) - enquiry_response(l2); - else - test_and_set_bit(FLG_ACK_PEND, &l2->flag); - skb_pull(skb, l2headersize(l2, 0)); - l2up(l2, DL_DATA_IND, skb); - } else { - /* n(s)!=v(r) */ - dev_kfree_skb(skb); - if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { - if (PollFlag) - enquiry_response(l2); - } else { - enquiry_cr(l2, REJ, RSP, PollFlag); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - } - } - } - if (legalnr(l2, nr)) { - if (!test_bit(FLG_PEER_BUSY, &l2->flag) && - (fi->state == ST_L2_7)) { - if (nr == l2->vs) { - stop_t200(l2, 13); - mISDN_FsmRestartTimer(&l2->t203, l2->T203, - EV_L2_T203, NULL, 7); - } else if (nr != l2->va) - restart_t200(l2, 14); - } - setva(l2, nr); - } else { - nrerrorrecovery(fi); - return; - } - if (skb_queue_len(&l2->i_queue) && (fi->state == ST_L2_7)) - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); - if (test_and_clear_bit(FLG_ACK_PEND, &l2->flag)) - enquiry_cr(l2, RR, RSP, 0); -} - -static void -l2_got_tei(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - u_int info; - - l2->tei = (signed char)(long)arg; - set_channel_address(&l2->ch, l2->sapi, l2->tei); - info = DL_INFO_L2_CONNECT; - l2up_create(l2, DL_INFORMATION_IND, sizeof(info), &info); - if (fi->state == ST_L2_3) { - establishlink(fi); - test_and_set_bit(FLG_L3_INIT, &l2->flag); - } else - mISDN_FsmChangeState(fi, ST_L2_4); - if (skb_queue_len(&l2->ui_queue)) - tx_ui(l2); -} - -static void -l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - if (test_bit(FLG_LAPD, &l2->flag) && - test_bit(FLG_DCHAN_BUSY, &l2->flag)) { - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); - } else if (l2->rc == l2->N200) { - mISDN_FsmChangeState(fi, ST_L2_4); - test_and_clear_bit(FLG_T200_RUN, &l2->flag); - skb_queue_purge(&l2->i_queue); - l2mgr(l2, MDL_ERROR_IND, (void *) 'G'); - if (test_bit(FLG_LAPB, &l2->flag)) - l2down_create(l2, PH_DEACTIVATE_REQ, - l2_newid(l2), 0, NULL); - st5_dl_release_l2l3(l2); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); - } else { - l2->rc++; - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); - send_uframe(l2, NULL, (test_bit(FLG_MOD128, &l2->flag) ? - SABME : SABM) | 0x10, CMD); - } -} - -static void -l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - if (test_bit(FLG_LAPD, &l2->flag) && - test_bit(FLG_DCHAN_BUSY, &l2->flag)) { - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); - } else if (l2->rc == l2->N200) { - mISDN_FsmChangeState(fi, ST_L2_4); - test_and_clear_bit(FLG_T200_RUN, &l2->flag); - l2mgr(l2, MDL_ERROR_IND, (void *) 'H'); - lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); - } else { - l2->rc++; - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, - NULL, 9); - send_uframe(l2, NULL, DISC | 0x10, CMD); - } -} - -static void -l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - if (test_bit(FLG_LAPD, &l2->flag) && - test_bit(FLG_DCHAN_BUSY, &l2->flag)) { - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); - return; - } - test_and_clear_bit(FLG_T200_RUN, &l2->flag); - l2->rc = 0; - mISDN_FsmChangeState(fi, ST_L2_8); - transmit_enquiry(l2); - l2->rc++; -} - -static void -l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - if (test_bit(FLG_LAPD, &l2->flag) && - test_bit(FLG_DCHAN_BUSY, &l2->flag)) { - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); - return; - } - test_and_clear_bit(FLG_T200_RUN, &l2->flag); - if (l2->rc == l2->N200) { - l2mgr(l2, MDL_ERROR_IND, (void *) 'I'); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); - } else { - transmit_enquiry(l2); - l2->rc++; - } -} - -static void -l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - if (test_bit(FLG_LAPD, &l2->flag) && - test_bit(FLG_DCHAN_BUSY, &l2->flag)) { - mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 9); - return; - } - mISDN_FsmChangeState(fi, ST_L2_8); - transmit_enquiry(l2); - l2->rc = 0; -} - -static void -l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb, *nskb; - u_char header[MAX_L2HEADER_LEN]; - u_int i, p1; - - if (!cansend(l2)) - return; - - skb = skb_dequeue(&l2->i_queue); - if (!skb) - return; - i = sethdraddr(l2, header, CMD); - if (test_bit(FLG_MOD128, &l2->flag)) { - header[i++] = l2->vs << 1; - header[i++] = l2->vr << 1; - } else - header[i++] = (l2->vr << 5) | (l2->vs << 1); - nskb = skb_realloc_headroom(skb, i); - if (!nskb) { - printk(KERN_WARNING "%s: no headroom(%d) copy for IFrame\n", - mISDNDevName4ch(&l2->ch), i); - skb_queue_head(&l2->i_queue, skb); - return; - } - if (test_bit(FLG_MOD128, &l2->flag)) { - p1 = (l2->vs - l2->va) % 128; - l2->vs = (l2->vs + 1) % 128; - } else { - p1 = (l2->vs - l2->va) % 8; - l2->vs = (l2->vs + 1) % 8; - } - p1 = (p1 + l2->sow) % l2->window; - if (l2->windowar[p1]) { - printk(KERN_WARNING "%s: l2 try overwrite ack queue entry %d\n", - mISDNDevName4ch(&l2->ch), p1); - dev_kfree_skb(l2->windowar[p1]); - } - l2->windowar[p1] = skb; - memcpy(skb_push(nskb, i), header, i); - l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) { - mISDN_FsmDelTimer(&l2->t203, 13); - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 11); - } -} - -static void -l2_st8_got_super(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - int PollFlag, rsp, rnr = 0; - unsigned int nr; - - rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - - skb_pull(skb, l2addrsize(l2)); - - if (IsRNR(skb->data, l2)) { - set_peer_busy(l2); - rnr = 1; - } else - clear_peer_busy(l2); - - if (test_bit(FLG_MOD128, &l2->flag)) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - nr = skb->data[1] >> 1; - } else { - PollFlag = (skb->data[0] & 0x10); - nr = (skb->data[0] >> 5) & 0x7; - } - dev_kfree_skb(skb); - if (rsp && PollFlag) { - if (legalnr(l2, nr)) { - if (rnr) { - restart_t200(l2, 15); - } else { - stop_t200(l2, 16); - mISDN_FsmAddTimer(&l2->t203, l2->T203, - EV_L2_T203, NULL, 5); - setva(l2, nr); - } - invoke_retransmission(l2, nr); - mISDN_FsmChangeState(fi, ST_L2_7); - if (skb_queue_len(&l2->i_queue) && cansend(l2)) - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); - } else - nrerrorrecovery(fi); - } else { - if (!rsp && PollFlag) - enquiry_response(l2); - if (legalnr(l2, nr)) - setva(l2, nr); - else - nrerrorrecovery(fi); - } -} - -static void -l2_got_FRMR(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_pull(skb, l2addrsize(l2) + 1); - - if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ - (IsUA(skb->data) && (fi->state == ST_L2_7))) { - l2mgr(l2, MDL_ERROR_IND, (void *) 'K'); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); - } - dev_kfree_skb(skb); -} - -static void -l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->ui_queue); - l2->tei = GROUP_TEI; - mISDN_FsmChangeState(fi, ST_L2_1); -} - -static void -l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->ui_queue); - l2->tei = GROUP_TEI; - l2up_create(l2, DL_RELEASE_IND, 0, NULL); - mISDN_FsmChangeState(fi, ST_L2_1); -} - -static void -l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - freewin(l2); - l2->tei = GROUP_TEI; - stop_t200(l2, 17); - st5_dl_release_l2l3(l2); - mISDN_FsmChangeState(fi, ST_L2_1); -} - -static void -l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->ui_queue); - l2->tei = GROUP_TEI; - stop_t200(l2, 18); - l2up_create(l2, DL_RELEASE_IND, 0, NULL); - mISDN_FsmChangeState(fi, ST_L2_1); -} - -static void -l2_tei_remove(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - freewin(l2); - l2->tei = GROUP_TEI; - stop_t200(l2, 17); - mISDN_FsmDelTimer(&l2->t203, 19); - l2up_create(l2, DL_RELEASE_IND, 0, NULL); -/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST, - * MGR_SHORTSTATUS_IND, SSTATUS_L2_RELEASED, - * 0, NULL, 0); - */ - mISDN_FsmChangeState(fi, ST_L2_1); -} - -static void -l2_st14_persistent_da(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) - l2up(l2, DL_RELEASE_IND, skb); - else - dev_kfree_skb(skb); -} - -static void -l2_st5_persistent_da(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - freewin(l2); - stop_t200(l2, 19); - st5_dl_release_l2l3(l2); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); - dev_kfree_skb(skb); -} - -static void -l2_st6_persistent_da(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_purge(&l2->ui_queue); - stop_t200(l2, 20); - l2up(l2, DL_RELEASE_CNF, skb); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); -} - -static void -l2_persistent_da(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - freewin(l2); - stop_t200(l2, 19); - mISDN_FsmDelTimer(&l2->t203, 19); - l2up(l2, DL_RELEASE_IND, skb); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); -} - -static void -l2_set_own_busy(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (!test_and_set_bit(FLG_OWN_BUSY, &l2->flag)) { - enquiry_cr(l2, RNR, RSP, 0); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - } - dev_kfree_skb(skb); -} - -static void -l2_clear_own_busy(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (!test_and_clear_bit(FLG_OWN_BUSY, &l2->flag)) { - enquiry_cr(l2, RR, RSP, 0); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - } - dev_kfree_skb(skb); -} - -static void -l2_frame_error(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - l2mgr(l2, MDL_ERROR_IND, arg); -} - -static void -l2_frame_error_reest(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - l2mgr(l2, MDL_ERROR_IND, arg); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); -} - -static struct FsmNode L2FnList[] = -{ - {ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign}, - {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3}, - {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish}, - {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3}, - {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, - {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, - {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release}, - {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel}, - {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect}, - {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect}, - {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest}, - {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull}, - {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_1, EV_L2_DL_UNITDATA, l2_queue_ui_assign}, - {ST_L2_2, EV_L2_DL_UNITDATA, l2_queue_ui}, - {ST_L2_3, EV_L2_DL_UNITDATA, l2_queue_ui}, - {ST_L2_4, EV_L2_DL_UNITDATA, l2_send_ui}, - {ST_L2_5, EV_L2_DL_UNITDATA, l2_send_ui}, - {ST_L2_6, EV_L2_DL_UNITDATA, l2_send_ui}, - {ST_L2_7, EV_L2_DL_UNITDATA, l2_send_ui}, - {ST_L2_8, EV_L2_DL_UNITDATA, l2_send_ui}, - {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, - {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, - {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, - {ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove}, - {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove}, - {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove}, - {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove}, - {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove}, - {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_4, EV_L2_SABME, l2_start_multi}, - {ST_L2_5, EV_L2_SABME, l2_send_UA}, - {ST_L2_6, EV_L2_SABME, l2_send_DM}, - {ST_L2_7, EV_L2_SABME, l2_restart_multi}, - {ST_L2_8, EV_L2_SABME, l2_restart_multi}, - {ST_L2_4, EV_L2_DISC, l2_send_DM}, - {ST_L2_5, EV_L2_DISC, l2_send_DM}, - {ST_L2_6, EV_L2_DISC, l2_send_UA}, - {ST_L2_7, EV_L2_DISC, l2_stop_multi}, - {ST_L2_8, EV_L2_DISC, l2_stop_multi}, - {ST_L2_4, EV_L2_UA, l2_mdl_error_ua}, - {ST_L2_5, EV_L2_UA, l2_connected}, - {ST_L2_6, EV_L2_UA, l2_released}, - {ST_L2_7, EV_L2_UA, l2_mdl_error_ua}, - {ST_L2_8, EV_L2_UA, l2_mdl_error_ua}, - {ST_L2_4, EV_L2_DM, l2_reestablish}, - {ST_L2_5, EV_L2_DM, l2_st5_dm_release}, - {ST_L2_6, EV_L2_DM, l2_st6_dm_release}, - {ST_L2_7, EV_L2_DM, l2_mdl_error_dm}, - {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm}, - {ST_L2_1, EV_L2_UI, l2_got_ui}, - {ST_L2_2, EV_L2_UI, l2_got_ui}, - {ST_L2_3, EV_L2_UI, l2_got_ui}, - {ST_L2_4, EV_L2_UI, l2_got_ui}, - {ST_L2_5, EV_L2_UI, l2_got_ui}, - {ST_L2_6, EV_L2_UI, l2_got_ui}, - {ST_L2_7, EV_L2_UI, l2_got_ui}, - {ST_L2_8, EV_L2_UI, l2_got_ui}, - {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_7, EV_L2_SUPER, l2_st7_got_super}, - {ST_L2_8, EV_L2_SUPER, l2_st8_got_super}, - {ST_L2_7, EV_L2_I, l2_got_iframe}, - {ST_L2_8, EV_L2_I, l2_got_iframe}, - {ST_L2_5, EV_L2_T200, l2_timeout}, - {ST_L2_6, EV_L2_T200, l2_timeout}, - {ST_L2_7, EV_L2_T200, l2_timeout}, - {ST_L2_8, EV_L2_T200, l2_timeout}, - {ST_L2_7, EV_L2_T203, l2_timeout}, - {ST_L2_5, EV_L2_T200I, l2_st5_tout_200}, - {ST_L2_6, EV_L2_T200I, l2_st6_tout_200}, - {ST_L2_7, EV_L2_T200I, l2_st7_tout_200}, - {ST_L2_8, EV_L2_T200I, l2_st8_tout_200}, - {ST_L2_7, EV_L2_T203I, l2_st7_tout_203}, - {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, - {ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, - {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, - {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, - {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, - {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error}, - {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error}, - {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error}, - {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest}, - {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest}, - {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistent_da}, - {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove}, - {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove}, - {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistent_da}, - {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistent_da}, - {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistent_da}, - {ST_L2_7, EV_L1_DEACTIVATE, l2_persistent_da}, - {ST_L2_8, EV_L1_DEACTIVATE, l2_persistent_da}, -}; - -static int -ph_data_indication(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) -{ - u_char *datap = skb->data; - int ret = -EINVAL; - int psapi, ptei; - u_int l; - int c = 0; - - l = l2addrsize(l2); - if (skb->len <= l) { - mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N'); - return ret; - } - if (test_bit(FLG_LAPD, &l2->flag)) { /* Maybe not needed */ - psapi = *datap++; - ptei = *datap++; - if ((psapi & 1) || !(ptei & 1)) { - printk(KERN_WARNING - "%s l2 D-channel frame wrong EA0/EA1\n", - mISDNDevName4ch(&l2->ch)); - return ret; - } - psapi >>= 2; - ptei >>= 1; - if (psapi != l2->sapi) { - /* not our business */ - if (*debug & DEBUG_L2) - printk(KERN_DEBUG "%s: sapi %d/%d mismatch\n", - mISDNDevName4ch(&l2->ch), psapi, - l2->sapi); - dev_kfree_skb(skb); - return 0; - } - if ((ptei != l2->tei) && (ptei != GROUP_TEI)) { - /* not our business */ - if (*debug & DEBUG_L2) - printk(KERN_DEBUG "%s: tei %d/%d mismatch\n", - mISDNDevName4ch(&l2->ch), ptei, l2->tei); - dev_kfree_skb(skb); - return 0; - } - } else - datap += l; - if (!(*datap & 1)) { /* I-Frame */ - c = iframe_error(l2, skb); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb); - } else if (IsSFrame(datap, l2)) { /* S-Frame */ - c = super_error(l2, skb); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb); - } else if (IsUI(datap)) { - c = UI_error(l2, skb); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb); - } else if (IsSABME(datap, l2)) { - c = unnum_error(l2, skb, CMD); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb); - } else if (IsUA(datap)) { - c = unnum_error(l2, skb, RSP); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb); - } else if (IsDISC(datap)) { - c = unnum_error(l2, skb, CMD); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb); - } else if (IsDM(datap)) { - c = unnum_error(l2, skb, RSP); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb); - } else if (IsFRMR(datap)) { - c = FRMR_error(l2, skb); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_FRMR, skb); - } else - c = 'L'; - if (c) { - printk(KERN_WARNING "%s:l2 D-channel frame error %c\n", - mISDNDevName4ch(&l2->ch), c); - mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *)(long)c); - } - return ret; -} - -static int -l2_send(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct layer2 *l2 = container_of(ch, struct layer2, ch); - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int ret = -EINVAL; - - if (*debug & DEBUG_L2_RECV) - printk(KERN_DEBUG "%s: %s prim(%x) id(%x) sapi(%d) tei(%d)\n", - __func__, mISDNDevName4ch(&l2->ch), hh->prim, hh->id, - l2->sapi, l2->tei); - if (hh->prim == DL_INTERN_MSG) { - struct mISDNhead *chh = hh + 1; /* saved copy */ - - *hh = *chh; - if (*debug & DEBUG_L2_RECV) - printk(KERN_DEBUG "%s: prim(%x) id(%x) internal msg\n", - mISDNDevName4ch(&l2->ch), hh->prim, hh->id); - } - switch (hh->prim) { - case PH_DATA_IND: - ret = ph_data_indication(l2, hh, skb); - break; - case PH_DATA_CNF: - ret = ph_data_confirm(l2, hh, skb); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_L1_ACTIV, &l2->flag); - l2up_create(l2, MPH_ACTIVATE_IND, 0, NULL); - if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) - ret = mISDN_FsmEvent(&l2->l2m, - EV_L2_DL_ESTABLISH_REQ, skb); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_L1_ACTIV, &l2->flag); - l2up_create(l2, MPH_DEACTIVATE_IND, 0, NULL); - ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, skb); - break; - case MPH_INFORMATION_IND: - if (!l2->up) - break; - ret = l2->up->send(l2->up, skb); - break; - case DL_DATA_REQ: - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb); - break; - case DL_UNITDATA_REQ: - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb); - break; - case DL_ESTABLISH_REQ: - if (test_bit(FLG_LAPB, &l2->flag)) - test_and_set_bit(FLG_ORIG, &l2->flag); - if (test_bit(FLG_L1_ACTIV, &l2->flag)) { - if (test_bit(FLG_LAPD, &l2->flag) || - test_bit(FLG_ORIG, &l2->flag)) - ret = mISDN_FsmEvent(&l2->l2m, - EV_L2_DL_ESTABLISH_REQ, skb); - } else { - if (test_bit(FLG_LAPD, &l2->flag) || - test_bit(FLG_ORIG, &l2->flag)) { - test_and_set_bit(FLG_ESTAB_PEND, - &l2->flag); - } - ret = l2down(l2, PH_ACTIVATE_REQ, l2_newid(l2), - skb); - } - break; - case DL_RELEASE_REQ: - if (test_bit(FLG_LAPB, &l2->flag)) - l2down_create(l2, PH_DEACTIVATE_REQ, - l2_newid(l2), 0, NULL); - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ, - skb); - break; - case DL_TIMER200_IND: - mISDN_FsmEvent(&l2->l2m, EV_L2_T200I, NULL); - break; - case DL_TIMER203_IND: - mISDN_FsmEvent(&l2->l2m, EV_L2_T203I, NULL); - break; - default: - if (*debug & DEBUG_L2) - l2m_debug(&l2->l2m, "l2 unknown pr %04x", - hh->prim); - } - if (ret) { - dev_kfree_skb(skb); - ret = 0; - } - return ret; -} - -int -tei_l2(struct layer2 *l2, u_int cmd, u_long arg) -{ - int ret = -EINVAL; - - if (*debug & DEBUG_L2_TEI) - printk(KERN_DEBUG "%s: cmd(%x) in %s\n", - mISDNDevName4ch(&l2->ch), cmd, __func__); - switch (cmd) { - case (MDL_ASSIGN_REQ): - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, (void *)arg); - break; - case (MDL_REMOVE_REQ): - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, NULL); - break; - case (MDL_ERROR_IND): - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL); - break; - case (MDL_ERROR_RSP): - /* ETS 300-125 5.3.2.1 Test: TC13010 */ - printk(KERN_NOTICE "%s: MDL_ERROR|REQ (tei_l2)\n", - mISDNDevName4ch(&l2->ch)); - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL); - break; - } - return ret; -} - -static void -release_l2(struct layer2 *l2) -{ - mISDN_FsmDelTimer(&l2->t200, 21); - mISDN_FsmDelTimer(&l2->t203, 16); - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - skb_queue_purge(&l2->down_queue); - ReleaseWin(l2); - if (test_bit(FLG_LAPD, &l2->flag)) { - TEIrelease(l2); - if (l2->ch.st) - l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, - CLOSE_CHANNEL, NULL); - } - kfree(l2); -} - -static int -l2_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct layer2 *l2 = container_of(ch, struct layer2, ch); - u_int info; - - if (*debug & DEBUG_L2_CTRL) - printk(KERN_DEBUG "%s: %s cmd(%x)\n", - mISDNDevName4ch(ch), __func__, cmd); - - switch (cmd) { - case OPEN_CHANNEL: - if (test_bit(FLG_LAPD, &l2->flag)) { - set_channel_address(&l2->ch, l2->sapi, l2->tei); - info = DL_INFO_L2_CONNECT; - l2up_create(l2, DL_INFORMATION_IND, - sizeof(info), &info); - } - break; - case CLOSE_CHANNEL: - if (l2->ch.peer) - l2->ch.peer->ctrl(l2->ch.peer, CLOSE_CHANNEL, NULL); - release_l2(l2); - break; - } - return 0; -} - -struct layer2 * -create_l2(struct mISDNchannel *ch, u_int protocol, u_long options, int tei, - int sapi) -{ - struct layer2 *l2; - struct channel_req rq; - - l2 = kzalloc_obj(struct layer2); - if (!l2) { - printk(KERN_ERR "kzalloc layer2 failed\n"); - return NULL; - } - l2->next_id = 1; - l2->down_id = MISDN_ID_NONE; - l2->up = ch; - l2->ch.st = ch->st; - l2->ch.send = l2_send; - l2->ch.ctrl = l2_ctrl; - switch (protocol) { - case ISDN_P_LAPD_NT: - test_and_set_bit(FLG_LAPD, &l2->flag); - test_and_set_bit(FLG_LAPD_NET, &l2->flag); - test_and_set_bit(FLG_MOD128, &l2->flag); - l2->sapi = sapi; - l2->maxlen = MAX_DFRAME_LEN; - if (test_bit(OPTION_L2_PMX, &options)) - l2->window = 7; - else - l2->window = 1; - if (test_bit(OPTION_L2_PTP, &options)) - test_and_set_bit(FLG_PTP, &l2->flag); - if (test_bit(OPTION_L2_FIXEDTEI, &options)) - test_and_set_bit(FLG_FIXED_TEI, &l2->flag); - l2->tei = tei; - l2->T200 = 1000; - l2->N200 = 3; - l2->T203 = 10000; - if (test_bit(OPTION_L2_PMX, &options)) - rq.protocol = ISDN_P_NT_E1; - else - rq.protocol = ISDN_P_NT_S0; - rq.adr.channel = 0; - l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq); - break; - case ISDN_P_LAPD_TE: - test_and_set_bit(FLG_LAPD, &l2->flag); - test_and_set_bit(FLG_MOD128, &l2->flag); - test_and_set_bit(FLG_ORIG, &l2->flag); - l2->sapi = sapi; - l2->maxlen = MAX_DFRAME_LEN; - if (test_bit(OPTION_L2_PMX, &options)) - l2->window = 7; - else - l2->window = 1; - if (test_bit(OPTION_L2_PTP, &options)) - test_and_set_bit(FLG_PTP, &l2->flag); - if (test_bit(OPTION_L2_FIXEDTEI, &options)) - test_and_set_bit(FLG_FIXED_TEI, &l2->flag); - l2->tei = tei; - l2->T200 = 1000; - l2->N200 = 3; - l2->T203 = 10000; - if (test_bit(OPTION_L2_PMX, &options)) - rq.protocol = ISDN_P_TE_E1; - else - rq.protocol = ISDN_P_TE_S0; - rq.adr.channel = 0; - l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq); - break; - case ISDN_P_B_X75SLP: - test_and_set_bit(FLG_LAPB, &l2->flag); - l2->window = 7; - l2->maxlen = MAX_DATA_SIZE; - l2->T200 = 1000; - l2->N200 = 4; - l2->T203 = 5000; - l2->addr.A = 3; - l2->addr.B = 1; - break; - default: - printk(KERN_ERR "layer2 create failed prt %x\n", - protocol); - kfree(l2); - return NULL; - } - skb_queue_head_init(&l2->i_queue); - skb_queue_head_init(&l2->ui_queue); - skb_queue_head_init(&l2->down_queue); - skb_queue_head_init(&l2->tmp_queue); - InitWin(l2); - l2->l2m.fsm = &l2fsm; - if (test_bit(FLG_LAPB, &l2->flag) || - test_bit(FLG_FIXED_TEI, &l2->flag) || - test_bit(FLG_LAPD_NET, &l2->flag)) - l2->l2m.state = ST_L2_4; - else - l2->l2m.state = ST_L2_1; - l2->l2m.debug = *debug; - l2->l2m.userdata = l2; - l2->l2m.userint = 0; - l2->l2m.printdebug = l2m_debug; - - mISDN_FsmInitTimer(&l2->l2m, &l2->t200); - mISDN_FsmInitTimer(&l2->l2m, &l2->t203); - return l2; -} - -static int -x75create(struct channel_req *crq) -{ - struct layer2 *l2; - - if (crq->protocol != ISDN_P_B_X75SLP) - return -EPROTONOSUPPORT; - l2 = create_l2(crq->ch, crq->protocol, 0, 0, 0); - if (!l2) - return -ENOMEM; - crq->ch = &l2->ch; - crq->protocol = ISDN_P_B_HDLC; - return 0; -} - -static struct Bprotocol X75SLP = { - .Bprotocols = (1 << (ISDN_P_B_X75SLP & ISDN_P_B_MASK)), - .name = "X75SLP", - .create = x75create -}; - -int -Isdnl2_Init(u_int *deb) -{ - int res; - debug = deb; - mISDN_register_Bprotocol(&X75SLP); - l2fsm.state_count = L2_STATE_COUNT; - l2fsm.event_count = L2_EVENT_COUNT; - l2fsm.strEvent = strL2Event; - l2fsm.strState = strL2State; - res = mISDN_FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList)); - if (res) - goto error; - res = TEIInit(deb); - if (res) - goto error_fsm; - return 0; - -error_fsm: - mISDN_FsmFree(&l2fsm); -error: - mISDN_unregister_Bprotocol(&X75SLP); - return res; -} - -void -Isdnl2_cleanup(void) -{ - mISDN_unregister_Bprotocol(&X75SLP); - TEIFree(); - mISDN_FsmFree(&l2fsm); -} diff --git a/drivers/isdn/mISDN/layer2.h b/drivers/isdn/mISDN/layer2.h deleted file mode 100644 index c466fd94aa02..000000000000 --- a/drivers/isdn/mISDN/layer2.h +++ /dev/null @@ -1,131 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Layer 2 defines - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include "fsm.h" - -#define MAX_WINDOW 8 - -struct manager { - struct mISDNchannel ch; - struct mISDNchannel bcast; - u_long options; - struct list_head layer2; - rwlock_t lock; - struct FsmInst deact; - struct FsmTimer datimer; - struct sk_buff_head sendq; - struct mISDNchannel *up; - u_int nextid; - u_int lastid; -}; - -struct teimgr { - int ri; - int rcnt; - struct FsmInst tei_m; - struct FsmTimer timer; - int tval, nval; - struct layer2 *l2; - struct manager *mgr; -}; - -struct laddr { - u_char A; - u_char B; -}; - -struct layer2 { - struct list_head list; - struct mISDNchannel ch; - u_long flag; - int id; - struct mISDNchannel *up; - signed char sapi; - signed char tei; - struct laddr addr; - u_int maxlen; - struct teimgr *tm; - u_int vs, va, vr; - int rc; - u_int window; - u_int sow; - struct FsmInst l2m; - struct FsmTimer t200, t203; - int T200, N200, T203; - u_int next_id; - u_int down_id; - struct sk_buff *windowar[MAX_WINDOW]; - struct sk_buff_head i_queue; - struct sk_buff_head ui_queue; - struct sk_buff_head down_queue; - struct sk_buff_head tmp_queue; -}; - -enum { - ST_L2_1, - ST_L2_2, - ST_L2_3, - ST_L2_4, - ST_L2_5, - ST_L2_6, - ST_L2_7, - ST_L2_8, -}; - -#define L2_STATE_COUNT (ST_L2_8 + 1) - -extern struct layer2 *create_l2(struct mISDNchannel *, u_int, - u_long, int, int); -extern int tei_l2(struct layer2 *, u_int, u_long arg); - - -/* from tei.c */ -extern int l2_tei(struct layer2 *, u_int, u_long arg); -extern void TEIrelease(struct layer2 *); -extern int TEIInit(u_int *); -extern void TEIFree(void); - -#define MAX_L2HEADER_LEN 4 - -#define RR 0x01 -#define RNR 0x05 -#define REJ 0x09 -#define SABME 0x6f -#define SABM 0x2f -#define DM 0x0f -#define UI 0x03 -#define DISC 0x43 -#define UA 0x63 -#define FRMR 0x87 -#define XID 0xaf - -#define CMD 0 -#define RSP 1 - -#define LC_FLUSH_WAIT 1 - -#define FLG_LAPB 0 -#define FLG_LAPD 1 -#define FLG_ORIG 2 -#define FLG_MOD128 3 -#define FLG_PEND_REL 4 -#define FLG_L3_INIT 5 -#define FLG_T200_RUN 6 -#define FLG_ACK_PEND 7 -#define FLG_REJEXC 8 -#define FLG_OWN_BUSY 9 -#define FLG_PEER_BUSY 10 -#define FLG_DCHAN_BUSY 11 -#define FLG_L1_ACTIV 12 -#define FLG_ESTAB_PEND 13 -#define FLG_PTP 14 -#define FLG_FIXED_TEI 15 -#define FLG_L2BLOCK 16 -#define FLG_L1_NOTREADY 17 -#define FLG_LAPD_NET 18 diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c deleted file mode 100644 index 77b900db1cac..000000000000 --- a/drivers/isdn/mISDN/socket.c +++ /dev/null @@ -1,825 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include -#include "core.h" - -static u_int *debug; - -static struct proto mISDN_proto = { - .name = "misdn", - .owner = THIS_MODULE, - .obj_size = sizeof(struct mISDN_sock) -}; - -#define _pms(sk) ((struct mISDN_sock *)sk) - -static struct mISDN_sock_list data_sockets = { - .lock = __RW_LOCK_UNLOCKED(data_sockets.lock) -}; - -static struct mISDN_sock_list base_sockets = { - .lock = __RW_LOCK_UNLOCKED(base_sockets.lock) -}; - -#define L2_HEADER_LEN 4 - -static inline struct sk_buff * -_l2_alloc_skb(unsigned int len, gfp_t gfp_mask) -{ - struct sk_buff *skb; - - skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask); - if (likely(skb)) - skb_reserve(skb, L2_HEADER_LEN); - return skb; -} - -static void -mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk) -{ - write_lock_bh(&l->lock); - sk_add_node(sk, &l->head); - write_unlock_bh(&l->lock); -} - -static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk) -{ - write_lock_bh(&l->lock); - sk_del_node_init(sk); - write_unlock_bh(&l->lock); -} - -static int -mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDN_sock *msk; - int err; - - msk = container_of(ch, struct mISDN_sock, ch); - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb); - if (msk->sk.sk_state == MISDN_CLOSED) - return -EUNATCH; - __net_timestamp(skb); - err = sock_queue_rcv_skb(&msk->sk, skb); - if (err) - printk(KERN_WARNING "%s: error %d\n", __func__, err); - return err; -} - -static int -mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct mISDN_sock *msk; - - msk = container_of(ch, struct mISDN_sock, ch); - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - msk->sk.sk_state = MISDN_CLOSED; - break; - } - return 0; -} - -static inline void -mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) -{ - struct __kernel_old_timeval tv; - - if (_pms(sk)->cmask & MISDN_TIME_STAMP) { - skb_get_timestamp(skb, &tv); - put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv); - } -} - -static int -mISDN_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, - int flags) -{ - struct sk_buff *skb; - struct sock *sk = sock->sk; - - int copied, err; - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n", - __func__, (int)len, flags, _pms(sk)->ch.nr, - sk->sk_protocol); - if (flags & (MSG_OOB)) - return -EOPNOTSUPP; - - if (sk->sk_state == MISDN_CLOSED) - return 0; - - skb = skb_recv_datagram(sk, flags, &err); - if (!skb) - return err; - - if (msg->msg_name) { - DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name); - - maddr->family = AF_ISDN; - maddr->dev = _pms(sk)->dev->id; - if ((sk->sk_protocol == ISDN_P_LAPD_TE) || - (sk->sk_protocol == ISDN_P_LAPD_NT)) { - maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff; - maddr->tei = (mISDN_HEAD_ID(skb) >> 8) & 0xff; - maddr->sapi = mISDN_HEAD_ID(skb) & 0xff; - } else { - maddr->channel = _pms(sk)->ch.nr; - maddr->sapi = _pms(sk)->ch.addr & 0xFF; - maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xFF; - } - msg->msg_namelen = sizeof(*maddr); - } - - copied = skb->len + MISDN_HEADER_LEN; - if (len < copied) { - if (flags & MSG_PEEK) - refcount_dec(&skb->users); - else - skb_queue_head(&sk->sk_receive_queue, skb); - return -ENOSPC; - } - memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb), - MISDN_HEADER_LEN); - - err = skb_copy_datagram_msg(skb, 0, msg, copied); - - mISDN_sock_cmsg(sk, msg, skb); - - skb_free_datagram(sk, skb); - - return err ? : copied; -} - -static int -mISDN_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct sk_buff *skb; - int err = -ENOMEM; - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n", - __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr, - sk->sk_protocol); - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL | MSG_ERRQUEUE)) - return -EINVAL; - - if (len < MISDN_HEADER_LEN) - return -EINVAL; - - if (sk->sk_state != MISDN_BOUND) - return -EBADFD; - - lock_sock(sk); - - skb = _l2_alloc_skb(len, GFP_KERNEL); - if (!skb) - goto done; - - if (memcpy_from_msg(skb_put(skb, len), msg, len)) { - err = -EFAULT; - goto done; - } - - memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN); - skb_pull(skb, MISDN_HEADER_LEN); - - if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) { - /* if we have a address, we use it */ - DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name); - mISDN_HEAD_ID(skb) = maddr->channel; - } else { /* use default for L2 messages */ - if ((sk->sk_protocol == ISDN_P_LAPD_TE) || - (sk->sk_protocol == ISDN_P_LAPD_NT)) - mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr; - } - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s: ID:%x\n", - __func__, mISDN_HEAD_ID(skb)); - - err = -ENODEV; - if (!_pms(sk)->ch.peer) - goto done; - err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb); - if (err) - goto done; - else { - skb = NULL; - err = len; - } - -done: - kfree_skb(skb); - release_sock(sk); - return err; -} - -static int -data_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); - if (!sk) - return 0; - switch (sk->sk_protocol) { - case ISDN_P_TE_S0: - case ISDN_P_NT_S0: - case ISDN_P_TE_E1: - case ISDN_P_NT_E1: - if (sk->sk_state == MISDN_BOUND) - delete_channel(&_pms(sk)->ch); - else - mISDN_sock_unlink(&data_sockets, sk); - break; - case ISDN_P_LAPD_TE: - case ISDN_P_LAPD_NT: - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - case ISDN_P_B_X75SLP: - case ISDN_P_B_L2DTMF: - case ISDN_P_B_L2DSP: - case ISDN_P_B_L2DSPHDLC: - delete_channel(&_pms(sk)->ch); - mISDN_sock_unlink(&data_sockets, sk); - break; - } - - lock_sock(sk); - - sock_orphan(sk); - skb_queue_purge(&sk->sk_receive_queue); - - release_sock(sk); - sock_put(sk); - - return 0; -} - -static int -data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p) -{ - struct mISDN_ctrl_req cq; - int err = -EINVAL, val[2]; - struct mISDNchannel *bchan, *next; - - lock_sock(sk); - if (!_pms(sk)->dev) { - err = -ENODEV; - goto done; - } - switch (cmd) { - case IMCTRLREQ: - if (copy_from_user(&cq, p, sizeof(cq))) { - err = -EFAULT; - break; - } - if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) { - list_for_each_entry_safe(bchan, next, - &_pms(sk)->dev->bchannels, list) { - if (bchan->nr == cq.channel) { - err = bchan->ctrl(bchan, - CONTROL_CHANNEL, &cq); - break; - } - } - } else - err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D, - CONTROL_CHANNEL, &cq); - if (err) - break; - if (copy_to_user(p, &cq, sizeof(cq))) - err = -EFAULT; - break; - case IMCLEAR_L2: - if (sk->sk_protocol != ISDN_P_LAPD_NT) { - err = -EINVAL; - break; - } - val[0] = cmd; - if (get_user(val[1], (int __user *)p)) { - err = -EFAULT; - break; - } - err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr, - CONTROL_CHANNEL, val); - break; - case IMHOLD_L1: - if (sk->sk_protocol != ISDN_P_LAPD_NT - && sk->sk_protocol != ISDN_P_LAPD_TE) { - err = -EINVAL; - break; - } - val[0] = cmd; - if (get_user(val[1], (int __user *)p)) { - err = -EFAULT; - break; - } - err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr, - CONTROL_CHANNEL, val); - break; - default: - err = -EINVAL; - break; - } -done: - release_sock(sk); - return err; -} - -static int -data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - int err = 0, id; - struct sock *sk = sock->sk; - struct mISDNdevice *dev; - struct mISDNversion ver; - - switch (cmd) { - case IMGETVERSION: - ver.major = MISDN_MAJOR_VERSION; - ver.minor = MISDN_MINOR_VERSION; - ver.release = MISDN_RELEASE; - if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) - err = -EFAULT; - break; - case IMGETCOUNT: - id = get_mdevice_count(); - if (put_user(id, (int __user *)arg)) - err = -EFAULT; - break; - case IMGETDEVINFO: - if (get_user(id, (int __user *)arg)) { - err = -EFAULT; - break; - } - dev = get_mdevice(id); - if (dev) { - struct mISDN_devinfo di; - - memset(&di, 0, sizeof(di)); - di.id = dev->id; - di.Dprotocols = dev->Dprotocols; - di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); - di.protocol = dev->D.protocol; - memcpy(di.channelmap, dev->channelmap, - sizeof(di.channelmap)); - di.nrbchan = dev->nrbchan; - strscpy(di.name, dev_name(&dev->dev), sizeof(di.name)); - if (copy_to_user((void __user *)arg, &di, sizeof(di))) - err = -EFAULT; - } else - err = -ENODEV; - break; - default: - if (sk->sk_state == MISDN_BOUND) - err = data_sock_ioctl_bound(sk, cmd, - (void __user *)arg); - else - err = -ENOTCONN; - } - return err; -} - -static int data_sock_setsockopt(struct socket *sock, int level, int optname, - sockptr_t optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - int err = 0, opt = 0; - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s(%p, %d, %x, optval, %d)\n", __func__, sock, - level, optname, optlen); - - lock_sock(sk); - - switch (optname) { - case MISDN_TIME_STAMP: - err = copy_safe_from_sockptr(&opt, sizeof(opt), - optval, optlen); - if (err) - break; - - if (opt) - _pms(sk)->cmask |= MISDN_TIME_STAMP; - else - _pms(sk)->cmask &= ~MISDN_TIME_STAMP; - break; - default: - err = -ENOPROTOOPT; - break; - } - release_sock(sk); - return err; -} - -static int data_sock_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - int len, opt; - - if (get_user(len, optlen)) - return -EFAULT; - - if (len != sizeof(char)) - return -EINVAL; - - switch (optname) { - case MISDN_TIME_STAMP: - if (_pms(sk)->cmask & MISDN_TIME_STAMP) - opt = 1; - else - opt = 0; - - if (put_user(opt, optval)) - return -EFAULT; - break; - default: - return -ENOPROTOOPT; - } - - return 0; -} - -static int -data_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr_len) -{ - struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; - struct sock *sk = sock->sk; - struct sock *csk; - int err = 0; - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); - if (addr_len != sizeof(struct sockaddr_mISDN)) - return -EINVAL; - if (!maddr || maddr->family != AF_ISDN) - return -EINVAL; - - lock_sock(sk); - - if (_pms(sk)->dev) { - err = -EALREADY; - goto done; - } - _pms(sk)->dev = get_mdevice(maddr->dev); - if (!_pms(sk)->dev) { - err = -ENODEV; - goto done; - } - - if (sk->sk_protocol < ISDN_P_B_START) { - read_lock_bh(&data_sockets.lock); - sk_for_each(csk, &data_sockets.head) { - if (sk == csk) - continue; - if (_pms(csk)->dev != _pms(sk)->dev) - continue; - if (csk->sk_protocol >= ISDN_P_B_START) - continue; - if (IS_ISDN_P_TE(csk->sk_protocol) - == IS_ISDN_P_TE(sk->sk_protocol)) - continue; - read_unlock_bh(&data_sockets.lock); - err = -EBUSY; - goto done; - } - read_unlock_bh(&data_sockets.lock); - } - - _pms(sk)->ch.send = mISDN_send; - _pms(sk)->ch.ctrl = mISDN_ctrl; - - switch (sk->sk_protocol) { - case ISDN_P_TE_S0: - case ISDN_P_NT_S0: - case ISDN_P_TE_E1: - case ISDN_P_NT_E1: - mISDN_sock_unlink(&data_sockets, sk); - err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch, - sk->sk_protocol, maddr); - if (err) - mISDN_sock_link(&data_sockets, sk); - break; - case ISDN_P_LAPD_TE: - case ISDN_P_LAPD_NT: - err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch, - sk->sk_protocol, maddr); - break; - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - case ISDN_P_B_X75SLP: - case ISDN_P_B_L2DTMF: - case ISDN_P_B_L2DSP: - case ISDN_P_B_L2DSPHDLC: - err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch, - sk->sk_protocol, maddr); - break; - default: - err = -EPROTONOSUPPORT; - } - if (err) - goto done; - sk->sk_state = MISDN_BOUND; - _pms(sk)->ch.protocol = sk->sk_protocol; - -done: - release_sock(sk); - return err; -} - -static int -data_sock_getname(struct socket *sock, struct sockaddr *addr, - int peer) -{ - struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; - struct sock *sk = sock->sk; - - if (!_pms(sk)->dev) - return -EBADFD; - - lock_sock(sk); - - maddr->family = AF_ISDN; - maddr->dev = _pms(sk)->dev->id; - maddr->channel = _pms(sk)->ch.nr; - maddr->sapi = _pms(sk)->ch.addr & 0xff; - maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff; - release_sock(sk); - return sizeof(*maddr); -} - -static const struct proto_ops data_sock_ops = { - .family = PF_ISDN, - .owner = THIS_MODULE, - .release = data_sock_release, - .ioctl = data_sock_ioctl, - .bind = data_sock_bind, - .getname = data_sock_getname, - .sendmsg = mISDN_sock_sendmsg, - .recvmsg = mISDN_sock_recvmsg, - .poll = datagram_poll, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = data_sock_setsockopt, - .getsockopt = data_sock_getsockopt, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static int -data_sock_create(struct net *net, struct socket *sock, int protocol, int kern) -{ - struct sock *sk; - - if (sock->type != SOCK_DGRAM) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock->ops = &data_sock_ops; - sock->state = SS_UNCONNECTED; - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - sk->sk_state = MISDN_OPEN; - mISDN_sock_link(&data_sockets, sk); - - return 0; -} - -static int -base_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); - if (!sk) - return 0; - - mISDN_sock_unlink(&base_sockets, sk); - sock_orphan(sk); - sock_put(sk); - - return 0; -} - -static int -base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - int err = 0, id; - struct mISDNdevice *dev; - struct mISDNversion ver; - - switch (cmd) { - case IMGETVERSION: - ver.major = MISDN_MAJOR_VERSION; - ver.minor = MISDN_MINOR_VERSION; - ver.release = MISDN_RELEASE; - if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) - err = -EFAULT; - break; - case IMGETCOUNT: - id = get_mdevice_count(); - if (put_user(id, (int __user *)arg)) - err = -EFAULT; - break; - case IMGETDEVINFO: - if (get_user(id, (int __user *)arg)) { - err = -EFAULT; - break; - } - dev = get_mdevice(id); - if (dev) { - struct mISDN_devinfo di; - - memset(&di, 0, sizeof(di)); - di.id = dev->id; - di.Dprotocols = dev->Dprotocols; - di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); - di.protocol = dev->D.protocol; - memcpy(di.channelmap, dev->channelmap, - sizeof(di.channelmap)); - di.nrbchan = dev->nrbchan; - strscpy(di.name, dev_name(&dev->dev), sizeof(di.name)); - if (copy_to_user((void __user *)arg, &di, sizeof(di))) - err = -EFAULT; - } else - err = -ENODEV; - break; - case IMSETDEVNAME: - { - struct mISDN_devrename dn; - if (copy_from_user(&dn, (void __user *)arg, - sizeof(dn))) { - err = -EFAULT; - break; - } - dn.name[sizeof(dn.name) - 1] = '\0'; - dev = get_mdevice(dn.id); - if (dev) - err = device_rename(&dev->dev, dn.name); - else - err = -ENODEV; - } - break; - default: - err = -EINVAL; - } - return err; -} - -static int -base_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr_len) -{ - struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; - struct sock *sk = sock->sk; - int err = 0; - - if (addr_len < sizeof(struct sockaddr_mISDN)) - return -EINVAL; - - if (!maddr || maddr->family != AF_ISDN) - return -EINVAL; - - lock_sock(sk); - - if (_pms(sk)->dev) { - err = -EALREADY; - goto done; - } - - _pms(sk)->dev = get_mdevice(maddr->dev); - if (!_pms(sk)->dev) { - err = -ENODEV; - goto done; - } - sk->sk_state = MISDN_BOUND; - -done: - release_sock(sk); - return err; -} - -static const struct proto_ops base_sock_ops = { - .family = PF_ISDN, - .owner = THIS_MODULE, - .release = base_sock_release, - .ioctl = base_sock_ioctl, - .bind = base_sock_bind, - .getname = sock_no_getname, - .sendmsg = sock_no_sendmsg, - .recvmsg = sock_no_recvmsg, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - - -static int -base_sock_create(struct net *net, struct socket *sock, int protocol, int kern) -{ - struct sock *sk; - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - if (!capable(CAP_NET_RAW)) - return -EPERM; - - sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - sock->ops = &base_sock_ops; - sock->state = SS_UNCONNECTED; - sock_reset_flag(sk, SOCK_ZAPPED); - sk->sk_protocol = protocol; - sk->sk_state = MISDN_OPEN; - mISDN_sock_link(&base_sockets, sk); - - return 0; -} - -static int -mISDN_sock_create(struct net *net, struct socket *sock, int proto, int kern) -{ - int err = -EPROTONOSUPPORT; - - switch (proto) { - case ISDN_P_BASE: - err = base_sock_create(net, sock, proto, kern); - break; - case ISDN_P_TE_S0: - case ISDN_P_NT_S0: - case ISDN_P_TE_E1: - case ISDN_P_NT_E1: - case ISDN_P_LAPD_TE: - case ISDN_P_LAPD_NT: - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - case ISDN_P_B_X75SLP: - case ISDN_P_B_L2DTMF: - case ISDN_P_B_L2DSP: - case ISDN_P_B_L2DSPHDLC: - err = data_sock_create(net, sock, proto, kern); - break; - default: - return err; - } - - return err; -} - -static const struct net_proto_family mISDN_sock_family_ops = { - .owner = THIS_MODULE, - .family = PF_ISDN, - .create = mISDN_sock_create, -}; - -int -misdn_sock_init(u_int *deb) -{ - int err; - - debug = deb; - err = sock_register(&mISDN_sock_family_ops); - if (err) - printk(KERN_ERR "%s: error(%d)\n", __func__, err); - return err; -} - -void -misdn_sock_cleanup(void) -{ - sock_unregister(PF_ISDN); -} diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c deleted file mode 100644 index 4e96684af0aa..000000000000 --- a/drivers/isdn/mISDN/stack.c +++ /dev/null @@ -1,654 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include - -#include "core.h" - -static u_int *debug; - -static inline void -_queue_message(struct mISDNstack *st, struct sk_buff *skb) -{ - struct mISDNhead *hh = mISDN_HEAD_P(skb); - - if (*debug & DEBUG_QUEUE_FUNC) - printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n", - __func__, hh->prim, hh->id, skb); - skb_queue_tail(&st->msgq, skb); - if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) { - test_and_set_bit(mISDN_STACK_WORK, &st->status); - wake_up_interruptible(&st->workq); - } -} - -static int -mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb) -{ - _queue_message(ch->st, skb); - return 0; -} - -static struct mISDNchannel * -get_channel4id(struct mISDNstack *st, u_int id) -{ - struct mISDNchannel *ch; - - mutex_lock(&st->lmutex); - list_for_each_entry(ch, &st->layer2, list) { - if (id == ch->nr) - goto unlock; - } - ch = NULL; -unlock: - mutex_unlock(&st->lmutex); - return ch; -} - -static void -send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb) -{ - struct sock *sk; - struct sk_buff *cskb = NULL; - - read_lock(&sl->lock); - sk_for_each(sk, &sl->head) { - if (sk->sk_state != MISDN_BOUND) - continue; - if (!cskb) - cskb = skb_copy(skb, GFP_ATOMIC); - if (!cskb) { - printk(KERN_WARNING "%s no skb\n", __func__); - break; - } - if (!sock_queue_rcv_skb(sk, cskb)) - cskb = NULL; - } - read_unlock(&sl->lock); - dev_kfree_skb(cskb); -} - -static void -send_layer2(struct mISDNstack *st, struct sk_buff *skb) -{ - struct sk_buff *cskb; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - struct mISDNchannel *ch; - int ret; - - if (!st) - return; - mutex_lock(&st->lmutex); - if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */ - list_for_each_entry(ch, &st->layer2, list) { - if (list_is_last(&ch->list, &st->layer2)) { - cskb = skb; - skb = NULL; - } else { - cskb = skb_copy(skb, GFP_KERNEL); - } - if (cskb) { - ret = ch->send(ch, cskb); - if (ret) { - if (*debug & DEBUG_SEND_ERR) - printk(KERN_DEBUG - "%s ch%d prim(%x) addr(%x)" - " err %d\n", - __func__, ch->nr, - hh->prim, ch->addr, ret); - dev_kfree_skb(cskb); - } - } else { - printk(KERN_WARNING "%s ch%d addr %x no mem\n", - __func__, ch->nr, ch->addr); - goto out; - } - } - } else { - list_for_each_entry(ch, &st->layer2, list) { - if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) { - ret = ch->send(ch, skb); - if (!ret) - skb = NULL; - goto out; - } - } - ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb); - if (!ret) - skb = NULL; - else if (*debug & DEBUG_SEND_ERR) - printk(KERN_DEBUG - "%s mgr prim(%x) err %d\n", - __func__, hh->prim, ret); - } -out: - mutex_unlock(&st->lmutex); - dev_kfree_skb(skb); -} - -static inline int -send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb) -{ - struct mISDNhead *hh = mISDN_HEAD_P(skb); - struct mISDNchannel *ch; - int lm; - - lm = hh->prim & MISDN_LAYERMASK; - if (*debug & DEBUG_QUEUE_FUNC) - printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n", - __func__, hh->prim, hh->id, skb); - if (lm == 0x1) { - if (!hlist_empty(&st->l1sock.head)) { - __net_timestamp(skb); - send_socklist(&st->l1sock, skb); - } - return st->layer1->send(st->layer1, skb); - } else if (lm == 0x2) { - if (!hlist_empty(&st->l1sock.head)) - send_socklist(&st->l1sock, skb); - send_layer2(st, skb); - return 0; - } else if (lm == 0x4) { - ch = get_channel4id(st, hh->id); - if (ch) - return ch->send(ch, skb); - else - printk(KERN_WARNING - "%s: dev(%s) prim(%x) id(%x) no channel\n", - __func__, dev_name(&st->dev->dev), hh->prim, - hh->id); - } else if (lm == 0x8) { - WARN_ON(lm == 0x8); - ch = get_channel4id(st, hh->id); - if (ch) - return ch->send(ch, skb); - else - printk(KERN_WARNING - "%s: dev(%s) prim(%x) id(%x) no channel\n", - __func__, dev_name(&st->dev->dev), hh->prim, - hh->id); - } else { - /* broadcast not handled yet */ - printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n", - __func__, dev_name(&st->dev->dev), hh->prim); - } - return -ESRCH; -} - -static void -do_clear_stack(struct mISDNstack *st) -{ -} - -static int -mISDNStackd(void *data) -{ - struct mISDNstack *st = data; -#ifdef MISDN_MSG_STATS - u64 utime, stime; -#endif - int err = 0; - - sigfillset(¤t->blocked); - if (*debug & DEBUG_MSG_THREAD) - printk(KERN_DEBUG "mISDNStackd %s started\n", - dev_name(&st->dev->dev)); - - if (st->notify != NULL) { - complete(st->notify); - st->notify = NULL; - } - - for (;;) { - struct sk_buff *skb; - - if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) { - test_and_clear_bit(mISDN_STACK_WORK, &st->status); - test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); - } else - test_and_set_bit(mISDN_STACK_RUNNING, &st->status); - while (test_bit(mISDN_STACK_WORK, &st->status)) { - skb = skb_dequeue(&st->msgq); - if (!skb) { - test_and_clear_bit(mISDN_STACK_WORK, - &st->status); - /* test if a race happens */ - skb = skb_dequeue(&st->msgq); - if (!skb) - continue; - test_and_set_bit(mISDN_STACK_WORK, - &st->status); - } -#ifdef MISDN_MSG_STATS - st->msg_cnt++; -#endif - err = send_msg_to_layer(st, skb); - if (unlikely(err)) { - if (*debug & DEBUG_SEND_ERR) - printk(KERN_DEBUG - "%s: %s prim(%x) id(%x) " - "send call(%d)\n", - __func__, dev_name(&st->dev->dev), - mISDN_HEAD_PRIM(skb), - mISDN_HEAD_ID(skb), err); - dev_kfree_skb(skb); - continue; - } - if (unlikely(test_bit(mISDN_STACK_STOPPED, - &st->status))) { - test_and_clear_bit(mISDN_STACK_WORK, - &st->status); - test_and_clear_bit(mISDN_STACK_RUNNING, - &st->status); - break; - } - } - if (test_bit(mISDN_STACK_CLEARING, &st->status)) { - test_and_set_bit(mISDN_STACK_STOPPED, &st->status); - test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); - do_clear_stack(st); - test_and_clear_bit(mISDN_STACK_CLEARING, &st->status); - test_and_set_bit(mISDN_STACK_RESTART, &st->status); - } - if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) { - test_and_clear_bit(mISDN_STACK_STOPPED, &st->status); - test_and_set_bit(mISDN_STACK_RUNNING, &st->status); - if (!skb_queue_empty(&st->msgq)) - test_and_set_bit(mISDN_STACK_WORK, - &st->status); - } - if (test_bit(mISDN_STACK_ABORT, &st->status)) - break; - if (st->notify != NULL) { - complete(st->notify); - st->notify = NULL; - } -#ifdef MISDN_MSG_STATS - st->sleep_cnt++; -#endif - test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); - wait_event_interruptible(st->workq, (st->status & - mISDN_STACK_ACTION_MASK)); - if (*debug & DEBUG_MSG_THREAD) - printk(KERN_DEBUG "%s: %s wake status %08lx\n", - __func__, dev_name(&st->dev->dev), st->status); - test_and_set_bit(mISDN_STACK_ACTIVE, &st->status); - - test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status); - - if (test_bit(mISDN_STACK_STOPPED, &st->status)) { - test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); -#ifdef MISDN_MSG_STATS - st->stopped_cnt++; -#endif - } - } -#ifdef MISDN_MSG_STATS - printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d " - "msg %d sleep %d stopped\n", - dev_name(&st->dev->dev), st->msg_cnt, st->sleep_cnt, - st->stopped_cnt); - task_cputime(st->thread, &utime, &stime); - printk(KERN_DEBUG - "mISDNStackd daemon for %s utime(%llu) stime(%llu)\n", - dev_name(&st->dev->dev), utime, stime); - printk(KERN_DEBUG - "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n", - dev_name(&st->dev->dev), st->thread->nvcsw, st->thread->nivcsw); - printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n", - dev_name(&st->dev->dev)); -#endif - test_and_set_bit(mISDN_STACK_KILLED, &st->status); - test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); - test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); - test_and_clear_bit(mISDN_STACK_ABORT, &st->status); - skb_queue_purge(&st->msgq); - st->thread = NULL; - if (st->notify != NULL) { - complete(st->notify); - st->notify = NULL; - } - return 0; -} - -static int -l1_receive(struct mISDNchannel *ch, struct sk_buff *skb) -{ - if (!ch->st) - return -ENODEV; - __net_timestamp(skb); - _queue_message(ch->st, skb); - return 0; -} - -void -set_channel_address(struct mISDNchannel *ch, u_int sapi, u_int tei) -{ - ch->addr = sapi | (tei << 8); -} - -void -__add_layer2(struct mISDNchannel *ch, struct mISDNstack *st) -{ - list_add_tail(&ch->list, &st->layer2); -} - -void -add_layer2(struct mISDNchannel *ch, struct mISDNstack *st) -{ - mutex_lock(&st->lmutex); - __add_layer2(ch, st); - mutex_unlock(&st->lmutex); -} - -static int -st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - if (!ch->st || !ch->st->layer1) - return -EINVAL; - return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg); -} - -int -create_stack(struct mISDNdevice *dev) -{ - struct mISDNstack *newst; - int err; - DECLARE_COMPLETION_ONSTACK(done); - - newst = kzalloc_obj(struct mISDNstack); - if (!newst) { - printk(KERN_ERR "kmalloc mISDN_stack failed\n"); - return -ENOMEM; - } - newst->dev = dev; - INIT_LIST_HEAD(&newst->layer2); - INIT_HLIST_HEAD(&newst->l1sock.head); - rwlock_init(&newst->l1sock.lock); - init_waitqueue_head(&newst->workq); - skb_queue_head_init(&newst->msgq); - mutex_init(&newst->lmutex); - dev->D.st = newst; - err = create_teimanager(dev); - if (err) { - printk(KERN_ERR "kmalloc teimanager failed\n"); - kfree(newst); - return err; - } - dev->teimgr->peer = &newst->own; - dev->teimgr->recv = mISDN_queue_message; - dev->teimgr->st = newst; - newst->layer1 = &dev->D; - dev->D.recv = l1_receive; - dev->D.peer = &newst->own; - newst->own.st = newst; - newst->own.ctrl = st_own_ctrl; - newst->own.send = mISDN_queue_message; - newst->own.recv = mISDN_queue_message; - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: st(%s)\n", __func__, - dev_name(&newst->dev->dev)); - newst->notify = &done; - newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s", - dev_name(&newst->dev->dev)); - if (IS_ERR(newst->thread)) { - err = PTR_ERR(newst->thread); - printk(KERN_ERR - "mISDN:cannot create kernel thread for %s (%d)\n", - dev_name(&newst->dev->dev), err); - delete_teimanager(dev->teimgr); - kfree(newst); - } else - wait_for_completion(&done); - return err; -} - -int -connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch, - u_int protocol, struct sockaddr_mISDN *adr) -{ - struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch); - struct channel_req rq; - int err; - - - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev_name(&dev->dev), protocol, adr->dev, - adr->channel, adr->sapi, adr->tei); - switch (protocol) { - case ISDN_P_NT_S0: - case ISDN_P_NT_E1: - case ISDN_P_TE_S0: - case ISDN_P_TE_E1: - ch->recv = mISDN_queue_message; - ch->peer = &dev->D.st->own; - ch->st = dev->D.st; - rq.protocol = protocol; - rq.adr.channel = adr->channel; - err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); - printk(KERN_DEBUG "%s: ret %d (dev %d)\n", __func__, err, - dev->id); - if (err) - return err; - write_lock_bh(&dev->D.st->l1sock.lock); - sk_add_node(&msk->sk, &dev->D.st->l1sock.head); - write_unlock_bh(&dev->D.st->l1sock.lock); - break; - default: - return -ENOPROTOOPT; - } - return 0; -} - -int -connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch, - u_int protocol, struct sockaddr_mISDN *adr) -{ - struct channel_req rq, rq2; - int pmask, err; - struct Bprotocol *bp; - - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev_name(&dev->dev), protocol, - adr->dev, adr->channel, adr->sapi, - adr->tei); - ch->st = dev->D.st; - pmask = 1 << (protocol & ISDN_P_B_MASK); - if (pmask & dev->Bprotocols) { - rq.protocol = protocol; - rq.adr = *adr; - err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); - if (err) - return err; - ch->recv = rq.ch->send; - ch->peer = rq.ch; - rq.ch->recv = ch->send; - rq.ch->peer = ch; - rq.ch->st = dev->D.st; - } else { - bp = get_Bprotocol4mask(pmask); - if (!bp) - return -ENOPROTOOPT; - rq2.protocol = protocol; - rq2.adr = *adr; - rq2.ch = ch; - err = bp->create(&rq2); - if (err) - return err; - ch->recv = rq2.ch->send; - ch->peer = rq2.ch; - rq2.ch->st = dev->D.st; - rq.protocol = rq2.protocol; - rq.adr = *adr; - err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); - if (err) { - rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL); - return err; - } - rq2.ch->recv = rq.ch->send; - rq2.ch->peer = rq.ch; - rq.ch->recv = rq2.ch->send; - rq.ch->peer = rq2.ch; - rq.ch->st = dev->D.st; - } - ch->protocol = protocol; - ch->nr = rq.ch->nr; - return 0; -} - -int -create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch, - u_int protocol, struct sockaddr_mISDN *adr) -{ - struct channel_req rq; - int err; - - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev_name(&dev->dev), protocol, - adr->dev, adr->channel, adr->sapi, - adr->tei); - rq.protocol = ISDN_P_TE_S0; - if (dev->Dprotocols & (1 << ISDN_P_TE_E1)) - rq.protocol = ISDN_P_TE_E1; - switch (protocol) { - case ISDN_P_LAPD_NT: - rq.protocol = ISDN_P_NT_S0; - if (dev->Dprotocols & (1 << ISDN_P_NT_E1)) - rq.protocol = ISDN_P_NT_E1; - fallthrough; - case ISDN_P_LAPD_TE: - ch->recv = mISDN_queue_message; - ch->peer = &dev->D.st->own; - ch->st = dev->D.st; - rq.adr.channel = 0; - err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); - printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err); - if (err) - break; - rq.protocol = protocol; - rq.adr = *adr; - rq.ch = ch; - err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq); - printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err); - if (!err) { - if ((protocol == ISDN_P_LAPD_NT) && !rq.ch) - break; - add_layer2(rq.ch, dev->D.st); - rq.ch->recv = mISDN_queue_message; - rq.ch->peer = &dev->D.st->own; - rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */ - } - break; - default: - err = -EPROTONOSUPPORT; - } - return err; -} - -void -delete_channel(struct mISDNchannel *ch) -{ - struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch); - struct mISDNchannel *pch; - - if (!ch->st) { - printk(KERN_WARNING "%s: no stack\n", __func__); - return; - } - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__, - dev_name(&ch->st->dev->dev), ch->protocol); - if (ch->protocol >= ISDN_P_B_START) { - if (ch->peer) { - ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL); - ch->peer = NULL; - } - return; - } - switch (ch->protocol) { - case ISDN_P_NT_S0: - case ISDN_P_TE_S0: - case ISDN_P_NT_E1: - case ISDN_P_TE_E1: - write_lock_bh(&ch->st->l1sock.lock); - sk_del_node_init(&msk->sk); - write_unlock_bh(&ch->st->l1sock.lock); - ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL); - break; - case ISDN_P_LAPD_TE: - pch = get_channel4id(ch->st, ch->nr); - if (pch) { - mutex_lock(&ch->st->lmutex); - list_del(&pch->list); - mutex_unlock(&ch->st->lmutex); - pch->ctrl(pch, CLOSE_CHANNEL, NULL); - pch = ch->st->dev->teimgr; - pch->ctrl(pch, CLOSE_CHANNEL, NULL); - } else - printk(KERN_WARNING "%s: no l2 channel\n", - __func__); - break; - case ISDN_P_LAPD_NT: - pch = ch->st->dev->teimgr; - if (pch) { - pch->ctrl(pch, CLOSE_CHANNEL, NULL); - } else - printk(KERN_WARNING "%s: no l2 channel\n", - __func__); - break; - default: - break; - } - return; -} - -void -delete_stack(struct mISDNdevice *dev) -{ - struct mISDNstack *st = dev->D.st; - DECLARE_COMPLETION_ONSTACK(done); - - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: st(%s)\n", __func__, - dev_name(&st->dev->dev)); - if (dev->teimgr) - delete_teimanager(dev->teimgr); - if (st->thread) { - if (st->notify) { - printk(KERN_WARNING "%s: notifier in use\n", - __func__); - complete(st->notify); - } - st->notify = &done; - test_and_set_bit(mISDN_STACK_ABORT, &st->status); - test_and_set_bit(mISDN_STACK_WAKEUP, &st->status); - wake_up_interruptible(&st->workq); - wait_for_completion(&done); - } - if (!list_empty(&st->layer2)) - printk(KERN_WARNING "%s: layer2 list not empty\n", - __func__); - if (!hlist_empty(&st->l1sock.head)) - printk(KERN_WARNING "%s: layer1 list not empty\n", - __func__); - kfree(st); -} - -void -mISDN_initstack(u_int *dp) -{ - debug = dp; -} diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c deleted file mode 100644 index 2bad3083be90..000000000000 --- a/drivers/isdn/mISDN/tei.c +++ /dev/null @@ -1,1416 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ -#include "layer2.h" -#include -#include -#include "core.h" - -#define ID_REQUEST 1 -#define ID_ASSIGNED 2 -#define ID_DENIED 3 -#define ID_CHK_REQ 4 -#define ID_CHK_RES 5 -#define ID_REMOVE 6 -#define ID_VERIFY 7 - -#define TEI_ENTITY_ID 0xf - -#define MGR_PH_ACTIVE 16 -#define MGR_PH_NOTREADY 17 - -#define DATIMER_VAL 10000 - -static u_int *debug; - -static struct Fsm deactfsm = {NULL, 0, 0, NULL, NULL}; -static struct Fsm teifsmu = {NULL, 0, 0, NULL, NULL}; -static struct Fsm teifsmn = {NULL, 0, 0, NULL, NULL}; - -enum { - ST_L1_DEACT, - ST_L1_DEACT_PENDING, - ST_L1_ACTIV, -}; -#define DEACT_STATE_COUNT (ST_L1_ACTIV + 1) - -static char *strDeactState[] = -{ - "ST_L1_DEACT", - "ST_L1_DEACT_PENDING", - "ST_L1_ACTIV", -}; - -enum { - EV_ACTIVATE, - EV_ACTIVATE_IND, - EV_DEACTIVATE, - EV_DEACTIVATE_IND, - EV_UI, - EV_DATIMER, -}; - -#define DEACT_EVENT_COUNT (EV_DATIMER + 1) - -static char *strDeactEvent[] = -{ - "EV_ACTIVATE", - "EV_ACTIVATE_IND", - "EV_DEACTIVATE", - "EV_DEACTIVATE_IND", - "EV_UI", - "EV_DATIMER", -}; - -static void -da_debug(struct FsmInst *fi, char *fmt, ...) -{ - struct manager *mgr = fi->userdata; - struct va_format vaf; - va_list va; - - if (!(*debug & DEBUG_L2_TEIFSM)) - return; - - va_start(va, fmt); - - vaf.fmt = fmt; - vaf.va = &va; - - printk(KERN_DEBUG "mgr(%d): %pV\n", mgr->ch.st->dev->id, &vaf); - - va_end(va); -} - -static void -da_activate(struct FsmInst *fi, int event, void *arg) -{ - struct manager *mgr = fi->userdata; - - if (fi->state == ST_L1_DEACT_PENDING) - mISDN_FsmDelTimer(&mgr->datimer, 1); - mISDN_FsmChangeState(fi, ST_L1_ACTIV); -} - -static void -da_deactivate_ind(struct FsmInst *fi, int event, void *arg) -{ - mISDN_FsmChangeState(fi, ST_L1_DEACT); -} - -static void -da_deactivate(struct FsmInst *fi, int event, void *arg) -{ - struct manager *mgr = fi->userdata; - struct layer2 *l2; - u_long flags; - - read_lock_irqsave(&mgr->lock, flags); - list_for_each_entry(l2, &mgr->layer2, list) { - if (l2->l2m.state > ST_L2_4) { - /* have still activ TEI */ - read_unlock_irqrestore(&mgr->lock, flags); - return; - } - } - read_unlock_irqrestore(&mgr->lock, flags); - /* All TEI are inactiv */ - if (!test_bit(OPTION_L1_HOLD, &mgr->options)) { - mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, - NULL, 1); - mISDN_FsmChangeState(fi, ST_L1_DEACT_PENDING); - } -} - -static void -da_ui(struct FsmInst *fi, int event, void *arg) -{ - struct manager *mgr = fi->userdata; - - /* restart da timer */ - if (!test_bit(OPTION_L1_HOLD, &mgr->options)) { - mISDN_FsmDelTimer(&mgr->datimer, 2); - mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, - NULL, 2); - } -} - -static void -da_timer(struct FsmInst *fi, int event, void *arg) -{ - struct manager *mgr = fi->userdata; - struct layer2 *l2; - u_long flags; - - /* check again */ - read_lock_irqsave(&mgr->lock, flags); - list_for_each_entry(l2, &mgr->layer2, list) { - if (l2->l2m.state > ST_L2_4) { - /* have still activ TEI */ - read_unlock_irqrestore(&mgr->lock, flags); - mISDN_FsmChangeState(fi, ST_L1_ACTIV); - return; - } - } - read_unlock_irqrestore(&mgr->lock, flags); - /* All TEI are inactiv */ - mISDN_FsmChangeState(fi, ST_L1_DEACT); - _queue_data(&mgr->ch, PH_DEACTIVATE_REQ, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); -} - -static struct FsmNode DeactFnList[] = -{ - {ST_L1_DEACT, EV_ACTIVATE_IND, da_activate}, - {ST_L1_ACTIV, EV_DEACTIVATE_IND, da_deactivate_ind}, - {ST_L1_ACTIV, EV_DEACTIVATE, da_deactivate}, - {ST_L1_DEACT_PENDING, EV_ACTIVATE, da_activate}, - {ST_L1_DEACT_PENDING, EV_UI, da_ui}, - {ST_L1_DEACT_PENDING, EV_DATIMER, da_timer}, -}; - -enum { - ST_TEI_NOP, - ST_TEI_IDREQ, - ST_TEI_IDVERIFY, -}; - -#define TEI_STATE_COUNT (ST_TEI_IDVERIFY + 1) - -static char *strTeiState[] = -{ - "ST_TEI_NOP", - "ST_TEI_IDREQ", - "ST_TEI_IDVERIFY", -}; - -enum { - EV_IDREQ, - EV_ASSIGN, - EV_ASSIGN_REQ, - EV_DENIED, - EV_CHKREQ, - EV_CHKRESP, - EV_REMOVE, - EV_VERIFY, - EV_TIMER, -}; - -#define TEI_EVENT_COUNT (EV_TIMER + 1) - -static char *strTeiEvent[] = -{ - "EV_IDREQ", - "EV_ASSIGN", - "EV_ASSIGN_REQ", - "EV_DENIED", - "EV_CHKREQ", - "EV_CHKRESP", - "EV_REMOVE", - "EV_VERIFY", - "EV_TIMER", -}; - -static void -tei_debug(struct FsmInst *fi, char *fmt, ...) -{ - struct teimgr *tm = fi->userdata; - struct va_format vaf; - va_list va; - - if (!(*debug & DEBUG_L2_TEIFSM)) - return; - - va_start(va, fmt); - - vaf.fmt = fmt; - vaf.va = &va; - - printk(KERN_DEBUG "sapi(%d) tei(%d): %pV\n", - tm->l2->sapi, tm->l2->tei, &vaf); - - va_end(va); -} - - - -static int -get_free_id(struct manager *mgr) -{ - DECLARE_BITMAP(ids, 64) = { [0 ... BITS_TO_LONGS(64) - 1] = 0 }; - int i; - struct layer2 *l2; - - list_for_each_entry(l2, &mgr->layer2, list) { - if (l2->ch.nr > 63) { - printk(KERN_WARNING - "%s: more as 63 layer2 for one device\n", - __func__); - return -EBUSY; - } - __set_bit(l2->ch.nr, ids); - } - i = find_next_zero_bit(ids, 64, 1); - if (i < 64) - return i; - printk(KERN_WARNING "%s: more as 63 layer2 for one device\n", - __func__); - return -EBUSY; -} - -static int -get_free_tei(struct manager *mgr) -{ - DECLARE_BITMAP(ids, 64) = { [0 ... BITS_TO_LONGS(64) - 1] = 0 }; - int i; - struct layer2 *l2; - - list_for_each_entry(l2, &mgr->layer2, list) { - if (l2->ch.nr == 0) - continue; - if ((l2->ch.addr & 0xff) != 0) - continue; - i = l2->ch.addr >> 8; - if (i < 64) - continue; - i -= 64; - - __set_bit(i, ids); - } - i = find_first_zero_bit(ids, 64); - if (i < 64) - return i + 64; - printk(KERN_WARNING "%s: more as 63 dynamic tei for one device\n", - __func__); - return -1; -} - -static void -teiup_create(struct manager *mgr, u_int prim, int len, void *arg) -{ - struct sk_buff *skb; - struct mISDNhead *hh; - int err; - - skb = mI_alloc_skb(len, GFP_ATOMIC); - if (!skb) - return; - hh = mISDN_HEAD_P(skb); - hh->prim = prim; - hh->id = (mgr->ch.nr << 16) | mgr->ch.addr; - if (len) - skb_put_data(skb, arg, len); - err = mgr->up->send(mgr->up, skb); - if (err) { - printk(KERN_WARNING "%s: err=%d\n", __func__, err); - dev_kfree_skb(skb); - } -} - -static u_int -new_id(struct manager *mgr) -{ - u_int id; - - id = mgr->nextid++; - if (id == 0x7fff) - mgr->nextid = 1; - id <<= 16; - id |= GROUP_TEI << 8; - id |= TEI_SAPI; - return id; -} - -static void -do_send(struct manager *mgr) -{ - if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) - return; - - if (!test_and_set_bit(MGR_PH_NOTREADY, &mgr->options)) { - struct sk_buff *skb = skb_dequeue(&mgr->sendq); - - if (!skb) { - test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); - return; - } - mgr->lastid = mISDN_HEAD_ID(skb); - mISDN_FsmEvent(&mgr->deact, EV_UI, NULL); - if (mgr->ch.recv(mgr->ch.peer, skb)) { - dev_kfree_skb(skb); - test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); - mgr->lastid = MISDN_ID_NONE; - } - } -} - -static void -do_ack(struct manager *mgr, u_int id) -{ - if (test_bit(MGR_PH_NOTREADY, &mgr->options)) { - if (id == mgr->lastid) { - if (test_bit(MGR_PH_ACTIVE, &mgr->options)) { - struct sk_buff *skb; - - skb = skb_dequeue(&mgr->sendq); - if (skb) { - mgr->lastid = mISDN_HEAD_ID(skb); - if (!mgr->ch.recv(mgr->ch.peer, skb)) - return; - dev_kfree_skb(skb); - } - } - mgr->lastid = MISDN_ID_NONE; - test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); - } - } -} - -static void -mgr_send_down(struct manager *mgr, struct sk_buff *skb) -{ - skb_queue_tail(&mgr->sendq, skb); - if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) { - _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - } else { - do_send(mgr); - } -} - -static int -dl_unit_data(struct manager *mgr, struct sk_buff *skb) -{ - if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) /* only net send UI */ - return -EINVAL; - if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) - _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - skb_push(skb, 3); - skb->data[0] = 0x02; /* SAPI 0 C/R = 1 */ - skb->data[1] = 0xff; /* TEI 127 */ - skb->data[2] = UI; /* UI frame */ - mISDN_HEAD_PRIM(skb) = PH_DATA_REQ; - mISDN_HEAD_ID(skb) = new_id(mgr); - skb_queue_tail(&mgr->sendq, skb); - do_send(mgr); - return 0; -} - -static unsigned int -random_ri(void) -{ - u16 x; - - get_random_bytes(&x, sizeof(x)); - return x; -} - -static struct layer2 * -findtei(struct manager *mgr, int tei) -{ - struct layer2 *l2; - u_long flags; - - read_lock_irqsave(&mgr->lock, flags); - list_for_each_entry(l2, &mgr->layer2, list) { - if ((l2->sapi == 0) && (l2->tei > 0) && - (l2->tei != GROUP_TEI) && (l2->tei == tei)) - goto done; - } - l2 = NULL; -done: - read_unlock_irqrestore(&mgr->lock, flags); - return l2; -} - -static void -put_tei_msg(struct manager *mgr, u_char m_id, unsigned int ri, int tei) -{ - struct sk_buff *skb; - u_char bp[8]; - - bp[0] = (TEI_SAPI << 2); - if (test_bit(MGR_OPT_NETWORK, &mgr->options)) - bp[0] |= 2; /* CR:=1 for net command */ - bp[1] = (GROUP_TEI << 1) | 0x1; - bp[2] = UI; - bp[3] = TEI_ENTITY_ID; - bp[4] = ri >> 8; - bp[5] = ri & 0xff; - bp[6] = m_id; - bp[7] = ((tei << 1) & 0xff) | 1; - skb = _alloc_mISDN_skb(PH_DATA_REQ, new_id(mgr), 8, bp, GFP_ATOMIC); - if (!skb) { - printk(KERN_WARNING "%s: no skb for tei msg\n", __func__); - return; - } - mgr_send_down(mgr, skb); -} - -static void -tei_id_request(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (tm->l2->tei != GROUP_TEI) { - tm->tei_m.printdebug(&tm->tei_m, - "assign request for already assigned tei %d", - tm->l2->tei); - return; - } - tm->ri = random_ri(); - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(&tm->tei_m, - "assign request ri %d", tm->ri); - put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI); - mISDN_FsmChangeState(fi, ST_TEI_IDREQ); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 1); - tm->nval = 3; -} - -static void -tei_id_assign(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - struct layer2 *l2; - u_char *dp = arg; - int ri, tei; - - ri = ((unsigned int) *dp++ << 8); - ri += *dp++; - dp++; - tei = *dp >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity assign ri %d tei %d", - ri, tei); - l2 = findtei(tm->mgr, tei); - if (l2) { /* same tei is in use */ - if (ri != l2->tm->ri) { - tm->tei_m.printdebug(fi, - "possible duplicate assignment tei %d", tei); - tei_l2(l2, MDL_ERROR_RSP, 0); - } - } else if (ri == tm->ri) { - mISDN_FsmDelTimer(&tm->timer, 1); - mISDN_FsmChangeState(fi, ST_TEI_NOP); - tei_l2(tm->l2, MDL_ASSIGN_REQ, tei); - } -} - -static void -tei_id_test_dup(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - struct layer2 *l2; - u_char *dp = arg; - int tei, ri; - - ri = ((unsigned int) *dp++ << 8); - ri += *dp++; - dp++; - tei = *dp >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d", - ri, tei); - l2 = findtei(tm->mgr, tei); - if (l2) { /* same tei is in use */ - if (ri != l2->tm->ri) { /* and it wasn't our request */ - tm->tei_m.printdebug(fi, - "possible duplicate assignment tei %d", tei); - mISDN_FsmEvent(&l2->tm->tei_m, EV_VERIFY, NULL); - } - } -} - -static void -tei_id_denied(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - int ri, tei; - - ri = ((unsigned int) *dp++ << 8); - ri += *dp++; - dp++; - tei = *dp >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity denied ri %d tei %d", - ri, tei); -} - -static void -tei_id_chk_req(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - int tei; - - tei = *(dp + 3) >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity check req tei %d", tei); - if ((tm->l2->tei != GROUP_TEI) && ((tei == GROUP_TEI) || - (tei == tm->l2->tei))) { - mISDN_FsmDelTimer(&tm->timer, 4); - mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); - put_tei_msg(tm->mgr, ID_CHK_RES, random_ri(), tm->l2->tei); - } -} - -static void -tei_id_remove(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - int tei; - - tei = *(dp + 3) >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity remove tei %d", tei); - if ((tm->l2->tei != GROUP_TEI) && - ((tei == GROUP_TEI) || (tei == tm->l2->tei))) { - mISDN_FsmDelTimer(&tm->timer, 5); - mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); - tei_l2(tm->l2, MDL_REMOVE_REQ, 0); - } -} - -static void -tei_id_verify(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "id verify request for tei %d", - tm->l2->tei); - put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei); - mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2); - tm->nval = 2; -} - -static void -tei_id_req_tout(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (--tm->nval) { - tm->ri = random_ri(); - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "assign req(%d) ri %d", - 4 - tm->nval, tm->ri); - put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 3); - } else { - tm->tei_m.printdebug(fi, "assign req failed"); - tei_l2(tm->l2, MDL_ERROR_RSP, 0); - mISDN_FsmChangeState(fi, ST_TEI_NOP); - } -} - -static void -tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (--tm->nval) { - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, - "id verify req(%d) for tei %d", - 3 - tm->nval, tm->l2->tei); - put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4); - } else { - tm->tei_m.printdebug(fi, "verify req for tei %d failed", - tm->l2->tei); - tei_l2(tm->l2, MDL_REMOVE_REQ, 0); - mISDN_FsmChangeState(fi, ST_TEI_NOP); - } -} - -static struct FsmNode TeiFnListUser[] = -{ - {ST_TEI_NOP, EV_IDREQ, tei_id_request}, - {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup}, - {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, - {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, - {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req}, - {ST_TEI_IDREQ, EV_TIMER, tei_id_req_tout}, - {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign}, - {ST_TEI_IDREQ, EV_DENIED, tei_id_denied}, - {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout}, - {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove}, - {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req}, -}; - -static void -tei_l2remove(struct layer2 *l2) -{ - put_tei_msg(l2->tm->mgr, ID_REMOVE, 0, l2->tei); - tei_l2(l2, MDL_REMOVE_REQ, 0); - list_del(&l2->ch.list); - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); -} - -static void -tei_assign_req(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - - if (tm->l2->tei == GROUP_TEI) { - tm->tei_m.printdebug(&tm->tei_m, - "net tei assign request without tei"); - return; - } - tm->ri = ((unsigned int) *dp++ << 8); - tm->ri += *dp++; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(&tm->tei_m, - "net assign request ri %d teim %d", tm->ri, *dp); - put_tei_msg(tm->mgr, ID_ASSIGNED, tm->ri, tm->l2->tei); - mISDN_FsmChangeState(fi, ST_TEI_NOP); -} - -static void -tei_id_chk_req_net(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "id check request for tei %d", - tm->l2->tei); - tm->rcnt = 0; - put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei); - mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2); - tm->nval = 2; -} - -static void -tei_id_chk_resp(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - int tei; - - tei = dp[3] >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity check resp tei %d", tei); - if (tei == tm->l2->tei) - tm->rcnt++; -} - -static void -tei_id_verify_net(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - int tei; - - tei = dp[3] >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity verify req tei %d/%d", - tei, tm->l2->tei); - if (tei == tm->l2->tei) - tei_id_chk_req_net(fi, event, arg); -} - -static void -tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (tm->rcnt == 1) { - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, - "check req for tei %d successful\n", tm->l2->tei); - mISDN_FsmChangeState(fi, ST_TEI_NOP); - } else if (tm->rcnt > 1) { - /* duplicate assignment; remove */ - tei_l2remove(tm->l2); - } else if (--tm->nval) { - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, - "id check req(%d) for tei %d", - 3 - tm->nval, tm->l2->tei); - put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4); - } else { - tm->tei_m.printdebug(fi, "check req for tei %d failed", - tm->l2->tei); - mISDN_FsmChangeState(fi, ST_TEI_NOP); - tei_l2remove(tm->l2); - } -} - -static struct FsmNode TeiFnListNet[] = -{ - {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req}, - {ST_TEI_NOP, EV_VERIFY, tei_id_verify_net}, - {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req_net}, - {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout_net}, - {ST_TEI_IDVERIFY, EV_CHKRESP, tei_id_chk_resp}, -}; - -static void -tei_ph_data_ind(struct teimgr *tm, u_int mt, u_char *dp, int len) -{ - if (test_bit(FLG_FIXED_TEI, &tm->l2->flag)) - return; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(&tm->tei_m, "tei handler mt %x", mt); - if (mt == ID_ASSIGNED) - mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN, dp); - else if (mt == ID_DENIED) - mISDN_FsmEvent(&tm->tei_m, EV_DENIED, dp); - else if (mt == ID_CHK_REQ) - mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, dp); - else if (mt == ID_REMOVE) - mISDN_FsmEvent(&tm->tei_m, EV_REMOVE, dp); - else if (mt == ID_VERIFY) - mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, dp); - else if (mt == ID_CHK_RES) - mISDN_FsmEvent(&tm->tei_m, EV_CHKRESP, dp); -} - -static struct layer2 * -create_new_tei(struct manager *mgr, int tei, int sapi) -{ - unsigned long opt = 0; - unsigned long flags; - int id; - struct layer2 *l2; - struct channel_req rq; - - if (!mgr->up) - return NULL; - if ((tei >= 0) && (tei < 64)) - test_and_set_bit(OPTION_L2_FIXEDTEI, &opt); - if (mgr->ch.st->dev->Dprotocols & ((1 << ISDN_P_TE_E1) | - (1 << ISDN_P_NT_E1))) { - test_and_set_bit(OPTION_L2_PMX, &opt); - rq.protocol = ISDN_P_NT_E1; - } else { - rq.protocol = ISDN_P_NT_S0; - } - l2 = create_l2(mgr->up, ISDN_P_LAPD_NT, opt, tei, sapi); - if (!l2) { - printk(KERN_WARNING "%s:no memory for layer2\n", __func__); - return NULL; - } - l2->tm = kzalloc_obj(struct teimgr); - if (!l2->tm) { - kfree(l2); - printk(KERN_WARNING "%s:no memory for teimgr\n", __func__); - return NULL; - } - l2->tm->mgr = mgr; - l2->tm->l2 = l2; - l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM; - l2->tm->tei_m.userdata = l2->tm; - l2->tm->tei_m.printdebug = tei_debug; - l2->tm->tei_m.fsm = &teifsmn; - l2->tm->tei_m.state = ST_TEI_NOP; - l2->tm->tval = 2000; /* T202 2 sec */ - mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer); - write_lock_irqsave(&mgr->lock, flags); - id = get_free_id(mgr); - list_add_tail(&l2->list, &mgr->layer2); - write_unlock_irqrestore(&mgr->lock, flags); - if (id < 0) { - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); - printk(KERN_WARNING "%s:no free id\n", __func__); - return NULL; - } else { - l2->ch.nr = id; - __add_layer2(&l2->ch, mgr->ch.st); - l2->ch.recv = mgr->ch.recv; - l2->ch.peer = mgr->ch.peer; - l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL); - /* We need open here L1 for the manager as well (refcounting) */ - rq.adr.dev = mgr->ch.st->dev->id; - id = mgr->ch.st->own.ctrl(&mgr->ch.st->own, OPEN_CHANNEL, &rq); - if (id < 0) { - printk(KERN_WARNING "%s: cannot open L1\n", __func__); - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); - l2 = NULL; - } - } - return l2; -} - -static void -new_tei_req(struct manager *mgr, u_char *dp) -{ - int tei, ri; - struct layer2 *l2; - - ri = dp[0] << 8; - ri += dp[1]; - if (!mgr->up) - goto denied; - if (!(dp[3] & 1)) /* Extension bit != 1 */ - goto denied; - if (dp[3] != 0xff) - tei = dp[3] >> 1; /* 3GPP TS 08.56 6.1.11.2 */ - else - tei = get_free_tei(mgr); - if (tei < 0) { - printk(KERN_WARNING "%s:No free tei\n", __func__); - goto denied; - } - l2 = create_new_tei(mgr, tei, CTRL_SAPI); - if (!l2) - goto denied; - else - mISDN_FsmEvent(&l2->tm->tei_m, EV_ASSIGN_REQ, dp); - return; -denied: - put_tei_msg(mgr, ID_DENIED, ri, GROUP_TEI); -} - -static int -ph_data_ind(struct manager *mgr, struct sk_buff *skb) -{ - int ret = -EINVAL; - struct layer2 *l2, *nl2; - u_char mt; - - if (skb->len < 8) { - if (*debug & DEBUG_L2_TEI) - printk(KERN_DEBUG "%s: short mgr frame %d/8\n", - __func__, skb->len); - goto done; - } - - if ((skb->data[0] >> 2) != TEI_SAPI) /* not for us */ - goto done; - if (skb->data[0] & 1) /* EA0 formal error */ - goto done; - if (!(skb->data[1] & 1)) /* EA1 formal error */ - goto done; - if ((skb->data[1] >> 1) != GROUP_TEI) /* not for us */ - goto done; - if ((skb->data[2] & 0xef) != UI) /* not UI */ - goto done; - if (skb->data[3] != TEI_ENTITY_ID) /* not tei entity */ - goto done; - mt = skb->data[6]; - switch (mt) { - case ID_REQUEST: - case ID_CHK_RES: - case ID_VERIFY: - if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) - goto done; - break; - case ID_ASSIGNED: - case ID_DENIED: - case ID_CHK_REQ: - case ID_REMOVE: - if (test_bit(MGR_OPT_NETWORK, &mgr->options)) - goto done; - break; - default: - goto done; - } - ret = 0; - if (mt == ID_REQUEST) { - new_tei_req(mgr, &skb->data[4]); - goto done; - } - list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { - tei_ph_data_ind(l2->tm, mt, &skb->data[4], skb->len - 4); - } -done: - return ret; -} - -int -l2_tei(struct layer2 *l2, u_int cmd, u_long arg) -{ - struct teimgr *tm = l2->tm; - - if (test_bit(FLG_FIXED_TEI, &l2->flag)) - return 0; - if (*debug & DEBUG_L2_TEI) - printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd); - switch (cmd) { - case MDL_ASSIGN_IND: - mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL); - break; - case MDL_ERROR_IND: - if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) - mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, &l2->tei); - if (test_bit(MGR_OPT_USER, &tm->mgr->options)) - mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL); - break; - case MDL_STATUS_UP_IND: - if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) - mISDN_FsmEvent(&tm->mgr->deact, EV_ACTIVATE, NULL); - break; - case MDL_STATUS_DOWN_IND: - if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) - mISDN_FsmEvent(&tm->mgr->deact, EV_DEACTIVATE, NULL); - break; - case MDL_STATUS_UI_IND: - if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) - mISDN_FsmEvent(&tm->mgr->deact, EV_UI, NULL); - break; - } - return 0; -} - -void -TEIrelease(struct layer2 *l2) -{ - struct teimgr *tm = l2->tm; - u_long flags; - - mISDN_FsmDelTimer(&tm->timer, 1); - write_lock_irqsave(&tm->mgr->lock, flags); - list_del(&l2->list); - write_unlock_irqrestore(&tm->mgr->lock, flags); - l2->tm = NULL; - kfree(tm); -} - -static int -create_teimgr(struct manager *mgr, struct channel_req *crq) -{ - struct layer2 *l2; - unsigned long opt = 0; - unsigned long flags; - int id; - struct channel_req l1rq; - - if (*debug & DEBUG_L2_TEI) - printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev_name(&mgr->ch.st->dev->dev), - crq->protocol, crq->adr.dev, crq->adr.channel, - crq->adr.sapi, crq->adr.tei); - if (crq->adr.tei > GROUP_TEI) - return -EINVAL; - if (crq->adr.tei < 64) - test_and_set_bit(OPTION_L2_FIXEDTEI, &opt); - if (crq->adr.tei == 0) - test_and_set_bit(OPTION_L2_PTP, &opt); - if (test_bit(MGR_OPT_NETWORK, &mgr->options)) { - if (crq->protocol == ISDN_P_LAPD_TE) - return -EPROTONOSUPPORT; - if ((crq->adr.tei != 0) && (crq->adr.tei != 127)) - return -EINVAL; - if (mgr->up) { - printk(KERN_WARNING - "%s: only one network manager is allowed\n", - __func__); - return -EBUSY; - } - } else if (test_bit(MGR_OPT_USER, &mgr->options)) { - if (crq->protocol == ISDN_P_LAPD_NT) - return -EPROTONOSUPPORT; - if ((crq->adr.tei >= 64) && (crq->adr.tei < GROUP_TEI)) - return -EINVAL; /* dyn tei */ - } else { - if (crq->protocol == ISDN_P_LAPD_NT) - test_and_set_bit(MGR_OPT_NETWORK, &mgr->options); - if (crq->protocol == ISDN_P_LAPD_TE) - test_and_set_bit(MGR_OPT_USER, &mgr->options); - } - l1rq.adr = crq->adr; - if (mgr->ch.st->dev->Dprotocols - & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1))) - test_and_set_bit(OPTION_L2_PMX, &opt); - if ((crq->protocol == ISDN_P_LAPD_NT) && (crq->adr.tei == 127)) { - mgr->up = crq->ch; - id = DL_INFO_L2_CONNECT; - teiup_create(mgr, DL_INFORMATION_IND, sizeof(id), &id); - if (test_bit(MGR_PH_ACTIVE, &mgr->options)) - teiup_create(mgr, PH_ACTIVATE_IND, 0, NULL); - crq->ch = NULL; - if (!list_empty(&mgr->layer2)) { - read_lock_irqsave(&mgr->lock, flags); - list_for_each_entry(l2, &mgr->layer2, list) { - l2->up = mgr->up; - l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL); - } - read_unlock_irqrestore(&mgr->lock, flags); - } - return 0; - } - l2 = create_l2(crq->ch, crq->protocol, opt, - crq->adr.tei, crq->adr.sapi); - if (!l2) - return -ENOMEM; - l2->tm = kzalloc_obj(struct teimgr); - if (!l2->tm) { - kfree(l2); - printk(KERN_ERR "kmalloc teimgr failed\n"); - return -ENOMEM; - } - l2->tm->mgr = mgr; - l2->tm->l2 = l2; - l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM; - l2->tm->tei_m.userdata = l2->tm; - l2->tm->tei_m.printdebug = tei_debug; - if (crq->protocol == ISDN_P_LAPD_TE) { - l2->tm->tei_m.fsm = &teifsmu; - l2->tm->tei_m.state = ST_TEI_NOP; - l2->tm->tval = 1000; /* T201 1 sec */ - if (test_bit(OPTION_L2_PMX, &opt)) - l1rq.protocol = ISDN_P_TE_E1; - else - l1rq.protocol = ISDN_P_TE_S0; - } else { - l2->tm->tei_m.fsm = &teifsmn; - l2->tm->tei_m.state = ST_TEI_NOP; - l2->tm->tval = 2000; /* T202 2 sec */ - if (test_bit(OPTION_L2_PMX, &opt)) - l1rq.protocol = ISDN_P_NT_E1; - else - l1rq.protocol = ISDN_P_NT_S0; - } - mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer); - write_lock_irqsave(&mgr->lock, flags); - id = get_free_id(mgr); - list_add_tail(&l2->list, &mgr->layer2); - write_unlock_irqrestore(&mgr->lock, flags); - if (id >= 0) { - l2->ch.nr = id; - l2->up->nr = id; - crq->ch = &l2->ch; - /* We need open here L1 for the manager as well (refcounting) */ - id = mgr->ch.st->own.ctrl(&mgr->ch.st->own, OPEN_CHANNEL, - &l1rq); - } - if (id < 0) - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); - return id; -} - -static int -mgr_send(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct manager *mgr; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int ret = -EINVAL; - - mgr = container_of(ch, struct manager, ch); - if (*debug & DEBUG_L2_RECV) - printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", - __func__, hh->prim, hh->id); - switch (hh->prim) { - case PH_DATA_IND: - mISDN_FsmEvent(&mgr->deact, EV_UI, NULL); - ret = ph_data_ind(mgr, skb); - break; - case PH_DATA_CNF: - do_ack(mgr, hh->id); - ret = 0; - break; - case PH_ACTIVATE_IND: - test_and_set_bit(MGR_PH_ACTIVE, &mgr->options); - if (mgr->up) - teiup_create(mgr, PH_ACTIVATE_IND, 0, NULL); - mISDN_FsmEvent(&mgr->deact, EV_ACTIVATE_IND, NULL); - do_send(mgr); - ret = 0; - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(MGR_PH_ACTIVE, &mgr->options); - if (mgr->up) - teiup_create(mgr, PH_DEACTIVATE_IND, 0, NULL); - mISDN_FsmEvent(&mgr->deact, EV_DEACTIVATE_IND, NULL); - ret = 0; - break; - case DL_UNITDATA_REQ: - return dl_unit_data(mgr, skb); - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -free_teimanager(struct manager *mgr) -{ - struct layer2 *l2, *nl2; - - test_and_clear_bit(OPTION_L1_HOLD, &mgr->options); - if (test_bit(MGR_OPT_NETWORK, &mgr->options)) { - /* not locked lock is taken in release tei */ - mgr->up = NULL; - if (test_bit(OPTION_L2_CLEANUP, &mgr->options)) { - list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { - put_tei_msg(mgr, ID_REMOVE, 0, l2->tei); - mutex_lock(&mgr->ch.st->lmutex); - list_del(&l2->ch.list); - mutex_unlock(&mgr->ch.st->lmutex); - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); - } - test_and_clear_bit(MGR_OPT_NETWORK, &mgr->options); - } else { - list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { - l2->up = NULL; - } - } - } - if (test_bit(MGR_OPT_USER, &mgr->options)) { - if (list_empty(&mgr->layer2)) - test_and_clear_bit(MGR_OPT_USER, &mgr->options); - } - mgr->ch.st->dev->D.ctrl(&mgr->ch.st->dev->D, CLOSE_CHANNEL, NULL); - return 0; -} - -static int -ctrl_teimanager(struct manager *mgr, void *arg) -{ - /* currently we only have one option */ - unsigned int *val = (unsigned int *)arg; - - switch (val[0]) { - case IMCLEAR_L2: - if (val[1]) - test_and_set_bit(OPTION_L2_CLEANUP, &mgr->options); - else - test_and_clear_bit(OPTION_L2_CLEANUP, &mgr->options); - break; - case IMHOLD_L1: - if (val[1]) - test_and_set_bit(OPTION_L1_HOLD, &mgr->options); - else - test_and_clear_bit(OPTION_L1_HOLD, &mgr->options); - break; - default: - return -EINVAL; - } - return 0; -} - -/* This function does create a L2 for fixed TEI in NT Mode */ -static int -check_data(struct manager *mgr, struct sk_buff *skb) -{ - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int ret, tei, sapi; - struct layer2 *l2; - - if (*debug & DEBUG_L2_CTRL) - printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", - __func__, hh->prim, hh->id); - if (test_bit(MGR_OPT_USER, &mgr->options)) - return -ENOTCONN; - if (hh->prim != PH_DATA_IND) - return -ENOTCONN; - if (skb->len != 3) - return -ENOTCONN; - if (skb->data[0] & 3) /* EA0 and CR must be 0 */ - return -EINVAL; - sapi = skb->data[0] >> 2; - if (!(skb->data[1] & 1)) /* invalid EA1 */ - return -EINVAL; - tei = skb->data[1] >> 1; - if (tei > 63) /* not a fixed tei */ - return -ENOTCONN; - if ((skb->data[2] & ~0x10) != SABME) - return -ENOTCONN; - /* We got a SABME for a fixed TEI */ - if (*debug & DEBUG_L2_CTRL) - printk(KERN_DEBUG "%s: SABME sapi(%d) tei(%d)\n", - __func__, sapi, tei); - l2 = create_new_tei(mgr, tei, sapi); - if (!l2) { - if (*debug & DEBUG_L2_CTRL) - printk(KERN_DEBUG "%s: failed to create new tei\n", - __func__); - return -ENOMEM; - } - ret = l2->ch.send(&l2->ch, skb); - return ret; -} - -void -delete_teimanager(struct mISDNchannel *ch) -{ - struct manager *mgr; - struct layer2 *l2, *nl2; - - mgr = container_of(ch, struct manager, ch); - /* not locked lock is taken in release tei */ - list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { - mutex_lock(&mgr->ch.st->lmutex); - list_del(&l2->ch.list); - mutex_unlock(&mgr->ch.st->lmutex); - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); - } - list_del(&mgr->ch.list); - list_del(&mgr->bcast.list); - skb_queue_purge(&mgr->sendq); - kfree(mgr); -} - -static int -mgr_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct manager *mgr; - int ret = -EINVAL; - - mgr = container_of(ch, struct manager, ch); - if (*debug & DEBUG_L2_CTRL) - printk(KERN_DEBUG "%s(%x, %p)\n", __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - ret = create_teimgr(mgr, arg); - break; - case CLOSE_CHANNEL: - ret = free_teimanager(mgr); - break; - case CONTROL_CHANNEL: - ret = ctrl_teimanager(mgr, arg); - break; - case CHECK_DATA: - ret = check_data(mgr, arg); - break; - } - return ret; -} - -static int -mgr_bcast(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct manager *mgr = container_of(ch, struct manager, bcast); - struct mISDNhead *hhc, *hh = mISDN_HEAD_P(skb); - struct sk_buff *cskb = NULL; - struct layer2 *l2; - u_long flags; - int ret; - - read_lock_irqsave(&mgr->lock, flags); - list_for_each_entry(l2, &mgr->layer2, list) { - if ((hh->id & MISDN_ID_SAPI_MASK) == - (l2->ch.addr & MISDN_ID_SAPI_MASK)) { - if (list_is_last(&l2->list, &mgr->layer2)) { - cskb = skb; - skb = NULL; - } else { - if (!cskb) - cskb = skb_copy(skb, GFP_ATOMIC); - } - if (cskb) { - hhc = mISDN_HEAD_P(cskb); - /* save original header behind normal header */ - hhc++; - *hhc = *hh; - hhc--; - hhc->prim = DL_INTERN_MSG; - hhc->id = l2->ch.nr; - ret = ch->st->own.recv(&ch->st->own, cskb); - if (ret) { - if (*debug & DEBUG_SEND_ERR) - printk(KERN_DEBUG - "%s ch%d prim(%x) addr(%x)" - " err %d\n", - __func__, l2->ch.nr, - hh->prim, l2->ch.addr, ret); - } else - cskb = NULL; - } else { - printk(KERN_WARNING "%s ch%d addr %x no mem\n", - __func__, ch->nr, ch->addr); - goto out; - } - } - } -out: - read_unlock_irqrestore(&mgr->lock, flags); - dev_kfree_skb(cskb); - dev_kfree_skb(skb); - return 0; -} - -static int -mgr_bcast_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - - return -EINVAL; -} - -int -create_teimanager(struct mISDNdevice *dev) -{ - struct manager *mgr; - - mgr = kzalloc_obj(struct manager); - if (!mgr) - return -ENOMEM; - INIT_LIST_HEAD(&mgr->layer2); - rwlock_init(&mgr->lock); - skb_queue_head_init(&mgr->sendq); - mgr->nextid = 1; - mgr->lastid = MISDN_ID_NONE; - mgr->ch.send = mgr_send; - mgr->ch.ctrl = mgr_ctrl; - mgr->ch.st = dev->D.st; - set_channel_address(&mgr->ch, TEI_SAPI, GROUP_TEI); - add_layer2(&mgr->ch, dev->D.st); - mgr->bcast.send = mgr_bcast; - mgr->bcast.ctrl = mgr_bcast_ctrl; - mgr->bcast.st = dev->D.st; - set_channel_address(&mgr->bcast, 0, GROUP_TEI); - add_layer2(&mgr->bcast, dev->D.st); - mgr->deact.debug = *debug & DEBUG_MANAGER; - mgr->deact.userdata = mgr; - mgr->deact.printdebug = da_debug; - mgr->deact.fsm = &deactfsm; - mgr->deact.state = ST_L1_DEACT; - mISDN_FsmInitTimer(&mgr->deact, &mgr->datimer); - dev->teimgr = &mgr->ch; - return 0; -} - -int TEIInit(u_int *deb) -{ - int res; - debug = deb; - teifsmu.state_count = TEI_STATE_COUNT; - teifsmu.event_count = TEI_EVENT_COUNT; - teifsmu.strEvent = strTeiEvent; - teifsmu.strState = strTeiState; - res = mISDN_FsmNew(&teifsmu, TeiFnListUser, ARRAY_SIZE(TeiFnListUser)); - if (res) - goto error; - teifsmn.state_count = TEI_STATE_COUNT; - teifsmn.event_count = TEI_EVENT_COUNT; - teifsmn.strEvent = strTeiEvent; - teifsmn.strState = strTeiState; - res = mISDN_FsmNew(&teifsmn, TeiFnListNet, ARRAY_SIZE(TeiFnListNet)); - if (res) - goto error_smn; - deactfsm.state_count = DEACT_STATE_COUNT; - deactfsm.event_count = DEACT_EVENT_COUNT; - deactfsm.strEvent = strDeactEvent; - deactfsm.strState = strDeactState; - res = mISDN_FsmNew(&deactfsm, DeactFnList, ARRAY_SIZE(DeactFnList)); - if (res) - goto error_deact; - return 0; - -error_deact: - mISDN_FsmFree(&teifsmn); -error_smn: - mISDN_FsmFree(&teifsmu); -error: - return res; -} - -void TEIFree(void) -{ - mISDN_FsmFree(&teifsmu); - mISDN_FsmFree(&teifsmn); - mISDN_FsmFree(&deactfsm); -} diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c deleted file mode 100644 index a18845755633..000000000000 --- a/drivers/isdn/mISDN/timerdev.c +++ /dev/null @@ -1,295 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * general timer device for using in ISDN stacks - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "core.h" - -static DEFINE_MUTEX(mISDN_mutex); -static u_int *debug; - - -struct mISDNtimerdev { - int next_id; - struct list_head pending; - struct list_head expired; - wait_queue_head_t wait; - u_int work; - spinlock_t lock; /* protect lists */ -}; - -struct mISDNtimer { - struct list_head list; - struct mISDNtimerdev *dev; - struct timer_list tl; - int id; -}; - -static int -mISDN_open(struct inode *ino, struct file *filep) -{ - struct mISDNtimerdev *dev; - - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); - dev = kmalloc_obj(struct mISDNtimerdev); - if (!dev) - return -ENOMEM; - dev->next_id = 1; - INIT_LIST_HEAD(&dev->pending); - INIT_LIST_HEAD(&dev->expired); - spin_lock_init(&dev->lock); - dev->work = 0; - init_waitqueue_head(&dev->wait); - filep->private_data = dev; - return nonseekable_open(ino, filep); -} - -static int -mISDN_close(struct inode *ino, struct file *filep) -{ - struct mISDNtimerdev *dev = filep->private_data; - struct list_head *list = &dev->pending; - struct mISDNtimer *timer, *next; - - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); - - spin_lock_irq(&dev->lock); - while (!list_empty(list)) { - timer = list_first_entry(list, struct mISDNtimer, list); - spin_unlock_irq(&dev->lock); - timer_shutdown_sync(&timer->tl); - spin_lock_irq(&dev->lock); - /* it might have been moved to ->expired */ - list_del(&timer->list); - kfree(timer); - } - spin_unlock_irq(&dev->lock); - - list_for_each_entry_safe(timer, next, &dev->expired, list) { - kfree(timer); - } - kfree(dev); - return 0; -} - -static ssize_t -mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off) -{ - struct mISDNtimerdev *dev = filep->private_data; - struct list_head *list = &dev->expired; - struct mISDNtimer *timer; - int ret = 0; - - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__, - filep, buf, (int)count, off); - - if (count < sizeof(int)) - return -ENOSPC; - - spin_lock_irq(&dev->lock); - while (list_empty(list) && (dev->work == 0)) { - spin_unlock_irq(&dev->lock); - if (filep->f_flags & O_NONBLOCK) - return -EAGAIN; - wait_event_interruptible(dev->wait, (READ_ONCE(dev->work) || - !list_empty(list))); - if (signal_pending(current)) - return -ERESTARTSYS; - spin_lock_irq(&dev->lock); - } - if (dev->work) - WRITE_ONCE(dev->work, 0); - if (!list_empty(list)) { - timer = list_first_entry(list, struct mISDNtimer, list); - list_del(&timer->list); - spin_unlock_irq(&dev->lock); - if (put_user(timer->id, (int __user *)buf)) - ret = -EFAULT; - else - ret = sizeof(int); - kfree(timer); - } else { - spin_unlock_irq(&dev->lock); - } - return ret; -} - -static __poll_t -mISDN_poll(struct file *filep, poll_table *wait) -{ - struct mISDNtimerdev *dev = filep->private_data; - __poll_t mask = EPOLLERR; - - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait); - if (dev) { - u32 work; - - poll_wait(filep, &dev->wait, wait); - mask = 0; - work = READ_ONCE(dev->work); - if (work || !list_empty(&dev->expired)) - mask |= (EPOLLIN | EPOLLRDNORM); - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__, - work, list_empty(&dev->expired)); - } - return mask; -} - -static void -dev_expire_timer(struct timer_list *t) -{ - struct mISDNtimer *timer = timer_container_of(timer, t, tl); - u_long flags; - - spin_lock_irqsave(&timer->dev->lock, flags); - if (timer->id >= 0) - list_move_tail(&timer->list, &timer->dev->expired); - wake_up_interruptible(&timer->dev->wait); - spin_unlock_irqrestore(&timer->dev->lock, flags); -} - -static int -misdn_add_timer(struct mISDNtimerdev *dev, int timeout) -{ - int id; - struct mISDNtimer *timer; - - if (!timeout) { - WRITE_ONCE(dev->work, 1); - wake_up_interruptible(&dev->wait); - id = 0; - } else { - timer = kzalloc_obj(struct mISDNtimer); - if (!timer) - return -ENOMEM; - timer->dev = dev; - timer_setup(&timer->tl, dev_expire_timer, 0); - spin_lock_irq(&dev->lock); - id = timer->id = dev->next_id++; - if (dev->next_id < 0) - dev->next_id = 1; - list_add_tail(&timer->list, &dev->pending); - timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000); - add_timer(&timer->tl); - spin_unlock_irq(&dev->lock); - } - return id; -} - -static int -misdn_del_timer(struct mISDNtimerdev *dev, int id) -{ - struct mISDNtimer *timer; - - spin_lock_irq(&dev->lock); - list_for_each_entry(timer, &dev->pending, list) { - if (timer->id == id) { - list_del_init(&timer->list); - timer->id = -1; - spin_unlock_irq(&dev->lock); - timer_shutdown_sync(&timer->tl); - kfree(timer); - return id; - } - } - spin_unlock_irq(&dev->lock); - return 0; -} - -static long -mISDN_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - struct mISDNtimerdev *dev = filep->private_data; - int id, tout, ret = 0; - - - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__, - filep, cmd, arg); - mutex_lock(&mISDN_mutex); - switch (cmd) { - case IMADDTIMER: - if (get_user(tout, (int __user *)arg)) { - ret = -EFAULT; - break; - } - id = misdn_add_timer(dev, tout); - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s add %d id %d\n", __func__, - tout, id); - if (id < 0) { - ret = id; - break; - } - if (put_user(id, (int __user *)arg)) - ret = -EFAULT; - break; - case IMDELTIMER: - if (get_user(id, (int __user *)arg)) { - ret = -EFAULT; - break; - } - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s del id %d\n", __func__, id); - id = misdn_del_timer(dev, id); - if (put_user(id, (int __user *)arg)) - ret = -EFAULT; - break; - default: - ret = -EINVAL; - } - mutex_unlock(&mISDN_mutex); - return ret; -} - -static const struct file_operations mISDN_fops = { - .owner = THIS_MODULE, - .read = mISDN_read, - .poll = mISDN_poll, - .unlocked_ioctl = mISDN_ioctl, - .open = mISDN_open, - .release = mISDN_close, -}; - -static struct miscdevice mISDNtimer = { - .minor = MISC_DYNAMIC_MINOR, - .name = "mISDNtimer", - .fops = &mISDN_fops, -}; - -int -mISDN_inittimer(u_int *deb) -{ - int err; - - debug = deb; - err = misc_register(&mISDNtimer); - if (err) - printk(KERN_WARNING "mISDN: Could not register timer device\n"); - return err; -} - -void mISDN_timer_cleanup(void) -{ - misc_deregister(&mISDNtimer); -} diff --git a/include/linux/isdn/capilli.h b/include/linux/isdn/capilli.h deleted file mode 100644 index 12be09b6883b..000000000000 --- a/include/linux/isdn/capilli.h +++ /dev/null @@ -1,95 +0,0 @@ -/* $Id: capilli.h,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ - * - * Kernel CAPI 2.0 Driver Interface for Linux - * - * Copyright 1999 by Carsten Paeth - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#ifndef __CAPILLI_H__ -#define __CAPILLI_H__ - -#include -#include -#include -#include - -typedef struct capiloaddatapart { - int user; /* data in userspace ? */ - int len; - unsigned char *data; -} capiloaddatapart; - -typedef struct capiloaddata { - capiloaddatapart firmware; - capiloaddatapart configuration; -} capiloaddata; - -typedef struct capicardparams { - unsigned int port; - unsigned irq; - int cardtype; - int cardnr; - unsigned int membase; -} capicardparams; - -struct capi_ctr { - /* filled in before calling attach_capi_ctr */ - struct module *owner; - void *driverdata; /* driver specific */ - char name[32]; /* name of controller */ - char *driver_name; /* name of driver */ - int (*load_firmware)(struct capi_ctr *, capiloaddata *); - void (*reset_ctr)(struct capi_ctr *); - void (*register_appl)(struct capi_ctr *, u16 appl, - capi_register_params *); - void (*release_appl)(struct capi_ctr *, u16 appl); - u16 (*send_message)(struct capi_ctr *, struct sk_buff *skb); - - char *(*procinfo)(struct capi_ctr *); - int (*proc_show)(struct seq_file *, void *); - - /* filled in before calling ready callback */ - u8 manu[CAPI_MANUFACTURER_LEN]; /* CAPI_GET_MANUFACTURER */ - capi_version version; /* CAPI_GET_VERSION */ - capi_profile profile; /* CAPI_GET_PROFILE */ - u8 serial[CAPI_SERIAL_LEN]; /* CAPI_GET_SERIAL */ - - /* management information for kcapi */ - - unsigned long nrecvctlpkt; - unsigned long nrecvdatapkt; - unsigned long nsentctlpkt; - unsigned long nsentdatapkt; - - int cnr; /* controller number */ - unsigned short state; /* controller state */ - int blocked; /* output blocked */ - int traceflag; /* capi trace */ - - struct proc_dir_entry *procent; - char procfn[128]; -}; - -int attach_capi_ctr(struct capi_ctr *); -int detach_capi_ctr(struct capi_ctr *); - -void capi_ctr_ready(struct capi_ctr * card); -void capi_ctr_down(struct capi_ctr * card); -void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *skb); - -// --------------------------------------------------------------------------- -// needed for AVM capi drivers - -struct capi_driver { - char name[32]; /* driver name */ - char revision[32]; - - /* management information for kcapi */ - struct list_head list; -}; - -#endif /* __CAPILLI_H__ */ diff --git a/include/linux/isdn/capiutil.h b/include/linux/isdn/capiutil.h deleted file mode 100644 index 953fd500dff7..000000000000 --- a/include/linux/isdn/capiutil.h +++ /dev/null @@ -1,60 +0,0 @@ -/* $Id: capiutil.h,v 1.5.6.2 2001/09/23 22:24:33 kai Exp $ - * - * CAPI 2.0 defines & types - * - * From CAPI 2.0 Development Kit AVM 1995 (msg.c) - * Rewritten for Linux 1996 by Carsten Paeth - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#ifndef __CAPIUTIL_H__ -#define __CAPIUTIL_H__ - -#include - -#define CAPIMSG_BASELEN 8 -#define CAPIMSG_U8(m, off) (m[off]) -#define CAPIMSG_U16(m, off) (m[off]|(m[(off)+1]<<8)) -#define CAPIMSG_U32(m, off) (m[off]|(m[(off)+1]<<8)|(m[(off)+2]<<16)|(m[(off)+3]<<24)) -#define CAPIMSG_LEN(m) CAPIMSG_U16(m,0) -#define CAPIMSG_APPID(m) CAPIMSG_U16(m,2) -#define CAPIMSG_COMMAND(m) CAPIMSG_U8(m,4) -#define CAPIMSG_SUBCOMMAND(m) CAPIMSG_U8(m,5) -#define CAPIMSG_CMD(m) (((m[4])<<8)|(m[5])) -#define CAPIMSG_MSGID(m) CAPIMSG_U16(m,6) -#define CAPIMSG_CONTROLLER(m) (m[8] & 0x7f) -#define CAPIMSG_CONTROL(m) CAPIMSG_U32(m, 8) -#define CAPIMSG_NCCI(m) CAPIMSG_CONTROL(m) -#define CAPIMSG_DATALEN(m) CAPIMSG_U16(m,16) /* DATA_B3_REQ */ - -static inline void capimsg_setu8(void *m, int off, __u8 val) -{ - ((__u8 *)m)[off] = val; -} - -static inline void capimsg_setu16(void *m, int off, __u16 val) -{ - ((__u8 *)m)[off] = val & 0xff; - ((__u8 *)m)[off+1] = (val >> 8) & 0xff; -} - -static inline void capimsg_setu32(void *m, int off, __u32 val) -{ - ((__u8 *)m)[off] = val & 0xff; - ((__u8 *)m)[off+1] = (val >> 8) & 0xff; - ((__u8 *)m)[off+2] = (val >> 16) & 0xff; - ((__u8 *)m)[off+3] = (val >> 24) & 0xff; -} - -#define CAPIMSG_SETLEN(m, len) capimsg_setu16(m, 0, len) -#define CAPIMSG_SETAPPID(m, applid) capimsg_setu16(m, 2, applid) -#define CAPIMSG_SETCOMMAND(m,cmd) capimsg_setu8(m, 4, cmd) -#define CAPIMSG_SETSUBCOMMAND(m, cmd) capimsg_setu8(m, 5, cmd) -#define CAPIMSG_SETMSGID(m, msgid) capimsg_setu16(m, 6, msgid) -#define CAPIMSG_SETCONTROL(m, contr) capimsg_setu32(m, 8, contr) -#define CAPIMSG_SETDATALEN(m, len) capimsg_setu16(m, 16, len) - -#endif /* __CAPIUTIL_H__ */ diff --git a/include/linux/kernelcapi.h b/include/linux/kernelcapi.h deleted file mode 100644 index 94ba42bf9da1..000000000000 --- a/include/linux/kernelcapi.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * $Id: kernelcapi.h,v 1.8.6.2 2001/02/07 11:31:31 kai Exp $ - * - * Kernel CAPI 2.0 Interface for Linux - * - * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) - * - */ -#ifndef __KERNELCAPI_H__ -#define __KERNELCAPI_H__ - -#include -#include -#include -#include -#include - -#define CAPI_NOERROR 0x0000 - -#define CAPI_TOOMANYAPPLS 0x1001 -#define CAPI_LOGBLKSIZETOSMALL 0x1002 -#define CAPI_BUFFEXECEEDS64K 0x1003 -#define CAPI_MSGBUFSIZETOOSMALL 0x1004 -#define CAPI_ANZLOGCONNNOTSUPPORTED 0x1005 -#define CAPI_REGRESERVED 0x1006 -#define CAPI_REGBUSY 0x1007 -#define CAPI_REGOSRESOURCEERR 0x1008 -#define CAPI_REGNOTINSTALLED 0x1009 -#define CAPI_REGCTRLERNOTSUPPORTEXTEQUIP 0x100a -#define CAPI_REGCTRLERONLYSUPPORTEXTEQUIP 0x100b - -#define CAPI_ILLAPPNR 0x1101 -#define CAPI_ILLCMDORSUBCMDORMSGTOSMALL 0x1102 -#define CAPI_SENDQUEUEFULL 0x1103 -#define CAPI_RECEIVEQUEUEEMPTY 0x1104 -#define CAPI_RECEIVEOVERFLOW 0x1105 -#define CAPI_UNKNOWNNOTPAR 0x1106 -#define CAPI_MSGBUSY 0x1107 -#define CAPI_MSGOSRESOURCEERR 0x1108 -#define CAPI_MSGNOTINSTALLED 0x1109 -#define CAPI_MSGCTRLERNOTSUPPORTEXTEQUIP 0x110a -#define CAPI_MSGCTRLERONLYSUPPORTEXTEQUIP 0x110b - -#endif /* __KERNELCAPI_H__ */ diff --git a/include/linux/mISDNdsp.h b/include/linux/mISDNdsp.h deleted file mode 100644 index 00758f45fddc..000000000000 --- a/include/linux/mISDNdsp.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __mISDNdsp_H__ -#define __mISDNdsp_H__ - -struct mISDN_dsp_element_arg { - char *name; - char *def; - char *desc; -}; - -struct mISDN_dsp_element { - char *name; - void *(*new)(const char *arg); - void (*free)(void *p); - void (*process_tx)(void *p, unsigned char *data, int len); - void (*process_rx)(void *p, unsigned char *data, int len, - unsigned int txlen); - int num_args; - struct mISDN_dsp_element_arg - *args; -}; - -extern int mISDN_dsp_element_register(struct mISDN_dsp_element *elem); -extern void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem); - -struct dsp_features { - int hfc_id; /* unique id to identify the chip (or -1) */ - int hfc_dtmf; /* set if HFCmulti card supports dtmf */ - int hfc_conf; /* set if HFCmulti card supports conferences */ - int hfc_loops; /* set if card supports tone loops */ - int hfc_echocanhw; /* set if card supports echocancelation*/ - int pcm_id; /* unique id to identify the pcm bus (or -1) */ - int pcm_slots; /* number of slots on the pcm bus */ - int pcm_banks; /* number of IO banks of pcm bus */ - int unclocked; /* data is not clocked (has jitter/loss) */ - int unordered; /* data is unordered (packets have index) */ -}; - -#endif - diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h deleted file mode 100644 index ef4f8eb02eac..000000000000 --- a/include/linux/mISDNhw.h +++ /dev/null @@ -1,192 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * - * Author Karsten Keil - * - * Basic declarations for the mISDN HW channels - * - * Copyright 2008 by Karsten Keil - */ - -#ifndef MISDNHW_H -#define MISDNHW_H -#include -#include - -/* - * HW DEBUG 0xHHHHGGGG - * H - hardware driver specific bits - * G - for all drivers - */ - -#define DEBUG_HW 0x00000001 -#define DEBUG_HW_OPEN 0x00000002 -#define DEBUG_HW_DCHANNEL 0x00000100 -#define DEBUG_HW_DFIFO 0x00000200 -#define DEBUG_HW_BCHANNEL 0x00001000 -#define DEBUG_HW_BFIFO 0x00002000 - -#define MAX_DFRAME_LEN_L1 300 -#define MAX_MON_FRAME 32 -#define MAX_LOG_SPACE 2048 -#define MISDN_COPY_SIZE 32 - -/* channel->Flags bit field */ -#define FLG_TX_BUSY 0 /* tx_buf in use */ -#define FLG_TX_NEXT 1 /* next_skb in use */ -#define FLG_L1_BUSY 2 /* L1 is permanent busy */ -#define FLG_L2_ACTIVATED 3 /* activated from L2 */ -#define FLG_OPEN 5 /* channel is in use */ -#define FLG_ACTIVE 6 /* channel is activated */ -#define FLG_BUSY_TIMER 7 -/* channel type */ -#define FLG_DCHANNEL 8 /* channel is D-channel */ -#define FLG_BCHANNEL 9 /* channel is B-channel */ -#define FLG_ECHANNEL 10 /* channel is E-channel */ -#define FLG_TRANSPARENT 12 /* channel use transparent data */ -#define FLG_HDLC 13 /* channel use hdlc data */ -#define FLG_L2DATA 14 /* channel use L2 DATA primitivs */ -#define FLG_ORIGIN 15 /* channel is on origin site */ -/* channel specific stuff */ -#define FLG_FILLEMPTY 16 /* fill fifo on first frame (empty) */ -/* arcofi specific */ -#define FLG_ARCOFI_TIMER 17 -#define FLG_ARCOFI_ERROR 18 -/* isar specific */ -#define FLG_INITIALIZED 17 -#define FLG_DLEETX 18 -#define FLG_LASTDLE 19 -#define FLG_FIRST 20 -#define FLG_LASTDATA 21 -#define FLG_NMD_DATA 22 -#define FLG_FTI_RUN 23 -#define FLG_LL_OK 24 -#define FLG_LL_CONN 25 -#define FLG_DTMFSEND 26 -#define FLG_TX_EMPTY 27 -/* stop sending received data upstream */ -#define FLG_RX_OFF 28 -/* workq events */ -#define FLG_RECVQUEUE 30 -#define FLG_PHCHANGE 31 - -#define schedule_event(s, ev) do { \ - test_and_set_bit(ev, &((s)->Flags)); \ - schedule_work(&((s)->workq)); \ - } while (0) - -struct dchannel { - struct mISDNdevice dev; - u_long Flags; - struct work_struct workq; - void (*phfunc) (struct dchannel *); - u_int state; - void *l1; - void *hw; - int slot; /* multiport card channel slot */ - struct timer_list timer; - /* receive data */ - struct sk_buff *rx_skb; - int maxlen; - /* send data */ - struct sk_buff_head squeue; - struct sk_buff_head rqueue; - struct sk_buff *tx_skb; - int tx_idx; - int debug; - /* statistics */ - int err_crc; - int err_tx; - int err_rx; -}; - -typedef int (dchannel_l1callback)(struct dchannel *, u_int); -extern int create_l1(struct dchannel *, dchannel_l1callback *); - -/* private L1 commands */ -#define INFO0 0x8002 -#define INFO1 0x8102 -#define INFO2 0x8202 -#define INFO3_P8 0x8302 -#define INFO3_P10 0x8402 -#define INFO4_P8 0x8502 -#define INFO4_P10 0x8602 -#define LOSTFRAMING 0x8702 -#define ANYSIGNAL 0x8802 -#define HW_POWERDOWN 0x8902 -#define HW_RESET_REQ 0x8a02 -#define HW_POWERUP_REQ 0x8b02 -#define HW_DEACT_REQ 0x8c02 -#define HW_ACTIVATE_REQ 0x8e02 -#define HW_D_NOBLOCKED 0x8f02 -#define HW_RESET_IND 0x9002 -#define HW_POWERUP_IND 0x9102 -#define HW_DEACT_IND 0x9202 -#define HW_ACTIVATE_IND 0x9302 -#define HW_DEACT_CNF 0x9402 -#define HW_TESTLOOP 0x9502 -#define HW_TESTRX_RAW 0x9602 -#define HW_TESTRX_HDLC 0x9702 -#define HW_TESTRX_OFF 0x9802 -#define HW_TIMER3_IND 0x9902 -#define HW_TIMER3_VALUE 0x9a00 -#define HW_TIMER3_VMASK 0x00FF - -struct layer1; -extern int l1_event(struct layer1 *, u_int); - -#define MISDN_BCH_FILL_SIZE 4 - -struct bchannel { - struct mISDNchannel ch; - int nr; - u_long Flags; - struct work_struct workq; - u_int state; - void *hw; - int slot; /* multiport card channel slot */ - struct timer_list timer; - /* receive data */ - u8 fill[MISDN_BCH_FILL_SIZE]; - struct sk_buff *rx_skb; - unsigned short maxlen; - unsigned short init_maxlen; /* initial value */ - unsigned short next_maxlen; /* pending value */ - unsigned short minlen; /* for transparent data */ - unsigned short init_minlen; /* initial value */ - unsigned short next_minlen; /* pending value */ - /* send data */ - struct sk_buff *next_skb; - struct sk_buff *tx_skb; - struct sk_buff_head rqueue; - int rcount; - int tx_idx; - int debug; - /* statistics */ - int err_crc; - int err_tx; - int err_rx; - int dropcnt; -}; - -extern int mISDN_initdchannel(struct dchannel *, int, void *); -extern int mISDN_initbchannel(struct bchannel *, unsigned short, - unsigned short); -extern int mISDN_freedchannel(struct dchannel *); -extern void mISDN_clear_bchannel(struct bchannel *); -extern void mISDN_freebchannel(struct bchannel *); -extern int mISDN_ctrl_bchannel(struct bchannel *, struct mISDN_ctrl_req *); -extern void queue_ch_frame(struct mISDNchannel *, u_int, - int, struct sk_buff *); -extern int dchannel_senddata(struct dchannel *, struct sk_buff *); -extern int bchannel_senddata(struct bchannel *, struct sk_buff *); -extern int bchannel_get_rxbuf(struct bchannel *, int); -extern void recv_Dchannel(struct dchannel *); -extern void recv_Echannel(struct dchannel *, struct dchannel *); -extern void recv_Bchannel(struct bchannel *, unsigned int, bool); -extern void recv_Dchannel_skb(struct dchannel *, struct sk_buff *); -extern void recv_Bchannel_skb(struct bchannel *, struct sk_buff *); -extern int get_next_bframe(struct bchannel *); -extern int get_next_dframe(struct dchannel *); - -#endif diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h deleted file mode 100644 index 7aab4a769736..000000000000 --- a/include/linux/mISDNif.h +++ /dev/null @@ -1,603 +0,0 @@ -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - * - * This code is free software; you can redistribute it and/or modify - * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE - * version 2.1 as published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU LESSER GENERAL PUBLIC LICENSE for more details. - * - */ - -#ifndef mISDNIF_H -#define mISDNIF_H - -#include -#include -#include - -/* - * ABI Version 32 bit - * - * <8 bit> Major version - * - changed if any interface become backwards incompatible - * - * <8 bit> Minor version - * - changed if any interface is extended but backwards compatible - * - * <16 bit> Release number - * - should be incremented on every checkin - */ -#define MISDN_MAJOR_VERSION 1 -#define MISDN_MINOR_VERSION 1 -#define MISDN_RELEASE 29 - -/* primitives for information exchange - * generell format - * <16 bit 0 > - * <8 bit command> - * BIT 8 = 1 LAYER private - * BIT 7 = 1 answer - * BIT 6 = 1 DATA - * <8 bit target layer mask> - * - * Layer = 00 is reserved for general commands - Layer = 01 L2 -> HW - Layer = 02 HW -> L2 - Layer = 04 L3 -> L2 - Layer = 08 L2 -> L3 - * Layer = FF is reserved for broadcast commands - */ - -#define MISDN_CMDMASK 0xff00 -#define MISDN_LAYERMASK 0x00ff - -/* generell commands */ -#define OPEN_CHANNEL 0x0100 -#define CLOSE_CHANNEL 0x0200 -#define CONTROL_CHANNEL 0x0300 -#define CHECK_DATA 0x0400 - -/* layer 2 -> layer 1 */ -#define PH_ACTIVATE_REQ 0x0101 -#define PH_DEACTIVATE_REQ 0x0201 -#define PH_DATA_REQ 0x2001 -#define MPH_ACTIVATE_REQ 0x0501 -#define MPH_DEACTIVATE_REQ 0x0601 -#define MPH_INFORMATION_REQ 0x0701 -#define PH_CONTROL_REQ 0x0801 - -/* layer 1 -> layer 2 */ -#define PH_ACTIVATE_IND 0x0102 -#define PH_ACTIVATE_CNF 0x4102 -#define PH_DEACTIVATE_IND 0x0202 -#define PH_DEACTIVATE_CNF 0x4202 -#define PH_DATA_IND 0x2002 -#define PH_DATA_E_IND 0x3002 -#define MPH_ACTIVATE_IND 0x0502 -#define MPH_DEACTIVATE_IND 0x0602 -#define MPH_INFORMATION_IND 0x0702 -#define PH_DATA_CNF 0x6002 -#define PH_CONTROL_IND 0x0802 -#define PH_CONTROL_CNF 0x4802 - -/* layer 3 -> layer 2 */ -#define DL_ESTABLISH_REQ 0x1004 -#define DL_RELEASE_REQ 0x1104 -#define DL_DATA_REQ 0x3004 -#define DL_UNITDATA_REQ 0x3104 -#define DL_INFORMATION_REQ 0x0004 - -/* layer 2 -> layer 3 */ -#define DL_ESTABLISH_IND 0x1008 -#define DL_ESTABLISH_CNF 0x5008 -#define DL_RELEASE_IND 0x1108 -#define DL_RELEASE_CNF 0x5108 -#define DL_DATA_IND 0x3008 -#define DL_UNITDATA_IND 0x3108 -#define DL_INFORMATION_IND 0x0008 - -/* intern layer 2 management */ -#define MDL_ASSIGN_REQ 0x1804 -#define MDL_ASSIGN_IND 0x1904 -#define MDL_REMOVE_REQ 0x1A04 -#define MDL_REMOVE_IND 0x1B04 -#define MDL_STATUS_UP_IND 0x1C04 -#define MDL_STATUS_DOWN_IND 0x1D04 -#define MDL_STATUS_UI_IND 0x1E04 -#define MDL_ERROR_IND 0x1F04 -#define MDL_ERROR_RSP 0x5F04 - -/* intern layer 2 */ -#define DL_TIMER200_IND 0x7004 -#define DL_TIMER203_IND 0x7304 -#define DL_INTERN_MSG 0x7804 - -/* DL_INFORMATION_IND types */ -#define DL_INFO_L2_CONNECT 0x0001 -#define DL_INFO_L2_REMOVED 0x0002 - -/* PH_CONTROL types */ -/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */ -#define DTMF_TONE_VAL 0x2000 -#define DTMF_TONE_MASK 0x007F -#define DTMF_TONE_START 0x2100 -#define DTMF_TONE_STOP 0x2200 -#define DTMF_HFC_COEF 0x4000 -#define DSP_CONF_JOIN 0x2403 -#define DSP_CONF_SPLIT 0x2404 -#define DSP_RECEIVE_OFF 0x2405 -#define DSP_RECEIVE_ON 0x2406 -#define DSP_ECHO_ON 0x2407 -#define DSP_ECHO_OFF 0x2408 -#define DSP_MIX_ON 0x2409 -#define DSP_MIX_OFF 0x240a -#define DSP_DELAY 0x240b -#define DSP_JITTER 0x240c -#define DSP_TXDATA_ON 0x240d -#define DSP_TXDATA_OFF 0x240e -#define DSP_TX_DEJITTER 0x240f -#define DSP_TX_DEJ_OFF 0x2410 -#define DSP_TONE_PATT_ON 0x2411 -#define DSP_TONE_PATT_OFF 0x2412 -#define DSP_VOL_CHANGE_TX 0x2413 -#define DSP_VOL_CHANGE_RX 0x2414 -#define DSP_BF_ENABLE_KEY 0x2415 -#define DSP_BF_DISABLE 0x2416 -#define DSP_BF_ACCEPT 0x2416 -#define DSP_BF_REJECT 0x2417 -#define DSP_PIPELINE_CFG 0x2418 -#define HFC_VOL_CHANGE_TX 0x2601 -#define HFC_VOL_CHANGE_RX 0x2602 -#define HFC_SPL_LOOP_ON 0x2603 -#define HFC_SPL_LOOP_OFF 0x2604 -/* for T30 FAX and analog modem */ -#define HW_MOD_FRM 0x4000 -#define HW_MOD_FRH 0x4001 -#define HW_MOD_FTM 0x4002 -#define HW_MOD_FTH 0x4003 -#define HW_MOD_FTS 0x4004 -#define HW_MOD_CONNECT 0x4010 -#define HW_MOD_OK 0x4011 -#define HW_MOD_NOCARR 0x4012 -#define HW_MOD_FCERROR 0x4013 -#define HW_MOD_READY 0x4014 -#define HW_MOD_LASTDATA 0x4015 - -/* DSP_TONE_PATT_ON parameter */ -#define TONE_OFF 0x0000 -#define TONE_GERMAN_DIALTONE 0x0001 -#define TONE_GERMAN_OLDDIALTONE 0x0002 -#define TONE_AMERICAN_DIALTONE 0x0003 -#define TONE_GERMAN_DIALPBX 0x0004 -#define TONE_GERMAN_OLDDIALPBX 0x0005 -#define TONE_AMERICAN_DIALPBX 0x0006 -#define TONE_GERMAN_RINGING 0x0007 -#define TONE_GERMAN_OLDRINGING 0x0008 -#define TONE_AMERICAN_RINGPBX 0x000b -#define TONE_GERMAN_RINGPBX 0x000c -#define TONE_GERMAN_OLDRINGPBX 0x000d -#define TONE_AMERICAN_RINGING 0x000e -#define TONE_GERMAN_BUSY 0x000f -#define TONE_GERMAN_OLDBUSY 0x0010 -#define TONE_AMERICAN_BUSY 0x0011 -#define TONE_GERMAN_HANGUP 0x0012 -#define TONE_GERMAN_OLDHANGUP 0x0013 -#define TONE_AMERICAN_HANGUP 0x0014 -#define TONE_SPECIAL_INFO 0x0015 -#define TONE_GERMAN_GASSENBESETZT 0x0016 -#define TONE_GERMAN_AUFSCHALTTON 0x0016 - -/* MPH_INFORMATION_IND */ -#define L1_SIGNAL_LOS_OFF 0x0010 -#define L1_SIGNAL_LOS_ON 0x0011 -#define L1_SIGNAL_AIS_OFF 0x0012 -#define L1_SIGNAL_AIS_ON 0x0013 -#define L1_SIGNAL_RDI_OFF 0x0014 -#define L1_SIGNAL_RDI_ON 0x0015 -#define L1_SIGNAL_SLIP_RX 0x0020 -#define L1_SIGNAL_SLIP_TX 0x0021 - -/* - * protocol ids - * D channel 1-31 - * B channel 33 - 63 - */ - -#define ISDN_P_NONE 0 -#define ISDN_P_BASE 0 -#define ISDN_P_TE_S0 0x01 -#define ISDN_P_NT_S0 0x02 -#define ISDN_P_TE_E1 0x03 -#define ISDN_P_NT_E1 0x04 -#define ISDN_P_TE_UP0 0x05 -#define ISDN_P_NT_UP0 0x06 - -#define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \ - (p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE)) -#define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \ - (p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT)) -#define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0)) -#define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1)) -#define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0)) - - -#define ISDN_P_LAPD_TE 0x10 -#define ISDN_P_LAPD_NT 0x11 - -#define ISDN_P_B_MASK 0x1f -#define ISDN_P_B_START 0x20 - -#define ISDN_P_B_RAW 0x21 -#define ISDN_P_B_HDLC 0x22 -#define ISDN_P_B_X75SLP 0x23 -#define ISDN_P_B_L2DTMF 0x24 -#define ISDN_P_B_L2DSP 0x25 -#define ISDN_P_B_L2DSPHDLC 0x26 -#define ISDN_P_B_T30_FAX 0x27 -#define ISDN_P_B_MODEM_ASYNC 0x28 - -#define OPTION_L2_PMX 1 -#define OPTION_L2_PTP 2 -#define OPTION_L2_FIXEDTEI 3 -#define OPTION_L2_CLEANUP 4 -#define OPTION_L1_HOLD 5 - -/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */ -#define MISDN_MAX_IDLEN 20 - -struct mISDNhead { - unsigned int prim; - unsigned int id; -} __packed; - -#define MISDN_HEADER_LEN sizeof(struct mISDNhead) -#define MAX_DATA_SIZE 2048 -#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN) -#define MAX_DFRAME_LEN 260 - -#define MISDN_ID_ADDR_MASK 0xFFFF -#define MISDN_ID_TEI_MASK 0xFF00 -#define MISDN_ID_SAPI_MASK 0x00FF -#define MISDN_ID_TEI_ANY 0x7F00 - -#define MISDN_ID_ANY 0xFFFF -#define MISDN_ID_NONE 0xFFFE - -#define GROUP_TEI 127 -#define TEI_SAPI 63 -#define CTRL_SAPI 0 - -#define MISDN_MAX_CHANNEL 127 -#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3) - -#define SOL_MISDN 0 - -struct sockaddr_mISDN { - sa_family_t family; - unsigned char dev; - unsigned char channel; - unsigned char sapi; - unsigned char tei; -}; - -struct mISDNversion { - unsigned char major; - unsigned char minor; - unsigned short release; -}; - -struct mISDN_devinfo { - u_int id; - u_int Dprotocols; - u_int Bprotocols; - u_int protocol; - u_char channelmap[MISDN_CHMAP_SIZE]; - u_int nrbchan; - char name[MISDN_MAX_IDLEN]; -}; - -struct mISDN_devrename { - u_int id; - char name[MISDN_MAX_IDLEN]; /* new name */ -}; - -/* MPH_INFORMATION_REQ payload */ -struct ph_info_ch { - __u32 protocol; - __u64 Flags; -}; - -struct ph_info_dch { - struct ph_info_ch ch; - __u16 state; - __u16 num_bch; -}; - -struct ph_info { - struct ph_info_dch dch; - struct ph_info_ch bch[]; -}; - -/* timer device ioctl */ -#define IMADDTIMER _IOR('I', 64, int) -#define IMDELTIMER _IOR('I', 65, int) - -/* socket ioctls */ -#define IMGETVERSION _IOR('I', 66, int) -#define IMGETCOUNT _IOR('I', 67, int) -#define IMGETDEVINFO _IOR('I', 68, int) -#define IMCTRLREQ _IOR('I', 69, int) -#define IMCLEAR_L2 _IOR('I', 70, int) -#define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename) -#define IMHOLD_L1 _IOR('I', 72, int) - -static inline int -test_channelmap(u_int nr, u_char *map) -{ - if (nr <= MISDN_MAX_CHANNEL) - return map[nr >> 3] & (1 << (nr & 7)); - else - return 0; -} - -static inline void -set_channelmap(u_int nr, u_char *map) -{ - map[nr >> 3] |= (1 << (nr & 7)); -} - -static inline void -clear_channelmap(u_int nr, u_char *map) -{ - map[nr >> 3] &= ~(1 << (nr & 7)); -} - -/* CONTROL_CHANNEL parameters */ -#define MISDN_CTRL_GETOP 0x0000 -#define MISDN_CTRL_LOOP 0x0001 -#define MISDN_CTRL_CONNECT 0x0002 -#define MISDN_CTRL_DISCONNECT 0x0004 -#define MISDN_CTRL_RX_BUFFER 0x0008 -#define MISDN_CTRL_PCMCONNECT 0x0010 -#define MISDN_CTRL_PCMDISCONNECT 0x0020 -#define MISDN_CTRL_SETPEER 0x0040 -#define MISDN_CTRL_UNSETPEER 0x0080 -#define MISDN_CTRL_RX_OFF 0x0100 -#define MISDN_CTRL_FILL_EMPTY 0x0200 -#define MISDN_CTRL_GETPEER 0x0400 -#define MISDN_CTRL_L1_TIMER3 0x0800 -#define MISDN_CTRL_HW_FEATURES_OP 0x2000 -#define MISDN_CTRL_HW_FEATURES 0x2001 -#define MISDN_CTRL_HFC_OP 0x4000 -#define MISDN_CTRL_HFC_PCM_CONN 0x4001 -#define MISDN_CTRL_HFC_PCM_DISC 0x4002 -#define MISDN_CTRL_HFC_CONF_JOIN 0x4003 -#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004 -#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005 -#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006 -#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007 -#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008 -#define MISDN_CTRL_HFC_WD_INIT 0x4009 -#define MISDN_CTRL_HFC_WD_RESET 0x400A - -/* special RX buffer value for MISDN_CTRL_RX_BUFFER request.p1 is the minimum - * buffer size request.p2 the maximum. Using MISDN_CTRL_RX_SIZE_IGNORE will - * not change the value, but still read back the actual stetting. - */ -#define MISDN_CTRL_RX_SIZE_IGNORE -1 - -/* socket options */ -#define MISDN_TIME_STAMP 0x0001 - -struct mISDN_ctrl_req { - int op; - int channel; - int p1; - int p2; -}; - -/* muxer options */ -#define MISDN_OPT_ALL 1 -#define MISDN_OPT_TEIMGR 2 - -#ifdef __KERNEL__ -#include -#include -#include -#include -#include - -#define DEBUG_CORE 0x000000ff -#define DEBUG_CORE_FUNC 0x00000002 -#define DEBUG_SOCKET 0x00000004 -#define DEBUG_MANAGER 0x00000008 -#define DEBUG_SEND_ERR 0x00000010 -#define DEBUG_MSG_THREAD 0x00000020 -#define DEBUG_QUEUE_FUNC 0x00000040 -#define DEBUG_L1 0x0000ff00 -#define DEBUG_L1_FSM 0x00000200 -#define DEBUG_L2 0x00ff0000 -#define DEBUG_L2_FSM 0x00020000 -#define DEBUG_L2_CTRL 0x00040000 -#define DEBUG_L2_RECV 0x00080000 -#define DEBUG_L2_TEI 0x00100000 -#define DEBUG_L2_TEIFSM 0x00200000 -#define DEBUG_TIMER 0x01000000 -#define DEBUG_CLOCK 0x02000000 - -#define mISDN_HEAD_P(s) ((struct mISDNhead *)&s->cb[0]) -#define mISDN_HEAD_PRIM(s) (((struct mISDNhead *)&s->cb[0])->prim) -#define mISDN_HEAD_ID(s) (((struct mISDNhead *)&s->cb[0])->id) - -/* socket states */ -#define MISDN_OPEN 1 -#define MISDN_BOUND 2 -#define MISDN_CLOSED 3 - -struct mISDNchannel; -struct mISDNdevice; -struct mISDNstack; -struct mISDNclock; - -struct channel_req { - u_int protocol; - struct sockaddr_mISDN adr; - struct mISDNchannel *ch; -}; - -typedef int (ctrl_func_t)(struct mISDNchannel *, u_int, void *); -typedef int (send_func_t)(struct mISDNchannel *, struct sk_buff *); -typedef int (create_func_t)(struct channel_req *); - -struct Bprotocol { - struct list_head list; - char *name; - u_int Bprotocols; - create_func_t *create; -}; - -struct mISDNchannel { - struct list_head list; - u_int protocol; - u_int nr; - u_long opt; - u_int addr; - struct mISDNstack *st; - struct mISDNchannel *peer; - send_func_t *send; - send_func_t *recv; - ctrl_func_t *ctrl; -}; - -struct mISDN_sock_list { - struct hlist_head head; - rwlock_t lock; -}; - -struct mISDN_sock { - struct sock sk; - struct mISDNchannel ch; - u_int cmask; - struct mISDNdevice *dev; -}; - - - -struct mISDNdevice { - struct mISDNchannel D; - u_int id; - u_int Dprotocols; - u_int Bprotocols; - u_int nrbchan; - u_char channelmap[MISDN_CHMAP_SIZE]; - struct list_head bchannels; - struct mISDNchannel *teimgr; - struct device dev; -}; - -struct mISDNstack { - u_long status; - struct mISDNdevice *dev; - struct task_struct *thread; - struct completion *notify; - wait_queue_head_t workq; - struct sk_buff_head msgq; - struct list_head layer2; - struct mISDNchannel *layer1; - struct mISDNchannel own; - struct mutex lmutex; /* protect lists */ - struct mISDN_sock_list l1sock; -#ifdef MISDN_MSG_STATS - u_int msg_cnt; - u_int sleep_cnt; - u_int stopped_cnt; -#endif -}; - -typedef int (clockctl_func_t)(void *, int); - -struct mISDNclock { - struct list_head list; - char name[64]; - int pri; - clockctl_func_t *ctl; - void *priv; -}; - -/* global alloc/queue functions */ - -static inline struct sk_buff * -mI_alloc_skb(unsigned int len, gfp_t gfp_mask) -{ - struct sk_buff *skb; - - skb = alloc_skb(len + MISDN_HEADER_LEN, gfp_mask); - if (likely(skb)) - skb_reserve(skb, MISDN_HEADER_LEN); - return skb; -} - -static inline struct sk_buff * -_alloc_mISDN_skb(u_int prim, u_int id, u_int len, void *dp, gfp_t gfp_mask) -{ - struct sk_buff *skb = mI_alloc_skb(len, gfp_mask); - struct mISDNhead *hh; - - if (!skb) - return NULL; - if (len) - skb_put_data(skb, dp, len); - hh = mISDN_HEAD_P(skb); - hh->prim = prim; - hh->id = id; - return skb; -} - -static inline void -_queue_data(struct mISDNchannel *ch, u_int prim, - u_int id, u_int len, void *dp, gfp_t gfp_mask) -{ - struct sk_buff *skb; - - if (!ch->peer) - return; - skb = _alloc_mISDN_skb(prim, id, len, dp, gfp_mask); - if (!skb) - return; - if (ch->recv(ch->peer, skb)) - dev_kfree_skb(skb); -} - -/* global register/unregister functions */ - -extern int mISDN_register_device(struct mISDNdevice *, - struct device *parent, char *name); -extern void mISDN_unregister_device(struct mISDNdevice *); -extern int mISDN_register_Bprotocol(struct Bprotocol *); -extern void mISDN_unregister_Bprotocol(struct Bprotocol *); -extern struct mISDNclock *mISDN_register_clock(char *, int, clockctl_func_t *, - void *); -extern void mISDN_unregister_clock(struct mISDNclock *); - -static inline struct mISDNdevice *dev_to_mISDN(const struct device *dev) -{ - if (dev) - return dev_get_drvdata(dev); - else - return NULL; -} - -extern void set_channel_address(struct mISDNchannel *, u_int, u_int); -extern void mISDN_clock_update(struct mISDNclock *, int, ktime_t *); -extern unsigned short mISDN_clock_get(void); -extern const char *mISDNDevName4ch(struct mISDNchannel *); - -#endif /* __KERNEL__ */ -#endif /* mISDNIF_H */ diff --git a/include/uapi/linux/capi.h b/include/uapi/linux/capi.h deleted file mode 100644 index 31f946f8a88d..000000000000 --- a/include/uapi/linux/capi.h +++ /dev/null @@ -1,134 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* $Id: capi.h,v 1.4.6.1 2001/09/23 22:25:05 kai Exp $ - * - * CAPI 2.0 Interface for Linux - * - * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#ifndef __LINUX_CAPI_H__ -#define __LINUX_CAPI_H__ - -#include -#include -#ifndef __KERNEL__ -#include -#endif - -/* - * CAPI_REGISTER - */ - -typedef struct capi_register_params { /* CAPI_REGISTER */ - __u32 level3cnt; /* No. of simulatneous user data connections */ - __u32 datablkcnt; /* No. of buffered data messages */ - __u32 datablklen; /* Size of buffered data messages */ -} capi_register_params; - -#define CAPI_REGISTER _IOW('C',0x01,struct capi_register_params) - -/* - * CAPI_GET_MANUFACTURER - */ - -#define CAPI_MANUFACTURER_LEN 64 - -#define CAPI_GET_MANUFACTURER _IOWR('C',0x06,int) /* broken: wanted size 64 (CAPI_MANUFACTURER_LEN) */ - -/* - * CAPI_GET_VERSION - */ - -typedef struct capi_version { - __u32 majorversion; - __u32 minorversion; - __u32 majormanuversion; - __u32 minormanuversion; -} capi_version; - -#define CAPI_GET_VERSION _IOWR('C',0x07,struct capi_version) - -/* - * CAPI_GET_SERIAL - */ - -#define CAPI_SERIAL_LEN 8 -#define CAPI_GET_SERIAL _IOWR('C',0x08,int) /* broken: wanted size 8 (CAPI_SERIAL_LEN) */ - -/* - * CAPI_GET_PROFILE - */ - -typedef struct capi_profile { - __u16 ncontroller; /* number of installed controller */ - __u16 nbchannel; /* number of B-Channels */ - __u32 goptions; /* global options */ - __u32 support1; /* B1 protocols support */ - __u32 support2; /* B2 protocols support */ - __u32 support3; /* B3 protocols support */ - __u32 reserved[6]; /* reserved */ - __u32 manu[5]; /* manufacturer specific information */ -} capi_profile; - -#define CAPI_GET_PROFILE _IOWR('C',0x09,struct capi_profile) - -typedef struct capi_manufacturer_cmd { - unsigned long cmd; - void __user *data; -} capi_manufacturer_cmd; - -/* - * CAPI_MANUFACTURER_CMD - */ - -#define CAPI_MANUFACTURER_CMD _IOWR('C',0x20, struct capi_manufacturer_cmd) - -/* - * CAPI_GET_ERRCODE - * capi errcode is set, * if read, write, or ioctl returns EIO, - * ioctl returns errcode directly, and in arg, if != 0 - */ - -#define CAPI_GET_ERRCODE _IOR('C',0x21, __u16) - -/* - * CAPI_INSTALLED - */ -#define CAPI_INSTALLED _IOR('C',0x22, __u16) - - -/* - * member contr is input for - * CAPI_GET_MANUFACTURER, CAPI_GET_VERSION, CAPI_GET_SERIAL - * and CAPI_GET_PROFILE - */ -typedef union capi_ioctl_struct { - __u32 contr; - capi_register_params rparams; - __u8 manufacturer[CAPI_MANUFACTURER_LEN]; - capi_version version; - __u8 serial[CAPI_SERIAL_LEN]; - capi_profile profile; - capi_manufacturer_cmd cmd; - __u16 errcode; -} capi_ioctl_struct; - -/* - * Middleware extension - */ - -#define CAPIFLAG_HIGHJACKING 0x0001 - -#define CAPI_GET_FLAGS _IOR('C',0x23, unsigned) -#define CAPI_SET_FLAGS _IOR('C',0x24, unsigned) -#define CAPI_CLR_FLAGS _IOR('C',0x25, unsigned) - -#define CAPI_NCCI_OPENCOUNT _IOR('C',0x26, unsigned) - -#define CAPI_NCCI_GETUNIT _IOR('C',0x27, unsigned) - -#endif /* __LINUX_CAPI_H__ */ diff --git a/include/uapi/linux/isdn/capicmd.h b/include/uapi/linux/isdn/capicmd.h deleted file mode 100644 index 5ec88e7548a9..000000000000 --- a/include/uapi/linux/isdn/capicmd.h +++ /dev/null @@ -1,117 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* $Id: capicmd.h,v 1.2.6.2 2001/09/23 22:24:33 kai Exp $ - * - * CAPI 2.0 Interface for Linux - * - * Copyright 1997 by Carsten Paeth - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#ifndef __CAPICMD_H__ -#define __CAPICMD_H__ - -#define CAPI_MSG_BASELEN 8 -#define CAPI_DATA_B3_REQ_LEN (CAPI_MSG_BASELEN+4+4+2+2+2) -#define CAPI_DATA_B3_RESP_LEN (CAPI_MSG_BASELEN+4+2) -#define CAPI_DISCONNECT_B3_RESP_LEN (CAPI_MSG_BASELEN+4) - -/*----- CAPI commands -----*/ -#define CAPI_ALERT 0x01 -#define CAPI_CONNECT 0x02 -#define CAPI_CONNECT_ACTIVE 0x03 -#define CAPI_CONNECT_B3_ACTIVE 0x83 -#define CAPI_CONNECT_B3 0x82 -#define CAPI_CONNECT_B3_T90_ACTIVE 0x88 -#define CAPI_DATA_B3 0x86 -#define CAPI_DISCONNECT_B3 0x84 -#define CAPI_DISCONNECT 0x04 -#define CAPI_FACILITY 0x80 -#define CAPI_INFO 0x08 -#define CAPI_LISTEN 0x05 -#define CAPI_MANUFACTURER 0xff -#define CAPI_RESET_B3 0x87 -#define CAPI_SELECT_B_PROTOCOL 0x41 - -/*----- CAPI subcommands -----*/ - -#define CAPI_REQ 0x80 -#define CAPI_CONF 0x81 -#define CAPI_IND 0x82 -#define CAPI_RESP 0x83 - -/*----- CAPI combined commands -----*/ - -#define CAPICMD(cmd,subcmd) (((cmd)<<8)|(subcmd)) - -#define CAPI_DISCONNECT_REQ CAPICMD(CAPI_DISCONNECT,CAPI_REQ) -#define CAPI_DISCONNECT_CONF CAPICMD(CAPI_DISCONNECT,CAPI_CONF) -#define CAPI_DISCONNECT_IND CAPICMD(CAPI_DISCONNECT,CAPI_IND) -#define CAPI_DISCONNECT_RESP CAPICMD(CAPI_DISCONNECT,CAPI_RESP) - -#define CAPI_ALERT_REQ CAPICMD(CAPI_ALERT,CAPI_REQ) -#define CAPI_ALERT_CONF CAPICMD(CAPI_ALERT,CAPI_CONF) - -#define CAPI_CONNECT_REQ CAPICMD(CAPI_CONNECT,CAPI_REQ) -#define CAPI_CONNECT_CONF CAPICMD(CAPI_CONNECT,CAPI_CONF) -#define CAPI_CONNECT_IND CAPICMD(CAPI_CONNECT,CAPI_IND) -#define CAPI_CONNECT_RESP CAPICMD(CAPI_CONNECT,CAPI_RESP) - -#define CAPI_CONNECT_ACTIVE_REQ CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_REQ) -#define CAPI_CONNECT_ACTIVE_CONF CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_CONF) -#define CAPI_CONNECT_ACTIVE_IND CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_IND) -#define CAPI_CONNECT_ACTIVE_RESP CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_RESP) - -#define CAPI_SELECT_B_PROTOCOL_REQ CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_REQ) -#define CAPI_SELECT_B_PROTOCOL_CONF CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_CONF) - -#define CAPI_CONNECT_B3_ACTIVE_REQ CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_REQ) -#define CAPI_CONNECT_B3_ACTIVE_CONF CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_CONF) -#define CAPI_CONNECT_B3_ACTIVE_IND CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_IND) -#define CAPI_CONNECT_B3_ACTIVE_RESP CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_RESP) - -#define CAPI_CONNECT_B3_REQ CAPICMD(CAPI_CONNECT_B3,CAPI_REQ) -#define CAPI_CONNECT_B3_CONF CAPICMD(CAPI_CONNECT_B3,CAPI_CONF) -#define CAPI_CONNECT_B3_IND CAPICMD(CAPI_CONNECT_B3,CAPI_IND) -#define CAPI_CONNECT_B3_RESP CAPICMD(CAPI_CONNECT_B3,CAPI_RESP) - - -#define CAPI_CONNECT_B3_T90_ACTIVE_IND CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_IND) -#define CAPI_CONNECT_B3_T90_ACTIVE_RESP CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_RESP) - -#define CAPI_DATA_B3_REQ CAPICMD(CAPI_DATA_B3,CAPI_REQ) -#define CAPI_DATA_B3_CONF CAPICMD(CAPI_DATA_B3,CAPI_CONF) -#define CAPI_DATA_B3_IND CAPICMD(CAPI_DATA_B3,CAPI_IND) -#define CAPI_DATA_B3_RESP CAPICMD(CAPI_DATA_B3,CAPI_RESP) - -#define CAPI_DISCONNECT_B3_REQ CAPICMD(CAPI_DISCONNECT_B3,CAPI_REQ) -#define CAPI_DISCONNECT_B3_CONF CAPICMD(CAPI_DISCONNECT_B3,CAPI_CONF) -#define CAPI_DISCONNECT_B3_IND CAPICMD(CAPI_DISCONNECT_B3,CAPI_IND) -#define CAPI_DISCONNECT_B3_RESP CAPICMD(CAPI_DISCONNECT_B3,CAPI_RESP) - -#define CAPI_RESET_B3_REQ CAPICMD(CAPI_RESET_B3,CAPI_REQ) -#define CAPI_RESET_B3_CONF CAPICMD(CAPI_RESET_B3,CAPI_CONF) -#define CAPI_RESET_B3_IND CAPICMD(CAPI_RESET_B3,CAPI_IND) -#define CAPI_RESET_B3_RESP CAPICMD(CAPI_RESET_B3,CAPI_RESP) - -#define CAPI_LISTEN_REQ CAPICMD(CAPI_LISTEN,CAPI_REQ) -#define CAPI_LISTEN_CONF CAPICMD(CAPI_LISTEN,CAPI_CONF) - -#define CAPI_MANUFACTURER_REQ CAPICMD(CAPI_MANUFACTURER,CAPI_REQ) -#define CAPI_MANUFACTURER_CONF CAPICMD(CAPI_MANUFACTURER,CAPI_CONF) -#define CAPI_MANUFACTURER_IND CAPICMD(CAPI_MANUFACTURER,CAPI_IND) -#define CAPI_MANUFACTURER_RESP CAPICMD(CAPI_MANUFACTURER,CAPI_RESP) - -#define CAPI_FACILITY_REQ CAPICMD(CAPI_FACILITY,CAPI_REQ) -#define CAPI_FACILITY_CONF CAPICMD(CAPI_FACILITY,CAPI_CONF) -#define CAPI_FACILITY_IND CAPICMD(CAPI_FACILITY,CAPI_IND) -#define CAPI_FACILITY_RESP CAPICMD(CAPI_FACILITY,CAPI_RESP) - -#define CAPI_INFO_REQ CAPICMD(CAPI_INFO,CAPI_REQ) -#define CAPI_INFO_CONF CAPICMD(CAPI_INFO,CAPI_CONF) -#define CAPI_INFO_IND CAPICMD(CAPI_INFO,CAPI_IND) -#define CAPI_INFO_RESP CAPICMD(CAPI_INFO,CAPI_RESP) - -#endif /* __CAPICMD_H__ */ diff --git a/include/uapi/linux/kernelcapi.h b/include/uapi/linux/kernelcapi.h deleted file mode 100644 index 325a856e0e20..000000000000 --- a/include/uapi/linux/kernelcapi.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * $Id: kernelcapi.h,v 1.8.6.2 2001/02/07 11:31:31 kai Exp $ - * - * Kernel CAPI 2.0 Interface for Linux - * - * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) - * - */ - -#ifndef _UAPI__KERNELCAPI_H__ -#define _UAPI__KERNELCAPI_H__ - -#define CAPI_MAXAPPL 240 /* maximum number of applications */ -#define CAPI_MAXCONTR 32 /* maximum number of controller */ -#define CAPI_MAXDATAWINDOW 8 - - -typedef struct kcapi_flagdef { - int contr; - int flag; -} kcapi_flagdef; - -typedef struct kcapi_carddef { - char driver[32]; - unsigned int port; - unsigned irq; - unsigned int membase; - int cardnr; -} kcapi_carddef; - -/* new ioctls >= 10 */ -#define KCAPI_CMD_TRACE 10 -#define KCAPI_CMD_ADDCARD 11 /* OBSOLETE */ - -/* - * flag > 2 => trace also data - * flag & 1 => show trace - */ -#define KCAPI_TRACE_OFF 0 -#define KCAPI_TRACE_SHORT_NO_DATA 1 -#define KCAPI_TRACE_FULL_NO_DATA 2 -#define KCAPI_TRACE_SHORT 3 -#define KCAPI_TRACE_FULL 4 - - - -#endif /* _UAPI__KERNELCAPI_H__ */ diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 6b2b65a66700..ee6457d1a5ee 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -33,7 +33,6 @@ menuconfig BT HCI Device drivers (Interface to the hardware) RFCOMM Module (RFCOMM Protocol) BNEP Module (Bluetooth Network Encapsulation Protocol) - CMTP Module (CAPI Message Transport Protocol) HIDP Module (Human Interface Device Protocol) Say Y here to compile Bluetooth support into the kernel or say M to @@ -58,8 +57,6 @@ source "net/bluetooth/rfcomm/Kconfig" source "net/bluetooth/bnep/Kconfig" -source "net/bluetooth/cmtp/Kconfig" - source "net/bluetooth/hidp/Kconfig" config BT_LE diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index a7eede7616d8..41049b280887 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_BT) += bluetooth.o obj-$(CONFIG_BT_RFCOMM) += rfcomm/ obj-$(CONFIG_BT_BNEP) += bnep/ -obj-$(CONFIG_BT_CMTP) += cmtp/ obj-$(CONFIG_BT_HIDP) += hidp/ obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o diff --git a/net/bluetooth/cmtp/Kconfig b/net/bluetooth/cmtp/Kconfig deleted file mode 100644 index 34e923466236..000000000000 --- a/net/bluetooth/cmtp/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config BT_CMTP - tristate "CMTP protocol support (DEPRECATED)" - depends on BT_BREDR && ISDN_CAPI && DEPRECATED - help - CMTP (CAPI Message Transport Protocol) is a transport layer - for CAPI messages. CMTP is required for the Bluetooth Common - ISDN Access Profile. - - Say Y here to compile CMTP support into the kernel or say M to - compile it as module (cmtp). - diff --git a/net/bluetooth/cmtp/Makefile b/net/bluetooth/cmtp/Makefile deleted file mode 100644 index b2262ca97499..000000000000 --- a/net/bluetooth/cmtp/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Linux Bluetooth CMTP layer -# - -obj-$(CONFIG_BT_CMTP) += cmtp.o - -cmtp-objs := core.o sock.o capi.o diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c deleted file mode 100644 index b95413bffa16..000000000000 --- a/net/bluetooth/cmtp/capi.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "cmtp.h" - -#define CAPI_INTEROPERABILITY 0x20 - -#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ) -#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF) -#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND) -#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP) - -#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2) -#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4) -#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2) -#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2) - -#define CAPI_FUNCTION_REGISTER 0 -#define CAPI_FUNCTION_RELEASE 1 -#define CAPI_FUNCTION_GET_PROFILE 2 -#define CAPI_FUNCTION_GET_MANUFACTURER 3 -#define CAPI_FUNCTION_GET_VERSION 4 -#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5 -#define CAPI_FUNCTION_MANUFACTURER 6 -#define CAPI_FUNCTION_LOOPBACK 7 - - -#define CMTP_MSGNUM 1 -#define CMTP_APPLID 2 -#define CMTP_MAPPING 3 - -static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl) -{ - struct cmtp_application *app = kzalloc_obj(*app); - - BT_DBG("session %p application %p appl %u", session, app, appl); - - if (!app) - return NULL; - - app->state = BT_OPEN; - app->appl = appl; - - list_add_tail(&app->list, &session->applications); - - return app; -} - -static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app) -{ - BT_DBG("session %p application %p", session, app); - - if (app) { - list_del(&app->list); - kfree(app); - } -} - -static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value) -{ - struct cmtp_application *app; - - list_for_each_entry(app, &session->applications, list) { - switch (pattern) { - case CMTP_MSGNUM: - if (app->msgnum == value) - return app; - break; - case CMTP_APPLID: - if (app->appl == value) - return app; - break; - case CMTP_MAPPING: - if (app->mapping == value) - return app; - break; - } - } - - return NULL; -} - -static int cmtp_msgnum_get(struct cmtp_session *session) -{ - session->msgnum++; - - if ((session->msgnum & 0xff) > 200) - session->msgnum = CMTP_INITIAL_MSGNUM + 1; - - return session->msgnum; -} - -static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct cmtp_scb *scb = (void *) skb->cb; - - BT_DBG("session %p skb %p len %u", session, skb, skb->len); - - scb->id = -1; - scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3); - - skb_queue_tail(&session->transmit, skb); - - wake_up_interruptible(sk_sleep(session->sock->sk)); -} - -static void cmtp_send_interopmsg(struct cmtp_session *session, - __u8 subcmd, __u16 appl, __u16 msgnum, - __u16 function, unsigned char *buf, int len) -{ - struct sk_buff *skb; - unsigned char *s; - - BT_DBG("session %p subcmd 0x%02x appl %u msgnum %u", session, subcmd, appl, msgnum); - - skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC); - if (!skb) { - BT_ERR("Can't allocate memory for interoperability packet"); - return; - } - - s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len); - - capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len); - capimsg_setu16(s, 2, appl); - capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY); - capimsg_setu8 (s, 5, subcmd); - capimsg_setu16(s, 6, msgnum); - - /* Interoperability selector (Bluetooth Device Management) */ - capimsg_setu16(s, 8, 0x0001); - - capimsg_setu8 (s, 10, 3 + len); - capimsg_setu16(s, 11, function); - capimsg_setu8 (s, 13, len); - - if (len > 0) - memcpy(s + 14, buf, len); - - cmtp_send_capimsg(session, skb); -} - -static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct capi_ctr *ctrl = &session->ctrl; - struct cmtp_application *application; - __u16 appl, msgnum, func, info; - __u32 controller; - - BT_DBG("session %p skb %p len %u", session, skb, skb->len); - - switch (CAPIMSG_SUBCOMMAND(skb->data)) { - case CAPI_CONF: - if (skb->len < CAPI_MSG_BASELEN + 10) - break; - - func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5); - info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8); - - switch (func) { - case CAPI_FUNCTION_REGISTER: - msgnum = CAPIMSG_MSGID(skb->data); - - application = cmtp_application_get(session, CMTP_MSGNUM, msgnum); - if (application) { - application->state = BT_CONNECTED; - application->msgnum = 0; - application->mapping = CAPIMSG_APPID(skb->data); - wake_up_interruptible(&session->wait); - } - - break; - - case CAPI_FUNCTION_RELEASE: - appl = CAPIMSG_APPID(skb->data); - - application = cmtp_application_get(session, CMTP_MAPPING, appl); - if (application) { - application->state = BT_CLOSED; - application->msgnum = 0; - wake_up_interruptible(&session->wait); - } - - break; - - case CAPI_FUNCTION_GET_PROFILE: - if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile)) - break; - - controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11); - msgnum = CAPIMSG_MSGID(skb->data); - - if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) { - session->ncontroller = controller; - wake_up_interruptible(&session->wait); - break; - } - - if (!info && ctrl) { - memcpy(&ctrl->profile, - skb->data + CAPI_MSG_BASELEN + 11, - sizeof(capi_profile)); - session->state = BT_CONNECTED; - capi_ctr_ready(ctrl); - } - - break; - - case CAPI_FUNCTION_GET_MANUFACTURER: - if (!info && ctrl && skb->len > CAPI_MSG_BASELEN + 14) - strscpy_pad(ctrl->manu, - skb->data + CAPI_MSG_BASELEN + 15, - skb->data[CAPI_MSG_BASELEN + 14]); - break; - - case CAPI_FUNCTION_GET_VERSION: - if (skb->len < CAPI_MSG_BASELEN + 32) - break; - - if (!info && ctrl) { - ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16); - ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20); - ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24); - ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28); - } - - break; - - case CAPI_FUNCTION_GET_SERIAL_NUMBER: - if (!info && ctrl && skb->len > CAPI_MSG_BASELEN + 16) - strscpy_pad(ctrl->serial, - skb->data + CAPI_MSG_BASELEN + 17, - skb->data[CAPI_MSG_BASELEN + 16]); - break; - } - - break; - - case CAPI_IND: - if (skb->len < CAPI_MSG_BASELEN + 6) - break; - - func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3); - - if (func == CAPI_FUNCTION_LOOPBACK) { - int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6, - skb->data[CAPI_MSG_BASELEN + 5]); - appl = CAPIMSG_APPID(skb->data); - msgnum = CAPIMSG_MSGID(skb->data); - cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func, - skb->data + CAPI_MSG_BASELEN + 6, len); - } - - break; - } - - kfree_skb(skb); -} - -void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct capi_ctr *ctrl = &session->ctrl; - struct cmtp_application *application; - __u16 appl; - __u32 contr; - - BT_DBG("session %p skb %p len %u", session, skb, skb->len); - - if (skb->len < CAPI_MSG_BASELEN) - return; - - if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) { - cmtp_recv_interopmsg(session, skb); - return; - } - - if (session->flags & BIT(CMTP_LOOPBACK)) { - kfree_skb(skb); - return; - } - - appl = CAPIMSG_APPID(skb->data); - contr = CAPIMSG_CONTROL(skb->data); - - application = cmtp_application_get(session, CMTP_MAPPING, appl); - if (application) { - appl = application->appl; - CAPIMSG_SETAPPID(skb->data, appl); - } else { - BT_ERR("Can't find application with id %u", appl); - kfree_skb(skb); - return; - } - - if ((contr & 0x7f) == 0x01) { - contr = (contr & 0xffffff80) | session->num; - CAPIMSG_SETCONTROL(skb->data, contr); - } - - capi_ctr_handle_message(ctrl, appl, skb); -} - -static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) -{ - BT_DBG("ctrl %p data %p", ctrl, data); - - return 0; -} - -static void cmtp_reset_ctr(struct capi_ctr *ctrl) -{ - struct cmtp_session *session = ctrl->driverdata; - - BT_DBG("ctrl %p", ctrl); - - capi_ctr_down(ctrl); - - atomic_inc(&session->terminate); - wake_up_process(session->task); -} - -static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp) -{ - DECLARE_WAITQUEUE(wait, current); - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - unsigned long timeo = CMTP_INTEROP_TIMEOUT; - unsigned char buf[8]; - int err = 0, nconn, want = rp->level3cnt; - - BT_DBG("ctrl %p appl %u level3cnt %u datablkcnt %u datablklen %u", - ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen); - - application = cmtp_application_add(session, appl); - if (!application) { - BT_ERR("Can't allocate memory for new application"); - return; - } - - if (want < 0) - nconn = ctrl->profile.nbchannel * -want; - else - nconn = want; - - if (nconn == 0) - nconn = ctrl->profile.nbchannel; - - capimsg_setu16(buf, 0, nconn); - capimsg_setu16(buf, 2, rp->datablkcnt); - capimsg_setu16(buf, 4, rp->datablklen); - - application->state = BT_CONFIG; - application->msgnum = cmtp_msgnum_get(session); - - cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum, - CAPI_FUNCTION_REGISTER, buf, 6); - - add_wait_queue(&session->wait, &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (!timeo) { - err = -EAGAIN; - break; - } - - if (application->state == BT_CLOSED) { - err = -application->err; - break; - } - - if (application->state == BT_CONNECTED) - break; - - if (signal_pending(current)) { - err = -EINTR; - break; - } - - timeo = schedule_timeout(timeo); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&session->wait, &wait); - - if (err) { - cmtp_application_del(session, application); - return; - } -} - -static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl) -{ - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - - BT_DBG("ctrl %p appl %u", ctrl, appl); - - application = cmtp_application_get(session, CMTP_APPLID, appl); - if (!application) { - BT_ERR("Can't find application"); - return; - } - - application->msgnum = cmtp_msgnum_get(session); - - cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum, - CAPI_FUNCTION_RELEASE, NULL, 0); - - wait_event_interruptible_timeout(session->wait, - (application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT); - - cmtp_application_del(session, application); -} - -static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) -{ - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - __u16 appl; - __u32 contr; - - BT_DBG("ctrl %p skb %p", ctrl, skb); - - appl = CAPIMSG_APPID(skb->data); - contr = CAPIMSG_CONTROL(skb->data); - - application = cmtp_application_get(session, CMTP_APPLID, appl); - if ((!application) || (application->state != BT_CONNECTED)) { - BT_ERR("Can't find application with id %u", appl); - return CAPI_ILLAPPNR; - } - - CAPIMSG_SETAPPID(skb->data, application->mapping); - - if ((contr & 0x7f) == session->num) { - contr = (contr & 0xffffff80) | 0x01; - CAPIMSG_SETCONTROL(skb->data, contr); - } - - cmtp_send_capimsg(session, skb); - - return CAPI_NOERROR; -} - -static char *cmtp_procinfo(struct capi_ctr *ctrl) -{ - return "CAPI Message Transport Protocol"; -} - -static int cmtp_proc_show(struct seq_file *m, void *v) -{ - struct capi_ctr *ctrl = m->private; - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *app; - - seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl)); - seq_printf(m, "addr %s\n", session->name); - seq_printf(m, "ctrl %d\n", session->num); - - list_for_each_entry(app, &session->applications, list) { - seq_printf(m, "appl %u -> %u\n", app->appl, app->mapping); - } - - return 0; -} - -int cmtp_attach_device(struct cmtp_session *session) -{ - unsigned char buf[4]; - long ret; - - BT_DBG("session %p", session); - - capimsg_setu32(buf, 0, 0); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM, - CAPI_FUNCTION_GET_PROFILE, buf, 4); - - ret = wait_event_interruptible_timeout(session->wait, - session->ncontroller, CMTP_INTEROP_TIMEOUT); - - BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name); - - if (!ret) - return -ETIMEDOUT; - - if (!session->ncontroller) - return -ENODEV; - - if (session->ncontroller > 1) - BT_INFO("Setting up only CAPI controller 1"); - - session->ctrl.owner = THIS_MODULE; - session->ctrl.driverdata = session; - strcpy(session->ctrl.name, session->name); - - session->ctrl.driver_name = "cmtp"; - session->ctrl.load_firmware = cmtp_load_firmware; - session->ctrl.reset_ctr = cmtp_reset_ctr; - session->ctrl.register_appl = cmtp_register_appl; - session->ctrl.release_appl = cmtp_release_appl; - session->ctrl.send_message = cmtp_send_message; - - session->ctrl.procinfo = cmtp_procinfo; - session->ctrl.proc_show = cmtp_proc_show; - - if (attach_capi_ctr(&session->ctrl) < 0) { - BT_ERR("Can't attach new controller"); - return -EBUSY; - } - - session->num = session->ctrl.cnr; - - BT_DBG("session %p num %d", session, session->num); - - capimsg_setu32(buf, 0, 1); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_MANUFACTURER, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_VERSION, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_PROFILE, buf, 4); - - return 0; -} - -void cmtp_detach_device(struct cmtp_session *session) -{ - BT_DBG("session %p", session); - - detach_capi_ctr(&session->ctrl); -} diff --git a/net/bluetooth/cmtp/cmtp.h b/net/bluetooth/cmtp/cmtp.h deleted file mode 100644 index f6b9dc4e408f..000000000000 --- a/net/bluetooth/cmtp/cmtp.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#ifndef __CMTP_H -#define __CMTP_H - -#include -#include - -#define BTNAMSIZ 21 - -/* CMTP ioctl defines */ -#define CMTPCONNADD _IOW('C', 200, int) -#define CMTPCONNDEL _IOW('C', 201, int) -#define CMTPGETCONNLIST _IOR('C', 210, int) -#define CMTPGETCONNINFO _IOR('C', 211, int) - -#define CMTP_LOOPBACK 0 - -struct cmtp_connadd_req { - int sock; /* Connected socket */ - __u32 flags; -}; - -struct cmtp_conndel_req { - bdaddr_t bdaddr; - __u32 flags; -}; - -struct cmtp_conninfo { - bdaddr_t bdaddr; - __u32 flags; - __u16 state; - int num; -}; - -struct cmtp_connlist_req { - __u32 cnum; - struct cmtp_conninfo __user *ci; -}; - -int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock); -int cmtp_del_connection(struct cmtp_conndel_req *req); -int cmtp_get_connlist(struct cmtp_connlist_req *req); -int cmtp_get_conninfo(struct cmtp_conninfo *ci); - -/* CMTP session defines */ -#define CMTP_INTEROP_TIMEOUT (HZ * 5) -#define CMTP_INITIAL_MSGNUM 0xff00 - -struct cmtp_session { - struct list_head list; - - struct socket *sock; - - bdaddr_t bdaddr; - - unsigned long state; - unsigned long flags; - - uint mtu; - - char name[BTNAMSIZ]; - - atomic_t terminate; - struct task_struct *task; - - wait_queue_head_t wait; - - int ncontroller; - int num; - struct capi_ctr ctrl; - - struct list_head applications; - - unsigned long blockids; - int msgnum; - - struct sk_buff_head transmit; - - struct sk_buff *reassembly[16]; -}; - -struct cmtp_application { - struct list_head list; - - unsigned long state; - int err; - - __u16 appl; - __u16 mapping; - - __u16 msgnum; -}; - -struct cmtp_scb { - int id; - int data; -}; - -int cmtp_attach_device(struct cmtp_session *session); -void cmtp_detach_device(struct cmtp_session *session); - -void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb); - -/* CMTP init defines */ -int cmtp_init_sockets(void); -void cmtp_cleanup_sockets(void); - -#endif /* __CMTP_H */ diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c deleted file mode 100644 index 261aeeda3236..000000000000 --- a/net/bluetooth/cmtp/core.c +++ /dev/null @@ -1,519 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "cmtp.h" - -#define VERSION "1.0" - -static DECLARE_RWSEM(cmtp_session_sem); -static LIST_HEAD(cmtp_session_list); - -static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) -{ - struct cmtp_session *session; - - BT_DBG(""); - - list_for_each_entry(session, &cmtp_session_list, list) - if (!bacmp(bdaddr, &session->bdaddr)) - return session; - - return NULL; -} - -static void __cmtp_link_session(struct cmtp_session *session) -{ - list_add(&session->list, &cmtp_session_list); -} - -static void __cmtp_unlink_session(struct cmtp_session *session) -{ - list_del(&session->list); -} - -static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci) -{ - u32 valid_flags = BIT(CMTP_LOOPBACK); - memset(ci, 0, sizeof(*ci)); - bacpy(&ci->bdaddr, &session->bdaddr); - - ci->flags = session->flags & valid_flags; - ci->state = session->state; - - ci->num = session->num; -} - - -static inline int cmtp_alloc_block_id(struct cmtp_session *session) -{ - int i, id = -1; - - for (i = 0; i < 16; i++) - if (!test_and_set_bit(i, &session->blockids)) { - id = i; - break; - } - - return id; -} - -static inline void cmtp_free_block_id(struct cmtp_session *session, int id) -{ - clear_bit(id, &session->blockids); -} - -static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count) -{ - struct sk_buff *skb = session->reassembly[id], *nskb; - int size; - - BT_DBG("session %p buf %p count %d", session, buf, count); - - size = (skb) ? skb->len + count : count; - - nskb = alloc_skb(size, GFP_ATOMIC); - if (!nskb) { - BT_ERR("Can't allocate memory for CAPI message"); - return; - } - - if (skb && (skb->len > 0)) - skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len); - - skb_put_data(nskb, buf, count); - - session->reassembly[id] = nskb; - - kfree_skb(skb); -} - -static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb) -{ - __u8 hdr, hdrlen, id; - __u16 len; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - while (skb->len > 0) { - hdr = skb->data[0]; - - switch (hdr & 0xc0) { - case 0x40: - hdrlen = 2; - len = skb->data[1]; - break; - case 0x80: - hdrlen = 3; - len = skb->data[1] | (skb->data[2] << 8); - break; - default: - hdrlen = 1; - len = 0; - break; - } - - id = (hdr & 0x3c) >> 2; - - BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id); - - if (hdrlen + len > skb->len) { - BT_ERR("Wrong size or header information in CMTP frame"); - break; - } - - if (len == 0) { - skb_pull(skb, hdrlen); - continue; - } - - switch (hdr & 0x03) { - case 0x00: - cmtp_add_msgpart(session, id, skb->data + hdrlen, len); - cmtp_recv_capimsg(session, session->reassembly[id]); - session->reassembly[id] = NULL; - break; - case 0x01: - cmtp_add_msgpart(session, id, skb->data + hdrlen, len); - break; - default: - kfree_skb(session->reassembly[id]); - session->reassembly[id] = NULL; - break; - } - - skb_pull(skb, hdrlen + len); - } - - kfree_skb(skb); - return 0; -} - -static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len) -{ - struct socket *sock = session->sock; - struct kvec iv = { data, len }; - struct msghdr msg; - - BT_DBG("session %p data %p len %d", session, data, len); - - if (!len) - return 0; - - memset(&msg, 0, sizeof(msg)); - - return kernel_sendmsg(sock, &msg, &iv, 1, len); -} - -static void cmtp_process_transmit(struct cmtp_session *session) -{ - struct sk_buff *skb, *nskb; - unsigned char *hdr; - unsigned int size, tail; - - BT_DBG("session %p", session); - - nskb = alloc_skb(session->mtu, GFP_ATOMIC); - if (!nskb) { - BT_ERR("Can't allocate memory for new frame"); - return; - } - - while ((skb = skb_dequeue(&session->transmit))) { - struct cmtp_scb *scb = (void *) skb->cb; - - tail = session->mtu - nskb->len; - if (tail < 5) { - cmtp_send_frame(session, nskb->data, nskb->len); - skb_trim(nskb, 0); - tail = session->mtu; - } - - size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len); - - if (scb->id < 0) { - scb->id = cmtp_alloc_block_id(session); - if (scb->id < 0) { - skb_queue_head(&session->transmit, skb); - break; - } - } - - if (size < 256) { - hdr = skb_put(nskb, 2); - hdr[0] = 0x40 - | ((scb->id << 2) & 0x3c) - | ((skb->len == size) ? 0x00 : 0x01); - hdr[1] = size; - } else { - hdr = skb_put(nskb, 3); - hdr[0] = 0x80 - | ((scb->id << 2) & 0x3c) - | ((skb->len == size) ? 0x00 : 0x01); - hdr[1] = size & 0xff; - hdr[2] = size >> 8; - } - - skb_copy_from_linear_data(skb, skb_put(nskb, size), size); - skb_pull(skb, size); - - if (skb->len > 0) { - skb_queue_head(&session->transmit, skb); - } else { - cmtp_free_block_id(session, scb->id); - if (scb->data) { - cmtp_send_frame(session, nskb->data, nskb->len); - skb_trim(nskb, 0); - } - kfree_skb(skb); - } - } - - cmtp_send_frame(session, nskb->data, nskb->len); - - kfree_skb(nskb); -} - -static int cmtp_session(void *arg) -{ - struct cmtp_session *session = arg; - struct sock *sk = session->sock->sk; - struct sk_buff *skb; - DEFINE_WAIT_FUNC(wait, woken_wake_function); - - BT_DBG("session %p", session); - - set_user_nice(current, -15); - - add_wait_queue(sk_sleep(sk), &wait); - while (1) { - if (atomic_read(&session->terminate)) - break; - if (sk->sk_state != BT_CONNECTED) - break; - - while ((skb = skb_dequeue(&sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - cmtp_recv_frame(session, skb); - else - kfree_skb(skb); - } - - cmtp_process_transmit(session); - - /* - * wait_woken() performs the necessary memory barriers - * for us; see the header comment for this primitive. - */ - wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); - } - remove_wait_queue(sk_sleep(sk), &wait); - - down_write(&cmtp_session_sem); - - if (!(session->flags & BIT(CMTP_LOOPBACK))) - cmtp_detach_device(session); - - fput(session->sock->file); - - __cmtp_unlink_session(session); - - up_write(&cmtp_session_sem); - - kfree(session); - module_put_and_kthread_exit(0); - return 0; -} - -int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) -{ - u32 valid_flags = BIT(CMTP_LOOPBACK); - struct cmtp_session *session, *s; - int i, err; - - BT_DBG(""); - - if (!l2cap_is_socket(sock)) - return -EBADFD; - - if (req->flags & ~valid_flags) - return -EINVAL; - - session = kzalloc_obj(struct cmtp_session); - if (!session) - return -ENOMEM; - - down_write(&cmtp_session_sem); - - s = __cmtp_get_session(&l2cap_pi(sock->sk)->chan->dst); - if (s && s->state == BT_CONNECTED) { - err = -EEXIST; - goto failed; - } - - bacpy(&session->bdaddr, &l2cap_pi(sock->sk)->chan->dst); - - session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu, - l2cap_pi(sock->sk)->chan->imtu); - - BT_DBG("mtu %d", session->mtu); - - sprintf(session->name, "%pMR", &session->bdaddr); - - session->sock = sock; - session->state = BT_CONFIG; - - init_waitqueue_head(&session->wait); - - session->msgnum = CMTP_INITIAL_MSGNUM; - - INIT_LIST_HEAD(&session->applications); - - skb_queue_head_init(&session->transmit); - - for (i = 0; i < 16; i++) - session->reassembly[i] = NULL; - - session->flags = req->flags; - - __cmtp_link_session(session); - - __module_get(THIS_MODULE); - session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d", - session->num); - if (IS_ERR(session->task)) { - module_put(THIS_MODULE); - err = PTR_ERR(session->task); - goto unlink; - } - - if (!(session->flags & BIT(CMTP_LOOPBACK))) { - err = cmtp_attach_device(session); - if (err < 0) { - /* Caller will call fput in case of failure, and so - * will cmtp_session kthread. - */ - get_file(session->sock->file); - - atomic_inc(&session->terminate); - wake_up_interruptible(sk_sleep(session->sock->sk)); - up_write(&cmtp_session_sem); - return err; - } - } - - up_write(&cmtp_session_sem); - return 0; - -unlink: - __cmtp_unlink_session(session); - -failed: - up_write(&cmtp_session_sem); - kfree(session); - return err; -} - -int cmtp_del_connection(struct cmtp_conndel_req *req) -{ - u32 valid_flags = 0; - struct cmtp_session *session; - int err = 0; - - BT_DBG(""); - - if (req->flags & ~valid_flags) - return -EINVAL; - - down_read(&cmtp_session_sem); - - session = __cmtp_get_session(&req->bdaddr); - if (session) { - /* Flush the transmit queue */ - skb_queue_purge(&session->transmit); - - /* Stop session thread */ - atomic_inc(&session->terminate); - - /* - * See the comment preceding the call to wait_woken() - * in cmtp_session(). - */ - wake_up_interruptible(sk_sleep(session->sock->sk)); - } else - err = -ENOENT; - - up_read(&cmtp_session_sem); - return err; -} - -int cmtp_get_connlist(struct cmtp_connlist_req *req) -{ - struct cmtp_session *session; - int err = 0, n = 0; - - BT_DBG(""); - - down_read(&cmtp_session_sem); - - list_for_each_entry(session, &cmtp_session_list, list) { - struct cmtp_conninfo ci; - - __cmtp_copy_session(session, &ci); - - if (copy_to_user(req->ci, &ci, sizeof(ci))) { - err = -EFAULT; - break; - } - - if (++n >= req->cnum) - break; - - req->ci++; - } - req->cnum = n; - - up_read(&cmtp_session_sem); - return err; -} - -int cmtp_get_conninfo(struct cmtp_conninfo *ci) -{ - struct cmtp_session *session; - int err = 0; - - down_read(&cmtp_session_sem); - - session = __cmtp_get_session(&ci->bdaddr); - if (session) - __cmtp_copy_session(session, ci); - else - err = -ENOENT; - - up_read(&cmtp_session_sem); - return err; -} - - -static int __init cmtp_init(void) -{ - BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION); - - return cmtp_init_sockets(); -} - -static void __exit cmtp_exit(void) -{ - cmtp_cleanup_sockets(); -} - -module_init(cmtp_init); -module_exit(cmtp_exit); - -MODULE_AUTHOR("Marcel Holtmann "); -MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-5"); diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c deleted file mode 100644 index 96d49d9fae96..000000000000 --- a/net/bluetooth/cmtp/sock.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -#include "cmtp.h" - -static struct bt_sock_list cmtp_sk_list = { - .lock = __RW_LOCK_UNLOCKED(cmtp_sk_list.lock) -}; - -static int cmtp_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!sk) - return 0; - - bt_sock_unlink(&cmtp_sk_list, sk); - - sock_orphan(sk); - sock_put(sk); - - return 0; -} - -static int do_cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, void __user *argp) -{ - struct cmtp_connadd_req ca; - struct cmtp_conndel_req cd; - struct cmtp_connlist_req cl; - struct cmtp_conninfo ci; - struct socket *nsock; - int err; - - BT_DBG("cmd %x arg %p", cmd, argp); - - switch (cmd) { - case CMTPCONNADD: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (copy_from_user(&ca, argp, sizeof(ca))) - return -EFAULT; - - nsock = sockfd_lookup(ca.sock, &err); - if (!nsock) - return err; - - if (nsock->sk->sk_state != BT_CONNECTED) { - sockfd_put(nsock); - return -EBADFD; - } - - err = cmtp_add_connection(&ca, nsock); - if (!err) { - if (copy_to_user(argp, &ca, sizeof(ca))) - err = -EFAULT; - } else - sockfd_put(nsock); - - return err; - - case CMTPCONNDEL: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (copy_from_user(&cd, argp, sizeof(cd))) - return -EFAULT; - - return cmtp_del_connection(&cd); - - case CMTPGETCONNLIST: - if (copy_from_user(&cl, argp, sizeof(cl))) - return -EFAULT; - - if (cl.cnum <= 0) - return -EINVAL; - - err = cmtp_get_connlist(&cl); - if (!err && copy_to_user(argp, &cl, sizeof(cl))) - return -EFAULT; - - return err; - - case CMTPGETCONNINFO: - if (copy_from_user(&ci, argp, sizeof(ci))) - return -EFAULT; - - err = cmtp_get_conninfo(&ci); - if (!err && copy_to_user(argp, &ci, sizeof(ci))) - return -EFAULT; - - return err; - } - - return -EINVAL; -} - -static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - return do_cmtp_sock_ioctl(sock, cmd, (void __user *)arg); -} - -#ifdef CONFIG_COMPAT -static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - void __user *argp = compat_ptr(arg); - if (cmd == CMTPGETCONNLIST) { - struct cmtp_connlist_req cl; - u32 __user *p = argp; - u32 uci; - int err; - - if (get_user(cl.cnum, p) || get_user(uci, p + 1)) - return -EFAULT; - - cl.ci = compat_ptr(uci); - - if (cl.cnum <= 0) - return -EINVAL; - - err = cmtp_get_connlist(&cl); - - if (!err && put_user(cl.cnum, p)) - err = -EFAULT; - - return err; - } - - return do_cmtp_sock_ioctl(sock, cmd, argp); -} -#endif - -static const struct proto_ops cmtp_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = cmtp_sock_release, - .ioctl = cmtp_sock_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = cmtp_sock_compat_ioctl, -#endif - .bind = sock_no_bind, - .getname = sock_no_getname, - .sendmsg = sock_no_sendmsg, - .recvmsg = sock_no_recvmsg, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static struct proto cmtp_proto = { - .name = "CMTP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct bt_sock) -}; - -static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &cmtp_proto, kern); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock->ops = &cmtp_sock_ops; - - sock->state = SS_UNCONNECTED; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - sk->sk_state = BT_OPEN; - - bt_sock_link(&cmtp_sk_list, sk); - - return 0; -} - -static const struct net_proto_family cmtp_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = cmtp_sock_create -}; - -int cmtp_init_sockets(void) -{ - int err; - - err = proto_register(&cmtp_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops); - if (err < 0) { - BT_ERR("Can't register CMTP socket"); - goto error; - } - - err = bt_procfs_init(&init_net, "cmtp", &cmtp_sk_list, NULL); - if (err < 0) { - BT_ERR("Failed to create CMTP proc file"); - bt_sock_unregister(BTPROTO_HIDP); - goto error; - } - - BT_INFO("CMTP socket layer initialized"); - - return 0; - -error: - proto_unregister(&cmtp_proto); - return err; -} - -void cmtp_cleanup_sockets(void) -{ - bt_procfs_cleanup(&init_net, "cmtp"); - bt_sock_unregister(BTPROTO_CMTP); - proto_unregister(&cmtp_proto); -} -- cgit v1.2.3 From dd8d4bc28ad7252610d8e79c1313a2d1e3499a51 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 20 Apr 2026 19:18:23 -0700 Subject: net: remove ax25 and amateur radio (hamradio) subsystem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the amateur radio (AX.25, NET/ROM, ROSE) protocol implementation and all associated hamradio device drivers from the kernel tree. This set of protocols has long been a huge bug/syzbot magnet, and since nobody stepped up to help us deal with the influx of the AI-generated bug reports we need to move it out of tree to protect our sanity. The code is moved to an out-of-tree repo: https://github.com/linux-netdev/mod-orphan if it's cleaned up and reworked there we can accept it back. Minimal stub headers are kept for include/net/ax25.h (AX25_P_IP, AX25_ADDR_LEN, ax25_address) and include/net/rose.h (ROSE_ADDR_LEN) so that the conditional integration code in arp.c and tun.c continues to compile and work when the out-of-tree modules are loaded. Signed-off-by: Jakub Kicinski Acked-by: Greg Kroah-Hartman Acked-by: Stephen Hemminger Acked-by: Carlos Bilbao Reviewed-by: Simon Horman Reviewed-by: Kuniyuki Iwashima Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260421021824.1293976-1-kuba@kernel.org Signed-off-by: Paolo Abeni --- Documentation/.renames.txt | 2 - Documentation/admin-guide/kernel-parameters.txt | 18 - Documentation/networking/6pack.rst | 191 -- Documentation/networking/ax25.rst | 17 - .../networking/device_drivers/hamradio/baycom.rst | 174 -- .../networking/device_drivers/hamradio/index.rst | 12 - .../device_drivers/hamradio/z8530drv.rst | 686 ------ Documentation/networking/device_drivers/index.rst | 1 - Documentation/networking/index.rst | 2 - Documentation/staging/magic-number.rst | 3 - .../translations/it_IT/staging/magic-number.rst | 3 - .../translations/sp_SP/process/magic-number.rst | 3 - .../translations/zh_CN/process/magic-number.rst | 3 - .../translations/zh_TW/process/magic-number.rst | 3 - MAINTAINERS | 73 - arch/mips/configs/bcm47xx_defconfig | 1 - arch/mips/configs/bigsur_defconfig | 10 - arch/mips/configs/gpr_defconfig | 11 - arch/mips/configs/mtx1_defconfig | 11 - arch/mips/configs/rb532_defconfig | 1 - arch/mips/configs/rm200_defconfig | 7 - arch/mips/configs/rt305x_defconfig | 1 - arch/mips/configs/xway_defconfig | 1 - drivers/net/Makefile | 1 - drivers/net/hamradio/6pack.c | 912 -------- drivers/net/hamradio/Kconfig | 162 -- drivers/net/hamradio/Makefile | 22 - drivers/net/hamradio/baycom_epp.c | 1316 ------------ drivers/net/hamradio/baycom_par.c | 598 ------ drivers/net/hamradio/baycom_ser_fdx.c | 678 ------ drivers/net/hamradio/baycom_ser_hdx.c | 727 ------- drivers/net/hamradio/bpqether.c | 593 ------ drivers/net/hamradio/hdlcdrv.c | 747 ------- drivers/net/hamradio/mkiss.c | 980 --------- drivers/net/hamradio/scc.c | 2179 -------------------- drivers/net/hamradio/yam.c | 1191 ----------- drivers/net/hamradio/z8530.h | 246 --- include/linux/hdlcdrv.h | 276 --- include/linux/netdevice.h | 5 +- include/linux/scc.h | 86 - include/linux/yam.h | 67 - include/net/ax25.h | 476 +---- include/net/netrom.h | 273 --- include/net/rose.h | 263 +-- include/uapi/linux/baycom.h | 40 - include/uapi/linux/hdlcdrv.h | 111 - include/uapi/linux/netrom.h | 37 - include/uapi/linux/rose.h | 91 - include/uapi/linux/scc.h | 174 -- net/Kconfig | 1 - net/Makefile | 3 - net/ax25/Kconfig | 108 - net/ax25/Makefile | 12 - net/ax25/af_ax25.c | 2089 ------------------- net/ax25/ax25_addr.c | 303 --- net/ax25/ax25_dev.c | 200 -- net/ax25/ax25_ds_in.c | 298 --- net/ax25/ax25_ds_subr.c | 204 -- net/ax25/ax25_ds_timer.c | 235 --- net/ax25/ax25_iface.c | 214 -- net/ax25/ax25_in.c | 455 ---- net/ax25/ax25_ip.c | 247 --- net/ax25/ax25_out.c | 398 ---- net/ax25/ax25_route.c | 416 ---- net/ax25/ax25_std_in.c | 443 ---- net/ax25/ax25_std_subr.c | 83 - net/ax25/ax25_std_timer.c | 175 -- net/ax25/ax25_subr.c | 296 --- net/ax25/ax25_timer.c | 224 -- net/ax25/ax25_uid.c | 204 -- net/ax25/sysctl_net_ax25.c | 181 -- net/ipv4/arp.c | 1 - net/netrom/Makefile | 10 - net/netrom/af_netrom.c | 1536 -------------- net/netrom/nr_dev.c | 178 -- net/netrom/nr_in.c | 301 --- net/netrom/nr_loopback.c | 73 - net/netrom/nr_out.c | 272 --- net/netrom/nr_route.c | 989 --------- net/netrom/nr_subr.c | 280 --- net/netrom/nr_timer.c | 249 --- net/netrom/sysctl_net_netrom.c | 156 -- net/rose/Makefile | 10 - net/rose/af_rose.c | 1687 --------------- net/rose/rose_dev.c | 141 -- net/rose/rose_in.c | 301 --- net/rose/rose_link.c | 289 --- net/rose/rose_loopback.c | 133 -- net/rose/rose_out.c | 122 -- net/rose/rose_route.c | 1333 ------------ net/rose/rose_subr.c | 556 ----- net/rose/rose_timer.c | 227 -- net/rose/sysctl_net_rose.c | 125 -- 93 files changed, 6 insertions(+), 29237 deletions(-) delete mode 100644 Documentation/networking/6pack.rst delete mode 100644 Documentation/networking/ax25.rst delete mode 100644 Documentation/networking/device_drivers/hamradio/baycom.rst delete mode 100644 Documentation/networking/device_drivers/hamradio/index.rst delete mode 100644 Documentation/networking/device_drivers/hamradio/z8530drv.rst delete mode 100644 drivers/net/hamradio/6pack.c delete mode 100644 drivers/net/hamradio/Kconfig delete mode 100644 drivers/net/hamradio/Makefile delete mode 100644 drivers/net/hamradio/baycom_epp.c delete mode 100644 drivers/net/hamradio/baycom_par.c delete mode 100644 drivers/net/hamradio/baycom_ser_fdx.c delete mode 100644 drivers/net/hamradio/baycom_ser_hdx.c delete mode 100644 drivers/net/hamradio/bpqether.c delete mode 100644 drivers/net/hamradio/hdlcdrv.c delete mode 100644 drivers/net/hamradio/mkiss.c delete mode 100644 drivers/net/hamradio/scc.c delete mode 100644 drivers/net/hamradio/yam.c delete mode 100644 drivers/net/hamradio/z8530.h delete mode 100644 include/linux/hdlcdrv.h delete mode 100644 include/linux/scc.h delete mode 100644 include/linux/yam.h delete mode 100644 include/net/netrom.h delete mode 100644 include/uapi/linux/baycom.h delete mode 100644 include/uapi/linux/hdlcdrv.h delete mode 100644 include/uapi/linux/netrom.h delete mode 100644 include/uapi/linux/rose.h delete mode 100644 include/uapi/linux/scc.h delete mode 100644 net/ax25/Kconfig delete mode 100644 net/ax25/Makefile delete mode 100644 net/ax25/af_ax25.c delete mode 100644 net/ax25/ax25_addr.c delete mode 100644 net/ax25/ax25_dev.c delete mode 100644 net/ax25/ax25_ds_in.c delete mode 100644 net/ax25/ax25_ds_subr.c delete mode 100644 net/ax25/ax25_ds_timer.c delete mode 100644 net/ax25/ax25_iface.c delete mode 100644 net/ax25/ax25_in.c delete mode 100644 net/ax25/ax25_ip.c delete mode 100644 net/ax25/ax25_out.c delete mode 100644 net/ax25/ax25_route.c delete mode 100644 net/ax25/ax25_std_in.c delete mode 100644 net/ax25/ax25_std_subr.c delete mode 100644 net/ax25/ax25_std_timer.c delete mode 100644 net/ax25/ax25_subr.c delete mode 100644 net/ax25/ax25_timer.c delete mode 100644 net/ax25/ax25_uid.c delete mode 100644 net/ax25/sysctl_net_ax25.c delete mode 100644 net/netrom/Makefile delete mode 100644 net/netrom/af_netrom.c delete mode 100644 net/netrom/nr_dev.c delete mode 100644 net/netrom/nr_in.c delete mode 100644 net/netrom/nr_loopback.c delete mode 100644 net/netrom/nr_out.c delete mode 100644 net/netrom/nr_route.c delete mode 100644 net/netrom/nr_subr.c delete mode 100644 net/netrom/nr_timer.c delete mode 100644 net/netrom/sysctl_net_netrom.c delete mode 100644 net/rose/Makefile delete mode 100644 net/rose/af_rose.c delete mode 100644 net/rose/rose_dev.c delete mode 100644 net/rose/rose_in.c delete mode 100644 net/rose/rose_link.c delete mode 100644 net/rose/rose_loopback.c delete mode 100644 net/rose/rose_out.c delete mode 100644 net/rose/rose_route.c delete mode 100644 net/rose/rose_subr.c delete mode 100644 net/rose/rose_timer.c delete mode 100644 net/rose/sysctl_net_rose.c (limited to 'include/uapi/linux') diff --git a/Documentation/.renames.txt b/Documentation/.renames.txt index a37d68471d50..e5f2f7447914 100644 --- a/Documentation/.renames.txt +++ b/Documentation/.renames.txt @@ -783,7 +783,6 @@ namespaces/compatibility-list admin-guide/namespaces/compatibility-list namespaces/index admin-guide/namespaces/index namespaces/resource-control admin-guide/namespaces/resource-control networking/altera_tse networking/device_drivers/ethernet/altera/altera_tse -networking/baycom networking/device_drivers/hamradio/baycom networking/bpf_flow_dissector bpf/prog_flow_dissector networking/cxacru networking/device_drivers/atm/cxacru networking/defza networking/device_drivers/fddi/defza @@ -848,7 +847,6 @@ networking/ixgbe networking/device_drivers/ethernet/intel/ixgbe networking/ixgbevf networking/device_drivers/ethernet/intel/ixgbevf networking/netdev-FAQ process/maintainer-netdev networking/skfp networking/device_drivers/fddi/skfp -networking/z8530drv networking/device_drivers/hamradio/z8530drv nfc/index driver-api/nfc/index nfc/nfc-hci driver-api/nfc/nfc-hci nfc/nfc-pn544 driver-api/nfc/nfc-pn544 diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index f2ce1f4975c1..09354ff7cff2 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6,7 +6,6 @@ APPARMOR AppArmor support is enabled. ARM ARM architecture is enabled. ARM64 ARM64 architecture is enabled. - AX25 Appropriate AX.25 support is enabled. CLK Common clock infrastructure is enabled. CMA Contiguous Memory Area support is enabled. DRM Direct Rendering Management support is enabled. @@ -633,23 +632,6 @@ Kernel parameters 1 - Enable the BAU. unset - Disable the BAU. - baycom_epp= [HW,AX25] - Format: , - - baycom_par= [HW,AX25] BayCom Parallel Port AX.25 Modem - Format: , - See header of drivers/net/hamradio/baycom_par.c. - - baycom_ser_fdx= [HW,AX25] - BayCom Serial Port AX.25 Modem (Full Duplex Mode) - Format: ,,[,] - See header of drivers/net/hamradio/baycom_ser_fdx.c. - - baycom_ser_hdx= [HW,AX25] - BayCom Serial Port AX.25 Modem (Half Duplex Mode) - Format: ,, - See header of drivers/net/hamradio/baycom_ser_hdx.c. - bdev_allow_write_mounted= Format: Control the ability to open a mounted block device diff --git a/Documentation/networking/6pack.rst b/Documentation/networking/6pack.rst deleted file mode 100644 index 66d5fd4fc821..000000000000 --- a/Documentation/networking/6pack.rst +++ /dev/null @@ -1,191 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -============== -6pack Protocol -============== - -This is the 6pack-mini-HOWTO, written by - -Andreas Könsgen DG3KQ - -:Internet: ajk@comnets.uni-bremen.de -:AMPR-net: dg3kq@db0pra.ampr.org -:AX.25: dg3kq@db0ach.#nrw.deu.eu - -Last update: April 7, 1998 - -1. What is 6pack, and what are the advantages to KISS? -====================================================== - -6pack is a transmission protocol for data exchange between the PC and -the TNC over a serial line. It can be used as an alternative to KISS. - -6pack has two major advantages: - -- The PC is given full control over the radio - channel. Special control data is exchanged between the PC and the TNC so - that the PC knows at any time if the TNC is receiving data, if a TNC - buffer underrun or overrun has occurred, if the PTT is - set and so on. This control data is processed at a higher priority than - normal data, so a data stream can be interrupted at any time to issue an - important event. This helps to improve the channel access and timing - algorithms as everything is computed in the PC. It would even be possible - to experiment with something completely different from the known CSMA and - DAMA channel access methods. - This kind of real-time control is especially important to supply several - TNCs that are connected between each other and the PC by a daisy chain - (however, this feature is not supported yet by the Linux 6pack driver). - -- Each packet transferred over the serial line is supplied with a checksum, - so it is easy to detect errors due to problems on the serial line. - Received packets that are corrupt are not passed on to the AX.25 layer. - Damaged packets that the TNC has received from the PC are not transmitted. - -More details about 6pack are described in the file 6pack.ps that is located -in the doc directory of the AX.25 utilities package. - -2. Who has developed the 6pack protocol? -======================================== - -The 6pack protocol has been developed by Ekki Plicht DF4OR, Henning Rech -DF9IC and Gunter Jost DK7WJ. A driver for 6pack, written by Gunter Jost and -Matthias Welwarsky DG2FEF, comes along with the PC version of FlexNet. -They have also written a firmware for TNCs to perform the 6pack -protocol (see section 4 below). - -3. Where can I get the latest version of 6pack for LinuX? -========================================================= - -At the moment, the 6pack stuff can obtained via anonymous ftp from -db0bm.automation.fh-aachen.de. In the directory /incoming/dg3kq, -there is a file named 6pack.tgz. - -4. Preparing the TNC for 6pack operation -======================================== - -To be able to use 6pack, a special firmware for the TNC is needed. The EPROM -of a newly bought TNC does not contain 6pack, so you will have to -program an EPROM yourself. The image file for 6pack EPROMs should be -available on any packet radio box where PC/FlexNet can be found. The name of -the file is 6pack.bin. This file is copyrighted and maintained by the FlexNet -team. It can be used under the terms of the license that comes along -with PC/FlexNet. Please do not ask me about the internals of this file as I -don't know anything about it. I used a textual description of the 6pack -protocol to program the Linux driver. - -TNCs contain a 64kByte EPROM, the lower half of which is used for -the firmware/KISS. The upper half is either empty or is sometimes -programmed with software called TAPR. In the latter case, the TNC -is supplied with a DIP switch so you can easily change between the -two systems. When programming a new EPROM, one of the systems is replaced -by 6pack. It is useful to replace TAPR, as this software is rarely used -nowadays. If your TNC is not equipped with the switch mentioned above, you -can build in one yourself that switches over the highest address pin -of the EPROM between HIGH and LOW level. After having inserted the new EPROM -and switched to 6pack, apply power to the TNC for a first test. The connect -and the status LED are lit for about a second if the firmware initialises -the TNC correctly. - -5. Building and installing the 6pack driver -=========================================== - -The driver has been tested with kernel version 2.1.90. Use with older -kernels may lead to a compilation error because the interface to a kernel -function has been changed in the 2.1.8x kernels. - -How to turn on 6pack support: ------------------------------ - -- In the linux kernel configuration program, select the code maturity level - options menu and turn on the prompting for development drivers. - -- Select the amateur radio support menu and turn on the serial port 6pack - driver. - -- Compile and install the kernel and the modules. - -To use the driver, the kissattach program delivered with the AX.25 utilities -has to be modified. - -- Do a cd to the directory that holds the kissattach sources. Edit the - kissattach.c file. At the top, insert the following lines:: - - #ifndef N_6PACK - #define N_6PACK (N_AX25+1) - #endif - - Then find the line: - - int disc = N_AX25; - - and replace N_AX25 by N_6PACK. - -- Recompile kissattach. Rename it to spattach to avoid confusions. - -Installing the driver: ----------------------- - -- Do an insmod 6pack. Look at your /var/log/messages file to check if the - module has printed its initialization message. - -- Do a spattach as you would launch kissattach when starting a KISS port. - Check if the kernel prints the message '6pack: TNC found'. - -- From here, everything should work as if you were setting up a KISS port. - The only difference is that the network device that represents - the 6pack port is called sp instead of sl or ax. So, sp0 would be the - first 6pack port. - -Although the driver has been tested on various platforms, I still declare it -ALPHA. BE CAREFUL! Sync your disks before insmoding the 6pack module -and spattaching. Watch out if your computer behaves strangely. Read section -6 of this file about known problems. - -Note that the connect and status LEDs of the TNC are controlled in a -different way than they are when the TNC is used with PC/FlexNet. When using -FlexNet, the connect LED is on if there is a connection; the status LED is -on if there is data in the buffer of the PC's AX.25 engine that has to be -transmitted. Under Linux, the 6pack layer is beyond the AX.25 layer, -so the 6pack driver doesn't know anything about connects or data that -has not yet been transmitted. Therefore the LEDs are controlled -as they are in KISS mode: The connect LED is turned on if data is transferred -from the PC to the TNC over the serial line, the status LED if data is -sent to the PC. - -6. Known problems -================= - -When testing the driver with 2.0.3x kernels and -operating with data rates on the radio channel of 9600 Baud or higher, -the driver may, on certain systems, sometimes print the message '6pack: -bad checksum', which is due to data loss if the other station sends two -or more subsequent packets. I have been told that this is due to a problem -with the serial driver of 2.0.3x kernels. I don't know yet if the problem -still exists with 2.1.x kernels, as I have heard that the serial driver -code has been changed with 2.1.x. - -When shutting down the sp interface with ifconfig, the kernel crashes if -there is still an AX.25 connection left over which an IP connection was -running, even if that IP connection is already closed. The problem does not -occur when there is a bare AX.25 connection still running. I don't know if -this is a problem of the 6pack driver or something else in the kernel. - -The driver has been tested as a module, not yet as a kernel-builtin driver. - -The 6pack protocol supports daisy-chaining of TNCs in a token ring, which is -connected to one serial port of the PC. This feature is not implemented -and at least at the moment I won't be able to do it because I do not have -the opportunity to build a TNC daisy-chain and test it. - -Some of the comments in the source code are inaccurate. They are left from -the SLIP/KISS driver, from which the 6pack driver has been derived. -I haven't modified or removed them yet -- sorry! The code itself needs -some cleaning and optimizing. This will be done in a later release. - -If you encounter a bug or if you have a question or suggestion concerning the -driver, feel free to mail me, using the addresses given at the beginning of -this file. - -Have fun! - -Andreas diff --git a/Documentation/networking/ax25.rst b/Documentation/networking/ax25.rst deleted file mode 100644 index 89c79dd6c6f9..000000000000 --- a/Documentation/networking/ax25.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -===== -AX.25 -===== - -To use the amateur radio protocols within Linux you will need to get a -suitable copy of the AX.25 Utilities. More detailed information about -AX.25, NET/ROM and ROSE, associated programs and utilities can be -found on https://linux-ax25.in-berlin.de. - -There is a mailing list for discussing Linux amateur radio matters -called linux-hams@vger.kernel.org. To subscribe to it, send a message to -linux-hams+subscribe@vger.kernel.org or use the web interface at -https://vger.kernel.org. The subject and body of the message are -ignored. You don't need to be subscribed to post but of course that -means you might miss an answer. diff --git a/Documentation/networking/device_drivers/hamradio/baycom.rst b/Documentation/networking/device_drivers/hamradio/baycom.rst deleted file mode 100644 index fe2d010f0e86..000000000000 --- a/Documentation/networking/device_drivers/hamradio/baycom.rst +++ /dev/null @@ -1,174 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -=============================== -Linux Drivers for Baycom Modems -=============================== - -Thomas M. Sailer, HB9JNX/AE4WA, - -The drivers for the baycom modems have been split into -separate drivers as they did not share any code, and the driver -and device names have changed. - -This document describes the Linux Kernel Drivers for simple Baycom style -amateur radio modems. - -The following drivers are available: -==================================== - -baycom_ser_fdx: - This driver supports the SER12 modems either full or half duplex. - Its baud rate may be changed via the ``baud`` module parameter, - therefore it supports just about every bit bang modem on a - serial port. Its devices are called bcsf0 through bcsf3. - This is the recommended driver for SER12 type modems, - however if you have a broken UART clone that does not have working - delta status bits, you may try baycom_ser_hdx. - -baycom_ser_hdx: - This is an alternative driver for SER12 type modems. - It only supports half duplex, and only 1200 baud. Its devices - are called bcsh0 through bcsh3. Use this driver only if baycom_ser_fdx - does not work with your UART. - -baycom_par: - This driver supports the par96 and picpar modems. - Its devices are called bcp0 through bcp3. - -baycom_epp: - This driver supports the EPP modem. - Its devices are called bce0 through bce3. - This driver is work-in-progress. - -The following modems are supported: - -======= ======================================================================== -ser12 This is a very simple 1200 baud AFSK modem. The modem consists only - of a modulator/demodulator chip, usually a TI TCM3105. The computer - is responsible for regenerating the receiver bit clock, as well as - for handling the HDLC protocol. The modem connects to a serial port, - hence the name. Since the serial port is not used as an async serial - port, the kernel driver for serial ports cannot be used, and this - driver only supports standard serial hardware (8250, 16450, 16550) - -par96 This is a modem for 9600 baud FSK compatible to the G3RUH standard. - The modem does all the filtering and regenerates the receiver clock. - Data is transferred from and to the PC via a shift register. - The shift register is filled with 16 bits and an interrupt is signalled. - The PC then empties the shift register in a burst. This modem connects - to the parallel port, hence the name. The modem leaves the - implementation of the HDLC protocol and the scrambler polynomial to - the PC. - -picpar This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem - is protocol compatible to par96, but uses only three low power ICs - and can therefore be fed from the parallel port and does not require - an additional power supply. Furthermore, it incorporates a carrier - detect circuitry. - -EPP This is a high-speed modem adaptor that connects to an enhanced parallel - port. - - Its target audience is users working over a high speed hub (76.8kbit/s). - -eppfpga This is a redesign of the EPP adaptor. -======= ======================================================================== - -All of the above modems only support half duplex communications. However, -the driver supports the KISS (see below) fullduplex command. It then simply -starts to send as soon as there's a packet to transmit and does not care -about DCD, i.e. it starts to send even if there's someone else on the channel. -This command is required by some implementations of the DAMA channel -access protocol. - - -The Interface of the drivers -============================ - -Unlike previous drivers, these drivers are no longer character devices, -but they are now true kernel network interfaces. Installation is therefore -simple. Once installed, four interfaces named bc{sf,sh,p,e}[0-3] are available. -sethdlc from the ax25 utilities may be used to set driver states etc. -Users of userland AX.25 stacks may use the net2kiss utility (also available -in the ax25 utilities package) to convert packets of a network interface -to a KISS stream on a pseudo tty. There's also a patch available from -me for WAMPES which allows attaching a kernel network interface directly. - - -Configuring the driver -====================== - -Every time a driver is inserted into the kernel, it has to know which -modems it should access at which ports. This can be done with the setbaycom -utility. If you are only using one modem, you can also configure the -driver from the insmod command line (or by means of an option line in -``/etc/modprobe.d/*.conf``). - -Examples:: - - modprobe baycom_ser_fdx mode="ser12*" iobase=0x3f8 irq=4 - sethdlc -i bcsf0 -p mode "ser12*" io 0x3f8 irq 4 - -Both lines configure the first port to drive a ser12 modem at the first -serial port (COM1 under DOS). The * in the mode parameter instructs the driver -to use the software DCD algorithm (see below):: - - insmod baycom_par mode="picpar" iobase=0x378 - sethdlc -i bcp0 -p mode "picpar" io 0x378 - -Both lines configure the first port to drive a picpar modem at the -first parallel port (LPT1 under DOS). (Note: picpar implies -hardware DCD, par96 implies software DCD). - -The channel access parameters can be set with sethdlc -a or kissparms. -Note that both utilities interpret the values slightly differently. - - -Hardware DCD versus Software DCD -================================ - -To avoid collisions on the air, the driver must know when the channel is -busy. This is the task of the DCD circuitry/software. The driver may either -utilise a software DCD algorithm (options=1) or use a DCD signal from -the hardware (options=0). - -======= ================================================================= -ser12 if software DCD is utilised, the radio's squelch should always be - open. It is highly recommended to use the software DCD algorithm, - as it is much faster than most hardware squelch circuitry. The - disadvantage is a slightly higher load on the system. - -par96 the software DCD algorithm for this type of modem is rather poor. - The modem simply does not provide enough information to implement - a reasonable DCD algorithm in software. Therefore, if your radio - feeds the DCD input of the PAR96 modem, the use of the hardware - DCD circuitry is recommended. - -picpar the picpar modem features a builtin DCD hardware, which is highly - recommended. -======= ================================================================= - - - -Compatibility with the rest of the Linux kernel -=============================================== - -The serial driver and the baycom serial drivers compete -for the same hardware resources. Of course only one driver can access a given -interface at a time. The serial driver grabs all interfaces it can find at -startup time. Therefore the baycom drivers subsequently won't be able to -access a serial port. You might therefore find it necessary to release -a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where -# is the number of the interface. The baycom drivers do not reserve any -ports at startup, unless one is specified on the 'insmod' command line. Another -method to solve the problem is to compile all drivers as modules and -leave it to kmod to load the correct driver depending on the application. - -The parallel port drivers (baycom_par, baycom_epp) now use the parport subsystem -to arbitrate the ports between different client drivers. - -vy 73s de - -Tom Sailer, sailer@ife.ee.ethz.ch - -hb9jnx @ hb9w.ampr.org diff --git a/Documentation/networking/device_drivers/hamradio/index.rst b/Documentation/networking/device_drivers/hamradio/index.rst deleted file mode 100644 index 6af481c5b020..000000000000 --- a/Documentation/networking/device_drivers/hamradio/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) - -Amateur Radio Device Drivers -============================ - -Contents: - -.. toctree:: - :maxdepth: 2 - - baycom - z8530drv diff --git a/Documentation/networking/device_drivers/hamradio/z8530drv.rst b/Documentation/networking/device_drivers/hamradio/z8530drv.rst deleted file mode 100644 index d2942760f167..000000000000 --- a/Documentation/networking/device_drivers/hamradio/z8530drv.rst +++ /dev/null @@ -1,686 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 -.. include:: - -========================================================= -SCC.C - Linux driver for Z8530 based HDLC cards for AX.25 -========================================================= - - -This is a subset of the documentation. To use this driver you MUST have the -full package from: - -Internet: - - 1. ftp://ftp.ccac.rwth-aachen.de/pub/jr/z8530drv-utils_3.0-3.tar.gz - - 2. ftp://ftp.pspt.fi/pub/ham/linux/ax25/z8530drv-utils_3.0-3.tar.gz - -Please note that the information in this document may be hopelessly outdated. -A new version of the documentation, along with links to other important -Linux Kernel AX.25 documentation and programs, is available on -http://yaina.de/jreuter - -Copyright |copy| 1993,2000 by Joerg Reuter DL1BKE - -portions Copyright |copy| 1993 Guido ten Dolle PE1NNZ - -for the complete copyright notice see >> Copying.Z8530DRV << - -1. Initialization of the driver -=============================== - -To use the driver, 3 steps must be performed: - - 1. if compiled as module: loading the module - 2. Setup of hardware, MODEM and KISS parameters with sccinit - 3. Attach each channel to the Linux kernel AX.25 with "ifconfig" - -Unlike the versions below 2.4 this driver is a real network device -driver. If you want to run xNOS instead of our fine kernel AX.25 -use a 2.x version (available from above sites) or read the -AX.25-HOWTO on how to emulate a KISS TNC on network device drivers. - - -1.1 Loading the module -====================== - -(If you're going to compile the driver as a part of the kernel image, - skip this chapter and continue with 1.2) - -Before you can use a module, you'll have to load it with:: - - insmod scc.o - -please read 'man insmod' that comes with module-init-tools. - -You should include the insmod in one of the /etc/rc.d/rc.* files, -and don't forget to insert a call of sccinit after that. It -will read your /etc/z8530drv.conf. - -1.2. /etc/z8530drv.conf -======================= - -To setup all parameters you must run /sbin/sccinit from one -of your rc.*-files. This has to be done BEFORE you can -"ifconfig" an interface. Sccinit reads the file /etc/z8530drv.conf -and sets the hardware, MODEM and KISS parameters. A sample file is -delivered with this package. Change it to your needs. - -The file itself consists of two main sections. - -1.2.1 configuration of hardware parameters -========================================== - -The hardware setup section defines the following parameters for each -Z8530:: - - chip 1 - data_a 0x300 # data port A - ctrl_a 0x304 # control port A - data_b 0x301 # data port B - ctrl_b 0x305 # control port B - irq 5 # IRQ No. 5 - pclock 4915200 # clock - board BAYCOM # hardware type - escc no # enhanced SCC chip? (8580/85180/85280) - vector 0 # latch for interrupt vector - special no # address of special function register - option 0 # option to set via sfr - - -chip - - this is just a delimiter to make sccinit a bit simpler to - program. A parameter has no effect. - -data_a - - the address of the data port A of this Z8530 (needed) -ctrl_a - - the address of the control port A (needed) -data_b - - the address of the data port B (needed) -ctrl_b - - the address of the control port B (needed) - -irq - - the used IRQ for this chip. Different chips can use different - IRQs or the same. If they share an interrupt, it needs to be - specified within one chip-definition only. - -pclock - the clock at the PCLK pin of the Z8530 (option, 4915200 is - default), measured in Hertz - -board - - the "type" of the board: - - ======================= ======== - SCC type value - ======================= ======== - PA0HZP SCC card PA0HZP - EAGLE card EAGLE - PC100 card PC100 - PRIMUS-PC (DG9BL) card PRIMUS - BayCom (U)SCC card BAYCOM - ======================= ======== - -escc - - if you want support for ESCC chips (8580, 85180, 85280), set - this to "yes" (option, defaults to "no") - -vector - - address of the vector latch (aka "intack port") for PA0HZP - cards. There can be only one vector latch for all chips! - (option, defaults to 0) - -special - - address of the special function register on several cards. - (option, defaults to 0) - -option - The value you write into that register (option, default is 0) - -You can specify up to four chips (8 channels). If this is not enough, -just change:: - - #define MAXSCC 4 - -to a higher value. - -Example for the BAYCOM USCC: ----------------------------- - -:: - - chip 1 - data_a 0x300 # data port A - ctrl_a 0x304 # control port A - data_b 0x301 # data port B - ctrl_b 0x305 # control port B - irq 5 # IRQ No. 5 (#) - board BAYCOM # hardware type (*) - # - # SCC chip 2 - # - chip 2 - data_a 0x302 - ctrl_a 0x306 - data_b 0x303 - ctrl_b 0x307 - board BAYCOM - -An example for a PA0HZP card: ------------------------------ - -:: - - chip 1 - data_a 0x153 - data_b 0x151 - ctrl_a 0x152 - ctrl_b 0x150 - irq 9 - pclock 4915200 - board PA0HZP - vector 0x168 - escc no - # - # - # - chip 2 - data_a 0x157 - data_b 0x155 - ctrl_a 0x156 - ctrl_b 0x154 - irq 9 - pclock 4915200 - board PA0HZP - vector 0x168 - escc no - -A DRSI would should probably work with this: --------------------------------------------- -(actually: two DRSI cards...) - -:: - - chip 1 - data_a 0x303 - data_b 0x301 - ctrl_a 0x302 - ctrl_b 0x300 - irq 7 - pclock 4915200 - board DRSI - escc no - # - # - # - chip 2 - data_a 0x313 - data_b 0x311 - ctrl_a 0x312 - ctrl_b 0x310 - irq 7 - pclock 4915200 - board DRSI - escc no - -Note that you cannot use the on-board baudrate generator off DRSI -cards. Use "mode dpll" for clock source (see below). - -This is based on information provided by Mike Bilow (and verified -by Paul Helay) - -The utility "gencfg" --------------------- - -If you only know the parameters for the PE1CHL driver for DOS, -run gencfg. It will generate the correct port addresses (I hope). -Its parameters are exactly the same as the ones you use with -the "attach scc" command in net, except that the string "init" must -not appear. Example:: - - gencfg 2 0x150 4 2 0 1 0x168 9 4915200 - -will print a skeleton z8530drv.conf for the OptoSCC to stdout. - -:: - - gencfg 2 0x300 2 4 5 -4 0 7 4915200 0x10 - -does the same for the BAYCOM USCC card. In my opinion it is much easier -to edit scc_config.h... - - -1.2.2 channel configuration -=========================== - -The channel definition is divided into three sub sections for each -channel: - -An example for scc0:: - - # DEVICE - - device scc0 # the device for the following params - - # MODEM / BUFFERS - - speed 1200 # the default baudrate - clock dpll # clock source: - # dpll = normal half duplex operation - # external = MODEM provides own Rx/Tx clock - # divider = use full duplex divider if - # installed (1) - mode nrzi # HDLC encoding mode - # nrzi = 1k2 MODEM, G3RUH 9k6 MODEM - # nrz = DF9IC 9k6 MODEM - # - bufsize 384 # size of buffers. Note that this must include - # the AX.25 header, not only the data field! - # (optional, defaults to 384) - - # KISS (Layer 1) - - txdelay 36 # (see chapter 1.4) - persist 64 - slot 8 - tail 8 - fulldup 0 - wait 12 - min 3 - maxkey 7 - idle 3 - maxdef 120 - group 0 - txoff off - softdcd on - slip off - -The order WITHIN these sections is unimportant. The order OF these -sections IS important. The MODEM parameters are set with the first -recognized KISS parameter... - -Please note that you can initialize the board only once after boot -(or insmod). You can change all parameters but "mode" and "clock" -later with the Sccparam program or through KISS. Just to avoid -security holes... - -(1) this divider is usually mounted on the SCC-PBC (PA0HZP) or not - present at all (BayCom). It feeds back the output of the DPLL - (digital pll) as transmit clock. Using this mode without a divider - installed will normally result in keying the transceiver until - maxkey expires --- of course without sending anything (useful). - -2. Attachment of a channel by your AX.25 software -================================================= - -2.1 Kernel AX.25 -================ - -To set up an AX.25 device you can simply type:: - - ifconfig scc0 44.128.1.1 hw ax25 dl0tha-7 - -This will create a network interface with the IP number 44.128.20.107 -and the callsign "dl0tha". If you do not have any IP number (yet) you -can use any of the 44.128.0.0 network. Note that you do not need -axattach. The purpose of axattach (like slattach) is to create a KISS -network device linked to a TTY. Please read the documentation of the -ax25-utils and the AX.25-HOWTO to learn how to set the parameters of -the kernel AX.25. - -2.2 NOS, NET and TFKISS -======================= - -Since the TTY driver (aka KISS TNC emulation) is gone you need -to emulate the old behaviour. The cost of using these programs is -that you probably need to compile the kernel AX.25, regardless of whether -you actually use it or not. First setup your /etc/ax25/axports, -for example:: - - 9k6 dl0tha-9 9600 255 4 9600 baud port (scc3) - axlink dl0tha-15 38400 255 4 Link to NOS - -Now "ifconfig" the scc device:: - - ifconfig scc3 44.128.1.1 hw ax25 dl0tha-9 - -You can now axattach a pseudo-TTY:: - - axattach /dev/ptys0 axlink - -and start your NOS and attach /dev/ptys0 there. The problem is that -NOS is reachable only via digipeating through the kernel AX.25 -(disastrous on a DAMA controlled channel). To solve this problem, -configure "rxecho" to echo the incoming frames from "9k6" to "axlink" -and outgoing frames from "axlink" to "9k6" and start:: - - rxecho - -Or simply use "kissbridge" coming with z8530drv-utils:: - - ifconfig scc3 hw ax25 dl0tha-9 - kissbridge scc3 /dev/ptys0 - - -3. Adjustment and Display of parameters -======================================= - -3.1 Displaying SCC Parameters: -============================== - -Once a SCC channel has been attached, the parameter settings and -some statistic information can be shown using the param program:: - - dl1bke-u:~$ sccstat scc0 - - Parameters: - - speed : 1200 baud - txdelay : 36 - persist : 255 - slottime : 0 - txtail : 8 - fulldup : 1 - waittime : 12 - mintime : 3 sec - maxkeyup : 7 sec - idletime : 3 sec - maxdefer : 120 sec - group : 0x00 - txoff : off - softdcd : on - SLIP : off - - Status: - - HDLC Z8530 Interrupts Buffers - ----------------------------------------------------------------------- - Sent : 273 RxOver : 0 RxInts : 125074 Size : 384 - Received : 1095 TxUnder: 0 TxInts : 4684 NoSpace : 0 - RxErrors : 1591 ExInts : 11776 - TxErrors : 0 SpInts : 1503 - Tx State : idle - - -The status info shown is: - -============== ============================================================== -Sent number of frames transmitted -Received number of frames received -RxErrors number of receive errors (CRC, ABORT) -TxErrors number of discarded Tx frames (due to various reasons) -Tx State status of the Tx interrupt handler: idle/busy/active/tail (2) -RxOver number of receiver overruns -TxUnder number of transmitter underruns -RxInts number of receiver interrupts -TxInts number of transmitter interrupts -EpInts number of receiver special condition interrupts -SpInts number of external/status interrupts -Size maximum size of an AX.25 frame (*with* AX.25 headers!) -NoSpace number of times a buffer could not get allocated -============== ============================================================== - -An overrun is abnormal. If lots of these occur, the product of -baudrate and number of interfaces is too high for the processing -power of your computer. NoSpace errors are unlikely to be caused by the -driver or the kernel AX.25. - - -3.2 Setting Parameters -====================== - - -The setting of parameters of the emulated KISS TNC is done in the -same way in the SCC driver. You can change parameters by using -the kissparms program from the ax25-utils package or use the program -"sccparam":: - - sccparam - -You can change the following parameters: - -=========== ===== -param value -=========== ===== -speed 1200 -txdelay 36 -persist 255 -slottime 0 -txtail 8 -fulldup 1 -waittime 12 -mintime 3 -maxkeyup 7 -idletime 3 -maxdefer 120 -group 0x00 -txoff off -softdcd on -SLIP off -=========== ===== - - -The parameters have the following meaning: - -speed: - The baudrate on this channel in bits/sec - - Example: sccparam /dev/scc3 speed 9600 - -txdelay: - The delay (in units of 10 ms) after keying of the - transmitter, until the first byte is sent. This is usually - called "TXDELAY" in a TNC. When 0 is specified, the driver - will just wait until the CTS signal is asserted. This - assumes the presence of a timer or other circuitry in the - MODEM and/or transmitter, that asserts CTS when the - transmitter is ready for data. - A normal value of this parameter is 30-36. - - Example: sccparam /dev/scc0 txd 20 - -persist: - This is the probability that the transmitter will be keyed - when the channel is found to be free. It is a value from 0 - to 255, and the probability is (value+1)/256. The value - should be somewhere near 50-60, and should be lowered when - the channel is used more heavily. - - Example: sccparam /dev/scc2 persist 20 - -slottime: - This is the time between samples of the channel. It is - expressed in units of 10 ms. About 200-300 ms (value 20-30) - seems to be a good value. - - Example: sccparam /dev/scc0 slot 20 - -tail: - The time the transmitter will remain keyed after the last - byte of a packet has been transferred to the SCC. This is - necessary because the CRC and a flag still have to leave the - SCC before the transmitter is keyed down. The value depends - on the baudrate selected. A few character times should be - sufficient, e.g. 40ms at 1200 baud. (value 4) - The value of this parameter is in 10 ms units. - - Example: sccparam /dev/scc2 4 - -full: - The full-duplex mode switch. This can be one of the following - values: - - 0: The interface will operate in CSMA mode (the normal - half-duplex packet radio operation) - 1: Fullduplex mode, i.e. the transmitter will be keyed at - any time, without checking the received carrier. It - will be unkeyed when there are no packets to be sent. - 2: Like 1, but the transmitter will remain keyed, also - when there are no packets to be sent. Flags will be - sent in that case, until a timeout (parameter 10) - occurs. - - Example: sccparam /dev/scc0 fulldup off - -wait: - The initial waittime before any transmit attempt, after the - frame has been queue for transmit. This is the length of - the first slot in CSMA mode. In full duplex modes it is - set to 0 for maximum performance. - The value of this parameter is in 10 ms units. - - Example: sccparam /dev/scc1 wait 4 - -maxkey: - The maximal time the transmitter will be keyed to send - packets, in seconds. This can be useful on busy CSMA - channels, to avoid "getting a bad reputation" when you are - generating a lot of traffic. After the specified time has - elapsed, no new frame will be started. Instead, the trans- - mitter will be switched off for a specified time (parameter - min), and then the selected algorithm for keyup will be - started again. - The value 0 as well as "off" will disable this feature, - and allow infinite transmission time. - - Example: sccparam /dev/scc0 maxk 20 - -min: - This is the time the transmitter will be switched off when - the maximum transmission time is exceeded. - - Example: sccparam /dev/scc3 min 10 - -idle: - This parameter specifies the maximum idle time in full duplex - 2 mode, in seconds. When no frames have been sent for this - time, the transmitter will be keyed down. A value of 0 is - has same result as the fullduplex mode 1. This parameter - can be disabled. - - Example: sccparam /dev/scc2 idle off # transmit forever - -maxdefer - This is the maximum time (in seconds) to wait for a free channel - to send. When this timer expires the transmitter will be keyed - IMMEDIATELY. If you love to get trouble with other users you - should set this to a very low value ;-) - - Example: sccparam /dev/scc0 maxdefer 240 # 2 minutes - - -txoff: - When this parameter has the value 0, the transmission of packets - is enable. Otherwise it is disabled. - - Example: sccparam /dev/scc2 txoff on - -group: - It is possible to build special radio equipment to use more than - one frequency on the same band, e.g. using several receivers and - only one transmitter that can be switched between frequencies. - Also, you can connect several radios that are active on the same - band. In these cases, it is not possible, or not a good idea, to - transmit on more than one frequency. The SCC driver provides a - method to lock transmitters on different interfaces, using the - "param group " command. This will only work when - you are using CSMA mode (parameter full = 0). - - The number must be 0 if you want no group restrictions, and - can be computed as follows to create restricted groups: - is the sum of some OCTAL numbers: - - - === ======================================================= - 200 This transmitter will only be keyed when all other - transmitters in the group are off. - 100 This transmitter will only be keyed when the carrier - detect of all other interfaces in the group is off. - 0xx A byte that can be used to define different groups. - Interfaces are in the same group, when the logical AND - between their xx values is nonzero. - === ======================================================= - - Examples: - - When 2 interfaces use group 201, their transmitters will never be - keyed at the same time. - - When 2 interfaces use group 101, the transmitters will only key - when both channels are clear at the same time. When group 301, - the transmitters will not be keyed at the same time. - - Don't forget to convert the octal numbers into decimal before - you set the parameter. - - Example: (to be written) - -softdcd: - use a software dcd instead of the real one... Useful for a very - slow squelch. - - Example: sccparam /dev/scc0 soft on - - -4. Problems -=========== - -If you have tx-problems with your BayCom USCC card please check -the manufacturer of the 8530. SGS chips have a slightly -different timing. Try Zilog... A solution is to write to register 8 -instead to the data port, but this won't work with the ESCC chips. -*SIGH!* - -A very common problem is that the PTT locks until the maxkeyup timer -expires, although interrupts and clock source are correct. In most -cases compiling the driver with CONFIG_SCC_DELAY (set with -make config) solves the problems. For more hints read the (pseudo) FAQ -and the documentation coming with z8530drv-utils. - -I got reports that the driver has problems on some 386-based systems. -(i.e. Amstrad) Those systems have a bogus AT bus timing which will -lead to delayed answers on interrupts. You can recognize these -problems by looking at the output of Sccstat for the suspected -port. If it shows under- and overruns you own such a system. - -Delayed processing of received data: This depends on - -- the kernel version - -- kernel profiling compiled or not - -- a high interrupt load - -- a high load of the machine --- running X, Xmorph, XV and Povray, - while compiling the kernel... hmm ... even with 32 MB RAM ... ;-) - Or running a named for the whole .ampr.org domain on an 8 MB - box... - -- using information from rxecho or kissbridge. - -Kernel panics: please read /linux/README and find out if it -really occurred within the scc driver. - -If you cannot solve a problem, send me - -- a description of the problem, -- information on your hardware (computer system, scc board, modem) -- your kernel version -- the output of cat /proc/net/z8530 - -4. Thor RLC100 -============== - -Mysteriously this board seems not to work with the driver. Anyone -got it up-and-running? - - -Many thanks to Linus Torvalds and Alan Cox for including the driver -in the Linux standard distribution and their support. - -:: - - Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ABH.#BAY.DEU.EU - Internet: jreuter@yaina.de - WWW : http://yaina.de/jreuter diff --git a/Documentation/networking/device_drivers/index.rst b/Documentation/networking/device_drivers/index.rst index 1df51c9f7827..1f54f01d24be 100644 --- a/Documentation/networking/device_drivers/index.rst +++ b/Documentation/networking/device_drivers/index.rst @@ -13,6 +13,5 @@ Contents: cellular/index ethernet/index fddi/index - hamradio/index wifi/index wwan/index diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst index 2e946924ad3f..44a422ad3b05 100644 --- a/Documentation/networking/index.rst +++ b/Documentation/networking/index.rst @@ -40,11 +40,9 @@ Contents: tls-handshake nfc 6lowpan - 6pack arcnet-hardware arcnet atm - ax25 bonding cdc_mbim dctcp diff --git a/Documentation/staging/magic-number.rst b/Documentation/staging/magic-number.rst index 79afddf0e692..670d3189a976 100644 --- a/Documentation/staging/magic-number.rst +++ b/Documentation/staging/magic-number.rst @@ -72,11 +72,8 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/uapi/l APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip/slip.h`` -BAYCOM_MAGIC 19730510 baycom_state ``drivers/net/hamradio/baycom_epp.c`` -HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h`` -YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c`` CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c`` QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c`` QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c`` diff --git a/Documentation/translations/it_IT/staging/magic-number.rst b/Documentation/translations/it_IT/staging/magic-number.rst index cd8f23571835..43dd6398300b 100644 --- a/Documentation/translations/it_IT/staging/magic-number.rst +++ b/Documentation/translations/it_IT/staging/magic-number.rst @@ -78,11 +78,8 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` -HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h`` -YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c`` CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c`` QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c`` QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c`` diff --git a/Documentation/translations/sp_SP/process/magic-number.rst b/Documentation/translations/sp_SP/process/magic-number.rst index beb4b4c1de11..f5b4c3f2849f 100644 --- a/Documentation/translations/sp_SP/process/magic-number.rst +++ b/Documentation/translations/sp_SP/process/magic-number.rst @@ -77,11 +77,8 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` -HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h`` -YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c`` CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c`` QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c`` QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c`` diff --git a/Documentation/translations/zh_CN/process/magic-number.rst b/Documentation/translations/zh_CN/process/magic-number.rst index 4ebc84cc0c54..05ee75cf4346 100644 --- a/Documentation/translations/zh_CN/process/magic-number.rst +++ b/Documentation/translations/zh_CN/process/magic-number.rst @@ -70,11 +70,8 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` -HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h`` -YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c`` CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c`` QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c`` QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c`` diff --git a/Documentation/translations/zh_TW/process/magic-number.rst b/Documentation/translations/zh_TW/process/magic-number.rst index 5582df6d7ca7..bc7eb025dd1e 100644 --- a/Documentation/translations/zh_TW/process/magic-number.rst +++ b/Documentation/translations/zh_TW/process/magic-number.rst @@ -64,11 +64,8 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` -HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h`` -YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c`` CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c`` QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c`` QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c`` diff --git a/MAINTAINERS b/MAINTAINERS index e4856d3427d9..867ca44422d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -102,12 +102,6 @@ F: Documentation/networking/6lowpan.rst F: include/net/6lowpan.h F: net/6lowpan/ -6PACK NETWORK DRIVER FOR AX.25 -M: Andreas Koensgen -L: linux-hams@vger.kernel.org -S: Maintained -F: drivers/net/hamradio/6pack.c - 802.11 (including CFG80211/NL80211) M: Johannes Berg L: linux-wireless@vger.kernel.org @@ -4280,14 +4274,6 @@ S: Maintained F: Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml F: drivers/video/backlight/aw99706.c -AX.25 NETWORK LAYER -L: linux-hams@vger.kernel.org -S: Orphan -W: https://linux-ax25.in-berlin.de -F: include/net/ax25.h -F: include/uapi/linux/ax25.h -F: net/ax25/ - AXENTIA ARM DEVICES M: Peter Rosin L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) @@ -4429,13 +4415,6 @@ F: include/uapi/linux/batadv_packet.h F: include/uapi/linux/batman_adv.h F: net/batman-adv/ -BAYCOM/HDLCDRV DRIVERS FOR AX.25 -M: Thomas Sailer -L: linux-hams@vger.kernel.org -S: Maintained -W: http://www.baycom.org/~tom/ham/ham.html -F: drivers/net/hamradio/baycom* - BCACHE (BLOCK LAYER CACHE) M: Coly Li M: Kent Overstreet @@ -7019,20 +6998,6 @@ S: Maintained F: drivers/rtc/rtc-ds1685.c F: include/linux/rtc/ds1685.h -DAMA SLAVE for AX.25 -M: Joerg Reuter -L: linux-hams@vger.kernel.org -S: Maintained -W: http://yaina.de/jreuter/ -W: http://www.qsl.net/dl1bke/ -F: net/ax25/af_ax25.c -F: net/ax25/ax25_dev.c -F: net/ax25/ax25_ds_* -F: net/ax25/ax25_in.c -F: net/ax25/ax25_out.c -F: net/ax25/ax25_timer.c -F: net/ax25/sysctl_net_ax25.c - DASHARO ACPI PLATFORM DRIVER M: MichaĹ‚ Kopeć S: Maintained @@ -11443,11 +11408,6 @@ T: git https://github.com/Rust-for-Linux/linux.git timekeeping-next F: rust/kernel/time.rs F: rust/kernel/time/ -HIGH-SPEED SCC DRIVER FOR AX.25 -L: linux-hams@vger.kernel.org -S: Orphan -F: drivers/net/hamradio/scc.c - HIGHPOINT ROCKETRAID 3xxx RAID DRIVER M: HighPoint Linux Team S: Supported @@ -18271,14 +18231,6 @@ F: net/bridge/br_netfilter*.c F: net/netfilter/ F: tools/testing/selftests/net/netfilter/ -NETROM NETWORK LAYER -L: linux-hams@vger.kernel.org -S: Orphan -W: https://linux-ax25.in-berlin.de -F: include/net/netrom.h -F: include/uapi/linux/netrom.h -F: net/netrom/ - NETRONIX EMBEDDED CONTROLLER M: Jonathan Neuschäfer S: Maintained @@ -23071,14 +23023,6 @@ F: include/linux/mfd/rohm-bd96802.h F: include/linux/mfd/rohm-generic.h F: include/linux/mfd/rohm-shared.h -ROSE NETWORK LAYER -L: linux-hams@vger.kernel.org -S: Orphan -W: https://linux-ax25.in-berlin.de -F: include/net/rose.h -F: include/uapi/linux/rose.h -F: net/rose/ - ROTATION DRIVER FOR ALLWINNER A83T M: Jernej Skrabec L: linux-media@vger.kernel.org @@ -29104,13 +29048,6 @@ F: lib/decompress_unxz.c F: lib/xz/ F: scripts/xz_wrap.sh -YAM DRIVER FOR AX.25 -M: Jean-Paul Roubelat -L: linux-hams@vger.kernel.org -S: Maintained -F: drivers/net/hamradio/yam* -F: include/linux/yam.h - YAMA SECURITY MODULE M: Kees Cook S: Supported @@ -29132,16 +29069,6 @@ S: Maintained F: Documentation/input/devices/yealink.rst F: drivers/input/misc/yealink.* -Z8530 DRIVER FOR AX.25 -M: Joerg Reuter -L: linux-hams@vger.kernel.org -S: Maintained -W: http://yaina.de/jreuter/ -W: http://www.qsl.net/dl1bke/ -F: Documentation/networking/device_drivers/hamradio/z8530drv.rst -F: drivers/net/hamradio/*scc.c -F: drivers/net/hamradio/z8530.h - ZD1211RW WIRELESS DRIVER L: linux-wireless@vger.kernel.org S: Orphan diff --git a/arch/mips/configs/bcm47xx_defconfig b/arch/mips/configs/bcm47xx_defconfig index d10b3d4adbd1..acbab8dae53f 100644 --- a/arch/mips/configs/bcm47xx_defconfig +++ b/arch/mips/configs/bcm47xx_defconfig @@ -28,7 +28,6 @@ CONFIG_NETFILTER=y CONFIG_VLAN_8021Q=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_FQ_CODEL=y -CONFIG_HAMRADIO=y CONFIG_CFG80211=y CONFIG_MAC80211=y CONFIG_MTD=y diff --git a/arch/mips/configs/bigsur_defconfig b/arch/mips/configs/bigsur_defconfig index 3b64e151e187..aa63ada62e28 100644 --- a/arch/mips/configs/bigsur_defconfig +++ b/arch/mips/configs/bigsur_defconfig @@ -84,16 +84,6 @@ CONFIG_IP_VS_FTP=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m CONFIG_VLAN_8021Q_GVRP=y -CONFIG_HAMRADIO=y -CONFIG_AX25=m -CONFIG_NETROM=m -CONFIG_ROSE=m -CONFIG_MKISS=m -CONFIG_6PACK=m -CONFIG_BPQETHER=m -CONFIG_BAYCOM_SER_FDX=m -CONFIG_BAYCOM_SER_HDX=m -CONFIG_YAM=m CONFIG_FW_LOADER=m CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_NBD=m diff --git a/arch/mips/configs/gpr_defconfig b/arch/mips/configs/gpr_defconfig index fdd28a89e336..261730af75c7 100644 --- a/arch/mips/configs/gpr_defconfig +++ b/arch/mips/configs/gpr_defconfig @@ -130,17 +130,6 @@ CONFIG_NET_EMATCH_TEXT=m CONFIG_NET_CLS_ACT=y CONFIG_NET_ACT_POLICE=y CONFIG_NET_PKTGEN=m -CONFIG_HAMRADIO=y -CONFIG_AX25=m -# CONFIG_AX25_DAMA_SLAVE is not set -CONFIG_NETROM=m -CONFIG_ROSE=m -CONFIG_MKISS=m -CONFIG_6PACK=m -CONFIG_BPQETHER=m -CONFIG_BAYCOM_SER_FDX=m -CONFIG_BAYCOM_SER_HDX=m -CONFIG_YAM=m CONFIG_CFG80211=y CONFIG_MAC80211=y CONFIG_MTD=y diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig index 72568f8ae653..315650c6fe0b 100644 --- a/arch/mips/configs/mtx1_defconfig +++ b/arch/mips/configs/mtx1_defconfig @@ -176,17 +176,6 @@ CONFIG_NET_EMATCH_TEXT=m CONFIG_NET_CLS_ACT=y CONFIG_NET_ACT_POLICE=y CONFIG_NET_PKTGEN=m -CONFIG_HAMRADIO=y -CONFIG_AX25=m -# CONFIG_AX25_DAMA_SLAVE is not set -CONFIG_NETROM=m -CONFIG_ROSE=m -CONFIG_MKISS=m -CONFIG_6PACK=m -CONFIG_BPQETHER=m -CONFIG_BAYCOM_SER_FDX=m -CONFIG_BAYCOM_SER_HDX=m -CONFIG_YAM=m CONFIG_BT=m CONFIG_BT_RFCOMM=m CONFIG_BT_RFCOMM_TTY=y diff --git a/arch/mips/configs/rb532_defconfig b/arch/mips/configs/rb532_defconfig index 30d18b084cda..a88322fe3935 100644 --- a/arch/mips/configs/rb532_defconfig +++ b/arch/mips/configs/rb532_defconfig @@ -95,7 +95,6 @@ CONFIG_NET_ACT_GACT=m CONFIG_GACT_PROB=y CONFIG_NET_ACT_MIRRED=m CONFIG_NET_ACT_PEDIT=m -CONFIG_HAMRADIO=y CONFIG_MTD=y CONFIG_MTD_BLOCK=y CONFIG_MTD_BLOCK2MTD=y diff --git a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig index b1e67ff0c4f0..ad9fbd0cbb38 100644 --- a/arch/mips/configs/rm200_defconfig +++ b/arch/mips/configs/rm200_defconfig @@ -147,13 +147,6 @@ CONFIG_NET_CLS_FW=m CONFIG_NET_CLS_U32=m CONFIG_NET_CLS_RSVP=m CONFIG_NET_CLS_RSVP6=m -CONFIG_HAMRADIO=y -CONFIG_AX25=m -CONFIG_NETROM=m -CONFIG_ROSE=m -CONFIG_MKISS=m -CONFIG_6PACK=m -CONFIG_BPQETHER=m CONFIG_CONNECTOR=m CONFIG_PARPORT=m CONFIG_PARPORT_PC=m diff --git a/arch/mips/configs/rt305x_defconfig b/arch/mips/configs/rt305x_defconfig index 8f9701efef19..c920976bedd0 100644 --- a/arch/mips/configs/rt305x_defconfig +++ b/arch/mips/configs/rt305x_defconfig @@ -64,7 +64,6 @@ CONFIG_BRIDGE=y # CONFIG_BRIDGE_IGMP_SNOOPING is not set CONFIG_VLAN_8021Q=y CONFIG_NET_SCHED=y -CONFIG_HAMRADIO=y CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y diff --git a/arch/mips/configs/xway_defconfig b/arch/mips/configs/xway_defconfig index aae8497b6872..f1c53bbb72e9 100644 --- a/arch/mips/configs/xway_defconfig +++ b/arch/mips/configs/xway_defconfig @@ -66,7 +66,6 @@ CONFIG_BRIDGE=y # CONFIG_BRIDGE_IGMP_SNOOPING is not set CONFIG_VLAN_8021Q=y CONFIG_NET_SCHED=y -CONFIG_HAMRADIO=y CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 3b2d28127634..b87a741fc952 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -54,7 +54,6 @@ obj-y += dsa/ endif obj-$(CONFIG_ETHERNET) += ethernet/ obj-$(CONFIG_FDDI) += fddi/ -obj-$(CONFIG_HAMRADIO) += hamradio/ obj-$(CONFIG_QCOM_IPA) += ipa/ obj-$(CONFIG_PLIP) += plip/ obj-$(CONFIG_PPP) += ppp/ diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c deleted file mode 100644 index c8b2dc5c1bec..000000000000 --- a/drivers/net/hamradio/6pack.c +++ /dev/null @@ -1,912 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * 6pack.c This module implements the 6pack protocol for kernel-based - * devices like TTY. It interfaces between a raw TTY and the - * kernel's AX.25 protocol layers. - * - * Authors: Andreas Könsgen - * Ralf Baechle DL5RB - * - * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by - * - * Laurence Culhane, - * Fred N. van Kempen, - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* sixpack priority commands */ -#define SIXP_SEOF 0x40 /* start and end of a 6pack frame */ -#define SIXP_TX_URUN 0x48 /* transmit overrun */ -#define SIXP_RX_ORUN 0x50 /* receive overrun */ -#define SIXP_RX_BUF_OVL 0x58 /* receive buffer overflow */ - -#define SIXP_CHKSUM 0xFF /* valid checksum of a 6pack frame */ - -/* masks to get certain bits out of the status bytes sent by the TNC */ - -#define SIXP_CMD_MASK 0xC0 -#define SIXP_CHN_MASK 0x07 -#define SIXP_PRIO_CMD_MASK 0x80 -#define SIXP_STD_CMD_MASK 0x40 -#define SIXP_PRIO_DATA_MASK 0x38 -#define SIXP_TX_MASK 0x20 -#define SIXP_RX_MASK 0x10 -#define SIXP_RX_DCD_MASK 0x18 -#define SIXP_LEDS_ON 0x78 -#define SIXP_LEDS_OFF 0x60 -#define SIXP_CON 0x08 -#define SIXP_STA 0x10 - -#define SIXP_FOUND_TNC 0xe9 -#define SIXP_CON_ON 0x68 -#define SIXP_DCD_MASK 0x08 -#define SIXP_DAMA_OFF 0 - -/* default level 2 parameters */ -#define SIXP_TXDELAY 25 /* 250 ms */ -#define SIXP_PERSIST 50 /* in 256ths */ -#define SIXP_SLOTTIME 10 /* 100 ms */ -#define SIXP_INIT_RESYNC_TIMEOUT (3*HZ/2) /* in 1 s */ -#define SIXP_RESYNC_TIMEOUT 5*HZ /* in 1 s */ - -/* 6pack configuration. */ -#define SIXP_NRUNIT 31 /* MAX number of 6pack channels */ -#define SIXP_MTU 256 /* Default MTU */ - -enum sixpack_flags { - SIXPF_ERROR, /* Parity, etc. error */ -}; - -struct sixpack { - /* Various fields. */ - struct tty_struct *tty; /* ptr to TTY structure */ - struct net_device *dev; /* easy for intr handling */ - - /* These are pointers to the malloc()ed frame buffers. */ - int rcount; /* received chars counter */ - unsigned char *xbuff; /* transmitter buffer */ - unsigned char *xhead; /* next byte to XMIT */ - int xleft; /* bytes left in XMIT queue */ - - u8 raw_buf[4]; - u8 cooked_buf[400]; - - unsigned int rx_count; - unsigned int rx_count_cooked; - spinlock_t rxlock; - - unsigned long flags; /* Flag values/ mode etc */ - unsigned char mode; /* 6pack mode */ - - /* 6pack stuff */ - unsigned char tx_delay; - unsigned char persistence; - unsigned char slottime; - unsigned char duplex; - unsigned char led_state; - u8 status; - u8 status1; - unsigned char status2; - unsigned char tx_enable; - unsigned char tnc_state; - - struct timer_list tx_t; - struct timer_list resync_t; - spinlock_t lock; -}; - -#define AX25_6PACK_HEADER_LEN 0 - -static void sixpack_decode(struct sixpack *, const u8 *, size_t); -static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char); - -/* - * Perform the persistence/slottime algorithm for CSMA access. If the - * persistence check was successful, write the data to the serial driver. - * Note that in case of DAMA operation, the data is not sent here. - */ - -static void sp_xmit_on_air(struct timer_list *t) -{ - struct sixpack *sp = timer_container_of(sp, t, tx_t); - int actual, when = sp->slottime; - static unsigned char random; - - random = random * 17 + 41; - - if (((sp->status1 & SIXP_DCD_MASK) == 0) && (random < sp->persistence)) { - sp->led_state = 0x70; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tx_enable = 1; - actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2); - sp->xleft -= actual; - sp->xhead += actual; - sp->led_state = 0x60; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->status2 = 0; - } else - mod_timer(&sp->tx_t, jiffies + ((when + 1) * HZ) / 100); -} - -/* ----> 6pack timer interrupt handler and friends. <---- */ - -/* Encapsulate one AX.25 frame and stuff into a TTY queue. */ -static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len) -{ - unsigned char *msg, *p = icp; - int actual, count; - - if (len > AX25_MTU + 73) { - msg = "oversized transmit packet!"; - goto out_drop; - } - - if (p[0] > 5) { - msg = "invalid KISS command"; - goto out_drop; - } - - if ((p[0] != 0) && (len > 2)) { - msg = "KISS control packet too long"; - goto out_drop; - } - - if ((p[0] == 0) && (len < 15)) { - msg = "bad AX.25 packet to transmit"; - goto out_drop; - } - - count = encode_sixpack(p, sp->xbuff, len, sp->tx_delay); - set_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags); - - switch (p[0]) { - case 1: sp->tx_delay = p[1]; - return; - case 2: sp->persistence = p[1]; - return; - case 3: sp->slottime = p[1]; - return; - case 4: /* ignored */ - return; - case 5: sp->duplex = p[1]; - return; - } - - if (p[0] != 0) - return; - - /* - * In case of fullduplex or DAMA operation, we don't take care about the - * state of the DCD or of any timers, as the determination of the - * correct time to send is the job of the AX.25 layer. We send - * immediately after data has arrived. - */ - if (sp->duplex == 1) { - sp->led_state = 0x70; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tx_enable = 1; - actual = sp->tty->ops->write(sp->tty, sp->xbuff, count); - sp->xleft = count - actual; - sp->xhead = sp->xbuff + actual; - sp->led_state = 0x60; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - } else { - sp->xleft = count; - sp->xhead = sp->xbuff; - sp->status2 = count; - sp_xmit_on_air(&sp->tx_t); - } - - return; - -out_drop: - sp->dev->stats.tx_dropped++; - netif_start_queue(sp->dev); - if (net_ratelimit()) - printk(KERN_DEBUG "%s: %s - dropped.\n", sp->dev->name, msg); -} - -/* Encapsulate an IP datagram and kick it into a TTY queue. */ - -static netdev_tx_t sp_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct sixpack *sp = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - spin_lock_bh(&sp->lock); - /* We were not busy, so we are now... :-) */ - netif_stop_queue(dev); - dev->stats.tx_bytes += skb->len; - sp_encaps(sp, skb->data, skb->len); - spin_unlock_bh(&sp->lock); - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static int sp_open_dev(struct net_device *dev) -{ - struct sixpack *sp = netdev_priv(dev); - - if (sp->tty == NULL) - return -ENODEV; - return 0; -} - -/* Close the low-level part of the 6pack channel. */ -static int sp_close(struct net_device *dev) -{ - struct sixpack *sp = netdev_priv(dev); - - spin_lock_bh(&sp->lock); - if (sp->tty) { - /* TTY discipline is running. */ - clear_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags); - } - netif_stop_queue(dev); - spin_unlock_bh(&sp->lock); - - return 0; -} - -static int sp_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr_ax25 *sa = addr; - - netif_tx_lock_bh(dev); - netif_addr_lock(dev); - __dev_addr_set(dev, &sa->sax25_call, AX25_ADDR_LEN); - netif_addr_unlock(dev); - netif_tx_unlock_bh(dev); - - return 0; -} - -static const struct net_device_ops sp_netdev_ops = { - .ndo_open = sp_open_dev, - .ndo_stop = sp_close, - .ndo_start_xmit = sp_xmit, - .ndo_set_mac_address = sp_set_mac_address, -}; - -static void sp_setup(struct net_device *dev) -{ - /* Finish setting up the DEVICE info. */ - dev->netdev_ops = &sp_netdev_ops; - dev->mtu = SIXP_MTU; - dev->hard_header_len = AX25_MAX_HEADER_LEN; - dev->header_ops = &ax25_header_ops; - - dev->addr_len = AX25_ADDR_LEN; - dev->type = ARPHRD_AX25; - dev->tx_queue_len = 10; - - /* Only activated in AX.25 mode */ - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); - - dev->flags = 0; -} - -/* Send one completely decapsulated IP datagram to the IP layer. */ - -/* - * This is the routine that sends the received data to the kernel AX.25. - * 'cmd' is the KISS command. For AX.25 data, it is zero. - */ - -static void sp_bump(struct sixpack *sp, char cmd) -{ - struct sk_buff *skb; - int count; - u8 *ptr; - - count = sp->rcount + 1; - - sp->dev->stats.rx_bytes += count; - - if ((skb = dev_alloc_skb(count + 1)) == NULL) - goto out_mem; - - ptr = skb_put(skb, count + 1); - *ptr++ = cmd; /* KISS command */ - - memcpy(ptr, sp->cooked_buf + 1, count); - skb->protocol = ax25_type_trans(skb, sp->dev); - netif_rx(skb); - sp->dev->stats.rx_packets++; - - return; - -out_mem: - sp->dev->stats.rx_dropped++; -} - - -/* ----------------------------------------------------------------------- */ - -/* - * Called by the TTY driver when there's room for more data. If we have - * more packets to send, we send them here. - */ -static void sixpack_write_wakeup(struct tty_struct *tty) -{ - struct sixpack *sp = tty->disc_data; - int actual; - - if (!sp) - return; - if (sp->xleft <= 0) { - /* Now serial buffer is almost free & we can start - * transmission of another packet */ - sp->dev->stats.tx_packets++; - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - sp->tx_enable = 0; - netif_wake_queue(sp->dev); - return; - } - - if (sp->tx_enable) { - actual = tty->ops->write(tty, sp->xhead, sp->xleft); - sp->xleft -= actual; - sp->xhead += actual; - } -} - -/* ----------------------------------------------------------------------- */ - -/* - * Handle the 'receiver data ready' interrupt. - * This function is called by the tty module in the kernel when - * a block of 6pack data has been received, which can now be decapsulated - * and sent on to some IP layer for further processing. - */ -static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp, - const u8 *fp, size_t count) -{ - struct sixpack *sp; - - if (!count) - return; - - sp = tty->disc_data; - if (!sp) - return; - - /* Read the characters out of the buffer */ - while (count--) { - if (fp && *fp++) { - if (!test_and_set_bit(SIXPF_ERROR, &sp->flags)) - sp->dev->stats.rx_errors++; - cp++; - continue; - } - sixpack_decode(sp, cp, 1); - cp++; - } - - tty_unthrottle(tty); -} - -/* - * Try to resync the TNC. Called by the resync timer defined in - * decode_prio_command - */ - -#define TNC_UNINITIALIZED 0 -#define TNC_UNSYNC_STARTUP 1 -#define TNC_UNSYNCED 2 -#define TNC_IN_SYNC 3 - -static void __tnc_set_sync_state(struct sixpack *sp, int new_tnc_state) -{ - char *msg; - - switch (new_tnc_state) { - default: /* gcc oh piece-o-crap ... */ - case TNC_UNSYNC_STARTUP: - msg = "Synchronizing with TNC"; - break; - case TNC_UNSYNCED: - msg = "Lost synchronization with TNC\n"; - break; - case TNC_IN_SYNC: - msg = "Found TNC"; - break; - } - - sp->tnc_state = new_tnc_state; - printk(KERN_INFO "%s: %s\n", sp->dev->name, msg); -} - -static inline void tnc_set_sync_state(struct sixpack *sp, int new_tnc_state) -{ - int old_tnc_state = sp->tnc_state; - - if (old_tnc_state != new_tnc_state) - __tnc_set_sync_state(sp, new_tnc_state); -} - -static void resync_tnc(struct timer_list *t) -{ - struct sixpack *sp = timer_container_of(sp, t, resync_t); - static char resync_cmd = 0xe8; - - /* clear any data that might have been received */ - - sp->rx_count = 0; - sp->rx_count_cooked = 0; - - /* reset state machine */ - - sp->status = 1; - sp->status1 = 1; - sp->status2 = 0; - - /* resync the TNC */ - - sp->led_state = 0x60; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tty->ops->write(sp->tty, &resync_cmd, 1); - - - /* Start resync timer again -- the TNC might be still absent */ - mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT); -} - -static inline int tnc_init(struct sixpack *sp) -{ - unsigned char inbyte = 0xe8; - - tnc_set_sync_state(sp, TNC_UNSYNC_STARTUP); - - sp->tty->ops->write(sp->tty, &inbyte, 1); - - mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT); - - return 0; -} - -/* - * Open the high-level part of the 6pack channel. - * This function is called by the TTY module when the - * 6pack line discipline is called for. Because we are - * sure the tty line exists, we only have to link it to - * a free 6pcack channel... - */ -static int sixpack_open(struct tty_struct *tty) -{ - char *xbuff = NULL; - struct net_device *dev; - struct sixpack *sp; - unsigned long len; - int err = 0; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (tty->ops->write == NULL) - return -EOPNOTSUPP; - - dev = alloc_netdev(sizeof(struct sixpack), "sp%d", NET_NAME_UNKNOWN, - sp_setup); - if (!dev) { - err = -ENOMEM; - goto out; - } - - sp = netdev_priv(dev); - sp->dev = dev; - - spin_lock_init(&sp->lock); - spin_lock_init(&sp->rxlock); - - /* !!! length of the buffers. MTU is IP MTU, not PACLEN! */ - - len = dev->mtu * 2; - - xbuff = kmalloc(len + 4, GFP_KERNEL); - if (xbuff == NULL) { - err = -ENOBUFS; - goto out_free; - } - - spin_lock_bh(&sp->lock); - - sp->tty = tty; - - sp->xbuff = xbuff; - - sp->rcount = 0; - sp->rx_count = 0; - sp->rx_count_cooked = 0; - sp->xleft = 0; - - sp->flags = 0; /* Clear ESCAPE & ERROR flags */ - - sp->duplex = 0; - sp->tx_delay = SIXP_TXDELAY; - sp->persistence = SIXP_PERSIST; - sp->slottime = SIXP_SLOTTIME; - sp->led_state = 0x60; - sp->status = 1; - sp->status1 = 1; - sp->status2 = 0; - sp->tx_enable = 0; - - netif_start_queue(dev); - - timer_setup(&sp->tx_t, sp_xmit_on_air, 0); - - timer_setup(&sp->resync_t, resync_tnc, 0); - - spin_unlock_bh(&sp->lock); - - /* Done. We have linked the TTY line to a channel. */ - tty->disc_data = sp; - tty->receive_room = 65536; - - /* Now we're ready to register. */ - err = register_netdev(dev); - if (err) - goto out_free; - - tnc_init(sp); - - return 0; - -out_free: - kfree(xbuff); - - free_netdev(dev); - -out: - return err; -} - - -/* - * Close down a 6pack channel. - * This means flushing out any pending queues, and then restoring the - * TTY line discipline to what it was before it got hooked to 6pack - * (which usually is TTY again). - */ -static void sixpack_close(struct tty_struct *tty) -{ - struct sixpack *sp; - - sp = tty->disc_data; - if (!sp) - return; - - tty->disc_data = NULL; - - /* We must stop the queue to avoid potentially scribbling - * on the free buffers. The sp->dead completion is not sufficient - * to protect us from sp->xbuff access. - */ - netif_stop_queue(sp->dev); - - unregister_netdev(sp->dev); - - timer_delete_sync(&sp->tx_t); - timer_delete_sync(&sp->resync_t); - - /* Free all 6pack frame buffers after unreg. */ - kfree(sp->xbuff); - - free_netdev(sp->dev); -} - -/* Perform I/O control on an active 6pack channel. */ -static int sixpack_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct sixpack *sp = tty->disc_data; - struct net_device *dev; - unsigned int tmp, err; - - if (!sp) - return -ENXIO; - dev = sp->dev; - - switch(cmd) { - case SIOCGIFNAME: - err = copy_to_user((void __user *) arg, dev->name, - strlen(dev->name) + 1) ? -EFAULT : 0; - break; - - case SIOCGIFENCAP: - err = put_user(0, (int __user *) arg); - break; - - case SIOCSIFENCAP: - if (get_user(tmp, (int __user *) arg)) { - err = -EFAULT; - break; - } - - sp->mode = tmp; - dev->addr_len = AX25_ADDR_LEN; - dev->hard_header_len = AX25_KISS_HEADER_LEN + - AX25_MAX_HEADER_LEN + 3; - dev->type = ARPHRD_AX25; - - err = 0; - break; - - case SIOCSIFHWADDR: { - char addr[AX25_ADDR_LEN]; - - if (copy_from_user(&addr, - (void __user *)arg, AX25_ADDR_LEN)) { - err = -EFAULT; - break; - } - - netif_tx_lock_bh(dev); - __dev_addr_set(dev, &addr, AX25_ADDR_LEN); - netif_tx_unlock_bh(dev); - err = 0; - break; - } - default: - err = tty_mode_ioctl(tty, cmd, arg); - } - - return err; -} - -static struct tty_ldisc_ops sp_ldisc = { - .owner = THIS_MODULE, - .num = N_6PACK, - .name = "6pack", - .open = sixpack_open, - .close = sixpack_close, - .ioctl = sixpack_ioctl, - .receive_buf = sixpack_receive_buf, - .write_wakeup = sixpack_write_wakeup, -}; - -/* Initialize 6pack control device -- register 6pack line discipline */ - -static int __init sixpack_init_driver(void) -{ - int status; - - /* Register the provided line protocol discipline */ - status = tty_register_ldisc(&sp_ldisc); - if (status) - pr_err("6pack: can't register line discipline (err = %d)\n", status); - - return status; -} - -static void __exit sixpack_exit_driver(void) -{ - tty_unregister_ldisc(&sp_ldisc); -} - -/* encode an AX.25 packet into 6pack */ - -static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, - int length, unsigned char tx_delay) -{ - int count = 0; - unsigned char checksum = 0, buf[400]; - int raw_count = 0; - - tx_buf_raw[raw_count++] = SIXP_PRIO_CMD_MASK | SIXP_TX_MASK; - tx_buf_raw[raw_count++] = SIXP_SEOF; - - buf[0] = tx_delay; - for (count = 1; count < length; count++) - buf[count] = tx_buf[count]; - - for (count = 0; count < length; count++) - checksum += buf[count]; - buf[length] = (unsigned char) 0xff - checksum; - - for (count = 0; count <= length; count++) { - if ((count % 3) == 0) { - tx_buf_raw[raw_count++] = (buf[count] & 0x3f); - tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x30); - } else if ((count % 3) == 1) { - tx_buf_raw[raw_count++] |= (buf[count] & 0x0f); - tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x3c); - } else { - tx_buf_raw[raw_count++] |= (buf[count] & 0x03); - tx_buf_raw[raw_count++] = (buf[count] >> 2); - } - } - if ((length % 3) != 2) - raw_count++; - tx_buf_raw[raw_count++] = SIXP_SEOF; - return raw_count; -} - -/* decode 4 sixpack-encoded bytes into 3 data bytes */ - -static void decode_data(struct sixpack *sp, u8 inbyte) -{ - u8 *buf; - - if (sp->rx_count != 3) { - sp->raw_buf[sp->rx_count++] = inbyte; - - return; - } - - if (sp->rx_count_cooked + 2 >= sizeof(sp->cooked_buf)) { - pr_err("6pack: cooked buffer overrun, data loss\n"); - sp->rx_count = 0; - return; - } - - buf = sp->raw_buf; - sp->cooked_buf[sp->rx_count_cooked++] = - buf[0] | ((buf[1] << 2) & 0xc0); - sp->cooked_buf[sp->rx_count_cooked++] = - (buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0); - sp->cooked_buf[sp->rx_count_cooked++] = - (buf[2] & 0x03) | (inbyte << 2); - sp->rx_count = 0; -} - -/* identify and execute a 6pack priority command byte */ - -static void decode_prio_command(struct sixpack *sp, u8 cmd) -{ - ssize_t actual; - - if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { /* idle ? */ - - /* RX and DCD flags can only be set in the same prio command, - if the DCD flag has been set without the RX flag in the previous - prio command. If DCD has not been set before, something in the - transmission has gone wrong. In this case, RX and DCD are - cleared in order to prevent the decode_data routine from - reading further data that might be corrupt. */ - - if (((sp->status & SIXP_DCD_MASK) == 0) && - ((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) { - if (sp->status != 1) - printk(KERN_DEBUG "6pack: protocol violation\n"); - else - sp->status = 0; - cmd &= ~SIXP_RX_DCD_MASK; - } - sp->status = cmd & SIXP_PRIO_DATA_MASK; - } else { /* output watchdog char if idle */ - if ((sp->status2 != 0) && (sp->duplex == 1)) { - sp->led_state = 0x70; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tx_enable = 1; - actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2); - sp->xleft -= actual; - sp->xhead += actual; - sp->led_state = 0x60; - sp->status2 = 0; - - } - } - - /* needed to trigger the TNC watchdog */ - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - - /* if the state byte has been received, the TNC is present, - so the resync timer can be reset. */ - - if (sp->tnc_state == TNC_IN_SYNC) - mod_timer(&sp->resync_t, jiffies + SIXP_INIT_RESYNC_TIMEOUT); - - sp->status1 = cmd & SIXP_PRIO_DATA_MASK; -} - -/* identify and execute a standard 6pack command byte */ - -static void decode_std_command(struct sixpack *sp, u8 cmd) -{ - u8 checksum = 0, rest = 0; - short i; - - switch (cmd & SIXP_CMD_MASK) { /* normal command */ - case SIXP_SEOF: - if ((sp->rx_count == 0) && (sp->rx_count_cooked == 0)) { - if ((sp->status & SIXP_RX_DCD_MASK) == - SIXP_RX_DCD_MASK) { - sp->led_state = 0x68; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - } - } else { - sp->led_state = 0x60; - /* fill trailing bytes with zeroes */ - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - spin_lock_bh(&sp->rxlock); - rest = sp->rx_count; - if (rest != 0) - for (i = rest; i <= 3; i++) - decode_data(sp, 0); - if (rest == 2) - sp->rx_count_cooked -= 2; - else if (rest == 3) - sp->rx_count_cooked -= 1; - for (i = 0; i < sp->rx_count_cooked; i++) - checksum += sp->cooked_buf[i]; - if (checksum != SIXP_CHKSUM) { - printk(KERN_DEBUG "6pack: bad checksum %2.2x\n", checksum); - } else { - sp->rcount = sp->rx_count_cooked-2; - sp_bump(sp, 0); - } - sp->rx_count_cooked = 0; - spin_unlock_bh(&sp->rxlock); - } - break; - case SIXP_TX_URUN: printk(KERN_DEBUG "6pack: TX underrun\n"); - break; - case SIXP_RX_ORUN: printk(KERN_DEBUG "6pack: RX overrun\n"); - break; - case SIXP_RX_BUF_OVL: - printk(KERN_DEBUG "6pack: RX buffer overflow\n"); - } -} - -/* decode a 6pack packet */ - -static void -sixpack_decode(struct sixpack *sp, const u8 *pre_rbuff, size_t count) -{ - size_t count1; - u8 inbyte; - - for (count1 = 0; count1 < count; count1++) { - inbyte = pre_rbuff[count1]; - if (inbyte == SIXP_FOUND_TNC) { - tnc_set_sync_state(sp, TNC_IN_SYNC); - timer_delete(&sp->resync_t); - } - if ((inbyte & SIXP_PRIO_CMD_MASK) != 0) - decode_prio_command(sp, inbyte); - else if ((inbyte & SIXP_STD_CMD_MASK) != 0) - decode_std_command(sp, inbyte); - else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) { - spin_lock_bh(&sp->rxlock); - decode_data(sp, inbyte); - spin_unlock_bh(&sp->rxlock); - } - } -} - -MODULE_AUTHOR("Ralf Baechle DO1GRB "); -MODULE_DESCRIPTION("6pack driver for AX.25"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_6PACK); - -module_init(sixpack_init_driver); -module_exit(sixpack_exit_driver); diff --git a/drivers/net/hamradio/Kconfig b/drivers/net/hamradio/Kconfig deleted file mode 100644 index 36a9aade9f33..000000000000 --- a/drivers/net/hamradio/Kconfig +++ /dev/null @@ -1,162 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config MKISS - tristate "Serial port KISS driver" - depends on AX25 && TTY - select CRC16 - help - KISS is a protocol used for the exchange of data between a computer - and a Terminal Node Controller (a small embedded system commonly - used for networking over AX.25 amateur radio connections; it - connects the computer's serial port with the radio's microphone - input and speaker output). - - Although KISS is less advanced than the 6pack protocol, it has - the advantage that it is already supported by most modern TNCs - without the need for a firmware upgrade. - - To compile this driver as a module, choose M here: the module - will be called mkiss. - -config 6PACK - tristate "Serial port 6PACK driver" - depends on AX25 && TTY - help - 6pack is a transmission protocol for the data exchange between your - PC and your TNC (the Terminal Node Controller acts as a kind of - modem connecting your computer's serial port to your radio's - microphone input and speaker output). This protocol can be used as - an alternative to KISS for networking over AX.25 amateur radio - connections, but it has some extended functionality. - - Note that this driver is still experimental and might cause - problems. For details about the features and the usage of the - driver, read . - - To compile this driver as a module, choose M here: the module - will be called 6pack. - -config BPQETHER - tristate "BPQ Ethernet driver" - depends on AX25 - help - AX.25 is the protocol used for computer communication over amateur - radio. If you say Y here, you will be able to send and receive AX.25 - traffic over Ethernet (also called "BPQ AX.25"), which could be - useful if some other computer on your local network has a direct - amateur radio connection. - -config SCC - tristate "Z8530 SCC driver" - depends on ISA && AX25 - help - These cards are used to connect your Linux box to an amateur radio - in order to communicate with other computers. If you want to use - this, read - - and the AX25-HOWTO, available from - . Also make sure to say Y - to "Amateur Radio AX.25 Level 2" support. - - To compile this driver as a module, choose M here: the module - will be called scc. - -config SCC_DELAY - bool "additional delay for PA0HZP OptoSCC compatible boards" - depends on SCC - help - Say Y here if you experience problems with the SCC driver not - working properly; please read - - for details. - - If unsure, say N. - -config SCC_TRXECHO - bool "support for TRX that feedback the tx signal to rx" - depends on SCC - help - Some transmitters feed the transmitted signal back to the receive - line. Say Y here to foil this by explicitly disabling the receiver - during data transmission. - - If in doubt, say Y. - -config BAYCOM_SER_FDX - tristate "BAYCOM ser12 fullduplex driver for AX.25" - depends on AX25 && HAS_IOPORT - select CRC_CCITT - help - This is one of two drivers for Baycom style simple amateur radio - modems that connect to a serial interface. The driver supports the - ser12 design in full-duplex mode. In addition, it allows the - baudrate to be set between 300 and 4800 baud (however not all modems - support all baudrates). This is the preferred driver. The next - driver, "BAYCOM ser12 half-duplex driver for AX.25" is the old - driver and still provided in case this driver does not work with - your serial interface chip. To configure the driver, use the sethdlc - utility available in the standard ax25 utilities package. - For more information on the modems, see - . - - To compile this driver as a module, choose M here: the module - will be called baycom_ser_fdx. This is recommended. - -config BAYCOM_SER_HDX - tristate "BAYCOM ser12 halfduplex driver for AX.25" - depends on AX25 && HAS_IOPORT - select CRC_CCITT - help - This is one of two drivers for Baycom style simple amateur radio - modems that connect to a serial interface. The driver supports the - ser12 design in half-duplex mode. This is the old driver. It is - still provided in case your serial interface chip does not work with - the full-duplex driver. This driver is deprecated. To configure - the driver, use the sethdlc utility available in the standard ax25 - utilities package. For more information on the modems, see - . - - To compile this driver as a module, choose M here: the module - will be called baycom_ser_hdx. This is recommended. - -config BAYCOM_PAR - tristate "BAYCOM picpar and par96 driver for AX.25" - depends on PARPORT && AX25 - select CRC_CCITT - help - This is a driver for Baycom style simple amateur radio modems that - connect to a parallel interface. The driver supports the picpar and - par96 designs. To configure the driver, use the sethdlc utility - available in the standard ax25 utilities package. - For more information on the modems, see - . - - To compile this driver as a module, choose M here: the module - will be called baycom_par. This is recommended. - -config BAYCOM_EPP - tristate "BAYCOM epp driver for AX.25" - depends on PARPORT && AX25 && !64BIT - select CRC_CCITT - help - This is a driver for Baycom style simple amateur radio modems that - connect to a parallel interface. The driver supports the EPP - designs. To configure the driver, use the sethdlc utility available - in the standard ax25 utilities package. - For more information on the modems, see - . - - To compile this driver as a module, choose M here: the module - will be called baycom_epp. This is recommended. - -config YAM - tristate "YAM driver for AX.25" - depends on AX25 && HAS_IOPORT - help - The YAM is a modem for packet radio which connects to the serial - port and includes some of the functions of a Terminal Node - Controller. If you have one of those, say Y here. - - To compile this driver as a module, choose M here: the module - will be called yam. - - diff --git a/drivers/net/hamradio/Makefile b/drivers/net/hamradio/Makefile deleted file mode 100644 index 25fc400369ba..000000000000 --- a/drivers/net/hamradio/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the Linux AX.25 and HFMODEM device drivers. -# -# -# 19971130 Moved the amateur radio related network drivers from -# drivers/net/ to drivers/hamradio for easier maintenance. -# Joerg Reuter DL1BKE -# -# 20000806 Rewritten to use lists instead of if-statements. -# Christoph Hellwig -# - -obj-$(CONFIG_SCC) += scc.o -obj-$(CONFIG_MKISS) += mkiss.o -obj-$(CONFIG_6PACK) += 6pack.o -obj-$(CONFIG_YAM) += yam.o -obj-$(CONFIG_BPQETHER) += bpqether.o -obj-$(CONFIG_BAYCOM_SER_FDX) += baycom_ser_fdx.o hdlcdrv.o -obj-$(CONFIG_BAYCOM_SER_HDX) += baycom_ser_hdx.o hdlcdrv.o -obj-$(CONFIG_BAYCOM_PAR) += baycom_par.o hdlcdrv.o -obj-$(CONFIG_BAYCOM_EPP) += baycom_epp.o hdlcdrv.o diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c deleted file mode 100644 index 5fda7a0fcce0..000000000000 --- a/drivers/net/hamradio/baycom_epp.c +++ /dev/null @@ -1,1316 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_epp.c -- baycom epp radio modem driver. - * - * Copyright (C) 1998-2000 - * Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * History: - * 0.1 xx.xx.1998 Initial version by Matthias Welwarsky (dg2fef) - * 0.2 21.04.1998 Massive rework by Thomas Sailer - * Integrated FPGA EPP modem configuration routines - * 0.3 11.05.1998 Took FPGA config out and moved it into a separate program - * 0.4 26.07.1999 Adapted to new lowlevel parport driver interface - * 0.5 03.08.1999 adapt to Linus' new __setup/__initcall - * removed some pre-2.2 kernel compatibility cruft - * 0.6 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts - * 0.7 12.02.2000 adapted to softnet driver interface - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG -#define BAYCOM_MAGIC 19730510 - -/* --------------------------------------------------------------------- */ - -static const char paranoia_str[] = KERN_ERR - "baycom_epp: bad magic number for hdlcdrv_state struct in routine %s\n"; - -static const char bc_drvname[] = "baycom_epp"; -static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_epp: version 0.7\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -/* EPP status register */ -#define EPP_DCDBIT 0x80 -#define EPP_PTTBIT 0x08 -#define EPP_NREF 0x01 -#define EPP_NRAEF 0x02 -#define EPP_NRHF 0x04 -#define EPP_NTHF 0x20 -#define EPP_NTAEF 0x10 -#define EPP_NTEF EPP_PTTBIT - -/* EPP control register */ -#define EPP_TX_FIFO_ENABLE 0x10 -#define EPP_RX_FIFO_ENABLE 0x08 -#define EPP_MODEM_ENABLE 0x20 -#define EPP_LEDS 0xC0 -#define EPP_IRQ_ENABLE 0x10 - -/* LPT registers */ -#define LPTREG_ECONTROL 0x402 -#define LPTREG_CONFIGB 0x401 -#define LPTREG_CONFIGA 0x400 -#define LPTREG_EPPDATA 0x004 -#define LPTREG_EPPADDR 0x003 -#define LPTREG_CONTROL 0x002 -#define LPTREG_STATUS 0x001 -#define LPTREG_DATA 0x000 - -/* LPT control register */ -#define LPTCTRL_PROGRAM 0x04 /* 0 to reprogram */ -#define LPTCTRL_WRITE 0x01 -#define LPTCTRL_ADDRSTB 0x08 -#define LPTCTRL_DATASTB 0x02 -#define LPTCTRL_INTEN 0x10 - -/* LPT status register */ -#define LPTSTAT_SHIFT_NINTR 6 -#define LPTSTAT_WAIT 0x80 -#define LPTSTAT_NINTR (1<0;len--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff]; - crc ^= 0xffff; - *buffer++ = crc; - *buffer++ = crc >> 8; -} -#endif - -/*---------------------------------------------------------------------------*/ - -static inline int check_crc_ccitt(const unsigned char *buf, int cnt) -{ - return (crc_ccitt(0xffff, buf, cnt) & 0xffff) == 0xf0b8; -} - -/*---------------------------------------------------------------------------*/ - -static inline int calc_crc_ccitt(const unsigned char *buf, int cnt) -{ - return (crc_ccitt(0xffff, buf, cnt) ^ 0xffff) & 0xffff; -} - -/* ---------------------------------------------------------------------- */ - -#define tenms_to_flags(bc,tenms) ((tenms * bc->bitrate) / 800) - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* ---------------------------------------------------------------------- */ -/* - * eppconfig_path should be setable via /proc/sys. - */ - -static char const eppconfig_path[] = "/usr/sbin/eppfpga"; - -static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL }; - -/* eppconfig: called during ifconfig up to configure the modem */ -static int eppconfig(struct baycom_state *bc) -{ - char modearg[256]; - char portarg[16]; - char *argv[] = { - (char *)eppconfig_path, - "-s", - "-p", portarg, - "-m", modearg, - NULL }; - - /* set up arguments */ - sprintf(modearg, "%sclk,%smodem,fclk=%d,bps=%d,divider=%d%s,extstat", - bc->cfg.intclk ? "int" : "ext", - bc->cfg.extmodem ? "ext" : "int", bc->cfg.fclk, bc->cfg.bps, - (bc->cfg.fclk + 8 * bc->cfg.bps) / (16 * bc->cfg.bps), - bc->cfg.loopback ? ",loopback" : ""); - sprintf(portarg, "%ld", bc->pdev->port->base); - printk(KERN_DEBUG "%s: %s -s -p %s -m %s\n", bc_drvname, eppconfig_path, portarg, modearg); - - return call_usermodehelper(eppconfig_path, argv, envp, UMH_WAIT_PROC); -} - -/* ---------------------------------------------------------------------- */ - -static inline void do_kiss_params(struct baycom_state *bc, - unsigned char *data, unsigned long len) -{ - -#ifdef KISS_VERBOSE -#define PKP(a,b) printk(KERN_INFO "baycomm_epp: channel params: " a "\n", b) -#else /* KISS_VERBOSE */ -#define PKP(a,b) -#endif /* KISS_VERBOSE */ - - if (len < 2) - return; - switch(data[0]) { - case PARAM_TXDELAY: - bc->ch_params.tx_delay = data[1]; - PKP("TX delay = %ums", 10 * bc->ch_params.tx_delay); - break; - case PARAM_PERSIST: - bc->ch_params.ppersist = data[1]; - PKP("p persistence = %u", bc->ch_params.ppersist); - break; - case PARAM_SLOTTIME: - bc->ch_params.slottime = data[1]; - PKP("slot time = %ums", bc->ch_params.slottime); - break; - case PARAM_TXTAIL: - bc->ch_params.tx_tail = data[1]; - PKP("TX tail = %ums", bc->ch_params.tx_tail); - break; - case PARAM_FULLDUP: - bc->ch_params.fulldup = !!data[1]; - PKP("%s duplex", bc->ch_params.fulldup ? "full" : "half"); - break; - default: - break; - } -#undef PKP -} - -/* --------------------------------------------------------------------- */ - -static void encode_hdlc(struct baycom_state *bc) -{ - struct sk_buff *skb; - unsigned char *wp, *bp; - int pkt_len; - unsigned bitstream, notbitstream, bitbuf, numbit, crc; - unsigned char crcarr[2]; - int j; - - if (bc->hdlctx.bufcnt > 0) - return; - skb = bc->skb; - if (!skb) - return; - bc->skb = NULL; - pkt_len = skb->len-1; /* strip KISS byte */ - wp = bc->hdlctx.buf; - bp = skb->data+1; - crc = calc_crc_ccitt(bp, pkt_len); - crcarr[0] = crc; - crcarr[1] = crc >> 8; - *wp++ = 0x7e; - bitstream = bitbuf = numbit = 0; - while (pkt_len > -2) { - bitstream >>= 8; - bitstream |= ((unsigned int)*bp) << 8; - bitbuf |= ((unsigned int)*bp) << numbit; - notbitstream = ~bitstream; - bp++; - pkt_len--; - if (!pkt_len) - bp = crcarr; - for (j = 0; j < 8; j++) - if (unlikely(!(notbitstream & (0x1f0 << j)))) { - bitstream &= ~(0x100 << j); - bitbuf = (bitbuf & (((2 << j) << numbit) - 1)) | - ((bitbuf & ~(((2 << j) << numbit) - 1)) << 1); - numbit++; - notbitstream = ~bitstream; - } - numbit += 8; - while (numbit >= 8) { - *wp++ = bitbuf; - bitbuf >>= 8; - numbit -= 8; - } - } - bitbuf |= 0x7e7e << numbit; - numbit += 16; - while (numbit >= 8) { - *wp++ = bitbuf; - bitbuf >>= 8; - numbit -= 8; - } - bc->hdlctx.bufptr = bc->hdlctx.buf; - bc->hdlctx.bufcnt = wp - bc->hdlctx.buf; - dev_kfree_skb(skb); - bc->dev->stats.tx_packets++; -} - -/* ---------------------------------------------------------------------- */ - -static int transmit(struct baycom_state *bc, int cnt, unsigned char stat) -{ - struct parport *pp = bc->pdev->port; - unsigned char tmp[128]; - int i, j; - - if (bc->hdlctx.state == tx_tail && !(stat & EPP_PTTBIT)) - bc->hdlctx.state = tx_idle; - if (bc->hdlctx.state == tx_idle && bc->hdlctx.calibrate <= 0) { - if (bc->hdlctx.bufcnt <= 0) - encode_hdlc(bc); - if (bc->hdlctx.bufcnt <= 0) - return 0; - if (!bc->ch_params.fulldup) { - if (!(stat & EPP_DCDBIT)) { - bc->hdlctx.slotcnt = bc->ch_params.slottime; - return 0; - } - if ((--bc->hdlctx.slotcnt) > 0) - return 0; - bc->hdlctx.slotcnt = bc->ch_params.slottime; - if (get_random_u8() > bc->ch_params.ppersist) - return 0; - } - } - if (bc->hdlctx.state == tx_idle && bc->hdlctx.bufcnt > 0) { - bc->hdlctx.state = tx_keyup; - bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_delay); - bc->ptt_keyed++; - } - while (cnt > 0) { - switch (bc->hdlctx.state) { - case tx_keyup: - i = min_t(int, cnt, bc->hdlctx.flags); - cnt -= i; - bc->hdlctx.flags -= i; - if (bc->hdlctx.flags <= 0) - bc->hdlctx.state = tx_data; - memset(tmp, 0x7e, sizeof(tmp)); - while (i > 0) { - j = (i > sizeof(tmp)) ? sizeof(tmp) : i; - if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) - return -1; - i -= j; - } - break; - - case tx_data: - if (bc->hdlctx.bufcnt <= 0) { - encode_hdlc(bc); - if (bc->hdlctx.bufcnt <= 0) { - bc->hdlctx.state = tx_tail; - bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_tail); - break; - } - } - i = min_t(int, cnt, bc->hdlctx.bufcnt); - bc->hdlctx.bufcnt -= i; - cnt -= i; - if (i != pp->ops->epp_write_data(pp, bc->hdlctx.bufptr, i, 0)) - return -1; - bc->hdlctx.bufptr += i; - break; - - case tx_tail: - encode_hdlc(bc); - if (bc->hdlctx.bufcnt > 0) { - bc->hdlctx.state = tx_data; - break; - } - i = min_t(int, cnt, bc->hdlctx.flags); - if (i) { - cnt -= i; - bc->hdlctx.flags -= i; - memset(tmp, 0x7e, sizeof(tmp)); - while (i > 0) { - j = (i > sizeof(tmp)) ? sizeof(tmp) : i; - if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) - return -1; - i -= j; - } - break; - } - fallthrough; - - default: - if (bc->hdlctx.calibrate <= 0) - return 0; - i = min_t(int, cnt, bc->hdlctx.calibrate); - cnt -= i; - bc->hdlctx.calibrate -= i; - memset(tmp, 0, sizeof(tmp)); - while (i > 0) { - j = (i > sizeof(tmp)) ? sizeof(tmp) : i; - if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) - return -1; - i -= j; - } - break; - } - } - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static void do_rxpacket(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct sk_buff *skb; - unsigned char *cp; - unsigned pktlen; - - if (bc->hdlcrx.bufcnt < 4) - return; - if (!check_crc_ccitt(bc->hdlcrx.buf, bc->hdlcrx.bufcnt)) - return; - pktlen = bc->hdlcrx.bufcnt-2+1; /* KISS kludge */ - if (!(skb = dev_alloc_skb(pktlen))) { - printk("%s: memory squeeze, dropping packet\n", dev->name); - dev->stats.rx_dropped++; - return; - } - cp = skb_put(skb, pktlen); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, bc->hdlcrx.buf, pktlen - 1); - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; -} - -static int receive(struct net_device *dev, int cnt) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp = bc->pdev->port; - unsigned int bitbuf, notbitstream, bitstream, numbits, state; - unsigned char tmp[128]; - unsigned char *cp; - int cnt2, ret = 0; - int j; - - numbits = bc->hdlcrx.numbits; - state = bc->hdlcrx.state; - bitstream = bc->hdlcrx.bitstream; - bitbuf = bc->hdlcrx.bitbuf; - while (cnt > 0) { - cnt2 = (cnt > sizeof(tmp)) ? sizeof(tmp) : cnt; - cnt -= cnt2; - if (cnt2 != pp->ops->epp_read_data(pp, tmp, cnt2, 0)) { - ret = -1; - break; - } - cp = tmp; - for (; cnt2 > 0; cnt2--, cp++) { - bitstream >>= 8; - bitstream |= (*cp) << 8; - bitbuf >>= 8; - bitbuf |= (*cp) << 8; - numbits += 8; - notbitstream = ~bitstream; - for (j = 0; j < 8; j++) { - - /* flag or abort */ - if (unlikely(!(notbitstream & (0x0fc << j)))) { - - /* abort received */ - if (!(notbitstream & (0x1fc << j))) - state = 0; - - /* flag received */ - else if ((bitstream & (0x1fe << j)) == (0x0fc << j)) { - if (state) - do_rxpacket(dev); - bc->hdlcrx.bufcnt = 0; - bc->hdlcrx.bufptr = bc->hdlcrx.buf; - state = 1; - numbits = 7-j; - } - } - - /* stuffed bit */ - else if (unlikely((bitstream & (0x1f8 << j)) == (0xf8 << j))) { - numbits--; - bitbuf = (bitbuf & ((~0xff) << j)) | ((bitbuf & ~((~0xff) << j)) << 1); - } - } - while (state && numbits >= 8) { - if (bc->hdlcrx.bufcnt >= TXBUFFER_SIZE) { - state = 0; - } else { - *(bc->hdlcrx.bufptr)++ = bitbuf >> (16-numbits); - bc->hdlcrx.bufcnt++; - numbits -= 8; - } - } - } - } - bc->hdlcrx.numbits = numbits; - bc->hdlcrx.state = state; - bc->hdlcrx.bitstream = bitstream; - bc->hdlcrx.bitbuf = bitbuf; - return ret; -} - -/* --------------------------------------------------------------------- */ - -#define GETTICK(x) \ -({ \ - x = (unsigned int)get_cycles(); \ -}) - -static void epp_bh(struct work_struct *work) -{ - struct net_device *dev; - struct baycom_state *bc; - struct parport *pp; - unsigned char stat; - unsigned char tmp[2]; - unsigned int time1 = 0, time2 = 0, time3 = 0; - int cnt, cnt2; - - bc = container_of(work, struct baycom_state, run_work.work); - dev = bc->dev; - if (!bc->work_running) - return; - baycom_int_freq(bc); - pp = bc->pdev->port; - /* update status */ - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - bc->stat = stat; - bc->debug_vals.last_pllcorr = stat; - GETTICK(time1); - if (bc->modem == EPP_FPGAEXTSTATUS) { - /* get input count */ - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, tmp, 2, 0) != 2) - goto epptimeout; - cnt = tmp[0] | (tmp[1] << 8); - cnt &= 0x7fff; - /* get output count */ - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, tmp, 2, 0) != 2) - goto epptimeout; - cnt2 = tmp[0] | (tmp[1] << 8); - cnt2 = 16384 - (cnt2 & 0x7fff); - /* return to normal */ - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - if (transmit(bc, cnt2, stat)) - goto epptimeout; - GETTICK(time2); - if (receive(dev, cnt)) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - bc->stat = stat; - } else { - /* try to tx */ - switch (stat & (EPP_NTAEF|EPP_NTHF)) { - case EPP_NTHF: - cnt = 2048 - 256; - break; - - case EPP_NTAEF: - cnt = 2048 - 1793; - break; - - case 0: - cnt = 0; - break; - - default: - cnt = 2048 - 1025; - break; - } - if (transmit(bc, cnt, stat)) - goto epptimeout; - GETTICK(time2); - /* do receiver */ - while ((stat & (EPP_NRAEF|EPP_NRHF)) != EPP_NRHF) { - switch (stat & (EPP_NRAEF|EPP_NRHF)) { - case EPP_NRAEF: - cnt = 1025; - break; - - case 0: - cnt = 1793; - break; - - default: - cnt = 256; - break; - } - if (receive(dev, cnt)) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - } - cnt = 0; - if (bc->bitrate < 50000) - cnt = 256; - else if (bc->bitrate < 100000) - cnt = 128; - while (cnt > 0 && stat & EPP_NREF) { - if (receive(dev, 1)) - goto epptimeout; - cnt--; - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - } - } - GETTICK(time3); -#ifdef BAYCOM_DEBUG - bc->debug_vals.mod_cycles = time2 - time1; - bc->debug_vals.demod_cycles = time3 - time2; -#endif /* BAYCOM_DEBUG */ - schedule_delayed_work(&bc->run_work, 1); - if (!bc->skb) - netif_wake_queue(dev); - return; - epptimeout: - printk(KERN_ERR "%s: EPP timeout!\n", bc_drvname); -} - -/* ---------------------------------------------------------------------- */ -/* - * ===================== network driver interface ========================= - */ - -static netdev_tx_t baycom_send_packet(struct sk_buff *skb, struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (skb->data[0] != 0) { - do_kiss_params(bc, skb->data, skb->len); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - if (bc->skb) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - /* strip KISS byte */ - if (skb->len >= HDLCDRV_MAXFLEN+1 || skb->len < 3) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - netif_stop_queue(dev); - bc->skb = skb; - return NETDEV_TX_OK; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *)addr; - - /* addr is an AX.25 shifted ASCII mac address */ - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void epp_wakeup(void *handle) -{ - struct net_device *dev = (struct net_device *)handle; - struct baycom_state *bc = netdev_priv(dev); - - printk(KERN_DEBUG "baycom_epp: %s: why am I being woken up?\n", dev->name); - if (!parport_claim(bc->pdev)) - printk(KERN_DEBUG "baycom_epp: %s: I'm broken.\n", dev->name); -} - -/* --------------------------------------------------------------------- */ - -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ - -static int epp_open(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp = parport_find_base(dev->base_addr); - unsigned int i, j; - unsigned char tmp[128]; - unsigned char stat; - unsigned long tstart; - struct pardev_cb par_cb; - - if (!pp) { - printk(KERN_ERR "%s: parport at 0x%lx unknown\n", bc_drvname, dev->base_addr); - return -ENXIO; - } -#if 0 - if (pp->irq < 0) { - printk(KERN_ERR "%s: parport at 0x%lx has no irq\n", bc_drvname, pp->base); - parport_put_port(pp); - return -ENXIO; - } -#endif - if ((~pp->modes) & (PARPORT_MODE_TRISTATE | PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) { - printk(KERN_ERR "%s: parport at 0x%lx cannot be used\n", - bc_drvname, pp->base); - parport_put_port(pp); - return -EIO; - } - memset(&bc->modem, 0, sizeof(bc->modem)); - memset(&par_cb, 0, sizeof(par_cb)); - par_cb.wakeup = epp_wakeup; - par_cb.private = (void *)dev; - par_cb.flags = PARPORT_DEV_EXCL; - for (i = 0; i < NR_PORTS; i++) - if (baycom_device[i] == dev) - break; - - if (i == NR_PORTS) { - pr_err("%s: no device found\n", bc_drvname); - parport_put_port(pp); - return -ENODEV; - } - - bc->pdev = parport_register_dev_model(pp, dev->name, &par_cb, i); - parport_put_port(pp); - if (!bc->pdev) { - printk(KERN_ERR "%s: cannot register parport at 0x%lx\n", bc_drvname, pp->base); - return -ENXIO; - } - if (parport_claim(bc->pdev)) { - printk(KERN_ERR "%s: parport at 0x%lx busy\n", bc_drvname, pp->base); - parport_unregister_device(bc->pdev); - return -EBUSY; - } - dev->irq = /*pp->irq*/ 0; - INIT_DELAYED_WORK(&bc->run_work, epp_bh); - bc->work_running = 1; - bc->modem = EPP_CONVENTIONAL; - if (eppconfig(bc)) - printk(KERN_INFO "%s: no FPGA detected, assuming conventional EPP modem\n", bc_drvname); - else - bc->modem = /*EPP_FPGA*/ EPP_FPGAEXTSTATUS; - parport_write_control(pp, LPTCTRL_PROGRAM); /* prepare EPP mode; we aren't using interrupts */ - /* reset the modem */ - tmp[0] = 0; - tmp[1] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE; - if (pp->ops->epp_write_addr(pp, tmp, 2, 0) != 2) - goto epptimeout; - /* autoprobe baud rate */ - tstart = jiffies; - i = 0; - while (time_before(jiffies, tstart + HZ/3)) { - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - if ((stat & (EPP_NRAEF|EPP_NRHF)) == EPP_NRHF) { - schedule(); - continue; - } - if (pp->ops->epp_read_data(pp, tmp, 128, 0) != 128) - goto epptimeout; - if (pp->ops->epp_read_data(pp, tmp, 128, 0) != 128) - goto epptimeout; - i += 256; - } - for (j = 0; j < 256; j++) { - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - if (!(stat & EPP_NREF)) - break; - if (pp->ops->epp_read_data(pp, tmp, 1, 0) != 1) - goto epptimeout; - i++; - } - tstart = jiffies - tstart; - bc->bitrate = i * (8 * HZ) / tstart; - j = 1; - i = bc->bitrate >> 3; - while (j < 7 && i > 150) { - j++; - i >>= 1; - } - printk(KERN_INFO "%s: autoprobed bitrate: %d int divider: %d int rate: %d\n", - bc_drvname, bc->bitrate, j, bc->bitrate >> (j+2)); - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE/*|j*/; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - /* - * initialise hdlc variables - */ - bc->hdlcrx.state = 0; - bc->hdlcrx.numbits = 0; - bc->hdlctx.state = tx_idle; - bc->hdlctx.bufcnt = 0; - bc->hdlctx.slotcnt = bc->ch_params.slottime; - bc->hdlctx.calibrate = 0; - /* start the bottom half stuff */ - schedule_delayed_work(&bc->run_work, 1); - netif_start_queue(dev); - return 0; - - epptimeout: - printk(KERN_ERR "%s: epp timeout during bitrate probe\n", bc_drvname); - parport_write_control(pp, 0); /* reset the adapter */ - parport_release(bc->pdev); - parport_unregister_device(bc->pdev); - return -EIO; -} - -/* --------------------------------------------------------------------- */ - -static int epp_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp = bc->pdev->port; - unsigned char tmp[1]; - - bc->work_running = 0; - cancel_delayed_work_sync(&bc->run_work); - bc->stat = EPP_DCDBIT; - tmp[0] = 0; - pp->ops->epp_write_addr(pp, tmp, 1, 0); - parport_write_control(pp, 0); /* reset the adapter */ - parport_release(bc->pdev); - parport_unregister_device(bc->pdev); - dev_kfree_skb(bc->skb); - bc->skb = NULL; - printk(KERN_INFO "%s: close epp at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - const char *cp; - - if (strstr(modestr,"intclk")) - bc->cfg.intclk = 1; - if (strstr(modestr,"extclk")) - bc->cfg.intclk = 0; - if (strstr(modestr,"intmodem")) - bc->cfg.extmodem = 0; - if (strstr(modestr,"extmodem")) - bc->cfg.extmodem = 1; - if (strstr(modestr,"loopback")) - bc->cfg.loopback = 1; - if (strstr(modestr, "noloopback")) - bc->cfg.loopback = 0; - if ((cp = strstr(modestr,"fclk="))) { - bc->cfg.fclk = simple_strtoul(cp+5, NULL, 0); - if (bc->cfg.fclk < 1000000) - bc->cfg.fclk = 1000000; - if (bc->cfg.fclk > 25000000) - bc->cfg.fclk = 25000000; - } - if ((cp = strstr(modestr,"bps="))) { - bc->cfg.bps = simple_strtoul(cp+4, NULL, 0); - if (bc->cfg.bps < 1000) - bc->cfg.bps = 1000; - if (bc->cfg.bps > 1500000) - bc->cfg.bps = 1500000; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd) -{ - struct baycom_state *bc = netdev_priv(dev); - struct hdlcdrv_ioctl hi; - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (copy_from_user(&hi, data, sizeof(hi))) - return -EFAULT; - switch (hi.cmd) { - default: - return -ENOIOCTLCMD; - - case HDLCDRVCTL_GETCHANNELPAR: - hi.data.cp.tx_delay = bc->ch_params.tx_delay; - hi.data.cp.tx_tail = bc->ch_params.tx_tail; - hi.data.cp.slottime = bc->ch_params.slottime; - hi.data.cp.ppersist = bc->ch_params.ppersist; - hi.data.cp.fulldup = bc->ch_params.fulldup; - break; - - case HDLCDRVCTL_SETCHANNELPAR: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - bc->ch_params.tx_delay = hi.data.cp.tx_delay; - bc->ch_params.tx_tail = hi.data.cp.tx_tail; - bc->ch_params.slottime = hi.data.cp.slottime; - bc->ch_params.ppersist = hi.data.cp.ppersist; - bc->ch_params.fulldup = hi.data.cp.fulldup; - bc->hdlctx.slotcnt = 1; - return 0; - - case HDLCDRVCTL_GETMODEMPAR: - hi.data.mp.iobase = dev->base_addr; - hi.data.mp.irq = dev->irq; - hi.data.mp.dma = dev->dma; - hi.data.mp.dma2 = 0; - hi.data.mp.seriobase = 0; - hi.data.mp.pariobase = 0; - hi.data.mp.midiiobase = 0; - break; - - case HDLCDRVCTL_SETMODEMPAR: - if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev)) - return -EACCES; - dev->base_addr = hi.data.mp.iobase; - dev->irq = /*hi.data.mp.irq*/0; - dev->dma = /*hi.data.mp.dma*/0; - return 0; - - case HDLCDRVCTL_GETSTAT: - hi.data.cs.ptt = !!(bc->stat & EPP_PTTBIT); - hi.data.cs.dcd = !(bc->stat & EPP_DCDBIT); - hi.data.cs.ptt_keyed = bc->ptt_keyed; - hi.data.cs.tx_packets = dev->stats.tx_packets; - hi.data.cs.tx_errors = dev->stats.tx_errors; - hi.data.cs.rx_packets = dev->stats.rx_packets; - hi.data.cs.rx_errors = dev->stats.rx_errors; - break; - - case HDLCDRVCTL_OLDGETSTAT: - hi.data.ocs.ptt = !!(bc->stat & EPP_PTTBIT); - hi.data.ocs.dcd = !(bc->stat & EPP_DCDBIT); - hi.data.ocs.ptt_keyed = bc->ptt_keyed; - break; - - case HDLCDRVCTL_CALIBRATE: - if (!capable(CAP_SYS_RAWIO)) - return -EACCES; - bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8; - return 0; - - case HDLCDRVCTL_DRIVERNAME: - strscpy_pad(hi.data.drivername, "baycom_epp"); - break; - - case HDLCDRVCTL_GETMODE: - sprintf(hi.data.modename, "%sclk,%smodem,fclk=%d,bps=%d%s", - bc->cfg.intclk ? "int" : "ext", - bc->cfg.extmodem ? "ext" : "int", bc->cfg.fclk, bc->cfg.bps, - bc->cfg.loopback ? ",loopback" : ""); - break; - - case HDLCDRVCTL_SETMODE: - if (!capable(CAP_NET_ADMIN) || netif_running(dev)) - return -EACCES; - hi.data.modename[sizeof(hi.data.modename)-1] = '\0'; - return baycom_setmode(bc, hi.data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy_pad(hi.data.modename, "intclk,extclk,intmodem,extmodem,divider=x"); - break; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE; - - } - if (copy_to_user(data, &hi, sizeof(hi))) - return -EFAULT; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static const struct net_device_ops baycom_netdev_ops = { - .ndo_open = epp_open, - .ndo_stop = epp_close, - .ndo_siocdevprivate = baycom_siocdevprivate, - .ndo_start_xmit = baycom_send_packet, - .ndo_set_mac_address = baycom_set_mac_address, -}; - -/* - * Check for a network adaptor of this type, and return '0' if one exists. - * If dev->base_addr == 0, probe all likely locations. - * If dev->base_addr == 1, always return failure. - * If dev->base_addr == 2, allocate space for the device and return success - * (detachable devices only). - */ -static void baycom_probe(struct net_device *dev) -{ - const struct hdlcdrv_channel_params dflt_ch_params = { - 20, 2, 10, 40, 0 - }; - struct baycom_state *bc; - - /* - * not a real probe! only initialize data structures - */ - bc = netdev_priv(dev); - /* - * initialize the baycom_state struct - */ - bc->ch_params = dflt_ch_params; - bc->ptt_keyed = 0; - - /* - * initialize the device struct - */ - - /* Fill in the fields of the device structure */ - bc->skb = NULL; - - dev->netdev_ops = &baycom_netdev_ops; - dev->header_ops = &ax25_header_ops; - - dev->type = ARPHRD_AX25; /* AF_AX25 device */ - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ - dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&null_ax25_address); - dev->tx_queue_len = 16; - - /* New style flags */ - dev->flags = 0; -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "", }; -static int iobase[NR_PORTS] = { 0x378, }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom epp amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int baycom_epp_par_probe(struct pardevice *par_dev) -{ - struct device_driver *drv = par_dev->dev.driver; - int len = strlen(drv->name); - - if (strncmp(par_dev->name, drv->name, len)) - return -ENODEV; - - return 0; -} - -static struct parport_driver baycom_epp_par_driver = { - .name = "bce", - .probe = baycom_epp_par_probe, -}; - -static void __init baycom_epp_dev_setup(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - /* - * initialize part of the baycom_state struct - */ - bc->dev = dev; - bc->magic = BAYCOM_MAGIC; - bc->cfg.fclk = 19666600; - bc->cfg.bps = 9600; - /* - * initialize part of the device struct - */ - baycom_probe(dev); -} - -static int __init init_baycomepp(void) -{ - int i, found = 0, ret; - char set_hw = 1; - - printk(bc_drvinfo); - - ret = parport_register_driver(&baycom_epp_par_driver); - if (ret) - return ret; - - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - - dev = alloc_netdev(sizeof(struct baycom_state), "bce%d", - NET_NAME_UNKNOWN, baycom_epp_dev_setup); - - if (!dev) { - printk(KERN_WARNING "bce%d : out of memory\n", i); - return found ? 0 : -ENOMEM; - } - - sprintf(dev->name, "bce%d", i); - dev->base_addr = iobase[i]; - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = 0; - - if (register_netdev(dev)) { - printk(KERN_WARNING "%s: cannot register net device %s\n", bc_drvname, dev->name); - free_netdev(dev); - break; - } - if (set_hw && baycom_setmode(netdev_priv(dev), mode[i])) - set_hw = 0; - baycom_device[i] = dev; - found++; - } - - if (found == 0) { - parport_unregister_driver(&baycom_epp_par_driver); - return -ENXIO; - } - - return 0; -} - -static void __exit cleanup_baycomepp(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - - if (dev) { - struct baycom_state *bc = netdev_priv(dev); - if (bc->magic == BAYCOM_MAGIC) { - unregister_netdev(dev); - free_netdev(dev); - } else - printk(paranoia_str, "cleanup_module"); - } - } - parport_unregister_driver(&baycom_epp_par_driver); -} - -module_init(init_baycomepp); -module_exit(cleanup_baycomepp); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_epp=io,mode - * mode: fpga config options - */ - -static int __init baycom_epp_setup(char *str) -{ - static unsigned __initdata nr_dev = 0; - int ints[2]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 2, ints); - if (ints[0] < 1) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - nr_dev++; - return 1; -} - -__setup("baycom_epp=", baycom_epp_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c deleted file mode 100644 index f03797103c6a..000000000000 --- a/drivers/net/hamradio/baycom_par.c +++ /dev/null @@ -1,598 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_par.c -- baycom par96 and picpar radio modem driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * Supported modems - * - * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. - * The modem does all the filtering and regenerates the receiver clock. - * Data is transferred from and to the PC via a shift register. - * The shift register is filled with 16 bits and an interrupt is - * signalled. The PC then empties the shift register in a burst. This - * modem connects to the parallel port, hence the name. The modem - * leaves the implementation of the HDLC protocol and the scrambler - * polynomial to the PC. This modem is no longer available (at least - * from Baycom) and has been replaced by the PICPAR modem (see below). - * You may however still build one from the schematics published in - * cq-DL :-). - * - * picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The - * modem is protocol compatible to par96, but uses only three low - * power ICs and can therefore be fed from the parallel port and - * does not require an additional power supply. It features - * built in DCD circuitry. The driver should therefore be configured - * for hardware DCD. - * - * Command line options (insmod command line) - * - * mode driver mode string. Valid choices are par96 and picpar. - * iobase base address of the port; common values are 0x378, 0x278, 0x3bc - * - * History: - * 0.1 26.06.1996 Adapted from baycom.c and made network driver interface - * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user) - * 0.3 26.04.1997 init code/data tagged - * 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints) - * 0.5 11.11.1997 split into separate files for ser12/par96 - * 0.6 03.08.1999 adapt to Linus' new __setup/__initcall - * removed some pre-2.2 kernel compatibility cruft - * 0.7 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts - * 0.8 12.02.2000 adapted to softnet driver interface - * removed direct parport access, uses parport driver methods - * 0.9 03.07.2000 fix interface name handling - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG - -/* - * modem options; bit mask - */ -#define BAYCOM_OPTIONS_SOFTDCD 1 - -/* --------------------------------------------------------------------- */ - -static const char bc_drvname[] = "baycom_par"; -static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_par: version 0.9\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -#define PAR96_BURSTBITS 16 -#define PAR96_BURST 4 -#define PAR96_PTT 2 -#define PAR96_TXBIT 1 -#define PAR96_ACK 0x40 -#define PAR96_RXBIT 0x20 -#define PAR96_DCD 0x10 -#define PAR97_POWER 0xf8 - -/* ---------------------------------------------------------------------- */ -/* - * Information that need to be kept for each board. - */ - -struct baycom_state { - struct hdlcdrv_state hdrv; - - struct pardevice *pdev; - unsigned int options; - - struct modem_state { - short arb_divider; - unsigned char flags; - unsigned int shreg; - struct modem_state_par96 { - int dcd_count; - unsigned int dcd_shreg; - unsigned long descram; - unsigned long scram; - } par96; - } modem; - -#ifdef BAYCOM_DEBUG - struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; - } debug_vals; -#endif /* BAYCOM_DEBUG */ -}; - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== PAR96 specific routines ========================= - */ - -#define PAR96_DESCRAM_TAP1 0x20000 -#define PAR96_DESCRAM_TAP2 0x01000 -#define PAR96_DESCRAM_TAP3 0x00001 - -#define PAR96_DESCRAM_TAPSH1 17 -#define PAR96_DESCRAM_TAPSH2 12 -#define PAR96_DESCRAM_TAPSH3 0 - -#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */ -#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */ - -/* --------------------------------------------------------------------- */ - -static inline void par96_tx(struct net_device *dev, struct baycom_state *bc) -{ - int i; - unsigned int data = hdlcdrv_getbits(&bc->hdrv); - struct parport *pp = bc->pdev->port; - - for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) { - unsigned char val = PAR97_POWER; - bc->modem.par96.scram = ((bc->modem.par96.scram << 1) | - (bc->modem.par96.scram & 1)); - if (!(data & 1)) - bc->modem.par96.scram ^= 1; - if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1)) - bc->modem.par96.scram ^= - (PAR96_SCRAM_TAPN << 1); - if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2)) - val |= PAR96_TXBIT; - pp->ops->write_data(pp, val); - pp->ops->write_data(pp, val | PAR96_BURST); - } -} - -/* --------------------------------------------------------------------- */ - -static inline void par96_rx(struct net_device *dev, struct baycom_state *bc) -{ - int i; - unsigned int data, mask, mask2, descx; - struct parport *pp = bc->pdev->port; - - /* - * do receiver; differential decode and descramble on the fly - */ - for(data = i = 0; i < PAR96_BURSTBITS; i++) { - bc->modem.par96.descram = (bc->modem.par96.descram << 1); - if (pp->ops->read_status(pp) & PAR96_RXBIT) - bc->modem.par96.descram |= 1; - descx = bc->modem.par96.descram ^ - (bc->modem.par96.descram >> 1); - /* now the diff decoded data is inverted in descram */ - pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT); - descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^ - (descx >> PAR96_DESCRAM_TAPSH2)); - data >>= 1; - if (!(descx & 1)) - data |= 0x8000; - pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT | PAR96_BURST); - } - hdlcdrv_putbits(&bc->hdrv, data); - /* - * do DCD algorithm - */ - if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { - bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16) - | (data << 16); - /* search for flags and set the dcd counter appropriately */ - for(mask = 0x1fe00, mask2 = 0xfc00, i = 0; - i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) - if ((bc->modem.par96.dcd_shreg & mask) == mask2) - bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4; - /* check for abort/noise sequences */ - for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0; - i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) - if (((bc->modem.par96.dcd_shreg & mask) == mask2) && - (bc->modem.par96.dcd_count >= 0)) - bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10; - /* decrement and set the dcd variable */ - if (bc->modem.par96.dcd_count >= 0) - bc->modem.par96.dcd_count -= 2; - hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0); - } else { - hdlcdrv_setdcd(&bc->hdrv, !!(pp->ops->read_status(pp) & PAR96_DCD)); - } -} - -/* --------------------------------------------------------------------- */ - -static void par96_interrupt(void *dev_id) -{ - struct net_device *dev = dev_id; - struct baycom_state *bc = netdev_priv(dev); - - baycom_int_freq(bc); - /* - * check if transmitter active - */ - if (hdlcdrv_ptt(&bc->hdrv)) - par96_tx(dev, bc); - else { - par96_rx(dev, bc); - if (--bc->modem.arb_divider <= 0) { - bc->modem.arb_divider = 6; - local_irq_enable(); - hdlcdrv_arbitrate(dev, &bc->hdrv); - } - } - local_irq_enable(); - hdlcdrv_transmitter(dev, &bc->hdrv); - hdlcdrv_receiver(dev, &bc->hdrv); - local_irq_disable(); -} - -/* --------------------------------------------------------------------- */ - -static void par96_wakeup(void *handle) -{ - struct net_device *dev = (struct net_device *)handle; - struct baycom_state *bc = netdev_priv(dev); - - printk(KERN_DEBUG "baycom_par: %s: why am I being woken up?\n", dev->name); - if (!parport_claim(bc->pdev)) - printk(KERN_DEBUG "baycom_par: %s: I'm broken.\n", dev->name); -} - -/* --------------------------------------------------------------------- */ - -static int par96_open(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct pardev_cb par_cb; - struct parport *pp; - int i; - - if (!dev || !bc) - return -ENXIO; - pp = parport_find_base(dev->base_addr); - if (!pp) { - printk(KERN_ERR "baycom_par: parport at 0x%lx unknown\n", dev->base_addr); - return -ENXIO; - } - if (pp->irq < 0) { - printk(KERN_ERR "baycom_par: parport at 0x%lx has no irq\n", pp->base); - parport_put_port(pp); - return -ENXIO; - } - if ((~pp->modes) & (PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) { - printk(KERN_ERR "baycom_par: parport at 0x%lx cannot be used\n", pp->base); - parport_put_port(pp); - return -ENXIO; - } - memset(&bc->modem, 0, sizeof(bc->modem)); - bc->hdrv.par.bitrate = 9600; - memset(&par_cb, 0, sizeof(par_cb)); - par_cb.wakeup = par96_wakeup; - par_cb.irq_func = par96_interrupt; - par_cb.private = (void *)dev; - par_cb.flags = PARPORT_DEV_EXCL; - for (i = 0; i < NR_PORTS; i++) - if (baycom_device[i] == dev) - break; - - if (i == NR_PORTS) { - pr_err("%s: no device found\n", bc_drvname); - parport_put_port(pp); - return -ENODEV; - } - bc->pdev = parport_register_dev_model(pp, dev->name, &par_cb, i); - parport_put_port(pp); - if (!bc->pdev) { - printk(KERN_ERR "baycom_par: cannot register parport at 0x%lx\n", dev->base_addr); - return -ENXIO; - } - if (parport_claim(bc->pdev)) { - printk(KERN_ERR "baycom_par: parport at 0x%lx busy\n", pp->base); - parport_unregister_device(bc->pdev); - return -EBUSY; - } - pp = bc->pdev->port; - dev->irq = pp->irq; - pp->ops->data_forward(pp); - bc->hdrv.par.bitrate = 9600; - pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER); /* switch off PTT */ - pp->ops->enable_irq(pp); - printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n", - bc_drvname, dev->base_addr, dev->irq, bc->options); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int par96_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp; - - if (!dev || !bc) - return -EINVAL; - pp = bc->pdev->port; - /* disable interrupt */ - pp->ops->disable_irq(pp); - /* switch off PTT */ - pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER); - parport_release(bc->pdev); - parport_unregister_device(bc->pdev); - printk(KERN_INFO "%s: close par96 at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== hdlcdrv driver interface ========================= - */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd); - -/* --------------------------------------------------------------------- */ - -static const struct hdlcdrv_ops par96_ops = { - .drvname = bc_drvname, - .drvinfo = bc_drvinfo, - .open = par96_open, - .close = par96_close, - .ioctl = baycom_ioctl -}; - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - if (!strncmp(modestr, "picpar", 6)) - bc->options = 0; - else if (!strncmp(modestr, "par96", 5)) - bc->options = BAYCOM_OPTIONS_SOFTDCD; - else - bc->options = !!strchr(modestr, '*'); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct baycom_state *bc; - struct baycom_ioctl bi; - - if (!dev) - return -EINVAL; - - bc = netdev_priv(dev); - BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC); - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - switch (hi->cmd) { - default: - break; - - case HDLCDRVCTL_GETMODE: - strscpy(hi->data.modename, bc->options ? "par96" : "picpar"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !capable(CAP_NET_ADMIN)) - return -EACCES; - hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; - return baycom_setmode(bc, hi->data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy(hi->data.modename, "par96,picpar"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE; - - } - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - -#ifdef BAYCOM_DEBUG - case BAYCOMCTL_GETDEBUG: - bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; - bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; - bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; - break; -#endif /* BAYCOM_DEBUG */ - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "picpar", }; -static int iobase[NR_PORTS] = { 0x378, }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode; eg. par96 or picpar"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int baycom_par_probe(struct pardevice *par_dev) -{ - struct device_driver *drv = par_dev->dev.driver; - int len = strlen(drv->name); - - if (strncmp(par_dev->name, drv->name, len)) - return -ENODEV; - - return 0; -} - -static struct parport_driver baycom_par_driver = { - .name = "bcp", - .probe = baycom_par_probe, -}; - -static int __init init_baycompar(void) -{ - int i, found = 0, ret; - char set_hw = 1; - - printk(bc_drvinfo); - - ret = parport_register_driver(&baycom_par_driver); - if (ret) - return ret; - - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - struct baycom_state *bc; - char ifname[IFNAMSIZ]; - - sprintf(ifname, "bcp%d", i); - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = 0; - - dev = hdlcdrv_register(&par96_ops, - sizeof(struct baycom_state), - ifname, iobase[i], 0, 0); - if (IS_ERR(dev)) - break; - - bc = netdev_priv(dev); - if (set_hw && baycom_setmode(bc, mode[i])) - set_hw = 0; - found++; - baycom_device[i] = dev; - } - - if (!found) { - parport_unregister_driver(&baycom_par_driver); - return -ENXIO; - } - return 0; -} - -static void __exit cleanup_baycompar(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - - if (dev) - hdlcdrv_unregister(dev); - } - parport_unregister_driver(&baycom_par_driver); -} - -module_init(init_baycompar); -module_exit(cleanup_baycompar); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_par=io,mode - * mode: par96,picpar - */ - -static int __init baycom_par_setup(char *str) -{ - static unsigned nr_dev; - int ints[2]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 2, ints); - if (ints[0] < 1) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - nr_dev++; - return 1; -} - -__setup("baycom_par=", baycom_par_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c deleted file mode 100644 index ee5bd3c12040..000000000000 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ /dev/null @@ -1,678 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_ser_fdx.c -- baycom ser12 fullduplex radio modem driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * Supported modems - * - * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only - * of a modulator/demodulator chip, usually a TI TCM3105. The computer - * is responsible for regenerating the receiver bit clock, as well as - * for handling the HDLC protocol. The modem connects to a serial port, - * hence the name. Since the serial port is not used as an async serial - * port, the kernel driver for serial ports cannot be used, and this - * driver only supports standard serial hardware (8250, 16450, 16550A) - * - * This modem usually draws its supply current out of the otherwise unused - * TXD pin of the serial port. Thus a contiguous stream of 0x00-bytes - * is transmitted to achieve a positive supply voltage. - * - * hsk: This is a 4800 baud FSK modem, designed for TNC use. It works fine - * in 'baycom-mode' :-) In contrast to the TCM3105 modem, power is - * externally supplied. So there's no need to provide the 0x00-byte-stream - * when receiving or idle, which drastically reduces interrupt load. - * - * Command line options (insmod command line) - * - * mode ser# hardware DCD - * ser#* software DCD - * ser#+ hardware DCD, inverted signal at DCD pin - * '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD' - * iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8 - * baud baud rate (between 300 and 4800) - * irq interrupt line of the port; common values are 4,3 - * - * History: - * 0.1 26.06.1996 Adapted from baycom.c and made network driver interface - * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user) - * 0.3 26.04.1997 init code/data tagged - * 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints) - * 0.5 11.11.1997 ser12/par96 split into separate files - * 0.6 24.01.1998 Thorsten Kranzkowski, dl8bcu and Thomas Sailer: - * reduced interrupt load in transmit case - * reworked receiver - * 0.7 03.08.1999 adapt to Linus' new __setup/__initcall - * 0.8 10.08.1999 use module_init/module_exit - * 0.9 12.02.2000 adapted to softnet driver interface - * 0.10 03.07.2000 fix interface name handling - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG - -/* --------------------------------------------------------------------- */ - -static const char bc_drvname[] = "baycom_ser_fdx"; -static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_ser_fdx: version 0.10\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define SER12_EXTENT 8 - -/* ---------------------------------------------------------------------- */ -/* - * Information that need to be kept for each board. - */ - -struct baycom_state { - struct hdlcdrv_state hdrv; - - unsigned int baud, baud_us, baud_arbdiv, baud_uartdiv, baud_dcdtimeout; - int opt_dcd; - - struct modem_state { - unsigned char flags; - unsigned char ptt; - unsigned int shreg; - struct modem_state_ser12 { - unsigned char tx_bit; - unsigned char last_rxbit; - int dcd_sum0, dcd_sum1, dcd_sum2; - int dcd_time; - unsigned int pll_time; - unsigned int txshreg; - } ser12; - } modem; - -#ifdef BAYCOM_DEBUG - struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; - } debug_vals; -#endif /* BAYCOM_DEBUG */ -}; - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== SER12 specific routines ========================= - */ - -/* --------------------------------------------------------------------- */ - -static inline void ser12_set_divisor(struct net_device *dev, - unsigned int divisor) -{ - outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ - outb(divisor, DLL(dev->base_addr)); - outb(divisor >> 8, DLM(dev->base_addr)); - outb(0x01, LCR(dev->base_addr)); /* word length = 6 */ - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - /* - * it is important not to set the divider while transmitting; - * this reportedly makes some UARTs generating interrupts - * in the hundredthousands per second region - * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno) - */ -} - -static __inline__ void ser12_rx(struct net_device *dev, struct baycom_state *bc, struct timespec64 *ts, unsigned char curs) -{ - int timediff; - int bdus8 = bc->baud_us >> 3; - int bdus4 = bc->baud_us >> 2; - int bdus2 = bc->baud_us >> 1; - - timediff = 1000000 + ts->tv_nsec / NSEC_PER_USEC - - bc->modem.ser12.pll_time; - while (timediff >= 500000) - timediff -= 1000000; - while (timediff >= bdus2) { - timediff -= bc->baud_us; - bc->modem.ser12.pll_time += bc->baud_us; - bc->modem.ser12.dcd_time--; - /* first check if there is room to add a bit */ - if (bc->modem.shreg & 1) { - hdlcdrv_putbits(&bc->hdrv, (bc->modem.shreg >> 1) ^ 0xffff); - bc->modem.shreg = 0x10000; - } - /* add a one bit */ - bc->modem.shreg >>= 1; - } - if (bc->modem.ser12.dcd_time <= 0) { - if (!bc->opt_dcd) - hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + - bc->modem.ser12.dcd_sum1 + - bc->modem.ser12.dcd_sum2) < 0); - bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; - bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; - bc->modem.ser12.dcd_sum0 = 2; /* slight bias */ - bc->modem.ser12.dcd_time += 120; - } - if (bc->modem.ser12.last_rxbit != curs) { - bc->modem.ser12.last_rxbit = curs; - bc->modem.shreg |= 0x10000; - /* adjust the PLL */ - if (timediff > 0) - bc->modem.ser12.pll_time += bdus8; - else - bc->modem.ser12.pll_time += 1000000 - bdus8; - /* update DCD */ - if (abs(timediff) > bdus4) - bc->modem.ser12.dcd_sum0 += 4; - else - bc->modem.ser12.dcd_sum0--; -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr = timediff; -#endif /* BAYCOM_DEBUG */ - } - while (bc->modem.ser12.pll_time >= 1000000) - bc->modem.ser12.pll_time -= 1000000; -} - -/* --------------------------------------------------------------------- */ - -static irqreturn_t ser12_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct baycom_state *bc = netdev_priv(dev); - struct timespec64 ts; - unsigned char iir, msr; - unsigned int txcount = 0; - - if (!bc || bc->hdrv.magic != HDLCDRV_MAGIC) - return IRQ_NONE; - /* fast way out for shared irq */ - if ((iir = inb(IIR(dev->base_addr))) & 1) - return IRQ_NONE; - /* get current time */ - ktime_get_ts64(&ts); - msr = inb(MSR(dev->base_addr)); - /* delta DCD */ - if ((msr & 8) && bc->opt_dcd) - hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80)); - do { - switch (iir & 6) { - case 6: - inb(LSR(dev->base_addr)); - break; - - case 4: - inb(RBR(dev->base_addr)); - break; - - case 2: - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - baycom_int_freq(bc); - txcount++; - /* - * first output the last bit (!) then call HDLC transmitter, - * since this may take quite long - */ - if (bc->modem.ptt) - outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr)); - else - outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ - break; - - default: - msr = inb(MSR(dev->base_addr)); - /* delta DCD */ - if ((msr & 8) && bc->opt_dcd) - hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80)); - break; - } - iir = inb(IIR(dev->base_addr)); - } while (!(iir & 1)); - ser12_rx(dev, bc, &ts, msr & 0x10); /* CTS */ - if (bc->modem.ptt && txcount) { - if (bc->modem.ser12.txshreg <= 1) { - bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); - if (!hdlcdrv_ptt(&bc->hdrv)) { - ser12_set_divisor(dev, 115200/100/8); - bc->modem.ptt = 0; - goto end_transmit; - } - } - bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1)); - bc->modem.ser12.txshreg >>= 1; - } - end_transmit: - local_irq_enable(); - if (!bc->modem.ptt && txcount) { - hdlcdrv_arbitrate(dev, &bc->hdrv); - if (hdlcdrv_ptt(&bc->hdrv)) { - ser12_set_divisor(dev, bc->baud_uartdiv); - bc->modem.ser12.txshreg = 1; - bc->modem.ptt = 1; - } - } - hdlcdrv_transmitter(dev, &bc->hdrv); - hdlcdrv_receiver(dev, &bc->hdrv); - local_irq_disable(); - return IRQ_HANDLED; -} - -/* --------------------------------------------------------------------- */ - -enum uart { c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A}; -static const char *uart_str[] = { - "unknown", "8250", "16450", "16550", "16550A" -}; - -static enum uart ser12_check_uart(unsigned int iobase) -{ - unsigned char b1,b2,b3; - enum uart u; - enum uart uart_tab[] = - { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; - - b1 = inb(MCR(iobase)); - outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ - b2 = inb(MSR(iobase)); - outb(0x1a, MCR(iobase)); - b3 = inb(MSR(iobase)) & 0xf0; - outb(b1, MCR(iobase)); /* restore old values */ - outb(b2, MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(RBR(iobase)); - inb(RBR(iobase)); - outb(0x01, FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, SCR(iobase)); - b1 = inb(SCR(iobase)); - outb(0xa5, SCR(iobase)); - b2 = inb(SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_open(struct net_device *dev) -{ - const unsigned int nr_irqs = irq_get_nr_irqs(); - struct baycom_state *bc = netdev_priv(dev); - enum uart u; - - if (!dev || !bc) - return -ENXIO; - if (!dev->base_addr || dev->base_addr > 0xffff-SER12_EXTENT || - dev->irq < 2 || dev->irq > nr_irqs) { - printk(KERN_INFO "baycom_ser_fdx: invalid portnumber (max %u) " - "or irq (2 <= irq <= %d)\n", - 0xffff-SER12_EXTENT, nr_irqs); - return -ENXIO; - } - if (bc->baud < 300 || bc->baud > 4800) { - printk(KERN_INFO "baycom_ser_fdx: invalid baudrate " - "(300...4800)\n"); - return -EINVAL; - } - if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx")) { - printk(KERN_WARNING "BAYCOM_SER_FSX: I/O port 0x%04lx busy\n", - dev->base_addr); - return -EACCES; - } - memset(&bc->modem, 0, sizeof(bc->modem)); - bc->hdrv.par.bitrate = bc->baud; - bc->baud_us = 1000000/bc->baud; - bc->baud_uartdiv = (115200/8)/bc->baud; - if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown){ - release_region(dev->base_addr, SER12_EXTENT); - return -EIO; - } - outb(0, FCR(dev->base_addr)); /* disable FIFOs */ - outb(0x0d, MCR(dev->base_addr)); - outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED, - "baycom_ser_fdx", dev)) { - release_region(dev->base_addr, SER12_EXTENT); - return -EBUSY; - } - /* - * set the SIO to 6 Bits/character; during receive, - * the baud rate is set to produce 100 ints/sec - * to feed the channel arbitration process, - * during transmit to baud ints/sec to run - * the transmitter - */ - ser12_set_divisor(dev, 115200/100/8); - /* - * enable transmitter empty interrupt and modem status interrupt - */ - outb(0x0a, IER(dev->base_addr)); - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - hdlcdrv_setdcd(&bc->hdrv, 0); - printk(KERN_INFO "%s: ser_fdx at iobase 0x%lx irq %u baud %u uart %s\n", - bc_drvname, dev->base_addr, dev->irq, bc->baud, uart_str[u]); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - if (!dev || !bc) - return -EINVAL; - /* - * disable interrupts - */ - outb(0, IER(dev->base_addr)); - outb(1, MCR(dev->base_addr)); - free_irq(dev->irq, dev); - release_region(dev->base_addr, SER12_EXTENT); - printk(KERN_INFO "%s: close ser_fdx at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== hdlcdrv driver interface ========================= - */ - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd); - -/* --------------------------------------------------------------------- */ - -static const struct hdlcdrv_ops ser12_ops = { - .drvname = bc_drvname, - .drvinfo = bc_drvinfo, - .open = ser12_open, - .close = ser12_close, - .ioctl = baycom_ioctl, -}; - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - unsigned int baud; - - if (!strncmp(modestr, "ser", 3)) { - baud = simple_strtoul(modestr+3, NULL, 10); - if (baud >= 3 && baud <= 48) - bc->baud = baud*100; - } - if (strchr(modestr, '*')) - bc->opt_dcd = 0; - else if (strchr(modestr, '+')) - bc->opt_dcd = -1; - else - bc->opt_dcd = 1; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct baycom_state *bc; - struct baycom_ioctl bi; - - if (!dev) - return -EINVAL; - - bc = netdev_priv(dev); - BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC); - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - switch (hi->cmd) { - default: - break; - - case HDLCDRVCTL_GETMODE: - sprintf(hi->data.modename, "ser%u", bc->baud / 100); - if (bc->opt_dcd <= 0) - strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : "+"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !capable(CAP_NET_ADMIN)) - return -EACCES; - hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; - return baycom_setmode(bc, hi->data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy(hi->data.modename, "ser12,ser3,ser24"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ; - - } - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - -#ifdef BAYCOM_DEBUG - case BAYCOMCTL_GETDEBUG: - bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; - bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; - bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; - break; -#endif /* BAYCOM_DEBUG */ - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "ser12*", }; -static int iobase[NR_PORTS] = { 0x3f8, }; -static int irq[NR_PORTS] = { 4, }; -static int baud[NR_PORTS] = { [0 ... NR_PORTS-1] = 1200 }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "baycom irq number"); -module_param_array(baud, int, NULL, 0); -MODULE_PARM_DESC(baud, "baycom baud rate (300 to 4800)"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int __init init_baycomserfdx(void) -{ - int i, found = 0; - char set_hw = 1; - - printk(bc_drvinfo); - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - struct baycom_state *bc; - char ifname[IFNAMSIZ]; - - sprintf(ifname, "bcsf%d", i); - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = irq[i] = 0; - - dev = hdlcdrv_register(&ser12_ops, - sizeof(struct baycom_state), - ifname, iobase[i], irq[i], 0); - if (IS_ERR(dev)) - break; - - bc = netdev_priv(dev); - if (set_hw && baycom_setmode(bc, mode[i])) - set_hw = 0; - bc->baud = baud[i]; - found++; - baycom_device[i] = dev; - } - - if (!found) - return -ENXIO; - return 0; -} - -static void __exit cleanup_baycomserfdx(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - if (dev) - hdlcdrv_unregister(dev); - } -} - -module_init(init_baycomserfdx); -module_exit(cleanup_baycomserfdx); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_ser_fdx=io,irq,mode - * mode: ser# hardware DCD - * ser#* software DCD - * ser#+ hardware DCD, inverted signal at DCD pin - * '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD' - */ - -static int __init baycom_ser_fdx_setup(char *str) -{ - static unsigned nr_dev; - int ints[4]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 4, ints); - if (ints[0] < 2) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - irq[nr_dev] = ints[2]; - if (ints[0] >= 3) - baud[nr_dev] = ints[3]; - nr_dev++; - return 1; -} - -__setup("baycom_ser_fdx=", baycom_ser_fdx_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c deleted file mode 100644 index 05bdad214799..000000000000 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ /dev/null @@ -1,727 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_ser_hdx.c -- baycom ser12 halfduplex radio modem driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * Supported modems - * - * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only - * of a modulator/demodulator chip, usually a TI TCM3105. The computer - * is responsible for regenerating the receiver bit clock, as well as - * for handling the HDLC protocol. The modem connects to a serial port, - * hence the name. Since the serial port is not used as an async serial - * port, the kernel driver for serial ports cannot be used, and this - * driver only supports standard serial hardware (8250, 16450, 16550A) - * - * Command line options (insmod command line) - * - * mode ser12 hardware DCD - * ser12* software DCD - * ser12@ hardware/software DCD, i.e. no explicit DCD signal but hardware - * mutes audio input to the modem - * ser12+ hardware DCD, inverted signal at DCD pin - * iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8 - * irq interrupt line of the port; common values are 4,3 - * - * History: - * 0.1 26.06.1996 Adapted from baycom.c and made network driver interface - * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user) - * 0.3 26.04.1997 init code/data tagged - * 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints) - * 0.5 11.11.1997 ser12/par96 split into separate files - * 0.6 14.04.1998 cleanups - * 0.7 03.08.1999 adapt to Linus' new __setup/__initcall - * 0.8 10.08.1999 use module_init/module_exit - * 0.9 12.02.2000 adapted to softnet driver interface - * 0.10 03.07.2000 fix interface name handling - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG - -/* --------------------------------------------------------------------- */ - -static const char bc_drvname[] = "baycom_ser_hdx"; -static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_ser_hdx: version 0.10\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define SER12_EXTENT 8 - -/* ---------------------------------------------------------------------- */ -/* - * Information that need to be kept for each board. - */ - -struct baycom_state { - struct hdlcdrv_state hdrv; - - int opt_dcd; - - struct modem_state { - short arb_divider; - unsigned char flags; - unsigned int shreg; - struct modem_state_ser12 { - unsigned char tx_bit; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned char last_sample; - unsigned char last_rxbit; - unsigned int dcd_shreg; - unsigned int dcd_time; - unsigned int bit_pll; - unsigned char interm_sample; - } ser12; - } modem; - -#ifdef BAYCOM_DEBUG - struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; - } debug_vals; -#endif /* BAYCOM_DEBUG */ -}; - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== SER12 specific routines ========================= - */ - -static inline void ser12_set_divisor(struct net_device *dev, - unsigned char divisor) -{ - outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ - outb(divisor, DLL(dev->base_addr)); - outb(0, DLM(dev->base_addr)); - outb(0x01, LCR(dev->base_addr)); /* word length = 6 */ - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - /* - * it is important not to set the divider while transmitting; - * this reportedly makes some UARTs generating interrupts - * in the hundredthousands per second region - * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno) - */ -} - -/* --------------------------------------------------------------------- */ - -/* - * must call the TX arbitrator every 10ms - */ -#define SER12_ARB_DIVIDER(bc) (bc->opt_dcd ? 24 : 36) - -#define SER12_DCD_INTERVAL(bc) (bc->opt_dcd ? 12 : 240) - -static inline void ser12_tx(struct net_device *dev, struct baycom_state *bc) -{ - /* one interrupt per channel bit */ - ser12_set_divisor(dev, 12); - /* - * first output the last bit (!) then call HDLC transmitter, - * since this may take quite long - */ - outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr)); - if (bc->modem.shreg <= 1) - bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); - bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ - (bc->modem.shreg & 1)); - bc->modem.shreg >>= 1; -} - -/* --------------------------------------------------------------------- */ - -static inline void ser12_rx(struct net_device *dev, struct baycom_state *bc) -{ - unsigned char cur_s; - /* - * do demodulator - */ - cur_s = inb(MSR(dev->base_addr)) & 0x10; /* the CTS line */ - hdlcdrv_channelbit(&bc->hdrv, cur_s); - bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) | - (cur_s != bc->modem.ser12.last_sample); - bc->modem.ser12.last_sample = cur_s; - if(bc->modem.ser12.dcd_shreg & 1) { - if (!bc->opt_dcd) { - unsigned int dcdspos, dcdsneg; - - dcdspos = dcdsneg = 0; - dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); - if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) - dcdspos += 2; - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); - - bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; - } else - bc->modem.ser12.dcd_sum0--; - } - if(!bc->modem.ser12.dcd_time) { - hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + - bc->modem.ser12.dcd_sum1 + - bc->modem.ser12.dcd_sum2) < 0); - bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; - bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; - /* offset to ensure DCD off on silent input */ - bc->modem.ser12.dcd_sum0 = 2; - bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); - } - bc->modem.ser12.dcd_time--; - if (!bc->opt_dcd) { - /* - * PLL code for the improved software DCD algorithm - */ - if (bc->modem.ser12.interm_sample) { - /* - * intermediate sample; set timing correction to normal - */ - ser12_set_divisor(dev, 4); - } else { - /* - * do PLL correction and call HDLC receiver - */ - switch (bc->modem.ser12.dcd_shreg & 7) { - case 1: /* transition too late */ - ser12_set_divisor(dev, 5); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr++; -#endif /* BAYCOM_DEBUG */ - break; - case 4: /* transition too early */ - ser12_set_divisor(dev, 3); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr--; -#endif /* BAYCOM_DEBUG */ - break; - default: - ser12_set_divisor(dev, 4); - break; - } - bc->modem.shreg >>= 1; - if (bc->modem.ser12.last_sample == - bc->modem.ser12.last_rxbit) - bc->modem.shreg |= 0x10000; - bc->modem.ser12.last_rxbit = - bc->modem.ser12.last_sample; - } - if (++bc->modem.ser12.interm_sample >= 3) - bc->modem.ser12.interm_sample = 0; - /* - * DCD stuff - */ - if (bc->modem.ser12.dcd_shreg & 1) { - unsigned int dcdspos, dcdsneg; - - dcdspos = dcdsneg = 0; - dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); - dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) - << 1; - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); - - bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; - } - } else { - /* - * PLL algorithm for the hardware squelch DCD algorithm - */ - if (bc->modem.ser12.interm_sample) { - /* - * intermediate sample; set timing correction to normal - */ - ser12_set_divisor(dev, 6); - } else { - /* - * do PLL correction and call HDLC receiver - */ - switch (bc->modem.ser12.dcd_shreg & 3) { - case 1: /* transition too late */ - ser12_set_divisor(dev, 7); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr++; -#endif /* BAYCOM_DEBUG */ - break; - case 2: /* transition too early */ - ser12_set_divisor(dev, 5); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr--; -#endif /* BAYCOM_DEBUG */ - break; - default: - ser12_set_divisor(dev, 6); - break; - } - bc->modem.shreg >>= 1; - if (bc->modem.ser12.last_sample == - bc->modem.ser12.last_rxbit) - bc->modem.shreg |= 0x10000; - bc->modem.ser12.last_rxbit = - bc->modem.ser12.last_sample; - } - bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample; - /* - * DCD stuff - */ - bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1); - } - outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ - if (bc->modem.shreg & 1) { - hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1); - bc->modem.shreg = 0x10000; - } - if(!bc->modem.ser12.dcd_time) { - if (bc->opt_dcd & 1) - hdlcdrv_setdcd(&bc->hdrv, !((inb(MSR(dev->base_addr)) ^ bc->opt_dcd) & 0x80)); - else - hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + - bc->modem.ser12.dcd_sum1 + - bc->modem.ser12.dcd_sum2) < 0); - bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; - bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; - /* offset to ensure DCD off on silent input */ - bc->modem.ser12.dcd_sum0 = 2; - bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); - } - bc->modem.ser12.dcd_time--; -} - -/* --------------------------------------------------------------------- */ - -static irqreturn_t ser12_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct baycom_state *bc = netdev_priv(dev); - unsigned char iir; - - if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) - return IRQ_NONE; - /* fast way out */ - if ((iir = inb(IIR(dev->base_addr))) & 1) - return IRQ_NONE; - baycom_int_freq(bc); - do { - switch (iir & 6) { - case 6: - inb(LSR(dev->base_addr)); - break; - - case 4: - inb(RBR(dev->base_addr)); - break; - - case 2: - /* - * check if transmitter active - */ - if (hdlcdrv_ptt(&bc->hdrv)) - ser12_tx(dev, bc); - else { - ser12_rx(dev, bc); - bc->modem.arb_divider--; - } - outb(0x00, THR(dev->base_addr)); - break; - - default: - inb(MSR(dev->base_addr)); - break; - } - iir = inb(IIR(dev->base_addr)); - } while (!(iir & 1)); - if (bc->modem.arb_divider <= 0) { - bc->modem.arb_divider = SER12_ARB_DIVIDER(bc); - local_irq_enable(); - hdlcdrv_arbitrate(dev, &bc->hdrv); - } - local_irq_enable(); - hdlcdrv_transmitter(dev, &bc->hdrv); - hdlcdrv_receiver(dev, &bc->hdrv); - local_irq_disable(); - return IRQ_HANDLED; -} - -/* --------------------------------------------------------------------- */ - -enum uart { c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A}; -static const char *uart_str[] = { - "unknown", "8250", "16450", "16550", "16550A" -}; - -static enum uart ser12_check_uart(unsigned int iobase) -{ - unsigned char b1,b2,b3; - enum uart u; - enum uart uart_tab[] = - { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; - - b1 = inb(MCR(iobase)); - outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ - b2 = inb(MSR(iobase)); - outb(0x1a, MCR(iobase)); - b3 = inb(MSR(iobase)) & 0xf0; - outb(b1, MCR(iobase)); /* restore old values */ - outb(b2, MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(RBR(iobase)); - inb(RBR(iobase)); - outb(0x01, FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, SCR(iobase)); - b1 = inb(SCR(iobase)); - outb(0xa5, SCR(iobase)); - b2 = inb(SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_open(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - enum uart u; - - if (!dev || !bc) - return -ENXIO; - if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT || - dev->irq < 2 || dev->irq > 15) - return -ENXIO; - if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12")) - return -EACCES; - memset(&bc->modem, 0, sizeof(bc->modem)); - bc->hdrv.par.bitrate = 1200; - if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown) { - release_region(dev->base_addr, SER12_EXTENT); - return -EIO; - } - outb(0, FCR(dev->base_addr)); /* disable FIFOs */ - outb(0x0d, MCR(dev->base_addr)); - outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED, - "baycom_ser12", dev)) { - release_region(dev->base_addr, SER12_EXTENT); - return -EBUSY; - } - /* - * enable transmitter empty interrupt - */ - outb(2, IER(dev->base_addr)); - /* - * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that - * we get exactly (hopefully) 2 or 3 interrupts per radio symbol, - * depending on the usage of the software DCD routine - */ - ser12_set_divisor(dev, bc->opt_dcd ? 6 : 4); - printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u uart %s\n", - bc_drvname, dev->base_addr, dev->irq, uart_str[u]); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - if (!dev || !bc) - return -EINVAL; - /* - * disable interrupts - */ - outb(0, IER(dev->base_addr)); - outb(1, MCR(dev->base_addr)); - free_irq(dev->irq, dev); - release_region(dev->base_addr, SER12_EXTENT); - printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== hdlcdrv driver interface ========================= - */ - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd); - -/* --------------------------------------------------------------------- */ - -static const struct hdlcdrv_ops ser12_ops = { - .drvname = bc_drvname, - .drvinfo = bc_drvinfo, - .open = ser12_open, - .close = ser12_close, - .ioctl = baycom_ioctl, -}; - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - if (strchr(modestr, '*')) - bc->opt_dcd = 0; - else if (strchr(modestr, '+')) - bc->opt_dcd = -1; - else if (strchr(modestr, '@')) - bc->opt_dcd = -2; - else - bc->opt_dcd = 1; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct baycom_state *bc; - struct baycom_ioctl bi; - - if (!dev) - return -EINVAL; - - bc = netdev_priv(dev); - BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC); - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - switch (hi->cmd) { - default: - break; - - case HDLCDRVCTL_GETMODE: - strscpy(hi->data.modename, "ser12"); - if (bc->opt_dcd <= 0) - strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : (bc->opt_dcd == -2) ? "@" : "+"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !capable(CAP_NET_ADMIN)) - return -EACCES; - hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; - return baycom_setmode(bc, hi->data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy(hi->data.modename, "ser12"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ; - - } - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - -#ifdef BAYCOM_DEBUG - case BAYCOMCTL_GETDEBUG: - bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; - bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; - bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; - break; -#endif /* BAYCOM_DEBUG */ - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "ser12*", }; -static int iobase[NR_PORTS] = { 0x3f8, }; -static int irq[NR_PORTS] = { 4, }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "baycom irq number"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int __init init_baycomserhdx(void) -{ - int i, found = 0; - char set_hw = 1; - - printk(bc_drvinfo); - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - struct baycom_state *bc; - char ifname[IFNAMSIZ]; - - sprintf(ifname, "bcsh%d", i); - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = irq[i] = 0; - - dev = hdlcdrv_register(&ser12_ops, - sizeof(struct baycom_state), - ifname, iobase[i], irq[i], 0); - if (IS_ERR(dev)) - break; - - bc = netdev_priv(dev); - if (set_hw && baycom_setmode(bc, mode[i])) - set_hw = 0; - found++; - baycom_device[i] = dev; - } - - if (!found) - return -ENXIO; - return 0; -} - -static void __exit cleanup_baycomserhdx(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - - if (dev) - hdlcdrv_unregister(dev); - } -} - -module_init(init_baycomserhdx); -module_exit(cleanup_baycomserhdx); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_ser_hdx=io,irq,mode - * mode: ser12 hardware DCD - * ser12* software DCD - * ser12@ hardware/software DCD, i.e. no explicit DCD signal but hardware - * mutes audio input to the modem - * ser12+ hardware DCD, inverted signal at DCD pin - */ - -static int __init baycom_ser_hdx_setup(char *str) -{ - static unsigned nr_dev; - int ints[3]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 3, ints); - if (ints[0] < 2) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - irq[nr_dev] = ints[2]; - nr_dev++; - return 1; -} - -__setup("baycom_ser_hdx=", baycom_ser_hdx_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c deleted file mode 100644 index 214fd1f819a1..000000000000 --- a/drivers/net/hamradio/bpqether.c +++ /dev/null @@ -1,593 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * G8BPQ compatible "AX.25 via ethernet" driver release 004 - * - * This code REQUIRES 2.0.0 or higher/ NET3.029 - * - * This is a "pseudo" network driver to allow AX.25 over Ethernet - * using G8BPQ encapsulation. It has been extracted from the protocol - * implementation because - * - * - things got unreadable within the protocol stack - * - to cure the protocol stack from "feature-ism" - * - a protocol implementation shouldn't need to know on - * which hardware it is running - * - user-level programs like the AX.25 utilities shouldn't - * need to know about the hardware. - * - IP over ethernet encapsulated AX.25 was impossible - * - rxecho.c did not work - * - to have room for extensions - * - it just deserves to "live" as an own driver - * - * This driver can use any ethernet destination address, and can be - * limited to accept frames from one dedicated ethernet card only. - * - * Note that the driver sets up the BPQ devices automagically on - * startup or (if started before the "insmod" of an ethernet device) - * on "ifconfig up". It hopefully will remove the BPQ on "rmmod"ing - * the ethernet device (in fact: as soon as another ethernet or bpq - * device gets "ifconfig"ured). - * - * I have heard that several people are thinking of experiments - * with highspeed packet radio using existing ethernet cards. - * Well, this driver is prepared for this purpose, just add - * your tx key control and a txdelay / tailtime algorithm, - * probably some buffering, and /voila/... - * - * History - * BPQ 001 Joerg(DL1BKE) Extracted BPQ code from AX.25 - * protocol stack and added my own - * yet existing patches - * BPQ 002 Joerg(DL1BKE) Scan network device list on - * startup. - * BPQ 003 Joerg(DL1BKE) Ethernet destination address - * and accepted source address - * can be configured by an ioctl() - * call. - * Fixed to match Linux networking - * changes - 2.1.15. - * BPQ 004 Joerg(DL1BKE) Fixed to not lock up on ifconfig. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -static const char banner[] __initconst = KERN_INFO \ - "AX.25: bpqether driver version 004\n"; - -static int bpq_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); -static int bpq_device_event(struct notifier_block *, unsigned long, void *); - -static struct packet_type bpq_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_BPQ), - .func = bpq_rcv, -}; - -static struct notifier_block bpq_dev_notifier = { - .notifier_call = bpq_device_event, -}; - - -struct bpqdev { - struct list_head bpq_list; /* list of bpq devices chain */ - struct net_device *ethdev; /* link to ethernet device */ - struct net_device *axdev; /* bpq device (bpq#) */ - char dest_addr[6]; /* ether destination address */ - char acpt_addr[6]; /* accept ether frames from this address only */ -}; - -static LIST_HEAD(bpq_devices); - -/* ------------------------------------------------------------------------ */ - - -/* - * Get the ethernet device for a BPQ device - */ -static inline struct net_device *bpq_get_ether_dev(struct net_device *dev) -{ - struct bpqdev *bpq = netdev_priv(dev); - - return bpq ? bpq->ethdev : NULL; -} - -/* - * Get the BPQ device for the ethernet device - */ -static inline struct net_device *bpq_get_ax25_dev(struct net_device *dev) -{ - struct bpqdev *bpq; - - list_for_each_entry_rcu(bpq, &bpq_devices, bpq_list, - lockdep_rtnl_is_held()) { - if (bpq->ethdev == dev) - return bpq->axdev; - } - return NULL; -} - -static inline int dev_is_ethdev(struct net_device *dev) -{ - return dev->type == ARPHRD_ETHER && !netdev_need_ops_lock(dev); -} - -/* ------------------------------------------------------------------------ */ - - -/* - * Receive an AX.25 frame via an ethernet interface. - */ -static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev) -{ - int len; - char * ptr; - struct ethhdr *eth; - struct bpqdev *bpq; - - if (!net_eq(dev_net(dev), &init_net)) - goto drop; - - if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) - return NET_RX_DROP; - - if (!pskb_may_pull(skb, sizeof(struct ethhdr))) - goto drop; - - rcu_read_lock(); - dev = bpq_get_ax25_dev(dev); - - if (dev == NULL || !netif_running(dev)) - goto drop_unlock; - - /* - * if we want to accept frames from just one ethernet device - * we check the source address of the sender. - */ - - bpq = netdev_priv(dev); - - eth = eth_hdr(skb); - - if (!(bpq->acpt_addr[0] & 0x01) && - !ether_addr_equal(eth->h_source, bpq->acpt_addr)) - goto drop_unlock; - - if (skb_cow(skb, sizeof(struct ethhdr))) - goto drop_unlock; - - len = skb->data[0] + skb->data[1] * 256 - 5; - - if (len < 0 || len > skb->len - 2) - goto drop_unlock; - - skb_pull(skb, 2); /* Remove the length bytes */ - skb_trim(skb, len); /* Set the length of the data */ - - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - - ptr = skb_push(skb, 1); - *ptr = 0; - - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); -unlock: - - rcu_read_unlock(); - - return 0; -drop_unlock: - kfree_skb(skb); - goto unlock; - -drop: - kfree_skb(skb); - return 0; -} - -/* - * Send an AX.25 frame via an ethernet interface - */ -static netdev_tx_t bpq_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned char *ptr; - struct bpqdev *bpq; - struct net_device *orig_dev; - int size; - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - /* - * Just to be *really* sure not to send anything if the interface - * is down, the ethernet device may have gone. - */ - if (!netif_running(dev)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - - skb_pull(skb, 1); /* Drop KISS byte */ - size = skb->len; - - /* - * We're about to mess with the skb which may still shared with the - * generic networking code so unshare and ensure it's got enough - * space for the BPQ headers. - */ - if (skb_cow(skb, AX25_BPQ_HEADER_LEN)) { - if (net_ratelimit()) - pr_err("bpqether: out of memory\n"); - kfree_skb(skb); - - return NETDEV_TX_OK; - } - - ptr = skb_push(skb, 2); /* Make space for length */ - - *ptr++ = (size + 5) % 256; - *ptr++ = (size + 5) / 256; - - bpq = netdev_priv(dev); - - orig_dev = dev; - if ((dev = bpq_get_ether_dev(dev)) == NULL) { - orig_dev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; - } - - skb->protocol = ax25_type_trans(skb, dev); - skb_reset_network_header(skb); - dev_hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); - dev->stats.tx_packets++; - dev->stats.tx_bytes+=skb->len; - - dev_queue_xmit(skb); - netif_wake_queue(dev); - return NETDEV_TX_OK; -} - -/* - * Set AX.25 callsign - */ -static int bpq_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *)addr; - - dev_addr_set(dev, sa->sa_data); - - return 0; -} - -/* Ioctl commands - * - * SIOCSBPQETHOPT reserved for enhancements - * SIOCSBPQETHADDR set the destination and accepted - * source ethernet address (broadcast - * or multicast: accept all) - */ -static int bpq_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd) -{ - struct bpq_ethaddr __user *ethaddr = data; - struct bpqdev *bpq = netdev_priv(dev); - struct bpq_req req; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch (cmd) { - case SIOCSBPQETHOPT: - if (copy_from_user(&req, data, sizeof(struct bpq_req))) - return -EFAULT; - switch (req.cmd) { - case SIOCGBPQETHPARAM: - case SIOCSBPQETHPARAM: - default: - return -EINVAL; - } - - break; - - case SIOCSBPQETHADDR: - if (copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN)) - return -EFAULT; - if (copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN)) - return -EFAULT; - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* - * open/close a device - */ -static int bpq_open(struct net_device *dev) -{ - netif_start_queue(dev); - return 0; -} - -static int bpq_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - - -/* ------------------------------------------------------------------------ */ - -#ifdef CONFIG_PROC_FS -/* - * Proc filesystem - */ -static void *bpq_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(RCU) -{ - int i = 1; - struct bpqdev *bpqdev; - - rcu_read_lock(); - - if (*pos == 0) - return SEQ_START_TOKEN; - - list_for_each_entry_rcu(bpqdev, &bpq_devices, bpq_list) { - if (i == *pos) - return bpqdev; - } - return NULL; -} - -static void *bpq_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct list_head *p; - struct bpqdev *bpqdev = v; - - ++*pos; - - if (v == SEQ_START_TOKEN) - p = rcu_dereference(list_next_rcu(&bpq_devices)); - else - p = rcu_dereference(list_next_rcu(&bpqdev->bpq_list)); - - return (p == &bpq_devices) ? NULL - : list_entry(p, struct bpqdev, bpq_list); -} - -static void bpq_seq_stop(struct seq_file *seq, void *v) - __releases(RCU) -{ - rcu_read_unlock(); -} - - -static int bpq_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "dev ether destination accept from\n"); - else { - const struct bpqdev *bpqdev = v; - - seq_printf(seq, "%-5s %-10s %pM ", - bpqdev->axdev->name, bpqdev->ethdev->name, - bpqdev->dest_addr); - - if (is_multicast_ether_addr(bpqdev->acpt_addr)) - seq_printf(seq, "*\n"); - else - seq_printf(seq, "%pM\n", bpqdev->acpt_addr); - - } - return 0; -} - -static const struct seq_operations bpq_seqops = { - .start = bpq_seq_start, - .next = bpq_seq_next, - .stop = bpq_seq_stop, - .show = bpq_seq_show, -}; -#endif -/* ------------------------------------------------------------------------ */ - -static const struct net_device_ops bpq_netdev_ops = { - .ndo_open = bpq_open, - .ndo_stop = bpq_close, - .ndo_start_xmit = bpq_xmit, - .ndo_set_mac_address = bpq_set_mac_address, - .ndo_siocdevprivate = bpq_siocdevprivate, -}; - -static void bpq_setup(struct net_device *dev) -{ - netdev_lockdep_set_classes(dev); - - dev->netdev_ops = &bpq_netdev_ops; - dev->needs_free_netdev = true; - - dev->flags = 0; - dev->lltx = true; /* Allow recursion */ - -#if IS_ENABLED(CONFIG_AX25) - dev->header_ops = &ax25_header_ops; -#endif - - dev->type = ARPHRD_AX25; - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; - dev->addr_len = AX25_ADDR_LEN; - - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); -} - -/* - * Setup a new device. - */ -static int bpq_new_device(struct net_device *edev) -{ - int err; - struct net_device *ndev; - struct bpqdev *bpq; - - ndev = alloc_netdev(sizeof(struct bpqdev), "bpq%d", NET_NAME_UNKNOWN, - bpq_setup); - if (!ndev) - return -ENOMEM; - - - bpq = netdev_priv(ndev); - dev_hold(edev); - bpq->ethdev = edev; - bpq->axdev = ndev; - - eth_broadcast_addr(bpq->dest_addr); - eth_broadcast_addr(bpq->acpt_addr); - - err = register_netdevice(ndev); - if (err) - goto error; - - /* List protected by RTNL */ - list_add_rcu(&bpq->bpq_list, &bpq_devices); - return 0; - - error: - dev_put(edev); - free_netdev(ndev); - return err; - -} - -static void bpq_free_device(struct net_device *ndev) -{ - struct bpqdev *bpq = netdev_priv(ndev); - - dev_put(bpq->ethdev); - list_del_rcu(&bpq->bpq_list); - - unregister_netdevice(ndev); -} - -/* - * Handle device status changes. - */ -static int bpq_device_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (!dev_is_ethdev(dev) && !bpq_get_ax25_dev(dev)) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_UP: /* new ethernet device -> new BPQ interface */ - if (bpq_get_ax25_dev(dev) == NULL) - bpq_new_device(dev); - break; - - case NETDEV_DOWN: /* ethernet device closed -> close BPQ interface */ - if ((dev = bpq_get_ax25_dev(dev)) != NULL) - dev_close(dev); - break; - - case NETDEV_UNREGISTER: /* ethernet device removed -> free BPQ interface */ - if ((dev = bpq_get_ax25_dev(dev)) != NULL) - bpq_free_device(dev); - break; - default: - break; - } - - return NOTIFY_DONE; -} - - -/* ------------------------------------------------------------------------ */ - -/* - * Initialize driver. To be called from af_ax25 if not compiled as a - * module - */ -static int __init bpq_init_driver(void) -{ -#ifdef CONFIG_PROC_FS - if (!proc_create_seq("bpqether", 0444, init_net.proc_net, &bpq_seqops)) { - printk(KERN_ERR - "bpq: cannot create /proc/net/bpqether entry.\n"); - return -ENOENT; - } -#endif /* CONFIG_PROC_FS */ - - dev_add_pack(&bpq_packet_type); - - register_netdevice_notifier(&bpq_dev_notifier); - - printk(banner); - - return 0; -} - -static void __exit bpq_cleanup_driver(void) -{ - struct bpqdev *bpq; - - dev_remove_pack(&bpq_packet_type); - - unregister_netdevice_notifier(&bpq_dev_notifier); - - remove_proc_entry("bpqether", init_net.proc_net); - - rtnl_lock(); - while (!list_empty(&bpq_devices)) { - bpq = list_entry(bpq_devices.next, struct bpqdev, bpq_list); - bpq_free_device(bpq->axdev); - } - rtnl_unlock(); -} - -MODULE_AUTHOR("Joerg Reuter DL1BKE "); -MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet"); -MODULE_LICENSE("GPL"); -module_init(bpq_init_driver); -module_exit(bpq_cleanup_driver); diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c deleted file mode 100644 index 3b88e465d08f..000000000000 --- a/drivers/net/hamradio/hdlcdrv.c +++ /dev/null @@ -1,747 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * hdlcdrv.c -- HDLC packet radio network driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * The driver was derived from Donald Beckers skeleton.c - * Written 1993-94 by Donald Becker. - * - * History: - * 0.1 21.09.1996 Started - * 18.10.1996 Changed to new user space access routines - * (copy_{to,from}_user) - * 0.2 21.11.1996 various small changes - * 0.3 03.03.1997 fixed (hopefully) IP not working with ax.25 as a module - * 0.4 16.04.1997 init code/data tagged - * 0.5 30.07.1997 made HDLC buffers bigger (solves a problem with the - * soundmodem driver) - * 0.6 05.04.1998 add spinlocks - * 0.7 03.08.1999 removed some old compatibility cruft - * 0.8 12.02.2000 adapted to softnet driver interface - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -/* --------------------------------------------------------------------- */ - -#define KISS_VERBOSE - -/* --------------------------------------------------------------------- */ - -#define PARAM_TXDELAY 1 -#define PARAM_PERSIST 2 -#define PARAM_SLOTTIME 3 -#define PARAM_TXTAIL 4 -#define PARAM_FULLDUP 5 -#define PARAM_HARDWARE 6 -#define PARAM_RETURN 255 - -/* --------------------------------------------------------------------- */ -/* - * the CRC routines are stolen from WAMPES - * by Dieter Deyke - */ - - -/*---------------------------------------------------------------------------*/ - -static inline void append_crc_ccitt(unsigned char *buffer, int len) -{ - unsigned int crc = crc_ccitt(0xffff, buffer, len) ^ 0xffff; - buffer += len; - *buffer++ = crc; - *buffer++ = crc >> 8; -} - -/*---------------------------------------------------------------------------*/ - -static inline int check_crc_ccitt(const unsigned char *buf, int cnt) -{ - return (crc_ccitt(0xffff, buf, cnt) & 0xffff) == 0xf0b8; -} - -/*---------------------------------------------------------------------------*/ - -#if 0 -static int calc_crc_ccitt(const unsigned char *buf, int cnt) -{ - unsigned int crc = 0xffff; - - for (; cnt > 0; cnt--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff]; - crc ^= 0xffff; - return crc & 0xffff; -} -#endif - -/* ---------------------------------------------------------------------- */ - -#define tenms_to_2flags(s,tenms) ((tenms * s->par.bitrate) / 100 / 16) - -/* ---------------------------------------------------------------------- */ -/* - * The HDLC routines - */ - -static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits, - int num) -{ - int added = 0; - - while (s->hdlcrx.rx_state && num >= 8) { - if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) { - s->hdlcrx.rx_state = 0; - return 0; - } - *s->hdlcrx.bp++ = bits >> (32-num); - s->hdlcrx.len++; - num -= 8; - added += 8; - } - return added; -} - -static void hdlc_rx_flag(struct net_device *dev, struct hdlcdrv_state *s) -{ - struct sk_buff *skb; - int pkt_len; - unsigned char *cp; - - if (s->hdlcrx.len < 4) - return; - if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len)) - return; - pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */ - if (!(skb = dev_alloc_skb(pkt_len))) { - printk("%s: memory squeeze, dropping packet\n", dev->name); - dev->stats.rx_dropped++; - return; - } - cp = skb_put(skb, pkt_len); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, s->hdlcrx.buffer, pkt_len - 1); - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; -} - -void hdlcdrv_receiver(struct net_device *dev, struct hdlcdrv_state *s) -{ - int i; - unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word; - - if (!s || s->magic != HDLCDRV_MAGIC) - return; - if (test_and_set_bit(0, &s->hdlcrx.in_hdlc_rx)) - return; - - while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) { - word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf); - -#ifdef HDLCDRV_DEBUG - hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word); -#endif /* HDLCDRV_DEBUG */ - s->hdlcrx.bitstream >>= 16; - s->hdlcrx.bitstream |= word << 16; - s->hdlcrx.bitbuf >>= 16; - s->hdlcrx.bitbuf |= word << 16; - s->hdlcrx.numbits += 16; - for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00, - mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; - i >= 0; - i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, - mask5 <<= 1, mask6 = (mask6 << 1) | 1) { - if ((s->hdlcrx.bitstream & mask1) == mask1) - s->hdlcrx.rx_state = 0; /* abort received */ - else if ((s->hdlcrx.bitstream & mask2) == mask3) { - /* flag received */ - if (s->hdlcrx.rx_state) { - hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf - << (8+i), - s->hdlcrx.numbits - -8-i); - hdlc_rx_flag(dev, s); - } - s->hdlcrx.len = 0; - s->hdlcrx.bp = s->hdlcrx.buffer; - s->hdlcrx.rx_state = 1; - s->hdlcrx.numbits = i; - } else if ((s->hdlcrx.bitstream & mask4) == mask5) { - /* stuffed bit */ - s->hdlcrx.numbits--; - s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) | - ((s->hdlcrx.bitbuf & mask6) << 1); - } - } - s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf, - s->hdlcrx.numbits); - } - clear_bit(0, &s->hdlcrx.in_hdlc_rx); -} - -/* ---------------------------------------------------------------------- */ - -static inline void do_kiss_params(struct hdlcdrv_state *s, - unsigned char *data, unsigned long len) -{ - -#ifdef KISS_VERBOSE -#define PKP(a,b) printk(KERN_INFO "hdlcdrv.c: channel params: " a "\n", b) -#else /* KISS_VERBOSE */ -#define PKP(a,b) -#endif /* KISS_VERBOSE */ - - if (len < 2) - return; - switch(data[0]) { - case PARAM_TXDELAY: - s->ch_params.tx_delay = data[1]; - PKP("TX delay = %ums", 10 * s->ch_params.tx_delay); - break; - case PARAM_PERSIST: - s->ch_params.ppersist = data[1]; - PKP("p persistence = %u", s->ch_params.ppersist); - break; - case PARAM_SLOTTIME: - s->ch_params.slottime = data[1]; - PKP("slot time = %ums", s->ch_params.slottime); - break; - case PARAM_TXTAIL: - s->ch_params.tx_tail = data[1]; - PKP("TX tail = %ums", s->ch_params.tx_tail); - break; - case PARAM_FULLDUP: - s->ch_params.fulldup = !!data[1]; - PKP("%s duplex", s->ch_params.fulldup ? "full" : "half"); - break; - default: - break; - } -#undef PKP -} - -/* ---------------------------------------------------------------------- */ - -void hdlcdrv_transmitter(struct net_device *dev, struct hdlcdrv_state *s) -{ - unsigned int mask1, mask2, mask3; - int i; - struct sk_buff *skb; - int pkt_len; - - if (!s || s->magic != HDLCDRV_MAGIC) - return; - if (test_and_set_bit(0, &s->hdlctx.in_hdlc_tx)) - return; - for (;;) { - if (s->hdlctx.numbits >= 16) { - if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) { - clear_bit(0, &s->hdlctx.in_hdlc_tx); - return; - } - hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf); - s->hdlctx.bitbuf >>= 16; - s->hdlctx.numbits -= 16; - } - switch (s->hdlctx.tx_state) { - default: - clear_bit(0, &s->hdlctx.in_hdlc_tx); - return; - case 0: - case 1: - if (s->hdlctx.numflags) { - s->hdlctx.numflags--; - s->hdlctx.bitbuf |= - 0x7e7e << s->hdlctx.numbits; - s->hdlctx.numbits += 16; - break; - } - if (s->hdlctx.tx_state == 1) { - clear_bit(0, &s->hdlctx.in_hdlc_tx); - return; - } - if (!(skb = s->skb)) { - int flgs = tenms_to_2flags(s, s->ch_params.tx_tail); - if (flgs < 2) - flgs = 2; - s->hdlctx.tx_state = 1; - s->hdlctx.numflags = flgs; - break; - } - s->skb = NULL; - netif_wake_queue(dev); - pkt_len = skb->len-1; /* strip KISS byte */ - if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) { - s->hdlctx.tx_state = 0; - s->hdlctx.numflags = 1; - dev_kfree_skb_irq(skb); - break; - } - skb_copy_from_linear_data_offset(skb, 1, - s->hdlctx.buffer, - pkt_len); - dev_kfree_skb_irq(skb); - s->hdlctx.bp = s->hdlctx.buffer; - append_crc_ccitt(s->hdlctx.buffer, pkt_len); - s->hdlctx.len = pkt_len+2; /* the appended CRC */ - s->hdlctx.tx_state = 2; - s->hdlctx.bitstream = 0; - dev->stats.tx_packets++; - break; - case 2: - if (!s->hdlctx.len) { - s->hdlctx.tx_state = 0; - s->hdlctx.numflags = 1; - break; - } - s->hdlctx.len--; - s->hdlctx.bitbuf |= *s->hdlctx.bp << - s->hdlctx.numbits; - s->hdlctx.bitstream >>= 8; - s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16; - mask1 = 0x1f000; - mask2 = 0x10000; - mask3 = 0xffffffff >> (31-s->hdlctx.numbits); - s->hdlctx.numbits += 8; - for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, - mask3 = (mask3 << 1) | 1) { - if ((s->hdlctx.bitstream & mask1) != mask1) - continue; - s->hdlctx.bitstream &= ~mask2; - s->hdlctx.bitbuf = - (s->hdlctx.bitbuf & mask3) | - ((s->hdlctx.bitbuf & - (~mask3)) << 1); - s->hdlctx.numbits++; - mask3 = (mask3 << 1) | 1; - } - break; - } - } -} - -/* ---------------------------------------------------------------------- */ - -static void start_tx(struct net_device *dev, struct hdlcdrv_state *s) -{ - s->hdlctx.tx_state = 0; - s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay); - s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0; - hdlcdrv_transmitter(dev, s); - s->hdlctx.ptt = 1; - s->ptt_keyed++; -} - -/* ---------------------------------------------------------------------- */ - -void hdlcdrv_arbitrate(struct net_device *dev, struct hdlcdrv_state *s) -{ - if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt || !s->skb) - return; - if (s->ch_params.fulldup) { - start_tx(dev, s); - return; - } - if (s->hdlcrx.dcd) { - s->hdlctx.slotcnt = s->ch_params.slottime; - return; - } - if ((--s->hdlctx.slotcnt) > 0) - return; - s->hdlctx.slotcnt = s->ch_params.slottime; - if (get_random_u8() > s->ch_params.ppersist) - return; - start_tx(dev, s); -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== network driver interface ========================= - */ - -static netdev_tx_t hdlcdrv_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct hdlcdrv_state *sm = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (skb->data[0] != 0) { - do_kiss_params(sm, skb->data, skb->len); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - if (sm->skb) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - netif_stop_queue(dev); - sm->skb = skb; - return NETDEV_TX_OK; -} - -/* --------------------------------------------------------------------- */ - -static int hdlcdrv_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *)addr; - - /* addr is an AX.25 shifted ASCII mac address */ - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ - -static int hdlcdrv_open(struct net_device *dev) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - int i; - - if (!s->ops || !s->ops->open) - return -ENODEV; - - /* - * initialise some variables - */ - s->opened = 1; - s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0; - s->hdlcrx.in_hdlc_rx = 0; - s->hdlcrx.rx_state = 0; - - s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0; - s->hdlctx.in_hdlc_tx = 0; - s->hdlctx.tx_state = 1; - s->hdlctx.numflags = 0; - s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0; - s->hdlctx.ptt = 0; - s->hdlctx.slotcnt = s->ch_params.slottime; - s->hdlctx.calibrate = 0; - - i = s->ops->open(dev); - if (i) - return i; - netif_start_queue(dev); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * The inverse routine to hdlcdrv_open(). - */ - -static int hdlcdrv_close(struct net_device *dev) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - int i = 0; - - netif_stop_queue(dev); - - if (s->ops && s->ops->close) - i = s->ops->close(dev); - dev_kfree_skb(s->skb); - s->skb = NULL; - s->opened = 0; - return i; -} - -/* --------------------------------------------------------------------- */ - -static int hdlcdrv_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - struct hdlcdrv_ioctl bi; - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (in_compat_syscall()) /* to be implemented */ - return -ENOIOCTLCMD; - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - - switch (bi.cmd) { - default: - if (s->ops && s->ops->ioctl) - return s->ops->ioctl(dev, data, &bi, cmd); - return -ENOIOCTLCMD; - - case HDLCDRVCTL_GETCHANNELPAR: - bi.data.cp.tx_delay = s->ch_params.tx_delay; - bi.data.cp.tx_tail = s->ch_params.tx_tail; - bi.data.cp.slottime = s->ch_params.slottime; - bi.data.cp.ppersist = s->ch_params.ppersist; - bi.data.cp.fulldup = s->ch_params.fulldup; - break; - - case HDLCDRVCTL_SETCHANNELPAR: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - s->ch_params.tx_delay = bi.data.cp.tx_delay; - s->ch_params.tx_tail = bi.data.cp.tx_tail; - s->ch_params.slottime = bi.data.cp.slottime; - s->ch_params.ppersist = bi.data.cp.ppersist; - s->ch_params.fulldup = bi.data.cp.fulldup; - s->hdlctx.slotcnt = 1; - return 0; - - case HDLCDRVCTL_GETMODEMPAR: - bi.data.mp.iobase = dev->base_addr; - bi.data.mp.irq = dev->irq; - bi.data.mp.dma = dev->dma; - bi.data.mp.dma2 = s->ptt_out.dma2; - bi.data.mp.seriobase = s->ptt_out.seriobase; - bi.data.mp.pariobase = s->ptt_out.pariobase; - bi.data.mp.midiiobase = s->ptt_out.midiiobase; - break; - - case HDLCDRVCTL_SETMODEMPAR: - if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev)) - return -EACCES; - dev->base_addr = bi.data.mp.iobase; - dev->irq = bi.data.mp.irq; - dev->dma = bi.data.mp.dma; - s->ptt_out.dma2 = bi.data.mp.dma2; - s->ptt_out.seriobase = bi.data.mp.seriobase; - s->ptt_out.pariobase = bi.data.mp.pariobase; - s->ptt_out.midiiobase = bi.data.mp.midiiobase; - return 0; - - case HDLCDRVCTL_GETSTAT: - bi.data.cs.ptt = hdlcdrv_ptt(s); - bi.data.cs.dcd = s->hdlcrx.dcd; - bi.data.cs.ptt_keyed = s->ptt_keyed; - bi.data.cs.tx_packets = dev->stats.tx_packets; - bi.data.cs.tx_errors = dev->stats.tx_errors; - bi.data.cs.rx_packets = dev->stats.rx_packets; - bi.data.cs.rx_errors = dev->stats.rx_errors; - break; - - case HDLCDRVCTL_OLDGETSTAT: - bi.data.ocs.ptt = hdlcdrv_ptt(s); - bi.data.ocs.dcd = s->hdlcrx.dcd; - bi.data.ocs.ptt_keyed = s->ptt_keyed; - break; - - case HDLCDRVCTL_CALIBRATE: - if(!capable(CAP_SYS_RAWIO)) - return -EPERM; - if (s->par.bitrate <= 0) - return -EINVAL; - if (bi.data.calibrate > INT_MAX / s->par.bitrate) - return -EINVAL; - s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16; - return 0; - - case HDLCDRVCTL_GETSAMPLES: -#ifndef HDLCDRV_DEBUG - return -EPERM; -#else /* HDLCDRV_DEBUG */ - if (s->bitbuf_channel.rd == s->bitbuf_channel.wr) - return -EAGAIN; - bi.data.bits = - s->bitbuf_channel.buffer[s->bitbuf_channel.rd]; - s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) % - sizeof(s->bitbuf_channel.buffer); - break; -#endif /* HDLCDRV_DEBUG */ - - case HDLCDRVCTL_GETBITS: -#ifndef HDLCDRV_DEBUG - return -EPERM; -#else /* HDLCDRV_DEBUG */ - if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr) - return -EAGAIN; - bi.data.bits = - s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd]; - s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) % - sizeof(s->bitbuf_hdlc.buffer); - break; -#endif /* HDLCDRV_DEBUG */ - - case HDLCDRVCTL_DRIVERNAME: - if (s->ops && s->ops->drvname) { - strscpy(bi.data.drivername, s->ops->drvname, - sizeof(bi.data.drivername)); - break; - } - bi.data.drivername[0] = '\0'; - break; - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -static const struct net_device_ops hdlcdrv_netdev = { - .ndo_open = hdlcdrv_open, - .ndo_stop = hdlcdrv_close, - .ndo_start_xmit = hdlcdrv_send_packet, - .ndo_siocdevprivate = hdlcdrv_siocdevprivate, - .ndo_set_mac_address = hdlcdrv_set_mac_address, -}; - -/* - * Initialize fields in hdlcdrv - */ -static void hdlcdrv_setup(struct net_device *dev) -{ - static const struct hdlcdrv_channel_params dflt_ch_params = { - 20, 2, 10, 40, 0 - }; - struct hdlcdrv_state *s = netdev_priv(dev); - - /* - * initialize the hdlcdrv_state struct - */ - s->ch_params = dflt_ch_params; - s->ptt_keyed = 0; - - spin_lock_init(&s->hdlcrx.hbuf.lock); - s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0; - s->hdlcrx.in_hdlc_rx = 0; - s->hdlcrx.rx_state = 0; - - spin_lock_init(&s->hdlctx.hbuf.lock); - s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0; - s->hdlctx.in_hdlc_tx = 0; - s->hdlctx.tx_state = 1; - s->hdlctx.numflags = 0; - s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0; - s->hdlctx.ptt = 0; - s->hdlctx.slotcnt = s->ch_params.slottime; - s->hdlctx.calibrate = 0; - -#ifdef HDLCDRV_DEBUG - s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0; - s->bitbuf_channel.shreg = 0x80; - - s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0; - s->bitbuf_hdlc.shreg = 0x80; -#endif /* HDLCDRV_DEBUG */ - - - /* Fill in the fields of the device structure */ - - s->skb = NULL; - - dev->netdev_ops = &hdlcdrv_netdev; - dev->header_ops = &ax25_header_ops; - - dev->type = ARPHRD_AX25; /* AF_AX25 device */ - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ - dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); - dev->tx_queue_len = 16; -} - -/* --------------------------------------------------------------------- */ -struct net_device *hdlcdrv_register(const struct hdlcdrv_ops *ops, - unsigned int privsize, const char *ifname, - unsigned int baseaddr, unsigned int irq, - unsigned int dma) -{ - struct net_device *dev; - struct hdlcdrv_state *s; - int err; - - if (privsize < sizeof(struct hdlcdrv_state)) - privsize = sizeof(struct hdlcdrv_state); - - dev = alloc_netdev(privsize, ifname, NET_NAME_UNKNOWN, hdlcdrv_setup); - if (!dev) - return ERR_PTR(-ENOMEM); - - /* - * initialize part of the hdlcdrv_state struct - */ - s = netdev_priv(dev); - s->magic = HDLCDRV_MAGIC; - s->ops = ops; - dev->base_addr = baseaddr; - dev->irq = irq; - dev->dma = dma; - - err = register_netdev(dev); - if (err < 0) { - printk(KERN_WARNING "hdlcdrv: cannot register net " - "device %s\n", dev->name); - free_netdev(dev); - dev = ERR_PTR(err); - } - return dev; -} - -/* --------------------------------------------------------------------- */ - -void hdlcdrv_unregister(struct net_device *dev) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - - BUG_ON(s->magic != HDLCDRV_MAGIC); - - if (s->opened && s->ops->close) - s->ops->close(dev); - unregister_netdev(dev); - - free_netdev(dev); -} - -/* --------------------------------------------------------------------- */ - -EXPORT_SYMBOL(hdlcdrv_receiver); -EXPORT_SYMBOL(hdlcdrv_transmitter); -EXPORT_SYMBOL(hdlcdrv_arbitrate); -EXPORT_SYMBOL(hdlcdrv_register); -EXPORT_SYMBOL(hdlcdrv_unregister); - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c deleted file mode 100644 index 5f38a002bd9e..000000000000 --- a/drivers/net/hamradio/mkiss.c +++ /dev/null @@ -1,980 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Copyright (C) Hans Alblas PE1AYX - * Copyright (C) 2004, 05 Ralf Baechle DL5RB - * Copyright (C) 2004, 05 Thomas Osterried DL9SAU - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define AX_MTU 236 - -/* some arch define END as assembly function ending, just undef it */ -#undef END -/* SLIP/KISS protocol characters. */ -#define END 0300 /* indicates end of frame */ -#define ESC 0333 /* indicates byte stuffing */ -#define ESC_END 0334 /* ESC ESC_END means END 'data' */ -#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ - -struct mkiss { - struct tty_struct *tty; /* ptr to TTY structure */ - struct net_device *dev; /* easy for intr handling */ - - /* These are pointers to the malloc()ed frame buffers. */ - spinlock_t buflock;/* lock for rbuf and xbuf */ - unsigned char *rbuff; /* receiver buffer */ - int rcount; /* received chars counter */ - unsigned char *xbuff; /* transmitter buffer */ - unsigned char *xhead; /* pointer to next byte to XMIT */ - int xleft; /* bytes left in XMIT queue */ - - /* Detailed SLIP statistics. */ - int mtu; /* Our mtu (to spot changes!) */ - int buffsize; /* Max buffers sizes */ - - unsigned long flags; /* Flag values/ mode etc */ - /* long req'd: used by set_bit --RR */ -#define AXF_INUSE 0 /* Channel in use */ -#define AXF_ESCAPE 1 /* ESC received */ -#define AXF_ERROR 2 /* Parity, etc. error */ -#define AXF_KEEPTEST 3 /* Keepalive test flag */ -#define AXF_OUTWAIT 4 /* is outpacket was flag */ - - int mode; - int crcmode; /* MW: for FlexNet, SMACK etc. */ - int crcauto; /* CRC auto mode */ - -#define CRC_MODE_NONE 0 -#define CRC_MODE_FLEX 1 -#define CRC_MODE_SMACK 2 -#define CRC_MODE_FLEX_TEST 3 -#define CRC_MODE_SMACK_TEST 4 - - refcount_t refcnt; - struct completion dead; -}; - -/*---------------------------------------------------------------------------*/ - -static const unsigned short crc_flex_table[] = { - 0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38, - 0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770, - 0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9, - 0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1, - 0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a, - 0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672, - 0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb, - 0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3, - 0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c, - 0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574, - 0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd, - 0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5, - 0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e, - 0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476, - 0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf, - 0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7, - 0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30, - 0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378, - 0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1, - 0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9, - 0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32, - 0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a, - 0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3, - 0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb, - 0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34, - 0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c, - 0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5, - 0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd, - 0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36, - 0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e, - 0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7, - 0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff -}; - -static unsigned short calc_crc_flex(unsigned char *cp, int size) -{ - unsigned short crc = 0xffff; - - while (size--) - crc = (crc << 8) ^ crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; - - return crc; -} - -static int check_crc_flex(unsigned char *cp, int size) -{ - unsigned short crc = 0xffff; - - if (size < 3) - return -1; - - while (size--) - crc = (crc << 8) ^ crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; - - if ((crc & 0xffff) != 0x7070) - return -1; - - return 0; -} - -static int check_crc_16(unsigned char *cp, int size) -{ - unsigned short crc = 0x0000; - - if (size < 3) - return -1; - - crc = crc16(0, cp, size); - - if (crc != 0x0000) - return -1; - - return 0; -} - -/* - * Standard encapsulation - */ - -static int kiss_esc(unsigned char *s, unsigned char *d, int len) -{ - unsigned char *ptr = d; - unsigned char c; - - /* - * Send an initial END character to flush out any data that may have - * accumulated in the receiver due to line noise. - */ - - *ptr++ = END; - - while (len-- > 0) { - switch (c = *s++) { - case END: - *ptr++ = ESC; - *ptr++ = ESC_END; - break; - case ESC: - *ptr++ = ESC; - *ptr++ = ESC_ESC; - break; - default: - *ptr++ = c; - break; - } - } - - *ptr++ = END; - - return ptr - d; -} - -/* - * MW: - * OK its ugly, but tell me a better solution without copying the - * packet to a temporary buffer :-) - */ -static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, - int len) -{ - unsigned char *ptr = d; - unsigned char c=0; - - *ptr++ = END; - while (len > 0) { - if (len > 2) - c = *s++; - else if (len > 1) - c = crc >> 8; - else - c = crc & 0xff; - - len--; - - switch (c) { - case END: - *ptr++ = ESC; - *ptr++ = ESC_END; - break; - case ESC: - *ptr++ = ESC; - *ptr++ = ESC_ESC; - break; - default: - *ptr++ = c; - break; - } - } - *ptr++ = END; - - return ptr - d; -} - -/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */ -static void ax_bump(struct mkiss *ax) -{ - struct sk_buff *skb; - int count; - - spin_lock_bh(&ax->buflock); - if (ax->rbuff[0] > 0x0f) { - if (ax->rbuff[0] & 0x80) { - if (check_crc_16(ax->rbuff, ax->rcount) < 0) { - ax->dev->stats.rx_errors++; - spin_unlock_bh(&ax->buflock); - - return; - } - if (ax->crcmode != CRC_MODE_SMACK && ax->crcauto) { - printk(KERN_INFO - "mkiss: %s: Switching to crc-smack\n", - ax->dev->name); - ax->crcmode = CRC_MODE_SMACK; - } - ax->rcount -= 2; - *ax->rbuff &= ~0x80; - } else if (ax->rbuff[0] & 0x20) { - if (check_crc_flex(ax->rbuff, ax->rcount) < 0) { - ax->dev->stats.rx_errors++; - spin_unlock_bh(&ax->buflock); - return; - } - if (ax->crcmode != CRC_MODE_FLEX && ax->crcauto) { - printk(KERN_INFO - "mkiss: %s: Switching to crc-flexnet\n", - ax->dev->name); - ax->crcmode = CRC_MODE_FLEX; - } - ax->rcount -= 2; - - /* - * dl9sau bugfix: the trailling two bytes flexnet crc - * will not be passed to the kernel. thus we have to - * correct the kissparm signature, because it indicates - * a crc but there's none - */ - *ax->rbuff &= ~0x20; - } - } - - count = ax->rcount; - - if ((skb = dev_alloc_skb(count)) == NULL) { - printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n", - ax->dev->name); - ax->dev->stats.rx_dropped++; - spin_unlock_bh(&ax->buflock); - return; - } - - skb_put_data(skb, ax->rbuff, count); - skb->protocol = ax25_type_trans(skb, ax->dev); - netif_rx(skb); - ax->dev->stats.rx_packets++; - ax->dev->stats.rx_bytes += count; - spin_unlock_bh(&ax->buflock); -} - -static void kiss_unesc(struct mkiss *ax, unsigned char s) -{ - switch (s) { - case END: - /* drop keeptest bit = VSV */ - if (test_bit(AXF_KEEPTEST, &ax->flags)) - clear_bit(AXF_KEEPTEST, &ax->flags); - - if (!test_and_clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2)) - ax_bump(ax); - - clear_bit(AXF_ESCAPE, &ax->flags); - ax->rcount = 0; - return; - - case ESC: - set_bit(AXF_ESCAPE, &ax->flags); - return; - case ESC_ESC: - if (test_and_clear_bit(AXF_ESCAPE, &ax->flags)) - s = ESC; - break; - case ESC_END: - if (test_and_clear_bit(AXF_ESCAPE, &ax->flags)) - s = END; - break; - } - - spin_lock_bh(&ax->buflock); - if (!test_bit(AXF_ERROR, &ax->flags)) { - if (ax->rcount < ax->buffsize) { - ax->rbuff[ax->rcount++] = s; - spin_unlock_bh(&ax->buflock); - return; - } - - ax->dev->stats.rx_over_errors++; - set_bit(AXF_ERROR, &ax->flags); - } - spin_unlock_bh(&ax->buflock); -} - -static int ax_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr_ax25 *sa = addr; - - netif_tx_lock_bh(dev); - netif_addr_lock(dev); - __dev_addr_set(dev, &sa->sax25_call, AX25_ADDR_LEN); - netif_addr_unlock(dev); - netif_tx_unlock_bh(dev); - - return 0; -} - -/*---------------------------------------------------------------------------*/ - -static void ax_changedmtu(struct mkiss *ax) -{ - struct net_device *dev = ax->dev; - unsigned char *xbuff, *rbuff, *oxbuff, *orbuff; - int len; - - len = dev->mtu * 2; - - /* - * allow for arrival of larger UDP packets, even if we say not to - * also fixes a bug in which SunOS sends 512-byte packets even with - * an MSS of 128 - */ - if (len < 576 * 2) - len = 576 * 2; - - xbuff = kmalloc(len + 4, GFP_ATOMIC); - rbuff = kmalloc(len + 4, GFP_ATOMIC); - - if (xbuff == NULL || rbuff == NULL) { - printk(KERN_ERR "mkiss: %s: unable to grow ax25 buffers, " - "MTU change cancelled.\n", - ax->dev->name); - dev->mtu = ax->mtu; - kfree(xbuff); - kfree(rbuff); - return; - } - - spin_lock_bh(&ax->buflock); - - oxbuff = ax->xbuff; - ax->xbuff = xbuff; - orbuff = ax->rbuff; - ax->rbuff = rbuff; - - if (ax->xleft) { - if (ax->xleft <= len) { - memcpy(ax->xbuff, ax->xhead, ax->xleft); - } else { - ax->xleft = 0; - dev->stats.tx_dropped++; - } - } - - ax->xhead = ax->xbuff; - - if (ax->rcount) { - if (ax->rcount <= len) { - memcpy(ax->rbuff, orbuff, ax->rcount); - } else { - ax->rcount = 0; - dev->stats.rx_over_errors++; - set_bit(AXF_ERROR, &ax->flags); - } - } - - ax->mtu = dev->mtu + 73; - ax->buffsize = len; - - spin_unlock_bh(&ax->buflock); - - kfree(oxbuff); - kfree(orbuff); -} - -/* Encapsulate one AX.25 packet and stuff into a TTY queue. */ -static void ax_encaps(struct net_device *dev, unsigned char *icp, int len) -{ - struct mkiss *ax = netdev_priv(dev); - unsigned char *p; - int actual, count; - - if (ax->mtu != ax->dev->mtu + 73) /* Someone has been ifconfigging */ - ax_changedmtu(ax); - - if (len > ax->mtu) { /* Sigh, shouldn't occur BUT ... */ - printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name); - dev->stats.tx_dropped++; - netif_start_queue(dev); - return; - } - - p = icp; - - spin_lock_bh(&ax->buflock); - if ((*p & 0x0f) != 0) { - /* Configuration Command (kissparms(1). - * Protocol spec says: never append CRC. - * This fixes a very old bug in the linux - * kiss driver. -- dl9sau */ - switch (*p & 0xff) { - case 0x85: - /* command from userspace especially for us, - * not for delivery to the tnc */ - if (len > 1) { - int cmd = (p[1] & 0xff); - switch(cmd) { - case 3: - ax->crcmode = CRC_MODE_SMACK; - break; - case 2: - ax->crcmode = CRC_MODE_FLEX; - break; - case 1: - ax->crcmode = CRC_MODE_NONE; - break; - case 0: - default: - ax->crcmode = CRC_MODE_SMACK_TEST; - cmd = 0; - } - ax->crcauto = (cmd ? 0 : 1); - printk(KERN_INFO "mkiss: %s: crc mode set to %d\n", - ax->dev->name, cmd); - } - spin_unlock_bh(&ax->buflock); - netif_start_queue(dev); - - return; - default: - count = kiss_esc(p, ax->xbuff, len); - } - } else { - unsigned short crc; - switch (ax->crcmode) { - case CRC_MODE_SMACK_TEST: - ax->crcmode = CRC_MODE_FLEX_TEST; - printk(KERN_INFO "mkiss: %s: Trying crc-smack\n", ax->dev->name); - fallthrough; - case CRC_MODE_SMACK: - *p |= 0x80; - crc = swab16(crc16(0, p, len)); - count = kiss_esc_crc(p, ax->xbuff, crc, len+2); - break; - case CRC_MODE_FLEX_TEST: - ax->crcmode = CRC_MODE_NONE; - printk(KERN_INFO "mkiss: %s: Trying crc-flexnet\n", ax->dev->name); - fallthrough; - case CRC_MODE_FLEX: - *p |= 0x20; - crc = calc_crc_flex(p, len); - count = kiss_esc_crc(p, ax->xbuff, crc, len+2); - break; - - default: - count = kiss_esc(p, ax->xbuff, len); - } - } - spin_unlock_bh(&ax->buflock); - - set_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); - actual = ax->tty->ops->write(ax->tty, ax->xbuff, count); - dev->stats.tx_packets++; - dev->stats.tx_bytes += actual; - - netif_trans_update(ax->dev); - ax->xleft = count - actual; - ax->xhead = ax->xbuff + actual; -} - -/* Encapsulate an AX.25 packet and kick it into a TTY queue. */ -static netdev_tx_t ax_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (!netif_running(dev)) { - printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name); - return NETDEV_TX_BUSY; - } - - if (netif_queue_stopped(dev)) { - /* - * May be we must check transmitter timeout here ? - * 14 Oct 1994 Dmitry Gorodchanin. - */ - if (time_before(jiffies, dev_trans_start(dev) + 20 * HZ)) { - /* 20 sec timeout not reached */ - return NETDEV_TX_BUSY; - } - - printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name, - (tty_chars_in_buffer(ax->tty) || ax->xleft) ? - "bad line quality" : "driver error"); - - ax->xleft = 0; - clear_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); - netif_start_queue(dev); - } - - /* We were not busy, so we are now... :-) */ - netif_stop_queue(dev); - ax_encaps(dev, skb->data, skb->len); - kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static int ax_open_dev(struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - - if (ax->tty == NULL) - return -ENODEV; - - return 0; -} - -/* Open the low-level part of the AX25 channel. Easy! */ -static int ax_open(struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - unsigned long len; - - if (ax->tty == NULL) - return -ENODEV; - - /* - * Allocate the frame buffers: - * - * rbuff Receive buffer. - * xbuff Transmit buffer. - */ - len = dev->mtu * 2; - - /* - * allow for arrival of larger UDP packets, even if we say not to - * also fixes a bug in which SunOS sends 512-byte packets even with - * an MSS of 128 - */ - if (len < 576 * 2) - len = 576 * 2; - - if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) - goto norbuff; - - if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) - goto noxbuff; - - ax->mtu = dev->mtu + 73; - ax->buffsize = len; - ax->rcount = 0; - ax->xleft = 0; - - ax->flags &= (1 << AXF_INUSE); /* Clear ESCAPE & ERROR flags */ - - spin_lock_init(&ax->buflock); - - return 0; - -noxbuff: - kfree(ax->rbuff); - -norbuff: - return -ENOMEM; -} - - -/* Close the low-level part of the AX25 channel. Easy! */ -static int ax_close(struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - - if (ax->tty) - clear_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); - - netif_stop_queue(dev); - - return 0; -} - -static const struct net_device_ops ax_netdev_ops = { - .ndo_open = ax_open_dev, - .ndo_stop = ax_close, - .ndo_start_xmit = ax_xmit, - .ndo_set_mac_address = ax_set_mac_address, -}; - -static void ax_setup(struct net_device *dev) -{ - /* Finish setting up the DEVICE info. */ - dev->mtu = AX_MTU; - dev->hard_header_len = AX25_MAX_HEADER_LEN; - dev->addr_len = AX25_ADDR_LEN; - dev->type = ARPHRD_AX25; - dev->tx_queue_len = 10; - dev->header_ops = &ax25_header_ops; - dev->netdev_ops = &ax_netdev_ops; - - - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); - - dev->flags = IFF_BROADCAST | IFF_MULTICAST; -} - -/* - * We have a potential race on dereferencing tty->disc_data, because the tty - * layer provides no locking at all - thus one cpu could be running - * sixpack_receive_buf while another calls sixpack_close, which zeroes - * tty->disc_data and frees the memory that sixpack_receive_buf is using. The - * best way to fix this is to use a rwlock in the tty struct, but for now we - * use a single global rwlock for all ttys in ppp line discipline. - */ -static DEFINE_RWLOCK(disc_data_lock); - -static struct mkiss *mkiss_get(struct tty_struct *tty) -{ - struct mkiss *ax; - - read_lock(&disc_data_lock); - ax = tty->disc_data; - if (ax) - refcount_inc(&ax->refcnt); - read_unlock(&disc_data_lock); - - return ax; -} - -static void mkiss_put(struct mkiss *ax) -{ - if (refcount_dec_and_test(&ax->refcnt)) - complete(&ax->dead); -} - -static int crc_force = 0; /* Can be overridden with insmod */ - -static int mkiss_open(struct tty_struct *tty) -{ - struct net_device *dev; - struct mkiss *ax; - int err; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (tty->ops->write == NULL) - return -EOPNOTSUPP; - - dev = alloc_netdev(sizeof(struct mkiss), "ax%d", NET_NAME_UNKNOWN, - ax_setup); - if (!dev) { - err = -ENOMEM; - goto out; - } - - ax = netdev_priv(dev); - ax->dev = dev; - - spin_lock_init(&ax->buflock); - refcount_set(&ax->refcnt, 1); - init_completion(&ax->dead); - - ax->tty = tty; - tty->disc_data = ax; - tty->receive_room = 65535; - - tty_driver_flush_buffer(tty); - - /* Restore default settings */ - dev->type = ARPHRD_AX25; - - /* Perform the low-level AX25 initialization. */ - err = ax_open(ax->dev); - if (err) - goto out_free_netdev; - - err = register_netdev(dev); - if (err) - goto out_free_buffers; - - /* after register_netdev() - because else printk smashes the kernel */ - switch (crc_force) { - case 3: - ax->crcmode = CRC_MODE_SMACK; - printk(KERN_INFO "mkiss: %s: crc mode smack forced.\n", - ax->dev->name); - break; - case 2: - ax->crcmode = CRC_MODE_FLEX; - printk(KERN_INFO "mkiss: %s: crc mode flexnet forced.\n", - ax->dev->name); - break; - case 1: - ax->crcmode = CRC_MODE_NONE; - printk(KERN_INFO "mkiss: %s: crc mode disabled.\n", - ax->dev->name); - break; - case 0: - default: - crc_force = 0; - printk(KERN_INFO "mkiss: %s: crc mode is auto.\n", - ax->dev->name); - ax->crcmode = CRC_MODE_SMACK_TEST; - } - ax->crcauto = (crc_force ? 0 : 1); - - netif_start_queue(dev); - - /* Done. We have linked the TTY line to a channel. */ - return 0; - -out_free_buffers: - kfree(ax->rbuff); - kfree(ax->xbuff); - -out_free_netdev: - free_netdev(dev); - -out: - return err; -} - -static void mkiss_close(struct tty_struct *tty) -{ - struct mkiss *ax; - - write_lock_irq(&disc_data_lock); - ax = tty->disc_data; - tty->disc_data = NULL; - write_unlock_irq(&disc_data_lock); - - if (!ax) - return; - - /* - * We have now ensured that nobody can start using ap from now on, but - * we have to wait for all existing users to finish. - */ - if (!refcount_dec_and_test(&ax->refcnt)) - wait_for_completion(&ax->dead); - /* - * Halt the transmit queue so that a new transmit cannot scribble - * on our buffers - */ - netif_stop_queue(ax->dev); - - unregister_netdev(ax->dev); - - /* Free all AX25 frame buffers after unreg. */ - kfree(ax->rbuff); - kfree(ax->xbuff); - - ax->tty = NULL; - - free_netdev(ax->dev); -} - -/* Perform I/O control on an active ax25 channel. */ -static int mkiss_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct mkiss *ax = mkiss_get(tty); - struct net_device *dev; - unsigned int tmp, err; - - /* First make sure we're connected. */ - if (ax == NULL) - return -ENXIO; - dev = ax->dev; - - switch (cmd) { - case SIOCGIFNAME: - err = copy_to_user((void __user *) arg, ax->dev->name, - strlen(ax->dev->name) + 1) ? -EFAULT : 0; - break; - - case SIOCGIFENCAP: - err = put_user(4, (int __user *) arg); - break; - - case SIOCSIFENCAP: - if (get_user(tmp, (int __user *) arg)) { - err = -EFAULT; - break; - } - - ax->mode = tmp; - dev->addr_len = AX25_ADDR_LEN; - dev->hard_header_len = AX25_KISS_HEADER_LEN + - AX25_MAX_HEADER_LEN + 3; - dev->type = ARPHRD_AX25; - - err = 0; - break; - - case SIOCSIFHWADDR: { - char addr[AX25_ADDR_LEN]; - - if (copy_from_user(&addr, - (void __user *) arg, AX25_ADDR_LEN)) { - err = -EFAULT; - break; - } - - netif_tx_lock_bh(dev); - __dev_addr_set(dev, addr, AX25_ADDR_LEN); - netif_tx_unlock_bh(dev); - - err = 0; - break; - } - default: - err = -ENOIOCTLCMD; - } - - mkiss_put(ax); - - return err; -} - -/* - * Handle the 'receiver data ready' interrupt. - * This function is called by the 'tty_io' module in the kernel when - * a block of data has been received, which can now be decapsulated - * and sent on to the AX.25 layer for further processing. - */ -static void mkiss_receive_buf(struct tty_struct *tty, const u8 *cp, - const u8 *fp, size_t count) -{ - struct mkiss *ax = mkiss_get(tty); - - if (!ax) - return; - - /* - * Argh! mtu change time! - costs us the packet part received - * at the change - */ - if (ax->mtu != ax->dev->mtu + 73) - ax_changedmtu(ax); - - /* Read the characters out of the buffer */ - while (count--) { - if (fp != NULL && *fp++) { - if (!test_and_set_bit(AXF_ERROR, &ax->flags)) - ax->dev->stats.rx_errors++; - cp++; - continue; - } - - kiss_unesc(ax, *cp++); - } - - mkiss_put(ax); - tty_unthrottle(tty); -} - -/* - * Called by the driver when there's room for more data. If we have - * more packets to send, we send them here. - */ -static void mkiss_write_wakeup(struct tty_struct *tty) -{ - struct mkiss *ax = mkiss_get(tty); - int actual; - - if (!ax) - return; - - if (ax->xleft <= 0) { - /* Now serial buffer is almost free & we can start - * transmission of another packet - */ - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - - netif_wake_queue(ax->dev); - goto out; - } - - actual = tty->ops->write(tty, ax->xhead, ax->xleft); - ax->xleft -= actual; - ax->xhead += actual; - -out: - mkiss_put(ax); -} - -static struct tty_ldisc_ops ax_ldisc = { - .owner = THIS_MODULE, - .num = N_AX25, - .name = "mkiss", - .open = mkiss_open, - .close = mkiss_close, - .ioctl = mkiss_ioctl, - .receive_buf = mkiss_receive_buf, - .write_wakeup = mkiss_write_wakeup -}; - -static const char banner[] __initconst = KERN_INFO \ - "mkiss: AX.25 Multikiss, Hans Albas PE1AYX\n"; -static const char msg_regfail[] __initconst = KERN_ERR \ - "mkiss: can't register line discipline (err = %d)\n"; - -static int __init mkiss_init_driver(void) -{ - int status; - - printk(banner); - - status = tty_register_ldisc(&ax_ldisc); - if (status != 0) - printk(msg_regfail, status); - - return status; -} - -static void __exit mkiss_exit_driver(void) -{ - tty_unregister_ldisc(&ax_ldisc); -} - -MODULE_AUTHOR("Ralf Baechle DL5RB "); -MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs"); -module_param(crc_force, int, 0); -MODULE_PARM_DESC(crc_force, "crc [0 = auto | 1 = none | 2 = flexnet | 3 = smack]"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_AX25); - -module_init(mkiss_init_driver); -module_exit(mkiss_exit_driver); diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c deleted file mode 100644 index 8569db4a7140..000000000000 --- a/drivers/net/hamradio/scc.c +++ /dev/null @@ -1,2179 +0,0 @@ -#define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $" - -#define VERSION "3.0" - -/* - * Please use z8530drv-utils-3.0 with this version. - * ------------------ - * - * You can find a subset of the documentation in - * Documentation/networking/device_drivers/hamradio/z8530drv.rst. - */ - -/* - ******************************************************************** - * SCC.C - Linux driver for Z8530 based HDLC cards for AX.25 * - ******************************************************************** - - - ******************************************************************** - - Copyright (c) 1993, 2000 Joerg Reuter DL1BKE - - portions (c) 1993 Guido ten Dolle PE1NNZ - - ******************************************************************** - - The driver and the programs in the archive are UNDER CONSTRUCTION. - The code is likely to fail, and so your kernel could --- even - a whole network. - - This driver is intended for Amateur Radio use. If you are running it - for commercial purposes, please drop me a note. I am nosy... - - ...BUT: - - ! You m u s t recognize the appropriate legislations of your country ! - ! before you connect a radio to the SCC board and start to transmit or ! - ! receive. The GPL allows you to use the d r i v e r, NOT the RADIO! ! - - For non-Amateur-Radio use please note that you might need a special - allowance/licence from the designer of the SCC Board and/or the - MODEM. - - This program is free software; you can redistribute it and/or modify - it under the terms of the (modified) GNU General Public License - delivered with the Linux kernel source. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should find a copy of the GNU General Public License in - /usr/src/linux/COPYING; - - ******************************************************************** - - - Incomplete history of z8530drv: - ------------------------------- - - 1994-09-13 started to write the driver, rescued most of my own - code (and Hans Alblas' memory buffer pool concept) from - an earlier project "sccdrv" which was initiated by - Guido ten Dolle. Not much of the old driver survived, - though. The first version I put my hands on was sccdrv1.3 - from August 1993. The memory buffer pool concept - appeared in an unauthorized sccdrv version (1.5) from - August 1994. - - 1995-01-31 changed copyright notice to GPL without limitations. - - . - . - . - - 1996-10-05 New semester, new driver... - - * KISS TNC emulator removed (TTY driver) - * Source moved to drivers/net/ - * Includes Z8530 defines from drivers/net/z8530.h - * Uses sk_buffer memory management - * Reduced overhead of /proc/net/z8530drv output - * Streamlined quite a lot things - * Invents brand new bugs... ;-) - - The move to version number 3.0 reflects theses changes. - You can use 'kissbridge' if you need a KISS TNC emulator. - - 1996-12-13 Fixed for Linux networking changes. (G4KLX) - 1997-01-08 Fixed the remaining problems. - 1997-04-02 Hopefully fixed the problems with the new *_timer() - routines, added calibration code. - 1997-10-12 Made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO - 1998-01-29 Small fix to avoid lock-up on initialization - 1998-09-29 Fixed the "grouping" bugs, tx_inhibit works again, - using dev->tx_queue_len now instead of MAXQUEUE now. - 1998-10-21 Postponed the spinlock changes, would need a lot of - testing I currently don't have the time to. Softdcd doesn't - work. - 1998-11-04 Softdcd does not work correctly in DPLL mode, in fact it - never did. The DPLL locks on noise, the SYNC unit sees - flags that aren't... Restarting the DPLL does not help - either, it resynchronizes too slow and the first received - frame gets lost. - 2000-02-13 Fixed for new network driver interface changes, still - does TX timeouts itself since it uses its own queue - scheme. - - Thanks to all who contributed to this driver with ideas and bug - reports! - - NB -- if you find errors, change something, please let me know - first before you distribute it... And please don't touch - the version number. Just replace my callsign in - "v3.0.dl1bke" with your own. Just to avoid confusion... - - If you want to add your modification to the linux distribution - please (!) contact me first. - - New versions of the driver will be announced on the linux-hams - mailing list on vger.kernel.org. To subscribe send an e-mail - to majordomo@vger.kernel.org with the following line in - the body of the mail: - - subscribe linux-hams - - The content of the "Subject" field will be ignored. - - vy 73, - Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ABH.#BAY.DEU.EU - Internet: jreuter@yaina.de - www : http://yaina.de/jreuter -*/ - -/* ----------------------------------------------------------------------- */ - -#undef SCC_LDELAY /* slow it even a bit more down */ -#undef SCC_DONT_CHECK /* don't look if the SCCs you specified are available */ - -#define SCC_MAXCHIPS 4 /* number of max. supported chips */ -#define SCC_BUFSIZE 384 /* must not exceed 4096 */ -#undef SCC_DEBUG - -#define SCC_DEFAULT_CLOCK 4915200 - /* default pclock if nothing is specified */ - -/* ----------------------------------------------------------------------- */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include "z8530.h" - -static const char banner[] __initconst = KERN_INFO \ - "AX.25: Z8530 SCC driver version "VERSION".dl1bke\n"; - -static void t_dwait(struct timer_list *t); -static void t_txdelay(struct timer_list *t); -static void t_tail(struct timer_list *t); -static void t_busy(struct timer_list *); -static void t_maxkeyup(struct timer_list *); -static void t_idle(struct timer_list *t); -static void scc_tx_done(struct scc_channel *); -static void scc_start_tx_timer(struct scc_channel *, - void (*)(struct timer_list *), unsigned long); -static void scc_start_maxkeyup(struct scc_channel *); -static void scc_start_defer(struct scc_channel *); - -static void z8530_init(void); - -static void init_channel(struct scc_channel *scc); -static void scc_key_trx (struct scc_channel *scc, char tx); -static void scc_init_timer(struct scc_channel *scc); - -static int scc_net_alloc(const char *name, struct scc_channel *scc); -static void scc_net_setup(struct net_device *dev); -static int scc_net_open(struct net_device *dev); -static int scc_net_close(struct net_device *dev); -static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb); -static netdev_tx_t scc_net_tx(struct sk_buff *skb, - struct net_device *dev); -static int scc_net_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd); -static int scc_net_set_mac_address(struct net_device *dev, void *addr); -static struct net_device_stats * scc_net_get_stats(struct net_device *dev); - -static unsigned char SCC_DriverName[] = "scc"; - -static struct irqflags { unsigned char used : 1; } Ivec[NR_IRQS]; - -static struct scc_channel SCC_Info[2 * SCC_MAXCHIPS]; /* information per channel */ - -static struct scc_ctrl { - io_port chan_A; - io_port chan_B; - int irq; -} SCC_ctrl[SCC_MAXCHIPS+1]; - -static unsigned char Driver_Initialized; -static int Nchips; -static io_port Vector_Latch; - - -/* ******************************************************************** */ -/* * Port Access Functions * */ -/* ******************************************************************** */ - -/* These provide interrupt save 2-step access to the Z8530 registers */ - -static DEFINE_SPINLOCK(iolock); /* Guards paired accesses */ - -static inline unsigned char InReg(io_port port, unsigned char reg) -{ - unsigned long flags; - unsigned char r; - - spin_lock_irqsave(&iolock, flags); -#ifdef SCC_LDELAY - Outb(port, reg); - udelay(SCC_LDELAY); - r=Inb(port); - udelay(SCC_LDELAY); -#else - Outb(port, reg); - r=Inb(port); -#endif - spin_unlock_irqrestore(&iolock, flags); - return r; -} - -static inline void OutReg(io_port port, unsigned char reg, unsigned char val) -{ - unsigned long flags; - - spin_lock_irqsave(&iolock, flags); -#ifdef SCC_LDELAY - Outb(port, reg); udelay(SCC_LDELAY); - Outb(port, val); udelay(SCC_LDELAY); -#else - Outb(port, reg); - Outb(port, val); -#endif - spin_unlock_irqrestore(&iolock, flags); -} - -static inline void wr(struct scc_channel *scc, unsigned char reg, - unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] = val)); -} - -static inline void or(struct scc_channel *scc, unsigned char reg, unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val)); -} - -static inline void cl(struct scc_channel *scc, unsigned char reg, unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val)); -} - -/* ******************************************************************** */ -/* * Some useful macros * */ -/* ******************************************************************** */ - -static inline void scc_discard_buffers(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - if (scc->tx_buff != NULL) - { - dev_kfree_skb_irq(scc->tx_buff); - scc->tx_buff = NULL; - } - - while (!skb_queue_empty(&scc->tx_queue)) - dev_kfree_skb_irq(skb_dequeue(&scc->tx_queue)); - - spin_unlock_irqrestore(&scc->lock, flags); -} - - - -/* ******************************************************************** */ -/* * Interrupt Service Routines * */ -/* ******************************************************************** */ - - -/* ----> subroutines for the interrupt handlers <---- */ - -static inline void scc_notify(struct scc_channel *scc, int event) -{ - struct sk_buff *skb; - char *bp; - - if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA) - return; - - skb = dev_alloc_skb(2); - if (skb != NULL) - { - bp = skb_put(skb, 2); - *bp++ = PARAM_HWEVENT; - *bp++ = event; - scc_net_rx(scc, skb); - } else - scc->stat.nospace++; -} - -static inline void flush_rx_FIFO(struct scc_channel *scc) -{ - int k; - - for (k=0; k<3; k++) - Inb(scc->data); - - if(scc->rx_buff != NULL) /* did we receive something? */ - { - scc->stat.rxerrs++; /* then count it as an error */ - dev_kfree_skb_irq(scc->rx_buff); - scc->rx_buff = NULL; - } -} - -static void start_hunt(struct scc_channel *scc) -{ - if ((scc->modem.clocksrc != CLK_EXTERNAL)) - OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ - or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ -} - -/* ----> four different interrupt handlers for Tx, Rx, changing of */ -/* DCD/CTS and Rx/Tx errors */ - -/* Transmitter interrupt handler */ -static inline void scc_txint(struct scc_channel *scc) -{ - struct sk_buff *skb; - - scc->stat.txints++; - skb = scc->tx_buff; - - /* send first octet */ - - if (skb == NULL) - { - skb = skb_dequeue(&scc->tx_queue); - scc->tx_buff = skb; - netif_wake_queue(scc->dev); - - if (skb == NULL) - { - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - if (skb->len == 0) /* Paranoia... */ - { - dev_kfree_skb_irq(skb); - scc->tx_buff = NULL; - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - scc->stat.tx_state = TXS_ACTIVE; - - OutReg(scc->ctrl, R0, RES_Tx_CRC); - /* reset CRC generator */ - or(scc,R10,ABUNDER); /* re-install underrun protection */ - Outb(scc->data,*skb->data); /* send byte */ - skb_pull(skb, 1); - - if (!scc->enhanced) /* reset EOM latch */ - Outb(scc->ctrl,RES_EOM_L); - return; - } - - /* End Of Frame... */ - - if (skb->len == 0) - { - Outb(scc->ctrl, RES_Tx_P); /* reset pending int */ - cl(scc, R10, ABUNDER); /* send CRC */ - dev_kfree_skb_irq(skb); - scc->tx_buff = NULL; - scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */ - return; - } - - /* send octet */ - - Outb(scc->data,*skb->data); - skb_pull(skb, 1); -} - - -/* External/Status interrupt handler */ -static inline void scc_exint(struct scc_channel *scc) -{ - unsigned char status,changes,chg_and_stat; - - scc->stat.exints++; - - status = InReg(scc->ctrl,R0); - changes = status ^ scc->status; - chg_and_stat = changes & status; - - /* ABORT: generated whenever DCD drops while receiving */ - - if (chg_and_stat & BRK_ABRT) /* Received an ABORT */ - flush_rx_FIFO(scc); - - /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */ - - if ((changes & SYNC_HUNT) && scc->kiss.softdcd) - { - if (status & SYNC_HUNT) - { - scc->dcd = 0; - flush_rx_FIFO(scc); - if ((scc->modem.clocksrc != CLK_EXTERNAL)) - OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ - } else { - scc->dcd = 1; - } - - scc_notify(scc, scc->dcd? HWEV_DCD_OFF:HWEV_DCD_ON); - } - - /* DCD: on = start to receive packet, off = ABORT condition */ - /* (a successfully received packet generates a special condition int) */ - - if((changes & DCD) && !scc->kiss.softdcd) /* DCD input changed state */ - { - if(status & DCD) /* DCD is now ON */ - { - start_hunt(scc); - scc->dcd = 1; - } else { /* DCD is now OFF */ - cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */ - flush_rx_FIFO(scc); - scc->dcd = 0; - } - - scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF); - } - -#ifdef notdef - /* CTS: use external TxDelay (what's that good for?!) - * Anyway: If we _could_ use it (BayCom USCC uses CTS for - * own purposes) we _should_ use the "autoenable" feature - * of the Z8530 and not this interrupt... - */ - - if (chg_and_stat & CTS) /* CTS is now ON */ - { - if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */ - scc_start_tx_timer(scc, t_txdelay, 0); - } -#endif - - if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM)) - { - scc->stat.tx_under++; /* oops, an underrun! count 'em */ - Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */ - - if (scc->tx_buff != NULL) - { - dev_kfree_skb_irq(scc->tx_buff); - scc->tx_buff = NULL; - } - - or(scc,R10,ABUNDER); - scc_start_tx_timer(scc, t_txdelay, 0); /* restart transmission */ - } - - scc->status = status; - Outb(scc->ctrl,RES_EXT_INT); -} - - -/* Receiver interrupt handler */ -static inline void scc_rxint(struct scc_channel *scc) -{ - struct sk_buff *skb; - - scc->stat.rxints++; - - if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - Inb(scc->data); /* discard char */ - or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ - return; - } - - skb = scc->rx_buff; - - if (skb == NULL) - { - skb = dev_alloc_skb(scc->stat.bufsize); - if (skb == NULL) - { - scc->dev_stat.rx_dropped++; - scc->stat.nospace++; - Inb(scc->data); - or(scc, R3, ENT_HM); - return; - } - - scc->rx_buff = skb; - skb_put_u8(skb, 0); /* KISS data */ - } - - if (skb->len >= scc->stat.bufsize) - { -#ifdef notdef - printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); -#endif - dev_kfree_skb_irq(skb); - scc->rx_buff = NULL; - Inb(scc->data); - or(scc, R3, ENT_HM); - return; - } - - skb_put_u8(skb, Inb(scc->data)); -} - - -/* Receive Special Condition interrupt handler */ -static inline void scc_spint(struct scc_channel *scc) -{ - unsigned char status; - struct sk_buff *skb; - - scc->stat.spints++; - - status = InReg(scc->ctrl,R1); /* read receiver status */ - - Inb(scc->data); /* throw away Rx byte */ - skb = scc->rx_buff; - - if(status & Rx_OVR) /* receiver overrun */ - { - scc->stat.rx_over++; /* count them */ - or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ - - if (skb != NULL) - dev_kfree_skb_irq(skb); - scc->rx_buff = skb = NULL; - } - - if(status & END_FR && skb != NULL) /* end of frame */ - { - /* CRC okay, frame ends on 8 bit boundary and received something ? */ - - if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0) - { - /* ignore last received byte (first of the CRC bytes) */ - skb_trim(skb, skb->len-1); - scc_net_rx(scc, skb); - scc->rx_buff = NULL; - scc->stat.rxframes++; - } else { /* a bad frame */ - dev_kfree_skb_irq(skb); - scc->rx_buff = NULL; - scc->stat.rxerrs++; - } - } - - Outb(scc->ctrl,ERR_RES); -} - - -/* ----> interrupt service routine for the Z8530 <---- */ - -static void scc_isr_dispatch(struct scc_channel *scc, int vector) -{ - spin_lock(&scc->lock); - switch (vector & VECTOR_MASK) - { - case TXINT: scc_txint(scc); break; - case EXINT: scc_exint(scc); break; - case RXINT: scc_rxint(scc); break; - case SPINT: scc_spint(scc); break; - } - spin_unlock(&scc->lock); -} - -/* If the card has a latch for the interrupt vector (like the PA0HZP card) - use it to get the number of the chip that generated the int. - If not: poll all defined chips. - */ - -#define SCC_IRQTIMEOUT 30000 - -static irqreturn_t scc_isr(int irq, void *dev_id) -{ - int chip_irq = (long) dev_id; - unsigned char vector; - struct scc_channel *scc; - struct scc_ctrl *ctrl; - int k; - - if (Vector_Latch) - { - for(k=0; k < SCC_IRQTIMEOUT; k++) - { - Outb(Vector_Latch, 0); /* Generate INTACK */ - - /* Read the vector */ - if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; - if (vector & 0x01) break; - - scc=&SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; - - scc_isr_dispatch(scc, vector); - - OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */ - } - - if (k == SCC_IRQTIMEOUT) - printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n"); - - return IRQ_HANDLED; - } - - /* Find the SCC generating the interrupt by polling all attached SCCs - * reading RR3A (the interrupt pending register) - */ - - ctrl = SCC_ctrl; - while (ctrl->chan_A) - { - if (ctrl->irq != chip_irq) - { - ctrl++; - continue; - } - - scc = NULL; - for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++) - { - vector=InReg(ctrl->chan_B,R2); /* Read the vector */ - if (vector & 0x01) break; - - scc = &SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; - - scc_isr_dispatch(scc, vector); - } - - if (k == SCC_IRQTIMEOUT) - { - printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n"); - break; - } - - /* This looks weird and it is. At least the BayCom USCC doesn't - * use the Interrupt Daisy Chain, thus we'll have to start - * all over again to be sure not to miss an interrupt from - * (any of) the other chip(s)... - * Honestly, the situation *is* braindamaged... - */ - - if (scc != NULL) - { - OutReg(scc->ctrl,R0,RES_H_IUS); - ctrl = SCC_ctrl; - } else - ctrl++; - } - return IRQ_HANDLED; -} - - - -/* ******************************************************************** */ -/* * Init Channel */ -/* ******************************************************************** */ - - -/* ----> set SCC channel speed <---- */ - -static inline void set_brg(struct scc_channel *scc, unsigned int tc) -{ - cl(scc,R14,BRENABL); /* disable baudrate generator */ - wr(scc,R12,tc & 255); /* brg rate LOW */ - wr(scc,R13,tc >> 8); /* brg rate HIGH */ - or(scc,R14,BRENABL); /* enable baudrate generator */ -} - -static inline void set_speed(struct scc_channel *scc) -{ - unsigned long flags; - spin_lock_irqsave(&scc->lock, flags); - - if (scc->modem.speed > 0) /* paranoia... */ - set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2); - - spin_unlock_irqrestore(&scc->lock, flags); -} - - -/* ----> initialize a SCC channel <---- */ - -static inline void init_brg(struct scc_channel *scc) -{ - wr(scc, R14, BRSRC); /* BRG source = PCLK */ - OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ - OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */ -} - -/* - * Initialization according to the Z8530 manual (SGS-Thomson's version): - * - * 1. Modes and constants - * - * WR9 11000000 chip reset - * WR4 XXXXXXXX Tx/Rx control, async or sync mode - * WR1 0XX00X00 select W/REQ (optional) - * WR2 XXXXXXXX program interrupt vector - * WR3 XXXXXXX0 select Rx control - * WR5 XXXX0XXX select Tx control - * WR6 XXXXXXXX sync character - * WR7 XXXXXXXX sync character - * WR9 000X0XXX select interrupt control - * WR10 XXXXXXXX miscellaneous control (optional) - * WR11 XXXXXXXX clock control - * WR12 XXXXXXXX time constant lower byte (optional) - * WR13 XXXXXXXX time constant upper byte (optional) - * WR14 XXXXXXX0 miscellaneous control - * WR14 XXXSSSSS commands (optional) - * - * 2. Enables - * - * WR14 000SSSS1 baud rate enable - * WR3 SSSSSSS1 Rx enable - * WR5 SSSS1SSS Tx enable - * WR0 10000000 reset Tx CRG (optional) - * WR1 XSS00S00 DMA enable (optional) - * - * 3. Interrupt status - * - * WR15 XXXXXXXX enable external/status - * WR0 00010000 reset external status - * WR0 00010000 reset external status twice - * WR1 SSSXXSXX enable Rx, Tx and Ext/status - * WR9 000SXSSS enable master interrupt enable - * - * 1 = set to one, 0 = reset to zero - * X = user defined, S = same as previous init - * - * - * Note that the implementation differs in some points from above scheme. - * - */ - -static void init_channel(struct scc_channel *scc) -{ - timer_delete(&scc->tx_t); - timer_delete(&scc->tx_wdog); - - disable_irq(scc->irq); - - wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */ - wr(scc,R1,0); /* no W/REQ operation */ - wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */ - wr(scc,R5,Tx8|DTR|TxCRC_ENAB); /* TX 8 bits/char, disabled, DTR */ - wr(scc,R6,0); /* SDLC address zero (not used) */ - wr(scc,R7,FLAG); /* SDLC flag value */ - wr(scc,R9,VIS); /* vector includes status */ - wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */ - wr(scc,R14, 0); - - -/* set clock sources: - - CLK_DPLL: normal halfduplex operation - - RxClk: use DPLL - TxClk: use DPLL - TRxC mode DPLL output - - CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem) - - BayCom: others: - - TxClk = pin RTxC TxClk = pin TRxC - RxClk = pin TRxC RxClk = pin RTxC - - - CLK_DIVIDER: - RxClk = use DPLL - TxClk = pin RTxC - - BayCom: others: - pin TRxC = DPLL pin TRxC = BRG - (RxClk * 1) (RxClk * 32) -*/ - - - switch(scc->modem.clocksrc) - { - case CLK_DPLL: - wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); - init_brg(scc); - break; - - case CLK_DIVIDER: - wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI); - init_brg(scc); - break; - - case CLK_EXTERNAL: - wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP); - OutReg(scc->ctrl, R14, DISDPLL); - break; - - } - - set_speed(scc); /* set baudrate */ - - if(scc->enhanced) - { - or(scc,R15,SHDLCE|FIFOE); /* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */ - wr(scc,R7,AUTOEOM); - } - - if(scc->kiss.softdcd || (InReg(scc->ctrl,R0) & DCD)) - /* DCD is now ON */ - { - start_hunt(scc); - } - - /* enable ABORT, DCD & SYNC/HUNT interrupts */ - - wr(scc,R15, BRKIE|TxUIE|(scc->kiss.softdcd? SYNCIE:DCDIE)); - - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */ - - or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */ - - scc->status = InReg(scc->ctrl,R0); /* read initial status */ - - or(scc,R9,MIE); /* master interrupt enable */ - - scc_init_timer(scc); - - enable_irq(scc->irq); -} - - - - -/* ******************************************************************** */ -/* * SCC timer functions * */ -/* ******************************************************************** */ - - -/* ----> scc_key_trx sets the time constant for the baudrate - generator and keys the transmitter <---- */ - -static void scc_key_trx(struct scc_channel *scc, char tx) -{ - unsigned int time_const; - - if (scc->brand & PRIMUS) - Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0)); - - if (scc->modem.speed < 300) - scc->modem.speed = 1200; - - time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2; - - disable_irq(scc->irq); - - if (tx) - { - or(scc, R1, TxINT_ENAB); /* t_maxkeyup may have reset these */ - or(scc, R15, TxUIE); - } - - if (scc->modem.clocksrc == CLK_DPLL) - { /* force simplex operation */ - if (tx) - { -#ifdef CONFIG_SCC_TRXECHO - cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */ - cl(scc, R15, DCDIE|SYNCIE); /* No DCD changes, please */ -#endif - set_brg(scc, time_const); /* reprogram baudrate generator */ - - /* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */ - wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR); - - /* By popular demand: tx_inhibit */ - if (scc->kiss.tx_inhibit) - { - or(scc,R5, TxENAB); - scc->wreg[R5] |= RTS; - } else { - or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */ - } - } else { - cl(scc,R5,RTS|TxENAB); - - set_brg(scc, time_const); /* reprogram baudrate generator */ - - /* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */ - wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); - -#ifndef CONFIG_SCC_TRXECHO - if (scc->kiss.softdcd) -#endif - { - or(scc,R15, scc->kiss.softdcd? SYNCIE:DCDIE); - start_hunt(scc); - } - } - } else { - if (tx) - { -#ifdef CONFIG_SCC_TRXECHO - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - cl(scc, R3, RxENABLE); - cl(scc, R15, DCDIE|SYNCIE); - } -#endif - - if (scc->kiss.tx_inhibit) - { - or(scc,R5, TxENAB); - scc->wreg[R5] |= RTS; - } else { - or(scc,R5,RTS|TxENAB); /* enable tx */ - } - } else { - cl(scc,R5,RTS|TxENAB); /* disable tx */ - - if ((scc->kiss.fulldup == KISS_DUPLEX_HALF) && -#ifndef CONFIG_SCC_TRXECHO - scc->kiss.softdcd) -#else - 1) -#endif - { - or(scc, R15, scc->kiss.softdcd? SYNCIE:DCDIE); - start_hunt(scc); - } - } - } - - enable_irq(scc->irq); -} - - -/* ----> SCC timer interrupt handler and friends. <---- */ - -static void __scc_start_tx_timer(struct scc_channel *scc, - void (*handler)(struct timer_list *t), - unsigned long when) -{ - timer_delete(&scc->tx_t); - - if (when == 0) - { - handler(&scc->tx_t); - } else - if (when != TIMER_OFF) - { - scc->tx_t.function = handler; - scc->tx_t.expires = jiffies + (when*HZ)/100; - add_timer(&scc->tx_t); - } -} - -static void scc_start_tx_timer(struct scc_channel *scc, - void (*handler)(struct timer_list *t), - unsigned long when) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - __scc_start_tx_timer(scc, handler, when); - spin_unlock_irqrestore(&scc->lock, flags); -} - -static void scc_start_defer(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - - if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF) - { - scc->tx_wdog.function = t_busy; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer; - add_timer(&scc->tx_wdog); - } - spin_unlock_irqrestore(&scc->lock, flags); -} - -static void scc_start_maxkeyup(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - - if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF) - { - scc->tx_wdog.function = t_maxkeyup; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup; - add_timer(&scc->tx_wdog); - } - spin_unlock_irqrestore(&scc->lock, flags); -} - -/* - * This is called from scc_txint() when there are no more frames to send. - * Not exactly a timer function, but it is a close friend of the family... - */ - -static void scc_tx_done(struct scc_channel *scc) -{ - /* - * trx remains keyed in fulldup mode 2 until t_idle expires. - */ - - switch (scc->kiss.fulldup) - { - case KISS_DUPLEX_LINK: - scc->stat.tx_state = TXS_IDLE2; - if (scc->kiss.idletime != TIMER_OFF) - scc_start_tx_timer(scc, t_idle, - scc->kiss.idletime*100); - break; - case KISS_DUPLEX_OPTIMA: - scc_notify(scc, HWEV_ALL_SENT); - break; - default: - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } - - netif_wake_queue(scc->dev); -} - - -static unsigned char Rand = 17; - -static inline int is_grouped(struct scc_channel *scc) -{ - int k; - struct scc_channel *scc2; - unsigned char grp1, grp2; - - grp1 = scc->kiss.group; - - for (k = 0; k < (Nchips * 2); k++) - { - scc2 = &SCC_Info[k]; - grp2 = scc2->kiss.group; - - if (scc2 == scc || !(scc2->dev && grp2)) - continue; - - if ((grp1 & 0x3f) == (grp2 & 0x3f)) - { - if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) ) - return 1; - - if ( (grp1 & RXGROUP) && scc2->dcd ) - return 1; - } - } - return 0; -} - -/* DWAIT and SLOTTIME expired - * - * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer - * else key trx and start txdelay - * fulldup == 1: key trx and start txdelay - * fulldup == 2: mintime expired, reset status or key trx and start txdelay - */ - -static void t_dwait(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - - if (scc->stat.tx_state == TXS_WAIT) /* maxkeyup or idle timeout */ - { - if (skb_queue_empty(&scc->tx_queue)) { /* nothing to send */ - scc->stat.tx_state = TXS_IDLE; - netif_wake_queue(scc->dev); /* t_maxkeyup locked it. */ - return; - } - - scc->stat.tx_state = TXS_BUSY; - } - - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - Rand = Rand * 17 + 31; - - if (scc->dcd || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) ) - { - scc_start_defer(scc); - scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime); - return ; - } - } - - if ( !(scc->wreg[R5] & RTS) ) - { - scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); - } else { - scc_start_tx_timer(scc, t_txdelay, 0); - } -} - - -/* TXDELAY expired - * - * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog. - */ - -static void t_txdelay(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - - scc_start_maxkeyup(scc); - - if (scc->tx_buff == NULL) - { - disable_irq(scc->irq); - scc_txint(scc); - enable_irq(scc->irq); - } -} - - -/* TAILTIME expired - * - * switch off transmitter. If we were stopped by Maxkeyup restart - * transmission after 'mintime' seconds - */ - -static void t_tail(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - scc_key_trx(scc, TX_OFF); - spin_unlock_irqrestore(&scc->lock, flags); - - if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */ - { - scc->stat.tx_state = TXS_WAIT; - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - return; - } - - scc->stat.tx_state = TXS_IDLE; - netif_wake_queue(scc->dev); -} - - -/* BUSY timeout - * - * throw away send buffers if DCD remains active too long. - */ - -static void t_busy(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_wdog); - - timer_delete(&scc->tx_t); - netif_stop_queue(scc->dev); /* don't pile on the wabbit! */ - - scc_discard_buffers(scc); - scc->stat.txerrs++; - scc->stat.tx_state = TXS_IDLE; - - netif_wake_queue(scc->dev); -} - -/* MAXKEYUP timeout - * - * this is our watchdog. - */ - -static void t_maxkeyup(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_wdog); - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - /* - * let things settle down before we start to - * accept new data. - */ - - netif_stop_queue(scc->dev); - scc_discard_buffers(scc); - - timer_delete(&scc->tx_t); - - cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */ - cl(scc, R15, TxUIE); /* count it. */ - OutReg(scc->ctrl, R0, RES_Tx_P); - - spin_unlock_irqrestore(&scc->lock, flags); - - scc->stat.txerrs++; - scc->stat.tx_state = TXS_TIMEOUT; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); -} - -/* IDLE timeout - * - * in fulldup mode 2 it keys down the transmitter after 'idle' seconds - * of inactivity. We will not restart transmission before 'mintime' - * expires. - */ - -static void t_idle(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - - timer_delete(&scc->tx_wdog); - - scc_key_trx(scc, TX_OFF); - if(scc->kiss.mintime) - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - scc->stat.tx_state = TXS_WAIT; -} - -static void scc_init_timer(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - scc->stat.tx_state = TXS_IDLE; - spin_unlock_irqrestore(&scc->lock, flags); -} - - -/* ******************************************************************** */ -/* * Set/get L1 parameters * */ -/* ******************************************************************** */ - - -/* - * this will set the "hardware" parameters through KISS commands or ioctl() - */ - -#define CAST(x) (unsigned long)(x) - -static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) -{ - switch (cmd) - { - case PARAM_TXDELAY: scc->kiss.txdelay=arg; break; - case PARAM_PERSIST: scc->kiss.persist=arg; break; - case PARAM_SLOTTIME: scc->kiss.slottime=arg; break; - case PARAM_TXTAIL: scc->kiss.tailtime=arg; break; - case PARAM_FULLDUP: scc->kiss.fulldup=arg; break; - case PARAM_DTR: break; /* does someone need this? */ - case PARAM_GROUP: scc->kiss.group=arg; break; - case PARAM_IDLE: scc->kiss.idletime=arg; break; - case PARAM_MIN: scc->kiss.mintime=arg; break; - case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break; - case PARAM_WAIT: scc->kiss.waittime=arg; break; - case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break; - case PARAM_TX: scc->kiss.tx_inhibit=arg; break; - - case PARAM_SOFTDCD: - scc->kiss.softdcd=arg; - if (arg) - { - or(scc, R15, SYNCIE); - cl(scc, R15, DCDIE); - start_hunt(scc); - } else { - or(scc, R15, DCDIE); - cl(scc, R15, SYNCIE); - } - break; - - case PARAM_SPEED: - if (arg < 256) - scc->modem.speed=arg*100; - else - scc->modem.speed=arg; - - if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */ - set_speed(scc); - break; - - case PARAM_RTS: - if ( !(scc->wreg[R5] & RTS) ) - { - if (arg != TX_OFF) { - scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); - } - } else { - if (arg == TX_OFF) - { - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } - } - break; - - case PARAM_HWEVENT: - scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF); - break; - - default: return -EINVAL; - } - - return 0; -} - - - -static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd) -{ - switch (cmd) - { - case PARAM_TXDELAY: return CAST(scc->kiss.txdelay); - case PARAM_PERSIST: return CAST(scc->kiss.persist); - case PARAM_SLOTTIME: return CAST(scc->kiss.slottime); - case PARAM_TXTAIL: return CAST(scc->kiss.tailtime); - case PARAM_FULLDUP: return CAST(scc->kiss.fulldup); - case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd); - case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0); - case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0); - case PARAM_SPEED: return CAST(scc->modem.speed); - case PARAM_GROUP: return CAST(scc->kiss.group); - case PARAM_IDLE: return CAST(scc->kiss.idletime); - case PARAM_MIN: return CAST(scc->kiss.mintime); - case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup); - case PARAM_WAIT: return CAST(scc->kiss.waittime); - case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer); - case PARAM_TX: return CAST(scc->kiss.tx_inhibit); - default: return NO_SUCH_PARAM; - } - -} - -#undef CAST - -/* ******************************************************************* */ -/* * Send calibration pattern * */ -/* ******************************************************************* */ - -static void scc_stop_calibrate(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_wdog); - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - scc_key_trx(scc, TX_OFF); - wr(scc, R6, 0); - wr(scc, R7, FLAG); - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); - - netif_wake_queue(scc->dev); - spin_unlock_irqrestore(&scc->lock, flags); -} - - -static void -scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - netif_stop_queue(scc->dev); - scc_discard_buffers(scc); - - timer_delete(&scc->tx_wdog); - - scc->tx_wdog.function = scc_stop_calibrate; - scc->tx_wdog.expires = jiffies + HZ*duration; - add_timer(&scc->tx_wdog); - - /* This doesn't seem to work. Why not? */ - wr(scc, R6, 0); - wr(scc, R7, pattern); - - /* - * Don't know if this works. - * Damn, where is my Z8530 programming manual...? - */ - - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); - - scc_key_trx(scc, TX_ON); - spin_unlock_irqrestore(&scc->lock, flags); -} - -/* ******************************************************************* */ -/* * Init channel structures, special HW, etc... * */ -/* ******************************************************************* */ - -/* - * Reset the Z8530s and setup special hardware - */ - -static void z8530_init(void) -{ - const unsigned int nr_irqs = irq_get_nr_irqs(); - struct scc_channel *scc; - int chip, k; - unsigned long flags; - char *flag; - - - printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2); - - flag=" "; - for (k = 0; k < nr_irqs; k++) - if (Ivec[k].used) - { - printk("%s%d", flag, k); - flag=","; - } - printk("\n"); - - - /* reset and pre-init all chips in the system */ - for (chip = 0; chip < Nchips; chip++) - { - scc=&SCC_Info[2*chip]; - if (!scc->ctrl) continue; - - /* Special SCC cards */ - - if(scc->brand & EAGLE) /* this is an EAGLE card */ - Outb(scc->special,0x08); /* enable interrupt on the board */ - - if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/PRIMUS card */ - Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */ - - - /* Reset and pre-init Z8530 */ - - spin_lock_irqsave(&scc->lock, flags); - - Outb(scc->ctrl, 0); - OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */ - udelay(100); /* give it 'a bit' more time than required */ - wr(scc, R2, chip*16); /* interrupt vector */ - wr(scc, R9, VIS); /* vector includes status */ - spin_unlock_irqrestore(&scc->lock, flags); - } - - - Driver_Initialized = 1; -} - -/* - * Allocate device structure, err, instance, and register driver - */ - -static int scc_net_alloc(const char *name, struct scc_channel *scc) -{ - int err; - struct net_device *dev; - - dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, scc_net_setup); - if (!dev) - return -ENOMEM; - - dev->ml_priv = scc; - scc->dev = dev; - spin_lock_init(&scc->lock); - timer_setup(&scc->tx_t, NULL, 0); - timer_setup(&scc->tx_wdog, NULL, 0); - - err = register_netdevice(dev); - if (err) { - printk(KERN_ERR "%s: can't register network device (%d)\n", - name, err); - free_netdev(dev); - scc->dev = NULL; - return err; - } - - return 0; -} - - - -/* ******************************************************************** */ -/* * Network driver methods * */ -/* ******************************************************************** */ - -static const struct net_device_ops scc_netdev_ops = { - .ndo_open = scc_net_open, - .ndo_stop = scc_net_close, - .ndo_start_xmit = scc_net_tx, - .ndo_set_mac_address = scc_net_set_mac_address, - .ndo_get_stats = scc_net_get_stats, - .ndo_siocdevprivate = scc_net_siocdevprivate, -}; - -/* ----> Initialize device <----- */ - -static void scc_net_setup(struct net_device *dev) -{ - dev->tx_queue_len = 16; /* should be enough... */ - - dev->netdev_ops = &scc_netdev_ops; - dev->header_ops = &ax25_header_ops; - - dev->flags = 0; - - dev->type = ARPHRD_AX25; - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; - dev->addr_len = AX25_ADDR_LEN; - - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); -} - -/* ----> open network device <---- */ - -static int scc_net_open(struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - - if (!scc->init) - return -EINVAL; - - scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); - - init_channel(scc); - - netif_start_queue(dev); - return 0; -} - -/* ----> close network device <---- */ - -static int scc_net_close(struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - unsigned long flags; - - netif_stop_queue(dev); - - spin_lock_irqsave(&scc->lock, flags); - Outb(scc->ctrl,0); /* Make sure pointer is written */ - wr(scc,R1,0); /* disable interrupts */ - wr(scc,R3,0); - spin_unlock_irqrestore(&scc->lock, flags); - - timer_delete_sync(&scc->tx_t); - timer_delete_sync(&scc->tx_wdog); - - scc_discard_buffers(scc); - - return 0; -} - -/* ----> receive frame, called from scc_rxint() <---- */ - -static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) -{ - if (skb->len == 0) { - dev_kfree_skb_irq(skb); - return; - } - - scc->dev_stat.rx_packets++; - scc->dev_stat.rx_bytes += skb->len; - - skb->protocol = ax25_type_trans(skb, scc->dev); - - netif_rx(skb); -} - -/* ----> transmit frame <---- */ - -static netdev_tx_t scc_net_tx(struct sk_buff *skb, struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - unsigned long flags; - char kisscmd; - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (skb->len > scc->stat.bufsize || skb->len < 2) { - scc->dev_stat.tx_dropped++; /* bogus frame */ - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - scc->dev_stat.tx_packets++; - scc->dev_stat.tx_bytes += skb->len; - scc->stat.txframes++; - - kisscmd = *skb->data & 0x1f; - skb_pull(skb, 1); - - if (kisscmd) { - scc_set_param(scc, kisscmd, *skb->data); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - spin_lock_irqsave(&scc->lock, flags); - - if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len) { - struct sk_buff *skb_del; - skb_del = skb_dequeue(&scc->tx_queue); - dev_kfree_skb_irq(skb_del); - } - skb_queue_tail(&scc->tx_queue, skb); - netif_trans_update(dev); - - - /* - * Start transmission if the trx state is idle or - * t_idle hasn't expired yet. Use dwait/persistence/slottime - * algorithm for normal halfduplex operation. - */ - - if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) { - scc->stat.tx_state = TXS_BUSY; - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - __scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime); - else - __scc_start_tx_timer(scc, t_dwait, 0); - } - spin_unlock_irqrestore(&scc->lock, flags); - return NETDEV_TX_OK; -} - -/* ----> ioctl functions <---- */ - -/* - * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg - * SIOCSCCINI - initialize driver arg: --- - * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg - * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg - * SIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg - * SIOCSCCCAL - send calib. pattern arg: (struct scc_calibrate *) arg - */ - -static int scc_net_siocdevprivate(struct net_device *dev, - struct ifreq *ifr, void __user *arg, int cmd) -{ - struct scc_kiss_cmd kiss_cmd; - struct scc_mem_config memcfg; - struct scc_hw_config hwcfg; - struct scc_calibrate cal; - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - int chan; - unsigned char device_name[IFNAMSIZ]; - - if (!Driver_Initialized) - { - if (cmd == SIOCSCCCFG) - { - int found = 1; - - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (in_compat_syscall()) - return -EOPNOTSUPP; - - if (!arg) return -EFAULT; - - if (Nchips >= SCC_MAXCHIPS) - return -EINVAL; - - if (copy_from_user(&hwcfg, arg, sizeof(hwcfg))) - return -EFAULT; - - if (hwcfg.irq == 2) hwcfg.irq = 9; - - if (hwcfg.irq < 0 || hwcfg.irq >= irq_get_nr_irqs()) - return -EINVAL; - - if (!Ivec[hwcfg.irq].used && hwcfg.irq) - { - if (request_irq(hwcfg.irq, scc_isr, - 0, "AX.25 SCC", - (void *)(long) hwcfg.irq)) - printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); - else - Ivec[hwcfg.irq].used = 1; - } - - if (hwcfg.vector_latch && !Vector_Latch) { - if (!request_region(hwcfg.vector_latch, 1, "scc vector latch")) - printk(KERN_WARNING "z8530drv: warning, cannot reserve vector latch port 0x%lx\n, disabled.", hwcfg.vector_latch); - else - Vector_Latch = hwcfg.vector_latch; - } - - if (hwcfg.clock == 0) - hwcfg.clock = SCC_DEFAULT_CLOCK; - -#ifndef SCC_DONT_CHECK - - if(request_region(hwcfg.ctrl_a, 1, "scc-probe")) - { - disable_irq(hwcfg.irq); - Outb(hwcfg.ctrl_a, 0); - OutReg(hwcfg.ctrl_a, R9, FHWRES); - udelay(100); - OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */ - udelay(5); - - if (InReg(hwcfg.ctrl_a,R13) != 0x55) - found = 0; - enable_irq(hwcfg.irq); - release_region(hwcfg.ctrl_a, 1); - } - else - found = 0; -#endif - - if (found) - { - SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a; - SCC_Info[2*Nchips ].data = hwcfg.data_a; - SCC_Info[2*Nchips ].irq = hwcfg.irq; - SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b; - SCC_Info[2*Nchips+1].data = hwcfg.data_b; - SCC_Info[2*Nchips+1].irq = hwcfg.irq; - - SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a; - SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b; - SCC_ctrl[Nchips].irq = hwcfg.irq; - } - - - for (chan = 0; chan < 2; chan++) - { - sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan); - - SCC_Info[2*Nchips+chan].special = hwcfg.special; - SCC_Info[2*Nchips+chan].clock = hwcfg.clock; - SCC_Info[2*Nchips+chan].brand = hwcfg.brand; - SCC_Info[2*Nchips+chan].option = hwcfg.option; - SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc; - -#ifdef SCC_DONT_CHECK - printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n", - device_name, - SCC_Info[2*Nchips+chan].data, - SCC_Info[2*Nchips+chan].ctrl); - -#else - printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n", - device_name, - chan? hwcfg.data_b : hwcfg.data_a, - chan? hwcfg.ctrl_b : hwcfg.ctrl_a, - found? "found" : "missing"); -#endif - - if (found) - { - request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl"); - request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data"); - if (Nchips+chan != 0 && - scc_net_alloc(device_name, - &SCC_Info[2*Nchips+chan])) - return -EINVAL; - } - } - - if (found) Nchips++; - - return 0; - } - - if (cmd == SIOCSCCINI) - { - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - - if (Nchips == 0) - return -EINVAL; - - z8530_init(); - return 0; - } - - return -EINVAL; /* confuse the user */ - } - - if (!scc->init) - { - if (cmd == SIOCSCCCHANINI) - { - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (!arg) return -EINVAL; - - scc->stat.bufsize = SCC_BUFSIZE; - - if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem))) - return -EINVAL; - - /* default KISS Params */ - - if (scc->modem.speed < 4800) - { - scc->kiss.txdelay = 36; /* 360 ms */ - scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 16; /* 160 ms */ - scc->kiss.tailtime = 4; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 10; /* 10 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ - } else { - scc->kiss.txdelay = 10; /* 100 ms */ - scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 8; /* 160 ms */ - scc->kiss.tailtime = 1; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 7; /* 7 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ - } - - scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); - scc->init = 1; - - return 0; - } - - return -EINVAL; - } - - switch(cmd) - { - case SIOCSCCRESERVED: - return -ENOIOCTLCMD; - - case SIOCSCCSMEM: - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg))) - return -EINVAL; - if (memcfg.bufsize < 16) - return -EINVAL; - scc->stat.bufsize = memcfg.bufsize; - return 0; - - case SIOCSCCGSTAT: - if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat))) - return -EINVAL; - return 0; - - case SIOCSCCGKISS: - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - kiss_cmd.param = scc_get_param(scc, kiss_cmd.command); - if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd))) - return -EINVAL; - return 0; - - case SIOCSCCSKISS: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param); - - case SIOCSCCCAL: - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0) - return -EINVAL; - - scc_start_calibrate(scc, cal.time, cal.pattern); - return 0; - - default: - return -ENOIOCTLCMD; - - } - - return -EINVAL; -} - -/* ----> set interface callsign <---- */ - -static int scc_net_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *) addr; - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* ----> get statistics <---- */ - -static struct net_device_stats *scc_net_get_stats(struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - - scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over; - scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under; - scc->dev_stat.rx_fifo_errors = scc->stat.rx_over; - scc->dev_stat.tx_fifo_errors = scc->stat.tx_under; - - return &scc->dev_stat; -} - -/* ******************************************************************** */ -/* * dump statistics to /proc/net/z8530drv * */ -/* ******************************************************************** */ - -#ifdef CONFIG_PROC_FS - -static inline struct scc_channel *scc_net_seq_idx(loff_t pos) -{ - int k; - - for (k = 0; k < Nchips*2; ++k) { - if (!SCC_Info[k].init) - continue; - if (pos-- == 0) - return &SCC_Info[k]; - } - return NULL; -} - -static void *scc_net_seq_start(struct seq_file *seq, loff_t *pos) -{ - return *pos ? scc_net_seq_idx(*pos - 1) : SEQ_START_TOKEN; - -} - -static void *scc_net_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - unsigned k; - struct scc_channel *scc = v; - ++*pos; - - for (k = (v == SEQ_START_TOKEN) ? 0 : (scc - SCC_Info)+1; - k < Nchips*2; ++k) { - if (SCC_Info[k].init) - return &SCC_Info[k]; - } - return NULL; -} - -static void scc_net_seq_stop(struct seq_file *seq, void *v) -{ -} - -static int scc_net_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) { - seq_puts(seq, "z8530drv-"VERSION"\n"); - } else if (!Driver_Initialized) { - seq_puts(seq, "not initialized\n"); - } else if (!Nchips) { - seq_puts(seq, "chips missing\n"); - } else { - const struct scc_channel *scc = v; - const struct scc_stat *stat = &scc->stat; - const struct scc_kiss *kiss = &scc->kiss; - - - /* dev data ctrl irq clock brand enh vector special option - * baud nrz clocksrc softdcd bufsize - * rxints txints exints spints - * rcvd rxerrs over / xmit txerrs under / nospace bufsize - * txd pers slot tail ful wait min maxk idl defr txof grp - * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## - * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ## - */ - - seq_printf(seq, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n", - scc->dev->name, - scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand, - scc->enhanced, Vector_Latch, scc->special, - scc->option); - seq_printf(seq, "\t%lu %d %d %d %d\n", - scc->modem.speed, scc->modem.nrz, - scc->modem.clocksrc, kiss->softdcd, - stat->bufsize); - seq_printf(seq, "\t%lu %lu %lu %lu\n", - stat->rxints, stat->txints, stat->exints, stat->spints); - seq_printf(seq, "\t%lu %lu %d / %lu %lu %d / %d %d\n", - stat->rxframes, stat->rxerrs, stat->rx_over, - stat->txframes, stat->txerrs, stat->tx_under, - stat->nospace, stat->tx_state); - -#define K(x) kiss->x - seq_printf(seq, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n", - K(txdelay), K(persist), K(slottime), K(tailtime), - K(fulldup), K(waittime), K(mintime), K(maxkeyup), - K(idletime), K(maxdefer), K(tx_inhibit), K(group)); -#undef K -#ifdef SCC_DEBUG - { - int reg; - - seq_printf(seq, "\tW "); - for (reg = 0; reg < 16; reg++) - seq_printf(seq, "%2.2x ", scc->wreg[reg]); - seq_printf(seq, "\n"); - - seq_printf(seq, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); - for (reg = 3; reg < 8; reg++) - seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg)); - seq_printf(seq, "XX "); - for (reg = 9; reg < 16; reg++) - seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg)); - seq_printf(seq, "\n"); - } -#endif - seq_putc(seq, '\n'); - } - - return 0; -} - -static const struct seq_operations scc_net_seq_ops = { - .start = scc_net_seq_start, - .next = scc_net_seq_next, - .stop = scc_net_seq_stop, - .show = scc_net_seq_show, -}; -#endif /* CONFIG_PROC_FS */ - - -/* ******************************************************************** */ -/* * Init SCC driver * */ -/* ******************************************************************** */ - -static int __init scc_init_driver (void) -{ - char devname[IFNAMSIZ]; - - printk(banner); - - sprintf(devname,"%s0", SCC_DriverName); - - rtnl_lock(); - if (scc_net_alloc(devname, SCC_Info)) { - rtnl_unlock(); - printk(KERN_ERR "z8530drv: cannot initialize module\n"); - return -EIO; - } - rtnl_unlock(); - - proc_create_seq("z8530drv", 0, init_net.proc_net, &scc_net_seq_ops); - - return 0; -} - -static void __exit scc_cleanup_driver(void) -{ - const unsigned int nr_irqs = irq_get_nr_irqs(); - io_port ctrl; - int k; - struct scc_channel *scc; - struct net_device *dev; - - if (Nchips == 0 && (dev = SCC_Info[0].dev)) - { - unregister_netdev(dev); - free_netdev(dev); - } - - /* Guard against chip prattle */ - local_irq_disable(); - - for (k = 0; k < Nchips; k++) - if ( (ctrl = SCC_ctrl[k].chan_A) ) - { - Outb(ctrl, 0); - OutReg(ctrl,R9,FHWRES); /* force hardware reset */ - udelay(50); - } - - /* To unload the port must be closed so no real IRQ pending */ - for (k = 0; k < nr_irqs ; k++) - if (Ivec[k].used) free_irq(k, NULL); - - local_irq_enable(); - - /* Now clean up */ - for (k = 0; k < Nchips*2; k++) - { - scc = &SCC_Info[k]; - if (scc->ctrl) - { - release_region(scc->ctrl, 1); - release_region(scc->data, 1); - } - if (scc->dev) - { - unregister_netdev(scc->dev); - free_netdev(scc->dev); - } - } - - - if (Vector_Latch) - release_region(Vector_Latch, 1); - - remove_proc_entry("z8530drv", init_net.proc_net); -} - -MODULE_AUTHOR("Joerg Reuter "); -MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards"); -MODULE_LICENSE("GPL"); -module_init(scc_init_driver); -module_exit(scc_cleanup_driver); diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c deleted file mode 100644 index 4106f0301ab4..000000000000 --- a/drivers/net/hamradio/yam.c +++ /dev/null @@ -1,1191 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * yam.c -- YAM radio modem driver. - * - * Copyright (C) 1998 Frederic Rible F1OAT (frible@teaser.fr) - * Adapted from baycom.c driver written by Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * History: - * 0.0 F1OAT 06.06.98 Begin of work with baycom.c source code V 0.3 - * 0.1 F1OAT 07.06.98 Add timer polling routine for channel arbitration - * 0.2 F6FBB 08.06.98 Added delay after FPGA programming - * 0.3 F6FBB 29.07.98 Delayed PTT implementation for dupmode=2 - * 0.4 F6FBB 30.07.98 Added TxTail, Slottime and Persistence - * 0.5 F6FBB 01.08.98 Shared IRQs, /proc/net and network statistics - * 0.6 F6FBB 25.08.98 Added 1200Bds format - * 0.7 F6FBB 12.09.98 Added to the kernel configuration - * 0.8 F6FBB 14.10.98 Fixed slottime/persistence timing bug - * OK1ZIA 2.09.01 Fixed "kfree_skb on hard IRQ" - * using dev_kfree_skb_any(). (important in 2.4 kernel) - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -/* --------------------------------------------------------------------- */ - -static const char yam_drvname[] = "yam"; -static const char yam_drvinfo[] __initconst = KERN_INFO \ - "YAM driver version 0.8 by F1OAT/F6FBB\n"; - -/* --------------------------------------------------------------------- */ - -#define FIRMWARE_9600 "yam/9600.bin" -#define FIRMWARE_1200 "yam/1200.bin" - -#define YAM_9600 1 -#define YAM_1200 2 - -#define NR_PORTS 4 -#define YAM_MAGIC 0xF10A7654 - -/* Transmitter states */ - -#define TX_OFF 0 -#define TX_HEAD 1 -#define TX_DATA 2 -#define TX_CRC1 3 -#define TX_CRC2 4 -#define TX_TAIL 5 - -#define YAM_MAX_FRAME 1024 - -#define DEFAULT_BITRATE 9600 /* bps */ -#define DEFAULT_HOLDD 10 /* sec */ -#define DEFAULT_TXD 300 /* ms */ -#define DEFAULT_TXTAIL 10 /* ms */ -#define DEFAULT_SLOT 100 /* ms */ -#define DEFAULT_PERS 64 /* 0->255 */ - -struct yam_port { - int magic; - int bitrate; - int baudrate; - int iobase; - int irq; - int dupmode; - - struct net_device *dev; - - int nb_rxint; - int nb_mdint; - - /* Parameters section */ - - int txd; /* tx delay */ - int holdd; /* duplex ptt delay */ - int txtail; /* txtail delay */ - int slot; /* slottime */ - int pers; /* persistence */ - - /* Tx section */ - - int tx_state; - int tx_count; - int slotcnt; - unsigned char tx_buf[YAM_MAX_FRAME]; - int tx_len; - int tx_crcl, tx_crch; - struct sk_buff_head send_queue; /* Packets awaiting transmission */ - - /* Rx section */ - - int dcd; - unsigned char rx_buf[YAM_MAX_FRAME]; - int rx_len; - int rx_crcl, rx_crch; -}; - -struct yam_mcs { - unsigned char bits[YAM_FPGA_SIZE]; - int bitrate; - struct yam_mcs *next; -}; - -static struct net_device *yam_devs[NR_PORTS]; - -static struct yam_mcs *yam_data; - -static DEFINE_TIMER(yam_timer, NULL); - -/* --------------------------------------------------------------------- */ - -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define YAM_EXTENT 8 - -/* Interrupt Identification Register Bit Masks */ -#define IIR_NOPEND 1 -#define IIR_MSR 0 -#define IIR_TX 2 -#define IIR_RX 4 -#define IIR_LSR 6 -#define IIR_TIMEOUT 12 /* Fifo mode only */ - -#define IIR_MASK 0x0F - -/* Interrupt Enable Register Bit Masks */ -#define IER_RX 1 /* enable rx interrupt */ -#define IER_TX 2 /* enable tx interrupt */ -#define IER_LSR 4 /* enable line status interrupts */ -#define IER_MSR 8 /* enable modem status interrupts */ - -/* Modem Control Register Bit Masks */ -#define MCR_DTR 0x01 /* DTR output */ -#define MCR_RTS 0x02 /* RTS output */ -#define MCR_OUT1 0x04 /* OUT1 output (not accessible in RS232) */ -#define MCR_OUT2 0x08 /* Master Interrupt enable (must be set on PCs) */ -#define MCR_LOOP 0x10 /* Loopback enable */ - -/* Modem Status Register Bit Masks */ -#define MSR_DCTS 0x01 /* Delta CTS input */ -#define MSR_DDSR 0x02 /* Delta DSR */ -#define MSR_DRIN 0x04 /* Delta RI */ -#define MSR_DDCD 0x08 /* Delta DCD */ -#define MSR_CTS 0x10 /* CTS input */ -#define MSR_DSR 0x20 /* DSR input */ -#define MSR_RING 0x40 /* RI input */ -#define MSR_DCD 0x80 /* DCD input */ - -/* line status register bit mask */ -#define LSR_RXC 0x01 -#define LSR_OE 0x02 -#define LSR_PE 0x04 -#define LSR_FE 0x08 -#define LSR_BREAK 0x10 -#define LSR_THRE 0x20 -#define LSR_TSRE 0x40 - -/* Line Control Register Bit Masks */ -#define LCR_DLAB 0x80 -#define LCR_BREAK 0x40 -#define LCR_PZERO 0x28 -#define LCR_PEVEN 0x18 -#define LCR_PODD 0x08 -#define LCR_STOP1 0x00 -#define LCR_STOP2 0x04 -#define LCR_BIT5 0x00 -#define LCR_BIT6 0x02 -#define LCR_BIT7 0x01 -#define LCR_BIT8 0x03 - -/* YAM Modem <-> UART Port mapping */ - -#define TX_RDY MSR_DCTS /* transmitter ready to send */ -#define RX_DCD MSR_DCD /* carrier detect */ -#define RX_FLAG MSR_RING /* hdlc flag received */ -#define FPGA_DONE MSR_DSR /* FPGA is configured */ -#define PTT_ON (MCR_RTS|MCR_OUT2) /* activate PTT */ -#define PTT_OFF (MCR_DTR|MCR_OUT2) /* release PTT */ - -#define ENABLE_RXINT IER_RX /* enable uart rx interrupt during rx */ -#define ENABLE_TXINT IER_MSR /* enable uart ms interrupt during tx */ -#define ENABLE_RTXINT (IER_RX|IER_MSR) /* full duplex operations */ - - -/************************************************************************* -* CRC Tables -************************************************************************/ - -static const unsigned char chktabl[256] = -{0x00, 0x89, 0x12, 0x9b, 0x24, 0xad, 0x36, 0xbf, 0x48, 0xc1, 0x5a, 0xd3, 0x6c, 0xe5, 0x7e, - 0xf7, 0x81, 0x08, 0x93, 0x1a, 0xa5, 0x2c, 0xb7, 0x3e, 0xc9, 0x40, 0xdb, 0x52, 0xed, 0x64, - 0xff, 0x76, 0x02, 0x8b, 0x10, 0x99, 0x26, 0xaf, 0x34, 0xbd, 0x4a, 0xc3, 0x58, 0xd1, 0x6e, - 0xe7, 0x7c, 0xf5, 0x83, 0x0a, 0x91, 0x18, 0xa7, 0x2e, 0xb5, 0x3c, 0xcb, 0x42, 0xd9, 0x50, - 0xef, 0x66, 0xfd, 0x74, 0x04, 0x8d, 0x16, 0x9f, 0x20, 0xa9, 0x32, 0xbb, 0x4c, 0xc5, 0x5e, - 0xd7, 0x68, 0xe1, 0x7a, 0xf3, 0x85, 0x0c, 0x97, 0x1e, 0xa1, 0x28, 0xb3, 0x3a, 0xcd, 0x44, - 0xdf, 0x56, 0xe9, 0x60, 0xfb, 0x72, 0x06, 0x8f, 0x14, 0x9d, 0x22, 0xab, 0x30, 0xb9, 0x4e, - 0xc7, 0x5c, 0xd5, 0x6a, 0xe3, 0x78, 0xf1, 0x87, 0x0e, 0x95, 0x1c, 0xa3, 0x2a, 0xb1, 0x38, - 0xcf, 0x46, 0xdd, 0x54, 0xeb, 0x62, 0xf9, 0x70, 0x08, 0x81, 0x1a, 0x93, 0x2c, 0xa5, 0x3e, - 0xb7, 0x40, 0xc9, 0x52, 0xdb, 0x64, 0xed, 0x76, 0xff, 0x89, 0x00, 0x9b, 0x12, 0xad, 0x24, - 0xbf, 0x36, 0xc1, 0x48, 0xd3, 0x5a, 0xe5, 0x6c, 0xf7, 0x7e, 0x0a, 0x83, 0x18, 0x91, 0x2e, - 0xa7, 0x3c, 0xb5, 0x42, 0xcb, 0x50, 0xd9, 0x66, 0xef, 0x74, 0xfd, 0x8b, 0x02, 0x99, 0x10, - 0xaf, 0x26, 0xbd, 0x34, 0xc3, 0x4a, 0xd1, 0x58, 0xe7, 0x6e, 0xf5, 0x7c, 0x0c, 0x85, 0x1e, - 0x97, 0x28, 0xa1, 0x3a, 0xb3, 0x44, 0xcd, 0x56, 0xdf, 0x60, 0xe9, 0x72, 0xfb, 0x8d, 0x04, - 0x9f, 0x16, 0xa9, 0x20, 0xbb, 0x32, 0xc5, 0x4c, 0xd7, 0x5e, 0xe1, 0x68, 0xf3, 0x7a, 0x0e, - 0x87, 0x1c, 0x95, 0x2a, 0xa3, 0x38, 0xb1, 0x46, 0xcf, 0x54, 0xdd, 0x62, 0xeb, 0x70, 0xf9, - 0x8f, 0x06, 0x9d, 0x14, 0xab, 0x22, 0xb9, 0x30, 0xc7, 0x4e, 0xd5, 0x5c, 0xe3, 0x6a, 0xf1, - 0x78}; -static const unsigned char chktabh[256] = -{0x00, 0x11, 0x23, 0x32, 0x46, 0x57, 0x65, 0x74, 0x8c, 0x9d, 0xaf, 0xbe, 0xca, 0xdb, 0xe9, - 0xf8, 0x10, 0x01, 0x33, 0x22, 0x56, 0x47, 0x75, 0x64, 0x9c, 0x8d, 0xbf, 0xae, 0xda, 0xcb, - 0xf9, 0xe8, 0x21, 0x30, 0x02, 0x13, 0x67, 0x76, 0x44, 0x55, 0xad, 0xbc, 0x8e, 0x9f, 0xeb, - 0xfa, 0xc8, 0xd9, 0x31, 0x20, 0x12, 0x03, 0x77, 0x66, 0x54, 0x45, 0xbd, 0xac, 0x9e, 0x8f, - 0xfb, 0xea, 0xd8, 0xc9, 0x42, 0x53, 0x61, 0x70, 0x04, 0x15, 0x27, 0x36, 0xce, 0xdf, 0xed, - 0xfc, 0x88, 0x99, 0xab, 0xba, 0x52, 0x43, 0x71, 0x60, 0x14, 0x05, 0x37, 0x26, 0xde, 0xcf, - 0xfd, 0xec, 0x98, 0x89, 0xbb, 0xaa, 0x63, 0x72, 0x40, 0x51, 0x25, 0x34, 0x06, 0x17, 0xef, - 0xfe, 0xcc, 0xdd, 0xa9, 0xb8, 0x8a, 0x9b, 0x73, 0x62, 0x50, 0x41, 0x35, 0x24, 0x16, 0x07, - 0xff, 0xee, 0xdc, 0xcd, 0xb9, 0xa8, 0x9a, 0x8b, 0x84, 0x95, 0xa7, 0xb6, 0xc2, 0xd3, 0xe1, - 0xf0, 0x08, 0x19, 0x2b, 0x3a, 0x4e, 0x5f, 0x6d, 0x7c, 0x94, 0x85, 0xb7, 0xa6, 0xd2, 0xc3, - 0xf1, 0xe0, 0x18, 0x09, 0x3b, 0x2a, 0x5e, 0x4f, 0x7d, 0x6c, 0xa5, 0xb4, 0x86, 0x97, 0xe3, - 0xf2, 0xc0, 0xd1, 0x29, 0x38, 0x0a, 0x1b, 0x6f, 0x7e, 0x4c, 0x5d, 0xb5, 0xa4, 0x96, 0x87, - 0xf3, 0xe2, 0xd0, 0xc1, 0x39, 0x28, 0x1a, 0x0b, 0x7f, 0x6e, 0x5c, 0x4d, 0xc6, 0xd7, 0xe5, - 0xf4, 0x80, 0x91, 0xa3, 0xb2, 0x4a, 0x5b, 0x69, 0x78, 0x0c, 0x1d, 0x2f, 0x3e, 0xd6, 0xc7, - 0xf5, 0xe4, 0x90, 0x81, 0xb3, 0xa2, 0x5a, 0x4b, 0x79, 0x68, 0x1c, 0x0d, 0x3f, 0x2e, 0xe7, - 0xf6, 0xc4, 0xd5, 0xa1, 0xb0, 0x82, 0x93, 0x6b, 0x7a, 0x48, 0x59, 0x2d, 0x3c, 0x0e, 0x1f, - 0xf7, 0xe6, 0xd4, 0xc5, 0xb1, 0xa0, 0x92, 0x83, 0x7b, 0x6a, 0x58, 0x49, 0x3d, 0x2c, 0x1e, - 0x0f}; - -/************************************************************************* -* FPGA functions -************************************************************************/ - -static void delay(int ms) -{ - unsigned long timeout = jiffies + ((ms * HZ) / 1000); - while (time_before(jiffies, timeout)) - cpu_relax(); -} - -/* - * reset FPGA - */ - -static void fpga_reset(int iobase) -{ - outb(0, IER(iobase)); - outb(LCR_DLAB | LCR_BIT5, LCR(iobase)); - outb(1, DLL(iobase)); - outb(0, DLM(iobase)); - - outb(LCR_BIT5, LCR(iobase)); - inb(LSR(iobase)); - inb(MSR(iobase)); - /* turn off FPGA supply voltage */ - outb(MCR_OUT1 | MCR_OUT2, MCR(iobase)); - delay(100); - /* turn on FPGA supply voltage again */ - outb(MCR_DTR | MCR_RTS | MCR_OUT1 | MCR_OUT2, MCR(iobase)); - delay(100); -} - -/* - * send one byte to FPGA - */ - -static int fpga_write(int iobase, unsigned char wrd) -{ - unsigned char bit; - int k; - unsigned long timeout = jiffies + HZ / 10; - - for (k = 0; k < 8; k++) { - bit = (wrd & 0x80) ? (MCR_RTS | MCR_DTR) : MCR_DTR; - outb(bit | MCR_OUT1 | MCR_OUT2, MCR(iobase)); - wrd <<= 1; - outb(0xfc, THR(iobase)); - while ((inb(LSR(iobase)) & LSR_TSRE) == 0) - if (time_after(jiffies, timeout)) - return -1; - } - - return 0; -} - -/* - * predef should be 0 for loading user defined mcs - * predef should be YAM_1200 for loading predef 1200 mcs - * predef should be YAM_9600 for loading predef 9600 mcs - */ -static unsigned char *add_mcs(unsigned char *bits, int bitrate, - unsigned int predef) -{ - const char *fw_name[2] = {FIRMWARE_9600, FIRMWARE_1200}; - const struct firmware *fw; - struct platform_device *pdev; - struct yam_mcs *p; - int err; - - switch (predef) { - case 0: - fw = NULL; - break; - case YAM_1200: - case YAM_9600: - predef--; - pdev = platform_device_register_simple("yam", 0, NULL, 0); - if (IS_ERR(pdev)) { - printk(KERN_ERR "yam: Failed to register firmware\n"); - return NULL; - } - err = request_firmware(&fw, fw_name[predef], &pdev->dev); - platform_device_unregister(pdev); - if (err) { - printk(KERN_ERR "Failed to load firmware \"%s\"\n", - fw_name[predef]); - return NULL; - } - if (fw->size != YAM_FPGA_SIZE) { - printk(KERN_ERR "Bogus length %zu in firmware \"%s\"\n", - fw->size, fw_name[predef]); - release_firmware(fw); - return NULL; - } - bits = (unsigned char *)fw->data; - break; - default: - printk(KERN_ERR "yam: Invalid predef number %u\n", predef); - return NULL; - } - - /* If it already exists, replace the bit data */ - p = yam_data; - while (p) { - if (p->bitrate == bitrate) { - memcpy(p->bits, bits, YAM_FPGA_SIZE); - goto out; - } - p = p->next; - } - - /* Allocate a new mcs */ - if ((p = kmalloc_obj(struct yam_mcs)) == NULL) { - release_firmware(fw); - return NULL; - } - memcpy(p->bits, bits, YAM_FPGA_SIZE); - p->bitrate = bitrate; - p->next = yam_data; - yam_data = p; - out: - release_firmware(fw); - return p->bits; -} - -static unsigned char *get_mcs(int bitrate) -{ - struct yam_mcs *p; - - p = yam_data; - while (p) { - if (p->bitrate == bitrate) - return p->bits; - p = p->next; - } - - /* Load predefined mcs data */ - switch (bitrate) { - case 1200: - /* setting predef as YAM_1200 for loading predef 1200 mcs */ - return add_mcs(NULL, bitrate, YAM_1200); - default: - /* setting predef as YAM_9600 for loading predef 9600 mcs */ - return add_mcs(NULL, bitrate, YAM_9600); - } -} - -/* - * download bitstream to FPGA - * data is contained in bits[] array in yam1200.h resp. yam9600.h - */ - -static int fpga_download(int iobase, int bitrate) -{ - int i, rc; - unsigned char *pbits; - - pbits = get_mcs(bitrate); - if (pbits == NULL) - return -1; - - fpga_reset(iobase); - for (i = 0; i < YAM_FPGA_SIZE; i++) { - if (fpga_write(iobase, pbits[i])) { - printk(KERN_ERR "yam: error in write cycle\n"); - return -1; /* write... */ - } - } - - fpga_write(iobase, 0xFF); - rc = inb(MSR(iobase)); /* check DONE signal */ - - /* Needed for some hardwares */ - delay(50); - - return (rc & MSR_DSR) ? 0 : -1; -} - - -/************************************************************************ -* Serial port init -************************************************************************/ - -static void yam_set_uart(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - int divisor = 115200 / yp->baudrate; - - outb(0, IER(dev->base_addr)); - outb(LCR_DLAB | LCR_BIT8, LCR(dev->base_addr)); - outb(divisor, DLL(dev->base_addr)); - outb(0, DLM(dev->base_addr)); - outb(LCR_BIT8, LCR(dev->base_addr)); - outb(PTT_OFF, MCR(dev->base_addr)); - outb(0x00, FCR(dev->base_addr)); - - /* Flush pending irq */ - - inb(RBR(dev->base_addr)); - inb(MSR(dev->base_addr)); - - /* Enable rx irq */ - - outb(ENABLE_RTXINT, IER(dev->base_addr)); -} - - -/* --------------------------------------------------------------------- */ - -enum uart { - c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A -}; - -static const char *uart_str[] = -{"unknown", "8250", "16450", "16550", "16550A"}; - -static enum uart yam_check_uart(unsigned int iobase) -{ - unsigned char b1, b2, b3; - enum uart u; - enum uart uart_tab[] = - {c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A}; - - b1 = inb(MCR(iobase)); - outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ - b2 = inb(MSR(iobase)); - outb(0x1a, MCR(iobase)); - b3 = inb(MSR(iobase)) & 0xf0; - outb(b1, MCR(iobase)); /* restore old values */ - outb(b2, MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(RBR(iobase)); - inb(RBR(iobase)); - outb(0x01, FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, SCR(iobase)); - b1 = inb(SCR(iobase)); - outb(0xa5, SCR(iobase)); - b2 = inb(SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/****************************************************************************** -* Rx Section -******************************************************************************/ -static inline void yam_rx_flag(struct net_device *dev, struct yam_port *yp) -{ - if (yp->dcd && yp->rx_len >= 3 && yp->rx_len < YAM_MAX_FRAME) { - int pkt_len = yp->rx_len - 2 + 1; /* -CRC + kiss */ - struct sk_buff *skb; - - if ((yp->rx_crch & yp->rx_crcl) != 0xFF) { - /* Bad crc */ - } else { - if (!(skb = dev_alloc_skb(pkt_len))) { - printk(KERN_WARNING "%s: memory squeeze, dropping packet\n", dev->name); - ++dev->stats.rx_dropped; - } else { - unsigned char *cp; - cp = skb_put(skb, pkt_len); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, yp->rx_buf, pkt_len - 1); - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); - ++dev->stats.rx_packets; - } - } - } - yp->rx_len = 0; - yp->rx_crcl = 0x21; - yp->rx_crch = 0xf3; -} - -static inline void yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsigned char rxb) -{ - if (yp->rx_len < YAM_MAX_FRAME) { - unsigned char c = yp->rx_crcl; - yp->rx_crcl = (chktabl[c] ^ yp->rx_crch); - yp->rx_crch = (chktabh[c] ^ rxb); - yp->rx_buf[yp->rx_len++] = rxb; - } -} - -/******************************************************************************** -* TX Section -********************************************************************************/ - -static void ptt_on(struct net_device *dev) -{ - outb(PTT_ON, MCR(dev->base_addr)); -} - -static void ptt_off(struct net_device *dev) -{ - outb(PTT_OFF, MCR(dev->base_addr)); -} - -static netdev_tx_t yam_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - skb_queue_tail(&yp->send_queue, skb); - netif_trans_update(dev); - return NETDEV_TX_OK; -} - -static void yam_start_tx(struct net_device *dev, struct yam_port *yp) -{ - if ((yp->tx_state == TX_TAIL) || (yp->txd == 0)) - yp->tx_count = 1; - else - yp->tx_count = (yp->bitrate * yp->txd) / 8000; - yp->tx_state = TX_HEAD; - ptt_on(dev); -} - -static void yam_arbitrate(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - - if (yp->magic != YAM_MAGIC || yp->tx_state != TX_OFF || - skb_queue_empty(&yp->send_queue)) - return; - /* tx_state is TX_OFF and there is data to send */ - - if (yp->dupmode) { - /* Full duplex mode, don't wait */ - yam_start_tx(dev, yp); - return; - } - if (yp->dcd) { - /* DCD on, wait slotime ... */ - yp->slotcnt = yp->slot / 10; - return; - } - /* Is slottime passed ? */ - if ((--yp->slotcnt) > 0) - return; - - yp->slotcnt = yp->slot / 10; - - /* is random > persist ? */ - if (get_random_u8() > yp->pers) - return; - - yam_start_tx(dev, yp); -} - -static void yam_dotimer(struct timer_list *unused) -{ - int i; - - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev = yam_devs[i]; - if (dev && netif_running(dev)) - yam_arbitrate(dev); - } - yam_timer.expires = jiffies + HZ / 100; - add_timer(&yam_timer); -} - -static void yam_tx_byte(struct net_device *dev, struct yam_port *yp) -{ - struct sk_buff *skb; - unsigned char b, temp; - - switch (yp->tx_state) { - case TX_OFF: - break; - case TX_HEAD: - if (--yp->tx_count <= 0) { - if (!(skb = skb_dequeue(&yp->send_queue))) { - ptt_off(dev); - yp->tx_state = TX_OFF; - break; - } - yp->tx_state = TX_DATA; - if (skb->data[0] != 0) { -/* do_kiss_params(s, skb->data, skb->len); */ - dev_kfree_skb_any(skb); - break; - } - yp->tx_len = skb->len - 1; /* strip KISS byte */ - if (yp->tx_len >= YAM_MAX_FRAME || yp->tx_len < 2) { - dev_kfree_skb_any(skb); - break; - } - skb_copy_from_linear_data_offset(skb, 1, - yp->tx_buf, - yp->tx_len); - dev_kfree_skb_any(skb); - yp->tx_count = 0; - yp->tx_crcl = 0x21; - yp->tx_crch = 0xf3; - yp->tx_state = TX_DATA; - } - break; - case TX_DATA: - b = yp->tx_buf[yp->tx_count++]; - outb(b, THR(dev->base_addr)); - temp = yp->tx_crcl; - yp->tx_crcl = chktabl[temp] ^ yp->tx_crch; - yp->tx_crch = chktabh[temp] ^ b; - if (yp->tx_count >= yp->tx_len) { - yp->tx_state = TX_CRC1; - } - break; - case TX_CRC1: - yp->tx_crch = chktabl[yp->tx_crcl] ^ yp->tx_crch; - yp->tx_crcl = chktabh[yp->tx_crcl] ^ chktabl[yp->tx_crch] ^ 0xff; - outb(yp->tx_crcl, THR(dev->base_addr)); - yp->tx_state = TX_CRC2; - break; - case TX_CRC2: - outb(chktabh[yp->tx_crch] ^ 0xFF, THR(dev->base_addr)); - if (skb_queue_empty(&yp->send_queue)) { - yp->tx_count = (yp->bitrate * yp->txtail) / 8000; - if (yp->dupmode == 2) - yp->tx_count += (yp->bitrate * yp->holdd) / 8; - if (yp->tx_count == 0) - yp->tx_count = 1; - yp->tx_state = TX_TAIL; - } else { - yp->tx_count = 1; - yp->tx_state = TX_HEAD; - } - ++dev->stats.tx_packets; - break; - case TX_TAIL: - if (--yp->tx_count <= 0) { - yp->tx_state = TX_OFF; - ptt_off(dev); - } - break; - } -} - -/*********************************************************************************** -* ISR routine -************************************************************************************/ - -static irqreturn_t yam_interrupt(int irq, void *dev_id) -{ - struct net_device *dev; - struct yam_port *yp; - unsigned char iir; - int counter = 100; - int i; - int handled = 0; - - for (i = 0; i < NR_PORTS; i++) { - dev = yam_devs[i]; - yp = netdev_priv(dev); - - if (!netif_running(dev)) - continue; - - while ((iir = IIR_MASK & inb(IIR(dev->base_addr))) != IIR_NOPEND) { - unsigned char msr = inb(MSR(dev->base_addr)); - unsigned char lsr = inb(LSR(dev->base_addr)); - unsigned char rxb; - - handled = 1; - - if (lsr & LSR_OE) - ++dev->stats.rx_fifo_errors; - - yp->dcd = (msr & RX_DCD) ? 1 : 0; - - if (--counter <= 0) { - printk(KERN_ERR "%s: too many irq iir=%d\n", - dev->name, iir); - goto out; - } - if (msr & TX_RDY) { - ++yp->nb_mdint; - yam_tx_byte(dev, yp); - } - if (lsr & LSR_RXC) { - ++yp->nb_rxint; - rxb = inb(RBR(dev->base_addr)); - if (msr & RX_FLAG) - yam_rx_flag(dev, yp); - else - yam_rx_byte(dev, yp, rxb); - } - } - } -out: - return IRQ_RETVAL(handled); -} - -#ifdef CONFIG_PROC_FS - -static void *yam_seq_start(struct seq_file *seq, loff_t *pos) -{ - return (*pos < NR_PORTS) ? yam_devs[*pos] : NULL; -} - -static void *yam_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - return (*pos < NR_PORTS) ? yam_devs[*pos] : NULL; -} - -static void yam_seq_stop(struct seq_file *seq, void *v) -{ -} - -static int yam_seq_show(struct seq_file *seq, void *v) -{ - struct net_device *dev = v; - const struct yam_port *yp = netdev_priv(dev); - - seq_printf(seq, "Device %s\n", dev->name); - seq_printf(seq, " Up %d\n", netif_running(dev)); - seq_printf(seq, " Speed %u\n", yp->bitrate); - seq_printf(seq, " IoBase 0x%x\n", yp->iobase); - seq_printf(seq, " BaudRate %u\n", yp->baudrate); - seq_printf(seq, " IRQ %u\n", yp->irq); - seq_printf(seq, " TxState %u\n", yp->tx_state); - seq_printf(seq, " Duplex %u\n", yp->dupmode); - seq_printf(seq, " HoldDly %u\n", yp->holdd); - seq_printf(seq, " TxDelay %u\n", yp->txd); - seq_printf(seq, " TxTail %u\n", yp->txtail); - seq_printf(seq, " SlotTime %u\n", yp->slot); - seq_printf(seq, " Persist %u\n", yp->pers); - seq_printf(seq, " TxFrames %lu\n", dev->stats.tx_packets); - seq_printf(seq, " RxFrames %lu\n", dev->stats.rx_packets); - seq_printf(seq, " TxInt %u\n", yp->nb_mdint); - seq_printf(seq, " RxInt %u\n", yp->nb_rxint); - seq_printf(seq, " RxOver %lu\n", dev->stats.rx_fifo_errors); - seq_printf(seq, "\n"); - return 0; -} - -static const struct seq_operations yam_seqops = { - .start = yam_seq_start, - .next = yam_seq_next, - .stop = yam_seq_stop, - .show = yam_seq_show, -}; -#endif - - -/* --------------------------------------------------------------------- */ - -static int yam_open(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - enum uart u; - int i; - int ret=0; - - printk(KERN_INFO "Trying %s at iobase 0x%lx irq %u\n", dev->name, dev->base_addr, dev->irq); - - if (!yp->bitrate) - return -ENXIO; - if (!dev->base_addr || dev->base_addr > 0x1000 - YAM_EXTENT || - dev->irq < 2 || dev->irq > 15) { - return -ENXIO; - } - if (!request_region(dev->base_addr, YAM_EXTENT, dev->name)) - { - printk(KERN_ERR "%s: cannot 0x%lx busy\n", dev->name, dev->base_addr); - return -EACCES; - } - if ((u = yam_check_uart(dev->base_addr)) == c_uart_unknown) { - printk(KERN_ERR "%s: cannot find uart type\n", dev->name); - ret = -EIO; - goto out_release_base; - } - if (fpga_download(dev->base_addr, yp->bitrate)) { - printk(KERN_ERR "%s: cannot init FPGA\n", dev->name); - ret = -EIO; - goto out_release_base; - } - outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, yam_interrupt, IRQF_SHARED, dev->name, dev)) { - printk(KERN_ERR "%s: irq %d busy\n", dev->name, dev->irq); - ret = -EBUSY; - goto out_release_base; - } - - yam_set_uart(dev); - - netif_start_queue(dev); - - yp->slotcnt = yp->slot / 10; - - /* Reset overruns for all ports - FPGA programming makes overruns */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *yam_dev = yam_devs[i]; - - inb(LSR(yam_dev->base_addr)); - yam_dev->stats.rx_fifo_errors = 0; - } - - printk(KERN_INFO "%s at iobase 0x%lx irq %u uart %s\n", dev->name, dev->base_addr, dev->irq, - uart_str[u]); - return 0; - -out_release_base: - release_region(dev->base_addr, YAM_EXTENT); - return ret; -} - -/* --------------------------------------------------------------------- */ - -static int yam_close(struct net_device *dev) -{ - struct sk_buff *skb; - struct yam_port *yp = netdev_priv(dev); - - if (!dev) - return -EINVAL; - - /* - * disable interrupts - */ - outb(0, IER(dev->base_addr)); - outb(1, MCR(dev->base_addr)); - /* Remove IRQ handler if last */ - free_irq(dev->irq,dev); - release_region(dev->base_addr, YAM_EXTENT); - netif_stop_queue(dev); - while ((skb = skb_dequeue(&yp->send_queue))) - dev_kfree_skb(skb); - - printk(KERN_INFO "%s: close yam at iobase 0x%lx irq %u\n", - yam_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int yam_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data, int cmd) -{ - struct yam_port *yp = netdev_priv(dev); - struct yamdrv_ioctl_cfg yi; - struct yamdrv_ioctl_mcs *ym; - int ioctl_cmd; - - if (copy_from_user(&ioctl_cmd, data, sizeof(int))) - return -EFAULT; - - if (yp->magic != YAM_MAGIC) - return -EINVAL; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (cmd != SIOCDEVPRIVATE) - return -EINVAL; - - switch (ioctl_cmd) { - - case SIOCYAMRESERVED: - return -EINVAL; /* unused */ - - case SIOCYAMSMCS: - if (netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - ym = memdup_user(data, sizeof(struct yamdrv_ioctl_mcs)); - if (IS_ERR(ym)) - return PTR_ERR(ym); - if (ym->cmd != SIOCYAMSMCS || ym->bitrate > YAM_MAXBITRATE) { - kfree(ym); - return -EINVAL; - } - /* setting predef as 0 for loading userdefined mcs data */ - add_mcs(ym->bits, ym->bitrate, 0); - kfree(ym); - break; - - case SIOCYAMSCFG: - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - if (copy_from_user(&yi, data, sizeof(struct yamdrv_ioctl_cfg))) - return -EFAULT; - - if (yi.cmd != SIOCYAMSCFG) - return -EINVAL; - if ((yi.cfg.mask & YAM_IOBASE) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - if ((yi.cfg.mask & YAM_IRQ) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - if ((yi.cfg.mask & YAM_BITRATE) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - if ((yi.cfg.mask & YAM_BAUDRATE) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - - if (yi.cfg.mask & YAM_IOBASE) { - yp->iobase = yi.cfg.iobase; - dev->base_addr = yi.cfg.iobase; - } - if (yi.cfg.mask & YAM_IRQ) { - if (yi.cfg.irq > 15) - return -EINVAL; - yp->irq = yi.cfg.irq; - dev->irq = yi.cfg.irq; - } - if (yi.cfg.mask & YAM_BITRATE) { - if (yi.cfg.bitrate > YAM_MAXBITRATE) - return -EINVAL; - yp->bitrate = yi.cfg.bitrate; - } - if (yi.cfg.mask & YAM_BAUDRATE) { - if (yi.cfg.baudrate > YAM_MAXBAUDRATE) - return -EINVAL; - yp->baudrate = yi.cfg.baudrate; - } - if (yi.cfg.mask & YAM_MODE) { - if (yi.cfg.mode > YAM_MAXMODE) - return -EINVAL; - yp->dupmode = yi.cfg.mode; - } - if (yi.cfg.mask & YAM_HOLDDLY) { - if (yi.cfg.holddly > YAM_MAXHOLDDLY) - return -EINVAL; - yp->holdd = yi.cfg.holddly; - } - if (yi.cfg.mask & YAM_TXDELAY) { - if (yi.cfg.txdelay > YAM_MAXTXDELAY) - return -EINVAL; - yp->txd = yi.cfg.txdelay; - } - if (yi.cfg.mask & YAM_TXTAIL) { - if (yi.cfg.txtail > YAM_MAXTXTAIL) - return -EINVAL; - yp->txtail = yi.cfg.txtail; - } - if (yi.cfg.mask & YAM_PERSIST) { - if (yi.cfg.persist > YAM_MAXPERSIST) - return -EINVAL; - yp->pers = yi.cfg.persist; - } - if (yi.cfg.mask & YAM_SLOTTIME) { - if (yi.cfg.slottime > YAM_MAXSLOTTIME) - return -EINVAL; - yp->slot = yi.cfg.slottime; - yp->slotcnt = yp->slot / 10; - } - break; - - case SIOCYAMGCFG: - memset(&yi, 0, sizeof(yi)); - yi.cfg.mask = 0xffffffff; - yi.cfg.iobase = yp->iobase; - yi.cfg.irq = yp->irq; - yi.cfg.bitrate = yp->bitrate; - yi.cfg.baudrate = yp->baudrate; - yi.cfg.mode = yp->dupmode; - yi.cfg.txdelay = yp->txd; - yi.cfg.holddly = yp->holdd; - yi.cfg.txtail = yp->txtail; - yi.cfg.persist = yp->pers; - yi.cfg.slottime = yp->slot; - if (copy_to_user(data, &yi, sizeof(struct yamdrv_ioctl_cfg))) - return -EFAULT; - break; - - default: - return -EINVAL; - - } - - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int yam_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *) addr; - - /* addr is an AX.25 shifted ASCII mac address */ - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static const struct net_device_ops yam_netdev_ops = { - .ndo_open = yam_open, - .ndo_stop = yam_close, - .ndo_start_xmit = yam_send_packet, - .ndo_siocdevprivate = yam_siocdevprivate, - .ndo_set_mac_address = yam_set_mac_address, -}; - -static void yam_setup(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - - yp->magic = YAM_MAGIC; - yp->bitrate = DEFAULT_BITRATE; - yp->baudrate = DEFAULT_BITRATE * 2; - yp->iobase = 0; - yp->irq = 0; - yp->dupmode = 0; - yp->holdd = DEFAULT_HOLDD; - yp->txd = DEFAULT_TXD; - yp->txtail = DEFAULT_TXTAIL; - yp->slot = DEFAULT_SLOT; - yp->pers = DEFAULT_PERS; - yp->dev = dev; - - dev->base_addr = yp->iobase; - dev->irq = yp->irq; - - skb_queue_head_init(&yp->send_queue); - - dev->netdev_ops = &yam_netdev_ops; - dev->header_ops = &ax25_header_ops; - - dev->type = ARPHRD_AX25; - dev->hard_header_len = AX25_MAX_HEADER_LEN; - dev->mtu = AX25_MTU; - dev->addr_len = AX25_ADDR_LEN; - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); -} - -static int __init yam_init_driver(void) -{ - struct net_device *dev; - int i, err; - char name[IFNAMSIZ]; - - printk(yam_drvinfo); - - for (i = 0; i < NR_PORTS; i++) { - sprintf(name, "yam%d", i); - - dev = alloc_netdev(sizeof(struct yam_port), name, - NET_NAME_UNKNOWN, yam_setup); - if (!dev) { - pr_err("yam: cannot allocate net device\n"); - err = -ENOMEM; - goto error; - } - - err = register_netdev(dev); - if (err) { - printk(KERN_WARNING "yam: cannot register net device %s\n", dev->name); - free_netdev(dev); - goto error; - } - yam_devs[i] = dev; - - } - - timer_setup(&yam_timer, yam_dotimer, 0); - yam_timer.expires = jiffies + HZ / 100; - add_timer(&yam_timer); - - proc_create_seq("yam", 0444, init_net.proc_net, &yam_seqops); - return 0; - error: - while (--i >= 0) { - unregister_netdev(yam_devs[i]); - free_netdev(yam_devs[i]); - } - return err; -} - -/* --------------------------------------------------------------------- */ - -static void __exit yam_cleanup_driver(void) -{ - struct yam_mcs *p; - int i; - - timer_delete_sync(&yam_timer); - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev = yam_devs[i]; - if (dev) { - unregister_netdev(dev); - free_netdev(dev); - } - } - - while (yam_data) { - p = yam_data; - yam_data = yam_data->next; - kfree(p); - } - - remove_proc_entry("yam", init_net.proc_net); -} - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("Frederic Rible F1OAT frible@teaser.fr"); -MODULE_DESCRIPTION("Yam amateur radio modem driver"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(FIRMWARE_1200); -MODULE_FIRMWARE(FIRMWARE_9600); - -module_init(yam_init_driver); -module_exit(yam_cleanup_driver); - -/* --------------------------------------------------------------------- */ - diff --git a/drivers/net/hamradio/z8530.h b/drivers/net/hamradio/z8530.h deleted file mode 100644 index 1655901d713b..000000000000 --- a/drivers/net/hamradio/z8530.h +++ /dev/null @@ -1,246 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/* 8530 Serial Communications Controller Register definitions */ -#define FLAG 0x7e - -/* Write Register 0 */ -#define R0 0 /* Register selects */ -#define R1 1 -#define R2 2 -#define R3 3 -#define R4 4 -#define R5 5 -#define R6 6 -#define R7 7 -#define R8 8 -#define R9 9 -#define R10 10 -#define R11 11 -#define R12 12 -#define R13 13 -#define R14 14 -#define R15 15 - -#define NULLCODE 0 /* Null Code */ -#define POINT_HIGH 0x8 /* Select upper half of registers */ -#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ -#define SEND_ABORT 0x18 /* HDLC Abort */ -#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ -#define RES_Tx_P 0x28 /* Reset TxINT Pending */ -#define ERR_RES 0x30 /* Error Reset */ -#define RES_H_IUS 0x38 /* Reset highest IUS */ - -#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ -#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ -#define RES_EOM_L 0xC0 /* Reset EOM latch */ - -/* Write Register 1 */ - -#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ -#define TxINT_ENAB 0x2 /* Tx Int Enable */ -#define PAR_SPEC 0x4 /* Parity is special condition */ - -#define RxINT_DISAB 0 /* Rx Int Disable */ -#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ -#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ -#define INT_ERR_Rx 0x18 /* Int on error only */ - -#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ -#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ -#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ - -/* Write Register #2 (Interrupt Vector) */ - -/* Write Register 3 */ - -#define RxENABLE 0x1 /* Rx Enable */ -#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ -#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ -#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ -#define ENT_HM 0x10 /* Enter Hunt Mode */ -#define AUTO_ENAB 0x20 /* Auto Enables */ -#define Rx5 0x0 /* Rx 5 Bits/Character */ -#define Rx7 0x40 /* Rx 7 Bits/Character */ -#define Rx6 0x80 /* Rx 6 Bits/Character */ -#define Rx8 0xc0 /* Rx 8 Bits/Character */ - -/* Write Register 4 */ - -#define PAR_ENA 0x1 /* Parity Enable */ -#define PAR_EVEN 0x2 /* Parity Even/Odd* */ - -#define SYNC_ENAB 0 /* Sync Modes Enable */ -#define SB1 0x4 /* 1 stop bit/char */ -#define SB15 0x8 /* 1.5 stop bits/char */ -#define SB2 0xc /* 2 stop bits/char */ - -#define MONSYNC 0 /* 8 Bit Sync character */ -#define BISYNC 0x10 /* 16 bit sync character */ -#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ -#define EXTSYNC 0x30 /* External Sync Mode */ - -#define X1CLK 0x0 /* x1 clock mode */ -#define X16CLK 0x40 /* x16 clock mode */ -#define X32CLK 0x80 /* x32 clock mode */ -#define X64CLK 0xC0 /* x64 clock mode */ - -/* Write Register 5 */ - -#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ -#define RTS 0x2 /* RTS */ -#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ -#define TxENAB 0x8 /* Tx Enable */ -#define SND_BRK 0x10 /* Send Break */ -#define Tx5 0x0 /* Tx 5 bits (or less)/character */ -#define Tx7 0x20 /* Tx 7 bits/character */ -#define Tx6 0x40 /* Tx 6 bits/character */ -#define Tx8 0x60 /* Tx 8 bits/character */ -#define DTR 0x80 /* DTR */ - -/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ - -/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ - -/* Write Register 8 (transmit buffer) */ - -/* Write Register 9 (Master interrupt control) */ -#define VIS 1 /* Vector Includes Status */ -#define NV 2 /* No Vector */ -#define DLC 4 /* Disable Lower Chain */ -#define MIE 8 /* Master Interrupt Enable */ -#define STATHI 0x10 /* Status high */ -#define NORESET 0 /* No reset on write to R9 */ -#define CHRB 0x40 /* Reset channel B */ -#define CHRA 0x80 /* Reset channel A */ -#define FHWRES 0xc0 /* Force hardware reset */ - -/* Write Register 10 (misc control bits) */ -#define BIT6 1 /* 6 bit/8bit sync */ -#define LOOPMODE 2 /* SDLC Loop mode */ -#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ -#define MARKIDLE 8 /* Mark/flag on idle */ -#define GAOP 0x10 /* Go active on poll */ -#define NRZ 0 /* NRZ mode */ -#define NRZI 0x20 /* NRZI mode */ -#define FM1 0x40 /* FM1 (transition = 1) */ -#define FM0 0x60 /* FM0 (transition = 0) */ -#define CRCPS 0x80 /* CRC Preset I/O */ - -/* Write Register 11 (Clock Mode control) */ -#define TRxCXT 0 /* TRxC = Xtal output */ -#define TRxCTC 1 /* TRxC = Transmit clock */ -#define TRxCBR 2 /* TRxC = BR Generator Output */ -#define TRxCDP 3 /* TRxC = DPLL output */ -#define TRxCOI 4 /* TRxC O/I */ -#define TCRTxCP 0 /* Transmit clock = RTxC pin */ -#define TCTRxCP 8 /* Transmit clock = TRxC pin */ -#define TCBR 0x10 /* Transmit clock = BR Generator output */ -#define TCDPLL 0x18 /* Transmit clock = DPLL output */ -#define RCRTxCP 0 /* Receive clock = RTxC pin */ -#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ -#define RCBR 0x40 /* Receive clock = BR Generator output */ -#define RCDPLL 0x60 /* Receive clock = DPLL output */ -#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ - -/* Write Register 12 (lower byte of baud rate generator time constant) */ - -/* Write Register 13 (upper byte of baud rate generator time constant) */ - -/* Write Register 14 (Misc control bits) */ -#define BRENABL 1 /* Baud rate generator enable */ -#define BRSRC 2 /* Baud rate generator source */ -#define DTRREQ 4 /* DTR/Request function */ -#define AUTOECHO 8 /* Auto Echo */ -#define LOOPBAK 0x10 /* Local loopback */ -#define SEARCH 0x20 /* Enter search mode */ -#define RMC 0x40 /* Reset missing clock */ -#define DISDPLL 0x60 /* Disable DPLL */ -#define SSBR 0x80 /* Set DPLL source = BR generator */ -#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ -#define SFMM 0xc0 /* Set FM mode */ -#define SNRZI 0xe0 /* Set NRZI mode */ - -/* Write Register 15 (external/status interrupt control) */ -#define ZCIE 2 /* Zero count IE */ -#define DCDIE 8 /* DCD IE */ -#define SYNCIE 0x10 /* Sync/hunt IE */ -#define CTSIE 0x20 /* CTS IE */ -#define TxUIE 0x40 /* Tx Underrun/EOM IE */ -#define BRKIE 0x80 /* Break/Abort IE */ - - -/* Read Register 0 */ -#define Rx_CH_AV 0x1 /* Rx Character Available */ -#define ZCOUNT 0x2 /* Zero count */ -#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ -#define DCD 0x8 /* DCD */ -#define SYNC_HUNT 0x10 /* Sync/hunt */ -#define CTS 0x20 /* CTS */ -#define TxEOM 0x40 /* Tx underrun */ -#define BRK_ABRT 0x80 /* Break/Abort */ - -/* Read Register 1 */ -#define ALL_SNT 0x1 /* All sent */ -/* Residue Data for 8 Rx bits/char programmed */ -#define RES3 0x8 /* 0/3 */ -#define RES4 0x4 /* 0/4 */ -#define RES5 0xc /* 0/5 */ -#define RES6 0x2 /* 0/6 */ -#define RES7 0xa /* 0/7 */ -#define RES8 0x6 /* 0/8 */ -#define RES18 0xe /* 1/8 */ -#define RES28 0x0 /* 2/8 */ -/* Special Rx Condition Interrupts */ -#define PAR_ERR 0x10 /* Parity error */ -#define Rx_OVR 0x20 /* Rx Overrun Error */ -#define CRC_ERR 0x40 /* CRC/Framing Error */ -#define END_FR 0x80 /* End of Frame (SDLC) */ - -/* Read Register 2 (channel b only) - Interrupt vector */ - -/* Read Register 3 (interrupt pending register) ch a only */ -#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ -#define CHBTxIP 0x2 /* Channel B Tx IP */ -#define CHBRxIP 0x4 /* Channel B Rx IP */ -#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ -#define CHATxIP 0x10 /* Channel A Tx IP */ -#define CHARxIP 0x20 /* Channel A Rx IP */ - -/* Read Register 8 (receive data register) */ - -/* Read Register 10 (misc status bits) */ -#define ONLOOP 2 /* On loop */ -#define LOOPSEND 0x10 /* Loop sending */ -#define CLK2MIS 0x40 /* Two clocks missing */ -#define CLK1MIS 0x80 /* One clock missing */ - -/* Read Register 12 (lower byte of baud rate generator constant) */ - -/* Read Register 13 (upper byte of baud rate generator constant) */ - -/* Read Register 15 (value of WR 15) */ - -/* Z85C30/Z85230 Enhanced SCC register definitions */ - -/* Write Register 7' (SDLC/HDLC Programmable Enhancements) */ -#define AUTOTXF 0x01 /* Auto Tx Flag */ -#define AUTOEOM 0x02 /* Auto EOM Latch Reset */ -#define AUTORTS 0x04 /* Auto RTS */ -#define TXDNRZI 0x08 /* TxD Pulled High in SDLC NRZI mode */ -#define RXFIFOH 0x08 /* Z85230: Int on RX FIFO half full */ -#define FASTDTR 0x10 /* Fast DTR/REQ Mode */ -#define CRCCBCR 0x20 /* CRC Check Bytes Completely Received */ -#define TXFIFOE 0x20 /* Z85230: Int on TX FIFO completely empty */ -#define EXTRDEN 0x40 /* Extended Read Enabled */ - -/* Write Register 15 (external/status interrupt control) */ -#define SHDLCE 1 /* SDLC/HDLC Enhancements Enable */ -#define FIFOE 4 /* FIFO Enable */ - -/* Read Register 6 (frame status FIFO) */ -#define BCLSB 0xff /* LSB of 14 bits count */ - -/* Read Register 7 (frame status FIFO) */ -#define BCMSB 0x3f /* MSB of 14 bits count */ -#define FDA 0x40 /* FIFO Data Available Status */ -#define FOS 0x80 /* FIFO Overflow Status */ diff --git a/include/linux/hdlcdrv.h b/include/linux/hdlcdrv.h deleted file mode 100644 index 5d70c3f98f5b..000000000000 --- a/include/linux/hdlcdrv.h +++ /dev/null @@ -1,276 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * hdlcdrv.h -- HDLC packet radio network driver. - * The Linux soundcard driver for 1200 baud and 9600 baud packet radio - * (C) 1996-1998 by Thomas Sailer, HB9JNX/AE4WA - */ -#ifndef _HDLCDRV_H -#define _HDLCDRV_H - - -#include -#include -#include -#include - -#define HDLCDRV_MAGIC 0x5ac6e778 -#define HDLCDRV_HDLCBUFFER 32 /* should be a power of 2 for speed reasons */ -#define HDLCDRV_BITBUFFER 256 /* should be a power of 2 for speed reasons */ -#undef HDLCDRV_LOOPBACK /* define for HDLC debugging purposes */ -#define HDLCDRV_DEBUG - -/* maximum packet length, excluding CRC */ -#define HDLCDRV_MAXFLEN 400 - - -struct hdlcdrv_hdlcbuffer { - spinlock_t lock; - unsigned rd, wr; - unsigned short buf[HDLCDRV_HDLCBUFFER]; -}; - -#ifdef HDLCDRV_DEBUG -struct hdlcdrv_bitbuffer { - unsigned int rd; - unsigned int wr; - unsigned int shreg; - unsigned char buffer[HDLCDRV_BITBUFFER]; -}; - -static inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf, - unsigned int bit) -{ - unsigned char new; - - new = buf->shreg & 1; - buf->shreg >>= 1; - buf->shreg |= (!!bit) << 7; - if (new) { - buf->buffer[buf->wr] = buf->shreg; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - buf->shreg = 0x80; - } -} - -static inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf, - unsigned int bits) -{ - buf->buffer[buf->wr] = bits & 0xff; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - buf->buffer[buf->wr] = (bits >> 8) & 0xff; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - -} -#endif /* HDLCDRV_DEBUG */ - -/* -------------------------------------------------------------------- */ -/* - * Information that need to be kept for each driver. - */ - -struct hdlcdrv_ops { - /* - * first some informations needed by the hdlcdrv routines - */ - const char *drvname; - const char *drvinfo; - /* - * the routines called by the hdlcdrv routines - */ - int (*open)(struct net_device *); - int (*close)(struct net_device *); - int (*ioctl)(struct net_device *, void __user *, - struct hdlcdrv_ioctl *, int); -}; - -struct hdlcdrv_state { - int magic; - int opened; - - const struct hdlcdrv_ops *ops; - - struct { - int bitrate; - } par; - - struct hdlcdrv_pttoutput { - int dma2; - int seriobase; - int pariobase; - int midiiobase; - unsigned int flags; - } ptt_out; - - struct hdlcdrv_channel_params ch_params; - - struct hdlcdrv_hdlcrx { - struct hdlcdrv_hdlcbuffer hbuf; - unsigned long in_hdlc_rx; - /* 0 = sync hunt, != 0 receiving */ - int rx_state; - unsigned int bitstream; - unsigned int bitbuf; - int numbits; - unsigned char dcd; - - int len; - unsigned char *bp; - unsigned char buffer[HDLCDRV_MAXFLEN+2]; - } hdlcrx; - - struct hdlcdrv_hdlctx { - struct hdlcdrv_hdlcbuffer hbuf; - unsigned long in_hdlc_tx; - /* - * 0 = send flags - * 1 = send txtail (flags) - * 2 = send packet - */ - int tx_state; - int numflags; - unsigned int bitstream; - unsigned char ptt; - int calibrate; - int slotcnt; - - unsigned int bitbuf; - int numbits; - - int len; - unsigned char *bp; - unsigned char buffer[HDLCDRV_MAXFLEN+2]; - } hdlctx; - -#ifdef HDLCDRV_DEBUG - struct hdlcdrv_bitbuffer bitbuf_channel; - struct hdlcdrv_bitbuffer bitbuf_hdlc; -#endif /* HDLCDRV_DEBUG */ - - int ptt_keyed; - - /* queued skb for transmission */ - struct sk_buff *skb; -}; - - -/* -------------------------------------------------------------------- */ - -static inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&hb->lock, flags); - ret = !((HDLCDRV_HDLCBUFFER - 1 + hb->rd - hb->wr) % HDLCDRV_HDLCBUFFER); - spin_unlock_irqrestore(&hb->lock, flags); - return ret; -} - -/* -------------------------------------------------------------------- */ - -static inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&hb->lock, flags); - ret = (hb->rd == hb->wr); - spin_unlock_irqrestore(&hb->lock, flags); - return ret; -} - -/* -------------------------------------------------------------------- */ - -static inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb) -{ - unsigned long flags; - unsigned short val; - unsigned newr; - - spin_lock_irqsave(&hb->lock, flags); - if (hb->rd == hb->wr) - val = 0; - else { - newr = (hb->rd+1) % HDLCDRV_HDLCBUFFER; - val = hb->buf[hb->rd]; - hb->rd = newr; - } - spin_unlock_irqrestore(&hb->lock, flags); - return val; -} - -/* -------------------------------------------------------------------- */ - -static inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb, - unsigned short val) -{ - unsigned newp; - unsigned long flags; - - spin_lock_irqsave(&hb->lock, flags); - newp = (hb->wr+1) % HDLCDRV_HDLCBUFFER; - if (newp != hb->rd) { - hb->buf[hb->wr] = val & 0xffff; - hb->wr = newp; - } - spin_unlock_irqrestore(&hb->lock, flags); -} - -/* -------------------------------------------------------------------- */ - -static inline void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits) -{ - hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, bits); -} - -static inline unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s) -{ - unsigned int ret; - - if (hdlcdrv_hbuf_empty(&s->hdlctx.hbuf)) { - if (s->hdlctx.calibrate > 0) - s->hdlctx.calibrate--; - else - s->hdlctx.ptt = 0; - ret = 0; - } else - ret = hdlcdrv_hbuf_get(&s->hdlctx.hbuf); -#ifdef HDLCDRV_LOOPBACK - hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, ret); -#endif /* HDLCDRV_LOOPBACK */ - return ret; -} - -static inline void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit) -{ -#ifdef HDLCDRV_DEBUG - hdlcdrv_add_bitbuffer(&s->bitbuf_channel, bit); -#endif /* HDLCDRV_DEBUG */ -} - -static inline void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd) -{ - s->hdlcrx.dcd = !!dcd; -} - -static inline int hdlcdrv_ptt(struct hdlcdrv_state *s) -{ - return s->hdlctx.ptt || (s->hdlctx.calibrate > 0); -} - -/* -------------------------------------------------------------------- */ - -void hdlcdrv_receiver(struct net_device *, struct hdlcdrv_state *); -void hdlcdrv_transmitter(struct net_device *, struct hdlcdrv_state *); -void hdlcdrv_arbitrate(struct net_device *, struct hdlcdrv_state *); -struct net_device *hdlcdrv_register(const struct hdlcdrv_ops *ops, - unsigned int privsize, const char *ifname, - unsigned int baseaddr, unsigned int irq, - unsigned int dma); -void hdlcdrv_unregister(struct net_device *dev); - -/* -------------------------------------------------------------------- */ - - - -#endif /* _HDLCDRV_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7969fcdd5ac4..e9e2ec8d4c19 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -162,7 +162,7 @@ static inline bool dev_xmit_complete(int rc) #if defined(CONFIG_HYPERV_NET) # define LL_MAX_HEADER 128 -#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) +#elif defined(CONFIG_WLAN) # if defined(CONFIG_MAC80211_MESH) # define LL_MAX_HEADER 128 # else @@ -2316,9 +2316,6 @@ struct net_device { #if IS_ENABLED(CONFIG_ATALK) void *atalk_ptr; #endif -#if IS_ENABLED(CONFIG_AX25) - struct ax25_dev __rcu *ax25_ptr; -#endif #if IS_ENABLED(CONFIG_CFG80211) struct wireless_dev *ieee80211_ptr; #endif diff --git a/include/linux/scc.h b/include/linux/scc.h deleted file mode 100644 index 745eabd17c10..000000000000 --- a/include/linux/scc.h +++ /dev/null @@ -1,86 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* $Id: scc.h,v 1.29 1997/04/02 14:56:45 jreuter Exp jreuter $ */ -#ifndef _SCC_H -#define _SCC_H - -#include - - -enum {TX_OFF, TX_ON}; /* command for scc_key_trx() */ - -/* Vector masks in RR2B */ - -#define VECTOR_MASK 0x06 -#define TXINT 0x00 -#define EXINT 0x02 -#define RXINT 0x04 -#define SPINT 0x06 - -#ifdef CONFIG_SCC_DELAY -#define Inb(port) inb_p(port) -#define Outb(port, val) outb_p(val, port) -#else -#define Inb(port) inb(port) -#define Outb(port, val) outb(val, port) -#endif - -/* SCC channel control structure for KISS */ - -struct scc_kiss { - unsigned char txdelay; /* Transmit Delay 10 ms/cnt */ - unsigned char persist; /* Persistence (0-255) as a % */ - unsigned char slottime; /* Delay to wait on persistence hit */ - unsigned char tailtime; /* Delay after last byte written */ - unsigned char fulldup; /* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */ - unsigned char waittime; /* Waittime before any transmit attempt */ - unsigned int maxkeyup; /* Maximum time to transmit (seconds) */ - unsigned int mintime; /* Minimal offtime after MAXKEYUP timeout (seconds) */ - unsigned int idletime; /* Maximum idle time in ALWAYS KEYED mode (seconds) */ - unsigned int maxdefer; /* Timer for CSMA channel busy limit */ - unsigned char tx_inhibit; /* Transmit is not allowed when set */ - unsigned char group; /* Group ID for AX.25 TX interlocking */ - unsigned char mode; /* 'normal' or 'hwctrl' mode (unused) */ - unsigned char softdcd; /* Use DPLL instead of DCD pin for carrier detect */ -}; - - -/* SCC channel structure */ - -struct scc_channel { - int init; /* channel exists? */ - - struct net_device *dev; /* link to device control structure */ - struct net_device_stats dev_stat;/* device statistics */ - - char brand; /* manufacturer of the board */ - long clock; /* used clock */ - - io_port ctrl; /* I/O address of CONTROL register */ - io_port data; /* I/O address of DATA register */ - io_port special; /* I/O address of special function port */ - int irq; /* Number of Interrupt */ - - char option; - char enhanced; /* Enhanced SCC support */ - - unsigned char wreg[16]; /* Copy of last written value in WRx */ - unsigned char status; /* Copy of R0 at last external interrupt */ - unsigned char dcd; /* DCD status */ - - struct scc_kiss kiss; /* control structure for KISS params */ - struct scc_stat stat; /* statistical information */ - struct scc_modem modem; /* modem information */ - - struct sk_buff_head tx_queue; /* next tx buffer */ - struct sk_buff *rx_buff; /* pointer to frame currently received */ - struct sk_buff *tx_buff; /* pointer to frame currently transmitted */ - - /* Timer */ - struct timer_list tx_t; /* tx timer for this channel */ - struct timer_list tx_wdog; /* tx watchdogs */ - - /* Channel lock */ - spinlock_t lock; /* Channel guard lock */ -}; - -#endif /* defined(_SCC_H) */ diff --git a/include/linux/yam.h b/include/linux/yam.h deleted file mode 100644 index a29b04fa1e66..000000000000 --- a/include/linux/yam.h +++ /dev/null @@ -1,67 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/*****************************************************************************/ - -/* - * yam.h -- YAM radio modem driver. - * - * Copyright (C) 1998 Frederic Rible F1OAT (frible@teaser.fr) - * Adapted from baycom.c driver written by Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - */ - -/*****************************************************************************/ - -#define SIOCYAMRESERVED (0) -#define SIOCYAMSCFG (1) /* Set configuration */ -#define SIOCYAMGCFG (2) /* Get configuration */ -#define SIOCYAMSMCS (3) /* Set mcs data */ - -#define YAM_IOBASE (1 << 0) -#define YAM_IRQ (1 << 1) -#define YAM_BITRATE (1 << 2) /* Bit rate of radio port ->57600 */ -#define YAM_MODE (1 << 3) /* 0=simplex 1=duplex 2=duplex+tempo */ -#define YAM_HOLDDLY (1 << 4) /* duplex tempo (sec) */ -#define YAM_TXDELAY (1 << 5) /* Tx Delay (ms) */ -#define YAM_TXTAIL (1 << 6) /* Tx Tail (ms) */ -#define YAM_PERSIST (1 << 7) /* Persist (ms) */ -#define YAM_SLOTTIME (1 << 8) /* Slottime (ms) */ -#define YAM_BAUDRATE (1 << 9) /* Baud rate of rs232 port ->115200 */ - -#define YAM_MAXBITRATE 57600 -#define YAM_MAXBAUDRATE 115200 -#define YAM_MAXMODE 2 -#define YAM_MAXHOLDDLY 99 -#define YAM_MAXTXDELAY 999 -#define YAM_MAXTXTAIL 999 -#define YAM_MAXPERSIST 255 -#define YAM_MAXSLOTTIME 999 - -#define YAM_FPGA_SIZE 5302 - -struct yamcfg { - unsigned int mask; /* Mask of commands */ - unsigned int iobase; /* IO Base of COM port */ - unsigned int irq; /* IRQ of COM port */ - unsigned int bitrate; /* Bit rate of radio port */ - unsigned int baudrate; /* Baud rate of the RS232 port */ - unsigned int txdelay; /* TxDelay */ - unsigned int txtail; /* TxTail */ - unsigned int persist; /* Persistence */ - unsigned int slottime; /* Slottime */ - unsigned int mode; /* mode 0 (simp), 1(Dupl), 2(Dupl+delay) */ - unsigned int holddly; /* PTT delay in FullDuplex 2 mode */ -}; - -struct yamdrv_ioctl_cfg { - int cmd; - struct yamcfg cfg; -}; - -struct yamdrv_ioctl_mcs { - int cmd; - unsigned int bitrate; - unsigned char bits[YAM_FPGA_SIZE]; -}; diff --git a/include/net/ax25.h b/include/net/ax25.h index 9fc6a6657266..6b2f518facdb 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -1,480 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* - * Declarations of AX.25 type objects. - * - * Alan Cox (GW4PTS) 10/11/93 - */ #ifndef _AX25_H -#define _AX25_H +#define _AX25_H #include -#include -#include -#include -#include -#include -#include -#include -#include -#define AX25_T1CLAMPLO 1 -#define AX25_T1CLAMPHI (30 * HZ) - -#define AX25_BPQ_HEADER_LEN 16 -#define AX25_KISS_HEADER_LEN 1 - -#define AX25_HEADER_LEN 17 -#define AX25_ADDR_LEN 7 -#define AX25_DIGI_HEADER_LEN (AX25_MAX_DIGIS * AX25_ADDR_LEN) -#define AX25_MAX_HEADER_LEN (AX25_HEADER_LEN + AX25_DIGI_HEADER_LEN) - -/* AX.25 Protocol IDs */ -#define AX25_P_ROSE 0x01 -#define AX25_P_VJCOMP 0x06 /* Compressed TCP/IP packet */ - /* Van Jacobsen (RFC 1144) */ -#define AX25_P_VJUNCOMP 0x07 /* Uncompressed TCP/IP packet */ - /* Van Jacobsen (RFC 1144) */ -#define AX25_P_SEGMENT 0x08 /* Segmentation fragment */ -#define AX25_P_TEXNET 0xc3 /* TEXTNET datagram protocol */ -#define AX25_P_LQ 0xc4 /* Link Quality Protocol */ -#define AX25_P_ATALK 0xca /* Appletalk */ -#define AX25_P_ATALK_ARP 0xcb /* Appletalk ARP */ -#define AX25_P_IP 0xcc /* ARPA Internet Protocol */ -#define AX25_P_ARP 0xcd /* ARPA Address Resolution */ -#define AX25_P_FLEXNET 0xce /* FlexNet */ -#define AX25_P_NETROM 0xcf /* NET/ROM */ -#define AX25_P_TEXT 0xF0 /* No layer 3 protocol impl. */ - -/* AX.25 Segment control values */ -#define AX25_SEG_REM 0x7F -#define AX25_SEG_FIRST 0x80 - -#define AX25_CBIT 0x80 /* Command/Response bit */ -#define AX25_EBIT 0x01 /* HDLC Address Extension bit */ -#define AX25_HBIT 0x80 /* Has been repeated bit */ - -#define AX25_SSSID_SPARE 0x60 /* Unused bits in SSID for standard AX.25 */ -#define AX25_ESSID_SPARE 0x20 /* Unused bits in SSID for extended AX.25 */ -#define AX25_DAMA_FLAG 0x20 /* Well, it is *NOT* unused! (dl1bke 951121 */ - -#define AX25_COND_ACK_PENDING 0x01 -#define AX25_COND_REJECT 0x02 -#define AX25_COND_PEER_RX_BUSY 0x04 -#define AX25_COND_OWN_RX_BUSY 0x08 -#define AX25_COND_DAMA_MODE 0x10 - -#ifndef _LINUX_NETDEVICE_H -#include -#endif - -/* Upper sub-layer (LAPB) definitions */ - -/* Control field templates */ -#define AX25_I 0x00 /* Information frames */ -#define AX25_S 0x01 /* Supervisory frames */ -#define AX25_RR 0x01 /* Receiver ready */ -#define AX25_RNR 0x05 /* Receiver not ready */ -#define AX25_REJ 0x09 /* Reject */ -#define AX25_U 0x03 /* Unnumbered frames */ -#define AX25_SABM 0x2f /* Set Asynchronous Balanced Mode */ -#define AX25_SABME 0x6f /* Set Asynchronous Balanced Mode Extended */ -#define AX25_DISC 0x43 /* Disconnect */ -#define AX25_DM 0x0f /* Disconnected mode */ -#define AX25_UA 0x63 /* Unnumbered acknowledge */ -#define AX25_FRMR 0x87 /* Frame reject */ -#define AX25_UI 0x03 /* Unnumbered information */ -#define AX25_XID 0xaf /* Exchange information */ -#define AX25_TEST 0xe3 /* Test */ - -#define AX25_PF 0x10 /* Poll/final bit for standard AX.25 */ -#define AX25_EPF 0x01 /* Poll/final bit for extended AX.25 */ - -#define AX25_ILLEGAL 0x100 /* Impossible to be a real frame type */ - -#define AX25_POLLOFF 0 -#define AX25_POLLON 1 - -/* AX25 L2 C-bit */ -#define AX25_COMMAND 1 -#define AX25_RESPONSE 2 - -/* Define Link State constants. */ - -enum { - AX25_STATE_0, /* Listening */ - AX25_STATE_1, /* SABM sent */ - AX25_STATE_2, /* DISC sent */ - AX25_STATE_3, /* Established */ - AX25_STATE_4 /* Recovery */ -}; - -#define AX25_MODULUS 8 /* Standard AX.25 modulus */ -#define AX25_EMODULUS 128 /* Extended AX.25 modulus */ - -enum { - AX25_PROTO_STD_SIMPLEX, - AX25_PROTO_STD_DUPLEX, -#ifdef CONFIG_AX25_DAMA_SLAVE - AX25_PROTO_DAMA_SLAVE, -#endif - __AX25_PROTO_MAX, - AX25_PROTO_MAX = __AX25_PROTO_MAX -1 -}; - -enum { - AX25_VALUES_IPDEFMODE, /* 0=DG 1=VC */ - AX25_VALUES_AXDEFMODE, /* 0=Normal 1=Extended Seq Nos */ - AX25_VALUES_BACKOFF, /* 0=None 1=Linear 2=Exponential */ - AX25_VALUES_CONMODE, /* Allow connected modes - 0=No 1=no "PID text" 2=all PIDs */ - AX25_VALUES_WINDOW, /* Default window size for standard AX.25 */ - AX25_VALUES_EWINDOW, /* Default window size for extended AX.25 */ - AX25_VALUES_T1, /* Default T1 timeout value */ - AX25_VALUES_T2, /* Default T2 timeout value */ - AX25_VALUES_T3, /* Default T3 timeout value */ - AX25_VALUES_IDLE, /* Connected mode idle timer */ - AX25_VALUES_N2, /* Default N2 value */ - AX25_VALUES_PACLEN, /* AX.25 MTU */ - AX25_VALUES_PROTOCOL, /* Std AX.25, DAMA Slave */ -#ifdef CONFIG_AX25_DAMA_SLAVE - AX25_VALUES_DS_TIMEOUT, /* DAMA Slave timeout */ -#endif - AX25_MAX_VALUES /* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */ -}; - -#define AX25_DEF_IPDEFMODE 0 /* Datagram */ -#define AX25_DEF_AXDEFMODE 0 /* Normal */ -#define AX25_DEF_BACKOFF 1 /* Linear backoff */ -#define AX25_DEF_CONMODE 2 /* Connected mode allowed */ -#define AX25_DEF_WINDOW 2 /* Window=2 */ -#define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */ -#define AX25_DEF_T1 10000 /* T1=10s */ -#define AX25_DEF_T2 3000 /* T2=3s */ -#define AX25_DEF_T3 300000 /* T3=300s */ -#define AX25_DEF_N2 10 /* N2=10 */ -#define AX25_DEF_IDLE 0 /* Idle=None */ -#define AX25_DEF_PACLEN 256 /* Paclen=256 */ -#define AX25_DEF_PROTOCOL AX25_PROTO_STD_SIMPLEX /* Standard AX.25 */ -#define AX25_DEF_DS_TIMEOUT 180000 /* DAMA timeout 3 minutes */ - -typedef struct ax25_uid_assoc { - struct hlist_node uid_node; - refcount_t refcount; - kuid_t uid; - ax25_address call; -} ax25_uid_assoc; - -#define ax25_uid_for_each(__ax25, list) \ - hlist_for_each_entry(__ax25, list, uid_node) - -#define ax25_uid_hold(ax25) \ - refcount_inc(&((ax25)->refcount)) - -static inline void ax25_uid_put(ax25_uid_assoc *assoc) -{ - if (refcount_dec_and_test(&assoc->refcount)) { - kfree(assoc); - } -} - -typedef struct { - ax25_address calls[AX25_MAX_DIGIS]; - unsigned char repeated[AX25_MAX_DIGIS]; - unsigned char ndigi; - signed char lastrepeat; -} ax25_digi; - -typedef struct ax25_route { - struct ax25_route *next; - ax25_address callsign; - struct net_device *dev; - ax25_digi *digipeat; - char ip_mode; -} ax25_route; - -void __ax25_put_route(ax25_route *ax25_rt); - -extern rwlock_t ax25_route_lock; - -static inline void ax25_route_lock_use(void) -{ - read_lock(&ax25_route_lock); -} - -static inline void ax25_route_lock_unuse(void) -{ - read_unlock(&ax25_route_lock); -} - -typedef struct { - char slave; /* slave_mode? */ - struct timer_list slave_timer; /* timeout timer */ - unsigned short slave_timeout; /* when? */ -} ax25_dama_info; - -typedef struct ax25_dev { - struct list_head list; - - struct net_device *dev; - netdevice_tracker dev_tracker; - - struct net_device *forward; - struct ctl_table_header *sysheader; - int values[AX25_MAX_VALUES]; -#ifdef CONFIG_AX25_DAMA_SLAVE - ax25_dama_info dama; -#endif - refcount_t refcount; - bool device_up; - struct rcu_head rcu; -} ax25_dev; - -typedef struct ax25_cb { - struct hlist_node ax25_node; - ax25_address source_addr, dest_addr; - ax25_digi *digipeat; - ax25_dev *ax25_dev; - netdevice_tracker dev_tracker; - unsigned char iamdigi; - unsigned char state, modulus, pidincl; - unsigned short vs, vr, va; - unsigned char condition, backoff; - unsigned char n2, n2count; - struct timer_list t1timer, t2timer, t3timer, idletimer; - unsigned long t1, t2, t3, idle, rtt; - unsigned short paclen, fragno, fraglen; - struct sk_buff_head write_queue; - struct sk_buff_head reseq_queue; - struct sk_buff_head ack_queue; - struct sk_buff_head frag_queue; - unsigned char window; - struct timer_list timer, dtimer; - struct sock *sk; /* Backlink to socket */ - refcount_t refcount; -} ax25_cb; - -struct ax25_sock { - struct sock sk; - struct ax25_cb *cb; -}; - -#define ax25_sk(ptr) container_of_const(ptr, struct ax25_sock, sk) - -static inline struct ax25_cb *sk_to_ax25(const struct sock *sk) -{ - return ax25_sk(sk)->cb; -} - -#define ax25_for_each(__ax25, list) \ - hlist_for_each_entry(__ax25, list, ax25_node) - -#define ax25_cb_hold(__ax25) \ - refcount_inc(&((__ax25)->refcount)) - -static __inline__ void ax25_cb_put(ax25_cb *ax25) -{ - if (refcount_dec_and_test(&ax25->refcount)) { - kfree(ax25->digipeat); - kfree(ax25); - } -} - -static inline void ax25_dev_hold(ax25_dev *ax25_dev) -{ - refcount_inc(&ax25_dev->refcount); -} - -static inline void ax25_dev_put(ax25_dev *ax25_dev) -{ - if (refcount_dec_and_test(&ax25_dev->refcount)) - kfree_rcu(ax25_dev, rcu); -} -static inline __be16 ax25_type_trans(struct sk_buff *skb, struct net_device *dev) -{ - skb->dev = dev; - skb_reset_mac_header(skb); - skb->pkt_type = PACKET_HOST; - return htons(ETH_P_AX25); -} - -/* af_ax25.c */ -extern struct hlist_head ax25_list; -extern spinlock_t ax25_list_lock; -void ax25_cb_add(ax25_cb *); -struct sock *ax25_find_listener(ax25_address *, int, struct net_device *, int); -struct sock *ax25_get_socket(ax25_address *, ax25_address *, int); -ax25_cb *ax25_find_cb(const ax25_address *, ax25_address *, ax25_digi *, - struct net_device *); -void ax25_send_to_raw(ax25_address *, struct sk_buff *, int); -void ax25_destroy_socket(ax25_cb *); -ax25_cb * __must_check ax25_create_cb(void); -void ax25_fillin_cb(ax25_cb *, ax25_dev *); -struct sock *ax25_make_new(struct sock *, struct ax25_dev *); - -/* ax25_addr.c */ -extern const ax25_address ax25_bcast; -extern const ax25_address ax25_defaddr; -extern const ax25_address null_ax25_address; -char *ax2asc(char *buf, const ax25_address *); -void asc2ax(ax25_address *addr, const char *callsign); -int ax25cmp(const ax25_address *, const ax25_address *); -int ax25digicmp(const ax25_digi *, const ax25_digi *); -const unsigned char *ax25_addr_parse(const unsigned char *, int, - ax25_address *, ax25_address *, ax25_digi *, int *, int *); -int ax25_addr_build(unsigned char *, const ax25_address *, - const ax25_address *, const ax25_digi *, int, int); -int ax25_addr_size(const ax25_digi *); -void ax25_digi_invert(const ax25_digi *, ax25_digi *); - -/* ax25_dev.c */ -extern spinlock_t ax25_dev_lock; - -#if IS_ENABLED(CONFIG_AX25) -static inline ax25_dev *ax25_dev_ax25dev(const struct net_device *dev) -{ - return rcu_dereference_rtnl(dev->ax25_ptr); -} -#endif - -ax25_dev *ax25_addr_ax25dev(ax25_address *); -void ax25_dev_device_up(struct net_device *); -void ax25_dev_device_down(struct net_device *); -int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *); -struct net_device *ax25_fwd_dev(struct net_device *); -void ax25_dev_free(void); - -/* ax25_ds_in.c */ -int ax25_ds_frame_in(ax25_cb *, struct sk_buff *, int); - -/* ax25_ds_subr.c */ -void ax25_ds_nr_error_recovery(ax25_cb *); -void ax25_ds_enquiry_response(ax25_cb *); -void ax25_ds_establish_data_link(ax25_cb *); -void ax25_dev_dama_off(ax25_dev *); -void ax25_dama_on(ax25_cb *); -void ax25_dama_off(ax25_cb *); - -/* ax25_ds_timer.c */ -void ax25_ds_setup_timer(ax25_dev *); -void ax25_ds_set_timer(ax25_dev *); -void ax25_ds_del_timer(ax25_dev *); -void ax25_ds_timer(ax25_cb *); -void ax25_ds_t1_timeout(ax25_cb *); -void ax25_ds_heartbeat_expiry(ax25_cb *); -void ax25_ds_t3timer_expiry(ax25_cb *); -void ax25_ds_idletimer_expiry(ax25_cb *); - -/* ax25_iface.c */ - -struct ax25_protocol { - struct ax25_protocol *next; - unsigned int pid; - int (*func)(struct sk_buff *, ax25_cb *); -}; - -void ax25_register_pid(struct ax25_protocol *ap); -void ax25_protocol_release(unsigned int); - -struct ax25_linkfail { - struct hlist_node lf_node; - void (*func)(ax25_cb *, int); -}; - -void ax25_linkfail_register(struct ax25_linkfail *lf); -void ax25_linkfail_release(struct ax25_linkfail *lf); -int __must_check ax25_listen_register(const ax25_address *, - struct net_device *); -void ax25_listen_release(const ax25_address *, struct net_device *); -int(*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *); -int ax25_listen_mine(const ax25_address *, struct net_device *); -void ax25_link_failed(ax25_cb *, int); -int ax25_protocol_is_registered(unsigned int); - -/* ax25_in.c */ -int ax25_rx_iframe(ax25_cb *, struct sk_buff *); -int ax25_kiss_rcv(struct sk_buff *, struct net_device *, struct packet_type *, - struct net_device *); - -/* ax25_ip.c */ -netdev_tx_t ax25_ip_xmit(struct sk_buff *skb); -extern const struct header_ops ax25_header_ops; - -/* ax25_out.c */ -ax25_cb *ax25_send_frame(struct sk_buff *, int, const ax25_address *, - ax25_address *, ax25_digi *, struct net_device *); -void ax25_output(ax25_cb *, int, struct sk_buff *); -void ax25_kick(ax25_cb *); -void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int); -void ax25_queue_xmit(struct sk_buff *skb, struct net_device *dev); -int ax25_check_iframes_acked(ax25_cb *, unsigned short); - -/* ax25_route.c */ -void ax25_rt_device_down(struct net_device *); -int ax25_rt_ioctl(unsigned int, void __user *); -extern const struct seq_operations ax25_rt_seqops; -ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev); -struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, - ax25_address *, ax25_digi *); -void ax25_rt_free(void); - -/* ax25_std_in.c */ -int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int); - -/* ax25_std_subr.c */ -void ax25_std_nr_error_recovery(ax25_cb *); -void ax25_std_establish_data_link(ax25_cb *); -void ax25_std_transmit_enquiry(ax25_cb *); -void ax25_std_enquiry_response(ax25_cb *); -void ax25_std_timeout_response(ax25_cb *); - -/* ax25_std_timer.c */ -void ax25_std_heartbeat_expiry(ax25_cb *); -void ax25_std_t1timer_expiry(ax25_cb *); -void ax25_std_t2timer_expiry(ax25_cb *); -void ax25_std_t3timer_expiry(ax25_cb *); -void ax25_std_idletimer_expiry(ax25_cb *); - -/* ax25_subr.c */ -void ax25_clear_queues(ax25_cb *); -void ax25_frames_acked(ax25_cb *, unsigned short); -void ax25_requeue_frames(ax25_cb *); -int ax25_validate_nr(ax25_cb *, unsigned short); -int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *); -void ax25_send_control(ax25_cb *, int, int, int); -void ax25_return_dm(struct net_device *, ax25_address *, ax25_address *, - ax25_digi *); -void ax25_calculate_t1(ax25_cb *); -void ax25_calculate_rtt(ax25_cb *); -void ax25_disconnect(ax25_cb *, int); - -/* ax25_timer.c */ -void ax25_setup_timers(ax25_cb *); -void ax25_start_heartbeat(ax25_cb *); -void ax25_start_t1timer(ax25_cb *); -void ax25_start_t2timer(ax25_cb *); -void ax25_start_t3timer(ax25_cb *); -void ax25_start_idletimer(ax25_cb *); -void ax25_stop_heartbeat(ax25_cb *); -void ax25_stop_t1timer(ax25_cb *); -void ax25_stop_t2timer(ax25_cb *); -void ax25_stop_t3timer(ax25_cb *); -void ax25_stop_idletimer(ax25_cb *); -int ax25_t1timer_running(ax25_cb *); -unsigned long ax25_display_timer(struct timer_list *); - -/* ax25_uid.c */ -extern int ax25_uid_policy; -ax25_uid_assoc *ax25_findbyuid(kuid_t); -int __must_check ax25_uid_ioctl(int, struct sockaddr_ax25 *); -extern const struct seq_operations ax25_uid_seqops; -void ax25_uid_free(void); - -/* sysctl_net_ax25.c */ -#ifdef CONFIG_SYSCTL -int ax25_register_dev_sysctl(ax25_dev *ax25_dev); -void ax25_unregister_dev_sysctl(ax25_dev *ax25_dev); -#else -static inline int ax25_register_dev_sysctl(ax25_dev *ax25_dev) { return 0; } -static inline void ax25_unregister_dev_sysctl(ax25_dev *ax25_dev) {} -#endif /* CONFIG_SYSCTL */ +#define AX25_ADDR_LEN 7 +#define AX25_P_IP 0xCC #endif diff --git a/include/net/netrom.h b/include/net/netrom.h deleted file mode 100644 index f0565a5987d1..000000000000 --- a/include/net/netrom.h +++ /dev/null @@ -1,273 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Declarations of NET/ROM type objects. - * - * Jonathan Naylor G4KLX 9/4/95 - */ - -#ifndef _NETROM_H -#define _NETROM_H - -#include -#include -#include -#include -#include -#include -#include - -#define NR_NETWORK_LEN 15 -#define NR_TRANSPORT_LEN 5 - -#define NR_PROTO_IP 0x0C - -#define NR_PROTOEXT 0x00 -#define NR_CONNREQ 0x01 -#define NR_CONNACK 0x02 -#define NR_DISCREQ 0x03 -#define NR_DISCACK 0x04 -#define NR_INFO 0x05 -#define NR_INFOACK 0x06 -#define NR_RESET 0x07 - -#define NR_CHOKE_FLAG 0x80 -#define NR_NAK_FLAG 0x40 -#define NR_MORE_FLAG 0x20 - -/* Define Link State constants. */ -enum { - NR_STATE_0, - NR_STATE_1, - NR_STATE_2, - NR_STATE_3 -}; - -#define NR_COND_ACK_PENDING 0x01 -#define NR_COND_REJECT 0x02 -#define NR_COND_PEER_RX_BUSY 0x04 -#define NR_COND_OWN_RX_BUSY 0x08 - -#define NR_DEFAULT_T1 120000 /* Outstanding frames - 120 seconds */ -#define NR_DEFAULT_T2 5000 /* Response delay - 5 seconds */ -#define NR_DEFAULT_N2 3 /* Number of Retries - 3 */ -#define NR_DEFAULT_T4 180000 /* Busy Delay - 180 seconds */ -#define NR_DEFAULT_IDLE 0 /* No Activity Timeout - none */ -#define NR_DEFAULT_WINDOW 4 /* Default Window Size - 4 */ -#define NR_DEFAULT_OBS 6 /* Default Obsolescence Count - 6 */ -#define NR_DEFAULT_QUAL 10 /* Default Neighbour Quality - 10 */ -#define NR_DEFAULT_TTL 16 /* Default Time To Live - 16 */ -#define NR_DEFAULT_ROUTING 1 /* Is routing enabled ? */ -#define NR_DEFAULT_FAILS 2 /* Link fails until route fails */ -#define NR_DEFAULT_RESET 0 /* Sent / accept reset cmds? */ - -#define NR_MODULUS 256 -#define NR_MAX_WINDOW_SIZE 127 /* Maximum Window Allowable - 127 */ -#define NR_MAX_PACKET_SIZE 236 /* Maximum Packet Length - 236 */ - -struct nr_sock { - struct sock sock; - ax25_address user_addr, source_addr, dest_addr; - struct net_device *device; - unsigned char my_index, my_id; - unsigned char your_index, your_id; - unsigned char state, condition, bpqext, window; - unsigned short vs, vr, va, vl; - unsigned char n2, n2count; - unsigned long t1, t2, t4, idle; - unsigned short fraglen; - struct timer_list t1timer; - struct timer_list t2timer; - struct timer_list t4timer; - struct timer_list idletimer; - struct sk_buff_head ack_queue; - struct sk_buff_head reseq_queue; - struct sk_buff_head frag_queue; -}; - -#define nr_sk(sk) ((struct nr_sock *)(sk)) - -struct nr_neigh { - struct hlist_node neigh_node; - ax25_address callsign; - ax25_digi *digipeat; - ax25_cb *ax25; - struct net_device *dev; - unsigned char quality; - unsigned char locked; - unsigned short count; - unsigned int number; - unsigned char failed; - refcount_t refcount; -}; - -struct nr_route { - unsigned char quality; - unsigned char obs_count; - struct nr_neigh *neighbour; -}; - -struct nr_node { - struct hlist_node node_node; - ax25_address callsign; - char mnemonic[7]; - unsigned char which; - unsigned char count; - struct nr_route routes[3]; - refcount_t refcount; - spinlock_t node_lock; -}; - -/********************************************************************* - * nr_node & nr_neigh lists, refcounting and locking - *********************************************************************/ - -#define nr_node_hold(__nr_node) \ - refcount_inc(&((__nr_node)->refcount)) - -static __inline__ void nr_node_put(struct nr_node *nr_node) -{ - if (refcount_dec_and_test(&nr_node->refcount)) { - kfree(nr_node); - } -} - -#define nr_neigh_hold(__nr_neigh) \ - refcount_inc(&((__nr_neigh)->refcount)) - -static __inline__ void nr_neigh_put(struct nr_neigh *nr_neigh) -{ - if (refcount_dec_and_test(&nr_neigh->refcount)) { - if (nr_neigh->ax25) - ax25_cb_put(nr_neigh->ax25); - kfree(nr_neigh->digipeat); - kfree(nr_neigh); - } -} - -/* nr_node_lock and nr_node_unlock also hold/put the node's refcounter. - */ -static __inline__ void nr_node_lock(struct nr_node *nr_node) -{ - nr_node_hold(nr_node); - spin_lock_bh(&nr_node->node_lock); -} - -static __inline__ void nr_node_unlock(struct nr_node *nr_node) -{ - spin_unlock_bh(&nr_node->node_lock); - nr_node_put(nr_node); -} - -#define nr_neigh_for_each(__nr_neigh, list) \ - hlist_for_each_entry(__nr_neigh, list, neigh_node) - -#define nr_neigh_for_each_safe(__nr_neigh, node2, list) \ - hlist_for_each_entry_safe(__nr_neigh, node2, list, neigh_node) - -#define nr_node_for_each(__nr_node, list) \ - hlist_for_each_entry(__nr_node, list, node_node) - -#define nr_node_for_each_safe(__nr_node, node2, list) \ - hlist_for_each_entry_safe(__nr_node, node2, list, node_node) - - -/*********************************************************************/ - -/* af_netrom.c */ -extern int sysctl_netrom_default_path_quality; -extern int sysctl_netrom_obsolescence_count_initialiser; -extern int sysctl_netrom_network_ttl_initialiser; -extern int sysctl_netrom_transport_timeout; -extern int sysctl_netrom_transport_maximum_tries; -extern int sysctl_netrom_transport_acknowledge_delay; -extern int sysctl_netrom_transport_busy_delay; -extern int sysctl_netrom_transport_requested_window_size; -extern int sysctl_netrom_transport_no_activity_timeout; -extern int sysctl_netrom_routing_control; -extern int sysctl_netrom_link_fails_count; -extern int sysctl_netrom_reset_circuit; - -int nr_rx_frame(struct sk_buff *, struct net_device *); -void nr_destroy_socket(struct sock *); - -/* nr_dev.c */ -int nr_rx_ip(struct sk_buff *, struct net_device *); -void nr_setup(struct net_device *); - -/* nr_in.c */ -int nr_process_rx_frame(struct sock *, struct sk_buff *); - -/* nr_loopback.c */ -void nr_loopback_init(void); -void nr_loopback_clear(void); -int nr_loopback_queue(struct sk_buff *); - -/* nr_out.c */ -void nr_output(struct sock *, struct sk_buff *); -void nr_send_nak_frame(struct sock *); -void nr_kick(struct sock *); -void nr_transmit_buffer(struct sock *, struct sk_buff *); -void nr_establish_data_link(struct sock *); -void nr_enquiry_response(struct sock *); -void nr_check_iframes_acked(struct sock *, unsigned short); - -/* nr_route.c */ -void nr_rt_device_down(struct net_device *); -struct net_device *nr_dev_first(void); -struct net_device *nr_dev_get(ax25_address *); -int nr_rt_ioctl(unsigned int, void __user *); -void nr_link_failed(ax25_cb *, int); -int nr_route_frame(struct sk_buff *, ax25_cb *); -extern const struct seq_operations nr_node_seqops; -extern const struct seq_operations nr_neigh_seqops; -void nr_rt_free(void); - -/* nr_subr.c */ -void nr_clear_queues(struct sock *); -void nr_frames_acked(struct sock *, unsigned short); -void nr_requeue_frames(struct sock *); -int nr_validate_nr(struct sock *, unsigned short); -int nr_in_rx_window(struct sock *, unsigned short); -void nr_write_internal(struct sock *, int); - -void __nr_transmit_reply(struct sk_buff *skb, int mine, unsigned char cmdflags); - -/* - * This routine is called when a Connect Acknowledge with the Choke Flag - * set is needed to refuse a connection. - */ -#define nr_transmit_refusal(skb, mine) \ -do { \ - __nr_transmit_reply((skb), (mine), NR_CONNACK | NR_CHOKE_FLAG); \ -} while (0) - -/* - * This routine is called when we don't have a circuit matching an incoming - * NET/ROM packet. This is an G8PZT Xrouter extension. - */ -#define nr_transmit_reset(skb, mine) \ -do { \ - __nr_transmit_reply((skb), (mine), NR_RESET); \ -} while (0) - -void nr_disconnect(struct sock *, int); - -/* nr_timer.c */ -void nr_init_timers(struct sock *sk); -void nr_start_heartbeat(struct sock *); -void nr_start_t1timer(struct sock *); -void nr_start_t2timer(struct sock *); -void nr_start_t4timer(struct sock *); -void nr_start_idletimer(struct sock *); -void nr_stop_heartbeat(struct sock *); -void nr_stop_t1timer(struct sock *); -void nr_stop_t2timer(struct sock *); -void nr_stop_t4timer(struct sock *); -void nr_stop_idletimer(struct sock *); -int nr_t1timer_running(struct sock *); - -/* sysctl_net_netrom.c */ -int nr_register_sysctl(void); -void nr_unregister_sysctl(void); - -#endif diff --git a/include/net/rose.h b/include/net/rose.h index 2b5491bbf39a..41bfcb224f0b 100644 --- a/include/net/rose.h +++ b/include/net/rose.h @@ -1,266 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* - * Declarations of Rose type objects. - * - * Jonathan Naylor G4KLX 25/8/96 - */ - #ifndef _ROSE_H -#define _ROSE_H - -#include -#include -#include -#include - -#define ROSE_ADDR_LEN 5 - -#define ROSE_MIN_LEN 3 - -#define ROSE_CALL_REQ_ADDR_LEN_OFF 3 -#define ROSE_CALL_REQ_ADDR_LEN_VAL 0xAA /* each address is 10 digits */ -#define ROSE_CALL_REQ_DEST_ADDR_OFF 4 -#define ROSE_CALL_REQ_SRC_ADDR_OFF 9 -#define ROSE_CALL_REQ_FACILITIES_OFF 14 - -#define ROSE_GFI 0x10 -#define ROSE_Q_BIT 0x80 -#define ROSE_D_BIT 0x40 -#define ROSE_M_BIT 0x10 - -#define ROSE_CALL_REQUEST 0x0B -#define ROSE_CALL_ACCEPTED 0x0F -#define ROSE_CLEAR_REQUEST 0x13 -#define ROSE_CLEAR_CONFIRMATION 0x17 -#define ROSE_DATA 0x00 -#define ROSE_INTERRUPT 0x23 -#define ROSE_INTERRUPT_CONFIRMATION 0x27 -#define ROSE_RR 0x01 -#define ROSE_RNR 0x05 -#define ROSE_REJ 0x09 -#define ROSE_RESET_REQUEST 0x1B -#define ROSE_RESET_CONFIRMATION 0x1F -#define ROSE_REGISTRATION_REQUEST 0xF3 -#define ROSE_REGISTRATION_CONFIRMATION 0xF7 -#define ROSE_RESTART_REQUEST 0xFB -#define ROSE_RESTART_CONFIRMATION 0xFF -#define ROSE_DIAGNOSTIC 0xF1 -#define ROSE_ILLEGAL 0xFD - -/* Define Link State constants. */ - -enum { - ROSE_STATE_0, /* Ready */ - ROSE_STATE_1, /* Awaiting Call Accepted */ - ROSE_STATE_2, /* Awaiting Clear Confirmation */ - ROSE_STATE_3, /* Data Transfer */ - ROSE_STATE_4, /* Awaiting Reset Confirmation */ - ROSE_STATE_5 /* Deferred Call Acceptance */ -}; - -#define ROSE_DEFAULT_T0 180000 /* Default T10 T20 value */ -#define ROSE_DEFAULT_T1 200000 /* Default T11 T21 value */ -#define ROSE_DEFAULT_T2 180000 /* Default T12 T22 value */ -#define ROSE_DEFAULT_T3 180000 /* Default T13 T23 value */ -#define ROSE_DEFAULT_HB 5000 /* Default Holdback value */ -#define ROSE_DEFAULT_IDLE 0 /* No Activity Timeout - none */ -#define ROSE_DEFAULT_ROUTING 1 /* Default routing flag */ -#define ROSE_DEFAULT_FAIL_TIMEOUT 120000 /* Time until link considered usable */ -#define ROSE_DEFAULT_MAXVC 50 /* Maximum number of VCs per neighbour */ -#define ROSE_DEFAULT_WINDOW_SIZE 7 /* Default window size */ - -#define ROSE_MODULUS 8 -#define ROSE_MAX_PACKET_SIZE 251 /* Maximum packet size */ - -#define ROSE_COND_ACK_PENDING 0x01 -#define ROSE_COND_PEER_RX_BUSY 0x02 -#define ROSE_COND_OWN_RX_BUSY 0x04 - -#define FAC_NATIONAL 0x00 -#define FAC_CCITT 0x0F - -#define FAC_NATIONAL_RAND 0x7F -#define FAC_NATIONAL_FLAGS 0x3F -#define FAC_NATIONAL_DEST_DIGI 0xE9 -#define FAC_NATIONAL_SRC_DIGI 0xEB -#define FAC_NATIONAL_FAIL_CALL 0xED -#define FAC_NATIONAL_FAIL_ADD 0xEE -#define FAC_NATIONAL_DIGIS 0xEF - -#define FAC_CCITT_DEST_NSAP 0xC9 -#define FAC_CCITT_SRC_NSAP 0xCB - -struct rose_neigh { - struct rose_neigh *next; - ax25_address callsign; - ax25_digi *digipeat; - ax25_cb *ax25; - struct net_device *dev; - unsigned short count; - refcount_t use; - unsigned int number; - char restarted; - char dce_mode; - char loopback; - struct sk_buff_head queue; - struct timer_list t0timer; - struct timer_list ftimer; -}; - -struct rose_node { - struct rose_node *next; - rose_address address; - unsigned short mask; - unsigned char count; - char loopback; - struct rose_neigh *neighbour[3]; -}; - -struct rose_route { - struct rose_route *next; - unsigned int lci1, lci2; - rose_address src_addr, dest_addr; - ax25_address src_call, dest_call; - struct rose_neigh *neigh1, *neigh2; - unsigned int rand; -}; - -struct rose_sock { - struct sock sock; - rose_address source_addr, dest_addr; - ax25_address source_call, dest_call; - unsigned char source_ndigis, dest_ndigis; - ax25_address source_digis[ROSE_MAX_DIGIS]; - ax25_address dest_digis[ROSE_MAX_DIGIS]; - struct rose_neigh *neighbour; - struct net_device *device; - netdevice_tracker dev_tracker; - unsigned int lci, rand; - unsigned char state, condition, qbitincl, defer; - unsigned char cause, diagnostic; - unsigned short vs, vr, va, vl; - unsigned long t1, t2, t3, hb, idle; -#ifdef M_BIT - unsigned short fraglen; - struct sk_buff_head frag_queue; -#endif - struct sk_buff_head ack_queue; - struct rose_facilities_struct facilities; - struct timer_list timer; - struct timer_list idletimer; -}; - -#define rose_sk(sk) ((struct rose_sock *)(sk)) - -static inline void rose_neigh_hold(struct rose_neigh *rose_neigh) -{ - refcount_inc(&rose_neigh->use); -} - -static inline void rose_neigh_put(struct rose_neigh *rose_neigh) -{ - if (refcount_dec_and_test(&rose_neigh->use)) { - if (rose_neigh->ax25) - ax25_cb_put(rose_neigh->ax25); - kfree(rose_neigh->digipeat); - kfree(rose_neigh); - } -} - -/* af_rose.c */ -extern ax25_address rose_callsign; -extern int sysctl_rose_restart_request_timeout; -extern int sysctl_rose_call_request_timeout; -extern int sysctl_rose_reset_request_timeout; -extern int sysctl_rose_clear_request_timeout; -extern int sysctl_rose_no_activity_timeout; -extern int sysctl_rose_ack_hold_back_timeout; -extern int sysctl_rose_routing_control; -extern int sysctl_rose_link_fail_timeout; -extern int sysctl_rose_maximum_vcs; -extern int sysctl_rose_window_size; - -int rosecmp(const rose_address *, const rose_address *); -int rosecmpm(const rose_address *, const rose_address *, unsigned short); -char *rose2asc(char *buf, const rose_address *); -struct sock *rose_find_socket(unsigned int, struct rose_neigh *); -void rose_kill_by_neigh(struct rose_neigh *); -unsigned int rose_new_lci(struct rose_neigh *); -int rose_rx_call_request(struct sk_buff *, struct net_device *, - struct rose_neigh *, unsigned int); -void rose_destroy_socket(struct sock *); - -/* rose_dev.c */ -void rose_setup(struct net_device *); - -/* rose_in.c */ -int rose_process_rx_frame(struct sock *, struct sk_buff *); - -/* rose_link.c */ -void rose_start_ftimer(struct rose_neigh *); -void rose_stop_ftimer(struct rose_neigh *); -void rose_stop_t0timer(struct rose_neigh *); -int rose_ftimer_running(struct rose_neigh *); -void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, - unsigned short); -void rose_transmit_clear_request(struct rose_neigh *, unsigned int, - unsigned char, unsigned char); -void rose_transmit_link(struct sk_buff *, struct rose_neigh *); - -/* rose_loopback.c */ -void rose_loopback_init(void); -void rose_loopback_clear(void); -int rose_loopback_queue(struct sk_buff *, struct rose_neigh *); - -/* rose_out.c */ -void rose_kick(struct sock *); -void rose_enquiry_response(struct sock *); - -/* rose_route.c */ -extern struct rose_neigh *rose_loopback_neigh; -extern const struct seq_operations rose_neigh_seqops; -extern const struct seq_operations rose_node_seqops; -extern struct seq_operations rose_route_seqops; - -void rose_add_loopback_neigh(void); -int __must_check rose_add_loopback_node(const rose_address *); -void rose_del_loopback_node(const rose_address *); -void rose_rt_device_down(struct net_device *); -void rose_link_device_down(struct net_device *); -struct net_device *rose_dev_first(void); -struct net_device *rose_dev_get(rose_address *); -struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *); -struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *, - unsigned char *, int); -int rose_rt_ioctl(unsigned int, void __user *); -void rose_link_failed(ax25_cb *, int); -int rose_route_frame(struct sk_buff *, ax25_cb *); -void rose_rt_free(void); - -/* rose_subr.c */ -void rose_clear_queues(struct sock *); -void rose_frames_acked(struct sock *, unsigned short); -void rose_requeue_frames(struct sock *); -int rose_validate_nr(struct sock *, unsigned short); -void rose_write_internal(struct sock *, int); -int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *); -int rose_parse_facilities(unsigned char *, unsigned int, - struct rose_facilities_struct *); -void rose_disconnect(struct sock *, int, int, int); - -/* rose_timer.c */ -void rose_start_heartbeat(struct sock *); -void rose_start_t1timer(struct sock *); -void rose_start_t2timer(struct sock *); -void rose_start_t3timer(struct sock *); -void rose_start_hbtimer(struct sock *); -void rose_start_idletimer(struct sock *); -void rose_stop_heartbeat(struct sock *); -void rose_stop_timer(struct sock *); -void rose_stop_idletimer(struct sock *); +#define _ROSE_H -/* sysctl_net_rose.c */ -void rose_register_sysctl(void); -void rose_unregister_sysctl(void); +#define ROSE_ADDR_LEN 5 #endif diff --git a/include/uapi/linux/baycom.h b/include/uapi/linux/baycom.h deleted file mode 100644 index 478cb565ae52..000000000000 --- a/include/uapi/linux/baycom.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * The Linux BAYCOM driver for the Baycom serial 1200 baud modem - * and the parallel 9600 baud modem - * (C) 1997-1998 by Thomas Sailer, HB9JNX/AE4WA - */ - -#ifndef _BAYCOM_H -#define _BAYCOM_H - -/* -------------------------------------------------------------------- */ -/* - * structs for the IOCTL commands - */ - -struct baycom_debug_data { - unsigned long debug1; - unsigned long debug2; - long debug3; -}; - -struct baycom_ioctl { - int cmd; - union { - struct baycom_debug_data dbg; - } data; -}; - -/* -------------------------------------------------------------------- */ - -/* - * ioctl values change for baycom - */ -#define BAYCOMCTL_GETDEBUG 0x92 - -/* -------------------------------------------------------------------- */ - -#endif /* _BAYCOM_H */ - -/* --------------------------------------------------------------------- */ diff --git a/include/uapi/linux/hdlcdrv.h b/include/uapi/linux/hdlcdrv.h deleted file mode 100644 index 9fe9499403a6..000000000000 --- a/include/uapi/linux/hdlcdrv.h +++ /dev/null @@ -1,111 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * hdlcdrv.h -- HDLC packet radio network driver. - * The Linux soundcard driver for 1200 baud and 9600 baud packet radio - * (C) 1996-1998 by Thomas Sailer, HB9JNX/AE4WA - */ - -#ifndef _UAPI_HDLCDRV_H -#define _UAPI_HDLCDRV_H - -/* -------------------------------------------------------------------- */ -/* - * structs for the IOCTL commands - */ - -struct hdlcdrv_params { - int iobase; - int irq; - int dma; - int dma2; - int seriobase; - int pariobase; - int midiiobase; -}; - -struct hdlcdrv_channel_params { - int tx_delay; /* the transmitter keyup delay in 10ms units */ - int tx_tail; /* the transmitter keyoff delay in 10ms units */ - int slottime; /* the slottime in 10ms; usually 10 = 100ms */ - int ppersist; /* the p-persistence 0..255 */ - int fulldup; /* some driver do not support full duplex, setting */ - /* this just makes them send even if DCD is on */ -}; - -struct hdlcdrv_old_channel_state { - int ptt; - int dcd; - int ptt_keyed; -}; - -struct hdlcdrv_channel_state { - int ptt; - int dcd; - int ptt_keyed; - unsigned long tx_packets; - unsigned long tx_errors; - unsigned long rx_packets; - unsigned long rx_errors; -}; - -struct hdlcdrv_ioctl { - int cmd; - union { - struct hdlcdrv_params mp; - struct hdlcdrv_channel_params cp; - struct hdlcdrv_channel_state cs; - struct hdlcdrv_old_channel_state ocs; - unsigned int calibrate; - unsigned char bits; - char modename[128]; - char drivername[32]; - } data; -}; - -/* -------------------------------------------------------------------- */ - -/* - * ioctl values - */ -#define HDLCDRVCTL_GETMODEMPAR 0 -#define HDLCDRVCTL_SETMODEMPAR 1 -#define HDLCDRVCTL_MODEMPARMASK 2 /* not handled by hdlcdrv */ -#define HDLCDRVCTL_GETCHANNELPAR 10 -#define HDLCDRVCTL_SETCHANNELPAR 11 -#define HDLCDRVCTL_OLDGETSTAT 20 -#define HDLCDRVCTL_CALIBRATE 21 -#define HDLCDRVCTL_GETSTAT 22 - -/* - * these are mainly for debugging purposes - */ -#define HDLCDRVCTL_GETSAMPLES 30 -#define HDLCDRVCTL_GETBITS 31 - -/* - * not handled by hdlcdrv, but by its depending drivers - */ -#define HDLCDRVCTL_GETMODE 40 -#define HDLCDRVCTL_SETMODE 41 -#define HDLCDRVCTL_MODELIST 42 -#define HDLCDRVCTL_DRIVERNAME 43 - -/* - * mask of needed modem parameters, returned by HDLCDRVCTL_MODEMPARMASK - */ -#define HDLCDRV_PARMASK_IOBASE (1<<0) -#define HDLCDRV_PARMASK_IRQ (1<<1) -#define HDLCDRV_PARMASK_DMA (1<<2) -#define HDLCDRV_PARMASK_DMA2 (1<<3) -#define HDLCDRV_PARMASK_SERIOBASE (1<<4) -#define HDLCDRV_PARMASK_PARIOBASE (1<<5) -#define HDLCDRV_PARMASK_MIDIIOBASE (1<<6) - -/* -------------------------------------------------------------------- */ - - -/* -------------------------------------------------------------------- */ - -#endif /* _UAPI_HDLCDRV_H */ - -/* -------------------------------------------------------------------- */ diff --git a/include/uapi/linux/netrom.h b/include/uapi/linux/netrom.h deleted file mode 100644 index 7498ea3c3940..000000000000 --- a/include/uapi/linux/netrom.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * These are the public elements of the Linux kernel NET/ROM implementation. - * For kernel AX.25 see the file ax25.h. This file requires ax25.h for the - * definition of the ax25_address structure. - */ - -#ifndef NETROM_KERNEL_H -#define NETROM_KERNEL_H - -#include - -#define NETROM_MTU 236 - -#define NETROM_T1 1 -#define NETROM_T2 2 -#define NETROM_N2 3 -#define NETROM_T4 6 -#define NETROM_IDLE 7 - -#define SIOCNRDECOBS (SIOCPROTOPRIVATE+2) - -struct nr_route_struct { -#define NETROM_NEIGH 0 -#define NETROM_NODE 1 - int type; - ax25_address callsign; - char device[16]; - unsigned int quality; - char mnemonic[7]; - ax25_address neighbour; - unsigned int obs_count; - unsigned int ndigis; - ax25_address digipeaters[AX25_MAX_DIGIS]; -}; - -#endif diff --git a/include/uapi/linux/rose.h b/include/uapi/linux/rose.h deleted file mode 100644 index 19aa4693c8fc..000000000000 --- a/include/uapi/linux/rose.h +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * These are the public elements of the Linux kernel Rose implementation. - * For kernel AX.25 see the file ax25.h. This file requires ax25.h for the - * definition of the ax25_address structure. - */ - -#ifndef ROSE_KERNEL_H -#define ROSE_KERNEL_H - -#include -#include - -#define ROSE_MTU 251 - -#define ROSE_MAX_DIGIS 6 - -#define ROSE_DEFER 1 -#define ROSE_T1 2 -#define ROSE_T2 3 -#define ROSE_T3 4 -#define ROSE_IDLE 5 -#define ROSE_QBITINCL 6 -#define ROSE_HOLDBACK 7 - -#define SIOCRSGCAUSE (SIOCPROTOPRIVATE+0) -#define SIOCRSSCAUSE (SIOCPROTOPRIVATE+1) -#define SIOCRSL2CALL (SIOCPROTOPRIVATE+2) -#define SIOCRSSL2CALL (SIOCPROTOPRIVATE+2) -#define SIOCRSACCEPT (SIOCPROTOPRIVATE+3) -#define SIOCRSCLRRT (SIOCPROTOPRIVATE+4) -#define SIOCRSGL2CALL (SIOCPROTOPRIVATE+5) -#define SIOCRSGFACILITIES (SIOCPROTOPRIVATE+6) - -#define ROSE_DTE_ORIGINATED 0x00 -#define ROSE_NUMBER_BUSY 0x01 -#define ROSE_INVALID_FACILITY 0x03 -#define ROSE_NETWORK_CONGESTION 0x05 -#define ROSE_OUT_OF_ORDER 0x09 -#define ROSE_ACCESS_BARRED 0x0B -#define ROSE_NOT_OBTAINABLE 0x0D -#define ROSE_REMOTE_PROCEDURE 0x11 -#define ROSE_LOCAL_PROCEDURE 0x13 -#define ROSE_SHIP_ABSENT 0x39 - -typedef struct { - char rose_addr[5]; -} rose_address; - -struct sockaddr_rose { - __kernel_sa_family_t srose_family; - rose_address srose_addr; - ax25_address srose_call; - int srose_ndigis; - ax25_address srose_digi; -}; - -struct full_sockaddr_rose { - __kernel_sa_family_t srose_family; - rose_address srose_addr; - ax25_address srose_call; - unsigned int srose_ndigis; - ax25_address srose_digis[ROSE_MAX_DIGIS]; -}; - -struct rose_route_struct { - rose_address address; - unsigned short mask; - ax25_address neighbour; - char device[16]; - unsigned char ndigis; - ax25_address digipeaters[AX25_MAX_DIGIS]; -}; - -struct rose_cause_struct { - unsigned char cause; - unsigned char diagnostic; -}; - -struct rose_facilities_struct { - rose_address source_addr, dest_addr; - ax25_address source_call, dest_call; - unsigned char source_ndigis, dest_ndigis; - ax25_address source_digis[ROSE_MAX_DIGIS]; - ax25_address dest_digis[ROSE_MAX_DIGIS]; - unsigned int rand; - rose_address fail_addr; - ax25_address fail_call; -}; - -#endif diff --git a/include/uapi/linux/scc.h b/include/uapi/linux/scc.h deleted file mode 100644 index 947edb17ce9d..000000000000 --- a/include/uapi/linux/scc.h +++ /dev/null @@ -1,174 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* $Id: scc.h,v 1.29 1997/04/02 14:56:45 jreuter Exp jreuter $ */ - -#ifndef _UAPI_SCC_H -#define _UAPI_SCC_H - -#include - -/* selection of hardware types */ - -#define PA0HZP 0x00 /* hardware type for PA0HZP SCC card and compatible */ -#define EAGLE 0x01 /* hardware type for EAGLE card */ -#define PC100 0x02 /* hardware type for PC100 card */ -#define PRIMUS 0x04 /* hardware type for PRIMUS-PC (DG9BL) card */ -#define DRSI 0x08 /* hardware type for DRSI PC*Packet card */ -#define BAYCOM 0x10 /* hardware type for BayCom (U)SCC */ - -/* DEV ioctl() commands */ - -enum SCC_ioctl_cmds { - SIOCSCCRESERVED = SIOCDEVPRIVATE, - SIOCSCCCFG, - SIOCSCCINI, - SIOCSCCCHANINI, - SIOCSCCSMEM, - SIOCSCCGKISS, - SIOCSCCSKISS, - SIOCSCCGSTAT, - SIOCSCCCAL -}; - -/* Device parameter control (from WAMPES) */ - -enum L1_params { - PARAM_DATA, - PARAM_TXDELAY, - PARAM_PERSIST, - PARAM_SLOTTIME, - PARAM_TXTAIL, - PARAM_FULLDUP, - PARAM_SOFTDCD, /* was: PARAM_HW */ - PARAM_MUTE, /* ??? */ - PARAM_DTR, - PARAM_RTS, - PARAM_SPEED, - PARAM_ENDDELAY, /* ??? */ - PARAM_GROUP, - PARAM_IDLE, - PARAM_MIN, - PARAM_MAXKEY, - PARAM_WAIT, - PARAM_MAXDEFER, - PARAM_TX, - PARAM_HWEVENT = 31, - PARAM_RETURN = 255 /* reset kiss mode */ -}; - -/* fulldup parameter */ - -enum FULLDUP_modes { - KISS_DUPLEX_HALF, /* normal CSMA operation */ - KISS_DUPLEX_FULL, /* fullduplex, key down trx after transmission */ - KISS_DUPLEX_LINK, /* fullduplex, key down trx after 'idletime' sec */ - KISS_DUPLEX_OPTIMA /* fullduplex, let the protocol layer control the hw */ -}; - -/* misc. parameters */ - -#define TIMER_OFF 65535U /* to switch off timers */ -#define NO_SUCH_PARAM 65534U /* param not implemented */ - -/* HWEVENT parameter */ - -enum HWEVENT_opts { - HWEV_DCD_ON, - HWEV_DCD_OFF, - HWEV_ALL_SENT -}; - -/* channel grouping */ - -#define RXGROUP 0100 /* if set, only tx when all channels clear */ -#define TXGROUP 0200 /* if set, don't transmit simultaneously */ - -/* Tx/Rx clock sources */ - -enum CLOCK_sources { - CLK_DPLL, /* normal halfduplex operation */ - CLK_EXTERNAL, /* external clocking (G3RUH/DF9IC modems) */ - CLK_DIVIDER, /* Rx = DPLL, Tx = divider (fullduplex with */ - /* modems without clock regeneration */ - CLK_BRG /* experimental fullduplex mode with DPLL/BRG for */ - /* MODEMs without clock recovery */ -}; - -/* Tx state */ - -enum TX_state { - TXS_IDLE, /* Transmitter off, no data pending */ - TXS_BUSY, /* waiting for permission to send / tailtime */ - TXS_ACTIVE, /* Transmitter on, sending data */ - TXS_NEWFRAME, /* reset CRC and send (next) frame */ - TXS_IDLE2, /* Transmitter on, no data pending */ - TXS_WAIT, /* Waiting for Mintime to expire */ - TXS_TIMEOUT /* We had a transmission timeout */ -}; - -typedef unsigned long io_port; /* type definition for an 'io port address' */ - -/* SCC statistical information */ - -struct scc_stat { - long rxints; /* Receiver interrupts */ - long txints; /* Transmitter interrupts */ - long exints; /* External/status interrupts */ - long spints; /* Special receiver interrupts */ - - long txframes; /* Packets sent */ - long rxframes; /* Number of Frames Actually Received */ - long rxerrs; /* CRC Errors */ - long txerrs; /* KISS errors */ - - unsigned int nospace; /* "Out of buffers" */ - unsigned int rx_over; /* Receiver Overruns */ - unsigned int tx_under; /* Transmitter Underruns */ - - unsigned int tx_state; /* Transmitter state */ - int tx_queued; /* tx frames enqueued */ - - unsigned int maxqueue; /* allocated tx_buffers */ - unsigned int bufsize; /* used buffersize */ -}; - -struct scc_modem { - long speed; /* Line speed, bps */ - char clocksrc; /* 0 = DPLL, 1 = external, 2 = divider */ - char nrz; /* NRZ instead of NRZI */ -}; - -struct scc_kiss_cmd { - int command; /* one of the KISS-Commands defined above */ - unsigned param; /* KISS-Param */ -}; - -struct scc_hw_config { - io_port data_a; /* data port channel A */ - io_port ctrl_a; /* control port channel A */ - io_port data_b; /* data port channel B */ - io_port ctrl_b; /* control port channel B */ - io_port vector_latch; /* INTACK-Latch (#) */ - io_port special; /* special function port */ - - int irq; /* irq */ - long clock; /* clock */ - char option; /* command for function port */ - - char brand; /* hardware type */ - char escc; /* use ext. features of a 8580/85180/85280 */ -}; - -/* (#) only one INTACK latch allowed. */ - - -struct scc_mem_config { - unsigned int dummy; - unsigned int bufsize; -}; - -struct scc_calibrate { - unsigned int time; - unsigned char pattern; -}; - -#endif /* _UAPI_SCC_H */ diff --git a/net/Kconfig b/net/Kconfig index 5c588dbcbdbd..bdea8aef7983 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -414,7 +414,6 @@ endmenu # Network testing endmenu # Networking options -source "net/ax25/Kconfig" source "net/can/Kconfig" source "net/bluetooth/Kconfig" source "net/rxrpc/Kconfig" diff --git a/net/Makefile b/net/Makefile index 98e182829eff..d2175fce0406 100644 --- a/net/Makefile +++ b/net/Makefile @@ -28,9 +28,6 @@ obj-y += dsa/ obj-$(CONFIG_ATALK) += appletalk/ obj-$(CONFIG_X25) += x25/ obj-$(CONFIG_LAPB) += lapb/ -obj-$(CONFIG_NETROM) += netrom/ -obj-$(CONFIG_ROSE) += rose/ -obj-$(CONFIG_AX25) += ax25/ obj-$(CONFIG_CAN) += can/ obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_SUNRPC) += sunrpc/ diff --git a/net/ax25/Kconfig b/net/ax25/Kconfig deleted file mode 100644 index 310169ce1488..000000000000 --- a/net/ax25/Kconfig +++ /dev/null @@ -1,108 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Amateur Radio protocols and AX.25 device configuration -# - -menuconfig HAMRADIO - depends on NET - bool "Amateur Radio support" - help - If you want to connect your Linux box to an amateur radio, answer Y - here. You want to read - and more specifically about AX.25 on Linux - . - - Note that the answer to this question won't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about amateur radio. - -comment "Packet Radio protocols" - depends on HAMRADIO - -config AX25 - tristate "Amateur Radio AX.25 Level 2 protocol" - depends on HAMRADIO - help - This is the protocol used for computer communication over amateur - radio. It is either used by itself for point-to-point links, or to - carry other protocols such as tcp/ip. To use it, you need a device - that connects your Linux box to your amateur radio. You can either - use a low speed TNC (a Terminal Node Controller acts as a kind of - modem connecting your computer's serial port to your radio's - microphone input and speaker output) supporting the KISS protocol or - one of the various SCC cards that are supported by the generic Z8530 - or the DMA SCC driver. Another option are the Baycom modem serial - and parallel port hacks or the sound card modem (supported by their - own drivers). If you say Y here, you also have to say Y to one of - those drivers. - - Information about where to get supporting software for Linux amateur - radio as well as information about how to configure an AX.25 port is - contained in the AX25-HOWTO, available from - . You might also want to - check out the file in the - kernel source. More information about digital amateur radio in - general is on the WWW at - . - - To compile this driver as a module, choose M here: the - module will be called ax25. - -config AX25_DAMA_SLAVE - bool "AX.25 DAMA Slave support" - default y - depends on AX25 - help - DAMA is a mechanism to prevent collisions when doing AX.25 - networking. A DAMA server (called "master") accepts incoming traffic - from clients (called "slaves") and redistributes it to other slaves. - If you say Y here, your Linux box will act as a DAMA slave; this is - transparent in that you don't have to do any special DAMA - configuration. Linux cannot yet act as a DAMA server. This option - only compiles DAMA slave support into the kernel. It still needs to - be enabled at runtime. For more about DAMA see - . If unsure, say Y. - -config NETROM - tristate "Amateur Radio NET/ROM protocol" - depends on AX25 - help - NET/ROM is a network layer protocol on top of AX.25 useful for - routing. - - A comprehensive listing of all the software for Linux amateur radio - users as well as information about how to configure an AX.25 port is - contained in the Linux Ham Wiki, available from - . You also might want to check out - the file . More information - about digital amateur radio in general is on the WWW at - . - - To compile this driver as a module, choose M here: the - module will be called netrom. - -config ROSE - tristate "Amateur Radio X.25 PLP (Rose)" - depends on AX25 - help - The Packet Layer Protocol (PLP) is a way to route packets over X.25 - connections in general and amateur radio AX.25 connections in - particular, essentially an alternative to NET/ROM. - - A comprehensive listing of all the software for Linux amateur radio - users as well as information about how to configure an AX.25 port is - contained in the Linux Ham Wiki, available from - . You also might want to check out - the file . More information - about digital amateur radio in general is on the WWW at - . - - To compile this driver as a module, choose M here: the - module will be called rose. - -menu "AX.25 network device drivers" - depends on HAMRADIO && AX25 - -source "drivers/net/hamradio/Kconfig" - -endmenu diff --git a/net/ax25/Makefile b/net/ax25/Makefile deleted file mode 100644 index 2e53affc8568..000000000000 --- a/net/ax25/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the Linux AX.25 layer. -# - -obj-$(CONFIG_AX25) += ax25.o - -ax25-y := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \ - ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \ - ax25_subr.o ax25_timer.o ax25_uid.o af_ax25.o -ax25-$(CONFIG_AX25_DAMA_SLAVE) += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o -ax25-$(CONFIG_SYSCTL) += sysctl_net_ax25.o diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c deleted file mode 100644 index 9d236e64f5f5..000000000000 --- a/net/ax25/af_ax25.c +++ /dev/null @@ -1,2089 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Darryl Miles G7LED (dlm@g7led.demon.co.uk) - * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) - * Copyright (C) Hans Alblas PE1AYX (hans@esrac.ele.tue.nl) - * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For TIOCINQ/OUTQ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -HLIST_HEAD(ax25_list); -DEFINE_SPINLOCK(ax25_list_lock); - -static const struct proto_ops ax25_proto_ops; - -static void ax25_free_sock(struct sock *sk) -{ - ax25_cb_put(sk_to_ax25(sk)); -} - -/* - * Socket removal during an interrupt is now safe. - */ -static void ax25_cb_del(ax25_cb *ax25) -{ - spin_lock_bh(&ax25_list_lock); - if (!hlist_unhashed(&ax25->ax25_node)) { - hlist_del_init(&ax25->ax25_node); - ax25_cb_put(ax25); - } - spin_unlock_bh(&ax25_list_lock); -} - -/* - * Kill all bound sockets on a dropped device. - */ -static void ax25_kill_by_device(struct net_device *dev) -{ - ax25_dev *ax25_dev; - ax25_cb *s; - struct sock *sk; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return; - ax25_dev->device_up = false; - - spin_lock_bh(&ax25_list_lock); -again: - ax25_for_each(s, &ax25_list) { - if (s->ax25_dev == ax25_dev) { - sk = s->sk; - if (!sk) { - spin_unlock_bh(&ax25_list_lock); - ax25_disconnect(s, ENETUNREACH); - s->ax25_dev = NULL; - ax25_cb_del(s); - spin_lock_bh(&ax25_list_lock); - goto again; - } - sock_hold(sk); - spin_unlock_bh(&ax25_list_lock); - lock_sock(sk); - ax25_disconnect(s, ENETUNREACH); - s->ax25_dev = NULL; - if (sk->sk_socket) { - netdev_put(ax25_dev->dev, - &s->dev_tracker); - ax25_dev_put(ax25_dev); - } - ax25_cb_del(s); - release_sock(sk); - spin_lock_bh(&ax25_list_lock); - sock_put(sk); - /* The entry could have been deleted from the - * list meanwhile and thus the next pointer is - * no longer valid. Play it safe and restart - * the scan. Forward progress is ensured - * because we set s->ax25_dev to NULL and we - * are never passed a NULL 'dev' argument. - */ - goto again; - } - } - spin_unlock_bh(&ax25_list_lock); -} - -/* - * Handle device status changes. - */ -static int ax25_device_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - /* Reject non AX.25 devices */ - if (dev->type != ARPHRD_AX25) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_UP: - ax25_dev_device_up(dev); - break; - case NETDEV_DOWN: - ax25_kill_by_device(dev); - ax25_rt_device_down(dev); - ax25_dev_device_down(dev); - break; - default: - break; - } - - return NOTIFY_DONE; -} - -/* - * Add a socket to the bound sockets list. - */ -void ax25_cb_add(ax25_cb *ax25) -{ - spin_lock_bh(&ax25_list_lock); - ax25_cb_hold(ax25); - hlist_add_head(&ax25->ax25_node, &ax25_list); - spin_unlock_bh(&ax25_list_lock); -} - -/* - * Find a socket that wants to accept the SABM we have just - * received. - */ -struct sock *ax25_find_listener(ax25_address *addr, int digi, - struct net_device *dev, int type) -{ - ax25_cb *s; - - spin_lock(&ax25_list_lock); - ax25_for_each(s, &ax25_list) { - if ((s->iamdigi && !digi) || (!s->iamdigi && digi)) - continue; - if (s->sk && !ax25cmp(&s->source_addr, addr) && - s->sk->sk_type == type && s->sk->sk_state == TCP_LISTEN) { - /* If device is null we match any device */ - if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) { - sock_hold(s->sk); - spin_unlock(&ax25_list_lock); - return s->sk; - } - } - } - spin_unlock(&ax25_list_lock); - - return NULL; -} - -/* - * Find an AX.25 socket given both ends. - */ -struct sock *ax25_get_socket(ax25_address *my_addr, ax25_address *dest_addr, - int type) -{ - struct sock *sk = NULL; - ax25_cb *s; - - spin_lock(&ax25_list_lock); - ax25_for_each(s, &ax25_list) { - if (s->sk && !ax25cmp(&s->source_addr, my_addr) && - !ax25cmp(&s->dest_addr, dest_addr) && - s->sk->sk_type == type) { - sk = s->sk; - sock_hold(sk); - break; - } - } - - spin_unlock(&ax25_list_lock); - - return sk; -} - -/* - * Find an AX.25 control block given both ends. It will only pick up - * floating AX.25 control blocks or non Raw socket bound control blocks. - */ -ax25_cb *ax25_find_cb(const ax25_address *src_addr, ax25_address *dest_addr, - ax25_digi *digi, struct net_device *dev) -{ - ax25_cb *s; - - spin_lock_bh(&ax25_list_lock); - ax25_for_each(s, &ax25_list) { - if (s->sk && s->sk->sk_type != SOCK_SEQPACKET) - continue; - if (s->ax25_dev == NULL) - continue; - if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) { - if (digi != NULL && digi->ndigi != 0) { - if (s->digipeat == NULL) - continue; - if (ax25digicmp(s->digipeat, digi) != 0) - continue; - } else { - if (s->digipeat != NULL && s->digipeat->ndigi != 0) - continue; - } - ax25_cb_hold(s); - spin_unlock_bh(&ax25_list_lock); - - return s; - } - } - spin_unlock_bh(&ax25_list_lock); - - return NULL; -} - -EXPORT_SYMBOL(ax25_find_cb); - -void ax25_send_to_raw(ax25_address *addr, struct sk_buff *skb, int proto) -{ - ax25_cb *s; - struct sk_buff *copy; - - spin_lock(&ax25_list_lock); - ax25_for_each(s, &ax25_list) { - if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && - s->sk->sk_type == SOCK_RAW && - s->sk->sk_protocol == proto && - s->ax25_dev->dev == skb->dev && - atomic_read(&s->sk->sk_rmem_alloc) <= s->sk->sk_rcvbuf) { - if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL) - continue; - if (sock_queue_rcv_skb(s->sk, copy) != 0) - kfree_skb(copy); - } - } - spin_unlock(&ax25_list_lock); -} - -/* - * Deferred destroy. - */ -void ax25_destroy_socket(ax25_cb *); - -/* - * Handler for deferred kills. - */ -static void ax25_destroy_timer(struct timer_list *t) -{ - ax25_cb *ax25 = timer_container_of(ax25, t, dtimer); - struct sock *sk; - - sk=ax25->sk; - - bh_lock_sock(sk); - sock_hold(sk); - ax25_destroy_socket(ax25); - bh_unlock_sock(sk); - sock_put(sk); -} - -/* - * This is called from user mode and the timers. Thus it protects itself - * against interrupt users but doesn't worry about being called during - * work. Once it is removed from the queue no interrupt or bottom half - * will touch it and we are (fairly 8-) ) safe. - */ -void ax25_destroy_socket(ax25_cb *ax25) -{ - struct sk_buff *skb; - - ax25_cb_del(ax25); - - ax25_stop_heartbeat(ax25); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); - - ax25_clear_queues(ax25); /* Flush the queues */ - - if (ax25->sk != NULL) { - while ((skb = skb_dequeue(&ax25->sk->sk_receive_queue)) != NULL) { - if (skb->sk != ax25->sk) { - /* A pending connection */ - ax25_cb *sax25 = sk_to_ax25(skb->sk); - - /* Queue the unaccepted socket for death */ - sock_orphan(skb->sk); - - /* 9A4GL: hack to release unaccepted sockets */ - skb->sk->sk_state = TCP_LISTEN; - - ax25_start_heartbeat(sax25); - sax25->state = AX25_STATE_0; - } - - kfree_skb(skb); - } - skb_queue_purge(&ax25->sk->sk_write_queue); - } - - if (ax25->sk != NULL) { - if (sk_has_allocations(ax25->sk)) { - /* Defer: outstanding buffers */ - timer_setup(&ax25->dtimer, ax25_destroy_timer, 0); - ax25->dtimer.expires = jiffies + 2 * HZ; - add_timer(&ax25->dtimer); - } else { - struct sock *sk=ax25->sk; - ax25->sk=NULL; - sock_put(sk); - } - } else { - ax25_cb_put(ax25); - } -} - -/* - * dl1bke 960311: set parameters for existing AX.25 connections, - * includes a KILL command to abort any connection. - * VERY useful for debugging ;-) - */ -static int ax25_ctl_ioctl(const unsigned int cmd, void __user *arg) -{ - struct ax25_ctl_struct ax25_ctl; - ax25_digi digi; - ax25_dev *ax25_dev; - ax25_cb *ax25; - unsigned int k; - int ret = 0; - - if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl))) - return -EFAULT; - - if (ax25_ctl.digi_count > AX25_MAX_DIGIS) - return -EINVAL; - - if (ax25_ctl.arg > ULONG_MAX / HZ && ax25_ctl.cmd != AX25_KILL) - return -EINVAL; - - ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr); - if (!ax25_dev) - return -ENODEV; - - digi.ndigi = ax25_ctl.digi_count; - for (k = 0; k < digi.ndigi; k++) - digi.calls[k] = ax25_ctl.digi_addr[k]; - - ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, &digi, ax25_dev->dev); - if (!ax25) { - ax25_dev_put(ax25_dev); - return -ENOTCONN; - } - - switch (ax25_ctl.cmd) { - case AX25_KILL: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); -#ifdef CONFIG_AX25_DAMA_SLAVE - if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) - ax25_dama_off(ax25); -#endif - ax25_disconnect(ax25, ENETRESET); - break; - - case AX25_WINDOW: - if (ax25->modulus == AX25_MODULUS) { - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7) - goto einval_put; - } else { - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63) - goto einval_put; - } - ax25->window = ax25_ctl.arg; - break; - - case AX25_T1: - if (ax25_ctl.arg < 1 || ax25_ctl.arg > ULONG_MAX / HZ) - goto einval_put; - ax25->rtt = (ax25_ctl.arg * HZ) / 2; - ax25->t1 = ax25_ctl.arg * HZ; - break; - - case AX25_T2: - if (ax25_ctl.arg < 1 || ax25_ctl.arg > ULONG_MAX / HZ) - goto einval_put; - ax25->t2 = ax25_ctl.arg * HZ; - break; - - case AX25_N2: - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31) - goto einval_put; - ax25->n2count = 0; - ax25->n2 = ax25_ctl.arg; - break; - - case AX25_T3: - if (ax25_ctl.arg > ULONG_MAX / HZ) - goto einval_put; - ax25->t3 = ax25_ctl.arg * HZ; - break; - - case AX25_IDLE: - if (ax25_ctl.arg > ULONG_MAX / (60 * HZ)) - goto einval_put; - - ax25->idle = ax25_ctl.arg * 60 * HZ; - break; - - case AX25_PACLEN: - if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535) - goto einval_put; - ax25->paclen = ax25_ctl.arg; - break; - - default: - goto einval_put; - } - -out_put: - ax25_dev_put(ax25_dev); - ax25_cb_put(ax25); - return ret; - -einval_put: - ret = -EINVAL; - goto out_put; -} - -static void ax25_fillin_cb_from_dev(ax25_cb *ax25, const ax25_dev *ax25_dev) -{ - ax25->rtt = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]) / 2; - ax25->t1 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]); - ax25->t2 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T2]); - ax25->t3 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T3]); - ax25->n2 = ax25_dev->values[AX25_VALUES_N2]; - ax25->paclen = ax25_dev->values[AX25_VALUES_PACLEN]; - ax25->idle = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_IDLE]); - ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF]; - - if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; - } -} - -/* - * Fill in a created AX.25 created control block with the default - * values for a particular device. - */ -void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev) -{ - ax25->ax25_dev = ax25_dev; - - if (ax25->ax25_dev != NULL) { - ax25_fillin_cb_from_dev(ax25, ax25_dev); - return; - } - - /* - * No device, use kernel / AX.25 spec default values - */ - ax25->rtt = msecs_to_jiffies(AX25_DEF_T1) / 2; - ax25->t1 = msecs_to_jiffies(AX25_DEF_T1); - ax25->t2 = msecs_to_jiffies(AX25_DEF_T2); - ax25->t3 = msecs_to_jiffies(AX25_DEF_T3); - ax25->n2 = AX25_DEF_N2; - ax25->paclen = AX25_DEF_PACLEN; - ax25->idle = msecs_to_jiffies(AX25_DEF_IDLE); - ax25->backoff = AX25_DEF_BACKOFF; - - if (AX25_DEF_AXDEFMODE) { - ax25->modulus = AX25_EMODULUS; - ax25->window = AX25_DEF_EWINDOW; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = AX25_DEF_WINDOW; - } -} - -/* - * Create an empty AX.25 control block. - */ -ax25_cb *ax25_create_cb(void) -{ - ax25_cb *ax25; - - if ((ax25 = kzalloc_obj(*ax25, GFP_ATOMIC)) == NULL) - return NULL; - - refcount_set(&ax25->refcount, 1); - - skb_queue_head_init(&ax25->write_queue); - skb_queue_head_init(&ax25->frag_queue); - skb_queue_head_init(&ax25->ack_queue); - skb_queue_head_init(&ax25->reseq_queue); - - ax25_setup_timers(ax25); - - ax25_fillin_cb(ax25, NULL); - - ax25->state = AX25_STATE_0; - - return ax25; -} - -/* - * Handling for system calls applied via the various interfaces to an - * AX25 socket object - */ - -static int ax25_setsockopt(struct socket *sock, int level, int optname, - sockptr_t optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - ax25_cb *ax25; - struct net_device *dev; - char devname[IFNAMSIZ]; - unsigned int opt; - int res = 0; - - if (level != SOL_AX25) - return -ENOPROTOOPT; - - if (optlen < sizeof(unsigned int)) - return -EINVAL; - - if (copy_from_sockptr(&opt, optval, sizeof(unsigned int))) - return -EFAULT; - - lock_sock(sk); - ax25 = sk_to_ax25(sk); - - switch (optname) { - case AX25_WINDOW: - if (ax25->modulus == AX25_MODULUS) { - if (opt < 1 || opt > 7) { - res = -EINVAL; - break; - } - } else { - if (opt < 1 || opt > 63) { - res = -EINVAL; - break; - } - } - ax25->window = opt; - break; - - case AX25_T1: - if (opt < 1 || opt > UINT_MAX / HZ) { - res = -EINVAL; - break; - } - ax25->rtt = (opt * HZ) >> 1; - ax25->t1 = opt * HZ; - break; - - case AX25_T2: - if (opt < 1 || opt > UINT_MAX / HZ) { - res = -EINVAL; - break; - } - ax25->t2 = opt * HZ; - break; - - case AX25_N2: - if (opt < 1 || opt > 31) { - res = -EINVAL; - break; - } - ax25->n2 = opt; - break; - - case AX25_T3: - if (opt < 1 || opt > UINT_MAX / HZ) { - res = -EINVAL; - break; - } - ax25->t3 = opt * HZ; - break; - - case AX25_IDLE: - if (opt > UINT_MAX / (60 * HZ)) { - res = -EINVAL; - break; - } - ax25->idle = opt * 60 * HZ; - break; - - case AX25_BACKOFF: - if (opt > 2) { - res = -EINVAL; - break; - } - ax25->backoff = opt; - break; - - case AX25_EXTSEQ: - ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS; - break; - - case AX25_PIDINCL: - ax25->pidincl = opt ? 1 : 0; - break; - - case AX25_IAMDIGI: - ax25->iamdigi = opt ? 1 : 0; - break; - - case AX25_PACLEN: - if (opt < 16 || opt > 65535) { - res = -EINVAL; - break; - } - ax25->paclen = opt; - break; - - case SO_BINDTODEVICE: - if (optlen > IFNAMSIZ - 1) - optlen = IFNAMSIZ - 1; - - memset(devname, 0, sizeof(devname)); - - if (copy_from_sockptr(devname, optval, optlen)) { - res = -EFAULT; - break; - } - - if (sk->sk_type == SOCK_SEQPACKET && - (sock->state != SS_UNCONNECTED || - sk->sk_state == TCP_LISTEN)) { - res = -EADDRNOTAVAIL; - break; - } - - rcu_read_lock(); - dev = dev_get_by_name_rcu(&init_net, devname); - if (!dev) { - rcu_read_unlock(); - res = -ENODEV; - break; - } - - if (ax25->ax25_dev) { - if (dev == ax25->ax25_dev->dev) { - rcu_read_unlock(); - break; - } - netdev_put(ax25->ax25_dev->dev, &ax25->dev_tracker); - ax25_dev_put(ax25->ax25_dev); - } - - ax25->ax25_dev = ax25_dev_ax25dev(dev); - if (!ax25->ax25_dev) { - rcu_read_unlock(); - res = -ENODEV; - break; - } - ax25_fillin_cb(ax25, ax25->ax25_dev); - netdev_hold(dev, &ax25->dev_tracker, GFP_ATOMIC); - ax25_dev_hold(ax25->ax25_dev); - rcu_read_unlock(); - break; - - default: - res = -ENOPROTOOPT; - } - release_sock(sk); - - return res; -} - -static int ax25_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - ax25_cb *ax25; - struct ax25_dev *ax25_dev; - char devname[IFNAMSIZ]; - void *valptr; - int val = 0; - int maxlen, length; - - if (level != SOL_AX25) - return -ENOPROTOOPT; - - if (get_user(maxlen, optlen)) - return -EFAULT; - - if (maxlen < 1) - return -EFAULT; - - valptr = &val; - length = min_t(unsigned int, maxlen, sizeof(int)); - - lock_sock(sk); - ax25 = sk_to_ax25(sk); - - switch (optname) { - case AX25_WINDOW: - val = ax25->window; - break; - - case AX25_T1: - val = ax25->t1 / HZ; - break; - - case AX25_T2: - val = ax25->t2 / HZ; - break; - - case AX25_N2: - val = ax25->n2; - break; - - case AX25_T3: - val = ax25->t3 / HZ; - break; - - case AX25_IDLE: - val = ax25->idle / (60 * HZ); - break; - - case AX25_BACKOFF: - val = ax25->backoff; - break; - - case AX25_EXTSEQ: - val = (ax25->modulus == AX25_EMODULUS); - break; - - case AX25_PIDINCL: - val = ax25->pidincl; - break; - - case AX25_IAMDIGI: - val = ax25->iamdigi; - break; - - case AX25_PACLEN: - val = ax25->paclen; - break; - - case SO_BINDTODEVICE: - ax25_dev = ax25->ax25_dev; - - if (ax25_dev != NULL && ax25_dev->dev != NULL) { - strscpy(devname, ax25_dev->dev->name, sizeof(devname)); - length = strlen(devname) + 1; - } else { - *devname = '\0'; - length = 1; - } - - valptr = devname; - break; - - default: - release_sock(sk); - return -ENOPROTOOPT; - } - release_sock(sk); - - if (put_user(length, optlen)) - return -EFAULT; - - return copy_to_user(optval, valptr, length) ? -EFAULT : 0; -} - -static int ax25_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - int res = 0; - - lock_sock(sk); - if (sk->sk_type == SOCK_SEQPACKET && sk->sk_state != TCP_LISTEN) { - sk->sk_max_ack_backlog = backlog; - sk->sk_state = TCP_LISTEN; - goto out; - } - res = -EOPNOTSUPP; - -out: - release_sock(sk); - - return res; -} - -/* - * XXX: when creating ax25_sock we should update the .obj_size setting - * below. - */ -static struct proto ax25_proto = { - .name = "AX25", - .owner = THIS_MODULE, - .obj_size = sizeof(struct ax25_sock), -}; - -static int ax25_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - ax25_cb *ax25; - - if (protocol < 0 || protocol > U8_MAX) - return -EINVAL; - - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - - switch (sock->type) { - case SOCK_DGRAM: - if (protocol == 0 || protocol == PF_AX25) - protocol = AX25_P_TEXT; - break; - - case SOCK_SEQPACKET: - switch (protocol) { - case 0: - case PF_AX25: /* For CLX */ - protocol = AX25_P_TEXT; - break; - case AX25_P_SEGMENT: -#ifdef CONFIG_INET - case AX25_P_ARP: - case AX25_P_IP: -#endif -#ifdef CONFIG_NETROM - case AX25_P_NETROM: -#endif -#ifdef CONFIG_ROSE - case AX25_P_ROSE: -#endif - return -ESOCKTNOSUPPORT; -#ifdef CONFIG_NETROM_MODULE - case AX25_P_NETROM: - if (ax25_protocol_is_registered(AX25_P_NETROM)) - return -ESOCKTNOSUPPORT; - break; -#endif -#ifdef CONFIG_ROSE_MODULE - case AX25_P_ROSE: - if (ax25_protocol_is_registered(AX25_P_ROSE)) - return -ESOCKTNOSUPPORT; - break; -#endif - default: - break; - } - break; - - case SOCK_RAW: - if (!capable(CAP_NET_RAW)) - return -EPERM; - break; - default: - return -ESOCKTNOSUPPORT; - } - - sk = sk_alloc(net, PF_AX25, GFP_ATOMIC, &ax25_proto, kern); - if (sk == NULL) - return -ENOMEM; - - ax25 = ax25_sk(sk)->cb = ax25_create_cb(); - if (!ax25) { - sk_free(sk); - return -ENOMEM; - } - - sock_init_data(sock, sk); - - sk->sk_destruct = ax25_free_sock; - sock->ops = &ax25_proto_ops; - sk->sk_protocol = protocol; - - ax25->sk = sk; - - return 0; -} - -struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev) -{ - struct sock *sk; - ax25_cb *ax25, *oax25; - - sk = sk_alloc(sock_net(osk), PF_AX25, GFP_ATOMIC, osk->sk_prot, 0); - if (sk == NULL) - return NULL; - - if ((ax25 = ax25_create_cb()) == NULL) { - sk_free(sk); - return NULL; - } - - switch (osk->sk_type) { - case SOCK_DGRAM: - break; - case SOCK_SEQPACKET: - break; - default: - sk_free(sk); - ax25_cb_put(ax25); - return NULL; - } - - sock_init_data(NULL, sk); - - sk->sk_type = osk->sk_type; - sk->sk_priority = READ_ONCE(osk->sk_priority); - sk->sk_protocol = osk->sk_protocol; - sk->sk_rcvbuf = osk->sk_rcvbuf; - sk->sk_sndbuf = osk->sk_sndbuf; - sk->sk_state = TCP_ESTABLISHED; - sock_copy_flags(sk, osk); - - oax25 = sk_to_ax25(osk); - - ax25->modulus = oax25->modulus; - ax25->backoff = oax25->backoff; - ax25->pidincl = oax25->pidincl; - ax25->iamdigi = oax25->iamdigi; - ax25->rtt = oax25->rtt; - ax25->t1 = oax25->t1; - ax25->t2 = oax25->t2; - ax25->t3 = oax25->t3; - ax25->n2 = oax25->n2; - ax25->idle = oax25->idle; - ax25->paclen = oax25->paclen; - ax25->window = oax25->window; - - ax25->ax25_dev = ax25_dev; - ax25->source_addr = oax25->source_addr; - - if (oax25->digipeat != NULL) { - ax25->digipeat = kmemdup(oax25->digipeat, sizeof(ax25_digi), - GFP_ATOMIC); - if (ax25->digipeat == NULL) { - sk_free(sk); - ax25_cb_put(ax25); - return NULL; - } - } - - ax25_sk(sk)->cb = ax25; - sk->sk_destruct = ax25_free_sock; - ax25->sk = sk; - - return sk; -} - -static int ax25_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - ax25_cb *ax25; - ax25_dev *ax25_dev; - - if (sk == NULL) - return 0; - - sock_hold(sk); - lock_sock(sk); - sock_orphan(sk); - ax25 = sk_to_ax25(sk); - ax25_dev = ax25->ax25_dev; - - if (sk->sk_type == SOCK_SEQPACKET) { - switch (ax25->state) { - case AX25_STATE_0: - if (!sock_flag(ax25->sk, SOCK_DEAD)) { - release_sock(sk); - ax25_disconnect(ax25, 0); - lock_sock(sk); - } - ax25_destroy_socket(ax25); - break; - - case AX25_STATE_1: - case AX25_STATE_2: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - release_sock(sk); - ax25_disconnect(ax25, 0); - lock_sock(sk); - if (!sock_flag(ax25->sk, SOCK_DESTROY)) - ax25_destroy_socket(ax25); - break; - - case AX25_STATE_3: - case AX25_STATE_4: - ax25_clear_queues(ax25); - ax25->n2count = 0; - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_send_control(ax25, - AX25_DISC, - AX25_POLLON, - AX25_COMMAND); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); - break; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); - break; -#endif - } - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25->state = AX25_STATE_2; - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DESTROY); - break; - - default: - break; - } - } else { - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - ax25_destroy_socket(ax25); - } - if (ax25_dev) { - if (!ax25_dev->device_up) { - timer_delete_sync(&ax25->timer); - timer_delete_sync(&ax25->t1timer); - timer_delete_sync(&ax25->t2timer); - timer_delete_sync(&ax25->t3timer); - timer_delete_sync(&ax25->idletimer); - } - netdev_put(ax25_dev->dev, &ax25->dev_tracker); - ax25_dev_put(ax25_dev); - } - - sock->sk = NULL; - release_sock(sk); - sock_put(sk); - - return 0; -} - -/* - * We support a funny extension here so you can (as root) give any callsign - * digipeated via a local address as source. This hack is obsolete now - * that we've implemented support for SO_BINDTODEVICE. It is however small - * and trivially backward compatible. - */ -static int ax25_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len) -{ - struct sock *sk = sock->sk; - struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; - ax25_dev *ax25_dev = NULL; - ax25_uid_assoc *user; - ax25_address call; - ax25_cb *ax25; - int err = 0; - - if (addr_len != sizeof(struct sockaddr_ax25) && - addr_len != sizeof(struct full_sockaddr_ax25)) - /* support for old structure may go away some time - * ax25_bind(): uses old (6 digipeater) socket structure. - */ - if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || - (addr_len > sizeof(struct full_sockaddr_ax25))) - return -EINVAL; - - if (addr->fsa_ax25.sax25_family != AF_AX25) - return -EINVAL; - - user = ax25_findbyuid(current_euid()); - if (user) { - call = user->call; - ax25_uid_put(user); - } else { - if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) - return -EACCES; - - call = addr->fsa_ax25.sax25_call; - } - - lock_sock(sk); - - ax25 = sk_to_ax25(sk); - if (!sock_flag(sk, SOCK_ZAPPED)) { - err = -EINVAL; - goto out; - } - - ax25->source_addr = call; - - /* - * User already set interface with SO_BINDTODEVICE - */ - if (ax25->ax25_dev != NULL) - goto done; - - if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) { - if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 && - (ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) { - err = -EADDRNOTAVAIL; - goto out; - } - } else { - if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) { - err = -EADDRNOTAVAIL; - goto out; - } - } - - if (ax25_dev) { - ax25_fillin_cb(ax25, ax25_dev); - netdev_hold(ax25_dev->dev, &ax25->dev_tracker, GFP_ATOMIC); - } - -done: - ax25_cb_add(ax25); - sock_reset_flag(sk, SOCK_ZAPPED); - -out: - release_sock(sk); - - return err; -} - -/* - * FIXME: nonblock behaviour looks like it may have a bug. - */ -static int __must_check ax25_connect(struct socket *sock, - struct sockaddr_unsized *uaddr, int addr_len, int flags) -{ - struct sock *sk = sock->sk; - ax25_cb *ax25 = sk_to_ax25(sk), *ax25t; - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; - ax25_digi *digi = NULL; - int ct = 0, err = 0; - - /* - * some sanity checks. code further down depends on this - */ - - if (addr_len == sizeof(struct sockaddr_ax25)) - /* support for this will go away in early 2.5.x - * ax25_connect(): uses obsolete socket structure - */ - ; - else if (addr_len != sizeof(struct full_sockaddr_ax25)) - /* support for old structure may go away some time - * ax25_connect(): uses old (6 digipeater) socket structure. - */ - if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || - (addr_len > sizeof(struct full_sockaddr_ax25))) - return -EINVAL; - - - if (fsa->fsa_ax25.sax25_family != AF_AX25) - return -EINVAL; - - lock_sock(sk); - - /* deal with restarts */ - if (sock->state == SS_CONNECTING) { - switch (sk->sk_state) { - case TCP_SYN_SENT: /* still trying */ - err = -EINPROGRESS; - goto out_release; - - case TCP_ESTABLISHED: /* connection established */ - sock->state = SS_CONNECTED; - goto out_release; - - case TCP_CLOSE: /* connection refused */ - sock->state = SS_UNCONNECTED; - err = -ECONNREFUSED; - goto out_release; - } - } - - if (sk->sk_state == TCP_ESTABLISHED && sk->sk_type == SOCK_SEQPACKET) { - err = -EISCONN; /* No reconnect on a seqpacket socket */ - goto out_release; - } - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - kfree(ax25->digipeat); - ax25->digipeat = NULL; - - /* - * Handle digi-peaters to be used. - */ - if (addr_len > sizeof(struct sockaddr_ax25) && - fsa->fsa_ax25.sax25_ndigis != 0) { - /* Valid number of digipeaters ? */ - if (fsa->fsa_ax25.sax25_ndigis < 1 || - fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS || - addr_len < sizeof(struct sockaddr_ax25) + - sizeof(ax25_address) * fsa->fsa_ax25.sax25_ndigis) { - err = -EINVAL; - goto out_release; - } - - if ((digi = kmalloc_obj(ax25_digi)) == NULL) { - err = -ENOBUFS; - goto out_release; - } - - digi->ndigi = fsa->fsa_ax25.sax25_ndigis; - digi->lastrepeat = -1; - - while (ct < fsa->fsa_ax25.sax25_ndigis) { - if ((fsa->fsa_digipeater[ct].ax25_call[6] & - AX25_HBIT) && ax25->iamdigi) { - digi->repeated[ct] = 1; - digi->lastrepeat = ct; - } else { - digi->repeated[ct] = 0; - } - digi->calls[ct] = fsa->fsa_digipeater[ct]; - ct++; - } - } - - /* Must bind first - autobinding does not work. */ - if (sock_flag(sk, SOCK_ZAPPED)) { - kfree(digi); - err = -EINVAL; - goto out_release; - } - - /* Check to see if the device has been filled in, error if it hasn't. */ - if (ax25->ax25_dev == NULL) { - kfree(digi); - err = -EHOSTUNREACH; - goto out_release; - } - - if (sk->sk_type == SOCK_SEQPACKET && - (ax25t=ax25_find_cb(&ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, - ax25->ax25_dev->dev))) { - kfree(digi); - err = -EADDRINUSE; /* Already such a connection */ - ax25_cb_put(ax25t); - goto out_release; - } - - ax25->dest_addr = fsa->fsa_ax25.sax25_call; - ax25->digipeat = digi; - - /* First the easy one */ - if (sk->sk_type != SOCK_SEQPACKET) { - sock->state = SS_CONNECTED; - sk->sk_state = TCP_ESTABLISHED; - goto out_release; - } - - /* Move to connecting socket, ax.25 lapb WAIT_UA.. */ - sock->state = SS_CONNECTING; - sk->sk_state = TCP_SYN_SENT; - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_establish_data_link(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - if (ax25->ax25_dev->dama.slave) - ax25_ds_establish_data_link(ax25); - else - ax25_std_establish_data_link(ax25); - break; -#endif - } - - ax25->state = AX25_STATE_1; - - ax25_start_heartbeat(ax25); - - /* Now the loop */ - if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) { - err = -EINPROGRESS; - goto out_release; - } - - if (sk->sk_state == TCP_SYN_SENT) { - DEFINE_WAIT(wait); - - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - if (sk->sk_state != TCP_SYN_SENT) - break; - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - - if (err) - goto out_release; - } - - if (sk->sk_state != TCP_ESTABLISHED) { - /* Not in ABM, not in WAIT_UA -> failed */ - sock->state = SS_UNCONNECTED; - err = sock_error(sk); /* Always set at this point */ - goto out_release; - } - - sock->state = SS_CONNECTED; - - err = 0; -out_release: - release_sock(sk); - - return err; -} - -static int ax25_accept(struct socket *sock, struct socket *newsock, - struct proto_accept_arg *arg) -{ - struct sk_buff *skb; - struct sock *newsk; - ax25_dev *ax25_dev; - DEFINE_WAIT(wait); - struct sock *sk; - ax25_cb *ax25; - int err = 0; - - if (sock->state != SS_UNCONNECTED) - return -EINVAL; - - if ((sk = sock->sk) == NULL) - return -EINVAL; - - lock_sock(sk); - if (sk->sk_type != SOCK_SEQPACKET) { - err = -EOPNOTSUPP; - goto out; - } - - if (sk->sk_state != TCP_LISTEN) { - err = -EINVAL; - goto out; - } - - /* - * The read queue this time is holding sockets ready to use - * hooked into the SABM we saved - */ - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - skb = skb_dequeue(&sk->sk_receive_queue); - if (skb) - break; - - if (arg->flags & O_NONBLOCK) { - err = -EWOULDBLOCK; - break; - } - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - - if (err) - goto out; - - newsk = skb->sk; - sock_graft(newsk, newsock); - - /* Now attach up the new socket */ - kfree_skb(skb); - sk_acceptq_removed(sk); - newsock->state = SS_CONNECTED; - ax25 = sk_to_ax25(newsk); - ax25_dev = ax25->ax25_dev; - netdev_hold(ax25_dev->dev, &ax25->dev_tracker, GFP_ATOMIC); - ax25_dev_hold(ax25_dev); - -out: - release_sock(sk); - - return err; -} - -static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, - int peer) -{ - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; - struct sock *sk = sock->sk; - unsigned char ndigi, i; - ax25_cb *ax25; - int err = 0; - - memset(fsa, 0, sizeof(*fsa)); - lock_sock(sk); - ax25 = sk_to_ax25(sk); - - if (peer != 0) { - if (sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - - fsa->fsa_ax25.sax25_family = AF_AX25; - fsa->fsa_ax25.sax25_call = ax25->dest_addr; - - if (ax25->digipeat != NULL) { - ndigi = ax25->digipeat->ndigi; - fsa->fsa_ax25.sax25_ndigis = ndigi; - for (i = 0; i < ndigi; i++) - fsa->fsa_digipeater[i] = - ax25->digipeat->calls[i]; - } - } else { - fsa->fsa_ax25.sax25_family = AF_AX25; - fsa->fsa_ax25.sax25_call = ax25->source_addr; - fsa->fsa_ax25.sax25_ndigis = 1; - if (ax25->ax25_dev != NULL) { - memcpy(&fsa->fsa_digipeater[0], - ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN); - } else { - fsa->fsa_digipeater[0] = null_ax25_address; - } - } - err = sizeof (struct full_sockaddr_ax25); - -out: - release_sock(sk); - - return err; -} - -static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) -{ - DECLARE_SOCKADDR(struct sockaddr_ax25 *, usax, msg->msg_name); - struct sock *sk = sock->sk; - struct sockaddr_ax25 sax; - struct sk_buff *skb; - ax25_digi dtmp, *dp; - ax25_cb *ax25; - size_t size; - int lv, err, addr_len = msg->msg_namelen; - - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_CMSG_COMPAT)) - return -EINVAL; - - lock_sock(sk); - ax25 = sk_to_ax25(sk); - - if (sock_flag(sk, SOCK_ZAPPED)) { - err = -EADDRNOTAVAIL; - goto out; - } - - if (sk->sk_shutdown & SEND_SHUTDOWN) { - send_sig(SIGPIPE, current, 0); - err = -EPIPE; - goto out; - } - - if (ax25->ax25_dev == NULL) { - err = -ENETUNREACH; - goto out; - } - - if (len > ax25->ax25_dev->dev->mtu) { - err = -EMSGSIZE; - goto out; - } - - if (usax != NULL) { - if (usax->sax25_family != AF_AX25) { - err = -EINVAL; - goto out; - } - - if (addr_len == sizeof(struct sockaddr_ax25)) - /* ax25_sendmsg(): uses obsolete socket structure */ - ; - else if (addr_len != sizeof(struct full_sockaddr_ax25)) - /* support for old structure may go away some time - * ax25_sendmsg(): uses old (6 digipeater) - * socket structure. - */ - if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || - (addr_len > sizeof(struct full_sockaddr_ax25))) { - err = -EINVAL; - goto out; - } - - - if (addr_len > sizeof(struct sockaddr_ax25) && usax->sax25_ndigis != 0) { - int ct = 0; - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax; - - /* Valid number of digipeaters ? */ - if (usax->sax25_ndigis < 1 || - usax->sax25_ndigis > AX25_MAX_DIGIS || - addr_len < sizeof(struct sockaddr_ax25) + - sizeof(ax25_address) * usax->sax25_ndigis) { - err = -EINVAL; - goto out; - } - - dtmp.ndigi = usax->sax25_ndigis; - - while (ct < usax->sax25_ndigis) { - dtmp.repeated[ct] = 0; - dtmp.calls[ct] = fsa->fsa_digipeater[ct]; - ct++; - } - - dtmp.lastrepeat = 0; - } - - sax = *usax; - if (sk->sk_type == SOCK_SEQPACKET && - ax25cmp(&ax25->dest_addr, &sax.sax25_call)) { - err = -EISCONN; - goto out; - } - if (usax->sax25_ndigis == 0) - dp = NULL; - else - dp = &dtmp; - } else { - /* - * FIXME: 1003.1g - if the socket is like this because - * it has become closed (not started closed) and is VC - * we ought to SIGPIPE, EPIPE - */ - if (sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - sax.sax25_family = AF_AX25; - sax.sax25_call = ax25->dest_addr; - dp = ax25->digipeat; - } - - /* Build a packet */ - /* Assume the worst case */ - size = len + ax25->ax25_dev->dev->hard_header_len; - - skb = sock_alloc_send_skb(sk, size, msg->msg_flags&MSG_DONTWAIT, &err); - if (skb == NULL) - goto out; - - skb_reserve(skb, size - len); - - /* User data follows immediately after the AX.25 data */ - if (memcpy_from_msg(skb_put(skb, len), msg, len)) { - err = -EFAULT; - kfree_skb(skb); - goto out; - } - - skb_reset_network_header(skb); - - /* Add the PID if one is not supplied by the user in the skb */ - if (!ax25->pidincl) - *(u8 *)skb_push(skb, 1) = sk->sk_protocol; - - if (sk->sk_type == SOCK_SEQPACKET) { - /* Connected mode sockets go via the LAPB machine */ - if (sk->sk_state != TCP_ESTABLISHED) { - kfree_skb(skb); - err = -ENOTCONN; - goto out; - } - - /* Shove it onto the queue and kick */ - ax25_output(ax25, ax25->paclen, skb); - - err = len; - goto out; - } - - skb_push(skb, 1 + ax25_addr_size(dp)); - - /* Building AX.25 Header */ - - /* Build an AX.25 header */ - lv = ax25_addr_build(skb->data, &ax25->source_addr, &sax.sax25_call, - dp, AX25_COMMAND, AX25_MODULUS); - - skb_set_transport_header(skb, lv); - - *skb_transport_header(skb) = AX25_UI; - - /* Datagram frames go straight out of the door as UI */ - ax25_queue_xmit(skb, ax25->ax25_dev->dev); - - err = len; - -out: - release_sock(sk); - - return err; -} - -static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, - int flags) -{ - struct sock *sk = sock->sk; - struct sk_buff *skb, *last; - struct sk_buff_head *sk_queue; - int copied; - int err = 0; - int off = 0; - long timeo; - - lock_sock(sk); - /* - * This works for seqpacket too. The receiver has ordered the - * queue for us! We do one quick check first though - */ - if (sk->sk_type == SOCK_SEQPACKET && sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - - /* We need support for non-blocking reads. */ - sk_queue = &sk->sk_receive_queue; - skb = __skb_try_recv_datagram(sk, sk_queue, flags, &off, &err, &last); - /* If no packet is available, release_sock(sk) and try again. */ - if (!skb) { - if (err != -EAGAIN) - goto out; - release_sock(sk); - timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); - while (timeo && !__skb_wait_for_more_packets(sk, sk_queue, &err, - &timeo, last)) { - skb = __skb_try_recv_datagram(sk, sk_queue, flags, &off, - &err, &last); - if (skb) - break; - - if (err != -EAGAIN) - goto done; - } - if (!skb) - goto done; - lock_sock(sk); - } - - if (!sk_to_ax25(sk)->pidincl) - skb_pull(skb, 1); /* Remove PID */ - - skb_reset_transport_header(skb); - copied = skb->len; - - if (copied > size) { - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - - skb_copy_datagram_msg(skb, 0, msg, copied); - - if (msg->msg_name) { - ax25_digi digi; - ax25_address src; - const unsigned char *mac = skb_mac_header(skb); - DECLARE_SOCKADDR(struct sockaddr_ax25 *, sax, msg->msg_name); - - memset(sax, 0, sizeof(struct full_sockaddr_ax25)); - ax25_addr_parse(mac + 1, skb->data - mac - 1, &src, NULL, - &digi, NULL, NULL); - sax->sax25_family = AF_AX25; - /* We set this correctly, even though we may not let the - application know the digi calls further down (because it - did NOT ask to know them). This could get political... **/ - sax->sax25_ndigis = digi.ndigi; - sax->sax25_call = src; - - if (sax->sax25_ndigis != 0) { - int ct; - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax; - - for (ct = 0; ct < digi.ndigi; ct++) - fsa->fsa_digipeater[ct] = digi.calls[ct]; - } - msg->msg_namelen = sizeof(struct full_sockaddr_ax25); - } - - skb_free_datagram(sk, skb); - err = copied; - -out: - release_sock(sk); - -done: - return err; -} - -static int ax25_shutdown(struct socket *sk, int how) -{ - /* FIXME - generate DM and RNR states */ - return -EOPNOTSUPP; -} - -static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - void __user *argp = (void __user *)arg; - int res = 0; - - lock_sock(sk); - switch (cmd) { - case TIOCOUTQ: { - long amount; - - amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - if (amount < 0) - amount = 0; - res = put_user(amount, (int __user *)argp); - break; - } - - case TIOCINQ: { - struct sk_buff *skb; - long amount = 0L; - /* These two are safe on a single CPU system as only user tasks fiddle here */ - if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) - amount = skb->len; - res = put_user(amount, (int __user *) argp); - break; - } - - case SIOCAX25ADDUID: /* Add a uid to the uid/call map table */ - case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */ - case SIOCAX25GETUID: { - struct sockaddr_ax25 sax25; - if (copy_from_user(&sax25, argp, sizeof(sax25))) { - res = -EFAULT; - break; - } - res = ax25_uid_ioctl(cmd, &sax25); - break; - } - - case SIOCAX25NOUID: { /* Set the default policy (default/bar) */ - long amount; - if (!capable(CAP_NET_ADMIN)) { - res = -EPERM; - break; - } - if (get_user(amount, (long __user *)argp)) { - res = -EFAULT; - break; - } - if (amount < 0 || amount > AX25_NOUID_BLOCK) { - res = -EINVAL; - break; - } - ax25_uid_policy = amount; - res = 0; - break; - } - - case SIOCADDRT: - case SIOCDELRT: - case SIOCAX25OPTRT: - if (!capable(CAP_NET_ADMIN)) { - res = -EPERM; - break; - } - res = ax25_rt_ioctl(cmd, argp); - break; - - case SIOCAX25CTLCON: - if (!capable(CAP_NET_ADMIN)) { - res = -EPERM; - break; - } - res = ax25_ctl_ioctl(cmd, argp); - break; - - case SIOCAX25GETINFO: - case SIOCAX25GETINFOOLD: { - ax25_cb *ax25 = sk_to_ax25(sk); - struct ax25_info_struct ax25_info; - - ax25_info.t1 = ax25->t1 / HZ; - ax25_info.t2 = ax25->t2 / HZ; - ax25_info.t3 = ax25->t3 / HZ; - ax25_info.idle = ax25->idle / (60 * HZ); - ax25_info.n2 = ax25->n2; - ax25_info.t1timer = ax25_display_timer(&ax25->t1timer) / HZ; - ax25_info.t2timer = ax25_display_timer(&ax25->t2timer) / HZ; - ax25_info.t3timer = ax25_display_timer(&ax25->t3timer) / HZ; - ax25_info.idletimer = ax25_display_timer(&ax25->idletimer) / (60 * HZ); - ax25_info.n2count = ax25->n2count; - ax25_info.state = ax25->state; - ax25_info.rcv_q = sk_rmem_alloc_get(sk); - ax25_info.snd_q = sk_wmem_alloc_get(sk); - ax25_info.vs = ax25->vs; - ax25_info.vr = ax25->vr; - ax25_info.va = ax25->va; - ax25_info.vs_max = ax25->vs; /* reserved */ - ax25_info.paclen = ax25->paclen; - ax25_info.window = ax25->window; - - /* old structure? */ - if (cmd == SIOCAX25GETINFOOLD) { - static int warned = 0; - if (!warned) { - printk(KERN_INFO "%s uses old SIOCAX25GETINFO\n", - current->comm); - warned=1; - } - - if (copy_to_user(argp, &ax25_info, sizeof(struct ax25_info_struct_deprecated))) { - res = -EFAULT; - break; - } - } else { - if (copy_to_user(argp, &ax25_info, sizeof(struct ax25_info_struct))) { - res = -EINVAL; - break; - } - } - res = 0; - break; - } - - case SIOCAX25ADDFWD: - case SIOCAX25DELFWD: { - struct ax25_fwd_struct ax25_fwd; - if (!capable(CAP_NET_ADMIN)) { - res = -EPERM; - break; - } - if (copy_from_user(&ax25_fwd, argp, sizeof(ax25_fwd))) { - res = -EFAULT; - break; - } - res = ax25_fwd_ioctl(cmd, &ax25_fwd); - break; - } - - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - res = -EINVAL; - break; - - default: - res = -ENOIOCTLCMD; - break; - } - release_sock(sk); - - return res; -} - -#ifdef CONFIG_PROC_FS - -static void *ax25_info_start(struct seq_file *seq, loff_t *pos) - __acquires(ax25_list_lock) -{ - spin_lock_bh(&ax25_list_lock); - return seq_hlist_start(&ax25_list, *pos); -} - -static void *ax25_info_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &ax25_list, pos); -} - -static void ax25_info_stop(struct seq_file *seq, void *v) - __releases(ax25_list_lock) -{ - spin_unlock_bh(&ax25_list_lock); -} - -static int ax25_info_show(struct seq_file *seq, void *v) -{ - ax25_cb *ax25 = hlist_entry(v, struct ax25_cb, ax25_node); - char buf[11]; - int k; - - - /* - * New format: - * magic dev src_addr dest_addr,digi1,digi2,.. st vs vr va t1 t1 t2 t2 t3 t3 idle idle n2 n2 rtt window paclen Snd-Q Rcv-Q inode - */ - - seq_printf(seq, "%p %s %s%s ", - ax25, - ax25->ax25_dev == NULL? "???" : ax25->ax25_dev->dev->name, - ax2asc(buf, &ax25->source_addr), - ax25->iamdigi? "*":""); - seq_printf(seq, "%s", ax2asc(buf, &ax25->dest_addr)); - - for (k=0; (ax25->digipeat != NULL) && (k < ax25->digipeat->ndigi); k++) { - seq_printf(seq, ",%s%s", - ax2asc(buf, &ax25->digipeat->calls[k]), - ax25->digipeat->repeated[k]? "*":""); - } - - seq_printf(seq, " %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %d %d", - ax25->state, - ax25->vs, ax25->vr, ax25->va, - ax25_display_timer(&ax25->t1timer) / HZ, ax25->t1 / HZ, - ax25_display_timer(&ax25->t2timer) / HZ, ax25->t2 / HZ, - ax25_display_timer(&ax25->t3timer) / HZ, ax25->t3 / HZ, - ax25_display_timer(&ax25->idletimer) / (60 * HZ), - ax25->idle / (60 * HZ), - ax25->n2count, ax25->n2, - ax25->rtt / HZ, - ax25->window, - ax25->paclen); - - if (ax25->sk != NULL) { - seq_printf(seq, " %d %d %llu\n", - sk_wmem_alloc_get(ax25->sk), - sk_rmem_alloc_get(ax25->sk), - sock_i_ino(ax25->sk)); - } else { - seq_puts(seq, " * * *\n"); - } - return 0; -} - -static const struct seq_operations ax25_info_seqops = { - .start = ax25_info_start, - .next = ax25_info_next, - .stop = ax25_info_stop, - .show = ax25_info_show, -}; -#endif - -static const struct net_proto_family ax25_family_ops = { - .family = PF_AX25, - .create = ax25_create, - .owner = THIS_MODULE, -}; - -static const struct proto_ops ax25_proto_ops = { - .family = PF_AX25, - .owner = THIS_MODULE, - .release = ax25_release, - .bind = ax25_bind, - .connect = ax25_connect, - .socketpair = sock_no_socketpair, - .accept = ax25_accept, - .getname = ax25_getname, - .poll = datagram_poll, - .ioctl = ax25_ioctl, - .gettstamp = sock_gettstamp, - .listen = ax25_listen, - .shutdown = ax25_shutdown, - .setsockopt = ax25_setsockopt, - .getsockopt = ax25_getsockopt, - .sendmsg = ax25_sendmsg, - .recvmsg = ax25_recvmsg, - .mmap = sock_no_mmap, -}; - -/* - * Called by socket.c on kernel start up - */ -static struct packet_type ax25_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_AX25), - .func = ax25_kiss_rcv, -}; - -static struct notifier_block ax25_dev_notifier = { - .notifier_call = ax25_device_event, -}; - -static int __init ax25_init(void) -{ - int rc = proto_register(&ax25_proto, 0); - - if (rc != 0) - goto out; - - sock_register(&ax25_family_ops); - dev_add_pack(&ax25_packet_type); - register_netdevice_notifier(&ax25_dev_notifier); - - proc_create_seq("ax25_route", 0444, init_net.proc_net, &ax25_rt_seqops); - proc_create_seq("ax25", 0444, init_net.proc_net, &ax25_info_seqops); - proc_create_seq("ax25_calls", 0444, init_net.proc_net, - &ax25_uid_seqops); -out: - return rc; -} -module_init(ax25_init); - - -MODULE_AUTHOR("Jonathan Naylor G4KLX "); -MODULE_DESCRIPTION("The amateur radio AX.25 link layer protocol"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_AX25); - -static void __exit ax25_exit(void) -{ - remove_proc_entry("ax25_route", init_net.proc_net); - remove_proc_entry("ax25", init_net.proc_net); - remove_proc_entry("ax25_calls", init_net.proc_net); - - unregister_netdevice_notifier(&ax25_dev_notifier); - - dev_remove_pack(&ax25_packet_type); - - sock_unregister(PF_AX25); - proto_unregister(&ax25_proto); - - ax25_rt_free(); - ax25_uid_free(); - ax25_dev_free(); -} -module_exit(ax25_exit); diff --git a/net/ax25/ax25_addr.c b/net/ax25/ax25_addr.c deleted file mode 100644 index f68865a4d0ab..000000000000 --- a/net/ax25/ax25_addr.c +++ /dev/null @@ -1,303 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * The default broadcast address of an interface is QST-0; the default address - * is LINUX-1. The null address is defined as a callsign of all spaces with - * an SSID of zero. - */ - -const ax25_address ax25_bcast = - {{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, 0 << 1}}; -const ax25_address ax25_defaddr = - {{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, 1 << 1}}; -const ax25_address null_ax25_address = - {{' ' << 1, ' ' << 1, ' ' << 1, ' ' << 1, ' ' << 1, ' ' << 1, 0 << 1}}; - -EXPORT_SYMBOL_GPL(ax25_bcast); -EXPORT_SYMBOL_GPL(ax25_defaddr); -EXPORT_SYMBOL(null_ax25_address); - -/* - * ax25 -> ascii conversion - */ -char *ax2asc(char *buf, const ax25_address *a) -{ - char c, *s; - int n; - - for (n = 0, s = buf; n < 6; n++) { - c = (a->ax25_call[n] >> 1) & 0x7F; - - if (c != ' ') *s++ = c; - } - - *s++ = '-'; - - if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) { - *s++ = '1'; - n -= 10; - } - - *s++ = n + '0'; - *s++ = '\0'; - - if (*buf == '\0' || *buf == '-') - return "*"; - - return buf; - -} - -EXPORT_SYMBOL(ax2asc); - -/* - * ascii -> ax25 conversion - */ -void asc2ax(ax25_address *addr, const char *callsign) -{ - const char *s; - int n; - - for (s = callsign, n = 0; n < 6; n++) { - if (*s != '\0' && *s != '-') - addr->ax25_call[n] = *s++; - else - addr->ax25_call[n] = ' '; - addr->ax25_call[n] <<= 1; - addr->ax25_call[n] &= 0xFE; - } - - if (*s++ == '\0') { - addr->ax25_call[6] = 0x00; - return; - } - - addr->ax25_call[6] = *s++ - '0'; - - if (*s != '\0') { - addr->ax25_call[6] *= 10; - addr->ax25_call[6] += *s++ - '0'; - } - - addr->ax25_call[6] <<= 1; - addr->ax25_call[6] &= 0x1E; -} - -EXPORT_SYMBOL(asc2ax); - -/* - * Compare two ax.25 addresses - */ -int ax25cmp(const ax25_address *a, const ax25_address *b) -{ - int ct = 0; - - while (ct < 6) { - if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */ - return 1; - ct++; - } - - if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */ - return 0; - - return 2; /* Partial match */ -} - -EXPORT_SYMBOL(ax25cmp); - -/* - * Compare two AX.25 digipeater paths. - */ -int ax25digicmp(const ax25_digi *digi1, const ax25_digi *digi2) -{ - int i; - - if (digi1->ndigi != digi2->ndigi) - return 1; - - if (digi1->lastrepeat != digi2->lastrepeat) - return 1; - - for (i = 0; i < digi1->ndigi; i++) - if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0) - return 1; - - return 0; -} - -/* - * Given an AX.25 address pull of to, from, digi list, command/response and the start of data - * - */ -const unsigned char *ax25_addr_parse(const unsigned char *buf, int len, - ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, - int *dama) -{ - int d = 0; - - if (len < 14) return NULL; - - if (flags != NULL) { - *flags = 0; - - if (buf[6] & AX25_CBIT) - *flags = AX25_COMMAND; - if (buf[13] & AX25_CBIT) - *flags = AX25_RESPONSE; - } - - if (dama != NULL) - *dama = ~buf[13] & AX25_DAMA_FLAG; - - /* Copy to, from */ - if (dest != NULL) - memcpy(dest, buf + 0, AX25_ADDR_LEN); - if (src != NULL) - memcpy(src, buf + 7, AX25_ADDR_LEN); - - buf += 2 * AX25_ADDR_LEN; - len -= 2 * AX25_ADDR_LEN; - - digi->lastrepeat = -1; - digi->ndigi = 0; - - while (!(buf[-1] & AX25_EBIT)) { - if (d >= AX25_MAX_DIGIS) - return NULL; - if (len < AX25_ADDR_LEN) - return NULL; - - memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); - digi->ndigi = d + 1; - - if (buf[6] & AX25_HBIT) { - digi->repeated[d] = 1; - digi->lastrepeat = d; - } else { - digi->repeated[d] = 0; - } - - buf += AX25_ADDR_LEN; - len -= AX25_ADDR_LEN; - d++; - } - - return buf; -} - -/* - * Assemble an AX.25 header from the bits - */ -int ax25_addr_build(unsigned char *buf, const ax25_address *src, - const ax25_address *dest, const ax25_digi *d, int flag, int modulus) -{ - int len = 0; - int ct = 0; - - memcpy(buf, dest, AX25_ADDR_LEN); - buf[6] &= ~(AX25_EBIT | AX25_CBIT); - buf[6] |= AX25_SSSID_SPARE; - - if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT; - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - - memcpy(buf, src, AX25_ADDR_LEN); - buf[6] &= ~(AX25_EBIT | AX25_CBIT); - buf[6] &= ~AX25_SSSID_SPARE; - - if (modulus == AX25_MODULUS) - buf[6] |= AX25_SSSID_SPARE; - else - buf[6] |= AX25_ESSID_SPARE; - - if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT; - - /* - * Fast path the normal digiless path - */ - if (d == NULL || d->ndigi == 0) { - buf[6] |= AX25_EBIT; - return 2 * AX25_ADDR_LEN; - } - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - - while (ct < d->ndigi) { - memcpy(buf, &d->calls[ct], AX25_ADDR_LEN); - - if (d->repeated[ct]) - buf[6] |= AX25_HBIT; - else - buf[6] &= ~AX25_HBIT; - - buf[6] &= ~AX25_EBIT; - buf[6] |= AX25_SSSID_SPARE; - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - ct++; - } - - buf[-1] |= AX25_EBIT; - - return len; -} - -int ax25_addr_size(const ax25_digi *dp) -{ - if (dp == NULL) - return 2 * AX25_ADDR_LEN; - - return AX25_ADDR_LEN * (2 + dp->ndigi); -} - -/* - * Reverse Digipeat List. May not pass both parameters as same struct - */ -void ax25_digi_invert(const ax25_digi *in, ax25_digi *out) -{ - int ct; - - out->ndigi = in->ndigi; - out->lastrepeat = in->ndigi - in->lastrepeat - 2; - - /* Invert the digipeaters */ - for (ct = 0; ct < in->ndigi; ct++) { - out->calls[ct] = in->calls[in->ndigi - ct - 1]; - - if (ct <= out->lastrepeat) { - out->calls[ct].ax25_call[6] |= AX25_HBIT; - out->repeated[ct] = 1; - } else { - out->calls[ct].ax25_call[6] &= ~AX25_HBIT; - out->repeated[ct] = 0; - } - } -} diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c deleted file mode 100644 index 3c0544fc4ad5..000000000000 --- a/net/ax25/ax25_dev.c +++ /dev/null @@ -1,200 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static LIST_HEAD(ax25_dev_list); -DEFINE_SPINLOCK(ax25_dev_lock); - -ax25_dev *ax25_addr_ax25dev(ax25_address *addr) -{ - ax25_dev *ax25_dev, *res = NULL; - - spin_lock_bh(&ax25_dev_lock); - list_for_each_entry(ax25_dev, &ax25_dev_list, list) - if (ax25cmp(addr, (const ax25_address *)ax25_dev->dev->dev_addr) == 0) { - res = ax25_dev; - ax25_dev_hold(ax25_dev); - break; - } - spin_unlock_bh(&ax25_dev_lock); - - return res; -} - -/* - * This is called when an interface is brought up. These are - * reasonable defaults. - */ -void ax25_dev_device_up(struct net_device *dev) -{ - ax25_dev *ax25_dev; - - ax25_dev = kzalloc_obj(*ax25_dev); - if (!ax25_dev) { - printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n"); - return; - } - - refcount_set(&ax25_dev->refcount, 1); - ax25_dev->dev = dev; - netdev_hold(dev, &ax25_dev->dev_tracker, GFP_KERNEL); - ax25_dev->forward = NULL; - ax25_dev->device_up = true; - - ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE; - ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE; - ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF; - ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE; - ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW; - ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW; - ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1; - ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2; - ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3; - ax25_dev->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE; - ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2; - ax25_dev->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN; - ax25_dev->values[AX25_VALUES_PROTOCOL] = AX25_DEF_PROTOCOL; - -#ifdef CONFIG_AX25_DAMA_SLAVE - ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT; - - ax25_ds_setup_timer(ax25_dev); -#endif - - spin_lock_bh(&ax25_dev_lock); - list_add(&ax25_dev->list, &ax25_dev_list); - rcu_assign_pointer(dev->ax25_ptr, ax25_dev); - spin_unlock_bh(&ax25_dev_lock); - - ax25_register_dev_sysctl(ax25_dev); -} - -void ax25_dev_device_down(struct net_device *dev) -{ - ax25_dev *s, *ax25_dev; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return; - - ax25_unregister_dev_sysctl(ax25_dev); - - spin_lock_bh(&ax25_dev_lock); - -#ifdef CONFIG_AX25_DAMA_SLAVE - timer_shutdown_sync(&ax25_dev->dama.slave_timer); -#endif - - /* - * Remove any packet forwarding that points to this device. - */ - list_for_each_entry(s, &ax25_dev_list, list) - if (s->forward == dev) - s->forward = NULL; - - list_for_each_entry(s, &ax25_dev_list, list) { - if (s == ax25_dev) { - list_del(&s->list); - break; - } - } - - RCU_INIT_POINTER(dev->ax25_ptr, NULL); - spin_unlock_bh(&ax25_dev_lock); - netdev_put(dev, &ax25_dev->dev_tracker); - ax25_dev_put(ax25_dev); -} - -int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd) -{ - ax25_dev *ax25_dev, *fwd_dev; - - if ((ax25_dev = ax25_addr_ax25dev(&fwd->port_from)) == NULL) - return -EINVAL; - - switch (cmd) { - case SIOCAX25ADDFWD: - fwd_dev = ax25_addr_ax25dev(&fwd->port_to); - if (!fwd_dev) { - ax25_dev_put(ax25_dev); - return -EINVAL; - } - if (ax25_dev->forward) { - ax25_dev_put(fwd_dev); - ax25_dev_put(ax25_dev); - return -EINVAL; - } - ax25_dev->forward = fwd_dev->dev; - ax25_dev_put(fwd_dev); - ax25_dev_put(ax25_dev); - break; - - case SIOCAX25DELFWD: - if (!ax25_dev->forward) { - ax25_dev_put(ax25_dev); - return -EINVAL; - } - ax25_dev->forward = NULL; - ax25_dev_put(ax25_dev); - break; - - default: - ax25_dev_put(ax25_dev); - return -EINVAL; - } - - return 0; -} - -struct net_device *ax25_fwd_dev(struct net_device *dev) -{ - ax25_dev *ax25_dev; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return dev; - - if (ax25_dev->forward == NULL) - return dev; - - return ax25_dev->forward; -} - -/* - * Free all memory associated with device structures. - */ -void __exit ax25_dev_free(void) -{ - ax25_dev *s, *n; - - spin_lock_bh(&ax25_dev_lock); - list_for_each_entry_safe(s, n, &ax25_dev_list, list) { - netdev_put(s->dev, &s->dev_tracker); - list_del(&s->list); - ax25_dev_put(s); - } - spin_unlock_bh(&ax25_dev_lock); -} diff --git a/net/ax25/ax25_ds_in.c b/net/ax25/ax25_ds_in.c deleted file mode 100644 index c62f8fb06189..000000000000 --- a/net/ax25/ax25_ds_in.c +++ /dev/null @@ -1,298 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * State machine for state 1, Awaiting Connection State. - * The handling of the timer(s) is in file ax25_ds_timer.c. - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_SABME: - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_UA: - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - if (ax25->sk != NULL) { - bh_lock_sock(ax25->sk); - ax25->sk->sk_state = TCP_ESTABLISHED; - /* - * For WAIT_SABM connections we will produce an accept - * ready socket here - */ - if (!sock_flag(ax25->sk, SOCK_DEAD)) - ax25->sk->sk_state_change(ax25->sk); - bh_unlock_sock(ax25->sk); - } - ax25_dama_on(ax25); - - /* according to DK4EG's spec we are required to - * send a RR RESPONSE FINAL NR=0. - */ - - ax25_std_enquiry_response(ax25); - break; - - case AX25_DM: - if (pf) - ax25_disconnect(ax25, ECONNREFUSED); - break; - - default: - if (pf) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - break; - } - - return 0; -} - -/* - * State machine for state 2, Awaiting Release State. - * The handling of the timer(s) is in file ax25_ds_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - case AX25_UA: - if (pf) { - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - } - break; - - case AX25_I: - case AX25_REJ: - case AX25_RNR: - case AX25_RR: - if (pf) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - } - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file ax25_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25_requeue_frames(ax25); - ax25_dama_on(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_dama_off(ax25); - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - - if (ax25_validate_nr(ax25, nr)) { - if (ax25_check_iframes_acked(ax25, nr)) - ax25->n2count=0; - if (type == AX25_COMMAND && pf) - ax25_ds_enquiry_response(ax25); - } else { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - - if (ax25_validate_nr(ax25, nr)) { - if (ax25->va != nr) - ax25->n2count=0; - - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_requeue_frames(ax25); - - if (type == AX25_COMMAND && pf) - ax25_ds_enquiry_response(ax25); - } else { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - if (ax25->condition & AX25_COND_PEER_RX_BUSY) { - ax25_frames_acked(ax25, nr); - ax25->n2count = 0; - } else { - if (ax25_check_iframes_acked(ax25, nr)) - ax25->n2count = 0; - } - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) ax25_ds_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_ds_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_ds_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_ds_enquiry_response(ax25); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_ds_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * Higher level upcall for a LAPB frame - */ -int ax25_ds_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - int queued = 0, frametype, ns, nr, pf; - - frametype = ax25_decode(ax25, skb, &ns, &nr, &pf); - - switch (ax25->state) { - case AX25_STATE_1: - queued = ax25_ds_state1_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_2: - queued = ax25_ds_state2_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_3: - queued = ax25_ds_state3_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - } - - return queued; -} diff --git a/net/ax25/ax25_ds_subr.c b/net/ax25/ax25_ds_subr.c deleted file mode 100644 index f00e27df3c76..000000000000 --- a/net/ax25/ax25_ds_subr.c +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void ax25_ds_nr_error_recovery(ax25_cb *ax25) -{ - ax25_ds_establish_data_link(ax25); -} - -/* - * dl1bke 960114: transmit I frames on DAMA poll - */ -void ax25_ds_enquiry_response(ax25_cb *ax25) -{ - ax25_cb *ax25o; - - /* Please note that neither DK4EG's nor DG2FEF's - * DAMA spec mention the following behaviour as seen - * with TheFirmware: - * - * DB0ACH->DL1BKE [DAMA] - * DL1BKE->DB0ACH - * DL1BKE-7->DB0PRA-6 DB0ACH - * DL1BKE->DB0ACH - * - * The Flexnet DAMA Master implementation apparently - * insists on the "proper" AX.25 behaviour: - * - * DB0ACH->DL1BKE [DAMA] - * DL1BKE->DB0ACH - * DL1BKE->DB0ACH - * DL1BKE-7->DB0PRA-6 DB0ACH - * - * Flexnet refuses to send us *any* I frame if we send - * a REJ in case AX25_COND_REJECT is set. It is superfluous in - * this mode anyway (a RR or RNR invokes the retransmission). - * Is this a Flexnet bug? - */ - - ax25_std_enquiry_response(ax25); - - if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) { - ax25_requeue_frames(ax25); - ax25_kick(ax25); - } - - if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL) - ax25_ds_t1_timeout(ax25); - else - ax25->n2count = 0; - - ax25_start_t3timer(ax25); - ax25_ds_set_timer(ax25->ax25_dev); - - spin_lock(&ax25_list_lock); - ax25_for_each(ax25o, &ax25_list) { - if (ax25o == ax25) - continue; - - if (ax25o->ax25_dev != ax25->ax25_dev) - continue; - - if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) { - ax25_ds_t1_timeout(ax25o); - continue; - } - - if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && ax25o->state == AX25_STATE_3) { - ax25_requeue_frames(ax25o); - ax25_kick(ax25o); - } - - if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL) - ax25_ds_t1_timeout(ax25o); - - /* do not start T3 for listening sockets (tnx DD8NE) */ - - if (ax25o->state != AX25_STATE_0) - ax25_start_t3timer(ax25o); - } - spin_unlock(&ax25_list_lock); -} - -void ax25_ds_establish_data_link(ax25_cb *ax25) -{ - ax25->condition &= AX25_COND_DAMA_MODE; - ax25->n2count = 0; - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); -} - -/* - * :::FIXME::: - * This is a kludge. Not all drivers recognize kiss commands. - * We need a driver level request to switch duplex mode, that does - * either SCC changing, PI config or KISS as required. Currently - * this request isn't reliable. - */ -static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param) -{ - struct sk_buff *skb; - unsigned char *p; - - if (ax25_dev->dev == NULL) - return; - - if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL) - return; - - skb_reset_network_header(skb); - p = skb_put(skb, 2); - - *p++ = cmd; - *p++ = param; - - skb->protocol = ax25_type_trans(skb, ax25_dev->dev); - - dev_queue_xmit(skb); -} - -/* - * A nasty problem arises if we count the number of DAMA connections - * wrong, especially when connections on the device already existed - * and our network node (or the sysop) decides to turn on DAMA Master - * mode. We thus flag the 'real' slave connections with - * ax25->dama_slave=1 and look on every disconnect if still slave - * connections exist. - */ -static int ax25_check_dama_slave(ax25_dev *ax25_dev) -{ - ax25_cb *ax25; - int res = 0; - - spin_lock(&ax25_list_lock); - ax25_for_each(ax25, &ax25_list) - if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1) { - res = 1; - break; - } - spin_unlock(&ax25_list_lock); - - return res; -} - -static void ax25_dev_dama_on(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) - return; - - if (ax25_dev->dama.slave == 0) - ax25_kiss_cmd(ax25_dev, 5, 1); - - ax25_dev->dama.slave = 1; - ax25_ds_set_timer(ax25_dev); -} - -void ax25_dev_dama_off(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) - return; - - if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev)) { - ax25_kiss_cmd(ax25_dev, 5, 0); - ax25_dev->dama.slave = 0; - ax25_ds_del_timer(ax25_dev); - } -} - -void ax25_dama_on(ax25_cb *ax25) -{ - ax25_dev_dama_on(ax25->ax25_dev); - ax25->condition |= AX25_COND_DAMA_MODE; -} - -void ax25_dama_off(ax25_cb *ax25) -{ - ax25->condition &= ~AX25_COND_DAMA_MODE; - ax25_dev_dama_off(ax25->ax25_dev); -} diff --git a/net/ax25/ax25_ds_timer.c b/net/ax25/ax25_ds_timer.c deleted file mode 100644 index 0c9e7775aa54..000000000000 --- a/net/ax25/ax25_ds_timer.c +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void ax25_ds_timeout(struct timer_list *); - -/* - * Add DAMA slave timeout timer to timer list. - * Unlike the connection based timers the timeout function gets - * triggered every second. Please note that NET_AX25_DAMA_SLAVE_TIMEOUT - * (aka /proc/sys/net/ax25/{dev}/dama_slave_timeout) is still in - * 1/10th of a second. - */ - -void ax25_ds_setup_timer(ax25_dev *ax25_dev) -{ - timer_setup(&ax25_dev->dama.slave_timer, ax25_ds_timeout, 0); -} - -void ax25_ds_del_timer(ax25_dev *ax25_dev) -{ - if (ax25_dev) - timer_delete(&ax25_dev->dama.slave_timer); -} - -void ax25_ds_set_timer(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) /* paranoia */ - return; - - ax25_dev->dama.slave_timeout = - msecs_to_jiffies(ax25_dev->values[AX25_VALUES_DS_TIMEOUT]) / 10; - mod_timer(&ax25_dev->dama.slave_timer, jiffies + HZ); -} - -/* - * DAMA Slave Timeout - * Silently discard all (slave) connections in case our master forgot us... - */ - -static void ax25_ds_timeout(struct timer_list *t) -{ - ax25_dev *ax25_dev = timer_container_of(ax25_dev, t, dama.slave_timer); - ax25_cb *ax25; - - if (ax25_dev == NULL || !ax25_dev->dama.slave) - return; /* Yikes! */ - - if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout) { - ax25_ds_set_timer(ax25_dev); - return; - } - - spin_lock(&ax25_list_lock); - ax25_for_each(ax25, &ax25_list) { - if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE)) - continue; - - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(ax25, ETIMEDOUT); - } - spin_unlock(&ax25_list_lock); - - ax25_dev_dama_off(ax25_dev); -} - -void ax25_ds_heartbeat_expiry(ax25_cb *ax25) -{ - struct sock *sk=ax25->sk; - - if (sk) - bh_lock_sock(sk); - - switch (ax25->state) { - - case AX25_STATE_0: - case AX25_STATE_2: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (!sk || sock_flag(sk, SOCK_DESTROY) || - (sk->sk_state == TCP_LISTEN && - sock_flag(sk, SOCK_DEAD))) { - if (sk) { - sock_hold(sk); - ax25_destroy_socket(ax25); - bh_unlock_sock(sk); - /* Ungrab socket and destroy it */ - sock_put(sk); - } else - ax25_destroy_socket(ax25); - return; - } - break; - - case AX25_STATE_3: - /* - * Check the state of the receive buffer. - */ - if (sk != NULL) { - if (atomic_read(&sk->sk_rmem_alloc) < - (sk->sk_rcvbuf >> 1) && - (ax25->condition & AX25_COND_OWN_RX_BUSY)) { - ax25->condition &= ~AX25_COND_OWN_RX_BUSY; - ax25->condition &= ~AX25_COND_ACK_PENDING; - break; - } - } - break; - } - - if (sk) - bh_unlock_sock(sk); - - ax25_start_heartbeat(ax25); -} - -/* dl1bke 960114: T3 works much like the IDLE timeout, but - * gets reloaded with every frame for this - * connection. - */ -void ax25_ds_t3timer_expiry(ax25_cb *ax25) -{ - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - ax25_disconnect(ax25, ETIMEDOUT); -} - -/* dl1bke 960228: close the connection when IDLE expires. - * unlike T3 this timer gets reloaded only on - * I frames. - */ -void ax25_ds_idletimer_expiry(ax25_cb *ax25) -{ - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25->state = AX25_STATE_2; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t3timer(ax25); - - if (ax25->sk != NULL) { - bh_lock_sock(ax25->sk); - ax25->sk->sk_state = TCP_CLOSE; - ax25->sk->sk_err = 0; - ax25->sk->sk_shutdown |= SEND_SHUTDOWN; - if (!sock_flag(ax25->sk, SOCK_DEAD)) { - ax25->sk->sk_state_change(ax25->sk); - sock_set_flag(ax25->sk, SOCK_DEAD); - } - bh_unlock_sock(ax25->sk); - } -} - -/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC - * within the poll of any connected channel. Remember - * that we are not allowed to send anything unless we - * get polled by the Master. - * - * Thus we'll have to do parts of our T1 handling in - * ax25_enquiry_response(). - */ -void ax25_ds_t1_timeout(ax25_cb *ax25) -{ - switch (ax25->state) { - case AX25_STATE_1: - if (ax25->n2count == ax25->n2) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25->n2count = 0; - ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND); - } - } else { - ax25->n2count++; - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLOFF, AX25_COMMAND); - } - break; - - case AX25_STATE_2: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - if (!sock_flag(ax25->sk, SOCK_DESTROY)) - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - } - break; - - case AX25_STATE_3: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - } - break; - } - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} diff --git a/net/ax25/ax25_iface.c b/net/ax25/ax25_iface.c deleted file mode 100644 index 3ad454416a5c..000000000000 --- a/net/ax25/ax25_iface.c +++ /dev/null @@ -1,214 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct ax25_protocol *protocol_list; -static DEFINE_RWLOCK(protocol_list_lock); - -static HLIST_HEAD(ax25_linkfail_list); -static DEFINE_SPINLOCK(linkfail_lock); - -static struct listen_struct { - struct listen_struct *next; - ax25_address callsign; - struct net_device *dev; -} *listen_list = NULL; -static DEFINE_SPINLOCK(listen_lock); - -/* - * Do not register the internal protocols AX25_P_TEXT, AX25_P_SEGMENT, - * AX25_P_IP or AX25_P_ARP ... - */ -void ax25_register_pid(struct ax25_protocol *ap) -{ - write_lock_bh(&protocol_list_lock); - ap->next = protocol_list; - protocol_list = ap; - write_unlock_bh(&protocol_list_lock); -} - -EXPORT_SYMBOL_GPL(ax25_register_pid); - -void ax25_protocol_release(unsigned int pid) -{ - struct ax25_protocol *protocol; - - write_lock_bh(&protocol_list_lock); - protocol = protocol_list; - if (protocol == NULL) - goto out; - - if (protocol->pid == pid) { - protocol_list = protocol->next; - goto out; - } - - while (protocol != NULL && protocol->next != NULL) { - if (protocol->next->pid == pid) { - protocol->next = protocol->next->next; - goto out; - } - - protocol = protocol->next; - } -out: - write_unlock_bh(&protocol_list_lock); -} - -EXPORT_SYMBOL(ax25_protocol_release); - -void ax25_linkfail_register(struct ax25_linkfail *lf) -{ - spin_lock_bh(&linkfail_lock); - hlist_add_head(&lf->lf_node, &ax25_linkfail_list); - spin_unlock_bh(&linkfail_lock); -} - -EXPORT_SYMBOL(ax25_linkfail_register); - -void ax25_linkfail_release(struct ax25_linkfail *lf) -{ - spin_lock_bh(&linkfail_lock); - hlist_del_init(&lf->lf_node); - spin_unlock_bh(&linkfail_lock); -} - -EXPORT_SYMBOL(ax25_linkfail_release); - -int ax25_listen_register(const ax25_address *callsign, struct net_device *dev) -{ - struct listen_struct *listen; - - if (ax25_listen_mine(callsign, dev)) - return 0; - - if ((listen = kmalloc_obj(*listen, GFP_ATOMIC)) == NULL) - return -ENOMEM; - - listen->callsign = *callsign; - listen->dev = dev; - - spin_lock_bh(&listen_lock); - listen->next = listen_list; - listen_list = listen; - spin_unlock_bh(&listen_lock); - - return 0; -} - -EXPORT_SYMBOL(ax25_listen_register); - -void ax25_listen_release(const ax25_address *callsign, struct net_device *dev) -{ - struct listen_struct *s, *listen; - - spin_lock_bh(&listen_lock); - listen = listen_list; - if (listen == NULL) { - spin_unlock_bh(&listen_lock); - return; - } - - if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) { - listen_list = listen->next; - spin_unlock_bh(&listen_lock); - kfree(listen); - return; - } - - while (listen != NULL && listen->next != NULL) { - if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) { - s = listen->next; - listen->next = listen->next->next; - spin_unlock_bh(&listen_lock); - kfree(s); - return; - } - - listen = listen->next; - } - spin_unlock_bh(&listen_lock); -} - -EXPORT_SYMBOL(ax25_listen_release); - -int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *) -{ - int (*res)(struct sk_buff *, ax25_cb *) = NULL; - struct ax25_protocol *protocol; - - read_lock(&protocol_list_lock); - for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) - if (protocol->pid == pid) { - res = protocol->func; - break; - } - read_unlock(&protocol_list_lock); - - return res; -} - -int ax25_listen_mine(const ax25_address *callsign, struct net_device *dev) -{ - struct listen_struct *listen; - - spin_lock_bh(&listen_lock); - for (listen = listen_list; listen != NULL; listen = listen->next) - if (ax25cmp(&listen->callsign, callsign) == 0 && - (listen->dev == dev || listen->dev == NULL)) { - spin_unlock_bh(&listen_lock); - return 1; - } - spin_unlock_bh(&listen_lock); - - return 0; -} - -void ax25_link_failed(ax25_cb *ax25, int reason) -{ - struct ax25_linkfail *lf; - - spin_lock_bh(&linkfail_lock); - hlist_for_each_entry(lf, &ax25_linkfail_list, lf_node) - lf->func(ax25, reason); - spin_unlock_bh(&linkfail_lock); -} - -int ax25_protocol_is_registered(unsigned int pid) -{ - struct ax25_protocol *protocol; - int res = 0; - - read_lock_bh(&protocol_list_lock); - for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) - if (protocol->pid == pid) { - res = 1; - break; - } - read_unlock_bh(&protocol_list_lock); - - return res; -} diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c deleted file mode 100644 index d75b3e9ed93d..000000000000 --- a/net/ax25/ax25_in.c +++ /dev/null @@ -1,455 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Given a fragment, queue it on the fragment queue and if the fragment - * is complete, send it back to ax25_rx_iframe. - */ -static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb) -{ - struct sk_buff *skbn, *skbo; - - if (ax25->fragno != 0) { - if (!(*skb->data & AX25_SEG_FIRST)) { - if ((ax25->fragno - 1) == (*skb->data & AX25_SEG_REM)) { - /* Enqueue fragment */ - ax25->fragno = *skb->data & AX25_SEG_REM; - skb_pull(skb, 1); /* skip fragno */ - ax25->fraglen += skb->len; - skb_queue_tail(&ax25->frag_queue, skb); - - /* Last fragment received ? */ - if (ax25->fragno == 0) { - skbn = alloc_skb(AX25_MAX_HEADER_LEN + - ax25->fraglen, - GFP_ATOMIC); - if (!skbn) { - skb_queue_purge(&ax25->frag_queue); - return 1; - } - - skb_reserve(skbn, AX25_MAX_HEADER_LEN); - - skbn->dev = ax25->ax25_dev->dev; - skb_reset_network_header(skbn); - skb_reset_transport_header(skbn); - - /* Copy data from the fragments */ - while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) { - skb_copy_from_linear_data(skbo, - skb_put(skbn, skbo->len), - skbo->len); - kfree_skb(skbo); - } - - ax25->fraglen = 0; - - if (ax25_rx_iframe(ax25, skbn) == 0) - kfree_skb(skbn); - } - - return 1; - } - } - } else { - /* First fragment received */ - if (*skb->data & AX25_SEG_FIRST) { - skb_queue_purge(&ax25->frag_queue); - ax25->fragno = *skb->data & AX25_SEG_REM; - skb_pull(skb, 1); /* skip fragno */ - ax25->fraglen = skb->len; - skb_queue_tail(&ax25->frag_queue, skb); - return 1; - } - } - - return 0; -} - -/* - * This is where all valid I frames are sent to, to be dispatched to - * whichever protocol requires them. - */ -int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) -{ - int (*func)(struct sk_buff *, ax25_cb *); - unsigned char pid; - int queued = 0; - - if (skb == NULL) return 0; - - ax25_start_idletimer(ax25); - - pid = *skb->data; - - if (pid == AX25_P_IP) { - /* working around a TCP bug to keep additional listeners - * happy. TCP re-uses the buffer and destroys the original - * content. - */ - struct sk_buff *skbn = skb_copy(skb, GFP_ATOMIC); - if (skbn != NULL) { - kfree_skb(skb); - skb = skbn; - } - - skb_pull(skb, 1); /* Remove PID */ - skb->mac_header = skb->network_header; - skb_reset_network_header(skb); - skb->dev = ax25->ax25_dev->dev; - skb->pkt_type = PACKET_HOST; - skb->protocol = htons(ETH_P_IP); - netif_rx(skb); - return 1; - } - if (pid == AX25_P_SEGMENT) { - skb_pull(skb, 1); /* Remove PID */ - return ax25_rx_fragment(ax25, skb); - } - - if ((func = ax25_protocol_function(pid)) != NULL) { - skb_pull(skb, 1); /* Remove PID */ - return (*func)(skb, ax25); - } - - if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2) { - if ((!ax25->pidincl && ax25->sk->sk_protocol == pid) || - ax25->pidincl) { - if (sock_queue_rcv_skb(ax25->sk, skb) == 0) - queued = 1; - else - ax25->condition |= AX25_COND_OWN_RX_BUSY; - } - } - - return queued; -} - -/* - * Higher level upcall for a LAPB frame - */ -static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama) -{ - int queued = 0; - - if (ax25->state == AX25_STATE_0) - return 0; - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - queued = ax25_std_frame_in(ax25, skb, type); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (dama || ax25->ax25_dev->dama.slave) - queued = ax25_ds_frame_in(ax25, skb, type); - else - queued = ax25_std_frame_in(ax25, skb, type); - break; -#endif - } - - return queued; -} - -static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, - const ax25_address *dev_addr, struct packet_type *ptype) -{ - ax25_address src, dest, *next_digi = NULL; - int type = 0, mine = 0, dama; - struct sock *make, *sk; - ax25_digi dp, reverse_dp; - ax25_cb *ax25; - ax25_dev *ax25_dev; - - /* - * Process the AX.25/LAPB frame. - */ - - skb_reset_transport_header(skb); - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - goto free; - - /* - * Parse the address header. - */ - - if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) - goto free; - - /* - * Ours perhaps ? - */ - if (dp.lastrepeat + 1 < dp.ndigi) /* Not yet digipeated completely */ - next_digi = &dp.calls[dp.lastrepeat + 1]; - - /* - * Pull of the AX.25 headers leaving the CTRL/PID bytes - */ - skb_pull(skb, ax25_addr_size(&dp)); - - /* For our port addresses ? */ - if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi) - mine = 1; - - /* Also match on any registered callsign from L3/4 */ - if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi) - mine = 1; - - /* UI frame - bypass LAPB processing */ - if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) { - skb_set_transport_header(skb, 2); /* skip control and pid */ - - ax25_send_to_raw(&dest, skb, skb->data[1]); - - if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) - goto free; - - /* Now we are pointing at the pid byte */ - switch (skb->data[1]) { - case AX25_P_IP: - skb_pull(skb,2); /* drop PID/CTRL */ - skb_reset_transport_header(skb); - skb_reset_network_header(skb); - skb->dev = dev; - skb->pkt_type = PACKET_HOST; - skb->protocol = htons(ETH_P_IP); - netif_rx(skb); - break; - - case AX25_P_ARP: - skb_pull(skb,2); - skb_reset_transport_header(skb); - skb_reset_network_header(skb); - skb->dev = dev; - skb->pkt_type = PACKET_HOST; - skb->protocol = htons(ETH_P_ARP); - netif_rx(skb); - break; - case AX25_P_TEXT: - /* Now find a suitable dgram socket */ - sk = ax25_get_socket(&dest, &src, SOCK_DGRAM); - if (sk != NULL) { - bh_lock_sock(sk); - if (atomic_read(&sk->sk_rmem_alloc) >= - sk->sk_rcvbuf) { - kfree_skb(skb); - } else { - /* - * Remove the control and PID. - */ - skb_pull(skb, 2); - if (sock_queue_rcv_skb(sk, skb) != 0) - kfree_skb(skb); - } - bh_unlock_sock(sk); - sock_put(sk); - } else { - kfree_skb(skb); - } - break; - - default: - kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */ - break; - } - - return 0; - } - - /* - * Is connected mode supported on this device ? - * If not, should we DM the incoming frame (except DMs) or - * silently ignore them. For now we stay quiet. - */ - if (ax25_dev->values[AX25_VALUES_CONMODE] == 0) - goto free; - - /* LAPB */ - - /* AX.25 state 1-4 */ - - ax25_digi_invert(&dp, &reverse_dp); - - if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) { - /* - * Process the frame. If it is queued up internally it - * returns one otherwise we free it immediately. This - * routine itself wakes the user context layers so we do - * no further work - */ - if (ax25_process_rx_frame(ax25, skb, type, dama) == 0) - kfree_skb(skb); - - ax25_cb_put(ax25); - return 0; - } - - /* AX.25 state 0 (disconnected) */ - - /* a) received not a SABM(E) */ - - if ((*skb->data & ~AX25_PF) != AX25_SABM && - (*skb->data & ~AX25_PF) != AX25_SABME) { - /* - * Never reply to a DM. Also ignore any connects for - * addresses that are not our interfaces and not a socket. - */ - if ((*skb->data & ~AX25_PF) != AX25_DM && mine) - ax25_return_dm(dev, &src, &dest, &dp); - - goto free; - } - - /* b) received SABM(E) */ - - if (dp.lastrepeat + 1 == dp.ndigi) - sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET); - else - sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET); - - if (sk != NULL) { - bh_lock_sock(sk); - if (sk_acceptq_is_full(sk) || - (make = ax25_make_new(sk, ax25_dev)) == NULL) { - if (mine) - ax25_return_dm(dev, &src, &dest, &dp); - kfree_skb(skb); - bh_unlock_sock(sk); - sock_put(sk); - - return 0; - } - - ax25 = sk_to_ax25(make); - skb_set_owner_r(skb, make); - skb_queue_head(&sk->sk_receive_queue, skb); - - make->sk_state = TCP_ESTABLISHED; - - sk_acceptq_added(sk); - bh_unlock_sock(sk); - } else { - if (!mine) - goto free; - - if ((ax25 = ax25_create_cb()) == NULL) { - ax25_return_dm(dev, &src, &dest, &dp); - goto free; - } - - ax25_fillin_cb(ax25, ax25_dev); - } - - ax25->source_addr = dest; - ax25->dest_addr = src; - - /* - * Sort out any digipeated paths. - */ - if (dp.ndigi && !ax25->digipeat && - (ax25->digipeat = kmalloc_obj(ax25_digi, GFP_ATOMIC)) == NULL) { - kfree_skb(skb); - ax25_destroy_socket(ax25); - if (sk) - sock_put(sk); - return 0; - } - - if (dp.ndigi == 0) { - kfree(ax25->digipeat); - ax25->digipeat = NULL; - } else { - /* Reverse the source SABM's path */ - memcpy(ax25->digipeat, &reverse_dp, sizeof(ax25_digi)); - } - - if ((*skb->data & ~AX25_PF) == AX25_SABME) { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; - } - - ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE); - -#ifdef CONFIG_AX25_DAMA_SLAVE - if (dama && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) - ax25_dama_on(ax25); -#endif - - ax25->state = AX25_STATE_3; - - ax25_cb_add(ax25); - - ax25_start_heartbeat(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - - if (sk) { - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_data_ready(sk); - sock_put(sk); - } else { -free: - kfree_skb(skb); - } - return 0; -} - -/* - * Receive an AX.25 frame via a SLIP interface. - */ -int ax25_kiss_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *ptype, struct net_device *orig_dev) -{ - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) - return NET_RX_DROP; - - skb_orphan(skb); - - if (!net_eq(dev_net(dev), &init_net)) { - kfree_skb(skb); - return 0; - } - - if ((*skb->data & 0x0F) != 0) { - kfree_skb(skb); /* Not a KISS data frame */ - return 0; - } - - skb_pull(skb, AX25_KISS_HEADER_LEN); /* Remove the KISS byte */ - - return ax25_rcv(skb, dev, (const ax25_address *)dev->dev_addr, ptype); -} diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c deleted file mode 100644 index 215d4ccf12b9..000000000000 --- a/net/ax25/ax25_ip.c +++ /dev/null @@ -1,247 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For TIOCINQ/OUTQ */ -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * IP over AX.25 encapsulation. - */ - -/* - * Shove an AX.25 UI header on an IP packet and handle ARP - */ - -#ifdef CONFIG_INET - -static int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned int len) -{ - unsigned char *buff; - - /* they sometimes come back to us... */ - if (type == ETH_P_AX25) - return 0; - - /* header is an AX.25 UI frame from us to them */ - buff = skb_push(skb, AX25_HEADER_LEN); - *buff++ = 0x00; /* KISS DATA */ - - if (daddr != NULL) - memcpy(buff, daddr, dev->addr_len); /* Address specified */ - - buff[6] &= ~AX25_CBIT; - buff[6] &= ~AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - if (saddr != NULL) - memcpy(buff, saddr, dev->addr_len); - else - memcpy(buff, dev->dev_addr, dev->addr_len); - - buff[6] &= ~AX25_CBIT; - buff[6] |= AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - *buff++ = AX25_UI; /* UI */ - - /* Append a suitable AX.25 PID */ - switch (type) { - case ETH_P_IP: - *buff++ = AX25_P_IP; - break; - case ETH_P_ARP: - *buff++ = AX25_P_ARP; - break; - default: - printk(KERN_ERR "AX.25: ax25_hard_header - wrong protocol type 0x%2.2x\n", type); - *buff++ = 0; - break; - } - - if (daddr != NULL) - return AX25_HEADER_LEN; - - return -AX25_HEADER_LEN; /* Unfinished header */ -} - -netdev_tx_t ax25_ip_xmit(struct sk_buff *skb) -{ - struct sk_buff *ourskb; - unsigned char *bp = skb->data; - ax25_route *route; - struct net_device *dev = NULL; - ax25_address *src, *dst; - ax25_digi *digipeat = NULL; - ax25_dev *ax25_dev; - ax25_cb *ax25; - char ip_mode = ' '; - - dst = (ax25_address *)(bp + 1); - src = (ax25_address *)(bp + 8); - - ax25_route_lock_use(); - route = ax25_get_route(dst, NULL); - if (route) { - digipeat = route->digipeat; - dev = route->dev; - ip_mode = route->ip_mode; - } - - if (dev == NULL) - dev = skb->dev; - - rcu_read_lock(); - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) { - kfree_skb(skb); - goto put; - } - - if (bp[16] == AX25_P_IP) { - if (ip_mode == 'V' || (ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { - /* - * We copy the buffer and release the original thereby - * keeping it straight - * - * Note: we report 1 back so the caller will - * not feed the frame direct to the physical device - * We don't want that to happen. (It won't be upset - * as we have pulled the frame from the queue by - * freeing it). - * - * NB: TCP modifies buffers that are still - * on a device queue, thus we use skb_copy() - * instead of using skb_clone() unless this - * gets fixed. - */ - - ax25_address src_c; - ax25_address dst_c; - - if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) { - kfree_skb(skb); - goto put; - } - - if (skb->sk != NULL) - skb_set_owner_w(ourskb, skb->sk); - - kfree_skb(skb); - /* dl9sau: bugfix - * after kfree_skb(), dst and src which were pointer - * to bp which is part of skb->data would not be valid - * anymore hope that after skb_pull(ourskb, ..) our - * dsc_c and src_c will not become invalid - */ - bp = ourskb->data; - dst_c = *(ax25_address *)(bp + 1); - src_c = *(ax25_address *)(bp + 8); - - skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */ - skb_reset_network_header(ourskb); - - ax25=ax25_send_frame( - ourskb, - ax25_dev->values[AX25_VALUES_PACLEN], - &src_c, - &dst_c, digipeat, dev); - if (ax25) { - ax25_cb_put(ax25); - } - goto put; - } - } - - bp[7] &= ~AX25_CBIT; - bp[7] &= ~AX25_EBIT; - bp[7] |= AX25_SSSID_SPARE; - - bp[14] &= ~AX25_CBIT; - bp[14] |= AX25_EBIT; - bp[14] |= AX25_SSSID_SPARE; - - skb_pull(skb, AX25_KISS_HEADER_LEN); - - if (digipeat != NULL) { - if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) - goto put; - - skb = ourskb; - } - - ax25_queue_xmit(skb, dev); - -put: - rcu_read_unlock(); - ax25_route_lock_unuse(); - return NETDEV_TX_OK; -} - -#else /* INET */ - -static int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned int len) -{ - return -AX25_HEADER_LEN; -} - -netdev_tx_t ax25_ip_xmit(struct sk_buff *skb) -{ - kfree_skb(skb); - return NETDEV_TX_OK; -} -#endif - -static bool ax25_validate_header(const char *header, unsigned int len) -{ - ax25_digi digi; - - if (!len) - return false; - - if (header[0]) - return true; - - return ax25_addr_parse(header + 1, len - 1, NULL, NULL, &digi, NULL, - NULL); -} - -const struct header_ops ax25_header_ops = { - .create = ax25_hard_header, - .validate = ax25_validate_header, -}; - -EXPORT_SYMBOL(ax25_header_ops); -EXPORT_SYMBOL(ax25_ip_xmit); diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c deleted file mode 100644 index 8bca2ace98e5..000000000000 --- a/net/ax25/ax25_out.c +++ /dev/null @@ -1,398 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static DEFINE_SPINLOCK(ax25_frag_lock); - -ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *src, ax25_address *dest, ax25_digi *digi, struct net_device *dev) -{ - ax25_dev *ax25_dev; - ax25_cb *ax25; - - /* - * Take the default packet length for the device if zero is - * specified. - */ - if (paclen == 0) { - rcu_read_lock(); - ax25_dev = ax25_dev_ax25dev(dev); - if (!ax25_dev) { - rcu_read_unlock(); - return NULL; - } - paclen = ax25_dev->values[AX25_VALUES_PACLEN]; - rcu_read_unlock(); - } - - /* - * Look for an existing connection. - */ - if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) { - ax25_output(ax25, paclen, skb); - return ax25; /* It already existed */ - } - - rcu_read_lock(); - ax25_dev = ax25_dev_ax25dev(dev); - if (!ax25_dev) { - rcu_read_unlock(); - return NULL; - } - - if ((ax25 = ax25_create_cb()) == NULL) { - rcu_read_unlock(); - return NULL; - } - ax25_fillin_cb(ax25, ax25_dev); - rcu_read_unlock(); - - ax25->source_addr = *src; - ax25->dest_addr = *dest; - - if (digi != NULL) { - ax25->digipeat = kmemdup(digi, sizeof(*digi), GFP_ATOMIC); - if (ax25->digipeat == NULL) { - ax25_cb_put(ax25); - return NULL; - } - } - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_establish_data_link(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25_dev->dama.slave) - ax25_ds_establish_data_link(ax25); - else - ax25_std_establish_data_link(ax25); - break; -#endif - } - - /* - * There is one ref for the state machine; a caller needs - * one more to put it back, just like with the existing one. - */ - ax25_cb_hold(ax25); - - ax25_cb_add(ax25); - - ax25->state = AX25_STATE_1; - - ax25_start_heartbeat(ax25); - - ax25_output(ax25, paclen, skb); - - return ax25; /* We had to create it */ -} - -EXPORT_SYMBOL(ax25_send_frame); - -/* - * All outgoing AX.25 I frames pass via this routine. Therefore this is - * where the fragmentation of frames takes place. If fragment is set to - * zero then we are not allowed to do fragmentation, even if the frame - * is too large. - */ -void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) -{ - struct sk_buff *skbn; - unsigned char *p; - int frontlen, len, fragno, ka9qfrag, first = 1; - - if (paclen < 16) { - WARN_ON_ONCE(1); - kfree_skb(skb); - return; - } - - if ((skb->len - 1) > paclen) { - if (*skb->data == AX25_P_TEXT) { - skb_pull(skb, 1); /* skip PID */ - ka9qfrag = 0; - } else { - paclen -= 2; /* Allow for fragment control info */ - ka9qfrag = 1; - } - - fragno = skb->len / paclen; - if (skb->len % paclen == 0) fragno--; - - frontlen = skb_headroom(skb); /* Address space + CTRL */ - - while (skb->len > 0) { - spin_lock_bh(&ax25_frag_lock); - if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) { - spin_unlock_bh(&ax25_frag_lock); - printk(KERN_CRIT "AX.25: ax25_output - out of memory\n"); - return; - } - - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); - - spin_unlock_bh(&ax25_frag_lock); - - len = (paclen > skb->len) ? skb->len : paclen; - - if (ka9qfrag == 1) { - skb_reserve(skbn, frontlen + 2); - skb_set_network_header(skbn, - skb_network_offset(skb)); - skb_copy_from_linear_data(skb, skb_put(skbn, len), len); - p = skb_push(skbn, 2); - - *p++ = AX25_P_SEGMENT; - - *p = fragno--; - if (first) { - *p |= AX25_SEG_FIRST; - first = 0; - } - } else { - skb_reserve(skbn, frontlen + 1); - skb_set_network_header(skbn, - skb_network_offset(skb)); - skb_copy_from_linear_data(skb, skb_put(skbn, len), len); - p = skb_push(skbn, 1); - *p = AX25_P_TEXT; - } - - skb_pull(skb, len); - skb_queue_tail(&ax25->write_queue, skbn); /* Throw it on the queue */ - } - - kfree_skb(skb); - } else { - skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */ - } - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_kick(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - /* - * A DAMA slave is _required_ to work as normal AX.25L2V2 - * if no DAMA master is available. - */ - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) ax25_kick(ax25); - break; -#endif - } -} - -/* - * This procedure is passed a buffer descriptor for an iframe. It builds - * the rest of the control part of the frame and then writes it out. - */ -static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) -{ - unsigned char *frame; - - if (skb == NULL) - return; - - skb_reset_network_header(skb); - - if (ax25->modulus == AX25_MODULUS) { - frame = skb_push(skb, 1); - - *frame = AX25_I; - *frame |= (poll_bit) ? AX25_PF : 0; - *frame |= (ax25->vr << 5); - *frame |= (ax25->vs << 1); - } else { - frame = skb_push(skb, 2); - - frame[0] = AX25_I; - frame[0] |= (ax25->vs << 1); - frame[1] = (poll_bit) ? AX25_EPF : 0; - frame[1] |= (ax25->vr << 1); - } - - ax25_start_idletimer(ax25); - - ax25_transmit_buffer(ax25, skb, AX25_COMMAND); -} - -void ax25_kick(ax25_cb *ax25) -{ - struct sk_buff *skb, *skbn; - int last = 1; - unsigned short start, end, next; - - if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4) - return; - - if (ax25->condition & AX25_COND_PEER_RX_BUSY) - return; - - if (skb_peek(&ax25->write_queue) == NULL) - return; - - start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs; - end = (ax25->va + ax25->window) % ax25->modulus; - - if (start == end) - return; - - /* - * Transmit data until either we're out of data to send or - * the window is full. Send a poll on the final I frame if - * the window is filled. - */ - - /* - * Dequeue the frame and copy it. - * Check for race with ax25_clear_queues(). - */ - skb = skb_dequeue(&ax25->write_queue); - if (!skb) - return; - - ax25->vs = start; - - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&ax25->write_queue, skb); - break; - } - - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); - - next = (ax25->vs + 1) % ax25->modulus; - last = (next == end); - - /* - * Transmit the frame copy. - * bke 960114: do not set the Poll bit on the last frame - * in DAMA mode. - */ - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25_send_iframe(ax25, skbn, AX25_POLLOFF); - break; -#endif - } - - ax25->vs = next; - - /* - * Requeue the original data frame. - */ - skb_queue_tail(&ax25->ack_queue, skb); - - } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL); - - ax25->condition &= ~AX25_COND_ACK_PENDING; - - if (!ax25_t1timer_running(ax25)) { - ax25_stop_t3timer(ax25); - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - } -} - -void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - unsigned char *ptr; - int headroom; - - if (ax25->ax25_dev == NULL) { - ax25_disconnect(ax25, ENETUNREACH); - return; - } - - headroom = ax25_addr_size(ax25->digipeat); - - if (unlikely(skb_headroom(skb) < headroom)) { - skb = skb_expand_head(skb, headroom); - if (!skb) { - printk(KERN_CRIT "AX.25: ax25_transmit_buffer - out of memory\n"); - return; - } - } - - ptr = skb_push(skb, headroom); - - ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus); - - ax25_queue_xmit(skb, ax25->ax25_dev->dev); -} - -/* - * A small shim to dev_queue_xmit to add the KISS control byte, and do - * any packet forwarding in operation. - */ -void ax25_queue_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned char *ptr; - - rcu_read_lock(); - skb->protocol = ax25_type_trans(skb, ax25_fwd_dev(dev)); - rcu_read_unlock(); - - ptr = skb_push(skb, 1); - *ptr = 0x00; /* KISS */ - - dev_queue_xmit(skb); -} - -int ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) -{ - if (ax25->vs == nr) { - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - return 1; - } else { - if (ax25->va != nr) { - ax25_frames_acked(ax25, nr); - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - return 1; - } - } - return 0; -} diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c deleted file mode 100644 index 1d5c59ccf142..000000000000 --- a/net/ax25/ax25_route.c +++ /dev/null @@ -1,416 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) - * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static ax25_route *ax25_route_list; -DEFINE_RWLOCK(ax25_route_lock); - -void ax25_rt_device_down(struct net_device *dev) -{ - ax25_route *s, *t, *ax25_rt; - - write_lock_bh(&ax25_route_lock); - ax25_rt = ax25_route_list; - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; - - if (s->dev == dev) { - if (ax25_route_list == s) { - ax25_route_list = s->next; - kfree(s->digipeat); - kfree(s); - } else { - for (t = ax25_route_list; t != NULL; t = t->next) { - if (t->next == s) { - t->next = s->next; - kfree(s->digipeat); - kfree(s); - break; - } - } - } - } - } - write_unlock_bh(&ax25_route_lock); -} - -static int __must_check ax25_rt_add(struct ax25_routes_struct *route) -{ - ax25_route *ax25_rt; - ax25_dev *ax25_dev; - int i; - - if (route->digi_count > AX25_MAX_DIGIS) - return -EINVAL; - - ax25_dev = ax25_addr_ax25dev(&route->port_addr); - if (!ax25_dev) - return -EINVAL; - - write_lock_bh(&ax25_route_lock); - - ax25_rt = ax25_route_list; - while (ax25_rt != NULL) { - if (ax25cmp(&ax25_rt->callsign, &route->dest_addr) == 0 && - ax25_rt->dev == ax25_dev->dev) { - kfree(ax25_rt->digipeat); - ax25_rt->digipeat = NULL; - if (route->digi_count != 0) { - if ((ax25_rt->digipeat = kmalloc_obj(ax25_digi, GFP_ATOMIC)) == NULL) { - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - return -ENOMEM; - } - ax25_rt->digipeat->lastrepeat = -1; - ax25_rt->digipeat->ndigi = route->digi_count; - for (i = 0; i < route->digi_count; i++) { - ax25_rt->digipeat->repeated[i] = 0; - ax25_rt->digipeat->calls[i] = route->digi_addr[i]; - } - } - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - return 0; - } - ax25_rt = ax25_rt->next; - } - - if ((ax25_rt = kmalloc_obj(ax25_route, GFP_ATOMIC)) == NULL) { - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - return -ENOMEM; - } - - ax25_rt->callsign = route->dest_addr; - ax25_rt->dev = ax25_dev->dev; - ax25_rt->digipeat = NULL; - ax25_rt->ip_mode = ' '; - if (route->digi_count != 0) { - if ((ax25_rt->digipeat = kmalloc_obj(ax25_digi, GFP_ATOMIC)) == NULL) { - write_unlock_bh(&ax25_route_lock); - kfree(ax25_rt); - ax25_dev_put(ax25_dev); - return -ENOMEM; - } - ax25_rt->digipeat->lastrepeat = -1; - ax25_rt->digipeat->ndigi = route->digi_count; - for (i = 0; i < route->digi_count; i++) { - ax25_rt->digipeat->repeated[i] = 0; - ax25_rt->digipeat->calls[i] = route->digi_addr[i]; - } - } - ax25_rt->next = ax25_route_list; - ax25_route_list = ax25_rt; - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - - return 0; -} - -void __ax25_put_route(ax25_route *ax25_rt) -{ - kfree(ax25_rt->digipeat); - kfree(ax25_rt); -} - -static int ax25_rt_del(struct ax25_routes_struct *route) -{ - ax25_route *s, *t, *ax25_rt; - ax25_dev *ax25_dev; - - if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL) - return -EINVAL; - - write_lock_bh(&ax25_route_lock); - - ax25_rt = ax25_route_list; - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; - if (s->dev == ax25_dev->dev && - ax25cmp(&route->dest_addr, &s->callsign) == 0) { - if (ax25_route_list == s) { - ax25_route_list = s->next; - __ax25_put_route(s); - } else { - for (t = ax25_route_list; t != NULL; t = t->next) { - if (t->next == s) { - t->next = s->next; - __ax25_put_route(s); - break; - } - } - } - } - } - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - - return 0; -} - -static int ax25_rt_opt(struct ax25_route_opt_struct *rt_option) -{ - ax25_route *ax25_rt; - ax25_dev *ax25_dev; - int err = 0; - - if ((ax25_dev = ax25_addr_ax25dev(&rt_option->port_addr)) == NULL) - return -EINVAL; - - write_lock_bh(&ax25_route_lock); - - ax25_rt = ax25_route_list; - while (ax25_rt != NULL) { - if (ax25_rt->dev == ax25_dev->dev && - ax25cmp(&rt_option->dest_addr, &ax25_rt->callsign) == 0) { - switch (rt_option->cmd) { - case AX25_SET_RT_IPMODE: - switch (rt_option->arg) { - case ' ': - case 'D': - case 'V': - ax25_rt->ip_mode = rt_option->arg; - break; - default: - err = -EINVAL; - goto out; - } - break; - default: - err = -EINVAL; - goto out; - } - } - ax25_rt = ax25_rt->next; - } - -out: - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - return err; -} - -int ax25_rt_ioctl(unsigned int cmd, void __user *arg) -{ - struct ax25_route_opt_struct rt_option; - struct ax25_routes_struct route; - - switch (cmd) { - case SIOCADDRT: - if (copy_from_user(&route, arg, sizeof(route))) - return -EFAULT; - return ax25_rt_add(&route); - - case SIOCDELRT: - if (copy_from_user(&route, arg, sizeof(route))) - return -EFAULT; - return ax25_rt_del(&route); - - case SIOCAX25OPTRT: - if (copy_from_user(&rt_option, arg, sizeof(rt_option))) - return -EFAULT; - return ax25_rt_opt(&rt_option); - - default: - return -EINVAL; - } -} - -#ifdef CONFIG_PROC_FS - -static void *ax25_rt_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(ax25_route_lock) -{ - struct ax25_route *ax25_rt; - int i = 1; - - read_lock(&ax25_route_lock); - if (*pos == 0) - return SEQ_START_TOKEN; - - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (i == *pos) - return ax25_rt; - ++i; - } - - return NULL; -} - -static void *ax25_rt_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - return (v == SEQ_START_TOKEN) ? ax25_route_list : - ((struct ax25_route *) v)->next; -} - -static void ax25_rt_seq_stop(struct seq_file *seq, void *v) - __releases(ax25_route_lock) -{ - read_unlock(&ax25_route_lock); -} - -static int ax25_rt_seq_show(struct seq_file *seq, void *v) -{ - char buf[11]; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, "callsign dev mode digipeaters\n"); - else { - struct ax25_route *ax25_rt = v; - const char *callsign; - int i; - - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0) - callsign = "default"; - else - callsign = ax2asc(buf, &ax25_rt->callsign); - - seq_printf(seq, "%-9s %-4s", - callsign, - ax25_rt->dev ? ax25_rt->dev->name : "???"); - - switch (ax25_rt->ip_mode) { - case 'V': - seq_puts(seq, " vc"); - break; - case 'D': - seq_puts(seq, " dg"); - break; - default: - seq_puts(seq, " *"); - break; - } - - if (ax25_rt->digipeat != NULL) - for (i = 0; i < ax25_rt->digipeat->ndigi; i++) - seq_printf(seq, " %s", - ax2asc(buf, &ax25_rt->digipeat->calls[i])); - - seq_puts(seq, "\n"); - } - return 0; -} - -const struct seq_operations ax25_rt_seqops = { - .start = ax25_rt_seq_start, - .next = ax25_rt_seq_next, - .stop = ax25_rt_seq_stop, - .show = ax25_rt_seq_show, -}; -#endif - -/* - * Find AX.25 route - * - * Only routes with a reference count of zero can be destroyed. - * Must be called with ax25_route_lock read locked. - */ -ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev) -{ - ax25_route *ax25_spe_rt = NULL; - ax25_route *ax25_def_rt = NULL; - ax25_route *ax25_rt; - - /* - * Bind to the physical interface we heard them on, or the default - * route if none is found; - */ - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (dev == NULL) { - if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL) - ax25_spe_rt = ax25_rt; - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL) - ax25_def_rt = ax25_rt; - } else { - if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev) - ax25_spe_rt = ax25_rt; - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev) - ax25_def_rt = ax25_rt; - } - } - - ax25_rt = ax25_def_rt; - if (ax25_spe_rt != NULL) - ax25_rt = ax25_spe_rt; - - return ax25_rt; -} - - -struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, - ax25_address *dest, ax25_digi *digi) -{ - unsigned char *bp; - int len; - - len = digi->ndigi * AX25_ADDR_LEN; - - if (unlikely(skb_headroom(skb) < len)) { - skb = skb_expand_head(skb, len); - if (!skb) { - printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n"); - return NULL; - } - } - - bp = skb_push(skb, len); - - ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS); - - return skb; -} - -/* - * Free all memory associated with routing structures. - */ -void __exit ax25_rt_free(void) -{ - ax25_route *s, *ax25_rt = ax25_route_list; - - write_lock_bh(&ax25_route_lock); - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; - - kfree(s->digipeat); - kfree(s); - } - write_unlock_bh(&ax25_route_lock); -} diff --git a/net/ax25/ax25_std_in.c b/net/ax25/ax25_std_in.c deleted file mode 100644 index ba176196ae06..000000000000 --- a/net/ax25/ax25_std_in.c +++ /dev/null @@ -1,443 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) - * - * Most of this code is based on the SDL diagrams published in the 7th ARRL - * Computer Networking Conference papers. The diagrams have mistakes in them, - * but are mostly correct. Before you modify the code could you read the SDL - * diagrams as the code is not obvious and probably very easy to break. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * State machine for state 1, Awaiting Connection State. - * The handling of the timer(s) is in file ax25_std_timer.c. - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_SABME: - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_UA: - if (pf) { - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - if (ax25->sk != NULL) { - bh_lock_sock(ax25->sk); - ax25->sk->sk_state = TCP_ESTABLISHED; - /* For WAIT_SABM connections we will produce an accept ready socket here */ - if (!sock_flag(ax25->sk, SOCK_DEAD)) - ax25->sk->sk_state_change(ax25->sk); - bh_unlock_sock(ax25->sk); - } - } - break; - - case AX25_DM: - if (pf) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ECONNREFUSED); - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } - } - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 2, Awaiting Release State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - case AX25_UA: - if (pf) - ax25_disconnect(ax25, 0); - break; - - case AX25_I: - case AX25_REJ: - case AX25_RNR: - case AX25_RR: - if (pf) ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25_requeue_frames(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_check_iframes_acked(ax25, nr); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_requeue_frames(ax25); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - if (ax25->condition & AX25_COND_PEER_RX_BUSY) { - ax25_frames_acked(ax25, nr); - } else { - ax25_check_iframes_acked(ax25, nr); - } - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) ax25_std_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_std_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_std_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_std_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * State machine for state 4, Timer Recovery State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - ax25_requeue_frames(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - if (type == AX25_RESPONSE && pf) { - ax25_stop_t1timer(ax25); - ax25->n2count = 0; - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - if (ax25->vs == ax25->va) { - ax25_start_t3timer(ax25); - ax25->state = AX25_STATE_3; - } else { - ax25_requeue_frames(ax25); - } - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - } - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - if (pf && type == AX25_RESPONSE) { - ax25_stop_t1timer(ax25); - ax25->n2count = 0; - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - if (ax25->vs == ax25->va) { - ax25_start_t3timer(ax25); - ax25->state = AX25_STATE_3; - } else { - ax25_requeue_frames(ax25); - } - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - } - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - ax25_requeue_frames(ax25); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - ax25_frames_acked(ax25, nr); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) - ax25_std_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_std_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_std_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_std_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * Higher level upcall for a LAPB frame - */ -int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - int queued = 0, frametype, ns, nr, pf; - - frametype = ax25_decode(ax25, skb, &ns, &nr, &pf); - - switch (ax25->state) { - case AX25_STATE_1: - queued = ax25_std_state1_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_2: - queued = ax25_std_state2_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_3: - queued = ax25_std_state3_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - case AX25_STATE_4: - queued = ax25_std_state4_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - } - - ax25_kick(ax25); - - return queued; -} diff --git a/net/ax25/ax25_std_subr.c b/net/ax25/ax25_std_subr.c deleted file mode 100644 index 4c36f1342558..000000000000 --- a/net/ax25/ax25_std_subr.c +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * The following routines are taken from page 170 of the 7th ARRL Computer - * Networking Conference paper, as is the whole state machine. - */ - -void ax25_std_nr_error_recovery(ax25_cb *ax25) -{ - ax25_std_establish_data_link(ax25); -} - -void ax25_std_establish_data_link(ax25_cb *ax25) -{ - ax25->condition = 0x00; - ax25->n2count = 0; - - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); - - ax25_calculate_t1(ax25); - ax25_stop_idletimer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t1timer(ax25); -} - -void ax25_std_transmit_enquiry(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND); - - ax25->condition &= ~AX25_COND_ACK_PENDING; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} - -void ax25_std_enquiry_response(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE); - - ax25->condition &= ~AX25_COND_ACK_PENDING; -} - -void ax25_std_timeout_response(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE); - - ax25->condition &= ~AX25_COND_ACK_PENDING; -} diff --git a/net/ax25/ax25_std_timer.c b/net/ax25/ax25_std_timer.c deleted file mode 100644 index b17da41210cb..000000000000 --- a/net/ax25/ax25_std_timer.c +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void ax25_std_heartbeat_expiry(ax25_cb *ax25) -{ - struct sock *sk = ax25->sk; - - if (sk) - bh_lock_sock(sk); - - switch (ax25->state) { - case AX25_STATE_0: - case AX25_STATE_2: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (!sk || sock_flag(sk, SOCK_DESTROY) || - (sk->sk_state == TCP_LISTEN && - sock_flag(sk, SOCK_DEAD))) { - if (sk) { - sock_hold(sk); - ax25_destroy_socket(ax25); - bh_unlock_sock(sk); - /* Ungrab socket and destroy it */ - sock_put(sk); - } else - ax25_destroy_socket(ax25); - return; - } - break; - - case AX25_STATE_3: - case AX25_STATE_4: - /* - * Check the state of the receive buffer. - */ - if (sk != NULL) { - if (atomic_read(&sk->sk_rmem_alloc) < - (sk->sk_rcvbuf >> 1) && - (ax25->condition & AX25_COND_OWN_RX_BUSY)) { - ax25->condition &= ~AX25_COND_OWN_RX_BUSY; - ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE); - break; - } - } - } - - if (sk) - bh_unlock_sock(sk); - - ax25_start_heartbeat(ax25); -} - -void ax25_std_t2timer_expiry(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_ACK_PENDING) { - ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25_std_timeout_response(ax25); - } -} - -void ax25_std_t3timer_expiry(ax25_cb *ax25) -{ - ax25->n2count = 0; - ax25_std_transmit_enquiry(ax25); - ax25->state = AX25_STATE_4; -} - -void ax25_std_idletimer_expiry(ax25_cb *ax25) -{ - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25->state = AX25_STATE_2; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - - if (ax25->sk != NULL) { - bh_lock_sock(ax25->sk); - ax25->sk->sk_state = TCP_CLOSE; - ax25->sk->sk_err = 0; - ax25->sk->sk_shutdown |= SEND_SHUTDOWN; - if (!sock_flag(ax25->sk, SOCK_DEAD)) { - ax25->sk->sk_state_change(ax25->sk); - sock_set_flag(ax25->sk, SOCK_DEAD); - } - bh_unlock_sock(ax25->sk); - } -} - -void ax25_std_t1timer_expiry(ax25_cb *ax25) -{ - switch (ax25->state) { - case AX25_STATE_1: - if (ax25->n2count == ax25->n2) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25->n2count = 0; - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - } - } else { - ax25->n2count++; - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); - } - break; - - case AX25_STATE_2: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - if (!sock_flag(ax25->sk, SOCK_DESTROY)) - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - } - break; - - case AX25_STATE_3: - ax25->n2count = 1; - ax25_std_transmit_enquiry(ax25); - ax25->state = AX25_STATE_4; - break; - - case AX25_STATE_4: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - ax25_std_transmit_enquiry(ax25); - } - break; - } - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c deleted file mode 100644 index bff4b203a893..000000000000 --- a/net/ax25/ax25_subr.c +++ /dev/null @@ -1,296 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * This routine purges all the queues of frames. - */ -void ax25_clear_queues(ax25_cb *ax25) -{ - skb_queue_purge(&ax25->write_queue); - skb_queue_purge(&ax25->ack_queue); - skb_queue_purge(&ax25->reseq_queue); - skb_queue_purge(&ax25->frag_queue); -} - -/* - * This routine purges the input queue of those frames that have been - * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the - * SDL diagram. - */ -void ax25_frames_acked(ax25_cb *ax25, unsigned short nr) -{ - struct sk_buff *skb; - - /* - * Remove all the ack-ed frames from the ack queue. - */ - if (ax25->va != nr) { - while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) { - skb = skb_dequeue(&ax25->ack_queue); - kfree_skb(skb); - ax25->va = (ax25->va + 1) % ax25->modulus; - } - } -} - -void ax25_requeue_frames(ax25_cb *ax25) -{ - struct sk_buff *skb; - - /* - * Requeue all the un-ack-ed frames on the output queue to be picked - * up by ax25_kick called from the timer. This arrangement handles the - * possibility of an empty output queue. - */ - while ((skb = skb_dequeue_tail(&ax25->ack_queue)) != NULL) - skb_queue_head(&ax25->write_queue, skb); -} - -/* - * Validate that the value of nr is between va and vs. Return true or - * false for testing. - */ -int ax25_validate_nr(ax25_cb *ax25, unsigned short nr) -{ - unsigned short vc = ax25->va; - - while (vc != ax25->vs) { - if (nr == vc) return 1; - vc = (vc + 1) % ax25->modulus; - } - - if (nr == ax25->vs) return 1; - - return 0; -} - -/* - * This routine is the centralised routine for parsing the control - * information for the different frame formats. - */ -int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf) -{ - unsigned char *frame; - int frametype = AX25_ILLEGAL; - - frame = skb->data; - *ns = *nr = *pf = 0; - - if (ax25->modulus == AX25_MODULUS) { - if ((frame[0] & AX25_S) == 0) { - frametype = AX25_I; /* I frame - carries NR/NS/PF */ - *ns = (frame[0] >> 1) & 0x07; - *nr = (frame[0] >> 5) & 0x07; - *pf = frame[0] & AX25_PF; - } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ - frametype = frame[0] & 0x0F; - *nr = (frame[0] >> 5) & 0x07; - *pf = frame[0] & AX25_PF; - } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ - frametype = frame[0] & ~AX25_PF; - *pf = frame[0] & AX25_PF; - } - skb_pull(skb, 1); - } else { - if ((frame[0] & AX25_S) == 0) { - frametype = AX25_I; /* I frame - carries NR/NS/PF */ - *ns = (frame[0] >> 1) & 0x7F; - *nr = (frame[1] >> 1) & 0x7F; - *pf = frame[1] & AX25_EPF; - skb_pull(skb, 2); - } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ - frametype = frame[0] & 0x0F; - *nr = (frame[1] >> 1) & 0x7F; - *pf = frame[1] & AX25_EPF; - skb_pull(skb, 2); - } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ - frametype = frame[0] & ~AX25_PF; - *pf = frame[0] & AX25_PF; - skb_pull(skb, 1); - } - } - - return frametype; -} - -/* - * This routine is called when the HDLC layer internally generates a - * command or response for the remote machine ( eg. RR, UA etc. ). - * Only supervisory or unnumbered frames are processed. - */ -void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type) -{ - struct sk_buff *skb; - unsigned char *dptr; - - if ((skb = alloc_skb(ax25->ax25_dev->dev->hard_header_len + 2, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skb, ax25->ax25_dev->dev->hard_header_len); - - skb_reset_network_header(skb); - - /* Assume a response - address structure for DTE */ - if (ax25->modulus == AX25_MODULUS) { - dptr = skb_put(skb, 1); - *dptr = frametype; - *dptr |= (poll_bit) ? AX25_PF : 0; - if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */ - *dptr |= (ax25->vr << 5); - } else { - if ((frametype & AX25_U) == AX25_U) { - dptr = skb_put(skb, 1); - *dptr = frametype; - *dptr |= (poll_bit) ? AX25_PF : 0; - } else { - dptr = skb_put(skb, 2); - dptr[0] = frametype; - dptr[1] = (ax25->vr << 1); - dptr[1] |= (poll_bit) ? AX25_EPF : 0; - } - } - - ax25_transmit_buffer(ax25, skb, type); -} - -/* - * Send a 'DM' to an unknown connection attempt, or an invalid caller. - * - * Note: src here is the sender, thus it's the target of the DM - */ -void ax25_return_dm(struct net_device *dev, ax25_address *src, ax25_address *dest, ax25_digi *digi) -{ - struct sk_buff *skb; - char *dptr; - ax25_digi retdigi; - - if (dev == NULL) - return; - - if ((skb = alloc_skb(dev->hard_header_len + 1, GFP_ATOMIC)) == NULL) - return; /* Next SABM will get DM'd */ - - skb_reserve(skb, dev->hard_header_len); - skb_reset_network_header(skb); - - ax25_digi_invert(digi, &retdigi); - - dptr = skb_put(skb, 1); - - *dptr = AX25_DM | AX25_PF; - - /* - * Do the address ourselves - */ - dptr = skb_push(skb, ax25_addr_size(digi)); - dptr += ax25_addr_build(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS); - - ax25_queue_xmit(skb, dev); -} - -/* - * Exponential backoff for AX.25 - */ -void ax25_calculate_t1(ax25_cb *ax25) -{ - int n, t = 2; - - switch (ax25->backoff) { - case 0: - break; - - case 1: - t += 2 * ax25->n2count; - break; - - case 2: - for (n = 0; n < ax25->n2count; n++) - t *= 2; - if (t > 8) t = 8; - break; - } - - ax25->t1 = t * ax25->rtt; -} - -/* - * Calculate the Round Trip Time - */ -void ax25_calculate_rtt(ax25_cb *ax25) -{ - if (ax25->backoff == 0) - return; - - if (ax25_t1timer_running(ax25) && ax25->n2count == 0) - ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25_display_timer(&ax25->t1timer)) / 10; - - if (ax25->rtt < AX25_T1CLAMPLO) - ax25->rtt = AX25_T1CLAMPLO; - - if (ax25->rtt > AX25_T1CLAMPHI) - ax25->rtt = AX25_T1CLAMPHI; -} - -void ax25_disconnect(ax25_cb *ax25, int reason) -{ - ax25_clear_queues(ax25); - - if (reason == ENETUNREACH) { - timer_delete_sync(&ax25->timer); - timer_delete_sync(&ax25->t1timer); - timer_delete_sync(&ax25->t2timer); - timer_delete_sync(&ax25->t3timer); - timer_delete_sync(&ax25->idletimer); - } else { - if (ax25->sk && !sock_flag(ax25->sk, SOCK_DESTROY)) - ax25_stop_heartbeat(ax25); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); - } - - ax25->state = AX25_STATE_0; - - ax25_link_failed(ax25, reason); - - if (ax25->sk != NULL) { - local_bh_disable(); - bh_lock_sock(ax25->sk); - ax25->sk->sk_state = TCP_CLOSE; - ax25->sk->sk_err = reason; - ax25->sk->sk_shutdown |= SEND_SHUTDOWN; - if (!sock_flag(ax25->sk, SOCK_DEAD)) { - ax25->sk->sk_state_change(ax25->sk); - sock_set_flag(ax25->sk, SOCK_DEAD); - } - bh_unlock_sock(ax25->sk); - local_bh_enable(); - } -} diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c deleted file mode 100644 index a69bfbc8b679..000000000000 --- a/net/ax25/ax25_timer.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Tomi Manninen OH2BNS (oh2bns@sral.fi) - * Copyright (C) Darryl Miles G7LED (dlm@g7led.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) - * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void ax25_heartbeat_expiry(struct timer_list *); -static void ax25_t1timer_expiry(struct timer_list *); -static void ax25_t2timer_expiry(struct timer_list *); -static void ax25_t3timer_expiry(struct timer_list *); -static void ax25_idletimer_expiry(struct timer_list *); - -void ax25_setup_timers(ax25_cb *ax25) -{ - timer_setup(&ax25->timer, ax25_heartbeat_expiry, 0); - timer_setup(&ax25->t1timer, ax25_t1timer_expiry, 0); - timer_setup(&ax25->t2timer, ax25_t2timer_expiry, 0); - timer_setup(&ax25->t3timer, ax25_t3timer_expiry, 0); - timer_setup(&ax25->idletimer, ax25_idletimer_expiry, 0); -} - -void ax25_start_heartbeat(ax25_cb *ax25) -{ - mod_timer(&ax25->timer, jiffies + 5 * HZ); -} - -void ax25_start_t1timer(ax25_cb *ax25) -{ - mod_timer(&ax25->t1timer, jiffies + ax25->t1); -} - -void ax25_start_t2timer(ax25_cb *ax25) -{ - mod_timer(&ax25->t2timer, jiffies + ax25->t2); -} - -void ax25_start_t3timer(ax25_cb *ax25) -{ - if (ax25->t3 > 0) - mod_timer(&ax25->t3timer, jiffies + ax25->t3); - else - timer_delete(&ax25->t3timer); -} - -void ax25_start_idletimer(ax25_cb *ax25) -{ - if (ax25->idle > 0) - mod_timer(&ax25->idletimer, jiffies + ax25->idle); - else - timer_delete(&ax25->idletimer); -} - -void ax25_stop_heartbeat(ax25_cb *ax25) -{ - timer_delete(&ax25->timer); -} - -void ax25_stop_t1timer(ax25_cb *ax25) -{ - timer_delete(&ax25->t1timer); -} - -void ax25_stop_t2timer(ax25_cb *ax25) -{ - timer_delete(&ax25->t2timer); -} - -void ax25_stop_t3timer(ax25_cb *ax25) -{ - timer_delete(&ax25->t3timer); -} - -void ax25_stop_idletimer(ax25_cb *ax25) -{ - timer_delete(&ax25->idletimer); -} - -int ax25_t1timer_running(ax25_cb *ax25) -{ - return timer_pending(&ax25->t1timer); -} - -unsigned long ax25_display_timer(struct timer_list *timer) -{ - long delta = timer->expires - jiffies; - - if (!timer_pending(timer)) - return 0; - - return max(0L, delta); -} - -EXPORT_SYMBOL(ax25_display_timer); - -static void ax25_heartbeat_expiry(struct timer_list *t) -{ - int proto = AX25_PROTO_STD_SIMPLEX; - ax25_cb *ax25 = timer_container_of(ax25, t, timer); - - if (ax25->ax25_dev) - proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]; - - switch (proto) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_heartbeat_expiry(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_heartbeat_expiry(ax25); - else - ax25_std_heartbeat_expiry(ax25); - break; -#endif - } -} - -static void ax25_t1timer_expiry(struct timer_list *t) -{ - ax25_cb *ax25 = timer_container_of(ax25, t, t1timer); - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t1timer_expiry(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) - ax25_std_t1timer_expiry(ax25); - break; -#endif - } -} - -static void ax25_t2timer_expiry(struct timer_list *t) -{ - ax25_cb *ax25 = timer_container_of(ax25, t, t2timer); - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t2timer_expiry(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) - ax25_std_t2timer_expiry(ax25); - break; -#endif - } -} - -static void ax25_t3timer_expiry(struct timer_list *t) -{ - ax25_cb *ax25 = timer_container_of(ax25, t, t3timer); - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t3timer_expiry(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_t3timer_expiry(ax25); - else - ax25_std_t3timer_expiry(ax25); - break; -#endif - } -} - -static void ax25_idletimer_expiry(struct timer_list *t) -{ - ax25_cb *ax25 = timer_container_of(ax25, t, idletimer); - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_idletimer_expiry(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_idletimer_expiry(ax25); - else - ax25_std_idletimer_expiry(ax25); - break; -#endif - } -} diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c deleted file mode 100644 index 159ce74273f0..000000000000 --- a/net/ax25/ax25_uid.c +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Callsign/UID mapper. This is in kernel space for security on multi-amateur machines. - */ - -static HLIST_HEAD(ax25_uid_list); -static DEFINE_RWLOCK(ax25_uid_lock); - -int ax25_uid_policy; - -EXPORT_SYMBOL(ax25_uid_policy); - -ax25_uid_assoc *ax25_findbyuid(kuid_t uid) -{ - ax25_uid_assoc *ax25_uid, *res = NULL; - - read_lock(&ax25_uid_lock); - ax25_uid_for_each(ax25_uid, &ax25_uid_list) { - if (uid_eq(ax25_uid->uid, uid)) { - ax25_uid_hold(ax25_uid); - res = ax25_uid; - break; - } - } - read_unlock(&ax25_uid_lock); - - return res; -} - -EXPORT_SYMBOL(ax25_findbyuid); - -int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) -{ - ax25_uid_assoc *ax25_uid; - ax25_uid_assoc *user; - unsigned long res; - - switch (cmd) { - case SIOCAX25GETUID: - res = -ENOENT; - read_lock(&ax25_uid_lock); - ax25_uid_for_each(ax25_uid, &ax25_uid_list) { - if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) { - res = from_kuid_munged(current_user_ns(), ax25_uid->uid); - break; - } - } - read_unlock(&ax25_uid_lock); - - return res; - - case SIOCAX25ADDUID: - { - kuid_t sax25_kuid; - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - sax25_kuid = make_kuid(current_user_ns(), sax->sax25_uid); - if (!uid_valid(sax25_kuid)) - return -EINVAL; - user = ax25_findbyuid(sax25_kuid); - if (user) { - ax25_uid_put(user); - return -EEXIST; - } - if (sax->sax25_uid == 0) - return -EINVAL; - if ((ax25_uid = kmalloc_obj(*ax25_uid)) == NULL) - return -ENOMEM; - - refcount_set(&ax25_uid->refcount, 1); - ax25_uid->uid = sax25_kuid; - ax25_uid->call = sax->sax25_call; - - write_lock(&ax25_uid_lock); - hlist_add_head(&ax25_uid->uid_node, &ax25_uid_list); - write_unlock(&ax25_uid_lock); - - return 0; - } - case SIOCAX25DELUID: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - ax25_uid = NULL; - write_lock(&ax25_uid_lock); - ax25_uid_for_each(ax25_uid, &ax25_uid_list) { - if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) - break; - } - if (ax25_uid == NULL) { - write_unlock(&ax25_uid_lock); - return -ENOENT; - } - hlist_del_init(&ax25_uid->uid_node); - ax25_uid_put(ax25_uid); - write_unlock(&ax25_uid_lock); - - return 0; - - default: - return -EINVAL; - } - - return -EINVAL; /*NOTREACHED */ -} - -#ifdef CONFIG_PROC_FS - -static void *ax25_uid_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(ax25_uid_lock) -{ - read_lock(&ax25_uid_lock); - return seq_hlist_start_head(&ax25_uid_list, *pos); -} - -static void *ax25_uid_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &ax25_uid_list, pos); -} - -static void ax25_uid_seq_stop(struct seq_file *seq, void *v) - __releases(ax25_uid_lock) -{ - read_unlock(&ax25_uid_lock); -} - -static int ax25_uid_seq_show(struct seq_file *seq, void *v) -{ - char buf[11]; - - if (v == SEQ_START_TOKEN) - seq_printf(seq, "Policy: %d\n", ax25_uid_policy); - else { - struct ax25_uid_assoc *pt; - - pt = hlist_entry(v, struct ax25_uid_assoc, uid_node); - seq_printf(seq, "%6d %s\n", - from_kuid_munged(seq_user_ns(seq), pt->uid), - ax2asc(buf, &pt->call)); - } - return 0; -} - -const struct seq_operations ax25_uid_seqops = { - .start = ax25_uid_seq_start, - .next = ax25_uid_seq_next, - .stop = ax25_uid_seq_stop, - .show = ax25_uid_seq_show, -}; -#endif - -/* - * Free all memory associated with UID/Callsign structures. - */ -void __exit ax25_uid_free(void) -{ - ax25_uid_assoc *ax25_uid; - - write_lock(&ax25_uid_lock); -again: - ax25_uid_for_each(ax25_uid, &ax25_uid_list) { - hlist_del_init(&ax25_uid->uid_node); - ax25_uid_put(ax25_uid); - goto again; - } - write_unlock(&ax25_uid_lock); -} diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c deleted file mode 100644 index 68753aa30334..000000000000 --- a/net/ax25/sysctl_net_ax25.c +++ /dev/null @@ -1,181 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com) - */ -#include -#include -#include -#include -#include - -static int min_ipdefmode[1], max_ipdefmode[] = {1}; -static int min_axdefmode[1], max_axdefmode[] = {1}; -static int min_backoff[1], max_backoff[] = {2}; -static int min_conmode[1], max_conmode[] = {2}; -static int min_window[] = {1}, max_window[] = {7}; -static int min_ewindow[] = {1}, max_ewindow[] = {63}; -static int min_t1[] = {1}, max_t1[] = {30000}; -static int min_t2[] = {1}, max_t2[] = {20000}; -static int min_t3[1], max_t3[] = {3600000}; -static int min_idle[1], max_idle[] = {65535000}; -static int min_n2[] = {1}, max_n2[] = {31}; -static int min_paclen[] = {1}, max_paclen[] = {512}; -static int min_proto[1], max_proto[] = { AX25_PROTO_MAX }; -#ifdef CONFIG_AX25_DAMA_SLAVE -static int min_ds_timeout[1], max_ds_timeout[] = {65535000}; -#endif - -static const struct ctl_table ax25_param_table[] = { - { - .procname = "ip_default_mode", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_ipdefmode, - .extra2 = &max_ipdefmode - }, - { - .procname = "ax25_default_mode", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_axdefmode, - .extra2 = &max_axdefmode - }, - { - .procname = "backoff_type", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_backoff, - .extra2 = &max_backoff - }, - { - .procname = "connect_mode", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_conmode, - .extra2 = &max_conmode - }, - { - .procname = "standard_window_size", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_window, - .extra2 = &max_window - }, - { - .procname = "extended_window_size", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_ewindow, - .extra2 = &max_ewindow - }, - { - .procname = "t1_timeout", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t1, - .extra2 = &max_t1 - }, - { - .procname = "t2_timeout", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t2, - .extra2 = &max_t2 - }, - { - .procname = "t3_timeout", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t3, - .extra2 = &max_t3 - }, - { - .procname = "idle_timeout", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_idle, - .extra2 = &max_idle - }, - { - .procname = "maximum_retry_count", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_n2, - .extra2 = &max_n2 - }, - { - .procname = "maximum_packet_length", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_paclen, - .extra2 = &max_paclen - }, - { - .procname = "protocol", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_proto, - .extra2 = &max_proto - }, -#ifdef CONFIG_AX25_DAMA_SLAVE - { - .procname = "dama_slave_timeout", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_ds_timeout, - .extra2 = &max_ds_timeout - }, -#endif -}; - -int ax25_register_dev_sysctl(ax25_dev *ax25_dev) -{ - char path[sizeof("net/ax25/") + IFNAMSIZ]; - int k; - struct ctl_table *table; - - table = kmemdup(ax25_param_table, sizeof(ax25_param_table), GFP_KERNEL); - if (!table) - return -ENOMEM; - - BUILD_BUG_ON(ARRAY_SIZE(ax25_param_table) != AX25_MAX_VALUES); - for (k = 0; k < AX25_MAX_VALUES; k++) - table[k].data = &ax25_dev->values[k]; - - snprintf(path, sizeof(path), "net/ax25/%s", ax25_dev->dev->name); - ax25_dev->sysheader = register_net_sysctl_sz(&init_net, path, table, - ARRAY_SIZE(ax25_param_table)); - if (!ax25_dev->sysheader) { - kfree(table); - return -ENOMEM; - } - return 0; -} - -void ax25_unregister_dev_sysctl(ax25_dev *ax25_dev) -{ - struct ctl_table_header *header = ax25_dev->sysheader; - const struct ctl_table *table; - - if (header) { - ax25_dev->sysheader = NULL; - table = header->ctl_table_arg; - unregister_net_sysctl_table(header); - kfree(table); - } -} diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 51d70180e1cc..d409f606aec0 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -109,7 +109,6 @@ #include #include #include -#include #include #include diff --git a/net/netrom/Makefile b/net/netrom/Makefile deleted file mode 100644 index 603e36c9af2e..000000000000 --- a/net/netrom/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Linux NET/ROM layer. -# - -obj-$(CONFIG_NETROM) += netrom.o - -netrom-y := af_netrom.o nr_dev.o nr_in.o nr_loopback.o \ - nr_out.o nr_route.o nr_subr.o nr_timer.o -netrom-$(CONFIG_SYSCTL) += sysctl_net_netrom.o diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c deleted file mode 100644 index 5fc54836dfa8..000000000000 --- a/net/netrom/af_netrom.c +++ /dev/null @@ -1,1536 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For TIOCINQ/OUTQ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int nr_ndevs = 4; - -int sysctl_netrom_default_path_quality = NR_DEFAULT_QUAL; -int sysctl_netrom_obsolescence_count_initialiser = NR_DEFAULT_OBS; -int sysctl_netrom_network_ttl_initialiser = NR_DEFAULT_TTL; -int sysctl_netrom_transport_timeout = NR_DEFAULT_T1; -int sysctl_netrom_transport_maximum_tries = NR_DEFAULT_N2; -int sysctl_netrom_transport_acknowledge_delay = NR_DEFAULT_T2; -int sysctl_netrom_transport_busy_delay = NR_DEFAULT_T4; -int sysctl_netrom_transport_requested_window_size = NR_DEFAULT_WINDOW; -int sysctl_netrom_transport_no_activity_timeout = NR_DEFAULT_IDLE; -int sysctl_netrom_routing_control = NR_DEFAULT_ROUTING; -int sysctl_netrom_link_fails_count = NR_DEFAULT_FAILS; -int sysctl_netrom_reset_circuit = NR_DEFAULT_RESET; - -static unsigned short circuit = 0x101; - -static HLIST_HEAD(nr_list); -static DEFINE_SPINLOCK(nr_list_lock); - -static const struct proto_ops nr_proto_ops; - -/* - * NETROM network devices are virtual network devices encapsulating NETROM - * frames into AX.25 which will be sent through an AX.25 device, so form a - * special "super class" of normal net devices; split their locks off into a - * separate class since they always nest. - */ -static struct lock_class_key nr_netdev_xmit_lock_key; -static struct lock_class_key nr_netdev_addr_lock_key; - -static void nr_set_lockdep_one(struct net_device *dev, - struct netdev_queue *txq, - void *_unused) -{ - lockdep_set_class(&txq->_xmit_lock, &nr_netdev_xmit_lock_key); -} - -static void nr_set_lockdep_key(struct net_device *dev) -{ - lockdep_set_class(&dev->addr_list_lock, &nr_netdev_addr_lock_key); - netdev_for_each_tx_queue(dev, nr_set_lockdep_one, NULL); -} - -/* - * Socket removal during an interrupt is now safe. - */ -static void nr_remove_socket(struct sock *sk) -{ - spin_lock_bh(&nr_list_lock); - sk_del_node_init(sk); - spin_unlock_bh(&nr_list_lock); -} - -/* - * Kill all bound sockets on a dropped device. - */ -static void nr_kill_by_device(struct net_device *dev) -{ - struct sock *s; - - spin_lock_bh(&nr_list_lock); - sk_for_each(s, &nr_list) - if (nr_sk(s)->device == dev) - nr_disconnect(s, ENETUNREACH); - spin_unlock_bh(&nr_list_lock); -} - -/* - * Handle device status changes. - */ -static int nr_device_event(struct notifier_block *this, unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event != NETDEV_DOWN) - return NOTIFY_DONE; - - nr_kill_by_device(dev); - nr_rt_device_down(dev); - - return NOTIFY_DONE; -} - -/* - * Add a socket to the bound sockets list. - */ -static void nr_insert_socket(struct sock *sk) -{ - spin_lock_bh(&nr_list_lock); - sk_add_node(sk, &nr_list); - spin_unlock_bh(&nr_list_lock); -} - -/* - * Find a socket that wants to accept the Connect Request we just - * received. - */ -static struct sock *nr_find_listener(ax25_address *addr) -{ - struct sock *s; - - spin_lock_bh(&nr_list_lock); - sk_for_each(s, &nr_list) - if (!ax25cmp(&nr_sk(s)->source_addr, addr) && - s->sk_state == TCP_LISTEN) { - sock_hold(s); - goto found; - } - s = NULL; -found: - spin_unlock_bh(&nr_list_lock); - return s; -} - -/* - * Find a connected NET/ROM socket given my circuit IDs. - */ -static struct sock *nr_find_socket(unsigned char index, unsigned char id) -{ - struct sock *s; - - spin_lock_bh(&nr_list_lock); - sk_for_each(s, &nr_list) { - struct nr_sock *nr = nr_sk(s); - - if (nr->my_index == index && nr->my_id == id) { - sock_hold(s); - goto found; - } - } - s = NULL; -found: - spin_unlock_bh(&nr_list_lock); - return s; -} - -/* - * Find a connected NET/ROM socket given their circuit IDs. - */ -static struct sock *nr_find_peer(unsigned char index, unsigned char id, - ax25_address *dest) -{ - struct sock *s; - - spin_lock_bh(&nr_list_lock); - sk_for_each(s, &nr_list) { - struct nr_sock *nr = nr_sk(s); - - if (nr->your_index == index && nr->your_id == id && - !ax25cmp(&nr->dest_addr, dest)) { - sock_hold(s); - goto found; - } - } - s = NULL; -found: - spin_unlock_bh(&nr_list_lock); - return s; -} - -/* - * Find next free circuit ID. - */ -static unsigned short nr_find_next_circuit(void) -{ - unsigned short id = circuit; - unsigned char i, j; - struct sock *sk; - - for (;;) { - i = id / 256; - j = id % 256; - - if (i != 0 && j != 0) { - if ((sk=nr_find_socket(i, j)) == NULL) - break; - sock_put(sk); - } - - id++; - } - - return id; -} - -/* - * Deferred destroy. - */ -void nr_destroy_socket(struct sock *); - -/* - * Handler for deferred kills. - */ -static void nr_destroy_timer(struct timer_list *t) -{ - struct sock *sk = timer_container_of(sk, t, sk_timer); - bh_lock_sock(sk); - sock_hold(sk); - nr_destroy_socket(sk); - bh_unlock_sock(sk); - sock_put(sk); -} - -/* - * This is called from user mode and the timers. Thus it protects itself - * against interrupt users but doesn't worry about being called during - * work. Once it is removed from the queue no interrupt or bottom half - * will touch it and we are (fairly 8-) ) safe. - */ -void nr_destroy_socket(struct sock *sk) -{ - struct sk_buff *skb; - - nr_remove_socket(sk); - - nr_stop_heartbeat(sk); - nr_stop_t1timer(sk); - nr_stop_t2timer(sk); - nr_stop_t4timer(sk); - nr_stop_idletimer(sk); - - nr_clear_queues(sk); /* Flush the queues */ - - while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { - if (skb->sk != sk) { /* A pending connection */ - /* Queue the unaccepted socket for death */ - sock_set_flag(skb->sk, SOCK_DEAD); - nr_start_heartbeat(skb->sk); - nr_sk(skb->sk)->state = NR_STATE_0; - } - - kfree_skb(skb); - } - - if (sk_has_allocations(sk)) { - /* Defer: outstanding buffers */ - sk->sk_timer.function = nr_destroy_timer; - sk->sk_timer.expires = jiffies + 2 * HZ; - add_timer(&sk->sk_timer); - } else - sock_put(sk); -} - -/* - * Handling for system calls applied via the various interfaces to a - * NET/ROM socket object. - */ - -static int nr_setsockopt(struct socket *sock, int level, int optname, - sockptr_t optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - unsigned int opt; - - if (level != SOL_NETROM) - return -ENOPROTOOPT; - - if (optlen < sizeof(unsigned int)) - return -EINVAL; - - if (copy_from_sockptr(&opt, optval, sizeof(opt))) - return -EFAULT; - - switch (optname) { - case NETROM_T1: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - nr->t1 = opt * HZ; - return 0; - - case NETROM_T2: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - nr->t2 = opt * HZ; - return 0; - - case NETROM_N2: - if (opt < 1 || opt > 31) - return -EINVAL; - nr->n2 = opt; - return 0; - - case NETROM_T4: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - nr->t4 = opt * HZ; - return 0; - - case NETROM_IDLE: - if (opt > UINT_MAX / (60 * HZ)) - return -EINVAL; - nr->idle = opt * 60 * HZ; - return 0; - - default: - return -ENOPROTOOPT; - } -} - -static int nr_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - int val = 0; - int len; - - if (level != SOL_NETROM) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - if (len < 0) - return -EINVAL; - - switch (optname) { - case NETROM_T1: - val = nr->t1 / HZ; - break; - - case NETROM_T2: - val = nr->t2 / HZ; - break; - - case NETROM_N2: - val = nr->n2; - break; - - case NETROM_T4: - val = nr->t4 / HZ; - break; - - case NETROM_IDLE: - val = nr->idle / (60 * HZ); - break; - - default: - return -ENOPROTOOPT; - } - - len = min_t(unsigned int, len, sizeof(int)); - - if (put_user(len, optlen)) - return -EFAULT; - - return copy_to_user(optval, &val, len) ? -EFAULT : 0; -} - -static int nr_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - - lock_sock(sk); - if (sock->state != SS_UNCONNECTED) { - release_sock(sk); - return -EINVAL; - } - - if (sk->sk_state != TCP_LISTEN) { - memset(&nr_sk(sk)->user_addr, 0, AX25_ADDR_LEN); - sk->sk_max_ack_backlog = backlog; - sk->sk_state = TCP_LISTEN; - release_sock(sk); - return 0; - } - release_sock(sk); - - return -EOPNOTSUPP; -} - -static struct proto nr_proto = { - .name = "NETROM", - .owner = THIS_MODULE, - .obj_size = sizeof(struct nr_sock), -}; - -static int nr_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - struct nr_sock *nr; - - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - - if (sock->type != SOCK_SEQPACKET || protocol != 0) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_NETROM, GFP_ATOMIC, &nr_proto, kern); - if (sk == NULL) - return -ENOMEM; - - nr = nr_sk(sk); - - sock_init_data(sock, sk); - - sock->ops = &nr_proto_ops; - sk->sk_protocol = protocol; - - skb_queue_head_init(&nr->ack_queue); - skb_queue_head_init(&nr->reseq_queue); - skb_queue_head_init(&nr->frag_queue); - - nr_init_timers(sk); - - nr->t1 = - msecs_to_jiffies(READ_ONCE(sysctl_netrom_transport_timeout)); - nr->t2 = - msecs_to_jiffies(READ_ONCE(sysctl_netrom_transport_acknowledge_delay)); - nr->n2 = - msecs_to_jiffies(READ_ONCE(sysctl_netrom_transport_maximum_tries)); - nr->t4 = - msecs_to_jiffies(READ_ONCE(sysctl_netrom_transport_busy_delay)); - nr->idle = - msecs_to_jiffies(READ_ONCE(sysctl_netrom_transport_no_activity_timeout)); - nr->window = READ_ONCE(sysctl_netrom_transport_requested_window_size); - - nr->bpqext = 1; - nr->state = NR_STATE_0; - - return 0; -} - -static struct sock *nr_make_new(struct sock *osk) -{ - struct sock *sk; - struct nr_sock *nr, *onr; - - if (osk->sk_type != SOCK_SEQPACKET) - return NULL; - - sk = sk_alloc(sock_net(osk), PF_NETROM, GFP_ATOMIC, osk->sk_prot, 0); - if (sk == NULL) - return NULL; - - nr = nr_sk(sk); - - sock_init_data(NULL, sk); - - sk->sk_type = osk->sk_type; - sk->sk_priority = READ_ONCE(osk->sk_priority); - sk->sk_protocol = osk->sk_protocol; - sk->sk_rcvbuf = osk->sk_rcvbuf; - sk->sk_sndbuf = osk->sk_sndbuf; - sk->sk_state = TCP_ESTABLISHED; - sock_copy_flags(sk, osk); - - skb_queue_head_init(&nr->ack_queue); - skb_queue_head_init(&nr->reseq_queue); - skb_queue_head_init(&nr->frag_queue); - - nr_init_timers(sk); - - onr = nr_sk(osk); - - nr->t1 = onr->t1; - nr->t2 = onr->t2; - nr->n2 = onr->n2; - nr->t4 = onr->t4; - nr->idle = onr->idle; - nr->window = onr->window; - - nr->device = onr->device; - nr->bpqext = onr->bpqext; - - return sk; -} - -static int nr_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr; - - if (sk == NULL) return 0; - - sock_hold(sk); - sock_orphan(sk); - lock_sock(sk); - nr = nr_sk(sk); - - switch (nr->state) { - case NR_STATE_0: - case NR_STATE_1: - case NR_STATE_2: - nr_disconnect(sk, 0); - nr_destroy_socket(sk); - break; - - case NR_STATE_3: - nr_clear_queues(sk); - nr->n2count = 0; - nr_write_internal(sk, NR_DISCREQ); - nr_start_t1timer(sk); - nr_stop_t2timer(sk); - nr_stop_t4timer(sk); - nr_stop_idletimer(sk); - nr->state = NR_STATE_2; - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DESTROY); - break; - - default: - break; - } - - sock->sk = NULL; - release_sock(sk); - sock_put(sk); - - return 0; -} - -static int nr_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; - struct net_device *dev; - ax25_uid_assoc *user; - ax25_address *source; - - lock_sock(sk); - if (!sock_flag(sk, SOCK_ZAPPED)) { - release_sock(sk); - return -EINVAL; - } - if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25)) { - release_sock(sk); - return -EINVAL; - } - if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25))) { - release_sock(sk); - return -EINVAL; - } - if (addr->fsa_ax25.sax25_family != AF_NETROM) { - release_sock(sk); - return -EINVAL; - } - if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) { - release_sock(sk); - return -EADDRNOTAVAIL; - } - - /* - * Only the super user can set an arbitrary user callsign. - */ - if (addr->fsa_ax25.sax25_ndigis == 1) { - if (!capable(CAP_NET_BIND_SERVICE)) { - dev_put(dev); - release_sock(sk); - return -EPERM; - } - nr->user_addr = addr->fsa_digipeater[0]; - nr->source_addr = addr->fsa_ax25.sax25_call; - } else { - source = &addr->fsa_ax25.sax25_call; - - user = ax25_findbyuid(current_euid()); - if (user) { - nr->user_addr = user->call; - ax25_uid_put(user); - } else { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) { - release_sock(sk); - dev_put(dev); - return -EPERM; - } - nr->user_addr = *source; - } - - nr->source_addr = *source; - } - - nr->device = dev; - nr_insert_socket(sk); - - sock_reset_flag(sk, SOCK_ZAPPED); - dev_put(dev); - release_sock(sk); - - return 0; -} - -static int nr_connect(struct socket *sock, struct sockaddr_unsized *uaddr, - int addr_len, int flags) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr; - const ax25_address *source = NULL; - ax25_uid_assoc *user; - struct net_device *dev; - int err = 0; - - lock_sock(sk); - if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { - sock->state = SS_CONNECTED; - goto out_release; /* Connect completed during a ERESTARTSYS event */ - } - - if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) { - sock->state = SS_UNCONNECTED; - err = -ECONNREFUSED; - goto out_release; - } - - if (sk->sk_state == TCP_ESTABLISHED) { - err = -EISCONN; /* No reconnect on a seqpacket socket */ - goto out_release; - } - - if (sock->state == SS_CONNECTING) { - err = -EALREADY; - goto out_release; - } - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) { - err = -EINVAL; - goto out_release; - } - if (addr->sax25_family != AF_NETROM) { - err = -EINVAL; - goto out_release; - } - if (sock_flag(sk, SOCK_ZAPPED)) { /* Must bind first - autobinding in this may or may not work */ - sock_reset_flag(sk, SOCK_ZAPPED); - - if ((dev = nr_dev_first()) == NULL) { - err = -ENETUNREACH; - goto out_release; - } - source = (const ax25_address *)dev->dev_addr; - - user = ax25_findbyuid(current_euid()); - if (user) { - nr->user_addr = user->call; - ax25_uid_put(user); - } else { - if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) { - dev_put(dev); - err = -EPERM; - goto out_release; - } - nr->user_addr = *source; - } - - nr->source_addr = *source; - nr->device = dev; - - dev_put(dev); - nr_insert_socket(sk); /* Finish the bind */ - } - - nr->dest_addr = addr->sax25_call; - - release_sock(sk); - circuit = nr_find_next_circuit(); - lock_sock(sk); - - nr->my_index = circuit / 256; - nr->my_id = circuit % 256; - - circuit++; - - /* Move to connecting socket, start sending Connect Requests */ - sock->state = SS_CONNECTING; - sk->sk_state = TCP_SYN_SENT; - - nr_establish_data_link(sk); - - nr->state = NR_STATE_1; - - nr_start_heartbeat(sk); - - /* Now the loop */ - if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) { - err = -EINPROGRESS; - goto out_release; - } - - /* - * A Connect Ack with Choke or timeout or failed routing will go to - * closed. - */ - if (sk->sk_state == TCP_SYN_SENT) { - DEFINE_WAIT(wait); - - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - if (sk->sk_state != TCP_SYN_SENT) - break; - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - if (err) - goto out_release; - } - - if (sk->sk_state != TCP_ESTABLISHED) { - sock->state = SS_UNCONNECTED; - err = sock_error(sk); /* Always set at this point */ - goto out_release; - } - - sock->state = SS_CONNECTED; - -out_release: - release_sock(sk); - - return err; -} - -static int nr_accept(struct socket *sock, struct socket *newsock, - struct proto_accept_arg *arg) -{ - struct sk_buff *skb; - struct sock *newsk; - DEFINE_WAIT(wait); - struct sock *sk; - int err = 0; - - if ((sk = sock->sk) == NULL) - return -EINVAL; - - lock_sock(sk); - if (sk->sk_type != SOCK_SEQPACKET) { - err = -EOPNOTSUPP; - goto out_release; - } - - if (sk->sk_state != TCP_LISTEN) { - err = -EINVAL; - goto out_release; - } - - /* - * The write queue this time is holding sockets ready to use - * hooked into the SABM we saved - */ - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - skb = skb_dequeue(&sk->sk_receive_queue); - if (skb) - break; - - if (arg->flags & O_NONBLOCK) { - err = -EWOULDBLOCK; - break; - } - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - if (err) - goto out_release; - - newsk = skb->sk; - sock_graft(newsk, newsock); - - /* Now attach up the new socket */ - kfree_skb(skb); - sk_acceptq_removed(sk); - -out_release: - release_sock(sk); - - return err; -} - -static int nr_getname(struct socket *sock, struct sockaddr *uaddr, - int peer) -{ - struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr; - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - int uaddr_len; - - memset(&sax->fsa_ax25, 0, sizeof(struct sockaddr_ax25)); - - lock_sock(sk); - if (peer != 0) { - if (sk->sk_state != TCP_ESTABLISHED) { - release_sock(sk); - return -ENOTCONN; - } - sax->fsa_ax25.sax25_family = AF_NETROM; - sax->fsa_ax25.sax25_ndigis = 1; - sax->fsa_ax25.sax25_call = nr->user_addr; - memset(sax->fsa_digipeater, 0, sizeof(sax->fsa_digipeater)); - sax->fsa_digipeater[0] = nr->dest_addr; - uaddr_len = sizeof(struct full_sockaddr_ax25); - } else { - sax->fsa_ax25.sax25_family = AF_NETROM; - sax->fsa_ax25.sax25_ndigis = 0; - sax->fsa_ax25.sax25_call = nr->source_addr; - uaddr_len = sizeof(struct sockaddr_ax25); - } - release_sock(sk); - - return uaddr_len; -} - -int nr_rx_frame(struct sk_buff *skb, struct net_device *dev) -{ - struct sock *sk; - struct sock *make; - struct nr_sock *nr_make; - ax25_address *src, *dest, *user; - unsigned short circuit_index, circuit_id; - unsigned short peer_circuit_index, peer_circuit_id; - unsigned short frametype, flags, window, timeout; - int ret; - - skb_orphan(skb); - - /* - * skb->data points to the netrom frame start - */ - - src = (ax25_address *)(skb->data + 0); - dest = (ax25_address *)(skb->data + 7); - - circuit_index = skb->data[15]; - circuit_id = skb->data[16]; - peer_circuit_index = skb->data[17]; - peer_circuit_id = skb->data[18]; - frametype = skb->data[19] & 0x0F; - flags = skb->data[19] & 0xF0; - - /* - * Check for an incoming IP over NET/ROM frame. - */ - if (frametype == NR_PROTOEXT && - circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) { - skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); - skb_reset_transport_header(skb); - - return nr_rx_ip(skb, dev); - } - - /* - * Find an existing socket connection, based on circuit ID, if it's - * a Connect Request base it on their circuit ID. - * - * Circuit ID 0/0 is not valid but it could still be a "reset" for a - * circuit that no longer exists at the other end ... - */ - - sk = NULL; - - if (circuit_index == 0 && circuit_id == 0) { - if (frametype == NR_CONNACK && flags == NR_CHOKE_FLAG) - sk = nr_find_peer(peer_circuit_index, peer_circuit_id, src); - } else { - if (frametype == NR_CONNREQ) - sk = nr_find_peer(circuit_index, circuit_id, src); - else - sk = nr_find_socket(circuit_index, circuit_id); - } - - if (sk != NULL) { - bh_lock_sock(sk); - skb_reset_transport_header(skb); - - if (frametype == NR_CONNACK && skb->len == 22) - nr_sk(sk)->bpqext = 1; - else - nr_sk(sk)->bpqext = 0; - - ret = nr_process_rx_frame(sk, skb); - bh_unlock_sock(sk); - sock_put(sk); - return ret; - } - - /* - * Now it should be a CONNREQ. - */ - if (frametype != NR_CONNREQ) { - /* - * Here it would be nice to be able to send a reset but - * NET/ROM doesn't have one. We've tried to extend the protocol - * by sending NR_CONNACK | NR_CHOKE_FLAGS replies but that - * apparently kills BPQ boxes... :-( - * So now we try to follow the established behaviour of - * G8PZT's Xrouter which is sending packets with command type 7 - * as an extension of the protocol. - */ - if (READ_ONCE(sysctl_netrom_reset_circuit) && - (frametype != NR_RESET || flags != 0)) - nr_transmit_reset(skb, 1); - - return 0; - } - - sk = nr_find_listener(dest); - - user = (ax25_address *)(skb->data + 21); - - if (sk == NULL || sk_acceptq_is_full(sk) || - (make = nr_make_new(sk)) == NULL) { - nr_transmit_refusal(skb, 0); - if (sk) - sock_put(sk); - return 0; - } - - bh_lock_sock(sk); - - window = skb->data[20]; - - sock_hold(make); - skb->sk = make; - skb->destructor = sock_efree; - make->sk_state = TCP_ESTABLISHED; - - /* Fill in his circuit details */ - nr_make = nr_sk(make); - nr_make->source_addr = *dest; - nr_make->dest_addr = *src; - nr_make->user_addr = *user; - - nr_make->your_index = circuit_index; - nr_make->your_id = circuit_id; - - bh_unlock_sock(sk); - circuit = nr_find_next_circuit(); - bh_lock_sock(sk); - - nr_make->my_index = circuit / 256; - nr_make->my_id = circuit % 256; - - circuit++; - - /* Window negotiation */ - if (window < nr_make->window) - nr_make->window = window; - - /* L4 timeout negotiation */ - if (skb->len == 37) { - timeout = skb->data[36] * 256 + skb->data[35]; - if (timeout * HZ < nr_make->t1) - nr_make->t1 = timeout * HZ; - nr_make->bpqext = 1; - } else { - nr_make->bpqext = 0; - } - - nr_write_internal(make, NR_CONNACK); - - nr_make->condition = 0x00; - nr_make->vs = 0; - nr_make->va = 0; - nr_make->vr = 0; - nr_make->vl = 0; - nr_make->state = NR_STATE_3; - sk_acceptq_added(sk); - skb_queue_head(&sk->sk_receive_queue, skb); - - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_data_ready(sk); - - bh_unlock_sock(sk); - sock_put(sk); - - nr_insert_socket(make); - - nr_start_heartbeat(make); - nr_start_idletimer(make); - - return 1; -} - -static int nr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - DECLARE_SOCKADDR(struct sockaddr_ax25 *, usax, msg->msg_name); - int err; - struct sockaddr_ax25 sax; - struct sk_buff *skb; - unsigned char *asmptr; - int size; - - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_CMSG_COMPAT)) - return -EINVAL; - - lock_sock(sk); - if (sock_flag(sk, SOCK_ZAPPED)) { - err = -EADDRNOTAVAIL; - goto out; - } - - if (sk->sk_shutdown & SEND_SHUTDOWN) { - send_sig(SIGPIPE, current, 0); - err = -EPIPE; - goto out; - } - - if (nr->device == NULL) { - err = -ENETUNREACH; - goto out; - } - - if (usax) { - if (msg->msg_namelen < sizeof(sax)) { - err = -EINVAL; - goto out; - } - sax = *usax; - if (ax25cmp(&nr->dest_addr, &sax.sax25_call) != 0) { - err = -EISCONN; - goto out; - } - if (sax.sax25_family != AF_NETROM) { - err = -EINVAL; - goto out; - } - } else { - if (sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - sax.sax25_family = AF_NETROM; - sax.sax25_call = nr->dest_addr; - } - - /* Build a packet - the conventional user limit is 236 bytes. We can - do ludicrously large NetROM frames but must not overflow */ - if (len > 65536) { - err = -EMSGSIZE; - goto out; - } - - size = len + NR_NETWORK_LEN + NR_TRANSPORT_LEN; - - if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) - goto out; - - skb_reserve(skb, size - len); - skb_reset_transport_header(skb); - - /* - * Push down the NET/ROM header - */ - - asmptr = skb_push(skb, NR_TRANSPORT_LEN); - - /* Build a NET/ROM Transport header */ - - *asmptr++ = nr->your_index; - *asmptr++ = nr->your_id; - *asmptr++ = 0; /* To be filled in later */ - *asmptr++ = 0; /* Ditto */ - *asmptr++ = NR_INFO; - - /* - * Put the data on the end - */ - skb_put(skb, len); - - /* User data follows immediately after the NET/ROM transport header */ - if (memcpy_from_msg(skb_transport_header(skb), msg, len)) { - kfree_skb(skb); - err = -EFAULT; - goto out; - } - - if (sk->sk_state != TCP_ESTABLISHED) { - kfree_skb(skb); - err = -ENOTCONN; - goto out; - } - - nr_output(sk, skb); /* Shove it onto the queue */ - - err = len; -out: - release_sock(sk); - return err; -} - -static int nr_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, - int flags) -{ - struct sock *sk = sock->sk; - DECLARE_SOCKADDR(struct sockaddr_ax25 *, sax, msg->msg_name); - size_t copied; - struct sk_buff *skb; - int er; - - /* - * This works for seqpacket too. The receiver has ordered the queue for - * us! We do one quick check first though - */ - - lock_sock(sk); - if (sk->sk_state != TCP_ESTABLISHED) { - release_sock(sk); - return -ENOTCONN; - } - - /* Now we can treat all alike */ - skb = skb_recv_datagram(sk, flags, &er); - if (!skb) { - release_sock(sk); - return er; - } - - skb_reset_transport_header(skb); - copied = skb->len; - - if (copied > size) { - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - - er = skb_copy_datagram_msg(skb, 0, msg, copied); - if (er < 0) { - skb_free_datagram(sk, skb); - release_sock(sk); - return er; - } - - if (sax != NULL) { - memset(sax, 0, sizeof(*sax)); - sax->sax25_family = AF_NETROM; - skb_copy_from_linear_data_offset(skb, 7, sax->sax25_call.ax25_call, - AX25_ADDR_LEN); - msg->msg_namelen = sizeof(*sax); - } - - skb_free_datagram(sk, skb); - - release_sock(sk); - return copied; -} - - -static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - void __user *argp = (void __user *)arg; - - switch (cmd) { - case TIOCOUTQ: { - long amount; - - lock_sock(sk); - amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - if (amount < 0) - amount = 0; - release_sock(sk); - return put_user(amount, (int __user *)argp); - } - - case TIOCINQ: { - struct sk_buff *skb; - long amount = 0L; - - lock_sock(sk); - /* These two are safe on a single CPU system as only user tasks fiddle here */ - if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) - amount = skb->len; - release_sock(sk); - return put_user(amount, (int __user *)argp); - } - - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - return -EINVAL; - - case SIOCADDRT: - case SIOCDELRT: - case SIOCNRDECOBS: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - return nr_rt_ioctl(cmd, argp); - - default: - return -ENOIOCTLCMD; - } - - return 0; -} - -#ifdef CONFIG_PROC_FS - -static void *nr_info_start(struct seq_file *seq, loff_t *pos) - __acquires(&nr_list_lock) -{ - spin_lock_bh(&nr_list_lock); - return seq_hlist_start_head(&nr_list, *pos); -} - -static void *nr_info_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &nr_list, pos); -} - -static void nr_info_stop(struct seq_file *seq, void *v) - __releases(&nr_list_lock) -{ - spin_unlock_bh(&nr_list_lock); -} - -static int nr_info_show(struct seq_file *seq, void *v) -{ - struct sock *s = sk_entry(v); - struct net_device *dev; - struct nr_sock *nr; - const char *devname; - char buf[11]; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, -"user_addr dest_node src_node dev my your st vs vr va t1 t2 t4 idle n2 wnd Snd-Q Rcv-Q inode\n"); - - else { - - bh_lock_sock(s); - nr = nr_sk(s); - - if ((dev = nr->device) == NULL) - devname = "???"; - else - devname = dev->name; - - seq_printf(seq, "%-9s ", ax2asc(buf, &nr->user_addr)); - seq_printf(seq, "%-9s ", ax2asc(buf, &nr->dest_addr)); - seq_printf(seq, -"%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3lu/%03lu %2lu/%02lu %3lu/%03lu %3lu/%03lu %2d/%02d %3d %5d %5d %llu\n", - ax2asc(buf, &nr->source_addr), - devname, - nr->my_index, - nr->my_id, - nr->your_index, - nr->your_id, - nr->state, - nr->vs, - nr->vr, - nr->va, - ax25_display_timer(&nr->t1timer) / HZ, - nr->t1 / HZ, - ax25_display_timer(&nr->t2timer) / HZ, - nr->t2 / HZ, - ax25_display_timer(&nr->t4timer) / HZ, - nr->t4 / HZ, - ax25_display_timer(&nr->idletimer) / (60 * HZ), - nr->idle / (60 * HZ), - nr->n2count, - nr->n2, - nr->window, - sk_wmem_alloc_get(s), - sk_rmem_alloc_get(s), - s->sk_socket ? SOCK_INODE(s->sk_socket)->i_ino : (u64)0); - - bh_unlock_sock(s); - } - return 0; -} - -static const struct seq_operations nr_info_seqops = { - .start = nr_info_start, - .next = nr_info_next, - .stop = nr_info_stop, - .show = nr_info_show, -}; -#endif /* CONFIG_PROC_FS */ - -static const struct net_proto_family nr_family_ops = { - .family = PF_NETROM, - .create = nr_create, - .owner = THIS_MODULE, -}; - -static const struct proto_ops nr_proto_ops = { - .family = PF_NETROM, - .owner = THIS_MODULE, - .release = nr_release, - .bind = nr_bind, - .connect = nr_connect, - .socketpair = sock_no_socketpair, - .accept = nr_accept, - .getname = nr_getname, - .poll = datagram_poll, - .ioctl = nr_ioctl, - .gettstamp = sock_gettstamp, - .listen = nr_listen, - .shutdown = sock_no_shutdown, - .setsockopt = nr_setsockopt, - .getsockopt = nr_getsockopt, - .sendmsg = nr_sendmsg, - .recvmsg = nr_recvmsg, - .mmap = sock_no_mmap, -}; - -static struct notifier_block nr_dev_notifier = { - .notifier_call = nr_device_event, -}; - -static struct net_device **dev_nr; - -static struct ax25_protocol nr_pid = { - .pid = AX25_P_NETROM, - .func = nr_route_frame -}; - -static struct ax25_linkfail nr_linkfail_notifier = { - .func = nr_link_failed, -}; - -static int __init nr_proto_init(void) -{ - int i; - int rc = proto_register(&nr_proto, 0); - - if (rc) - return rc; - - if (nr_ndevs > 0x7fffffff/sizeof(struct net_device *)) { - pr_err("NET/ROM: %s - nr_ndevs parameter too large\n", - __func__); - rc = -EINVAL; - goto unregister_proto; - } - - dev_nr = kzalloc_objs(struct net_device *, nr_ndevs); - if (!dev_nr) { - pr_err("NET/ROM: %s - unable to allocate device array\n", - __func__); - rc = -ENOMEM; - goto unregister_proto; - } - - for (i = 0; i < nr_ndevs; i++) { - char name[IFNAMSIZ]; - struct net_device *dev; - - sprintf(name, "nr%d", i); - dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, nr_setup); - if (!dev) { - rc = -ENOMEM; - goto fail; - } - - dev->base_addr = i; - rc = register_netdev(dev); - if (rc) { - free_netdev(dev); - goto fail; - } - nr_set_lockdep_key(dev); - dev_nr[i] = dev; - } - - rc = sock_register(&nr_family_ops); - if (rc) - goto fail; - - rc = register_netdevice_notifier(&nr_dev_notifier); - if (rc) - goto out_sock; - - ax25_register_pid(&nr_pid); - ax25_linkfail_register(&nr_linkfail_notifier); - -#ifdef CONFIG_SYSCTL - rc = nr_register_sysctl(); - if (rc) - goto out_sysctl; -#endif - - nr_loopback_init(); - - rc = -ENOMEM; - if (!proc_create_seq("nr", 0444, init_net.proc_net, &nr_info_seqops)) - goto proc_remove1; - if (!proc_create_seq("nr_neigh", 0444, init_net.proc_net, - &nr_neigh_seqops)) - goto proc_remove2; - if (!proc_create_seq("nr_nodes", 0444, init_net.proc_net, - &nr_node_seqops)) - goto proc_remove3; - - return 0; - -proc_remove3: - remove_proc_entry("nr_neigh", init_net.proc_net); -proc_remove2: - remove_proc_entry("nr", init_net.proc_net); -proc_remove1: - - nr_loopback_clear(); - nr_rt_free(); - -#ifdef CONFIG_SYSCTL - nr_unregister_sysctl(); -out_sysctl: -#endif - ax25_linkfail_release(&nr_linkfail_notifier); - ax25_protocol_release(AX25_P_NETROM); - unregister_netdevice_notifier(&nr_dev_notifier); -out_sock: - sock_unregister(PF_NETROM); -fail: - while (--i >= 0) { - unregister_netdev(dev_nr[i]); - free_netdev(dev_nr[i]); - } - kfree(dev_nr); -unregister_proto: - proto_unregister(&nr_proto); - return rc; -} - -module_init(nr_proto_init); - -module_param(nr_ndevs, int, 0); -MODULE_PARM_DESC(nr_ndevs, "number of NET/ROM devices"); - -MODULE_AUTHOR("Jonathan Naylor G4KLX "); -MODULE_DESCRIPTION("The amateur radio NET/ROM network and transport layer protocol"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_NETROM); - -static void __exit nr_exit(void) -{ - int i; - - remove_proc_entry("nr", init_net.proc_net); - remove_proc_entry("nr_neigh", init_net.proc_net); - remove_proc_entry("nr_nodes", init_net.proc_net); - nr_loopback_clear(); - - nr_rt_free(); - -#ifdef CONFIG_SYSCTL - nr_unregister_sysctl(); -#endif - - ax25_linkfail_release(&nr_linkfail_notifier); - ax25_protocol_release(AX25_P_NETROM); - - unregister_netdevice_notifier(&nr_dev_notifier); - - sock_unregister(PF_NETROM); - - for (i = 0; i < nr_ndevs; i++) { - struct net_device *dev = dev_nr[i]; - if (dev) { - unregister_netdev(dev); - free_netdev(dev); - } - } - - kfree(dev_nr); - proto_unregister(&nr_proto); -} -module_exit(nr_exit); diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c deleted file mode 100644 index 2c34389c3ce6..000000000000 --- a/net/netrom/nr_dev.c +++ /dev/null @@ -1,178 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For the statistics structure. */ -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -/* - * Only allow IP over NET/ROM frames through if the netrom device is up. - */ - -int nr_rx_ip(struct sk_buff *skb, struct net_device *dev) -{ - struct net_device_stats *stats = &dev->stats; - - if (!netif_running(dev)) { - stats->rx_dropped++; - return 0; - } - - stats->rx_packets++; - stats->rx_bytes += skb->len; - - skb->protocol = htons(ETH_P_IP); - - /* Spoof incoming device */ - skb->dev = dev; - skb->mac_header = skb->network_header; - skb_reset_network_header(skb); - skb->pkt_type = PACKET_HOST; - - netif_rx(skb); - - return 1; -} - -static int nr_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, - const void *daddr, const void *saddr, unsigned int len) -{ - unsigned char *buff = skb_push(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); - - memcpy(buff, (saddr != NULL) ? saddr : dev->dev_addr, dev->addr_len); - buff[6] &= ~AX25_CBIT; - buff[6] &= ~AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - if (daddr != NULL) - memcpy(buff, daddr, dev->addr_len); - buff[6] &= ~AX25_CBIT; - buff[6] |= AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - *buff++ = READ_ONCE(sysctl_netrom_network_ttl_initialiser); - - *buff++ = NR_PROTO_IP; - *buff++ = NR_PROTO_IP; - *buff++ = 0; - *buff++ = 0; - *buff++ = NR_PROTOEXT; - - if (daddr != NULL) - return 37; - - return -37; -} - -static int __must_check nr_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = addr; - int err; - - if (!memcmp(dev->dev_addr, sa->sa_data, dev->addr_len)) - return 0; - - if (dev->flags & IFF_UP) { - err = ax25_listen_register((ax25_address *)sa->sa_data, NULL); - if (err) - return err; - - ax25_listen_release((const ax25_address *)dev->dev_addr, NULL); - } - - dev_addr_set(dev, sa->sa_data); - - return 0; -} - -static int nr_open(struct net_device *dev) -{ - int err; - - err = ax25_listen_register((const ax25_address *)dev->dev_addr, NULL); - if (err) - return err; - - netif_start_queue(dev); - - return 0; -} - -static int nr_close(struct net_device *dev) -{ - ax25_listen_release((const ax25_address *)dev->dev_addr, NULL); - netif_stop_queue(dev); - return 0; -} - -static netdev_tx_t nr_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct net_device_stats *stats = &dev->stats; - unsigned int len = skb->len; - - if (!nr_route_frame(skb, NULL)) { - kfree_skb(skb); - stats->tx_errors++; - return NETDEV_TX_OK; - } - - stats->tx_packets++; - stats->tx_bytes += len; - - return NETDEV_TX_OK; -} - -static const struct header_ops nr_header_ops = { - .create = nr_header, -}; - -static const struct net_device_ops nr_netdev_ops = { - .ndo_open = nr_open, - .ndo_stop = nr_close, - .ndo_start_xmit = nr_xmit, - .ndo_set_mac_address = nr_set_mac_address, -}; - -void nr_setup(struct net_device *dev) -{ - dev->mtu = NR_MAX_PACKET_SIZE; - dev->netdev_ops = &nr_netdev_ops; - dev->header_ops = &nr_header_ops; - dev->hard_header_len = NR_NETWORK_LEN + NR_TRANSPORT_LEN; - dev->addr_len = AX25_ADDR_LEN; - dev->type = ARPHRD_NETROM; - - /* New-style flags. */ - dev->flags = IFF_NOARP; -} diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c deleted file mode 100644 index 97944db6b5ac..000000000000 --- a/net/netrom/nr_in.c +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) -{ - struct sk_buff *skbo, *skbn = skb; - struct nr_sock *nr = nr_sk(sk); - - skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); - - nr_start_idletimer(sk); - - if (more) { - nr->fraglen += skb->len; - skb_queue_tail(&nr->frag_queue, skb); - return 0; - } - - if (!more && nr->fraglen > 0) { /* End of fragment */ - nr->fraglen += skb->len; - skb_queue_tail(&nr->frag_queue, skb); - - if ((skbn = alloc_skb(nr->fraglen, GFP_ATOMIC)) == NULL) - return 1; - - skb_reset_transport_header(skbn); - - while ((skbo = skb_dequeue(&nr->frag_queue)) != NULL) { - skb_copy_from_linear_data(skbo, - skb_put(skbn, skbo->len), - skbo->len); - kfree_skb(skbo); - } - - nr->fraglen = 0; - } - - return sock_queue_rcv_skb(sk, skbn); -} - -/* - * State machine for state 1, Awaiting Connection State. - * The handling of the timer(s) is in file nr_timer.c. - * Handling of state 0 and connection release is in netrom.c. - */ -static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, - int frametype) -{ - switch (frametype) { - case NR_CONNACK: { - struct nr_sock *nr = nr_sk(sk); - - nr_stop_t1timer(sk); - nr_start_idletimer(sk); - nr->your_index = skb->data[17]; - nr->your_id = skb->data[18]; - nr->vs = 0; - nr->va = 0; - nr->vr = 0; - nr->vl = 0; - nr->state = NR_STATE_3; - nr->n2count = 0; - nr->window = skb->data[20]; - sk->sk_state = TCP_ESTABLISHED; - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_state_change(sk); - break; - } - - case NR_CONNACK | NR_CHOKE_FLAG: - nr_disconnect(sk, ECONNREFUSED); - break; - - case NR_RESET: - if (READ_ONCE(sysctl_netrom_reset_circuit)) - nr_disconnect(sk, ECONNRESET); - break; - - default: - break; - } - return 0; -} - -/* - * State machine for state 2, Awaiting Release State. - * The handling of the timer(s) is in file nr_timer.c - * Handling of state 0 and connection release is in netrom.c. - */ -static int nr_state2_machine(struct sock *sk, struct sk_buff *skb, - int frametype) -{ - switch (frametype) { - case NR_CONNACK | NR_CHOKE_FLAG: - nr_disconnect(sk, ECONNRESET); - break; - - case NR_DISCREQ: - nr_write_internal(sk, NR_DISCACK); - fallthrough; - case NR_DISCACK: - nr_disconnect(sk, 0); - break; - - case NR_RESET: - if (READ_ONCE(sysctl_netrom_reset_circuit)) - nr_disconnect(sk, ECONNRESET); - break; - - default: - break; - } - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file nr_timer.c - * Handling of state 0 and connection release is in netrom.c. - */ -static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype) -{ - struct nr_sock *nrom = nr_sk(sk); - struct sk_buff_head temp_queue; - struct sk_buff *skbn; - unsigned short save_vr; - unsigned short nr, ns; - int queued = 0; - - nr = skb->data[18]; - - switch (frametype) { - case NR_CONNREQ: - nr_write_internal(sk, NR_CONNACK); - break; - - case NR_DISCREQ: - nr_write_internal(sk, NR_DISCACK); - nr_disconnect(sk, 0); - break; - - case NR_CONNACK | NR_CHOKE_FLAG: - case NR_DISCACK: - nr_disconnect(sk, ECONNRESET); - break; - - case NR_INFOACK: - case NR_INFOACK | NR_CHOKE_FLAG: - case NR_INFOACK | NR_NAK_FLAG: - case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG: - if (frametype & NR_CHOKE_FLAG) { - nrom->condition |= NR_COND_PEER_RX_BUSY; - nr_start_t4timer(sk); - } else { - nrom->condition &= ~NR_COND_PEER_RX_BUSY; - nr_stop_t4timer(sk); - } - if (!nr_validate_nr(sk, nr)) { - break; - } - if (frametype & NR_NAK_FLAG) { - nr_frames_acked(sk, nr); - nr_send_nak_frame(sk); - } else { - if (nrom->condition & NR_COND_PEER_RX_BUSY) { - nr_frames_acked(sk, nr); - } else { - nr_check_iframes_acked(sk, nr); - } - } - break; - - case NR_INFO: - case NR_INFO | NR_NAK_FLAG: - case NR_INFO | NR_CHOKE_FLAG: - case NR_INFO | NR_MORE_FLAG: - case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG: - case NR_INFO | NR_CHOKE_FLAG | NR_MORE_FLAG: - case NR_INFO | NR_NAK_FLAG | NR_MORE_FLAG: - case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG: - if (frametype & NR_CHOKE_FLAG) { - nrom->condition |= NR_COND_PEER_RX_BUSY; - nr_start_t4timer(sk); - } else { - nrom->condition &= ~NR_COND_PEER_RX_BUSY; - nr_stop_t4timer(sk); - } - if (nr_validate_nr(sk, nr)) { - if (frametype & NR_NAK_FLAG) { - nr_frames_acked(sk, nr); - nr_send_nak_frame(sk); - } else { - if (nrom->condition & NR_COND_PEER_RX_BUSY) { - nr_frames_acked(sk, nr); - } else { - nr_check_iframes_acked(sk, nr); - } - } - } - queued = 1; - skb_queue_head(&nrom->reseq_queue, skb); - if (nrom->condition & NR_COND_OWN_RX_BUSY) - break; - skb_queue_head_init(&temp_queue); - do { - save_vr = nrom->vr; - while ((skbn = skb_dequeue(&nrom->reseq_queue)) != NULL) { - ns = skbn->data[17]; - if (ns == nrom->vr) { - if (nr_queue_rx_frame(sk, skbn, frametype & NR_MORE_FLAG) == 0) { - nrom->vr = (nrom->vr + 1) % NR_MODULUS; - } else { - nrom->condition |= NR_COND_OWN_RX_BUSY; - skb_queue_tail(&temp_queue, skbn); - } - } else if (nr_in_rx_window(sk, ns)) { - skb_queue_tail(&temp_queue, skbn); - } else { - kfree_skb(skbn); - } - } - while ((skbn = skb_dequeue(&temp_queue)) != NULL) { - skb_queue_tail(&nrom->reseq_queue, skbn); - } - } while (save_vr != nrom->vr); - /* - * Window is full, ack it immediately. - */ - if (((nrom->vl + nrom->window) % NR_MODULUS) == nrom->vr) { - nr_enquiry_response(sk); - } else { - if (!(nrom->condition & NR_COND_ACK_PENDING)) { - nrom->condition |= NR_COND_ACK_PENDING; - nr_start_t2timer(sk); - } - } - break; - - case NR_RESET: - if (READ_ONCE(sysctl_netrom_reset_circuit)) - nr_disconnect(sk, ECONNRESET); - break; - - default: - break; - } - return queued; -} - -/* Higher level upcall for a LAPB frame - called with sk locked */ -int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb) -{ - struct nr_sock *nr = nr_sk(sk); - int queued = 0, frametype; - - if (nr->state == NR_STATE_0) - return 0; - - frametype = skb->data[19]; - - switch (nr->state) { - case NR_STATE_1: - queued = nr_state1_machine(sk, skb, frametype); - break; - case NR_STATE_2: - queued = nr_state2_machine(sk, skb, frametype); - break; - case NR_STATE_3: - queued = nr_state3_machine(sk, skb, frametype); - break; - } - - nr_kick(sk); - - return queued; -} diff --git a/net/netrom/nr_loopback.c b/net/netrom/nr_loopback.c deleted file mode 100644 index 7a9d765b30c0..000000000000 --- a/net/netrom/nr_loopback.c +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi) - */ -#include -#include -#include -#include -#include -#include -#include -#include - -static void nr_loopback_timer(struct timer_list *); - -static struct sk_buff_head loopback_queue; -static DEFINE_TIMER(loopback_timer, nr_loopback_timer); - -void __init nr_loopback_init(void) -{ - skb_queue_head_init(&loopback_queue); -} - -static inline int nr_loopback_running(void) -{ - return timer_pending(&loopback_timer); -} - -int nr_loopback_queue(struct sk_buff *skb) -{ - struct sk_buff *skbn; - - if ((skbn = alloc_skb(skb->len, GFP_ATOMIC)) != NULL) { - skb_copy_from_linear_data(skb, skb_put(skbn, skb->len), skb->len); - skb_reset_transport_header(skbn); - - skb_queue_tail(&loopback_queue, skbn); - - if (!nr_loopback_running()) - mod_timer(&loopback_timer, jiffies + 10); - } - - kfree_skb(skb); - return 1; -} - -static void nr_loopback_timer(struct timer_list *unused) -{ - struct sk_buff *skb; - ax25_address *nr_dest; - struct net_device *dev; - - if ((skb = skb_dequeue(&loopback_queue)) != NULL) { - nr_dest = (ax25_address *)(skb->data + 7); - - dev = nr_dev_get(nr_dest); - - if (dev == NULL || nr_rx_frame(skb, dev) == 0) - kfree_skb(skb); - - dev_put(dev); - - if (!skb_queue_empty(&loopback_queue) && !nr_loopback_running()) - mod_timer(&loopback_timer, jiffies + 10); - } -} - -void nr_loopback_clear(void) -{ - timer_delete_sync(&loopback_timer); - skb_queue_purge(&loopback_queue); -} diff --git a/net/netrom/nr_out.c b/net/netrom/nr_out.c deleted file mode 100644 index 2b3cbceb0b52..000000000000 --- a/net/netrom/nr_out.c +++ /dev/null @@ -1,272 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * This is where all NET/ROM frames pass, except for IP-over-NET/ROM which - * cannot be fragmented in this manner. - */ -void nr_output(struct sock *sk, struct sk_buff *skb) -{ - struct sk_buff *skbn; - unsigned char transport[NR_TRANSPORT_LEN]; - int err, frontlen, len; - - if (skb->len - NR_TRANSPORT_LEN > NR_MAX_PACKET_SIZE) { - /* Save a copy of the Transport Header */ - skb_copy_from_linear_data(skb, transport, NR_TRANSPORT_LEN); - skb_pull(skb, NR_TRANSPORT_LEN); - - frontlen = skb_headroom(skb); - - while (skb->len > 0) { - if ((skbn = sock_alloc_send_skb(sk, frontlen + NR_MAX_PACKET_SIZE, 0, &err)) == NULL) { - kfree_skb(skb); - return; - } - - skb_reserve(skbn, frontlen); - - len = (NR_MAX_PACKET_SIZE > skb->len) ? skb->len : NR_MAX_PACKET_SIZE; - - /* Copy the user data */ - skb_copy_from_linear_data(skb, skb_put(skbn, len), len); - skb_pull(skb, len); - - /* Duplicate the Transport Header */ - skb_push(skbn, NR_TRANSPORT_LEN); - skb_copy_to_linear_data(skbn, transport, - NR_TRANSPORT_LEN); - if (skb->len > 0) - skbn->data[4] |= NR_MORE_FLAG; - - skb_queue_tail(&sk->sk_write_queue, skbn); /* Throw it on the queue */ - } - - kfree_skb(skb); - } else { - skb_queue_tail(&sk->sk_write_queue, skb); /* Throw it on the queue */ - } - - nr_kick(sk); -} - -/* - * This procedure is passed a buffer descriptor for an iframe. It builds - * the rest of the control part of the frame and then writes it out. - */ -static void nr_send_iframe(struct sock *sk, struct sk_buff *skb) -{ - struct nr_sock *nr = nr_sk(sk); - - if (skb == NULL) - return; - - skb->data[2] = nr->vs; - skb->data[3] = nr->vr; - - if (nr->condition & NR_COND_OWN_RX_BUSY) - skb->data[4] |= NR_CHOKE_FLAG; - - nr_start_idletimer(sk); - - nr_transmit_buffer(sk, skb); -} - -void nr_send_nak_frame(struct sock *sk) -{ - struct sk_buff *skb, *skbn; - struct nr_sock *nr = nr_sk(sk); - - if ((skb = skb_peek(&nr->ack_queue)) == NULL) - return; - - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) - return; - - skbn->data[2] = nr->va; - skbn->data[3] = nr->vr; - - if (nr->condition & NR_COND_OWN_RX_BUSY) - skbn->data[4] |= NR_CHOKE_FLAG; - - nr_transmit_buffer(sk, skbn); - - nr->condition &= ~NR_COND_ACK_PENDING; - nr->vl = nr->vr; - - nr_stop_t1timer(sk); -} - -void nr_kick(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - struct sk_buff *skb, *skbn; - unsigned short start, end; - - if (nr->state != NR_STATE_3) - return; - - if (nr->condition & NR_COND_PEER_RX_BUSY) - return; - - if (!skb_peek(&sk->sk_write_queue)) - return; - - start = (skb_peek(&nr->ack_queue) == NULL) ? nr->va : nr->vs; - end = (nr->va + nr->window) % NR_MODULUS; - - if (start == end) - return; - - nr->vs = start; - - /* - * Transmit data until either we're out of data to send or - * the window is full. - */ - - /* - * Dequeue the frame and copy it. - */ - skb = skb_dequeue(&sk->sk_write_queue); - - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&sk->sk_write_queue, skb); - break; - } - - skb_set_owner_w(skbn, sk); - - /* - * Transmit the frame copy. - */ - nr_send_iframe(sk, skbn); - - nr->vs = (nr->vs + 1) % NR_MODULUS; - - /* - * Requeue the original data frame. - */ - skb_queue_tail(&nr->ack_queue, skb); - - } while (nr->vs != end && - (skb = skb_dequeue(&sk->sk_write_queue)) != NULL); - - nr->vl = nr->vr; - nr->condition &= ~NR_COND_ACK_PENDING; - - if (!nr_t1timer_running(sk)) - nr_start_t1timer(sk); -} - -void nr_transmit_buffer(struct sock *sk, struct sk_buff *skb) -{ - struct nr_sock *nr = nr_sk(sk); - unsigned char *dptr; - - /* - * Add the protocol byte and network header. - */ - dptr = skb_push(skb, NR_NETWORK_LEN); - - memcpy(dptr, &nr->source_addr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] &= ~AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - - memcpy(dptr, &nr->dest_addr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] |= AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - - *dptr++ = READ_ONCE(sysctl_netrom_network_ttl_initialiser); - - if (!nr_route_frame(skb, NULL)) { - kfree_skb(skb); - nr_disconnect(sk, ENETUNREACH); - } -} - -/* - * The following routines are taken from page 170 of the 7th ARRL Computer - * Networking Conference paper, as is the whole state machine. - */ - -void nr_establish_data_link(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - nr->condition = 0x00; - nr->n2count = 0; - - nr_write_internal(sk, NR_CONNREQ); - - nr_stop_t2timer(sk); - nr_stop_t4timer(sk); - nr_stop_idletimer(sk); - nr_start_t1timer(sk); -} - -/* - * Never send a NAK when we are CHOKEd. - */ -void nr_enquiry_response(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - int frametype = NR_INFOACK; - - if (nr->condition & NR_COND_OWN_RX_BUSY) { - frametype |= NR_CHOKE_FLAG; - } else { - if (skb_peek(&nr->reseq_queue) != NULL) - frametype |= NR_NAK_FLAG; - } - - nr_write_internal(sk, frametype); - - nr->vl = nr->vr; - nr->condition &= ~NR_COND_ACK_PENDING; -} - -void nr_check_iframes_acked(struct sock *sk, unsigned short nr) -{ - struct nr_sock *nrom = nr_sk(sk); - - if (nrom->vs == nr) { - nr_frames_acked(sk, nr); - nr_stop_t1timer(sk); - nrom->n2count = 0; - } else { - if (nrom->va != nr) { - nr_frames_acked(sk, nr); - nr_start_t1timer(sk); - } - } -} diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c deleted file mode 100644 index 9cc29ae85b06..000000000000 --- a/net/netrom/nr_route.c +++ /dev/null @@ -1,989 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For TIOCINQ/OUTQ */ -#include -#include -#include -#include -#include -#include -#include -#include - -static unsigned int nr_neigh_no = 1; - -static HLIST_HEAD(nr_node_list); -static DEFINE_SPINLOCK(nr_node_list_lock); -static HLIST_HEAD(nr_neigh_list); -static DEFINE_SPINLOCK(nr_neigh_list_lock); - -static struct nr_node *nr_node_get(ax25_address *callsign) -{ - struct nr_node *found = NULL; - struct nr_node *nr_node; - - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each(nr_node, &nr_node_list) - if (ax25cmp(callsign, &nr_node->callsign) == 0) { - nr_node_hold(nr_node); - found = nr_node; - break; - } - spin_unlock_bh(&nr_node_list_lock); - return found; -} - -static struct nr_neigh *nr_neigh_get_dev(ax25_address *callsign, - struct net_device *dev) -{ - struct nr_neigh *found = NULL; - struct nr_neigh *nr_neigh; - - spin_lock_bh(&nr_neigh_list_lock); - nr_neigh_for_each(nr_neigh, &nr_neigh_list) - if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && - nr_neigh->dev == dev) { - nr_neigh_hold(nr_neigh); - found = nr_neigh; - break; - } - spin_unlock_bh(&nr_neigh_list_lock); - return found; -} - -static void nr_remove_neigh(struct nr_neigh *); - -/* re-sort the routes in quality order. */ -static void re_sort_routes(struct nr_node *nr_node, int x, int y) -{ - if (nr_node->routes[y].quality > nr_node->routes[x].quality) { - if (nr_node->which == x) - nr_node->which = y; - else if (nr_node->which == y) - nr_node->which = x; - - swap(nr_node->routes[x], nr_node->routes[y]); - } -} - -/* - * Add a new route to a node, and in the process add the node and the - * neighbour if it is new. - */ -static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic, - ax25_address *ax25, ax25_digi *ax25_digi, struct net_device *dev, - int quality, int obs_count) -{ - struct nr_node *nr_node; - struct nr_neigh *nr_neigh; - int i, found; - struct net_device *odev; - - if ((odev=nr_dev_get(nr)) != NULL) { /* Can't add routes to ourself */ - dev_put(odev); - return -EINVAL; - } - - nr_node = nr_node_get(nr); - - nr_neigh = nr_neigh_get_dev(ax25, dev); - - /* - * The L2 link to a neighbour has failed in the past - * and now a frame comes from this neighbour. We assume - * it was a temporary trouble with the link and reset the - * routes now (and not wait for a node broadcast). - */ - if (nr_neigh != NULL && nr_neigh->failed != 0 && quality == 0) { - struct nr_node *nr_nodet; - - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each(nr_nodet, &nr_node_list) { - nr_node_lock(nr_nodet); - for (i = 0; i < nr_nodet->count; i++) - if (nr_nodet->routes[i].neighbour == nr_neigh) - if (i < nr_nodet->which) - nr_nodet->which = i; - nr_node_unlock(nr_nodet); - } - spin_unlock_bh(&nr_node_list_lock); - } - - if (nr_neigh != NULL) - nr_neigh->failed = 0; - - if (quality == 0 && nr_neigh != NULL && nr_node != NULL) { - nr_neigh_put(nr_neigh); - nr_node_put(nr_node); - return 0; - } - - if (nr_neigh == NULL) { - if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) { - if (nr_node) - nr_node_put(nr_node); - return -ENOMEM; - } - - nr_neigh->callsign = *ax25; - nr_neigh->digipeat = NULL; - nr_neigh->ax25 = NULL; - nr_neigh->dev = dev; - nr_neigh->quality = READ_ONCE(sysctl_netrom_default_path_quality); - nr_neigh->locked = 0; - nr_neigh->count = 0; - nr_neigh->number = nr_neigh_no++; - nr_neigh->failed = 0; - refcount_set(&nr_neigh->refcount, 1); - - if (ax25_digi != NULL && ax25_digi->ndigi > 0) { - nr_neigh->digipeat = kmemdup(ax25_digi, - sizeof(*ax25_digi), - GFP_KERNEL); - if (nr_neigh->digipeat == NULL) { - kfree(nr_neigh); - if (nr_node) - nr_node_put(nr_node); - return -ENOMEM; - } - } - - spin_lock_bh(&nr_neigh_list_lock); - hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list); - nr_neigh_hold(nr_neigh); - spin_unlock_bh(&nr_neigh_list_lock); - } - - if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked) - nr_neigh->quality = quality; - - if (nr_node == NULL) { - if ((nr_node = kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL) { - if (nr_neigh) - nr_neigh_put(nr_neigh); - return -ENOMEM; - } - - nr_node->callsign = *nr; - strscpy(nr_node->mnemonic, mnemonic); - - nr_node->which = 0; - nr_node->count = 1; - refcount_set(&nr_node->refcount, 1); - spin_lock_init(&nr_node->node_lock); - - nr_node->routes[0].quality = quality; - nr_node->routes[0].obs_count = obs_count; - nr_node->routes[0].neighbour = nr_neigh; - - nr_neigh_hold(nr_neigh); - nr_neigh->count++; - - spin_lock_bh(&nr_node_list_lock); - hlist_add_head(&nr_node->node_node, &nr_node_list); - /* refcount initialized at 1 */ - spin_unlock_bh(&nr_node_list_lock); - - nr_neigh_put(nr_neigh); - return 0; - } - nr_node_lock(nr_node); - - if (quality != 0) - strscpy(nr_node->mnemonic, mnemonic); - - for (found = 0, i = 0; i < nr_node->count; i++) { - if (nr_node->routes[i].neighbour == nr_neigh) { - nr_node->routes[i].quality = quality; - nr_node->routes[i].obs_count = obs_count; - found = 1; - break; - } - } - - if (!found) { - /* We have space at the bottom, slot it in */ - if (nr_node->count < 3) { - nr_node->routes[2] = nr_node->routes[1]; - nr_node->routes[1] = nr_node->routes[0]; - - nr_node->routes[0].quality = quality; - nr_node->routes[0].obs_count = obs_count; - nr_node->routes[0].neighbour = nr_neigh; - - nr_node->which++; - nr_node->count++; - nr_neigh_hold(nr_neigh); - nr_neigh->count++; - } else { - /* It must be better than the worst */ - if (quality > nr_node->routes[2].quality) { - nr_node->routes[2].neighbour->count--; - nr_neigh_put(nr_node->routes[2].neighbour); - - if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked) - nr_remove_neigh(nr_node->routes[2].neighbour); - - nr_node->routes[2].quality = quality; - nr_node->routes[2].obs_count = obs_count; - nr_node->routes[2].neighbour = nr_neigh; - - nr_neigh_hold(nr_neigh); - nr_neigh->count++; - } - } - } - - /* Now re-sort the routes in quality order */ - switch (nr_node->count) { - case 3: - re_sort_routes(nr_node, 0, 1); - re_sort_routes(nr_node, 1, 2); - fallthrough; - case 2: - re_sort_routes(nr_node, 0, 1); - break; - case 1: - break; - } - - for (i = 0; i < nr_node->count; i++) { - if (nr_node->routes[i].neighbour == nr_neigh) { - if (i < nr_node->which) - nr_node->which = i; - break; - } - } - - nr_neigh_put(nr_neigh); - nr_node_unlock(nr_node); - nr_node_put(nr_node); - return 0; -} - -static void nr_remove_node_locked(struct nr_node *nr_node) -{ - lockdep_assert_held(&nr_node_list_lock); - - hlist_del_init(&nr_node->node_node); - nr_node_put(nr_node); -} - -static inline void __nr_remove_neigh(struct nr_neigh *nr_neigh) -{ - hlist_del_init(&nr_neigh->neigh_node); - nr_neigh_put(nr_neigh); -} - -#define nr_remove_neigh_locked(__neigh) \ - __nr_remove_neigh(__neigh) - -static void nr_remove_neigh(struct nr_neigh *nr_neigh) -{ - spin_lock_bh(&nr_neigh_list_lock); - __nr_remove_neigh(nr_neigh); - spin_unlock_bh(&nr_neigh_list_lock); -} - -/* - * "Delete" a node. Strictly speaking remove a route to a node. The node - * is only deleted if no routes are left to it. - */ -static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct net_device *dev) -{ - struct nr_node *nr_node; - struct nr_neigh *nr_neigh; - int i; - - nr_node = nr_node_get(callsign); - - if (nr_node == NULL) - return -EINVAL; - - nr_neigh = nr_neigh_get_dev(neighbour, dev); - - if (nr_neigh == NULL) { - nr_node_put(nr_node); - return -EINVAL; - } - - spin_lock_bh(&nr_node_list_lock); - nr_node_lock(nr_node); - for (i = 0; i < nr_node->count; i++) { - if (nr_node->routes[i].neighbour == nr_neigh) { - nr_neigh->count--; - nr_neigh_put(nr_neigh); - - if (nr_neigh->count == 0 && !nr_neigh->locked) - nr_remove_neigh(nr_neigh); - nr_neigh_put(nr_neigh); - - nr_node->count--; - - if (nr_node->count == 0) { - nr_remove_node_locked(nr_node); - } else { - switch (i) { - case 0: - nr_node->routes[0] = nr_node->routes[1]; - fallthrough; - case 1: - nr_node->routes[1] = nr_node->routes[2]; - fallthrough; - case 2: - break; - } - nr_node_put(nr_node); - } - nr_node_unlock(nr_node); - spin_unlock_bh(&nr_node_list_lock); - - return 0; - } - } - nr_neigh_put(nr_neigh); - nr_node_unlock(nr_node); - spin_unlock_bh(&nr_node_list_lock); - nr_node_put(nr_node); - - return -EINVAL; -} - -/* - * Lock a neighbour with a quality. - */ -static int __must_check nr_add_neigh(ax25_address *callsign, - ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality) -{ - struct nr_neigh *nr_neigh; - - nr_neigh = nr_neigh_get_dev(callsign, dev); - if (nr_neigh) { - nr_neigh->quality = quality; - nr_neigh->locked = 1; - nr_neigh_put(nr_neigh); - return 0; - } - - if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) - return -ENOMEM; - - nr_neigh->callsign = *callsign; - nr_neigh->digipeat = NULL; - nr_neigh->ax25 = NULL; - nr_neigh->dev = dev; - nr_neigh->quality = quality; - nr_neigh->locked = 1; - nr_neigh->count = 0; - nr_neigh->number = nr_neigh_no++; - nr_neigh->failed = 0; - refcount_set(&nr_neigh->refcount, 1); - - if (ax25_digi != NULL && ax25_digi->ndigi > 0) { - nr_neigh->digipeat = kmemdup(ax25_digi, sizeof(*ax25_digi), - GFP_KERNEL); - if (nr_neigh->digipeat == NULL) { - kfree(nr_neigh); - return -ENOMEM; - } - } - - spin_lock_bh(&nr_neigh_list_lock); - hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list); - /* refcount is initialized at 1 */ - spin_unlock_bh(&nr_neigh_list_lock); - - return 0; -} - -/* - * "Delete" a neighbour. The neighbour is only removed if the number - * of nodes that may use it is zero. - */ -static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned int quality) -{ - struct nr_neigh *nr_neigh; - - nr_neigh = nr_neigh_get_dev(callsign, dev); - - if (nr_neigh == NULL) return -EINVAL; - - nr_neigh->quality = quality; - nr_neigh->locked = 0; - - if (nr_neigh->count == 0) - nr_remove_neigh(nr_neigh); - nr_neigh_put(nr_neigh); - - return 0; -} - -/* - * Decrement the obsolescence count by one. If a route is reduced to a - * count of zero, remove it. Also remove any unlocked neighbours with - * zero nodes routing via it. - */ -static int nr_dec_obs(void) -{ - struct nr_neigh *nr_neigh; - struct nr_node *s; - struct hlist_node *nodet; - int i; - - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each_safe(s, nodet, &nr_node_list) { - nr_node_lock(s); - for (i = 0; i < s->count; i++) { - switch (s->routes[i].obs_count) { - case 0: /* A locked entry */ - break; - - case 1: /* From 1 -> 0 */ - nr_neigh = s->routes[i].neighbour; - - nr_neigh->count--; - nr_neigh_put(nr_neigh); - - if (nr_neigh->count == 0 && !nr_neigh->locked) - nr_remove_neigh(nr_neigh); - - s->count--; - - switch (i) { - case 0: - s->routes[0] = s->routes[1]; - fallthrough; - case 1: - s->routes[1] = s->routes[2]; - break; - case 2: - break; - } - break; - - default: - s->routes[i].obs_count--; - break; - - } - } - - if (s->count <= 0) - nr_remove_node_locked(s); - nr_node_unlock(s); - } - spin_unlock_bh(&nr_node_list_lock); - - return 0; -} - -/* - * A device has been removed. Remove its routes and neighbours. - */ -void nr_rt_device_down(struct net_device *dev) -{ - struct nr_neigh *s; - struct hlist_node *nodet, *node2t; - struct nr_node *t; - int i; - - spin_lock_bh(&nr_neigh_list_lock); - nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) { - if (s->dev == dev) { - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each_safe(t, node2t, &nr_node_list) { - nr_node_lock(t); - for (i = 0; i < t->count; i++) { - if (t->routes[i].neighbour == s) { - t->count--; - - switch (i) { - case 0: - t->routes[0] = t->routes[1]; - fallthrough; - case 1: - t->routes[1] = t->routes[2]; - break; - case 2: - break; - } - } - } - - if (t->count <= 0) - nr_remove_node_locked(t); - nr_node_unlock(t); - } - spin_unlock_bh(&nr_node_list_lock); - - nr_remove_neigh_locked(s); - } - } - spin_unlock_bh(&nr_neigh_list_lock); -} - -/* - * Check that the device given is a valid AX.25 interface that is "up". - * Or a valid ethernet interface with an AX.25 callsign binding. - */ -static struct net_device *nr_ax25_dev_get(char *devname) -{ - struct net_device *dev; - - if ((dev = dev_get_by_name(&init_net, devname)) == NULL) - return NULL; - - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25) - return dev; - - dev_put(dev); - return NULL; -} - -/* - * Find the first active NET/ROM device, usually "nr0". - */ -struct net_device *nr_dev_first(void) -{ - struct net_device *dev, *first = NULL; - - rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM) - if (first == NULL || strncmp(dev->name, first->name, 3) < 0) - first = dev; - } - dev_hold(first); - rcu_read_unlock(); - - return first; -} - -/* - * Find the NET/ROM device for the given callsign. - */ -struct net_device *nr_dev_get(ax25_address *addr) -{ - struct net_device *dev; - - rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && - ax25cmp(addr, (const ax25_address *)dev->dev_addr) == 0) { - dev_hold(dev); - goto out; - } - } - dev = NULL; -out: - rcu_read_unlock(); - return dev; -} - -static ax25_digi *nr_call_to_digi(ax25_digi *digi, int ndigis, - ax25_address *digipeaters) -{ - int i; - - if (ndigis == 0) - return NULL; - - for (i = 0; i < ndigis; i++) { - digi->calls[i] = digipeaters[i]; - digi->repeated[i] = 0; - } - - digi->ndigi = ndigis; - digi->lastrepeat = -1; - - return digi; -} - -/* - * Handle the ioctls that control the routing functions. - */ -int nr_rt_ioctl(unsigned int cmd, void __user *arg) -{ - struct nr_route_struct nr_route; - struct net_device *dev; - ax25_digi digi; - int ret; - - switch (cmd) { - case SIOCADDRT: - if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) - return -EFAULT; - if (nr_route.ndigis > AX25_MAX_DIGIS) - return -EINVAL; - if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) - return -EINVAL; - switch (nr_route.type) { - case NETROM_NODE: - if (strnlen(nr_route.mnemonic, 7) == 7) { - ret = -EINVAL; - break; - } - - ret = nr_add_node(&nr_route.callsign, - nr_route.mnemonic, - &nr_route.neighbour, - nr_call_to_digi(&digi, nr_route.ndigis, - nr_route.digipeaters), - dev, nr_route.quality, - nr_route.obs_count); - break; - case NETROM_NEIGH: - ret = nr_add_neigh(&nr_route.callsign, - nr_call_to_digi(&digi, nr_route.ndigis, - nr_route.digipeaters), - dev, nr_route.quality); - break; - default: - ret = -EINVAL; - } - dev_put(dev); - return ret; - - case SIOCDELRT: - if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) - return -EFAULT; - if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) - return -EINVAL; - switch (nr_route.type) { - case NETROM_NODE: - ret = nr_del_node(&nr_route.callsign, - &nr_route.neighbour, dev); - break; - case NETROM_NEIGH: - ret = nr_del_neigh(&nr_route.callsign, - dev, nr_route.quality); - break; - default: - ret = -EINVAL; - } - dev_put(dev); - return ret; - - case SIOCNRDECOBS: - return nr_dec_obs(); - - default: - return -EINVAL; - } - - return 0; -} - -/* - * A level 2 link has timed out, therefore it appears to be a poor link, - * then don't use that neighbour until it is reset. - */ -void nr_link_failed(ax25_cb *ax25, int reason) -{ - struct nr_neigh *s, *nr_neigh = NULL; - struct nr_node *nr_node = NULL; - - spin_lock_bh(&nr_neigh_list_lock); - nr_neigh_for_each(s, &nr_neigh_list) { - if (s->ax25 == ax25) { - nr_neigh_hold(s); - nr_neigh = s; - break; - } - } - spin_unlock_bh(&nr_neigh_list_lock); - - if (nr_neigh == NULL) - return; - - nr_neigh->ax25 = NULL; - ax25_cb_put(ax25); - - if (++nr_neigh->failed < READ_ONCE(sysctl_netrom_link_fails_count)) { - nr_neigh_put(nr_neigh); - return; - } - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each(nr_node, &nr_node_list) { - nr_node_lock(nr_node); - if (nr_node->which < nr_node->count && - nr_node->routes[nr_node->which].neighbour == nr_neigh) - nr_node->which++; - nr_node_unlock(nr_node); - } - spin_unlock_bh(&nr_node_list_lock); - nr_neigh_put(nr_neigh); -} - -/* - * Route a frame to an appropriate AX.25 connection. A NULL ax25_cb - * indicates an internally generated frame. - */ -int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25) -{ - ax25_address *nr_src, *nr_dest; - struct nr_neigh *nr_neigh; - struct nr_node *nr_node; - struct net_device *dev; - unsigned char *dptr; - ax25_cb *ax25s; - int ret; - struct sk_buff *nskb, *oskb; - - /* - * Reject malformed packets early. Check that it contains at least 2 - * addresses and 1 byte more for Time-To-Live - */ - if (skb->len < 2 * sizeof(ax25_address) + 1) - return 0; - - nr_src = (ax25_address *)(skb->data + 0); - nr_dest = (ax25_address *)(skb->data + 7); - - if (ax25 != NULL) { - ret = nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat, - ax25->ax25_dev->dev, 0, - READ_ONCE(sysctl_netrom_obsolescence_count_initialiser)); - if (ret) - return ret; - } - - if ((dev = nr_dev_get(nr_dest)) != NULL) { /* Its for me */ - if (ax25 == NULL) /* Its from me */ - ret = nr_loopback_queue(skb); - else - ret = nr_rx_frame(skb, dev); - dev_put(dev); - return ret; - } - - if (!READ_ONCE(sysctl_netrom_routing_control) && ax25 != NULL) - return 0; - - /* Its Time-To-Live has expired */ - if (skb->data[14] == 1) { - return 0; - } - - nr_node = nr_node_get(nr_dest); - if (nr_node == NULL) - return 0; - nr_node_lock(nr_node); - - if (nr_node->which >= nr_node->count) { - nr_node_unlock(nr_node); - nr_node_put(nr_node); - return 0; - } - - nr_neigh = nr_node->routes[nr_node->which].neighbour; - - if ((dev = nr_dev_first()) == NULL) { - nr_node_unlock(nr_node); - nr_node_put(nr_node); - return 0; - } - - /* We are going to change the netrom headers so we should get our - own skb, we also did not know until now how much header space - we had to reserve... - RXQ */ - nskb = skb_copy_expand(skb, dev->hard_header_len, 0, GFP_ATOMIC); - - if (!nskb) { - nr_node_unlock(nr_node); - nr_node_put(nr_node); - dev_put(dev); - return 0; - } - oskb = skb; - skb = nskb; - skb->data[14]--; - - dptr = skb_push(skb, 1); - *dptr = AX25_P_NETROM; - - ax25s = nr_neigh->ax25; - nr_neigh->ax25 = ax25_send_frame(skb, 256, - (const ax25_address *)dev->dev_addr, - &nr_neigh->callsign, - nr_neigh->digipeat, nr_neigh->dev); - if (ax25s) - ax25_cb_put(ax25s); - - dev_put(dev); - ret = (nr_neigh->ax25 != NULL); - nr_node_unlock(nr_node); - nr_node_put(nr_node); - - if (ret) - kfree_skb(oskb); - - return ret; -} - -#ifdef CONFIG_PROC_FS - -static void *nr_node_start(struct seq_file *seq, loff_t *pos) - __acquires(&nr_node_list_lock) -{ - spin_lock_bh(&nr_node_list_lock); - return seq_hlist_start_head(&nr_node_list, *pos); -} - -static void *nr_node_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &nr_node_list, pos); -} - -static void nr_node_stop(struct seq_file *seq, void *v) - __releases(&nr_node_list_lock) -{ - spin_unlock_bh(&nr_node_list_lock); -} - -static int nr_node_show(struct seq_file *seq, void *v) -{ - char buf[11]; - int i; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "callsign mnemonic w n qual obs neigh qual obs neigh qual obs neigh\n"); - else { - struct nr_node *nr_node = hlist_entry(v, struct nr_node, - node_node); - - nr_node_lock(nr_node); - seq_printf(seq, "%-9s %-7s %d %d", - ax2asc(buf, &nr_node->callsign), - (nr_node->mnemonic[0] == '\0') ? "*" : nr_node->mnemonic, - nr_node->which + 1, - nr_node->count); - - for (i = 0; i < nr_node->count; i++) { - seq_printf(seq, " %3d %d %05d", - nr_node->routes[i].quality, - nr_node->routes[i].obs_count, - nr_node->routes[i].neighbour->number); - } - nr_node_unlock(nr_node); - - seq_puts(seq, "\n"); - } - return 0; -} - -const struct seq_operations nr_node_seqops = { - .start = nr_node_start, - .next = nr_node_next, - .stop = nr_node_stop, - .show = nr_node_show, -}; - -static void *nr_neigh_start(struct seq_file *seq, loff_t *pos) - __acquires(&nr_neigh_list_lock) -{ - spin_lock_bh(&nr_neigh_list_lock); - return seq_hlist_start_head(&nr_neigh_list, *pos); -} - -static void *nr_neigh_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &nr_neigh_list, pos); -} - -static void nr_neigh_stop(struct seq_file *seq, void *v) - __releases(&nr_neigh_list_lock) -{ - spin_unlock_bh(&nr_neigh_list_lock); -} - -static int nr_neigh_show(struct seq_file *seq, void *v) -{ - char buf[11]; - int i; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, "addr callsign dev qual lock count failed digipeaters\n"); - else { - struct nr_neigh *nr_neigh; - - nr_neigh = hlist_entry(v, struct nr_neigh, neigh_node); - seq_printf(seq, "%05d %-9s %-4s %3d %d %3d %3d", - nr_neigh->number, - ax2asc(buf, &nr_neigh->callsign), - nr_neigh->dev ? nr_neigh->dev->name : "???", - nr_neigh->quality, - nr_neigh->locked, - nr_neigh->count, - nr_neigh->failed); - - if (nr_neigh->digipeat != NULL) { - for (i = 0; i < nr_neigh->digipeat->ndigi; i++) - seq_printf(seq, " %s", - ax2asc(buf, &nr_neigh->digipeat->calls[i])); - } - - seq_puts(seq, "\n"); - } - return 0; -} - -const struct seq_operations nr_neigh_seqops = { - .start = nr_neigh_start, - .next = nr_neigh_next, - .stop = nr_neigh_stop, - .show = nr_neigh_show, -}; -#endif - -/* - * Free all memory associated with the nodes and routes lists. - */ -void nr_rt_free(void) -{ - struct nr_neigh *s = NULL; - struct nr_node *t = NULL; - struct hlist_node *nodet; - - spin_lock_bh(&nr_neigh_list_lock); - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each_safe(t, nodet, &nr_node_list) { - nr_node_lock(t); - nr_remove_node_locked(t); - nr_node_unlock(t); - } - nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) { - while(s->count) { - s->count--; - nr_neigh_put(s); - } - nr_remove_neigh_locked(s); - } - spin_unlock_bh(&nr_node_list_lock); - spin_unlock_bh(&nr_neigh_list_lock); -} diff --git a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c deleted file mode 100644 index c3bbd5880850..000000000000 --- a/net/netrom/nr_subr.c +++ /dev/null @@ -1,280 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * This routine purges all of the queues of frames. - */ -void nr_clear_queues(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - skb_queue_purge(&sk->sk_write_queue); - skb_queue_purge(&nr->ack_queue); - skb_queue_purge(&nr->reseq_queue); - skb_queue_purge(&nr->frag_queue); -} - -/* - * This routine purges the input queue of those frames that have been - * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the - * SDL diagram. - */ -void nr_frames_acked(struct sock *sk, unsigned short nr) -{ - struct nr_sock *nrom = nr_sk(sk); - struct sk_buff *skb; - - /* - * Remove all the ack-ed frames from the ack queue. - */ - if (nrom->va != nr) { - while (skb_peek(&nrom->ack_queue) != NULL && nrom->va != nr) { - skb = skb_dequeue(&nrom->ack_queue); - kfree_skb(skb); - nrom->va = (nrom->va + 1) % NR_MODULUS; - } - } -} - -/* - * Requeue all the un-ack-ed frames on the output queue to be picked - * up by nr_kick called from the timer. This arrangement handles the - * possibility of an empty output queue. - */ -void nr_requeue_frames(struct sock *sk) -{ - struct sk_buff *skb, *skb_prev = NULL; - - while ((skb = skb_dequeue(&nr_sk(sk)->ack_queue)) != NULL) { - if (skb_prev == NULL) - skb_queue_head(&sk->sk_write_queue, skb); - else - skb_append(skb_prev, skb, &sk->sk_write_queue); - skb_prev = skb; - } -} - -/* - * Validate that the value of nr is between va and vs. Return true or - * false for testing. - */ -int nr_validate_nr(struct sock *sk, unsigned short nr) -{ - struct nr_sock *nrom = nr_sk(sk); - unsigned short vc = nrom->va; - - while (vc != nrom->vs) { - if (nr == vc) return 1; - vc = (vc + 1) % NR_MODULUS; - } - - return nr == nrom->vs; -} - -/* - * Check that ns is within the receive window. - */ -int nr_in_rx_window(struct sock *sk, unsigned short ns) -{ - struct nr_sock *nr = nr_sk(sk); - unsigned short vc = nr->vr; - unsigned short vt = (nr->vl + nr->window) % NR_MODULUS; - - while (vc != vt) { - if (ns == vc) return 1; - vc = (vc + 1) % NR_MODULUS; - } - - return 0; -} - -/* - * This routine is called when the HDLC layer internally generates a - * control frame. - */ -void nr_write_internal(struct sock *sk, int frametype) -{ - struct nr_sock *nr = nr_sk(sk); - struct sk_buff *skb; - unsigned char *dptr; - int len, timeout; - - len = NR_TRANSPORT_LEN; - - switch (frametype & 0x0F) { - case NR_CONNREQ: - len += 17; - break; - case NR_CONNACK: - len += (nr->bpqext) ? 2 : 1; - break; - case NR_DISCREQ: - case NR_DISCACK: - case NR_INFOACK: - break; - default: - printk(KERN_ERR "NET/ROM: nr_write_internal - invalid frame type %d\n", frametype); - return; - } - - skb = alloc_skb(NR_NETWORK_LEN + len, GFP_ATOMIC); - if (!skb) - return; - - /* - * Space for AX.25 and NET/ROM network header - */ - skb_reserve(skb, NR_NETWORK_LEN); - - dptr = skb_put(skb, len); - - switch (frametype & 0x0F) { - case NR_CONNREQ: - timeout = nr->t1 / HZ; - *dptr++ = nr->my_index; - *dptr++ = nr->my_id; - *dptr++ = 0; - *dptr++ = 0; - *dptr++ = frametype; - *dptr++ = nr->window; - memcpy(dptr, &nr->user_addr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] &= ~AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - memcpy(dptr, &nr->source_addr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] &= ~AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - *dptr++ = timeout % 256; - *dptr++ = timeout / 256; - break; - - case NR_CONNACK: - *dptr++ = nr->your_index; - *dptr++ = nr->your_id; - *dptr++ = nr->my_index; - *dptr++ = nr->my_id; - *dptr++ = frametype; - *dptr++ = nr->window; - if (nr->bpqext) - *dptr++ = READ_ONCE(sysctl_netrom_network_ttl_initialiser); - break; - - case NR_DISCREQ: - case NR_DISCACK: - *dptr++ = nr->your_index; - *dptr++ = nr->your_id; - *dptr++ = 0; - *dptr++ = 0; - *dptr++ = frametype; - break; - - case NR_INFOACK: - *dptr++ = nr->your_index; - *dptr++ = nr->your_id; - *dptr++ = 0; - *dptr++ = nr->vr; - *dptr++ = frametype; - break; - } - - nr_transmit_buffer(sk, skb); -} - -/* - * This routine is called to send an error reply. - */ -void __nr_transmit_reply(struct sk_buff *skb, int mine, unsigned char cmdflags) -{ - struct sk_buff *skbn; - unsigned char *dptr; - int len; - - len = NR_NETWORK_LEN + NR_TRANSPORT_LEN + 1; - - if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skbn, 0); - - dptr = skb_put(skbn, NR_NETWORK_LEN + NR_TRANSPORT_LEN); - - skb_copy_from_linear_data_offset(skb, 7, dptr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] &= ~AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - - skb_copy_from_linear_data(skb, dptr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] |= AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - - *dptr++ = READ_ONCE(sysctl_netrom_network_ttl_initialiser); - - if (mine) { - *dptr++ = 0; - *dptr++ = 0; - *dptr++ = skb->data[15]; - *dptr++ = skb->data[16]; - } else { - *dptr++ = skb->data[15]; - *dptr++ = skb->data[16]; - *dptr++ = 0; - *dptr++ = 0; - } - - *dptr++ = cmdflags; - *dptr++ = 0; - - if (!nr_route_frame(skbn, NULL)) - kfree_skb(skbn); -} - -void nr_disconnect(struct sock *sk, int reason) -{ - nr_stop_t1timer(sk); - nr_stop_t2timer(sk); - nr_stop_t4timer(sk); - nr_stop_idletimer(sk); - - nr_clear_queues(sk); - - nr_sk(sk)->state = NR_STATE_0; - - sk->sk_state = TCP_CLOSE; - sk->sk_err = reason; - sk->sk_shutdown |= SEND_SHUTDOWN; - - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - } -} diff --git a/net/netrom/nr_timer.c b/net/netrom/nr_timer.c deleted file mode 100644 index b3a62b1f3a09..000000000000 --- a/net/netrom/nr_timer.c +++ /dev/null @@ -1,249 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void nr_heartbeat_expiry(struct timer_list *); -static void nr_t1timer_expiry(struct timer_list *); -static void nr_t2timer_expiry(struct timer_list *); -static void nr_t4timer_expiry(struct timer_list *); -static void nr_idletimer_expiry(struct timer_list *); - -void nr_init_timers(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - timer_setup(&nr->t1timer, nr_t1timer_expiry, 0); - timer_setup(&nr->t2timer, nr_t2timer_expiry, 0); - timer_setup(&nr->t4timer, nr_t4timer_expiry, 0); - timer_setup(&nr->idletimer, nr_idletimer_expiry, 0); - - /* initialized by sock_init_data */ - sk->sk_timer.function = nr_heartbeat_expiry; -} - -void nr_start_t1timer(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - sk_reset_timer(sk, &nr->t1timer, jiffies + nr->t1); -} - -void nr_start_t2timer(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - sk_reset_timer(sk, &nr->t2timer, jiffies + nr->t2); -} - -void nr_start_t4timer(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - sk_reset_timer(sk, &nr->t4timer, jiffies + nr->t4); -} - -void nr_start_idletimer(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - if (nr->idle > 0) - sk_reset_timer(sk, &nr->idletimer, jiffies + nr->idle); -} - -void nr_start_heartbeat(struct sock *sk) -{ - sk_reset_timer(sk, &sk->sk_timer, jiffies + 5 * HZ); -} - -void nr_stop_t1timer(struct sock *sk) -{ - sk_stop_timer(sk, &nr_sk(sk)->t1timer); -} - -void nr_stop_t2timer(struct sock *sk) -{ - sk_stop_timer(sk, &nr_sk(sk)->t2timer); -} - -void nr_stop_t4timer(struct sock *sk) -{ - sk_stop_timer(sk, &nr_sk(sk)->t4timer); -} - -void nr_stop_idletimer(struct sock *sk) -{ - sk_stop_timer(sk, &nr_sk(sk)->idletimer); -} - -void nr_stop_heartbeat(struct sock *sk) -{ - sk_stop_timer(sk, &sk->sk_timer); -} - -int nr_t1timer_running(struct sock *sk) -{ - return timer_pending(&nr_sk(sk)->t1timer); -} - -static void nr_heartbeat_expiry(struct timer_list *t) -{ - struct sock *sk = timer_container_of(sk, t, sk_timer); - struct nr_sock *nr = nr_sk(sk); - - bh_lock_sock(sk); - switch (nr->state) { - case NR_STATE_0: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (sock_flag(sk, SOCK_DESTROY) || - (sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) { - if (sk->sk_state == TCP_LISTEN) - sock_hold(sk); - bh_unlock_sock(sk); - nr_destroy_socket(sk); - goto out; - } - break; - - case NR_STATE_3: - /* - * Check for the state of the receive buffer. - */ - if (atomic_read(&sk->sk_rmem_alloc) < (sk->sk_rcvbuf / 2) && - (nr->condition & NR_COND_OWN_RX_BUSY)) { - nr->condition &= ~NR_COND_OWN_RX_BUSY; - nr->condition &= ~NR_COND_ACK_PENDING; - nr->vl = nr->vr; - nr_write_internal(sk, NR_INFOACK); - break; - } - break; - } - - nr_start_heartbeat(sk); - bh_unlock_sock(sk); -out: - sock_put(sk); -} - -static void nr_t2timer_expiry(struct timer_list *t) -{ - struct nr_sock *nr = timer_container_of(nr, t, t2timer); - struct sock *sk = &nr->sock; - - bh_lock_sock(sk); - if (nr->condition & NR_COND_ACK_PENDING) { - nr->condition &= ~NR_COND_ACK_PENDING; - nr_enquiry_response(sk); - } - bh_unlock_sock(sk); - sock_put(sk); -} - -static void nr_t4timer_expiry(struct timer_list *t) -{ - struct nr_sock *nr = timer_container_of(nr, t, t4timer); - struct sock *sk = &nr->sock; - - bh_lock_sock(sk); - nr_sk(sk)->condition &= ~NR_COND_PEER_RX_BUSY; - bh_unlock_sock(sk); - sock_put(sk); -} - -static void nr_idletimer_expiry(struct timer_list *t) -{ - struct nr_sock *nr = timer_container_of(nr, t, idletimer); - struct sock *sk = &nr->sock; - - bh_lock_sock(sk); - - nr_clear_queues(sk); - - nr->n2count = 0; - nr_write_internal(sk, NR_DISCREQ); - nr->state = NR_STATE_2; - - nr_start_t1timer(sk); - nr_stop_t2timer(sk); - nr_stop_t4timer(sk); - - sk->sk_state = TCP_CLOSE; - sk->sk_err = 0; - sk->sk_shutdown |= SEND_SHUTDOWN; - - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - } - bh_unlock_sock(sk); - sock_put(sk); -} - -static void nr_t1timer_expiry(struct timer_list *t) -{ - struct nr_sock *nr = timer_container_of(nr, t, t1timer); - struct sock *sk = &nr->sock; - - bh_lock_sock(sk); - switch (nr->state) { - case NR_STATE_1: - if (nr->n2count == nr->n2) { - nr_disconnect(sk, ETIMEDOUT); - goto out; - } else { - nr->n2count++; - nr_write_internal(sk, NR_CONNREQ); - } - break; - - case NR_STATE_2: - if (nr->n2count == nr->n2) { - nr_disconnect(sk, ETIMEDOUT); - goto out; - } else { - nr->n2count++; - nr_write_internal(sk, NR_DISCREQ); - } - break; - - case NR_STATE_3: - if (nr->n2count == nr->n2) { - nr_disconnect(sk, ETIMEDOUT); - goto out; - } else { - nr->n2count++; - nr_requeue_frames(sk); - } - break; - } - - nr_start_t1timer(sk); -out: - bh_unlock_sock(sk); - sock_put(sk); -} diff --git a/net/netrom/sysctl_net_netrom.c b/net/netrom/sysctl_net_netrom.c deleted file mode 100644 index 7dc0fa628f2e..000000000000 --- a/net/netrom/sysctl_net_netrom.c +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com) - */ -#include -#include -#include -#include -#include - -/* - * Values taken from NET/ROM documentation. - */ -static int min_quality[] = {0}, max_quality[] = {255}; -static int min_obs[] = {0}, max_obs[] = {255}; -static int min_ttl[] = {0}, max_ttl[] = {255}; -static int min_t1[] = {5 * HZ}; -static int max_t1[] = {600 * HZ}; -static int min_n2[] = {2}, max_n2[] = {127}; -static int min_t2[] = {1 * HZ}; -static int max_t2[] = {60 * HZ}; -static int min_t4[] = {1 * HZ}; -static int max_t4[] = {1000 * HZ}; -static int min_window[] = {1}, max_window[] = {127}; -static int min_idle[] = {0 * HZ}; -static int max_idle[] = {65535 * HZ}; -static int min_route[] = {0}, max_route[] = {1}; -static int min_fails[] = {1}, max_fails[] = {10}; -static int min_reset[] = {0}, max_reset[] = {1}; - -static struct ctl_table_header *nr_table_header; - -static struct ctl_table nr_table[] = { - { - .procname = "default_path_quality", - .data = &sysctl_netrom_default_path_quality, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_quality, - .extra2 = &max_quality - }, - { - .procname = "obsolescence_count_initialiser", - .data = &sysctl_netrom_obsolescence_count_initialiser, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_obs, - .extra2 = &max_obs - }, - { - .procname = "network_ttl_initialiser", - .data = &sysctl_netrom_network_ttl_initialiser, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_ttl, - .extra2 = &max_ttl - }, - { - .procname = "transport_timeout", - .data = &sysctl_netrom_transport_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t1, - .extra2 = &max_t1 - }, - { - .procname = "transport_maximum_tries", - .data = &sysctl_netrom_transport_maximum_tries, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_n2, - .extra2 = &max_n2 - }, - { - .procname = "transport_acknowledge_delay", - .data = &sysctl_netrom_transport_acknowledge_delay, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t2, - .extra2 = &max_t2 - }, - { - .procname = "transport_busy_delay", - .data = &sysctl_netrom_transport_busy_delay, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t4, - .extra2 = &max_t4 - }, - { - .procname = "transport_requested_window_size", - .data = &sysctl_netrom_transport_requested_window_size, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_window, - .extra2 = &max_window - }, - { - .procname = "transport_no_activity_timeout", - .data = &sysctl_netrom_transport_no_activity_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_idle, - .extra2 = &max_idle - }, - { - .procname = "routing_control", - .data = &sysctl_netrom_routing_control, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_route, - .extra2 = &max_route - }, - { - .procname = "link_fails_count", - .data = &sysctl_netrom_link_fails_count, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_fails, - .extra2 = &max_fails - }, - { - .procname = "reset", - .data = &sysctl_netrom_reset_circuit, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_reset, - .extra2 = &max_reset - }, -}; - -int __init nr_register_sysctl(void) -{ - nr_table_header = register_net_sysctl(&init_net, "net/netrom", nr_table); - if (!nr_table_header) - return -ENOMEM; - return 0; -} - -void nr_unregister_sysctl(void) -{ - unregister_net_sysctl_table(nr_table_header); -} diff --git a/net/rose/Makefile b/net/rose/Makefile deleted file mode 100644 index 3e6638f5ba57..000000000000 --- a/net/rose/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Linux Rose (X.25 PLP) layer. -# - -obj-$(CONFIG_ROSE) += rose.o - -rose-y := af_rose.o rose_dev.o rose_in.o rose_link.o rose_loopback.o \ - rose_out.o rose_route.o rose_subr.o rose_timer.o -rose-$(CONFIG_SYSCTL) += sysctl_net_rose.o diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c deleted file mode 100644 index d5032840ee48..000000000000 --- a/net/rose/af_rose.c +++ /dev/null @@ -1,1687 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Terry Dawson VK2KTJ (terry@animats.net) - * Copyright (C) Tomi Manninen OH2BNS (oh2bns@sral.fi) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int rose_ndevs = 10; - -int sysctl_rose_restart_request_timeout = ROSE_DEFAULT_T0; -int sysctl_rose_call_request_timeout = ROSE_DEFAULT_T1; -int sysctl_rose_reset_request_timeout = ROSE_DEFAULT_T2; -int sysctl_rose_clear_request_timeout = ROSE_DEFAULT_T3; -int sysctl_rose_no_activity_timeout = ROSE_DEFAULT_IDLE; -int sysctl_rose_ack_hold_back_timeout = ROSE_DEFAULT_HB; -int sysctl_rose_routing_control = ROSE_DEFAULT_ROUTING; -int sysctl_rose_link_fail_timeout = ROSE_DEFAULT_FAIL_TIMEOUT; -int sysctl_rose_maximum_vcs = ROSE_DEFAULT_MAXVC; -int sysctl_rose_window_size = ROSE_DEFAULT_WINDOW_SIZE; - -static HLIST_HEAD(rose_list); -static DEFINE_SPINLOCK(rose_list_lock); - -static const struct proto_ops rose_proto_ops; - -ax25_address rose_callsign; - -/* - * ROSE network devices are virtual network devices encapsulating ROSE - * frames into AX.25 which will be sent through an AX.25 device, so form a - * special "super class" of normal net devices; split their locks off into a - * separate class since they always nest. - */ -static struct lock_class_key rose_netdev_xmit_lock_key; -static struct lock_class_key rose_netdev_addr_lock_key; - -static void rose_set_lockdep_one(struct net_device *dev, - struct netdev_queue *txq, - void *_unused) -{ - lockdep_set_class(&txq->_xmit_lock, &rose_netdev_xmit_lock_key); -} - -static void rose_set_lockdep_key(struct net_device *dev) -{ - lockdep_set_class(&dev->addr_list_lock, &rose_netdev_addr_lock_key); - netdev_for_each_tx_queue(dev, rose_set_lockdep_one, NULL); -} - -/* - * Convert a ROSE address into text. - */ -char *rose2asc(char *buf, const rose_address *addr) -{ - if (addr->rose_addr[0] == 0x00 && addr->rose_addr[1] == 0x00 && - addr->rose_addr[2] == 0x00 && addr->rose_addr[3] == 0x00 && - addr->rose_addr[4] == 0x00) { - strcpy(buf, "*"); - } else { - sprintf(buf, "%02X%02X%02X%02X%02X", addr->rose_addr[0] & 0xFF, - addr->rose_addr[1] & 0xFF, - addr->rose_addr[2] & 0xFF, - addr->rose_addr[3] & 0xFF, - addr->rose_addr[4] & 0xFF); - } - - return buf; -} - -/* - * Compare two ROSE addresses, 0 == equal. - */ -int rosecmp(const rose_address *addr1, const rose_address *addr2) -{ - int i; - - for (i = 0; i < 5; i++) - if (addr1->rose_addr[i] != addr2->rose_addr[i]) - return 1; - - return 0; -} - -/* - * Compare two ROSE addresses for only mask digits, 0 == equal. - */ -int rosecmpm(const rose_address *addr1, const rose_address *addr2, - unsigned short mask) -{ - unsigned int i, j; - - if (mask > 10) - return 1; - - for (i = 0; i < mask; i++) { - j = i / 2; - - if ((i % 2) != 0) { - if ((addr1->rose_addr[j] & 0x0F) != (addr2->rose_addr[j] & 0x0F)) - return 1; - } else { - if ((addr1->rose_addr[j] & 0xF0) != (addr2->rose_addr[j] & 0xF0)) - return 1; - } - } - - return 0; -} - -/* - * Socket removal during an interrupt is now safe. - */ -static void rose_remove_socket(struct sock *sk) -{ - spin_lock_bh(&rose_list_lock); - sk_del_node_init(sk); - spin_unlock_bh(&rose_list_lock); -} - -/* - * Kill all bound sockets on a broken link layer connection to a - * particular neighbour. - */ -void rose_kill_by_neigh(struct rose_neigh *neigh) -{ - struct sock *s; - - spin_lock_bh(&rose_list_lock); - sk_for_each(s, &rose_list) { - struct rose_sock *rose = rose_sk(s); - - if (rose->neighbour == neigh) { - rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); - rose_neigh_put(rose->neighbour); - rose->neighbour = NULL; - } - } - spin_unlock_bh(&rose_list_lock); -} - -/* - * Kill all bound sockets on a dropped device. - */ -static void rose_kill_by_device(struct net_device *dev) -{ - struct sock *sk, *array[16]; - struct rose_sock *rose; - bool rescan; - int i, cnt; - -start: - rescan = false; - cnt = 0; - spin_lock_bh(&rose_list_lock); - sk_for_each(sk, &rose_list) { - rose = rose_sk(sk); - if (rose->device == dev) { - if (cnt == ARRAY_SIZE(array)) { - rescan = true; - break; - } - sock_hold(sk); - array[cnt++] = sk; - } - } - spin_unlock_bh(&rose_list_lock); - - for (i = 0; i < cnt; i++) { - sk = array[i]; - rose = rose_sk(sk); - lock_sock(sk); - spin_lock_bh(&rose_list_lock); - if (rose->device == dev) { - rose_disconnect(sk, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); - if (rose->neighbour) - rose_neigh_put(rose->neighbour); - netdev_put(rose->device, &rose->dev_tracker); - rose->device = NULL; - } - spin_unlock_bh(&rose_list_lock); - release_sock(sk); - sock_put(sk); - cond_resched(); - } - if (rescan) - goto start; -} - -/* - * Handle device status changes. - */ -static int rose_device_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event != NETDEV_DOWN) - return NOTIFY_DONE; - - switch (dev->type) { - case ARPHRD_ROSE: - rose_kill_by_device(dev); - break; - case ARPHRD_AX25: - rose_link_device_down(dev); - rose_rt_device_down(dev); - break; - } - - return NOTIFY_DONE; -} - -/* - * Add a socket to the bound sockets list. - */ -static void rose_insert_socket(struct sock *sk) -{ - - spin_lock_bh(&rose_list_lock); - sk_add_node(sk, &rose_list); - spin_unlock_bh(&rose_list_lock); -} - -/* - * Find a socket that wants to accept the Call Request we just - * received. - */ -static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) -{ - struct sock *s; - - spin_lock_bh(&rose_list_lock); - sk_for_each(s, &rose_list) { - struct rose_sock *rose = rose_sk(s); - - if (!rosecmp(&rose->source_addr, addr) && - !ax25cmp(&rose->source_call, call) && - !rose->source_ndigis && s->sk_state == TCP_LISTEN) - goto found; - } - - sk_for_each(s, &rose_list) { - struct rose_sock *rose = rose_sk(s); - - if (!rosecmp(&rose->source_addr, addr) && - !ax25cmp(&rose->source_call, &null_ax25_address) && - s->sk_state == TCP_LISTEN) - goto found; - } - s = NULL; -found: - spin_unlock_bh(&rose_list_lock); - return s; -} - -/* - * Find a connected ROSE socket given my LCI and device. - */ -struct sock *rose_find_socket(unsigned int lci, struct rose_neigh *neigh) -{ - struct sock *s; - - spin_lock_bh(&rose_list_lock); - sk_for_each(s, &rose_list) { - struct rose_sock *rose = rose_sk(s); - - if (rose->lci == lci && rose->neighbour == neigh) - goto found; - } - s = NULL; -found: - spin_unlock_bh(&rose_list_lock); - return s; -} - -/* - * Find a unique LCI for a given device. - */ -unsigned int rose_new_lci(struct rose_neigh *neigh) -{ - int lci; - - if (neigh->dce_mode) { - for (lci = 1; lci <= sysctl_rose_maximum_vcs; lci++) - if (rose_find_socket(lci, neigh) == NULL && rose_route_free_lci(lci, neigh) == NULL) - return lci; - } else { - for (lci = sysctl_rose_maximum_vcs; lci > 0; lci--) - if (rose_find_socket(lci, neigh) == NULL && rose_route_free_lci(lci, neigh) == NULL) - return lci; - } - - return 0; -} - -/* - * Deferred destroy. - */ -void rose_destroy_socket(struct sock *); - -/* - * Handler for deferred kills. - */ -static void rose_destroy_timer(struct timer_list *t) -{ - struct sock *sk = timer_container_of(sk, t, sk_timer); - - rose_destroy_socket(sk); -} - -/* - * This is called from user mode and the timers. Thus it protects itself - * against interrupt users but doesn't worry about being called during - * work. Once it is removed from the queue no interrupt or bottom half - * will touch it and we are (fairly 8-) ) safe. - */ -void rose_destroy_socket(struct sock *sk) -{ - struct sk_buff *skb; - - rose_remove_socket(sk); - rose_stop_heartbeat(sk); - rose_stop_idletimer(sk); - rose_stop_timer(sk); - - rose_clear_queues(sk); /* Flush the queues */ - - while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { - if (skb->sk != sk) { /* A pending connection */ - /* Queue the unaccepted socket for death */ - sock_set_flag(skb->sk, SOCK_DEAD); - rose_start_heartbeat(skb->sk); - rose_sk(skb->sk)->state = ROSE_STATE_0; - } - - kfree_skb(skb); - } - - if (sk_has_allocations(sk)) { - /* Defer: outstanding buffers */ - timer_setup(&sk->sk_timer, rose_destroy_timer, 0); - sk->sk_timer.expires = jiffies + 10 * HZ; - add_timer(&sk->sk_timer); - } else - sock_put(sk); -} - -/* - * Handling for system calls applied via the various interfaces to a - * ROSE socket object. - */ - -static int rose_setsockopt(struct socket *sock, int level, int optname, - sockptr_t optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - unsigned int opt; - - if (level != SOL_ROSE) - return -ENOPROTOOPT; - - if (optlen < sizeof(unsigned int)) - return -EINVAL; - - if (copy_from_sockptr(&opt, optval, sizeof(unsigned int))) - return -EFAULT; - - switch (optname) { - case ROSE_DEFER: - rose->defer = opt ? 1 : 0; - return 0; - - case ROSE_T1: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - rose->t1 = opt * HZ; - return 0; - - case ROSE_T2: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - rose->t2 = opt * HZ; - return 0; - - case ROSE_T3: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - rose->t3 = opt * HZ; - return 0; - - case ROSE_HOLDBACK: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - rose->hb = opt * HZ; - return 0; - - case ROSE_IDLE: - if (opt > UINT_MAX / (60 * HZ)) - return -EINVAL; - rose->idle = opt * 60 * HZ; - return 0; - - case ROSE_QBITINCL: - rose->qbitincl = opt ? 1 : 0; - return 0; - - default: - return -ENOPROTOOPT; - } -} - -static int rose_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - int val = 0; - int len; - - if (level != SOL_ROSE) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - if (len < 0) - return -EINVAL; - - switch (optname) { - case ROSE_DEFER: - val = rose->defer; - break; - - case ROSE_T1: - val = rose->t1 / HZ; - break; - - case ROSE_T2: - val = rose->t2 / HZ; - break; - - case ROSE_T3: - val = rose->t3 / HZ; - break; - - case ROSE_HOLDBACK: - val = rose->hb / HZ; - break; - - case ROSE_IDLE: - val = rose->idle / (60 * HZ); - break; - - case ROSE_QBITINCL: - val = rose->qbitincl; - break; - - default: - return -ENOPROTOOPT; - } - - len = min_t(unsigned int, len, sizeof(int)); - - if (put_user(len, optlen)) - return -EFAULT; - - return copy_to_user(optval, &val, len) ? -EFAULT : 0; -} - -static int rose_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - - lock_sock(sk); - if (sock->state != SS_UNCONNECTED) { - release_sock(sk); - return -EINVAL; - } - - if (sk->sk_state != TCP_LISTEN) { - struct rose_sock *rose = rose_sk(sk); - - rose->dest_ndigis = 0; - memset(&rose->dest_addr, 0, ROSE_ADDR_LEN); - memset(&rose->dest_call, 0, AX25_ADDR_LEN); - memset(rose->dest_digis, 0, AX25_ADDR_LEN * ROSE_MAX_DIGIS); - sk->sk_max_ack_backlog = backlog; - sk->sk_state = TCP_LISTEN; - release_sock(sk); - return 0; - } - release_sock(sk); - - return -EOPNOTSUPP; -} - -static struct proto rose_proto = { - .name = "ROSE", - .owner = THIS_MODULE, - .obj_size = sizeof(struct rose_sock), -}; - -static int rose_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - struct rose_sock *rose; - - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - - if (sock->type != SOCK_SEQPACKET || protocol != 0) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_ROSE, GFP_ATOMIC, &rose_proto, kern); - if (sk == NULL) - return -ENOMEM; - - rose = rose_sk(sk); - - sock_init_data(sock, sk); - - skb_queue_head_init(&rose->ack_queue); -#ifdef M_BIT - skb_queue_head_init(&rose->frag_queue); - rose->fraglen = 0; -#endif - - sock->ops = &rose_proto_ops; - sk->sk_protocol = protocol; - - timer_setup(&rose->timer, NULL, 0); - timer_setup(&rose->idletimer, NULL, 0); - - rose->t1 = msecs_to_jiffies(sysctl_rose_call_request_timeout); - rose->t2 = msecs_to_jiffies(sysctl_rose_reset_request_timeout); - rose->t3 = msecs_to_jiffies(sysctl_rose_clear_request_timeout); - rose->hb = msecs_to_jiffies(sysctl_rose_ack_hold_back_timeout); - rose->idle = msecs_to_jiffies(sysctl_rose_no_activity_timeout); - - rose->state = ROSE_STATE_0; - - return 0; -} - -static struct sock *rose_make_new(struct sock *osk) -{ - struct sock *sk; - struct rose_sock *rose, *orose; - - if (osk->sk_type != SOCK_SEQPACKET) - return NULL; - - sk = sk_alloc(sock_net(osk), PF_ROSE, GFP_ATOMIC, &rose_proto, 0); - if (sk == NULL) - return NULL; - - rose = rose_sk(sk); - - sock_init_data(NULL, sk); - - skb_queue_head_init(&rose->ack_queue); -#ifdef M_BIT - skb_queue_head_init(&rose->frag_queue); - rose->fraglen = 0; -#endif - - sk->sk_type = osk->sk_type; - sk->sk_priority = READ_ONCE(osk->sk_priority); - sk->sk_protocol = osk->sk_protocol; - sk->sk_rcvbuf = osk->sk_rcvbuf; - sk->sk_sndbuf = osk->sk_sndbuf; - sk->sk_state = TCP_ESTABLISHED; - sock_copy_flags(sk, osk); - - timer_setup(&rose->timer, NULL, 0); - timer_setup(&rose->idletimer, NULL, 0); - - orose = rose_sk(osk); - rose->t1 = orose->t1; - rose->t2 = orose->t2; - rose->t3 = orose->t3; - rose->hb = orose->hb; - rose->idle = orose->idle; - rose->defer = orose->defer; - rose->device = orose->device; - if (rose->device) - netdev_hold(rose->device, &rose->dev_tracker, GFP_ATOMIC); - rose->qbitincl = orose->qbitincl; - - return sk; -} - -static int rose_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose; - - if (sk == NULL) return 0; - - sock_hold(sk); - sock_orphan(sk); - lock_sock(sk); - rose = rose_sk(sk); - - switch (rose->state) { - case ROSE_STATE_0: - release_sock(sk); - rose_disconnect(sk, 0, -1, -1); - lock_sock(sk); - rose_destroy_socket(sk); - break; - - case ROSE_STATE_2: - rose_neigh_put(rose->neighbour); - release_sock(sk); - rose_disconnect(sk, 0, -1, -1); - lock_sock(sk); - rose_destroy_socket(sk); - break; - - case ROSE_STATE_1: - case ROSE_STATE_3: - case ROSE_STATE_4: - case ROSE_STATE_5: - rose_clear_queues(sk); - rose_stop_idletimer(sk); - rose_write_internal(sk, ROSE_CLEAR_REQUEST); - rose_start_t3timer(sk); - rose->state = ROSE_STATE_2; - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - sock_set_flag(sk, SOCK_DESTROY); - break; - - default: - break; - } - - spin_lock_bh(&rose_list_lock); - netdev_put(rose->device, &rose->dev_tracker); - rose->device = NULL; - spin_unlock_bh(&rose_list_lock); - sock->sk = NULL; - release_sock(sk); - sock_put(sk); - - return 0; -} - -static int rose_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; - struct net_device *dev; - ax25_address *source; - ax25_uid_assoc *user; - int err = -EINVAL; - int n; - - if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose)) - return -EINVAL; - - if (addr->srose_family != AF_ROSE) - return -EINVAL; - - if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1) - return -EINVAL; - - if ((unsigned int) addr->srose_ndigis > ROSE_MAX_DIGIS) - return -EINVAL; - - lock_sock(sk); - - if (!sock_flag(sk, SOCK_ZAPPED)) - goto out_release; - - err = -EADDRNOTAVAIL; - dev = rose_dev_get(&addr->srose_addr); - if (!dev) - goto out_release; - - source = &addr->srose_call; - - user = ax25_findbyuid(current_euid()); - if (user) { - rose->source_call = user->call; - ax25_uid_put(user); - } else { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) { - dev_put(dev); - err = -EACCES; - goto out_release; - } - rose->source_call = *source; - } - - rose->source_addr = addr->srose_addr; - rose->device = dev; - netdev_tracker_alloc(rose->device, &rose->dev_tracker, GFP_KERNEL); - rose->source_ndigis = addr->srose_ndigis; - - if (addr_len == sizeof(struct full_sockaddr_rose)) { - struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr; - for (n = 0 ; n < addr->srose_ndigis ; n++) - rose->source_digis[n] = full_addr->srose_digis[n]; - } else { - if (rose->source_ndigis == 1) { - rose->source_digis[0] = addr->srose_digi; - } - } - - rose_insert_socket(sk); - - sock_reset_flag(sk, SOCK_ZAPPED); - err = 0; -out_release: - release_sock(sk); - return err; -} - -static int rose_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len, - int flags) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; - unsigned char cause, diagnostic; - ax25_uid_assoc *user; - int n, err = 0; - - if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose)) - return -EINVAL; - - if (addr->srose_family != AF_ROSE) - return -EINVAL; - - if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1) - return -EINVAL; - - if ((unsigned int) addr->srose_ndigis > ROSE_MAX_DIGIS) - return -EINVAL; - - /* Source + Destination digis should not exceed ROSE_MAX_DIGIS */ - if ((rose->source_ndigis + addr->srose_ndigis) > ROSE_MAX_DIGIS) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { - /* Connect completed during a ERESTARTSYS event */ - sock->state = SS_CONNECTED; - goto out_release; - } - - if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) { - sock->state = SS_UNCONNECTED; - err = -ECONNREFUSED; - goto out_release; - } - - if (sk->sk_state == TCP_ESTABLISHED) { - /* No reconnect on a seqpacket socket */ - err = -EISCONN; - goto out_release; - } - - if (sk->sk_state == TCP_SYN_SENT) { - err = -EALREADY; - goto out_release; - } - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, - &diagnostic, 0); - if (!rose->neighbour) { - err = -ENETUNREACH; - goto out_release; - } - - rose->lci = rose_new_lci(rose->neighbour); - if (!rose->lci) { - err = -ENETUNREACH; - rose_neigh_put(rose->neighbour); - goto out_release; - } - - if (sock_flag(sk, SOCK_ZAPPED)) { /* Must bind first - autobinding in this may or may not work */ - struct net_device *dev; - - sock_reset_flag(sk, SOCK_ZAPPED); - - dev = rose_dev_first(); - if (!dev) { - err = -ENETUNREACH; - rose_neigh_put(rose->neighbour); - goto out_release; - } - - user = ax25_findbyuid(current_euid()); - if (!user) { - err = -EINVAL; - rose_neigh_put(rose->neighbour); - dev_put(dev); - goto out_release; - } - - memcpy(&rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN); - rose->source_call = user->call; - rose->device = dev; - netdev_tracker_alloc(rose->device, &rose->dev_tracker, - GFP_KERNEL); - ax25_uid_put(user); - - rose_insert_socket(sk); /* Finish the bind */ - } - rose->dest_addr = addr->srose_addr; - rose->dest_call = addr->srose_call; - rose->rand = ((long)rose & 0xFFFF) + rose->lci; - rose->dest_ndigis = addr->srose_ndigis; - - if (addr_len == sizeof(struct full_sockaddr_rose)) { - struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr; - for (n = 0 ; n < addr->srose_ndigis ; n++) - rose->dest_digis[n] = full_addr->srose_digis[n]; - } else { - if (rose->dest_ndigis == 1) { - rose->dest_digis[0] = addr->srose_digi; - } - } - - /* Move to connecting socket, start sending Connect Requests */ - sock->state = SS_CONNECTING; - sk->sk_state = TCP_SYN_SENT; - - rose->state = ROSE_STATE_1; - - rose_write_internal(sk, ROSE_CALL_REQUEST); - rose_start_heartbeat(sk); - rose_start_t1timer(sk); - - /* Now the loop */ - if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) { - err = -EINPROGRESS; - goto out_release; - } - - /* - * A Connect Ack with Choke or timeout or failed routing will go to - * closed. - */ - if (sk->sk_state == TCP_SYN_SENT) { - DEFINE_WAIT(wait); - - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - if (sk->sk_state != TCP_SYN_SENT) - break; - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - - if (err) - goto out_release; - } - - if (sk->sk_state != TCP_ESTABLISHED) { - sock->state = SS_UNCONNECTED; - err = sock_error(sk); /* Always set at this point */ - goto out_release; - } - - sock->state = SS_CONNECTED; - -out_release: - release_sock(sk); - - return err; -} - -static int rose_accept(struct socket *sock, struct socket *newsock, - struct proto_accept_arg *arg) -{ - struct sk_buff *skb; - struct sock *newsk; - DEFINE_WAIT(wait); - struct sock *sk; - int err = 0; - - if ((sk = sock->sk) == NULL) - return -EINVAL; - - lock_sock(sk); - if (sk->sk_type != SOCK_SEQPACKET) { - err = -EOPNOTSUPP; - goto out_release; - } - - if (sk->sk_state != TCP_LISTEN) { - err = -EINVAL; - goto out_release; - } - - /* - * The write queue this time is holding sockets ready to use - * hooked into the SABM we saved - */ - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - - skb = skb_dequeue(&sk->sk_receive_queue); - if (skb) - break; - - if (arg->flags & O_NONBLOCK) { - err = -EWOULDBLOCK; - break; - } - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - if (err) - goto out_release; - - newsk = skb->sk; - sock_graft(newsk, newsock); - - /* Now attach up the new socket */ - skb->sk = NULL; - kfree_skb(skb); - sk_acceptq_removed(sk); - -out_release: - release_sock(sk); - - return err; -} - -static int rose_getname(struct socket *sock, struct sockaddr *uaddr, - int peer) -{ - struct full_sockaddr_rose *srose = (struct full_sockaddr_rose *)uaddr; - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - int n; - - memset(srose, 0, sizeof(*srose)); - if (peer != 0) { - if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; - srose->srose_family = AF_ROSE; - srose->srose_addr = rose->dest_addr; - srose->srose_call = rose->dest_call; - srose->srose_ndigis = rose->dest_ndigis; - for (n = 0; n < rose->dest_ndigis; n++) - srose->srose_digis[n] = rose->dest_digis[n]; - } else { - srose->srose_family = AF_ROSE; - srose->srose_addr = rose->source_addr; - srose->srose_call = rose->source_call; - srose->srose_ndigis = rose->source_ndigis; - for (n = 0; n < rose->source_ndigis; n++) - srose->srose_digis[n] = rose->source_digis[n]; - } - - return sizeof(struct full_sockaddr_rose); -} - -int rose_rx_call_request(struct sk_buff *skb, struct net_device *dev, struct rose_neigh *neigh, unsigned int lci) -{ - struct sock *sk; - struct sock *make; - struct rose_sock *make_rose; - struct rose_facilities_struct facilities; - int n; - - skb->sk = NULL; /* Initially we don't know who it's for */ - - /* - * skb->data points to the rose frame start - */ - memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); - - if (!rose_parse_facilities(skb->data + ROSE_CALL_REQ_FACILITIES_OFF, - skb->len - ROSE_CALL_REQ_FACILITIES_OFF, - &facilities)) { - rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76); - return 0; - } - - sk = rose_find_listener(&facilities.source_addr, &facilities.source_call); - - /* - * We can't accept the Call Request. - */ - if (sk == NULL || sk_acceptq_is_full(sk) || - (make = rose_make_new(sk)) == NULL) { - rose_transmit_clear_request(neigh, lci, ROSE_NETWORK_CONGESTION, 120); - return 0; - } - - skb->sk = make; - make->sk_state = TCP_ESTABLISHED; - make_rose = rose_sk(make); - - make_rose->lci = lci; - make_rose->dest_addr = facilities.dest_addr; - make_rose->dest_call = facilities.dest_call; - make_rose->dest_ndigis = facilities.dest_ndigis; - for (n = 0 ; n < facilities.dest_ndigis ; n++) - make_rose->dest_digis[n] = facilities.dest_digis[n]; - make_rose->source_addr = facilities.source_addr; - make_rose->source_call = facilities.source_call; - make_rose->source_ndigis = facilities.source_ndigis; - for (n = 0 ; n < facilities.source_ndigis ; n++) - make_rose->source_digis[n] = facilities.source_digis[n]; - make_rose->neighbour = neigh; - make_rose->device = dev; - /* Caller got a reference for us. */ - netdev_tracker_alloc(make_rose->device, &make_rose->dev_tracker, - GFP_ATOMIC); - make_rose->facilities = facilities; - - rose_neigh_hold(make_rose->neighbour); - - if (rose_sk(sk)->defer) { - make_rose->state = ROSE_STATE_5; - } else { - rose_write_internal(make, ROSE_CALL_ACCEPTED); - make_rose->state = ROSE_STATE_3; - rose_start_idletimer(make); - } - - make_rose->condition = 0x00; - make_rose->vs = 0; - make_rose->va = 0; - make_rose->vr = 0; - make_rose->vl = 0; - sk_acceptq_added(sk); - - rose_insert_socket(make); - - skb_queue_head(&sk->sk_receive_queue, skb); - - rose_start_heartbeat(make); - - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_data_ready(sk); - - return 1; -} - -static int rose_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - DECLARE_SOCKADDR(struct sockaddr_rose *, usrose, msg->msg_name); - int err; - struct full_sockaddr_rose srose; - struct sk_buff *skb; - unsigned char *asmptr; - int n, size, qbit = 0; - - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_CMSG_COMPAT)) - return -EINVAL; - - if (sock_flag(sk, SOCK_ZAPPED)) - return -EADDRNOTAVAIL; - - if (sk->sk_shutdown & SEND_SHUTDOWN) { - send_sig(SIGPIPE, current, 0); - return -EPIPE; - } - - if (rose->neighbour == NULL || rose->device == NULL) - return -ENETUNREACH; - - if (usrose != NULL) { - if (msg->msg_namelen != sizeof(struct sockaddr_rose) && msg->msg_namelen != sizeof(struct full_sockaddr_rose)) - return -EINVAL; - memset(&srose, 0, sizeof(struct full_sockaddr_rose)); - memcpy(&srose, usrose, msg->msg_namelen); - if (rosecmp(&rose->dest_addr, &srose.srose_addr) != 0 || - ax25cmp(&rose->dest_call, &srose.srose_call) != 0) - return -EISCONN; - if (srose.srose_ndigis != rose->dest_ndigis) - return -EISCONN; - if (srose.srose_ndigis == rose->dest_ndigis) { - for (n = 0 ; n < srose.srose_ndigis ; n++) - if (ax25cmp(&rose->dest_digis[n], - &srose.srose_digis[n])) - return -EISCONN; - } - if (srose.srose_family != AF_ROSE) - return -EINVAL; - } else { - if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; - - srose.srose_family = AF_ROSE; - srose.srose_addr = rose->dest_addr; - srose.srose_call = rose->dest_call; - srose.srose_ndigis = rose->dest_ndigis; - for (n = 0 ; n < rose->dest_ndigis ; n++) - srose.srose_digis[n] = rose->dest_digis[n]; - } - - /* Build a packet */ - /* Sanity check the packet size */ - if (len > 65535) - return -EMSGSIZE; - - size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN; - - if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) - return err; - - skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN); - - /* - * Put the data on the end - */ - - skb_reset_transport_header(skb); - skb_put(skb, len); - - err = memcpy_from_msg(skb_transport_header(skb), msg, len); - if (err) { - kfree_skb(skb); - return err; - } - - /* - * If the Q BIT Include socket option is in force, the first - * byte of the user data is the logical value of the Q Bit. - */ - if (rose->qbitincl) { - qbit = skb->data[0]; - skb_pull(skb, 1); - } - - /* - * Push down the ROSE header - */ - asmptr = skb_push(skb, ROSE_MIN_LEN); - - /* Build a ROSE Network header */ - asmptr[0] = ((rose->lci >> 8) & 0x0F) | ROSE_GFI; - asmptr[1] = (rose->lci >> 0) & 0xFF; - asmptr[2] = ROSE_DATA; - - if (qbit) - asmptr[0] |= ROSE_Q_BIT; - - if (sk->sk_state != TCP_ESTABLISHED) { - kfree_skb(skb); - return -ENOTCONN; - } - -#ifdef M_BIT -#define ROSE_PACLEN (256-ROSE_MIN_LEN) - if (skb->len - ROSE_MIN_LEN > ROSE_PACLEN) { - unsigned char header[ROSE_MIN_LEN]; - struct sk_buff *skbn; - int frontlen; - int lg; - - /* Save a copy of the Header */ - skb_copy_from_linear_data(skb, header, ROSE_MIN_LEN); - skb_pull(skb, ROSE_MIN_LEN); - - frontlen = skb_headroom(skb); - - while (skb->len > 0) { - if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, &err)) == NULL) { - kfree_skb(skb); - return err; - } - - skbn->sk = sk; - skbn->free = 1; - skbn->arp = 1; - - skb_reserve(skbn, frontlen); - - lg = (ROSE_PACLEN > skb->len) ? skb->len : ROSE_PACLEN; - - /* Copy the user data */ - skb_copy_from_linear_data(skb, skb_put(skbn, lg), lg); - skb_pull(skb, lg); - - /* Duplicate the Header */ - skb_push(skbn, ROSE_MIN_LEN); - skb_copy_to_linear_data(skbn, header, ROSE_MIN_LEN); - - if (skb->len > 0) - skbn->data[2] |= M_BIT; - - skb_queue_tail(&sk->sk_write_queue, skbn); /* Throw it on the queue */ - } - - skb->free = 1; - kfree_skb(skb); - } else { - skb_queue_tail(&sk->sk_write_queue, skb); /* Throw it on the queue */ - } -#else - skb_queue_tail(&sk->sk_write_queue, skb); /* Shove it onto the queue */ -#endif - - rose_kick(sk); - - return len; -} - - -static int rose_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, - int flags) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - size_t copied; - unsigned char *asmptr; - struct sk_buff *skb; - int n, er, qbit; - - /* - * This works for seqpacket too. The receiver has ordered the queue for - * us! We do one quick check first though - */ - if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; - - /* Now we can treat all alike */ - skb = skb_recv_datagram(sk, flags, &er); - if (!skb) - return er; - - qbit = (skb->data[0] & ROSE_Q_BIT) == ROSE_Q_BIT; - - skb_pull(skb, ROSE_MIN_LEN); - - if (rose->qbitincl) { - asmptr = skb_push(skb, 1); - *asmptr = qbit; - } - - skb_reset_transport_header(skb); - copied = skb->len; - - if (copied > size) { - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - - skb_copy_datagram_msg(skb, 0, msg, copied); - - if (msg->msg_name) { - struct sockaddr_rose *srose; - DECLARE_SOCKADDR(struct full_sockaddr_rose *, full_srose, - msg->msg_name); - - memset(msg->msg_name, 0, sizeof(struct full_sockaddr_rose)); - srose = msg->msg_name; - srose->srose_family = AF_ROSE; - srose->srose_addr = rose->dest_addr; - srose->srose_call = rose->dest_call; - srose->srose_ndigis = rose->dest_ndigis; - for (n = 0 ; n < rose->dest_ndigis ; n++) - full_srose->srose_digis[n] = rose->dest_digis[n]; - msg->msg_namelen = sizeof(struct full_sockaddr_rose); - } - - skb_free_datagram(sk, skb); - - return copied; -} - - -static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - void __user *argp = (void __user *)arg; - - switch (cmd) { - case TIOCOUTQ: { - long amount; - - amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - if (amount < 0) - amount = 0; - return put_user(amount, (unsigned int __user *) argp); - } - - case TIOCINQ: { - struct sk_buff *skb; - long amount = 0L; - - spin_lock_irq(&sk->sk_receive_queue.lock); - if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) - amount = skb->len; - spin_unlock_irq(&sk->sk_receive_queue.lock); - return put_user(amount, (unsigned int __user *) argp); - } - - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - return -EINVAL; - - case SIOCADDRT: - case SIOCDELRT: - case SIOCRSCLRRT: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - return rose_rt_ioctl(cmd, argp); - - case SIOCRSGCAUSE: { - struct rose_cause_struct rose_cause; - rose_cause.cause = rose->cause; - rose_cause.diagnostic = rose->diagnostic; - return copy_to_user(argp, &rose_cause, sizeof(struct rose_cause_struct)) ? -EFAULT : 0; - } - - case SIOCRSSCAUSE: { - struct rose_cause_struct rose_cause; - if (copy_from_user(&rose_cause, argp, sizeof(struct rose_cause_struct))) - return -EFAULT; - rose->cause = rose_cause.cause; - rose->diagnostic = rose_cause.diagnostic; - return 0; - } - - case SIOCRSSL2CALL: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) - ax25_listen_release(&rose_callsign, NULL); - if (copy_from_user(&rose_callsign, argp, sizeof(ax25_address))) - return -EFAULT; - if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) - return ax25_listen_register(&rose_callsign, NULL); - - return 0; - - case SIOCRSGL2CALL: - return copy_to_user(argp, &rose_callsign, sizeof(ax25_address)) ? -EFAULT : 0; - - case SIOCRSACCEPT: - if (rose->state == ROSE_STATE_5) { - rose_write_internal(sk, ROSE_CALL_ACCEPTED); - rose_start_idletimer(sk); - rose->condition = 0x00; - rose->vs = 0; - rose->va = 0; - rose->vr = 0; - rose->vl = 0; - rose->state = ROSE_STATE_3; - } - return 0; - - default: - return -ENOIOCTLCMD; - } - - return 0; -} - -#ifdef CONFIG_PROC_FS -static void *rose_info_start(struct seq_file *seq, loff_t *pos) - __acquires(rose_list_lock) -{ - spin_lock_bh(&rose_list_lock); - return seq_hlist_start_head(&rose_list, *pos); -} - -static void *rose_info_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &rose_list, pos); -} - -static void rose_info_stop(struct seq_file *seq, void *v) - __releases(rose_list_lock) -{ - spin_unlock_bh(&rose_list_lock); -} - -static int rose_info_show(struct seq_file *seq, void *v) -{ - char buf[11], rsbuf[11]; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "dest_addr dest_call src_addr src_call dev lci neigh st vs vr va t t1 t2 t3 hb idle Snd-Q Rcv-Q inode\n"); - - else { - struct sock *s = sk_entry(v); - struct rose_sock *rose = rose_sk(s); - const char *devname, *callsign; - const struct net_device *dev = rose->device; - - if (!dev) - devname = "???"; - else - devname = dev->name; - - seq_printf(seq, "%-10s %-9s ", - rose2asc(rsbuf, &rose->dest_addr), - ax2asc(buf, &rose->dest_call)); - - if (ax25cmp(&rose->source_call, &null_ax25_address) == 0) - callsign = "??????-?"; - else - callsign = ax2asc(buf, &rose->source_call); - - seq_printf(seq, - "%-10s %-9s %-5s %3.3X %05d %d %d %d %d %3lu %3lu %3lu %3lu %3lu %3lu/%03lu %5d %5d %llu\n", - rose2asc(rsbuf, &rose->source_addr), - callsign, - devname, - rose->lci & 0x0FFF, - (rose->neighbour) ? rose->neighbour->number : 0, - rose->state, - rose->vs, - rose->vr, - rose->va, - ax25_display_timer(&rose->timer) / HZ, - rose->t1 / HZ, - rose->t2 / HZ, - rose->t3 / HZ, - rose->hb / HZ, - ax25_display_timer(&rose->idletimer) / (60 * HZ), - rose->idle / (60 * HZ), - sk_wmem_alloc_get(s), - sk_rmem_alloc_get(s), - s->sk_socket ? SOCK_INODE(s->sk_socket)->i_ino : (u64)0); - } - - return 0; -} - -static const struct seq_operations rose_info_seqops = { - .start = rose_info_start, - .next = rose_info_next, - .stop = rose_info_stop, - .show = rose_info_show, -}; -#endif /* CONFIG_PROC_FS */ - -static const struct net_proto_family rose_family_ops = { - .family = PF_ROSE, - .create = rose_create, - .owner = THIS_MODULE, -}; - -static const struct proto_ops rose_proto_ops = { - .family = PF_ROSE, - .owner = THIS_MODULE, - .release = rose_release, - .bind = rose_bind, - .connect = rose_connect, - .socketpair = sock_no_socketpair, - .accept = rose_accept, - .getname = rose_getname, - .poll = datagram_poll, - .ioctl = rose_ioctl, - .gettstamp = sock_gettstamp, - .listen = rose_listen, - .shutdown = sock_no_shutdown, - .setsockopt = rose_setsockopt, - .getsockopt = rose_getsockopt, - .sendmsg = rose_sendmsg, - .recvmsg = rose_recvmsg, - .mmap = sock_no_mmap, -}; - -static struct notifier_block rose_dev_notifier = { - .notifier_call = rose_device_event, -}; - -static struct net_device **dev_rose; - -static struct ax25_protocol rose_pid = { - .pid = AX25_P_ROSE, - .func = rose_route_frame -}; - -static struct ax25_linkfail rose_linkfail_notifier = { - .func = rose_link_failed -}; - -static int __init rose_proto_init(void) -{ - int i; - int rc; - - if (rose_ndevs > 0x7FFFFFFF/sizeof(struct net_device *)) { - printk(KERN_ERR "ROSE: rose_proto_init - rose_ndevs parameter too large\n"); - rc = -EINVAL; - goto out; - } - - rc = proto_register(&rose_proto, 0); - if (rc != 0) - goto out; - - rose_callsign = null_ax25_address; - - dev_rose = kzalloc_objs(struct net_device *, rose_ndevs); - if (dev_rose == NULL) { - printk(KERN_ERR "ROSE: rose_proto_init - unable to allocate device structure\n"); - rc = -ENOMEM; - goto out_proto_unregister; - } - - for (i = 0; i < rose_ndevs; i++) { - struct net_device *dev; - char name[IFNAMSIZ]; - - sprintf(name, "rose%d", i); - dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, rose_setup); - if (!dev) { - printk(KERN_ERR "ROSE: rose_proto_init - unable to allocate memory\n"); - rc = -ENOMEM; - goto fail; - } - rc = register_netdev(dev); - if (rc) { - printk(KERN_ERR "ROSE: netdevice registration failed\n"); - free_netdev(dev); - goto fail; - } - rose_set_lockdep_key(dev); - dev_rose[i] = dev; - } - - sock_register(&rose_family_ops); - register_netdevice_notifier(&rose_dev_notifier); - - ax25_register_pid(&rose_pid); - ax25_linkfail_register(&rose_linkfail_notifier); - -#ifdef CONFIG_SYSCTL - rose_register_sysctl(); -#endif - rose_loopback_init(); - - rose_add_loopback_neigh(); - - proc_create_seq("rose", 0444, init_net.proc_net, &rose_info_seqops); - proc_create_seq("rose_neigh", 0444, init_net.proc_net, - &rose_neigh_seqops); - proc_create_seq("rose_nodes", 0444, init_net.proc_net, - &rose_node_seqops); - proc_create_seq("rose_routes", 0444, init_net.proc_net, - &rose_route_seqops); -out: - return rc; -fail: - while (--i >= 0) { - unregister_netdev(dev_rose[i]); - free_netdev(dev_rose[i]); - } - kfree(dev_rose); -out_proto_unregister: - proto_unregister(&rose_proto); - goto out; -} -module_init(rose_proto_init); - -module_param(rose_ndevs, int, 0); -MODULE_PARM_DESC(rose_ndevs, "number of ROSE devices"); - -MODULE_AUTHOR("Jonathan Naylor G4KLX "); -MODULE_DESCRIPTION("The amateur radio ROSE network layer protocol"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_ROSE); - -static void __exit rose_exit(void) -{ - int i; - - remove_proc_entry("rose", init_net.proc_net); - remove_proc_entry("rose_neigh", init_net.proc_net); - remove_proc_entry("rose_nodes", init_net.proc_net); - remove_proc_entry("rose_routes", init_net.proc_net); - rose_loopback_clear(); - - rose_rt_free(); - - ax25_protocol_release(AX25_P_ROSE); - ax25_linkfail_release(&rose_linkfail_notifier); - - if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) - ax25_listen_release(&rose_callsign, NULL); - -#ifdef CONFIG_SYSCTL - rose_unregister_sysctl(); -#endif - unregister_netdevice_notifier(&rose_dev_notifier); - - sock_unregister(PF_ROSE); - - for (i = 0; i < rose_ndevs; i++) { - struct net_device *dev = dev_rose[i]; - - if (dev) { - unregister_netdev(dev); - free_netdev(dev); - } - } - - kfree(dev_rose); - proto_unregister(&rose_proto); -} - -module_exit(rose_exit); diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c deleted file mode 100644 index f1a76a5820f1..000000000000 --- a/net/rose/rose_dev.c +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -static int rose_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, - const void *daddr, const void *saddr, unsigned int len) -{ - unsigned char *buff = skb_push(skb, ROSE_MIN_LEN + 2); - - if (daddr) - memcpy(buff + 7, daddr, dev->addr_len); - - *buff++ = ROSE_GFI | ROSE_Q_BIT; - *buff++ = 0x00; - *buff++ = ROSE_DATA; - *buff++ = 0x7F; - *buff++ = AX25_P_IP; - - if (daddr != NULL) - return 37; - - return -37; -} - -static int rose_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = addr; - int err; - - if (!memcmp(dev->dev_addr, sa->sa_data, dev->addr_len)) - return 0; - - if (dev->flags & IFF_UP) { - err = rose_add_loopback_node((rose_address *)sa->sa_data); - if (err) - return err; - - rose_del_loopback_node((const rose_address *)dev->dev_addr); - } - - dev_addr_set(dev, sa->sa_data); - - return 0; -} - -static int rose_open(struct net_device *dev) -{ - int err; - - err = rose_add_loopback_node((const rose_address *)dev->dev_addr); - if (err) - return err; - - netif_start_queue(dev); - - return 0; -} - -static int rose_close(struct net_device *dev) -{ - netif_stop_queue(dev); - rose_del_loopback_node((const rose_address *)dev->dev_addr); - return 0; -} - -static netdev_tx_t rose_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct net_device_stats *stats = &dev->stats; - unsigned int len = skb->len; - - if (!netif_running(dev)) { - printk(KERN_ERR "ROSE: rose_xmit - called when iface is down\n"); - return NETDEV_TX_BUSY; - } - - if (!rose_route_frame(skb, NULL)) { - dev_kfree_skb(skb); - stats->tx_errors++; - return NETDEV_TX_OK; - } - - stats->tx_packets++; - stats->tx_bytes += len; - return NETDEV_TX_OK; -} - -static const struct header_ops rose_header_ops = { - .create = rose_header, -}; - -static const struct net_device_ops rose_netdev_ops = { - .ndo_open = rose_open, - .ndo_stop = rose_close, - .ndo_start_xmit = rose_xmit, - .ndo_set_mac_address = rose_set_mac_address, -}; - -void rose_setup(struct net_device *dev) -{ - dev->mtu = ROSE_MAX_PACKET_SIZE - 2; - dev->netdev_ops = &rose_netdev_ops; - - dev->header_ops = &rose_header_ops; - dev->hard_header_len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN; - dev->addr_len = ROSE_ADDR_LEN; - dev->type = ARPHRD_ROSE; - - /* New-style flags. */ - dev->flags = IFF_NOARP; -} diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c deleted file mode 100644 index ca4f217ef3d3..000000000000 --- a/net/rose/rose_in.c +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * - * Most of this code is based on the SDL diagrams published in the 7th ARRL - * Computer Networking Conference papers. The diagrams have mistakes in them, - * but are mostly correct. Before you modify the code could you read the SDL - * diagrams as the code is not obvious and probably very easy to break. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * State machine for state 1, Awaiting Call Accepted State. - * The handling of the timer(s) is in file rose_timer.c. - * Handling of state 0 and connection release is in af_rose.c. - */ -static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype) -{ - struct rose_sock *rose = rose_sk(sk); - - switch (frametype) { - case ROSE_CALL_ACCEPTED: - rose_stop_timer(sk); - rose_start_idletimer(sk); - rose->condition = 0x00; - rose->vs = 0; - rose->va = 0; - rose->vr = 0; - rose->vl = 0; - rose->state = ROSE_STATE_3; - sk->sk_state = TCP_ESTABLISHED; - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_state_change(sk); - break; - - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); - rose_neigh_put(rose->neighbour); - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 2, Awaiting Clear Confirmation State. - * The handling of the timer(s) is in file rose_timer.c - * Handling of state 0 and connection release is in af_rose.c. - */ -static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype) -{ - struct rose_sock *rose = rose_sk(sk); - - switch (frametype) { - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - rose_neigh_put(rose->neighbour); - break; - - case ROSE_CLEAR_CONFIRMATION: - rose_disconnect(sk, 0, -1, -1); - rose_neigh_put(rose->neighbour); - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file rose_timer.c - * Handling of state 0 and connection release is in af_rose.c. - */ -static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m) -{ - struct rose_sock *rose = rose_sk(sk); - int queued = 0; - - switch (frametype) { - case ROSE_RESET_REQUEST: - rose_stop_timer(sk); - rose_start_idletimer(sk); - rose_write_internal(sk, ROSE_RESET_CONFIRMATION); - rose->condition = 0x00; - rose->vs = 0; - rose->vr = 0; - rose->va = 0; - rose->vl = 0; - rose_requeue_frames(sk); - break; - - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - rose_neigh_put(rose->neighbour); - break; - - case ROSE_RR: - case ROSE_RNR: - if (!rose_validate_nr(sk, nr)) { - rose_write_internal(sk, ROSE_RESET_REQUEST); - rose->condition = 0x00; - rose->vs = 0; - rose->vr = 0; - rose->va = 0; - rose->vl = 0; - rose->state = ROSE_STATE_4; - rose_start_t2timer(sk); - rose_stop_idletimer(sk); - } else { - rose_frames_acked(sk, nr); - if (frametype == ROSE_RNR) { - rose->condition |= ROSE_COND_PEER_RX_BUSY; - } else { - rose->condition &= ~ROSE_COND_PEER_RX_BUSY; - } - } - break; - - case ROSE_DATA: /* XXX */ - rose->condition &= ~ROSE_COND_PEER_RX_BUSY; - if (!rose_validate_nr(sk, nr)) { - rose_write_internal(sk, ROSE_RESET_REQUEST); - rose->condition = 0x00; - rose->vs = 0; - rose->vr = 0; - rose->va = 0; - rose->vl = 0; - rose->state = ROSE_STATE_4; - rose_start_t2timer(sk); - rose_stop_idletimer(sk); - break; - } - rose_frames_acked(sk, nr); - if (ns == rose->vr) { - rose_start_idletimer(sk); - if (!sk_filter_trim_cap(sk, skb, ROSE_MIN_LEN) && - __sock_queue_rcv_skb(sk, skb) == 0) { - rose->vr = (rose->vr + 1) % ROSE_MODULUS; - queued = 1; - } else { - /* Should never happen ! */ - rose_write_internal(sk, ROSE_RESET_REQUEST); - rose->condition = 0x00; - rose->vs = 0; - rose->vr = 0; - rose->va = 0; - rose->vl = 0; - rose->state = ROSE_STATE_4; - rose_start_t2timer(sk); - rose_stop_idletimer(sk); - break; - } - if (atomic_read(&sk->sk_rmem_alloc) > - (sk->sk_rcvbuf >> 1)) - rose->condition |= ROSE_COND_OWN_RX_BUSY; - } - /* - * If the window is full, ack the frame, else start the - * acknowledge hold back timer. - */ - if (((rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == rose->vr) { - rose->condition &= ~ROSE_COND_ACK_PENDING; - rose_stop_timer(sk); - rose_enquiry_response(sk); - } else { - rose->condition |= ROSE_COND_ACK_PENDING; - rose_start_hbtimer(sk); - } - break; - - default: - printk(KERN_WARNING "ROSE: unknown %02X in state 3\n", frametype); - break; - } - - return queued; -} - -/* - * State machine for state 4, Awaiting Reset Confirmation State. - * The handling of the timer(s) is in file rose_timer.c - * Handling of state 0 and connection release is in af_rose.c. - */ -static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype) -{ - struct rose_sock *rose = rose_sk(sk); - - switch (frametype) { - case ROSE_RESET_REQUEST: - rose_write_internal(sk, ROSE_RESET_CONFIRMATION); - fallthrough; - case ROSE_RESET_CONFIRMATION: - rose_stop_timer(sk); - rose_start_idletimer(sk); - rose->condition = 0x00; - rose->va = 0; - rose->vr = 0; - rose->vs = 0; - rose->vl = 0; - rose->state = ROSE_STATE_3; - rose_requeue_frames(sk); - break; - - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - rose_neigh_put(rose->neighbour); - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 5, Awaiting Call Acceptance State. - * The handling of the timer(s) is in file rose_timer.c - * Handling of state 0 and connection release is in af_rose.c. - */ -static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int frametype) -{ - if (frametype == ROSE_CLEAR_REQUEST) { - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - rose_neigh_put(rose_sk(sk)->neighbour); - } - - return 0; -} - -/* Higher level upcall for a LAPB frame */ -int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb) -{ - struct rose_sock *rose = rose_sk(sk); - int queued = 0, frametype, ns, nr, q, d, m; - - if (rose->state == ROSE_STATE_0) - return 0; - - frametype = rose_decode(skb, &ns, &nr, &q, &d, &m); - - /* - * ROSE_CLEAR_REQUEST carries cause and diagnostic in bytes 3..4. - * Reject a malformed frame that is too short to contain them. - */ - if (frametype == ROSE_CLEAR_REQUEST && skb->len < 5) - return 0; - - switch (rose->state) { - case ROSE_STATE_1: - queued = rose_state1_machine(sk, skb, frametype); - break; - case ROSE_STATE_2: - queued = rose_state2_machine(sk, skb, frametype); - break; - case ROSE_STATE_3: - queued = rose_state3_machine(sk, skb, frametype, ns, nr, q, d, m); - break; - case ROSE_STATE_4: - queued = rose_state4_machine(sk, skb, frametype); - break; - case ROSE_STATE_5: - queued = rose_state5_machine(sk, skb, frametype); - break; - } - - rose_kick(sk); - - return queued; -} diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c deleted file mode 100644 index 7746229fdc8c..000000000000 --- a/net/rose/rose_link.c +++ /dev/null @@ -1,289 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void rose_ftimer_expiry(struct timer_list *); -static void rose_t0timer_expiry(struct timer_list *); - -static void rose_transmit_restart_confirmation(struct rose_neigh *neigh); -static void rose_transmit_restart_request(struct rose_neigh *neigh); - -void rose_start_ftimer(struct rose_neigh *neigh) -{ - timer_delete(&neigh->ftimer); - - neigh->ftimer.function = rose_ftimer_expiry; - neigh->ftimer.expires = - jiffies + msecs_to_jiffies(sysctl_rose_link_fail_timeout); - - add_timer(&neigh->ftimer); -} - -static void rose_start_t0timer(struct rose_neigh *neigh) -{ - timer_delete(&neigh->t0timer); - - neigh->t0timer.function = rose_t0timer_expiry; - neigh->t0timer.expires = - jiffies + msecs_to_jiffies(sysctl_rose_restart_request_timeout); - - add_timer(&neigh->t0timer); -} - -void rose_stop_ftimer(struct rose_neigh *neigh) -{ - timer_delete(&neigh->ftimer); -} - -void rose_stop_t0timer(struct rose_neigh *neigh) -{ - timer_delete(&neigh->t0timer); -} - -int rose_ftimer_running(struct rose_neigh *neigh) -{ - return timer_pending(&neigh->ftimer); -} - -static int rose_t0timer_running(struct rose_neigh *neigh) -{ - return timer_pending(&neigh->t0timer); -} - -static void rose_ftimer_expiry(struct timer_list *t) -{ -} - -static void rose_t0timer_expiry(struct timer_list *t) -{ - struct rose_neigh *neigh = timer_container_of(neigh, t, t0timer); - - rose_transmit_restart_request(neigh); - - neigh->dce_mode = 0; - - rose_start_t0timer(neigh); -} - -/* - * Interface to ax25_send_frame. Changes my level 2 callsign depending - * on whether we have a global ROSE callsign or use the default port - * callsign. - */ -static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh) -{ - const ax25_address *rose_call; - ax25_cb *ax25s; - - if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) - rose_call = (const ax25_address *)neigh->dev->dev_addr; - else - rose_call = &rose_callsign; - - ax25s = neigh->ax25; - neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); - if (ax25s) - ax25_cb_put(ax25s); - - return neigh->ax25 != NULL; -} - -/* - * Interface to ax25_link_up. Changes my level 2 callsign depending - * on whether we have a global ROSE callsign or use the default port - * callsign. - */ -static int rose_link_up(struct rose_neigh *neigh) -{ - const ax25_address *rose_call; - ax25_cb *ax25s; - - if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) - rose_call = (const ax25_address *)neigh->dev->dev_addr; - else - rose_call = &rose_callsign; - - ax25s = neigh->ax25; - neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); - if (ax25s) - ax25_cb_put(ax25s); - - return neigh->ax25 != NULL; -} - -/* - * This handles all restart and diagnostic frames. - */ -void rose_link_rx_restart(struct sk_buff *skb, struct rose_neigh *neigh, unsigned short frametype) -{ - struct sk_buff *skbn; - - switch (frametype) { - case ROSE_RESTART_REQUEST: - rose_stop_t0timer(neigh); - neigh->restarted = 1; - neigh->dce_mode = (skb->data[3] == ROSE_DTE_ORIGINATED); - rose_transmit_restart_confirmation(neigh); - break; - - case ROSE_RESTART_CONFIRMATION: - rose_stop_t0timer(neigh); - neigh->restarted = 1; - break; - - case ROSE_DIAGNOSTIC: - pr_warn("ROSE: received diagnostic #%d - %3ph\n", skb->data[3], - skb->data + 4); - break; - - default: - printk(KERN_WARNING "ROSE: received unknown %02X with LCI 000\n", frametype); - break; - } - - if (neigh->restarted) { - while ((skbn = skb_dequeue(&neigh->queue)) != NULL) - if (!rose_send_frame(skbn, neigh)) - kfree_skb(skbn); - } -} - -/* - * This routine is called when a Restart Request is needed - */ -static void rose_transmit_restart_request(struct rose_neigh *neigh) -{ - struct sk_buff *skb; - unsigned char *dptr; - int len; - - len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3; - - if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN); - - dptr = skb_put(skb, ROSE_MIN_LEN + 3); - - *dptr++ = AX25_P_ROSE; - *dptr++ = ROSE_GFI; - *dptr++ = 0x00; - *dptr++ = ROSE_RESTART_REQUEST; - *dptr++ = ROSE_DTE_ORIGINATED; - *dptr++ = 0; - - if (!rose_send_frame(skb, neigh)) - kfree_skb(skb); -} - -/* - * This routine is called when a Restart Confirmation is needed - */ -static void rose_transmit_restart_confirmation(struct rose_neigh *neigh) -{ - struct sk_buff *skb; - unsigned char *dptr; - int len; - - len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1; - - if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN); - - dptr = skb_put(skb, ROSE_MIN_LEN + 1); - - *dptr++ = AX25_P_ROSE; - *dptr++ = ROSE_GFI; - *dptr++ = 0x00; - *dptr++ = ROSE_RESTART_CONFIRMATION; - - if (!rose_send_frame(skb, neigh)) - kfree_skb(skb); -} - -/* - * This routine is called when a Clear Request is needed outside of the context - * of a connected socket. - */ -void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, unsigned char cause, unsigned char diagnostic) -{ - struct sk_buff *skb; - unsigned char *dptr; - int len; - - if (!neigh->dev) - return; - - len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3; - - if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN); - - dptr = skb_put(skb, ROSE_MIN_LEN + 3); - - *dptr++ = AX25_P_ROSE; - *dptr++ = ((lci >> 8) & 0x0F) | ROSE_GFI; - *dptr++ = ((lci >> 0) & 0xFF); - *dptr++ = ROSE_CLEAR_REQUEST; - *dptr++ = cause; - *dptr++ = diagnostic; - - if (!rose_send_frame(skb, neigh)) - kfree_skb(skb); -} - -void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh) -{ - unsigned char *dptr; - - if (neigh->loopback) { - rose_loopback_queue(skb, neigh); - return; - } - - if (!rose_link_up(neigh)) - neigh->restarted = 0; - - dptr = skb_push(skb, 1); - *dptr++ = AX25_P_ROSE; - - if (neigh->restarted) { - if (!rose_send_frame(skb, neigh)) - kfree_skb(skb); - } else { - skb_queue_tail(&neigh->queue, skb); - - if (!rose_t0timer_running(neigh)) { - rose_transmit_restart_request(neigh); - neigh->dce_mode = 0; - rose_start_t0timer(neigh); - } - } -} diff --git a/net/rose/rose_loopback.c b/net/rose/rose_loopback.c deleted file mode 100644 index b538e39b3df5..000000000000 --- a/net/rose/rose_loopback.c +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include - -static struct sk_buff_head loopback_queue; -#define ROSE_LOOPBACK_LIMIT 1000 -static struct timer_list loopback_timer; - -static void rose_set_loopback_timer(void); -static void rose_loopback_timer(struct timer_list *unused); - -void rose_loopback_init(void) -{ - skb_queue_head_init(&loopback_queue); - - timer_setup(&loopback_timer, rose_loopback_timer, 0); -} - -static int rose_loopback_running(void) -{ - return timer_pending(&loopback_timer); -} - -int rose_loopback_queue(struct sk_buff *skb, struct rose_neigh *neigh) -{ - struct sk_buff *skbn = NULL; - - if (skb_queue_len(&loopback_queue) < ROSE_LOOPBACK_LIMIT) - skbn = skb_clone(skb, GFP_ATOMIC); - - if (skbn) { - consume_skb(skb); - skb_queue_tail(&loopback_queue, skbn); - - if (!rose_loopback_running()) - rose_set_loopback_timer(); - } else { - kfree_skb(skb); - } - - return 1; -} - -static void rose_set_loopback_timer(void) -{ - mod_timer(&loopback_timer, jiffies + 10); -} - -static void rose_loopback_timer(struct timer_list *unused) -{ - struct sk_buff *skb; - struct net_device *dev; - rose_address *dest; - struct sock *sk; - unsigned short frametype; - unsigned int lci_i, lci_o; - int count; - - for (count = 0; count < ROSE_LOOPBACK_LIMIT; count++) { - skb = skb_dequeue(&loopback_queue); - if (!skb) - return; - if (skb->len < ROSE_MIN_LEN) { - kfree_skb(skb); - continue; - } - lci_i = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); - frametype = skb->data[2]; - if (frametype == ROSE_CALL_REQUEST && - (skb->len <= ROSE_CALL_REQ_FACILITIES_OFF || - skb->data[ROSE_CALL_REQ_ADDR_LEN_OFF] != - ROSE_CALL_REQ_ADDR_LEN_VAL)) { - kfree_skb(skb); - continue; - } - dest = (rose_address *)(skb->data + ROSE_CALL_REQ_DEST_ADDR_OFF); - lci_o = ROSE_DEFAULT_MAXVC + 1 - lci_i; - - skb_reset_transport_header(skb); - - sk = rose_find_socket(lci_o, rose_loopback_neigh); - if (sk) { - if (rose_process_rx_frame(sk, skb) == 0) - kfree_skb(skb); - continue; - } - - if (frametype == ROSE_CALL_REQUEST) { - if (!rose_loopback_neigh->dev && - !rose_loopback_neigh->loopback) { - kfree_skb(skb); - continue; - } - - dev = rose_dev_get(dest); - if (!dev) { - kfree_skb(skb); - continue; - } - - if (rose_rx_call_request(skb, dev, rose_loopback_neigh, lci_o) == 0) { - dev_put(dev); - kfree_skb(skb); - } - } else { - kfree_skb(skb); - } - } - if (!skb_queue_empty(&loopback_queue)) - mod_timer(&loopback_timer, jiffies + 1); -} - -void __exit rose_loopback_clear(void) -{ - struct sk_buff *skb; - - timer_delete(&loopback_timer); - - while ((skb = skb_dequeue(&loopback_queue)) != NULL) { - skb->sk = NULL; - kfree_skb(skb); - } -} diff --git a/net/rose/rose_out.c b/net/rose/rose_out.c deleted file mode 100644 index 9050e33c9604..000000000000 --- a/net/rose/rose_out.c +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * This procedure is passed a buffer descriptor for an iframe. It builds - * the rest of the control part of the frame and then writes it out. - */ -static void rose_send_iframe(struct sock *sk, struct sk_buff *skb) -{ - struct rose_sock *rose = rose_sk(sk); - - if (skb == NULL) - return; - - skb->data[2] |= (rose->vr << 5) & 0xE0; - skb->data[2] |= (rose->vs << 1) & 0x0E; - - rose_start_idletimer(sk); - - rose_transmit_link(skb, rose->neighbour); -} - -void rose_kick(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - struct sk_buff *skb, *skbn; - unsigned short start, end; - - if (rose->state != ROSE_STATE_3) - return; - - if (rose->condition & ROSE_COND_PEER_RX_BUSY) - return; - - if (!skb_peek(&sk->sk_write_queue)) - return; - - start = (skb_peek(&rose->ack_queue) == NULL) ? rose->va : rose->vs; - end = (rose->va + sysctl_rose_window_size) % ROSE_MODULUS; - - if (start == end) - return; - - rose->vs = start; - - /* - * Transmit data until either we're out of data to send or - * the window is full. - */ - - skb = skb_dequeue(&sk->sk_write_queue); - - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&sk->sk_write_queue, skb); - break; - } - - skb_set_owner_w(skbn, sk); - - /* - * Transmit the frame copy. - */ - rose_send_iframe(sk, skbn); - - rose->vs = (rose->vs + 1) % ROSE_MODULUS; - - /* - * Requeue the original data frame. - */ - skb_queue_tail(&rose->ack_queue, skb); - - } while (rose->vs != end && - (skb = skb_dequeue(&sk->sk_write_queue)) != NULL); - - rose->vl = rose->vr; - rose->condition &= ~ROSE_COND_ACK_PENDING; - - rose_stop_timer(sk); -} - -/* - * The following routines are taken from page 170 of the 7th ARRL Computer - * Networking Conference paper, as is the whole state machine. - */ - -void rose_enquiry_response(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - if (rose->condition & ROSE_COND_OWN_RX_BUSY) - rose_write_internal(sk, ROSE_RNR); - else - rose_write_internal(sk, ROSE_RR); - - rose->vl = rose->vr; - rose->condition &= ~ROSE_COND_ACK_PENDING; - - rose_stop_timer(sk); -} diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c deleted file mode 100644 index e31842e6b3c8..000000000000 --- a/net/rose/rose_route.c +++ /dev/null @@ -1,1333 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Terry Dawson VK2KTJ (terry@animats.net) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For TIOCINQ/OUTQ */ -#include -#include -#include -#include -#include -#include -#include - -static unsigned int rose_neigh_no = 1; - -static struct rose_node *rose_node_list; -static DEFINE_SPINLOCK(rose_node_list_lock); -static struct rose_neigh *rose_neigh_list; -static DEFINE_SPINLOCK(rose_neigh_list_lock); -static struct rose_route *rose_route_list; -static DEFINE_SPINLOCK(rose_route_list_lock); - -struct rose_neigh *rose_loopback_neigh; - -/* - * Add a new route to a node, and in the process add the node and the - * neighbour if it is new. - */ -static int __must_check rose_add_node(struct rose_route_struct *rose_route, - struct net_device *dev) -{ - struct rose_node *rose_node, *rose_tmpn, *rose_tmpp; - struct rose_neigh *rose_neigh; - int i, res = 0; - - spin_lock_bh(&rose_node_list_lock); - spin_lock_bh(&rose_neigh_list_lock); - - rose_node = rose_node_list; - while (rose_node != NULL) { - if ((rose_node->mask == rose_route->mask) && - (rosecmpm(&rose_route->address, &rose_node->address, - rose_route->mask) == 0)) - break; - rose_node = rose_node->next; - } - - if (rose_node != NULL && rose_node->loopback) { - res = -EINVAL; - goto out; - } - - rose_neigh = rose_neigh_list; - while (rose_neigh != NULL) { - if (ax25cmp(&rose_route->neighbour, - &rose_neigh->callsign) == 0 && - rose_neigh->dev == dev) - break; - rose_neigh = rose_neigh->next; - } - - if (rose_neigh == NULL) { - rose_neigh = kmalloc_obj(*rose_neigh, GFP_ATOMIC); - if (rose_neigh == NULL) { - res = -ENOMEM; - goto out; - } - - rose_neigh->callsign = rose_route->neighbour; - rose_neigh->digipeat = NULL; - rose_neigh->ax25 = NULL; - rose_neigh->dev = dev; - rose_neigh->count = 0; - rose_neigh->dce_mode = 0; - rose_neigh->loopback = 0; - rose_neigh->number = rose_neigh_no++; - rose_neigh->restarted = 0; - refcount_set(&rose_neigh->use, 1); - - skb_queue_head_init(&rose_neigh->queue); - - timer_setup(&rose_neigh->ftimer, NULL, 0); - timer_setup(&rose_neigh->t0timer, NULL, 0); - - if (rose_route->ndigis != 0) { - rose_neigh->digipeat = - kmalloc_obj(ax25_digi, GFP_ATOMIC); - if (rose_neigh->digipeat == NULL) { - kfree(rose_neigh); - res = -ENOMEM; - goto out; - } - - rose_neigh->digipeat->ndigi = rose_route->ndigis; - rose_neigh->digipeat->lastrepeat = -1; - - for (i = 0; i < rose_route->ndigis; i++) { - rose_neigh->digipeat->calls[i] = - rose_route->digipeaters[i]; - rose_neigh->digipeat->repeated[i] = 0; - } - } - - rose_neigh->next = rose_neigh_list; - rose_neigh_list = rose_neigh; - } - - /* - * This is a new node to be inserted into the list. Find where it needs - * to be inserted into the list, and insert it. We want to be sure - * to order the list in descending order of mask size to ensure that - * later when we are searching this list the first match will be the - * best match. - */ - if (rose_node == NULL) { - rose_tmpn = rose_node_list; - rose_tmpp = NULL; - - while (rose_tmpn != NULL) { - if (rose_tmpn->mask > rose_route->mask) { - rose_tmpp = rose_tmpn; - rose_tmpn = rose_tmpn->next; - } else { - break; - } - } - - /* create new node */ - rose_node = kmalloc_obj(*rose_node, GFP_ATOMIC); - if (rose_node == NULL) { - res = -ENOMEM; - goto out; - } - - rose_node->address = rose_route->address; - rose_node->mask = rose_route->mask; - rose_node->count = 1; - rose_node->loopback = 0; - rose_node->neighbour[0] = rose_neigh; - - if (rose_tmpn == NULL) { - if (rose_tmpp == NULL) { /* Empty list */ - rose_node_list = rose_node; - rose_node->next = NULL; - } else { - rose_tmpp->next = rose_node; - rose_node->next = NULL; - } - } else { - if (rose_tmpp == NULL) { /* 1st node */ - rose_node->next = rose_node_list; - rose_node_list = rose_node; - } else { - rose_tmpp->next = rose_node; - rose_node->next = rose_tmpn; - } - } - rose_neigh->count++; - rose_neigh_hold(rose_neigh); - - goto out; - } - - /* We have space, slot it in */ - if (rose_node->count < 3) { - rose_node->neighbour[rose_node->count] = rose_neigh; - rose_node->count++; - rose_neigh->count++; - rose_neigh_hold(rose_neigh); - } - -out: - spin_unlock_bh(&rose_neigh_list_lock); - spin_unlock_bh(&rose_node_list_lock); - - return res; -} - -/* - * Caller is holding rose_node_list_lock. - */ -static void rose_remove_node(struct rose_node *rose_node) -{ - struct rose_node *s; - - if ((s = rose_node_list) == rose_node) { - rose_node_list = rose_node->next; - kfree(rose_node); - return; - } - - while (s != NULL && s->next != NULL) { - if (s->next == rose_node) { - s->next = rose_node->next; - kfree(rose_node); - return; - } - - s = s->next; - } -} - -/* - * Caller is holding rose_neigh_list_lock. - */ -static void rose_remove_neigh(struct rose_neigh *rose_neigh) -{ - struct rose_neigh *s; - - timer_delete_sync(&rose_neigh->ftimer); - timer_delete_sync(&rose_neigh->t0timer); - - skb_queue_purge(&rose_neigh->queue); - - if ((s = rose_neigh_list) == rose_neigh) { - rose_neigh_list = rose_neigh->next; - return; - } - - while (s != NULL && s->next != NULL) { - if (s->next == rose_neigh) { - s->next = rose_neigh->next; - return; - } - - s = s->next; - } -} - -/* - * Caller is holding rose_route_list_lock. - */ -static void rose_remove_route(struct rose_route *rose_route) -{ - struct rose_route *s; - - if (rose_route->neigh1 != NULL) - rose_neigh_put(rose_route->neigh1); - - if (rose_route->neigh2 != NULL) - rose_neigh_put(rose_route->neigh2); - - if ((s = rose_route_list) == rose_route) { - rose_route_list = rose_route->next; - kfree(rose_route); - return; - } - - while (s != NULL && s->next != NULL) { - if (s->next == rose_route) { - s->next = rose_route->next; - kfree(rose_route); - return; - } - - s = s->next; - } -} - -/* - * "Delete" a node. Strictly speaking remove a route to a node. The node - * is only deleted if no routes are left to it. - */ -static int rose_del_node(struct rose_route_struct *rose_route, - struct net_device *dev) -{ - struct rose_node *rose_node; - struct rose_neigh *rose_neigh; - int i, err = 0; - - spin_lock_bh(&rose_node_list_lock); - spin_lock_bh(&rose_neigh_list_lock); - - rose_node = rose_node_list; - while (rose_node != NULL) { - if ((rose_node->mask == rose_route->mask) && - (rosecmpm(&rose_route->address, &rose_node->address, - rose_route->mask) == 0)) - break; - rose_node = rose_node->next; - } - - if (rose_node == NULL || rose_node->loopback) { - err = -EINVAL; - goto out; - } - - rose_neigh = rose_neigh_list; - while (rose_neigh != NULL) { - if (ax25cmp(&rose_route->neighbour, - &rose_neigh->callsign) == 0 && - rose_neigh->dev == dev) - break; - rose_neigh = rose_neigh->next; - } - - if (rose_neigh == NULL) { - err = -EINVAL; - goto out; - } - - for (i = 0; i < rose_node->count; i++) { - if (rose_node->neighbour[i] == rose_neigh) { - rose_neigh->count--; - rose_neigh_put(rose_neigh); - - if (rose_neigh->count == 0) { - rose_remove_neigh(rose_neigh); - rose_neigh_put(rose_neigh); - } - - rose_node->count--; - - if (rose_node->count == 0) { - rose_remove_node(rose_node); - } else { - switch (i) { - case 0: - rose_node->neighbour[0] = - rose_node->neighbour[1]; - fallthrough; - case 1: - rose_node->neighbour[1] = - rose_node->neighbour[2]; - break; - case 2: - break; - } - } - goto out; - } - } - err = -EINVAL; - -out: - spin_unlock_bh(&rose_neigh_list_lock); - spin_unlock_bh(&rose_node_list_lock); - - return err; -} - -/* - * Add the loopback neighbour. - */ -void rose_add_loopback_neigh(void) -{ - struct rose_neigh *sn; - - rose_loopback_neigh = kmalloc_obj(struct rose_neigh); - if (!rose_loopback_neigh) - return; - sn = rose_loopback_neigh; - - sn->callsign = null_ax25_address; - sn->digipeat = NULL; - sn->ax25 = NULL; - sn->dev = NULL; - sn->count = 0; - sn->dce_mode = 1; - sn->loopback = 1; - sn->number = rose_neigh_no++; - sn->restarted = 1; - refcount_set(&sn->use, 1); - - skb_queue_head_init(&sn->queue); - - timer_setup(&sn->ftimer, NULL, 0); - timer_setup(&sn->t0timer, NULL, 0); - - spin_lock_bh(&rose_neigh_list_lock); - sn->next = rose_neigh_list; - rose_neigh_list = sn; - spin_unlock_bh(&rose_neigh_list_lock); -} - -/* - * Add a loopback node. - */ -int rose_add_loopback_node(const rose_address *address) -{ - struct rose_node *rose_node; - int err = 0; - - spin_lock_bh(&rose_node_list_lock); - - rose_node = rose_node_list; - while (rose_node != NULL) { - if ((rose_node->mask == 10) && - (rosecmpm(address, &rose_node->address, 10) == 0) && - rose_node->loopback) - break; - rose_node = rose_node->next; - } - - if (rose_node != NULL) - goto out; - - if ((rose_node = kmalloc_obj(*rose_node, GFP_ATOMIC)) == NULL) { - err = -ENOMEM; - goto out; - } - - rose_node->address = *address; - rose_node->mask = 10; - rose_node->count = 1; - rose_node->loopback = 1; - rose_node->neighbour[0] = rose_loopback_neigh; - - /* Insert at the head of list. Address is always mask=10 */ - rose_node->next = rose_node_list; - rose_node_list = rose_node; - - rose_loopback_neigh->count++; - rose_neigh_hold(rose_loopback_neigh); - -out: - spin_unlock_bh(&rose_node_list_lock); - - return err; -} - -/* - * Delete a loopback node. - */ -void rose_del_loopback_node(const rose_address *address) -{ - struct rose_node *rose_node; - - spin_lock_bh(&rose_node_list_lock); - - rose_node = rose_node_list; - while (rose_node != NULL) { - if ((rose_node->mask == 10) && - (rosecmpm(address, &rose_node->address, 10) == 0) && - rose_node->loopback) - break; - rose_node = rose_node->next; - } - - if (rose_node == NULL) - goto out; - - rose_remove_node(rose_node); - - rose_loopback_neigh->count--; - rose_neigh_put(rose_loopback_neigh); - -out: - spin_unlock_bh(&rose_node_list_lock); -} - -/* - * A device has been removed. Remove its routes and neighbours. - */ -void rose_rt_device_down(struct net_device *dev) -{ - struct rose_neigh *s, *rose_neigh; - struct rose_node *t, *rose_node; - int i; - - spin_lock_bh(&rose_node_list_lock); - spin_lock_bh(&rose_neigh_list_lock); - rose_neigh = rose_neigh_list; - while (rose_neigh != NULL) { - s = rose_neigh; - rose_neigh = rose_neigh->next; - - if (s->dev != dev) - continue; - - rose_node = rose_node_list; - - while (rose_node != NULL) { - t = rose_node; - rose_node = rose_node->next; - - for (i = t->count - 1; i >= 0; i--) { - if (t->neighbour[i] != s) - continue; - - t->count--; - - memmove(&t->neighbour[i], &t->neighbour[i + 1], - sizeof(t->neighbour[0]) * - (t->count - i)); - rose_neigh_put(s); - } - - if (t->count <= 0) - rose_remove_node(t); - } - - rose_remove_neigh(s); - rose_neigh_put(s); - } - spin_unlock_bh(&rose_neigh_list_lock); - spin_unlock_bh(&rose_node_list_lock); -} - -#if 0 /* Currently unused */ -/* - * A device has been removed. Remove its links. - */ -void rose_route_device_down(struct net_device *dev) -{ - struct rose_route *s, *rose_route; - - spin_lock_bh(&rose_route_list_lock); - rose_route = rose_route_list; - while (rose_route != NULL) { - s = rose_route; - rose_route = rose_route->next; - - if (s->neigh1->dev == dev || s->neigh2->dev == dev) - rose_remove_route(s); - } - spin_unlock_bh(&rose_route_list_lock); -} -#endif - -/* - * Clear all nodes and neighbours out, except for neighbours with - * active connections going through them. - * Do not clear loopback neighbour and nodes. - */ -static int rose_clear_routes(void) -{ - struct rose_neigh *s, *rose_neigh; - struct rose_node *t, *rose_node; - int i; - - spin_lock_bh(&rose_node_list_lock); - spin_lock_bh(&rose_neigh_list_lock); - - rose_neigh = rose_neigh_list; - rose_node = rose_node_list; - - while (rose_node != NULL) { - t = rose_node; - rose_node = rose_node->next; - - if (!t->loopback) { - for (i = 0; i < t->count; i++) - rose_neigh_put(t->neighbour[i]); - rose_remove_node(t); - } - } - - while (rose_neigh != NULL) { - s = rose_neigh; - rose_neigh = rose_neigh->next; - - if (!s->loopback) { - rose_remove_neigh(s); - rose_neigh_put(s); - } - } - - spin_unlock_bh(&rose_neigh_list_lock); - spin_unlock_bh(&rose_node_list_lock); - - return 0; -} - -/* - * Check that the device given is a valid AX.25 interface that is "up". - * called with RTNL - */ -static struct net_device *rose_ax25_dev_find(char *devname) -{ - struct net_device *dev; - - if ((dev = __dev_get_by_name(&init_net, devname)) == NULL) - return NULL; - - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25) - return dev; - - return NULL; -} - -/* - * Find the first active ROSE device, usually "rose0". - */ -struct net_device *rose_dev_first(void) -{ - struct net_device *dev, *first = NULL; - - rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE) - if (first == NULL || strncmp(dev->name, first->name, 3) < 0) - first = dev; - } - if (first) - dev_hold(first); - rcu_read_unlock(); - - return first; -} - -/* - * Find the ROSE device for the given address. - */ -struct net_device *rose_dev_get(rose_address *addr) -{ - struct net_device *dev; - - rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && - rosecmp(addr, (const rose_address *)dev->dev_addr) == 0) { - dev_hold(dev); - goto out; - } - } - dev = NULL; -out: - rcu_read_unlock(); - return dev; -} - -static int rose_dev_exists(rose_address *addr) -{ - struct net_device *dev; - - rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && - rosecmp(addr, (const rose_address *)dev->dev_addr) == 0) - goto out; - } - dev = NULL; -out: - rcu_read_unlock(); - return dev != NULL; -} - - - - -struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neigh) -{ - struct rose_route *rose_route; - - for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) - if ((rose_route->neigh1 == neigh && rose_route->lci1 == lci) || - (rose_route->neigh2 == neigh && rose_route->lci2 == lci)) - return rose_route; - - return NULL; -} - -/* - * Find a neighbour or a route given a ROSE address. - */ -struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, - unsigned char *diagnostic, int route_frame) -{ - struct rose_neigh *res = NULL; - struct rose_node *node; - int failed = 0; - int i; - - if (!route_frame) spin_lock_bh(&rose_node_list_lock); - for (node = rose_node_list; node != NULL; node = node->next) { - if (rosecmpm(addr, &node->address, node->mask) == 0) { - for (i = 0; i < node->count; i++) { - if (node->neighbour[i]->restarted) { - res = node->neighbour[i]; - rose_neigh_hold(node->neighbour[i]); - goto out; - } - } - } - } - if (!route_frame) { /* connect request */ - for (node = rose_node_list; node != NULL; node = node->next) { - if (rosecmpm(addr, &node->address, node->mask) == 0) { - for (i = 0; i < node->count; i++) { - if (!rose_ftimer_running(node->neighbour[i])) { - res = node->neighbour[i]; - rose_neigh_hold(node->neighbour[i]); - goto out; - } - failed = 1; - } - } - } - } - - if (failed) { - *cause = ROSE_OUT_OF_ORDER; - *diagnostic = 0; - } else { - *cause = ROSE_NOT_OBTAINABLE; - *diagnostic = 0; - } - -out: - if (!route_frame) spin_unlock_bh(&rose_node_list_lock); - return res; -} - -/* - * Handle the ioctls that control the routing functions. - */ -int rose_rt_ioctl(unsigned int cmd, void __user *arg) -{ - struct rose_route_struct rose_route; - struct net_device *dev; - int err; - - switch (cmd) { - case SIOCADDRT: - if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) - return -EFAULT; - if ((dev = rose_ax25_dev_find(rose_route.device)) == NULL) - return -EINVAL; - if (rose_dev_exists(&rose_route.address)) /* Can't add routes to ourself */ - return -EINVAL; - if (rose_route.mask > 10) /* Mask can't be more than 10 digits */ - return -EINVAL; - if (rose_route.ndigis > AX25_MAX_DIGIS) - return -EINVAL; - err = rose_add_node(&rose_route, dev); - return err; - - case SIOCDELRT: - if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) - return -EFAULT; - if ((dev = rose_ax25_dev_find(rose_route.device)) == NULL) - return -EINVAL; - err = rose_del_node(&rose_route, dev); - return err; - - case SIOCRSCLRRT: - return rose_clear_routes(); - - default: - return -EINVAL; - } - - return 0; -} - -static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) -{ - struct rose_route *rose_route, *s; - - rose_neigh->restarted = 0; - - rose_stop_t0timer(rose_neigh); - rose_start_ftimer(rose_neigh); - - skb_queue_purge(&rose_neigh->queue); - - spin_lock_bh(&rose_route_list_lock); - - rose_route = rose_route_list; - - while (rose_route != NULL) { - if ((rose_route->neigh1 == rose_neigh && rose_route->neigh2 == rose_neigh) || - (rose_route->neigh1 == rose_neigh && rose_route->neigh2 == NULL) || - (rose_route->neigh2 == rose_neigh && rose_route->neigh1 == NULL)) { - s = rose_route->next; - rose_remove_route(rose_route); - rose_route = s; - continue; - } - - if (rose_route->neigh1 == rose_neigh) { - rose_neigh_put(rose_route->neigh1); - rose_route->neigh1 = NULL; - rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0); - } - - if (rose_route->neigh2 == rose_neigh) { - rose_neigh_put(rose_route->neigh2); - rose_route->neigh2 = NULL; - rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0); - } - - rose_route = rose_route->next; - } - spin_unlock_bh(&rose_route_list_lock); -} - -/* - * A level 2 link has timed out, therefore it appears to be a poor link, - * then don't use that neighbour until it is reset. Blow away all through - * routes and connections using this route. - */ -void rose_link_failed(ax25_cb *ax25, int reason) -{ - struct rose_neigh *rose_neigh; - - spin_lock_bh(&rose_neigh_list_lock); - rose_neigh = rose_neigh_list; - while (rose_neigh != NULL) { - if (rose_neigh->ax25 == ax25) - break; - rose_neigh = rose_neigh->next; - } - - if (rose_neigh != NULL) { - rose_neigh->ax25 = NULL; - ax25_cb_put(ax25); - - rose_del_route_by_neigh(rose_neigh); - rose_kill_by_neigh(rose_neigh); - } - spin_unlock_bh(&rose_neigh_list_lock); -} - -/* - * A device has been "downed" remove its link status. Blow away all - * through routes and connections that use this device. - */ -void rose_link_device_down(struct net_device *dev) -{ - struct rose_neigh *rose_neigh; - - for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) { - if (rose_neigh->dev == dev) { - rose_del_route_by_neigh(rose_neigh); - rose_kill_by_neigh(rose_neigh); - } - } -} - -/* - * Route a frame to an appropriate AX.25 connection. - * A NULL ax25_cb indicates an internally generated frame. - */ -int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) -{ - struct rose_neigh *rose_neigh, *new_neigh; - struct rose_route *rose_route; - struct rose_facilities_struct facilities; - rose_address *src_addr, *dest_addr; - struct sock *sk; - unsigned short frametype; - unsigned int lci, new_lci; - unsigned char cause, diagnostic; - struct net_device *dev; - int res = 0; - char buf[11]; - - if (skb->len < ROSE_MIN_LEN) - return res; - - if (!ax25) - return rose_loopback_queue(skb, NULL); - - frametype = skb->data[2]; - lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); - if (frametype == ROSE_CALL_REQUEST && - (skb->len <= ROSE_CALL_REQ_FACILITIES_OFF || - skb->data[ROSE_CALL_REQ_ADDR_LEN_OFF] != - ROSE_CALL_REQ_ADDR_LEN_VAL)) - return res; - src_addr = (rose_address *)(skb->data + ROSE_CALL_REQ_SRC_ADDR_OFF); - dest_addr = (rose_address *)(skb->data + ROSE_CALL_REQ_DEST_ADDR_OFF); - - spin_lock_bh(&rose_neigh_list_lock); - spin_lock_bh(&rose_route_list_lock); - - rose_neigh = rose_neigh_list; - while (rose_neigh != NULL) { - if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && - ax25->ax25_dev->dev == rose_neigh->dev) - break; - rose_neigh = rose_neigh->next; - } - - if (rose_neigh == NULL) { - printk("rose_route : unknown neighbour or device %s\n", - ax2asc(buf, &ax25->dest_addr)); - goto out; - } - - /* - * Obviously the link is working, halt the ftimer. - */ - rose_stop_ftimer(rose_neigh); - - /* - * LCI of zero is always for us, and its always a restart - * frame. - */ - if (lci == 0) { - rose_link_rx_restart(skb, rose_neigh, frametype); - goto out; - } - - /* - * Find an existing socket. - */ - if ((sk = rose_find_socket(lci, rose_neigh)) != NULL) { - if (frametype == ROSE_CALL_REQUEST) { - struct rose_sock *rose = rose_sk(sk); - - /* Remove an existing unused socket */ - rose_clear_queues(sk); - rose->cause = ROSE_NETWORK_CONGESTION; - rose->diagnostic = 0; - rose_neigh_put(rose->neighbour); - rose->neighbour = NULL; - rose->lci = 0; - rose->state = ROSE_STATE_0; - sk->sk_state = TCP_CLOSE; - sk->sk_err = 0; - sk->sk_shutdown |= SEND_SHUTDOWN; - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - } - } - else { - skb_reset_transport_header(skb); - res = rose_process_rx_frame(sk, skb); - goto out; - } - } - - /* - * Is is a Call Request and is it for us ? - */ - if (frametype == ROSE_CALL_REQUEST) - if ((dev = rose_dev_get(dest_addr)) != NULL) { - res = rose_rx_call_request(skb, dev, rose_neigh, lci); - dev_put(dev); - goto out; - } - - if (!sysctl_rose_routing_control) { - rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0); - goto out; - } - - /* - * Route it to the next in line if we have an entry for it. - */ - rose_route = rose_route_list; - while (rose_route != NULL) { - if (rose_route->lci1 == lci && - rose_route->neigh1 == rose_neigh) { - if (frametype == ROSE_CALL_REQUEST) { - /* F6FBB - Remove an existing unused route */ - rose_remove_route(rose_route); - break; - } else if (rose_route->neigh2 != NULL) { - skb->data[0] &= 0xF0; - skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F; - skb->data[1] = (rose_route->lci2 >> 0) & 0xFF; - rose_transmit_link(skb, rose_route->neigh2); - if (frametype == ROSE_CLEAR_CONFIRMATION) - rose_remove_route(rose_route); - res = 1; - goto out; - } else { - if (frametype == ROSE_CLEAR_CONFIRMATION) - rose_remove_route(rose_route); - goto out; - } - } - if (rose_route->lci2 == lci && - rose_route->neigh2 == rose_neigh) { - if (frametype == ROSE_CALL_REQUEST) { - /* F6FBB - Remove an existing unused route */ - rose_remove_route(rose_route); - break; - } else if (rose_route->neigh1 != NULL) { - skb->data[0] &= 0xF0; - skb->data[0] |= (rose_route->lci1 >> 8) & 0x0F; - skb->data[1] = (rose_route->lci1 >> 0) & 0xFF; - rose_transmit_link(skb, rose_route->neigh1); - if (frametype == ROSE_CLEAR_CONFIRMATION) - rose_remove_route(rose_route); - res = 1; - goto out; - } else { - if (frametype == ROSE_CLEAR_CONFIRMATION) - rose_remove_route(rose_route); - goto out; - } - } - rose_route = rose_route->next; - } - - /* - * We know that: - * 1. The frame isn't for us, - * 2. It isn't "owned" by any existing route. - */ - if (frametype != ROSE_CALL_REQUEST) { /* XXX */ - res = 0; - goto out; - } - - memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); - - if (!rose_parse_facilities(skb->data + ROSE_CALL_REQ_FACILITIES_OFF, - skb->len - ROSE_CALL_REQ_FACILITIES_OFF, - &facilities)) { - rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76); - goto out; - } - - /* - * Check for routing loops. - */ - rose_route = rose_route_list; - while (rose_route != NULL) { - if (rose_route->rand == facilities.rand && - rosecmp(src_addr, &rose_route->src_addr) == 0 && - ax25cmp(&facilities.dest_call, &rose_route->src_call) == 0 && - ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) { - rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120); - goto out; - } - rose_route = rose_route->next; - } - - if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic, 1)) == NULL) { - rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic); - goto out; - } - - if ((new_lci = rose_new_lci(new_neigh)) == 0) { - rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71); - goto put_neigh; - } - - if ((rose_route = kmalloc_obj(*rose_route, GFP_ATOMIC)) == NULL) { - rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120); - goto put_neigh; - } - - rose_route->lci1 = lci; - rose_route->src_addr = *src_addr; - rose_route->dest_addr = *dest_addr; - rose_route->src_call = facilities.dest_call; - rose_route->dest_call = facilities.source_call; - rose_route->rand = facilities.rand; - rose_route->neigh1 = rose_neigh; - rose_route->lci2 = new_lci; - rose_route->neigh2 = new_neigh; - - rose_neigh_hold(rose_route->neigh1); - rose_neigh_hold(rose_route->neigh2); - - rose_route->next = rose_route_list; - rose_route_list = rose_route; - - skb->data[0] &= 0xF0; - skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F; - skb->data[1] = (rose_route->lci2 >> 0) & 0xFF; - - rose_transmit_link(skb, rose_route->neigh2); - res = 1; - -put_neigh: - rose_neigh_put(new_neigh); -out: - spin_unlock_bh(&rose_route_list_lock); - spin_unlock_bh(&rose_neigh_list_lock); - - return res; -} - -#ifdef CONFIG_PROC_FS - -static void *rose_node_start(struct seq_file *seq, loff_t *pos) - __acquires(rose_node_list_lock) -{ - struct rose_node *rose_node; - int i = 1; - - spin_lock_bh(&rose_node_list_lock); - if (*pos == 0) - return SEQ_START_TOKEN; - - for (rose_node = rose_node_list; rose_node && i < *pos; - rose_node = rose_node->next, ++i); - - return (i == *pos) ? rose_node : NULL; -} - -static void *rose_node_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - - return (v == SEQ_START_TOKEN) ? rose_node_list - : ((struct rose_node *)v)->next; -} - -static void rose_node_stop(struct seq_file *seq, void *v) - __releases(rose_node_list_lock) -{ - spin_unlock_bh(&rose_node_list_lock); -} - -static int rose_node_show(struct seq_file *seq, void *v) -{ - char rsbuf[11]; - int i; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, "address mask n neigh neigh neigh\n"); - else { - const struct rose_node *rose_node = v; - seq_printf(seq, "%-10s %04d %d", - rose2asc(rsbuf, &rose_node->address), - rose_node->mask, - rose_node->count); - - for (i = 0; i < rose_node->count; i++) - seq_printf(seq, " %05d", rose_node->neighbour[i]->number); - - seq_puts(seq, "\n"); - } - return 0; -} - -const struct seq_operations rose_node_seqops = { - .start = rose_node_start, - .next = rose_node_next, - .stop = rose_node_stop, - .show = rose_node_show, -}; - -static void *rose_neigh_start(struct seq_file *seq, loff_t *pos) - __acquires(rose_neigh_list_lock) -{ - struct rose_neigh *rose_neigh; - int i = 1; - - spin_lock_bh(&rose_neigh_list_lock); - if (*pos == 0) - return SEQ_START_TOKEN; - - for (rose_neigh = rose_neigh_list; rose_neigh && i < *pos; - rose_neigh = rose_neigh->next, ++i); - - return (i == *pos) ? rose_neigh : NULL; -} - -static void *rose_neigh_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - - return (v == SEQ_START_TOKEN) ? rose_neigh_list - : ((struct rose_neigh *)v)->next; -} - -static void rose_neigh_stop(struct seq_file *seq, void *v) - __releases(rose_neigh_list_lock) -{ - spin_unlock_bh(&rose_neigh_list_lock); -} - -static int rose_neigh_show(struct seq_file *seq, void *v) -{ - char buf[11]; - int i; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "addr callsign dev count use mode restart t0 tf digipeaters\n"); - else { - struct rose_neigh *rose_neigh = v; - - /* if (!rose_neigh->loopback) { */ - seq_printf(seq, "%05d %-9s %-4s %3d %3d %3s %3s %3lu %3lu", - rose_neigh->number, - (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(buf, &rose_neigh->callsign), - rose_neigh->dev ? rose_neigh->dev->name : "???", - rose_neigh->count, - refcount_read(&rose_neigh->use) - rose_neigh->count - 1, - (rose_neigh->dce_mode) ? "DCE" : "DTE", - (rose_neigh->restarted) ? "yes" : "no", - ax25_display_timer(&rose_neigh->t0timer) / HZ, - ax25_display_timer(&rose_neigh->ftimer) / HZ); - - if (rose_neigh->digipeat != NULL) { - for (i = 0; i < rose_neigh->digipeat->ndigi; i++) - seq_printf(seq, " %s", ax2asc(buf, &rose_neigh->digipeat->calls[i])); - } - - seq_puts(seq, "\n"); - } - return 0; -} - - -const struct seq_operations rose_neigh_seqops = { - .start = rose_neigh_start, - .next = rose_neigh_next, - .stop = rose_neigh_stop, - .show = rose_neigh_show, -}; - -static void *rose_route_start(struct seq_file *seq, loff_t *pos) - __acquires(rose_route_list_lock) -{ - struct rose_route *rose_route; - int i = 1; - - spin_lock_bh(&rose_route_list_lock); - if (*pos == 0) - return SEQ_START_TOKEN; - - for (rose_route = rose_route_list; rose_route && i < *pos; - rose_route = rose_route->next, ++i); - - return (i == *pos) ? rose_route : NULL; -} - -static void *rose_route_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - - return (v == SEQ_START_TOKEN) ? rose_route_list - : ((struct rose_route *)v)->next; -} - -static void rose_route_stop(struct seq_file *seq, void *v) - __releases(rose_route_list_lock) -{ - spin_unlock_bh(&rose_route_list_lock); -} - -static int rose_route_show(struct seq_file *seq, void *v) -{ - char buf[11], rsbuf[11]; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "lci address callsign neigh <-> lci address callsign neigh\n"); - else { - struct rose_route *rose_route = v; - - if (rose_route->neigh1) - seq_printf(seq, - "%3.3X %-10s %-9s %05d ", - rose_route->lci1, - rose2asc(rsbuf, &rose_route->src_addr), - ax2asc(buf, &rose_route->src_call), - rose_route->neigh1->number); - else - seq_puts(seq, - "000 * * 00000 "); - - if (rose_route->neigh2) - seq_printf(seq, - "%3.3X %-10s %-9s %05d\n", - rose_route->lci2, - rose2asc(rsbuf, &rose_route->dest_addr), - ax2asc(buf, &rose_route->dest_call), - rose_route->neigh2->number); - else - seq_puts(seq, - "000 * * 00000\n"); - } - return 0; -} - -struct seq_operations rose_route_seqops = { - .start = rose_route_start, - .next = rose_route_next, - .stop = rose_route_stop, - .show = rose_route_show, -}; -#endif /* CONFIG_PROC_FS */ - -/* - * Release all memory associated with ROSE routing structures. - */ -void __exit rose_rt_free(void) -{ - struct rose_neigh *s, *rose_neigh = rose_neigh_list; - struct rose_node *t, *rose_node = rose_node_list; - struct rose_route *u, *rose_route = rose_route_list; - int i; - - while (rose_neigh != NULL) { - s = rose_neigh; - rose_neigh = rose_neigh->next; - - rose_remove_neigh(s); - rose_neigh_put(s); - } - - while (rose_node != NULL) { - t = rose_node; - rose_node = rose_node->next; - - for (i = 0; i < t->count; i++) - rose_neigh_put(t->neighbour[i]); - rose_remove_node(t); - } - - while (rose_route != NULL) { - u = rose_route; - rose_route = rose_route->next; - - rose_remove_route(u); - } -} diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c deleted file mode 100644 index 4dbc437a9e22..000000000000 --- a/net/rose/rose_subr.c +++ /dev/null @@ -1,556 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose); - -/* - * This routine purges all of the queues of frames. - */ -void rose_clear_queues(struct sock *sk) -{ - skb_queue_purge(&sk->sk_write_queue); - skb_queue_purge(&rose_sk(sk)->ack_queue); -} - -/* - * This routine purges the input queue of those frames that have been - * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the - * SDL diagram. - */ -void rose_frames_acked(struct sock *sk, unsigned short nr) -{ - struct sk_buff *skb; - struct rose_sock *rose = rose_sk(sk); - - /* - * Remove all the ack-ed frames from the ack queue. - */ - if (rose->va != nr) { - while (skb_peek(&rose->ack_queue) != NULL && rose->va != nr) { - skb = skb_dequeue(&rose->ack_queue); - kfree_skb(skb); - rose->va = (rose->va + 1) % ROSE_MODULUS; - } - } -} - -void rose_requeue_frames(struct sock *sk) -{ - struct sk_buff *skb, *skb_prev = NULL; - - /* - * Requeue all the un-ack-ed frames on the output queue to be picked - * up by rose_kick. This arrangement handles the possibility of an - * empty output queue. - */ - while ((skb = skb_dequeue(&rose_sk(sk)->ack_queue)) != NULL) { - if (skb_prev == NULL) - skb_queue_head(&sk->sk_write_queue, skb); - else - skb_append(skb_prev, skb, &sk->sk_write_queue); - skb_prev = skb; - } -} - -/* - * Validate that the value of nr is between va and vs. Return true or - * false for testing. - */ -int rose_validate_nr(struct sock *sk, unsigned short nr) -{ - struct rose_sock *rose = rose_sk(sk); - unsigned short vc = rose->va; - - while (vc != rose->vs) { - if (nr == vc) return 1; - vc = (vc + 1) % ROSE_MODULUS; - } - - return nr == rose->vs; -} - -/* - * This routine is called when the packet layer internally generates a - * control frame. - */ -void rose_write_internal(struct sock *sk, int frametype) -{ - struct rose_sock *rose = rose_sk(sk); - struct sk_buff *skb; - unsigned char *dptr; - unsigned char lci1, lci2; - int maxfaclen = 0; - int len, faclen; - int reserve; - - reserve = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1; - len = ROSE_MIN_LEN; - - switch (frametype) { - case ROSE_CALL_REQUEST: - len += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN; - maxfaclen = 256; - break; - case ROSE_CALL_ACCEPTED: - case ROSE_CLEAR_REQUEST: - case ROSE_RESET_REQUEST: - len += 2; - break; - } - - skb = alloc_skb(reserve + len + maxfaclen, GFP_ATOMIC); - if (!skb) - return; - - /* - * Space for AX.25 header and PID. - */ - skb_reserve(skb, reserve); - - dptr = skb_put(skb, len); - - lci1 = (rose->lci >> 8) & 0x0F; - lci2 = (rose->lci >> 0) & 0xFF; - - switch (frametype) { - case ROSE_CALL_REQUEST: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = ROSE_CALL_REQ_ADDR_LEN_VAL; - memcpy(dptr, &rose->dest_addr, ROSE_ADDR_LEN); - dptr += ROSE_ADDR_LEN; - memcpy(dptr, &rose->source_addr, ROSE_ADDR_LEN); - dptr += ROSE_ADDR_LEN; - faclen = rose_create_facilities(dptr, rose); - skb_put(skb, faclen); - dptr += faclen; - break; - - case ROSE_CALL_ACCEPTED: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = 0x00; /* Address length */ - *dptr++ = 0; /* Facilities length */ - break; - - case ROSE_CLEAR_REQUEST: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = rose->cause; - *dptr++ = rose->diagnostic; - break; - - case ROSE_RESET_REQUEST: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = ROSE_DTE_ORIGINATED; - *dptr++ = 0; - break; - - case ROSE_RR: - case ROSE_RNR: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr = frametype; - *dptr++ |= (rose->vr << 5) & 0xE0; - break; - - case ROSE_CLEAR_CONFIRMATION: - case ROSE_RESET_CONFIRMATION: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - break; - - default: - printk(KERN_ERR "ROSE: rose_write_internal - invalid frametype %02X\n", frametype); - kfree_skb(skb); - return; - } - - rose_transmit_link(skb, rose->neighbour); -} - -int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m) -{ - unsigned char *frame; - - frame = skb->data; - - *ns = *nr = *q = *d = *m = 0; - - switch (frame[2]) { - case ROSE_CALL_REQUEST: - case ROSE_CALL_ACCEPTED: - case ROSE_CLEAR_REQUEST: - case ROSE_CLEAR_CONFIRMATION: - case ROSE_RESET_REQUEST: - case ROSE_RESET_CONFIRMATION: - return frame[2]; - default: - break; - } - - if ((frame[2] & 0x1F) == ROSE_RR || - (frame[2] & 0x1F) == ROSE_RNR) { - *nr = (frame[2] >> 5) & 0x07; - return frame[2] & 0x1F; - } - - if ((frame[2] & 0x01) == ROSE_DATA) { - *q = (frame[0] & ROSE_Q_BIT) == ROSE_Q_BIT; - *d = (frame[0] & ROSE_D_BIT) == ROSE_D_BIT; - *m = (frame[2] & ROSE_M_BIT) == ROSE_M_BIT; - *nr = (frame[2] >> 5) & 0x07; - *ns = (frame[2] >> 1) & 0x07; - return ROSE_DATA; - } - - return ROSE_ILLEGAL; -} - -static int rose_parse_national(unsigned char *p, struct rose_facilities_struct *facilities, int len) -{ - unsigned char *pt; - unsigned char l, lg, n = 0; - int fac_national_digis_received = 0; - - do { - switch (*p & 0xC0) { - case 0x00: - if (len < 2) - return -1; - p += 2; - n += 2; - len -= 2; - break; - - case 0x40: - if (len < 3) - return -1; - if (*p == FAC_NATIONAL_RAND) - facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF); - p += 3; - n += 3; - len -= 3; - break; - - case 0x80: - if (len < 4) - return -1; - p += 4; - n += 4; - len -= 4; - break; - - case 0xC0: - if (len < 2) - return -1; - l = p[1]; - if (len < 2 + l) - return -1; - if (*p == FAC_NATIONAL_DEST_DIGI) { - if (!fac_national_digis_received) { - if (l < AX25_ADDR_LEN) - return -1; - memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN); - facilities->source_ndigis = 1; - } - } - else if (*p == FAC_NATIONAL_SRC_DIGI) { - if (!fac_national_digis_received) { - if (l < AX25_ADDR_LEN) - return -1; - memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN); - facilities->dest_ndigis = 1; - } - } - else if (*p == FAC_NATIONAL_FAIL_CALL) { - if (l < AX25_ADDR_LEN) - return -1; - memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN); - } - else if (*p == FAC_NATIONAL_FAIL_ADD) { - if (l < 1 + ROSE_ADDR_LEN) - return -1; - memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN); - } - else if (*p == FAC_NATIONAL_DIGIS) { - if (l % AX25_ADDR_LEN) - return -1; - fac_national_digis_received = 1; - facilities->source_ndigis = 0; - facilities->dest_ndigis = 0; - for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) { - if (pt[6] & AX25_HBIT) { - if (facilities->dest_ndigis >= ROSE_MAX_DIGIS) - return -1; - memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN); - } else { - if (facilities->source_ndigis >= ROSE_MAX_DIGIS) - return -1; - memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN); - } - } - } - p += l + 2; - n += l + 2; - len -= l + 2; - break; - } - } while (*p != 0x00 && len > 0); - - return n; -} - -static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *facilities, int len) -{ - unsigned char l, n = 0; - char callsign[11]; - - do { - switch (*p & 0xC0) { - case 0x00: - if (len < 2) - return -1; - p += 2; - n += 2; - len -= 2; - break; - - case 0x40: - if (len < 3) - return -1; - p += 3; - n += 3; - len -= 3; - break; - - case 0x80: - if (len < 4) - return -1; - p += 4; - n += 4; - len -= 4; - break; - - case 0xC0: - if (len < 2) - return -1; - l = p[1]; - - /* Prevent overflows*/ - if (l < 10 || l > 20) - return -1; - - if (*p == FAC_CCITT_DEST_NSAP) { - memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN); - memcpy(callsign, p + 12, l - 10); - callsign[l - 10] = '\0'; - asc2ax(&facilities->source_call, callsign); - } - if (*p == FAC_CCITT_SRC_NSAP) { - memcpy(&facilities->dest_addr, p + 7, ROSE_ADDR_LEN); - memcpy(callsign, p + 12, l - 10); - callsign[l - 10] = '\0'; - asc2ax(&facilities->dest_call, callsign); - } - p += l + 2; - n += l + 2; - len -= l + 2; - break; - } - } while (*p != 0x00 && len > 0); - - return n; -} - -int rose_parse_facilities(unsigned char *p, unsigned packet_len, - struct rose_facilities_struct *facilities) -{ - int facilities_len, len; - - facilities_len = *p++; - - if (facilities_len == 0 || (unsigned int)facilities_len > packet_len) - return 0; - - while (facilities_len >= 3 && *p == 0x00) { - facilities_len--; - p++; - - switch (*p) { - case FAC_NATIONAL: /* National */ - len = rose_parse_national(p + 1, facilities, facilities_len - 1); - break; - - case FAC_CCITT: /* CCITT */ - len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); - break; - - default: - printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p); - len = 1; - break; - } - - if (len < 0) - return 0; - if (WARN_ON(len >= facilities_len)) - return 0; - facilities_len -= len + 1; - p += len + 1; - } - - return facilities_len == 0; -} - -static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose) -{ - unsigned char *p = buffer + 1; - char *callsign; - char buf[11]; - int len, nb; - - /* National Facilities */ - if (rose->rand != 0 || rose->source_ndigis == 1 || rose->dest_ndigis == 1) { - *p++ = 0x00; - *p++ = FAC_NATIONAL; - - if (rose->rand != 0) { - *p++ = FAC_NATIONAL_RAND; - *p++ = (rose->rand >> 8) & 0xFF; - *p++ = (rose->rand >> 0) & 0xFF; - } - - /* Sent before older facilities */ - if ((rose->source_ndigis > 0) || (rose->dest_ndigis > 0)) { - int maxdigi = 0; - *p++ = FAC_NATIONAL_DIGIS; - *p++ = AX25_ADDR_LEN * (rose->source_ndigis + rose->dest_ndigis); - for (nb = 0 ; nb < rose->source_ndigis ; nb++) { - if (++maxdigi >= ROSE_MAX_DIGIS) - break; - memcpy(p, &rose->source_digis[nb], AX25_ADDR_LEN); - p[6] |= AX25_HBIT; - p += AX25_ADDR_LEN; - } - for (nb = 0 ; nb < rose->dest_ndigis ; nb++) { - if (++maxdigi >= ROSE_MAX_DIGIS) - break; - memcpy(p, &rose->dest_digis[nb], AX25_ADDR_LEN); - p[6] &= ~AX25_HBIT; - p += AX25_ADDR_LEN; - } - } - - /* For compatibility */ - if (rose->source_ndigis > 0) { - *p++ = FAC_NATIONAL_SRC_DIGI; - *p++ = AX25_ADDR_LEN; - memcpy(p, &rose->source_digis[0], AX25_ADDR_LEN); - p += AX25_ADDR_LEN; - } - - /* For compatibility */ - if (rose->dest_ndigis > 0) { - *p++ = FAC_NATIONAL_DEST_DIGI; - *p++ = AX25_ADDR_LEN; - memcpy(p, &rose->dest_digis[0], AX25_ADDR_LEN); - p += AX25_ADDR_LEN; - } - } - - *p++ = 0x00; - *p++ = FAC_CCITT; - - *p++ = FAC_CCITT_DEST_NSAP; - - callsign = ax2asc(buf, &rose->dest_call); - - *p++ = strlen(callsign) + 10; - *p++ = (strlen(callsign) + 9) * 2; /* ??? */ - - *p++ = 0x47; *p++ = 0x00; *p++ = 0x11; - *p++ = ROSE_ADDR_LEN * 2; - memcpy(p, &rose->dest_addr, ROSE_ADDR_LEN); - p += ROSE_ADDR_LEN; - - memcpy(p, callsign, strlen(callsign)); - p += strlen(callsign); - - *p++ = FAC_CCITT_SRC_NSAP; - - callsign = ax2asc(buf, &rose->source_call); - - *p++ = strlen(callsign) + 10; - *p++ = (strlen(callsign) + 9) * 2; /* ??? */ - - *p++ = 0x47; *p++ = 0x00; *p++ = 0x11; - *p++ = ROSE_ADDR_LEN * 2; - memcpy(p, &rose->source_addr, ROSE_ADDR_LEN); - p += ROSE_ADDR_LEN; - - memcpy(p, callsign, strlen(callsign)); - p += strlen(callsign); - - len = p - buffer; - buffer[0] = len - 1; - - return len; -} - -void rose_disconnect(struct sock *sk, int reason, int cause, int diagnostic) -{ - struct rose_sock *rose = rose_sk(sk); - - rose_stop_timer(sk); - rose_stop_idletimer(sk); - - rose_clear_queues(sk); - - rose->lci = 0; - rose->state = ROSE_STATE_0; - - if (cause != -1) - rose->cause = cause; - - if (diagnostic != -1) - rose->diagnostic = diagnostic; - - sk->sk_state = TCP_CLOSE; - sk->sk_err = reason; - sk->sk_shutdown |= SEND_SHUTDOWN; - - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - } -} diff --git a/net/rose/rose_timer.c b/net/rose/rose_timer.c deleted file mode 100644 index bb60a1654d61..000000000000 --- a/net/rose/rose_timer.c +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void rose_heartbeat_expiry(struct timer_list *t); -static void rose_timer_expiry(struct timer_list *); -static void rose_idletimer_expiry(struct timer_list *); - -void rose_start_heartbeat(struct sock *sk) -{ - sk_stop_timer(sk, &sk->sk_timer); - - sk->sk_timer.function = rose_heartbeat_expiry; - sk->sk_timer.expires = jiffies + 5 * HZ; - - sk_reset_timer(sk, &sk->sk_timer, sk->sk_timer.expires); -} - -void rose_start_t1timer(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - sk_stop_timer(sk, &rose->timer); - - rose->timer.function = rose_timer_expiry; - rose->timer.expires = jiffies + rose->t1; - - sk_reset_timer(sk, &rose->timer, rose->timer.expires); -} - -void rose_start_t2timer(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - sk_stop_timer(sk, &rose->timer); - - rose->timer.function = rose_timer_expiry; - rose->timer.expires = jiffies + rose->t2; - - sk_reset_timer(sk, &rose->timer, rose->timer.expires); -} - -void rose_start_t3timer(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - sk_stop_timer(sk, &rose->timer); - - rose->timer.function = rose_timer_expiry; - rose->timer.expires = jiffies + rose->t3; - - sk_reset_timer(sk, &rose->timer, rose->timer.expires); -} - -void rose_start_hbtimer(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - sk_stop_timer(sk, &rose->timer); - - rose->timer.function = rose_timer_expiry; - rose->timer.expires = jiffies + rose->hb; - - sk_reset_timer(sk, &rose->timer, rose->timer.expires); -} - -void rose_start_idletimer(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - sk_stop_timer(sk, &rose->idletimer); - - if (rose->idle > 0) { - rose->idletimer.function = rose_idletimer_expiry; - rose->idletimer.expires = jiffies + rose->idle; - - sk_reset_timer(sk, &rose->idletimer, rose->idletimer.expires); - } -} - -void rose_stop_heartbeat(struct sock *sk) -{ - sk_stop_timer(sk, &sk->sk_timer); -} - -void rose_stop_timer(struct sock *sk) -{ - sk_stop_timer(sk, &rose_sk(sk)->timer); -} - -void rose_stop_idletimer(struct sock *sk) -{ - sk_stop_timer(sk, &rose_sk(sk)->idletimer); -} - -static void rose_heartbeat_expiry(struct timer_list *t) -{ - struct sock *sk = timer_container_of(sk, t, sk_timer); - struct rose_sock *rose = rose_sk(sk); - - bh_lock_sock(sk); - if (sock_owned_by_user(sk)) { - sk_reset_timer(sk, &sk->sk_timer, jiffies + HZ/20); - goto out; - } - switch (rose->state) { - case ROSE_STATE_0: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (sock_flag(sk, SOCK_DESTROY) || - (sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) { - bh_unlock_sock(sk); - rose_destroy_socket(sk); - sock_put(sk); - return; - } - break; - - case ROSE_STATE_3: - /* - * Check for the state of the receive buffer. - */ - if (atomic_read(&sk->sk_rmem_alloc) < (sk->sk_rcvbuf / 2) && - (rose->condition & ROSE_COND_OWN_RX_BUSY)) { - rose->condition &= ~ROSE_COND_OWN_RX_BUSY; - rose->condition &= ~ROSE_COND_ACK_PENDING; - rose->vl = rose->vr; - rose_write_internal(sk, ROSE_RR); - rose_stop_timer(sk); /* HB */ - break; - } - break; - } - - rose_start_heartbeat(sk); -out: - bh_unlock_sock(sk); - sock_put(sk); -} - -static void rose_timer_expiry(struct timer_list *t) -{ - struct rose_sock *rose = timer_container_of(rose, t, timer); - struct sock *sk = &rose->sock; - - bh_lock_sock(sk); - if (sock_owned_by_user(sk)) { - sk_reset_timer(sk, &rose->timer, jiffies + HZ/20); - goto out; - } - switch (rose->state) { - case ROSE_STATE_1: /* T1 */ - case ROSE_STATE_4: /* T2 */ - rose_write_internal(sk, ROSE_CLEAR_REQUEST); - rose->state = ROSE_STATE_2; - rose_start_t3timer(sk); - break; - - case ROSE_STATE_2: /* T3 */ - rose_neigh_put(rose->neighbour); - rose_disconnect(sk, ETIMEDOUT, -1, -1); - break; - - case ROSE_STATE_3: /* HB */ - if (rose->condition & ROSE_COND_ACK_PENDING) { - rose->condition &= ~ROSE_COND_ACK_PENDING; - rose_enquiry_response(sk); - } - break; - } -out: - bh_unlock_sock(sk); - sock_put(sk); -} - -static void rose_idletimer_expiry(struct timer_list *t) -{ - struct rose_sock *rose = timer_container_of(rose, t, idletimer); - struct sock *sk = &rose->sock; - - bh_lock_sock(sk); - if (sock_owned_by_user(sk)) { - sk_reset_timer(sk, &rose->idletimer, jiffies + HZ/20); - goto out; - } - rose_clear_queues(sk); - - rose_write_internal(sk, ROSE_CLEAR_REQUEST); - rose_sk(sk)->state = ROSE_STATE_2; - - rose_start_t3timer(sk); - - sk->sk_state = TCP_CLOSE; - sk->sk_err = 0; - sk->sk_shutdown |= SEND_SHUTDOWN; - - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - } -out: - bh_unlock_sock(sk); - sock_put(sk); -} diff --git a/net/rose/sysctl_net_rose.c b/net/rose/sysctl_net_rose.c deleted file mode 100644 index d801315b7083..000000000000 --- a/net/rose/sysctl_net_rose.c +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com) - */ -#include -#include -#include -#include -#include - -static int min_timer[] = {1 * HZ}; -static int max_timer[] = {300 * HZ}; -static int min_idle[] = {0 * HZ}; -static int max_idle[] = {65535 * HZ}; -static int min_route[1], max_route[] = {1}; -static int min_ftimer[] = {60 * HZ}; -static int max_ftimer[] = {600 * HZ}; -static int min_maxvcs[] = {1}, max_maxvcs[] = {254}; -static int min_window[] = {1}, max_window[] = {7}; - -static struct ctl_table_header *rose_table_header; - -static struct ctl_table rose_table[] = { - { - .procname = "restart_request_timeout", - .data = &sysctl_rose_restart_request_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_timer, - .extra2 = &max_timer - }, - { - .procname = "call_request_timeout", - .data = &sysctl_rose_call_request_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_timer, - .extra2 = &max_timer - }, - { - .procname = "reset_request_timeout", - .data = &sysctl_rose_reset_request_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_timer, - .extra2 = &max_timer - }, - { - .procname = "clear_request_timeout", - .data = &sysctl_rose_clear_request_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_timer, - .extra2 = &max_timer - }, - { - .procname = "no_activity_timeout", - .data = &sysctl_rose_no_activity_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_idle, - .extra2 = &max_idle - }, - { - .procname = "acknowledge_hold_back_timeout", - .data = &sysctl_rose_ack_hold_back_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_timer, - .extra2 = &max_timer - }, - { - .procname = "routing_control", - .data = &sysctl_rose_routing_control, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_route, - .extra2 = &max_route - }, - { - .procname = "link_fail_timeout", - .data = &sysctl_rose_link_fail_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_ftimer, - .extra2 = &max_ftimer - }, - { - .procname = "maximum_virtual_circuits", - .data = &sysctl_rose_maximum_vcs, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_maxvcs, - .extra2 = &max_maxvcs - }, - { - .procname = "window_size", - .data = &sysctl_rose_window_size, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_window, - .extra2 = &max_window - }, -}; - -void __init rose_register_sysctl(void) -{ - rose_table_header = register_net_sysctl(&init_net, "net/rose", rose_table); -} - -void rose_unregister_sysctl(void) -{ - unregister_net_sysctl_table(rose_table_header); -} -- cgit v1.2.3