From 5037a00992e5fcb3d8509964313565a3dab6697c Mon Sep 17 00:00:00 2001 From: Sunil Dutt Date: Thu, 25 Jan 2018 17:13:37 +0200 Subject: nl80211: Introduce scan flags to emphasize requested scan behavior This commit defines new scan flags (LOW_SPAN, LOW_POWER, HIGH_LATENCY) to emphasize the requested scan behavior for the driver. These flags are optional and are mutually exclusive. The implementation of the respective functionality can be driver/hardware specific. These flags can be used to control the compromise between how long a scan takes, how much power it uses, and high accurate/complete the scan is in finding the BSSs. Signed-off-by: Sunil Dutt Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index c587a61c32bf..6f60503fa617 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4945,6 +4945,9 @@ enum nl80211_feature_flags { * probe request tx deferral and suppression * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL * value in %NL80211_ATTR_USE_MFP. + * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan. + * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan. + * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4972,6 +4975,9 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE, NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION, NL80211_EXT_FEATURE_MFP_OPTIONAL, + NL80211_EXT_FEATURE_LOW_SPAN_SCAN, + NL80211_EXT_FEATURE_LOW_POWER_SCAN, + NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -5032,6 +5038,10 @@ enum nl80211_timeout_reason { * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN * requests. * + * NL80211_SCAN_FLAG_LOW_SPAN, NL80211_SCAN_FLAG_LOW_POWER, and + * NL80211_SCAN_FLAG_HIGH_ACCURACY flags are exclusive of each other, i.e., only + * one of them can be used in the request. + * * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured @@ -5059,7 +5069,20 @@ enum nl80211_timeout_reason { * and suppression (if it has received a broadcast Probe Response frame, * Beacon frame or FILS Discovery frame from an AP that the STA considers * a suitable candidate for (re-)association - suitable in terms of - * SSID and/or RSSI + * SSID and/or RSSI. + * @NL80211_SCAN_FLAG_LOW_SPAN: Span corresponds to the total time taken to + * accomplish the scan. Thus, this flag intends the driver to perform the + * scan request with lesser span/duration. It is specific to the driver + * implementations on how this is accomplished. Scan accuracy may get + * impacted with this flag. + * @NL80211_SCAN_FLAG_LOW_POWER: This flag intends the scan attempts to consume + * optimal possible power. Drivers can resort to their specific means to + * optimize the power. Scan accuracy may get impacted with this flag. + * @NL80211_SCAN_FLAG_HIGH_ACCURACY: Accuracy here intends to the extent of scan + * results obtained. Thus HIGH_ACCURACY scan flag aims to get maximum + * possible scan results. This flag hints the driver to use the best + * possible scan configuration to improve the accuracy in scanning. + * Latency and power use may get impacted with this flag. */ enum nl80211_scan_flags { NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, @@ -5070,6 +5093,9 @@ enum nl80211_scan_flags { NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP = 1<<5, NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE = 1<<6, NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 1<<7, + NL80211_SCAN_FLAG_LOW_SPAN = 1<<8, + NL80211_SCAN_FLAG_LOW_POWER = 1<<9, + NL80211_SCAN_FLAG_HIGH_ACCURACY = 1<<10, }; /** -- cgit v1.2.3 From 40cbfa90218bc570a7959b436b9d48a18c361041 Mon Sep 17 00:00:00 2001 From: Srinivas Dasari Date: Thu, 25 Jan 2018 17:13:38 +0200 Subject: cfg80211/nl80211: Optional authentication offload to userspace This interface allows the host driver to offload the authentication to user space. This is exclusively defined for host drivers that do not define separate commands for authentication and association, but rely on userspace SME (e.g., in wpa_supplicant for the ~WPA_DRIVER_FLAGS_SME case) for the authentication to happen. This can be used to implement SAE without full implementation in the kernel/firmware while still being able to use NL80211_CMD_CONNECT with driver-based BSS selection. Host driver sends NL80211_CMD_EXTERNAL_AUTH event to start/abort authentication to the port on which connect is triggered and status of authentication is further indicated by user space to host driver through the same command response interface. User space entities advertise this capability through the NL80211_ATTR_EXTERNAL_AUTH_SUPP flag in the NL80211_CMD_CONNECT request. Host drivers shall look at this capability to offload the authentication. Signed-off-by: Srinivas Dasari Signed-off-by: Jouni Malinen [add socket connection ownership check] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 54 +++++++++++++++++++++++-- include/uapi/linux/nl80211.h | 47 ++++++++++++++++++++++ net/wireless/nl80211.c | 94 ++++++++++++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 15 +++++++ net/wireless/trace.h | 23 +++++++++++ 5 files changed, 230 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 81174f9b8d14..68def3e5b013 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1905,11 +1905,16 @@ struct cfg80211_auth_request { * @ASSOC_REQ_DISABLE_HT: Disable HT (802.11n) * @ASSOC_REQ_DISABLE_VHT: Disable VHT * @ASSOC_REQ_USE_RRM: Declare RRM capability in this association + * @CONNECT_REQ_EXTERNAL_AUTH_SUPPORT: User space indicates external + * authentication capability. Drivers can offload authentication to + * userspace if this flag is set. Only applicable for cfg80211_connect() + * request (connect callback). */ enum cfg80211_assoc_req_flags { - ASSOC_REQ_DISABLE_HT = BIT(0), - ASSOC_REQ_DISABLE_VHT = BIT(1), - ASSOC_REQ_USE_RRM = BIT(2), + ASSOC_REQ_DISABLE_HT = BIT(0), + ASSOC_REQ_DISABLE_VHT = BIT(1), + ASSOC_REQ_USE_RRM = BIT(2), + CONNECT_REQ_EXTERNAL_AUTH_SUPPORT = BIT(3), }; /** @@ -2600,6 +2605,33 @@ struct cfg80211_pmk_conf { const u8 *pmk_r0_name; }; +/** + * struct cfg80211_external_auth_params - Trigger External authentication. + * + * Commonly used across the external auth request and event interfaces. + * + * @action: action type / trigger for external authentication. Only significant + * for the authentication request event interface (driver to user space). + * @bssid: BSSID of the peer with which the authentication has + * to happen. Used by both the authentication request event and + * authentication response command interface. + * @ssid: SSID of the AP. Used by both the authentication request event and + * authentication response command interface. + * @key_mgmt_suite: AKM suite of the respective authentication. Used by the + * authentication request event interface. + * @status: status code, %WLAN_STATUS_SUCCESS for successful authentication, + * use %WLAN_STATUS_UNSPECIFIED_FAILURE if user space cannot give you + * the real status code for failures. Used only for the authentication + * response command interface (user space to driver). + */ +struct cfg80211_external_auth_params { + enum nl80211_external_auth_action action; + u8 bssid[ETH_ALEN] __aligned(2); + struct cfg80211_ssid ssid; + unsigned int key_mgmt_suite; + u16 status; +}; + /** * struct cfg80211_ops - backend description for wireless configuration * @@ -2923,6 +2955,9 @@ struct cfg80211_pmk_conf { * (invoked with the wireless_dev mutex held) * @del_pmk: delete the previously configured PMK for the given authenticator. * (invoked with the wireless_dev mutex held) + * + * @external_auth: indicates result of offloaded authentication processing from + * user space */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -3216,6 +3251,8 @@ struct cfg80211_ops { const struct cfg80211_pmk_conf *conf); int (*del_pmk)(struct wiphy *wiphy, struct net_device *dev, const u8 *aa); + int (*external_auth)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_external_auth_params *params); }; /* @@ -6202,6 +6239,17 @@ void cfg80211_nan_func_terminated(struct wireless_dev *wdev, /* ethtool helper */ void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); +/** + * cfg80211_external_auth_request - userspace request for authentication + * @netdev: network device + * @params: External authentication parameters + * @gfp: allocation flags + * Returns: 0 on success, < 0 on error + */ +int cfg80211_external_auth_request(struct net_device *netdev, + struct cfg80211_external_auth_params *params, + gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 6f60503fa617..c2342456cf16 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -992,6 +992,27 @@ * * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded. * + * @NL80211_CMD_EXTERNAL_AUTH: This interface is exclusively defined for host + * drivers that do not define separate commands for authentication and + * association, but rely on user space for the authentication to happen. + * This interface acts both as the event request (driver to user space) + * to trigger the authentication and command response (userspace to + * driver) to indicate the authentication status. + * + * User space uses the %NL80211_CMD_CONNECT command to the host driver to + * trigger a connection. The host driver selects a BSS and further uses + * this interface to offload only the authentication part to the user + * space. Authentication frames are passed between the driver and user + * space through the %NL80211_CMD_FRAME interface. Host driver proceeds + * further with the association after getting successful authentication + * status. User space indicates the authentication status through + * %NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH + * command interface. + * + * Host driver reports this status on an authentication failure to the + * user space through the connect result as the user space would have + * initiated the connection through the connect request. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1198,6 +1219,8 @@ enum nl80211_commands { NL80211_CMD_RELOAD_REGDB, + NL80211_CMD_EXTERNAL_AUTH, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2153,6 +2176,16 @@ enum nl80211_commands { * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT. * @NL80211_ATTR_PORT_AUTHORIZED: (reserved) * + * @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external + * authentication operation (u32 attribute with an + * &enum nl80211_external_auth_action value). This is used with the + * &NL80211_CMD_EXTERNAL_AUTH request event. + * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user + * space supports external authentication. This attribute shall be used + * only with %NL80211_CMD_CONNECT request. The driver may offload + * authentication processing to user space if this capability is indicated + * in NL80211_CMD_CONNECT requests from the user space. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2579,6 +2612,9 @@ enum nl80211_attrs { NL80211_ATTR_PMKR0_NAME, NL80211_ATTR_PORT_AUTHORIZED, + NL80211_ATTR_EXTERNAL_AUTH_ACTION, + NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -5495,4 +5531,15 @@ enum nl80211_nan_match_attributes { NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1 }; +/** + * nl80211_external_auth_action - Action to perform with external + * authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION. + * @NL80211_EXTERNAL_AUTH_START: Start the authentication. + * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication. + */ +enum nl80211_external_auth_action { + NL80211_EXTERNAL_AUTH_START, + NL80211_EXTERNAL_AUTH_ABORT, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index dd249ec9f228..aa6b64069c80 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -420,6 +420,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_FILS_CACHE_ID] = { .len = 2 }, [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN }, [NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG }, + [NL80211_ATTR_EXTERNAL_AUTH_SUPPORT] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -9161,6 +9162,15 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + if (nla_get_flag(info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT])) { + if (!info->attrs[NL80211_ATTR_SOCKET_OWNER]) { + GENL_SET_ERR_MSG(info, + "external auth requires connection ownership"); + return -EINVAL; + } + connect.flags |= CONNECT_REQ_EXTERNAL_AUTH_SUPPORT; + } + wdev_lock(dev->ieee80211_ptr); err = cfg80211_connect(rdev, dev, &connect, connkeys, @@ -12469,6 +12479,41 @@ static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) return ret; } +static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct cfg80211_external_auth_params params; + + if (rdev->ops->external_auth) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_SSID]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_BSSID]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_STATUS_CODE]) + return -EINVAL; + + memset(¶ms, 0, sizeof(params)); + + params.ssid.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + if (params.ssid.ssid_len == 0 || + params.ssid.ssid_len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + memcpy(params.ssid.ssid, nla_data(info->attrs[NL80211_ATTR_SSID]), + params.ssid.ssid_len); + + memcpy(params.bssid, nla_data(info->attrs[NL80211_ATTR_BSSID]), + ETH_ALEN); + + params.status = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); + + return rdev_external_auth(rdev, dev, ¶ms); +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -13364,6 +13409,14 @@ static const struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_EXTERNAL_AUTH, + .doit = nl80211_external_auth, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; @@ -15375,6 +15428,47 @@ void nl80211_send_ap_stopped(struct wireless_dev *wdev) nlmsg_free(msg); } +int cfg80211_external_auth_request(struct net_device *dev, + struct cfg80211_external_auth_params *params, + gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + + if (!wdev->conn_owner_nlportid) + return -EINVAL; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_EXTERNAL_AUTH); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || + nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, params->key_mgmt_suite) || + nla_put_u32(msg, NL80211_ATTR_EXTERNAL_AUTH_ACTION, + params->action) || + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid) || + nla_put(msg, NL80211_ATTR_SSID, params->ssid.ssid_len, + params->ssid.ssid)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, + wdev->conn_owner_nlportid); + return 0; + + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} +EXPORT_SYMBOL(cfg80211_external_auth_request); + /* initialisation/exit functions */ int __init nl80211_init(void) diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 0c06240d25af..84f23ae015fc 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1190,4 +1190,19 @@ static inline int rdev_del_pmk(struct cfg80211_registered_device *rdev, trace_rdev_return_int(&rdev->wiphy, ret); return ret; } + +static inline int +rdev_external_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_external_auth_params *params) +{ + int ret = -EOPNOTSUPP; + + trace_rdev_external_auth(&rdev->wiphy, dev, params); + if (rdev->ops->external_auth) + ret = rdev->ops->external_auth(&rdev->wiphy, dev, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index bcfedd39e7a3..5152938b358d 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2319,6 +2319,29 @@ TRACE_EVENT(rdev_del_pmk, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(aa)) ); +TRACE_EVENT(rdev_external_auth, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_external_auth_params *params), + TP_ARGS(wiphy, netdev, params), + TP_STRUCT__entry(WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(bssid) + __array(u8, ssid, IEEE80211_MAX_SSID_LEN + 1) + __field(u16, status) + ), + TP_fast_assign(WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(bssid, params->bssid); + memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1); + memcpy(__entry->ssid, params->ssid.ssid, + params->ssid.ssid_len); + __entry->status = params->status; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT + ", ssid: %s, status: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, + __entry->bssid, __entry->ssid, __entry->status) +); + /************************************************************* * cfg80211 exported functions traces * *************************************************************/ -- cgit v1.2.3 From 466b9936bf93b7ec3bce1dcd493262ff0a8a4f44 Mon Sep 17 00:00:00 2001 From: "tamizhr@codeaurora.org" Date: Wed, 31 Jan 2018 16:24:49 +0530 Subject: cfg80211: Add support to notify station's opmode change to userspace ht/vht action frames will be sent to AP from station to notify change of its ht/vht opmode(max bandwidth, smps mode or nss) modified values. Currently these valuse used by driver/firmware for rate control algorithm. This patch introduces NL80211_CMD_STA_OPMODE_CHANGED command to notify those modified/current supported values(max bandwidth, smps mode, max nss) to userspace application. This will be useful for the application like steering, which closely monitoring station's capability changes. Since the application has taken these values during station association. Signed-off-by: Tamizh chelvam Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 43 ++++++++++++++++++++++++++++++++++ include/uapi/linux/nl80211.h | 12 ++++++++++ net/wireless/nl80211.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 68def3e5b013..7d49cd0cf92d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3553,6 +3553,35 @@ enum wiphy_vendor_command_flags { WIPHY_VENDOR_CMD_NEED_RUNNING = BIT(2), }; +/** + * enum wiphy_opmode_flag - Station's ht/vht operation mode information flags + * + * @STA_OPMODE_MAX_BW_CHANGED: Max Bandwidth changed + * @STA_OPMODE_SMPS_MODE_CHANGED: SMPS mode changed + * @STA_OPMODE_N_SS_CHANGED: max N_SS (number of spatial streams) changed + * + */ +enum wiphy_opmode_flag { + STA_OPMODE_MAX_BW_CHANGED = BIT(0), + STA_OPMODE_SMPS_MODE_CHANGED = BIT(1), + STA_OPMODE_N_SS_CHANGED = BIT(2), +}; + +/** + * struct sta_opmode_info - Station's ht/vht operation mode information + * @changed: contains value from &enum wiphy_opmode_flag + * @smps_mode: New SMPS mode of a station + * @bw: new max bandwidth value of a station + * @rx_nss: new rx_nss value of a station + */ + +struct sta_opmode_info { + u32 changed; + u8 smps_mode; + u8 bw; + u8 rx_nss; +}; + /** * struct wiphy_vendor_command - vendor command definition * @info: vendor command identifying information, as used in nl80211 @@ -5721,6 +5750,20 @@ void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp); void cfg80211_radar_event(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, gfp_t gfp); +/** + * cfg80211_sta_opmode_change_notify - STA's ht/vht operation mode change event + * @dev: network device + * @mac: MAC address of a station which opmode got modified + * @sta_opmode: station's current opmode value + * @gfp: context flags + * + * Driver should call this function when station's opmode modified via action + * frame. + */ +void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac, + struct sta_opmode_info *sta_opmode, + gfp_t gfp); + /** * cfg80211_cac_event - Channel availability check (CAC) event * @netdev: network device diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index c2342456cf16..ca3d5a613fc0 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1013,6 +1013,11 @@ * user space through the connect result as the user space would have * initiated the connection through the connect request. * + * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's + * ht opmode or vht opmode changes using any of &NL80211_ATTR_SMPS_MODE, + * &NL80211_ATTR_CHANNEL_WIDTH,&NL80211_ATTR_NSS attributes with its + * address(specified in &NL80211_ATTR_MAC). + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1221,6 +1226,8 @@ enum nl80211_commands { NL80211_CMD_EXTERNAL_AUTH, + NL80211_CMD_STA_OPMODE_CHANGED, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2186,6 +2193,9 @@ enum nl80211_commands { * authentication processing to user space if this capability is indicated * in NL80211_CMD_CONNECT requests from the user space. * + * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this + * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2615,6 +2625,8 @@ enum nl80211_attrs { NL80211_ATTR_EXTERNAL_AUTH_ACTION, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, + NL80211_ATTR_NSS, + /* 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 bdb70fe74e3c..cc6ec5bab676 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14950,6 +14950,61 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac, + struct sta_opmode_info *sta_opmode, + gfp_t gfp) +{ + struct sk_buff *msg; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + void *hdr; + + if (WARN_ON(!mac)) + return; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_STA_OPMODE_CHANGED); + if (!hdr) { + nlmsg_free(msg); + return; + } + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex)) + goto nla_put_failure; + + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac)) + goto nla_put_failure; + + if ((sta_opmode->changed & STA_OPMODE_SMPS_MODE_CHANGED) && + nla_put_u8(msg, NL80211_ATTR_SMPS_MODE, sta_opmode->smps_mode)) + goto nla_put_failure; + + if ((sta_opmode->changed & STA_OPMODE_MAX_BW_CHANGED) && + nla_put_u8(msg, NL80211_ATTR_CHANNEL_WIDTH, sta_opmode->bw)) + goto nla_put_failure; + + if ((sta_opmode->changed & STA_OPMODE_N_SS_CHANGED) && + nla_put_u8(msg, NL80211_ATTR_NSS, sta_opmode->rx_nss)) + 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_sta_opmode_change_notify); + void cfg80211_probe_status(struct net_device *dev, const u8 *addr, u64 cookie, bool acked, gfp_t gfp) { -- cgit v1.2.3 From 65074d43fc77bcae32776724b7fa2696923c78e4 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Wed, 6 Dec 2017 14:45:13 -0800 Subject: perf/core: Prepare perf_event.h for new types: 'perf_kprobe' and 'perf_uprobe' Two new perf types, perf_kprobe and perf_uprobe, will be added to allow creating [k,u]probe with perf_event_open. These [k,u]probe are associated with the file decriptor created by perf_event_open(), thus are easy to clean when the file descriptor is destroyed. kprobe_func and uprobe_path are added to union config1 for pointers to function name for kprobe or binary path for uprobe. kprobe_addr and probe_offset are added to union config2 for kernel address (when kprobe_func is NULL), or [k,u]probe offset. Signed-off-by: Song Liu Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Yonghong Song Reviewed-by: Josef Bacik Acked-by: Alexei Starovoitov Cc: Cc: Cc: Cc: Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20171206224518.3598254-4-songliubraving@fb.com Signed-off-by: Ingo Molnar --- include/uapi/linux/perf_event.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index c77c9a2ebbbb..5d49cfc509e7 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -380,10 +380,14 @@ struct perf_event_attr { __u32 bp_type; union { __u64 bp_addr; + __u64 kprobe_func; /* for perf_kprobe */ + __u64 uprobe_path; /* for perf_uprobe */ __u64 config1; /* extension of config */ }; union { __u64 bp_len; + __u64 kprobe_addr; /* when kprobe_func == NULL */ + __u64 probe_offset; /* for perf_[k,u]probe */ __u64 config2; /* extension of config1 */ }; __u64 branch_sample_type; /* enum perf_branch_sample_type */ -- cgit v1.2.3 From a11024457d348672b26b3d4581ed19c793399b48 Mon Sep 17 00:00:00 2001 From: Felix Kuehling Date: Tue, 6 Feb 2018 20:32:46 -0500 Subject: uapi: Fix type used in ioctl parameter structures Use __u32 and __u64 instead of POSIX types that may not be defined in user mode builds. Signed-off-by: Felix Kuehling Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- include/uapi/linux/kfd_ioctl.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/kfd_ioctl.h b/include/uapi/linux/kfd_ioctl.h index f4cab5b3ba9a..111d73ba2d96 100644 --- a/include/uapi/linux/kfd_ioctl.h +++ b/include/uapi/linux/kfd_ioctl.h @@ -263,10 +263,10 @@ struct kfd_ioctl_get_tile_config_args { }; struct kfd_ioctl_set_trap_handler_args { - uint64_t tba_addr; /* to KFD */ - uint64_t tma_addr; /* to KFD */ - uint32_t gpu_id; /* to KFD */ - uint32_t pad; + __u64 tba_addr; /* to KFD */ + __u64 tma_addr; /* to KFD */ + __u32 gpu_id; /* to KFD */ + __u32 pad; }; #define AMDKFD_IOCTL_BASE 'K' -- cgit v1.2.3 From e1603b6effe177210701d3d7132d1b68e7bd2c93 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Fri, 9 Feb 2018 18:04:54 +0300 Subject: inotify: Extend ioctl to allow to request id of new watch descriptor Watch descriptor is id of the watch created by inotify_add_watch(). It is allocated in inotify_add_to_idr(), and takes the numbers starting from 1. Every new inotify watch obtains next available number (usually, old + 1), as served by idr_alloc_cyclic(). CRIU (Checkpoint/Restore In Userspace) project supports inotify files, and restores watched descriptors with the same numbers, they had before dump. Since there was no kernel support, we had to use cycle to add a watch with specific descriptor id: while (1) { int wd; wd = inotify_add_watch(inotify_fd, path, mask); if (wd < 0) { break; } else if (wd == desired_wd_id) { ret = 0; break; } inotify_rm_watch(inotify_fd, wd); } (You may find the actual code at the below link: https://github.com/checkpoint-restore/criu/blob/v3.7/criu/fsnotify.c#L577) The cycle is suboptiomal and very expensive, but since there is no better kernel support, it was the only way to restore that. Happily, we had met mostly descriptors with small id, and this approach had worked somehow. But recent time containers with inotify with big watch descriptors begun to come, and this way stopped to work at all. When descriptor id is something about 0x34d71d6, the restoring process spins in busy loop for a long time, and the restore hungs and delay of migration from node to node could easily be watched. This patch aims to solve this problem. It introduces new ioctl INOTIFY_IOC_SETNEXTWD, which allows to request the number of next created watch descriptor from userspace. It simply calls idr_set_cursor() primitive to populate idr::idr_next, so that next idr_alloc_cyclic() allocation will return this id, if it is not occupied. This is the way which is used to restore some other resources from userspace. For example, /proc/sys/kernel/ns_last_pid works the same for task pids. The new code is under CONFIG_CHECKPOINT_RESTORE #define, so small system may exclude it. v2: Use INT_MAX instead of custom definition of max id, as IDR subsystem guarantees id is between 0 and INT_MAX. CC: Jan Kara CC: Matthew Wilcox CC: Andrew Morton CC: Amir Goldstein Signed-off-by: Kirill Tkhai Reviewed-by: Cyrill Gorcunov Reviewed-by: Matthew Wilcox Reviewed-by: Andrew Morton Signed-off-by: Jan Kara --- fs/notify/inotify/inotify_user.c | 14 ++++++++++++++ include/uapi/linux/inotify.h | 8 ++++++++ 2 files changed, 22 insertions(+) (limited to 'include/uapi/linux') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 2c908b31d6c9..8f17719842ec 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -307,6 +307,20 @@ static long inotify_ioctl(struct file *file, unsigned int cmd, spin_unlock(&group->notification_lock); ret = put_user(send_len, (int __user *) p); break; +#ifdef CONFIG_CHECKPOINT_RESTORE + case INOTIFY_IOC_SETNEXTWD: + ret = -EINVAL; + if (arg >= 1 && arg <= INT_MAX) { + struct inotify_group_private_data *data; + + data = &group->inotify_data; + spin_lock(&data->idr_lock); + idr_set_cursor(&data->idr, (unsigned int)arg); + spin_unlock(&data->idr_lock); + ret = 0; + } + break; +#endif /* CONFIG_CHECKPOINT_RESTORE */ } return ret; diff --git a/include/uapi/linux/inotify.h b/include/uapi/linux/inotify.h index 5474461683db..4800bf2a531d 100644 --- a/include/uapi/linux/inotify.h +++ b/include/uapi/linux/inotify.h @@ -71,5 +71,13 @@ struct inotify_event { #define IN_CLOEXEC O_CLOEXEC #define IN_NONBLOCK O_NONBLOCK +/* + * ioctl numbers: inotify uses 'I' prefix for all ioctls, + * except historical FIONREAD, which is based on 'T'. + * + * INOTIFY_IOC_SETNEXTWD: set desired number of next created + * watch descriptor. + */ +#define INOTIFY_IOC_SETNEXTWD _IOW('I', 0, __s32) #endif /* _UAPI_LINUX_INOTIFY_H */ -- cgit v1.2.3 From ab15d248cc96d12c928a69d3485a98d223c607ae Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 7 Feb 2018 09:05:46 -0500 Subject: media: include/(uapi/)media: add SPDX license info Replace the old license information with the corresponding SPDX license for those headers that I authored. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/cec-notifier.h | 14 +------------- include/media/cec-pin.h | 14 +------------- include/media/cec.h | 14 +------------- include/media/i2c/ad9389b.h | 14 +------------- include/media/i2c/adv7511.h | 14 +------------- include/media/i2c/adv7604.h | 15 +-------------- include/media/i2c/adv7842.h | 15 +-------------- include/media/i2c/tc358743.h | 18 ++---------------- include/media/i2c/ths7303.h | 10 +--------- include/media/i2c/uda1342.h | 15 +-------------- include/media/tpg/v4l2-tpg.h | 14 +------------- include/media/v4l2-dv-timings.h | 15 +-------------- include/media/v4l2-rect.h | 14 +------------- include/uapi/linux/cec-funcs.h | 29 ----------------------------- include/uapi/linux/cec.h | 29 ----------------------------- 15 files changed, 14 insertions(+), 230 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/media/cec-notifier.h b/include/media/cec-notifier.h index 57ec319a7f44..cf0add70b0e7 100644 --- a/include/media/cec-notifier.h +++ b/include/media/cec-notifier.h @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * cec-notifier.h - notify CEC drivers of physical address changes * * Copyright 2016 Russell King * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef LINUX_CEC_NOTIFIER_H diff --git a/include/media/cec-pin.h b/include/media/cec-pin.h index 83b3e17e0a07..ed16c6dde0ba 100644 --- a/include/media/cec-pin.h +++ b/include/media/cec-pin.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * cec-pin.h - low-level CEC pin control * * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef LINUX_CEC_PIN_H diff --git a/include/media/cec.h b/include/media/cec.h index 7cdf71d7125a..9afba9b558df 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * cec - HDMI Consumer Electronics Control support header * * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _MEDIA_CEC_H diff --git a/include/media/i2c/ad9389b.h b/include/media/i2c/ad9389b.h index 5ba9af869b8b..30f9ea9a1273 100644 --- a/include/media/i2c/ad9389b.h +++ b/include/media/i2c/ad9389b.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Analog Devices AD9389B/AD9889B video encoder driver header * * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef AD9389B_H diff --git a/include/media/i2c/adv7511.h b/include/media/i2c/adv7511.h index 61c3d711cc69..1874c05f486f 100644 --- a/include/media/i2c/adv7511.h +++ b/include/media/i2c/adv7511.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Analog Devices ADV7511 HDMI Transmitter Device Driver * * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef ADV7511_H diff --git a/include/media/i2c/adv7604.h b/include/media/i2c/adv7604.h index 2e6857dee0cc..77a9799128b6 100644 --- a/include/media/i2c/adv7604.h +++ b/include/media/i2c/adv7604.h @@ -1,21 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * adv7604 - Analog Devices ADV7604 video decoder driver * * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _ADV7604_ diff --git a/include/media/i2c/adv7842.h b/include/media/i2c/adv7842.h index 7f53ada9bdf1..05e01f0dd3c2 100644 --- a/include/media/i2c/adv7842.h +++ b/include/media/i2c/adv7842.h @@ -1,21 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * adv7842 - Analog Devices ADV7842 video decoder driver * * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _ADV7842_ diff --git a/include/media/i2c/tc358743.h b/include/media/i2c/tc358743.h index 4513f2f9cfbc..b343650c2948 100644 --- a/include/media/i2c/tc358743.h +++ b/include/media/i2c/tc358743.h @@ -1,22 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * tc358743 - Toshiba HDMI to CSI-2 bridge * - * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights - * reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved. */ /* diff --git a/include/media/i2c/ths7303.h b/include/media/i2c/ths7303.h index 834e2f95b630..95492d12786d 100644 --- a/include/media/i2c/ths7303.h +++ b/include/media/i2c/ths7303.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2013 Texas Instruments Inc * @@ -7,15 +8,6 @@ * Hans Verkuil * Lad, Prabhakar * Martin Bugge - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef THS7353_H diff --git a/include/media/i2c/uda1342.h b/include/media/i2c/uda1342.h index cd156403a368..cb412d4c1088 100644 --- a/include/media/i2c/uda1342.h +++ b/include/media/i2c/uda1342.h @@ -1,21 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * uda1342.h - definition for uda1342 inputs * * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _UDA1342_H_ diff --git a/include/media/tpg/v4l2-tpg.h b/include/media/tpg/v4l2-tpg.h index 823fadede7bf..eb191e85d363 100644 --- a/include/media/tpg/v4l2-tpg.h +++ b/include/media/tpg/v4l2-tpg.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * v4l2-tpg.h - Test Pattern Generator * * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _V4L2_TPG_H_ diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index ebf00e07a515..8778263a8f5d 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -1,21 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * v4l2-dv-timings - Internal header with dv-timings helper functions * * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef __V4L2_DV_TIMINGS_H diff --git a/include/media/v4l2-rect.h b/include/media/v4l2-rect.h index d2125f0cc7cd..595c3ba05f23 100644 --- a/include/media/v4l2-rect.h +++ b/include/media/v4l2-rect.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * v4l2-rect.h - v4l2_rect helper functions * * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _V4L2_RECT_H_ diff --git a/include/uapi/linux/cec-funcs.h b/include/uapi/linux/cec-funcs.h index 28e8a2a86e16..8997d5068c08 100644 --- a/include/uapi/linux/cec-funcs.h +++ b/include/uapi/linux/cec-funcs.h @@ -3,35 +3,6 @@ * cec - HDMI Consumer Electronics Control message functions * * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * Alternatively you can redistribute this file under the terms of the - * BSD license as stated below: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. The names of its contributors may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _CEC_UAPI_FUNCS_H diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index b51fbe1941a7..20fe091b7e96 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -3,35 +3,6 @@ * cec - HDMI Consumer Electronics Control public header * * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * Alternatively you can redistribute this file under the terms of the - * BSD license as stated below: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. The names of its contributors may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _CEC_UAPI_H -- cgit v1.2.3 From 01883eda72bd3f0a6c81447e4f223de14033fd9d Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Thu, 15 Feb 2018 10:49:35 -0800 Subject: rds: support for zcopy completion notification RDS removes a datagram (rds_message) from the retransmit queue when an ACK is received. The ACK indicates that the receiver has queued the RDS datagram, so that the sender can safely forget the datagram. When all references to the rds_message are quiesced, rds_message_purge is called to release resources used by the rds_message If the datagram to be removed had pinned pages set up, add an entry to the rs->rs_znotify_queue so that the notifcation will be sent up via rds_rm_zerocopy_callback() when the rds_message is eventually freed by rds_message_purge. rds_rm_zerocopy_callback() attempts to batch the number of cookies sent with each notification to a max of SO_EE_ORIGIN_MAX_ZCOOKIES. This is achieved by checking the tail skb in the sk_error_queue: if this has room for one more cookie, the cookie from the current notification is added; else a new skb is added to the sk_error_queue. Every invocation of rds_rm_zerocopy_callback() will trigger a ->sk_error_report to notify the application. Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/uapi/linux/errqueue.h | 2 ++ net/rds/af_rds.c | 2 ++ net/rds/message.c | 83 +++++++++++++++++++++++++++++++++++++++---- net/rds/rds.h | 14 ++++++++ net/rds/recv.c | 2 ++ 5 files changed, 96 insertions(+), 7 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/errqueue.h b/include/uapi/linux/errqueue.h index dc64cfaf13da..28812eda4209 100644 --- a/include/uapi/linux/errqueue.h +++ b/include/uapi/linux/errqueue.h @@ -20,11 +20,13 @@ struct sock_extended_err { #define SO_EE_ORIGIN_ICMP6 3 #define SO_EE_ORIGIN_TXSTATUS 4 #define SO_EE_ORIGIN_ZEROCOPY 5 +#define SO_EE_ORIGIN_ZCOOKIE 6 #define SO_EE_ORIGIN_TIMESTAMPING SO_EE_ORIGIN_TXSTATUS #define SO_EE_OFFENDER(ee) ((struct sockaddr*)((ee)+1)) #define SO_EE_CODE_ZEROCOPY_COPIED 1 +#define SO_EE_ORIGIN_MAX_ZCOOKIES 8 /** * struct scm_timestamping - timestamps exposed through cmsg diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index 0a8eefd256b3..a937f18896ae 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -182,6 +182,8 @@ static __poll_t rds_poll(struct file *file, struct socket *sock, mask |= (EPOLLIN | EPOLLRDNORM); if (rs->rs_snd_bytes < rds_sk_sndbuf(rs)) mask |= (EPOLLOUT | EPOLLWRNORM); + if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) + mask |= POLLERR; read_unlock_irqrestore(&rs->rs_recv_lock, flags); /* clear state any time we wake a seen-congested socket */ diff --git a/net/rds/message.c b/net/rds/message.c index ef3daafa3d79..bf1a656b198a 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -33,6 +33,9 @@ #include #include #include +#include +#include +#include #include "rds.h" @@ -53,29 +56,95 @@ void rds_message_addref(struct rds_message *rm) } EXPORT_SYMBOL_GPL(rds_message_addref); +static inline bool skb_zcookie_add(struct sk_buff *skb, u32 cookie) +{ + struct sock_exterr_skb *serr = SKB_EXT_ERR(skb); + int ncookies; + u32 *ptr; + + if (serr->ee.ee_origin != SO_EE_ORIGIN_ZCOOKIE) + return false; + ncookies = serr->ee.ee_data; + if (ncookies == SO_EE_ORIGIN_MAX_ZCOOKIES) + return false; + ptr = skb_put(skb, sizeof(u32)); + *ptr = cookie; + serr->ee.ee_data = ++ncookies; + return true; +} + +static void rds_rm_zerocopy_callback(struct rds_sock *rs, + struct rds_znotifier *znotif) +{ + struct sock *sk = rds_rs_to_sk(rs); + struct sk_buff *skb, *tail; + struct sock_exterr_skb *serr; + unsigned long flags; + struct sk_buff_head *q; + u32 cookie = znotif->z_cookie; + + q = &sk->sk_error_queue; + spin_lock_irqsave(&q->lock, flags); + tail = skb_peek_tail(q); + + if (tail && skb_zcookie_add(tail, cookie)) { + spin_unlock_irqrestore(&q->lock, flags); + mm_unaccount_pinned_pages(&znotif->z_mmp); + consume_skb(rds_skb_from_znotifier(znotif)); + sk->sk_error_report(sk); + return; + } + + skb = rds_skb_from_znotifier(znotif); + serr = SKB_EXT_ERR(skb); + memset(&serr->ee, 0, sizeof(serr->ee)); + serr->ee.ee_errno = 0; + serr->ee.ee_origin = SO_EE_ORIGIN_ZCOOKIE; + serr->ee.ee_info = 0; + WARN_ON(!skb_zcookie_add(skb, cookie)); + + __skb_queue_tail(q, skb); + + spin_unlock_irqrestore(&q->lock, flags); + sk->sk_error_report(sk); + + mm_unaccount_pinned_pages(&znotif->z_mmp); +} + /* * This relies on dma_map_sg() not touching sg[].page during merging. */ static void rds_message_purge(struct rds_message *rm) { unsigned long i, flags; + bool zcopy = false; if (unlikely(test_bit(RDS_MSG_PAGEVEC, &rm->m_flags))) return; - for (i = 0; i < rm->data.op_nents; i++) { - rdsdebug("putting data page %p\n", (void *)sg_page(&rm->data.op_sg[i])); - /* XXX will have to put_page for page refs */ - __free_page(sg_page(&rm->data.op_sg[i])); - } - rm->data.op_nents = 0; spin_lock_irqsave(&rm->m_rs_lock, flags); if (rm->m_rs) { - sock_put(rds_rs_to_sk(rm->m_rs)); + struct rds_sock *rs = rm->m_rs; + + if (rm->data.op_mmp_znotifier) { + zcopy = true; + rds_rm_zerocopy_callback(rs, rm->data.op_mmp_znotifier); + rm->data.op_mmp_znotifier = NULL; + } + sock_put(rds_rs_to_sk(rs)); rm->m_rs = NULL; } spin_unlock_irqrestore(&rm->m_rs_lock, flags); + for (i = 0; i < rm->data.op_nents; i++) { + /* XXX will have to put_page for page refs */ + if (!zcopy) + __free_page(sg_page(&rm->data.op_sg[i])); + else + put_page(sg_page(&rm->data.op_sg[i])); + } + rm->data.op_nents = 0; + if (rm->rdma.op_active) rds_rdma_free_op(&rm->rdma); if (rm->rdma.op_rdma_mr) diff --git a/net/rds/rds.h b/net/rds/rds.h index 7301b9b01890..24576bc4a5e9 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -356,6 +356,19 @@ static inline u32 rds_rdma_cookie_offset(rds_rdma_cookie_t cookie) #define RDS_MSG_PAGEVEC 7 #define RDS_MSG_FLUSH 8 +struct rds_znotifier { + struct list_head z_list; + struct mmpin z_mmp; + u32 z_cookie; +}; + +#define RDS_ZCOPY_SKB(__skb) ((struct rds_znotifier *)&((__skb)->cb[0])) + +static inline struct sk_buff *rds_skb_from_znotifier(struct rds_znotifier *z) +{ + return container_of((void *)z, struct sk_buff, cb); +} + struct rds_message { refcount_t m_refcount; struct list_head m_sock_item; @@ -436,6 +449,7 @@ struct rds_message { unsigned int op_count; unsigned int op_dmasg; unsigned int op_dmaoff; + struct rds_znotifier *op_mmp_znotifier; struct scatterlist *op_sg; } data; }; diff --git a/net/rds/recv.c b/net/rds/recv.c index b25bcfe411ca..b080961464df 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -594,6 +594,8 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, if (msg_flags & MSG_OOB) goto out; + if (msg_flags & MSG_ERRQUEUE) + return sock_recv_errqueue(sk, msg, size, SOL_IP, IP_RECVERR); while (1) { /* If there are pending notifications, do those - and nothing else */ -- cgit v1.2.3 From 0cebaccef3acbdfbc2d85880a2efb765d2f4e2e3 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Thu, 15 Feb 2018 10:49:36 -0800 Subject: rds: zerocopy Tx support. If the MSG_ZEROCOPY flag is specified with rds_sendmsg(), and, if the SO_ZEROCOPY socket option has been set on the PF_RDS socket, application pages sent down with rds_sendmsg() are pinned. The pinning uses the accounting infrastructure added by Commit a91dbff551a6 ("sock: ulimit on MSG_ZEROCOPY pages") The payload bytes in the message may not be modified for the duration that the message has been pinned. A multi-threaded application using this infrastructure may thus need to be notified about send-completion so that it can free/reuse the buffers passed to rds_sendmsg(). Notification of send-completion will identify each message-buffer by a cookie that the application must specify as ancillary data to rds_sendmsg(). The ancillary data in this case has cmsg_level == SOL_RDS and cmsg_type == RDS_CMSG_ZCOPY_COOKIE. Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/uapi/linux/rds.h | 1 + net/rds/message.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++- net/rds/rds.h | 3 ++- net/rds/send.c | 44 +++++++++++++++++++++++++++++++++++------ 4 files changed, 91 insertions(+), 8 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/rds.h b/include/uapi/linux/rds.h index e71d4491f225..12e3bca32cad 100644 --- a/include/uapi/linux/rds.h +++ b/include/uapi/linux/rds.h @@ -103,6 +103,7 @@ #define RDS_CMSG_MASKED_ATOMIC_FADD 8 #define RDS_CMSG_MASKED_ATOMIC_CSWP 9 #define RDS_CMSG_RXPATH_LATENCY 11 +#define RDS_CMSG_ZCOPY_COOKIE 12 #define RDS_INFO_FIRST 10000 #define RDS_INFO_COUNTERS 10000 diff --git a/net/rds/message.c b/net/rds/message.c index bf1a656b198a..651834513481 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -341,12 +341,14 @@ struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned in return rm; } -int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from) +int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from, + bool zcopy) { unsigned long to_copy, nbytes; unsigned long sg_off; struct scatterlist *sg; int ret = 0; + int length = iov_iter_count(from); rm->m_inc.i_hdr.h_len = cpu_to_be32(iov_iter_count(from)); @@ -356,6 +358,53 @@ int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from) sg = rm->data.op_sg; sg_off = 0; /* Dear gcc, sg->page will be null from kzalloc. */ + if (zcopy) { + int total_copied = 0; + struct sk_buff *skb; + + skb = alloc_skb(SO_EE_ORIGIN_MAX_ZCOOKIES * sizeof(u32), + GFP_KERNEL); + if (!skb) + return -ENOMEM; + rm->data.op_mmp_znotifier = RDS_ZCOPY_SKB(skb); + if (mm_account_pinned_pages(&rm->data.op_mmp_znotifier->z_mmp, + length)) { + ret = -ENOMEM; + goto err; + } + while (iov_iter_count(from)) { + struct page *pages; + size_t start; + ssize_t copied; + + copied = iov_iter_get_pages(from, &pages, PAGE_SIZE, + 1, &start); + if (copied < 0) { + struct mmpin *mmp; + int i; + + for (i = 0; i < rm->data.op_nents; i++) + put_page(sg_page(&rm->data.op_sg[i])); + mmp = &rm->data.op_mmp_znotifier->z_mmp; + mm_unaccount_pinned_pages(mmp); + ret = -EFAULT; + goto err; + } + total_copied += copied; + iov_iter_advance(from, copied); + length -= copied; + sg_set_page(sg, pages, copied, start); + rm->data.op_nents++; + sg++; + } + WARN_ON_ONCE(length != 0); + return ret; +err: + consume_skb(skb); + rm->data.op_mmp_znotifier = NULL; + return ret; + } /* zcopy */ + while (iov_iter_count(from)) { if (!sg_page(sg)) { ret = rds_page_remainder_alloc(sg, iov_iter_count(from), diff --git a/net/rds/rds.h b/net/rds/rds.h index 24576bc4a5e9..31cd38852050 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -785,7 +785,8 @@ rds_conn_connecting(struct rds_connection *conn) /* message.c */ struct rds_message *rds_message_alloc(unsigned int nents, gfp_t gfp); struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents); -int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from); +int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from, + bool zcopy); struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned int total_len); void rds_message_populate_header(struct rds_header *hdr, __be16 sport, __be16 dport, u64 seq); diff --git a/net/rds/send.c b/net/rds/send.c index e8f3ff471b15..028ab598ac1b 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -875,12 +875,13 @@ out: * rds_message is getting to be quite complicated, and we'd like to allocate * it all in one go. This figures out how big it needs to be up front. */ -static int rds_rm_size(struct msghdr *msg, int data_len) +static int rds_rm_size(struct msghdr *msg, int num_sgs) { struct cmsghdr *cmsg; int size = 0; int cmsg_groups = 0; int retval; + bool zcopy_cookie = false; for_each_cmsghdr(cmsg, msg) { if (!CMSG_OK(msg, cmsg)) @@ -899,6 +900,8 @@ static int rds_rm_size(struct msghdr *msg, int data_len) break; + case RDS_CMSG_ZCOPY_COOKIE: + zcopy_cookie = true; case RDS_CMSG_RDMA_DEST: case RDS_CMSG_RDMA_MAP: cmsg_groups |= 2; @@ -919,7 +922,10 @@ static int rds_rm_size(struct msghdr *msg, int data_len) } - size += ceil(data_len, PAGE_SIZE) * sizeof(struct scatterlist); + if ((msg->msg_flags & MSG_ZEROCOPY) && !zcopy_cookie) + return -EINVAL; + + size += num_sgs * sizeof(struct scatterlist); /* Ensure (DEST, MAP) are never used with (ARGS, ATOMIC) */ if (cmsg_groups == 3) @@ -928,6 +934,18 @@ static int rds_rm_size(struct msghdr *msg, int data_len) return size; } +static int rds_cmsg_zcopy(struct rds_sock *rs, struct rds_message *rm, + struct cmsghdr *cmsg) +{ + u32 *cookie; + + if (cmsg->cmsg_len < CMSG_LEN(sizeof(*cookie))) + return -EINVAL; + cookie = CMSG_DATA(cmsg); + rm->data.op_mmp_znotifier->z_cookie = *cookie; + return 0; +} + static int rds_cmsg_send(struct rds_sock *rs, struct rds_message *rm, struct msghdr *msg, int *allocated_mr) { @@ -970,6 +988,10 @@ static int rds_cmsg_send(struct rds_sock *rs, struct rds_message *rm, ret = rds_cmsg_atomic(rs, rm, cmsg); break; + case RDS_CMSG_ZCOPY_COOKIE: + ret = rds_cmsg_zcopy(rs, rm, cmsg); + break; + default: return -EINVAL; } @@ -1040,10 +1062,13 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) long timeo = sock_sndtimeo(sk, nonblock); struct rds_conn_path *cpath; size_t total_payload_len = payload_len, rdma_payload_len = 0; + bool zcopy = ((msg->msg_flags & MSG_ZEROCOPY) && + sock_flag(rds_rs_to_sk(rs), SOCK_ZEROCOPY)); + int num_sgs = ceil(payload_len, PAGE_SIZE); /* Mirror Linux UDP mirror of BSD error message compatibility */ /* XXX: Perhaps MSG_MORE someday */ - if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_CMSG_COMPAT)) { + if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_CMSG_COMPAT | MSG_ZEROCOPY)) { ret = -EOPNOTSUPP; goto out; } @@ -1087,8 +1112,15 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) goto out; } + if (zcopy) { + if (rs->rs_transport->t_type != RDS_TRANS_TCP) { + ret = -EOPNOTSUPP; + goto out; + } + num_sgs = iov_iter_npages(&msg->msg_iter, INT_MAX); + } /* size of rm including all sgs */ - ret = rds_rm_size(msg, payload_len); + ret = rds_rm_size(msg, num_sgs); if (ret < 0) goto out; @@ -1100,12 +1132,12 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) /* Attach data to the rm */ if (payload_len) { - rm->data.op_sg = rds_message_alloc_sgs(rm, ceil(payload_len, PAGE_SIZE)); + rm->data.op_sg = rds_message_alloc_sgs(rm, num_sgs); if (!rm->data.op_sg) { ret = -ENOMEM; goto out; } - ret = rds_message_copy_from_user(rm, &msg->msg_iter); + ret = rds_message_copy_from_user(rm, &msg->msg_iter, zcopy); if (ret) goto out; } -- cgit v1.2.3 From 1ec010e705934c8acbe7dbf31afc81e60e3d828b Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Fri, 16 Feb 2018 11:03:07 +0100 Subject: tun: export flags, uid, gid, queue information over netlink Signed-off-by: Sabrina Dubroca Reviewed-by: Stefano Brivio Signed-off-by: David S. Miller --- drivers/net/tun.c | 56 ++++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/if_link.h | 18 ++++++++++++++ 2 files changed, 74 insertions(+) (limited to 'include/uapi/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 8e9a0ac644d2..86b16013e9c5 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2291,11 +2291,67 @@ static int tun_validate(struct nlattr *tb[], struct nlattr *data[], return -EINVAL; } +static size_t tun_get_size(const struct net_device *dev) +{ + BUILD_BUG_ON(sizeof(u32) != sizeof(uid_t)); + BUILD_BUG_ON(sizeof(u32) != sizeof(gid_t)); + + return nla_total_size(sizeof(uid_t)) + /* OWNER */ + nla_total_size(sizeof(gid_t)) + /* GROUP */ + nla_total_size(sizeof(u8)) + /* TYPE */ + nla_total_size(sizeof(u8)) + /* PI */ + nla_total_size(sizeof(u8)) + /* VNET_HDR */ + nla_total_size(sizeof(u8)) + /* PERSIST */ + nla_total_size(sizeof(u8)) + /* MULTI_QUEUE */ + nla_total_size(sizeof(u32)) + /* NUM_QUEUES */ + nla_total_size(sizeof(u32)) + /* NUM_DISABLED_QUEUES */ + 0; +} + +static int tun_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct tun_struct *tun = netdev_priv(dev); + + if (nla_put_u8(skb, IFLA_TUN_TYPE, tun->flags & TUN_TYPE_MASK)) + goto nla_put_failure; + if (uid_valid(tun->owner) && + nla_put_u32(skb, IFLA_TUN_OWNER, + from_kuid_munged(current_user_ns(), tun->owner))) + goto nla_put_failure; + if (gid_valid(tun->group) && + nla_put_u32(skb, IFLA_TUN_GROUP, + from_kgid_munged(current_user_ns(), tun->group))) + goto nla_put_failure; + if (nla_put_u8(skb, IFLA_TUN_PI, !(tun->flags & IFF_NO_PI))) + goto nla_put_failure; + if (nla_put_u8(skb, IFLA_TUN_VNET_HDR, !!(tun->flags & IFF_VNET_HDR))) + goto nla_put_failure; + if (nla_put_u8(skb, IFLA_TUN_PERSIST, !!(tun->flags & IFF_PERSIST))) + goto nla_put_failure; + if (nla_put_u8(skb, IFLA_TUN_MULTI_QUEUE, + !!(tun->flags & IFF_MULTI_QUEUE))) + goto nla_put_failure; + if (tun->flags & IFF_MULTI_QUEUE) { + if (nla_put_u32(skb, IFLA_TUN_NUM_QUEUES, tun->numqueues)) + goto nla_put_failure; + if (nla_put_u32(skb, IFLA_TUN_NUM_DISABLED_QUEUES, + tun->numdisabled)) + goto nla_put_failure; + } + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + static struct rtnl_link_ops tun_link_ops __read_mostly = { .kind = DRV_NAME, .priv_size = sizeof(struct tun_struct), .setup = tun_setup, .validate = tun_validate, + .get_size = tun_get_size, + .fill_info = tun_fill_info, }; static void tun_sock_write_space(struct sock *sk) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 6d9447700e18..11d0c0ea2bfa 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -941,4 +941,22 @@ enum { IFLA_EVENT_BONDING_OPTIONS, /* change in bonding options */ }; +/* tun section */ + +enum { + IFLA_TUN_UNSPEC, + IFLA_TUN_OWNER, + IFLA_TUN_GROUP, + IFLA_TUN_TYPE, + IFLA_TUN_PI, + IFLA_TUN_VNET_HDR, + IFLA_TUN_PERSIST, + IFLA_TUN_MULTI_QUEUE, + IFLA_TUN_NUM_QUEUES, + IFLA_TUN_NUM_DISABLED_QUEUES, + __IFLA_TUN_MAX, +}; + +#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) + #endif /* _UAPI_LINUX_IF_LINK_H */ -- cgit v1.2.3 From c4b50cd31d25c3d17886ffc47ca4a9a12c6dc9bf Mon Sep 17 00:00:00 2001 From: Venkateswara Naralasetty Date: Tue, 13 Feb 2018 11:03:06 +0530 Subject: cfg80211: send ack_signal to user in probe client response This patch provides support to get ack signal in probe client response and in station info from user. Signed-off-by: Venkateswara Naralasetty [squash in compilation fixes] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wil6210/cfg80211.c | 3 ++- include/net/cfg80211.h | 7 ++++++- include/uapi/linux/nl80211.h | 3 +++ net/mac80211/status.c | 2 +- net/wireless/nl80211.c | 8 ++++++-- 5 files changed, 18 insertions(+), 5 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 768f63f38341..b799a5384abb 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1599,7 +1599,8 @@ static void wil_probe_client_handle(struct wil6210_priv *wil, */ bool alive = (sta->status == wil_sta_connected); - cfg80211_probe_status(ndev, sta->addr, req->cookie, alive, GFP_KERNEL); + cfg80211_probe_status(ndev, sta->addr, req->cookie, alive, + 0, false, GFP_KERNEL); } static struct list_head *next_probe_client(struct wil6210_priv *wil) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7d49cd0cf92d..56e905cd4b07 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1147,6 +1147,7 @@ struct cfg80211_tid_stats { * @rx_duration: aggregate PPDU duration(usecs) for all the frames from a peer * @pertid: per-TID statistics, see &struct cfg80211_tid_stats, using the last * (IEEE80211_NUM_TIDS) index for MSDUs not encapsulated in QoS-MPDUs. + * @ack_signal: signal strength (in dBm) of the last ACK frame. */ struct station_info { u64 filled; @@ -1191,6 +1192,7 @@ struct station_info { u64 rx_duration; u8 rx_beacon_signal_avg; struct cfg80211_tid_stats pertid[IEEE80211_NUM_TIDS + 1]; + s8 ack_signal; }; #if IS_ENABLED(CONFIG_CFG80211) @@ -5838,10 +5840,13 @@ bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, * @addr: the address of the peer * @cookie: the cookie filled in @probe_client previously * @acked: indicates whether probe was acked or not + * @ack_signal: signal strength (in dBm) of the ACK frame. + * @is_valid_ack_signal: indicates the ack_signal is valid or not. * @gfp: allocation flags */ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, - u64 cookie, bool acked, gfp_t gfp); + u64 cookie, bool acked, s32 ack_signal, + bool is_valid_ack_signal, gfp_t gfp); /** * cfg80211_report_obss_beacon - report beacon from other APs diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index ca3d5a613fc0..c13c84304be3 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2626,6 +2626,7 @@ enum nl80211_attrs { NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, NL80211_ATTR_NSS, + NL80211_ATTR_ACK_SIGNAL, /* add attributes here, update the policy in nl80211.c */ @@ -2947,6 +2948,7 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_RX_DURATION: aggregate PPDU duration for all frames * received from the station (u64, usec) * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment + * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2985,6 +2987,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_TID_STATS, NL80211_STA_INFO_RX_DURATION, NL80211_STA_INFO_PAD, + NL80211_STA_INFO_ACK_SIGNAL, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/net/mac80211/status.c b/net/mac80211/status.c index da7427a41529..d74d44e65bd7 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -486,7 +486,7 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local, if (ieee80211_is_nullfunc(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control)) cfg80211_probe_status(sdata->dev, hdr->addr1, - cookie, acked, + cookie, acked, 0, false, GFP_ATOMIC); else cfg80211_mgmt_tx_status(&sdata->wdev, cookie, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c6f256b29c73..050ff61b06a3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4486,6 +4486,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, PUT_SINFO_U64(RX_DROP_MISC, rx_dropped_misc); PUT_SINFO_U64(BEACON_RX, rx_beacon); PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8); + PUT_SINFO(ACK_SIGNAL, ack_signal, u8); #undef PUT_SINFO #undef PUT_SINFO_U64 @@ -14984,7 +14985,8 @@ nla_put_failure: EXPORT_SYMBOL(cfg80211_sta_opmode_change_notify); void cfg80211_probe_status(struct net_device *dev, const u8 *addr, - u64 cookie, bool acked, gfp_t gfp) + u64 cookie, bool acked, s32 ack_signal, + bool is_valid_ack_signal, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); @@ -15009,7 +15011,9 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie, NL80211_ATTR_PAD) || - (acked && nla_put_flag(msg, NL80211_ATTR_ACK))) + (acked && nla_put_flag(msg, NL80211_ATTR_ACK)) || + (is_valid_ack_signal && nla_put_s32(msg, NL80211_ATTR_ACK_SIGNAL, + ack_signal))) goto nla_put_failure; genlmsg_end(msg, hdr); -- cgit v1.2.3 From ccc007e4a746bb592d3e72106f00241f81d51410 Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Thu, 15 Feb 2018 19:42:43 +0200 Subject: net: sched: add em_ipt ematch for calling xtables matches The commit a new tc ematch for using netfilter xtable matches. This allows early classification as well as mirroning/redirecting traffic based on logic implemented in netfilter extensions. Current supported use case is classification based on the incoming IPSec state used during decpsulation using the 'policy' iptables extension (xt_policy). The module dynamically fetches the netfilter match module and calls it using a fake xt_action_param structure based on validated userspace provided parameters. As the xt_policy match does not access skb->data, no skb modifications are needed on match. Signed-off-by: Eyal Birger Signed-off-by: David S. Miller --- include/uapi/linux/pkt_cls.h | 3 +- include/uapi/linux/tc_ematch/tc_em_ipt.h | 20 +++ net/sched/Kconfig | 12 ++ net/sched/Makefile | 1 + net/sched/em_ipt.c | 257 +++++++++++++++++++++++++++++++ 5 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 include/uapi/linux/tc_ematch/tc_em_ipt.h create mode 100644 net/sched/em_ipt.c (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 46c506615f4a..7cafb26df555 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -555,7 +555,8 @@ enum { #define TCF_EM_VLAN 6 #define TCF_EM_CANID 7 #define TCF_EM_IPSET 8 -#define TCF_EM_MAX 8 +#define TCF_EM_IPT 9 +#define TCF_EM_MAX 9 enum { TCF_EM_PROG_TC diff --git a/include/uapi/linux/tc_ematch/tc_em_ipt.h b/include/uapi/linux/tc_ematch/tc_em_ipt.h new file mode 100644 index 000000000000..49a65530992c --- /dev/null +++ b/include/uapi/linux/tc_ematch/tc_em_ipt.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_EM_IPT_H +#define __LINUX_TC_EM_IPT_H + +#include +#include + +enum { + TCA_EM_IPT_UNSPEC, + TCA_EM_IPT_HOOK, + TCA_EM_IPT_MATCH_NAME, + TCA_EM_IPT_MATCH_REVISION, + TCA_EM_IPT_NFPROTO, + TCA_EM_IPT_MATCH_DATA, + __TCA_EM_IPT_MAX +}; + +#define TCA_EM_IPT_MAX (__TCA_EM_IPT_MAX - 1) + +#endif diff --git a/net/sched/Kconfig b/net/sched/Kconfig index f24a6ae6819a..a01169fb5325 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -658,6 +658,18 @@ config NET_EMATCH_IPSET To compile this code as a module, choose M here: the module will be called em_ipset. +config NET_EMATCH_IPT + tristate "IPtables Matches" + depends on NET_EMATCH && NETFILTER && NETFILTER_XTABLES + ---help--- + Say Y here to be able to classify packets based on iptables + matches. + Current supported match is "policy" which allows packet classification + based on IPsec policy that was used during decapsulation + + To compile this code as a module, choose M here: the + module will be called em_ipt. + config NET_CLS_ACT bool "Actions" select NET_CLS diff --git a/net/sched/Makefile b/net/sched/Makefile index 5b635447e3f8..8811d3804878 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -75,3 +75,4 @@ obj-$(CONFIG_NET_EMATCH_META) += em_meta.o obj-$(CONFIG_NET_EMATCH_TEXT) += em_text.o obj-$(CONFIG_NET_EMATCH_CANID) += em_canid.o obj-$(CONFIG_NET_EMATCH_IPSET) += em_ipset.o +obj-$(CONFIG_NET_EMATCH_IPT) += em_ipt.o diff --git a/net/sched/em_ipt.c b/net/sched/em_ipt.c new file mode 100644 index 000000000000..a5f34e930eff --- /dev/null +++ b/net/sched/em_ipt.c @@ -0,0 +1,257 @@ +/* + * net/sched/em_ipt.c IPtables matches Ematch + * + * (c) 2018 Eyal Birger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct em_ipt_match { + const struct xt_match *match; + u32 hook; + u8 match_data[0] __aligned(8); +}; + +struct em_ipt_xt_match { + char *match_name; + int (*validate_match_data)(struct nlattr **tb, u8 mrev); +}; + +static const struct nla_policy em_ipt_policy[TCA_EM_IPT_MAX + 1] = { + [TCA_EM_IPT_MATCH_NAME] = { .type = NLA_STRING, + .len = XT_EXTENSION_MAXNAMELEN }, + [TCA_EM_IPT_MATCH_REVISION] = { .type = NLA_U8 }, + [TCA_EM_IPT_HOOK] = { .type = NLA_U32 }, + [TCA_EM_IPT_NFPROTO] = { .type = NLA_U8 }, + [TCA_EM_IPT_MATCH_DATA] = { .type = NLA_UNSPEC }, +}; + +static int check_match(struct net *net, struct em_ipt_match *im, int mdata_len) +{ + struct xt_mtchk_param mtpar = {}; + union { + struct ipt_entry e4; + struct ip6t_entry e6; + } e = {}; + + mtpar.net = net; + mtpar.table = "filter"; + mtpar.hook_mask = 1 << im->hook; + mtpar.family = im->match->family; + mtpar.match = im->match; + mtpar.entryinfo = &e; + mtpar.matchinfo = (void *)im->match_data; + return xt_check_match(&mtpar, mdata_len, 0, 0); +} + +static int policy_validate_match_data(struct nlattr **tb, u8 mrev) +{ + if (mrev != 0) { + pr_err("only policy match revision 0 supported"); + return -EINVAL; + } + + if (nla_get_u32(tb[TCA_EM_IPT_HOOK]) != NF_INET_PRE_ROUTING) { + pr_err("policy can only be matched on NF_INET_PRE_ROUTING"); + return -EINVAL; + } + + return 0; +} + +static const struct em_ipt_xt_match em_ipt_xt_matches[] = { + { + .match_name = "policy", + .validate_match_data = policy_validate_match_data + }, + {} +}; + +static struct xt_match *get_xt_match(struct nlattr **tb) +{ + const struct em_ipt_xt_match *m; + struct nlattr *mname_attr; + u8 nfproto, mrev = 0; + int ret; + + mname_attr = tb[TCA_EM_IPT_MATCH_NAME]; + for (m = em_ipt_xt_matches; m->match_name; m++) { + if (!nla_strcmp(mname_attr, m->match_name)) + break; + } + + if (!m->match_name) { + pr_err("Unsupported xt match"); + return ERR_PTR(-EINVAL); + } + + if (tb[TCA_EM_IPT_MATCH_REVISION]) + mrev = nla_get_u8(tb[TCA_EM_IPT_MATCH_REVISION]); + + ret = m->validate_match_data(tb, mrev); + if (ret < 0) + return ERR_PTR(ret); + + nfproto = nla_get_u8(tb[TCA_EM_IPT_NFPROTO]); + return xt_request_find_match(nfproto, m->match_name, mrev); +} + +static int em_ipt_change(struct net *net, void *data, int data_len, + struct tcf_ematch *em) +{ + struct nlattr *tb[TCA_EM_IPT_MAX + 1]; + struct em_ipt_match *im = NULL; + struct xt_match *match; + int mdata_len, ret; + + ret = nla_parse(tb, TCA_EM_IPT_MAX, data, data_len, em_ipt_policy, + NULL); + if (ret < 0) + return ret; + + if (!tb[TCA_EM_IPT_HOOK] || !tb[TCA_EM_IPT_MATCH_NAME] || + !tb[TCA_EM_IPT_MATCH_DATA] || !tb[TCA_EM_IPT_NFPROTO]) + return -EINVAL; + + match = get_xt_match(tb); + if (IS_ERR(match)) { + pr_err("unable to load match\n"); + return PTR_ERR(match); + } + + mdata_len = XT_ALIGN(nla_len(tb[TCA_EM_IPT_MATCH_DATA])); + im = kzalloc(sizeof(*im) + mdata_len, GFP_KERNEL); + if (!im) { + ret = -ENOMEM; + goto err; + } + + im->match = match; + im->hook = nla_get_u32(tb[TCA_EM_IPT_HOOK]); + nla_memcpy(im->match_data, tb[TCA_EM_IPT_MATCH_DATA], mdata_len); + + ret = check_match(net, im, mdata_len); + if (ret) + goto err; + + em->datalen = sizeof(*im) + mdata_len; + em->data = (unsigned long)im; + return 0; + +err: + kfree(im); + module_put(match->me); + return ret; +} + +static void em_ipt_destroy(struct tcf_ematch *em) +{ + struct em_ipt_match *im = (void *)em->data; + + if (!im) + return; + + if (im->match->destroy) { + struct xt_mtdtor_param par = { + .net = em->net, + .match = im->match, + .matchinfo = im->match_data, + .family = im->match->family + }; + im->match->destroy(&par); + } + module_put(im->match->me); + kfree((void *)im); +} + +static int em_ipt_match(struct sk_buff *skb, struct tcf_ematch *em, + struct tcf_pkt_info *info) +{ + const struct em_ipt_match *im = (const void *)em->data; + struct xt_action_param acpar = {}; + struct net_device *indev = NULL; + struct nf_hook_state state; + int ret; + + rcu_read_lock(); + + if (skb->skb_iif) + indev = dev_get_by_index_rcu(em->net, skb->skb_iif); + + nf_hook_state_init(&state, im->hook, im->match->family, + indev ?: skb->dev, skb->dev, NULL, em->net, NULL); + + acpar.match = im->match; + acpar.matchinfo = im->match_data; + acpar.state = &state; + + ret = im->match->match(skb, &acpar); + + rcu_read_unlock(); + return ret; +} + +static int em_ipt_dump(struct sk_buff *skb, struct tcf_ematch *em) +{ + struct em_ipt_match *im = (void *)em->data; + + if (nla_put_string(skb, TCA_EM_IPT_MATCH_NAME, im->match->name) < 0) + return -EMSGSIZE; + if (nla_put_u32(skb, TCA_EM_IPT_HOOK, im->hook) < 0) + return -EMSGSIZE; + if (nla_put_u8(skb, TCA_EM_IPT_MATCH_REVISION, im->match->revision) < 0) + return -EMSGSIZE; + if (nla_put_u8(skb, TCA_EM_IPT_NFPROTO, im->match->family) < 0) + return -EMSGSIZE; + if (nla_put(skb, TCA_EM_IPT_MATCH_DATA, + im->match->usersize ?: im->match->matchsize, + im->match_data) < 0) + return -EMSGSIZE; + + return 0; +} + +static struct tcf_ematch_ops em_ipt_ops = { + .kind = TCF_EM_IPT, + .change = em_ipt_change, + .destroy = em_ipt_destroy, + .match = em_ipt_match, + .dump = em_ipt_dump, + .owner = THIS_MODULE, + .link = LIST_HEAD_INIT(em_ipt_ops.link) +}; + +static int __init init_em_ipt(void) +{ + return tcf_em_register(&em_ipt_ops); +} + +static void __exit exit_em_ipt(void) +{ + tcf_em_unregister(&em_ipt_ops); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eyal Birger "); +MODULE_DESCRIPTION("TC extended match for IPtables matches"); + +module_init(init_em_ipt); +module_exit(exit_em_ipt); + +MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPT); -- cgit v1.2.3 From cac56209a66ea3b0be67aa2966b2c628b944da1e Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 20 Feb 2018 08:55:58 -0500 Subject: net: Allow a rule to track originating protocol Allow a rule that is being added/deleted/modified or dumped to contain the originating protocol's id. The protocol is handled just like a routes originating protocol is. This is especially useful because there is starting to be a plethora of different user space programs adding rules. Allow the vrf device to specify that the kernel is the originator of the rule created for this device. Signed-off-by: Donald Sharp Signed-off-by: David S. Miller --- drivers/net/vrf.c | 1 + include/net/fib_rules.h | 3 ++- include/uapi/linux/fib_rules.h | 2 +- net/core/fib_rules.c | 7 ++++++- 4 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 239c78c53e58..951a4b42cb29 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -1174,6 +1174,7 @@ static int vrf_fib_rule(const struct net_device *dev, __u8 family, bool add_it) memset(frh, 0, sizeof(*frh)); frh->family = family; frh->action = FR_ACT_TO_TBL; + frh->proto = RTPROT_KERNEL; if (nla_put_u8(skb, FRA_L3MDEV, 1)) goto nla_put_failure; diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 648caf90ec07..b166ef07e6d4 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -26,7 +26,8 @@ struct fib_rule { u32 table; u8 action; u8 l3mdev; - /* 2 bytes hole, try to use */ + u8 proto; + /* 1 byte hole, try to use */ u32 target; __be64 tun_id; struct fib_rule __rcu *ctarget; diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index 2b642bf9b5a0..925539172d5b 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -23,8 +23,8 @@ struct fib_rule_hdr { __u8 tos; __u8 table; + __u8 proto; __u8 res1; /* reserved */ - __u8 res2; /* reserved */ __u8 action; __u32 flags; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index cb071b8e8d17..88298f18cbae 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -51,6 +51,7 @@ int fib_default_rule_add(struct fib_rules_ops *ops, r->pref = pref; r->table = table; r->flags = flags; + r->proto = RTPROT_KERNEL; r->fr_net = ops->fro_net; r->uid_range = fib_kuid_range_unset; @@ -465,6 +466,7 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, } refcount_set(&rule->refcnt, 1); rule->fr_net = net; + rule->proto = frh->proto; rule->pref = tb[FRA_PRIORITY] ? nla_get_u32(tb[FRA_PRIORITY]) : fib_default_rule_pref(ops); @@ -664,6 +666,9 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, } list_for_each_entry(rule, &ops->rules_list, list) { + if (frh->proto && (frh->proto != rule->proto)) + continue; + if (frh->action && (frh->action != rule->action)) continue; @@ -808,9 +813,9 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, if (nla_put_u32(skb, FRA_SUPPRESS_PREFIXLEN, rule->suppress_prefixlen)) goto nla_put_failure; frh->res1 = 0; - frh->res2 = 0; frh->action = rule->action; frh->flags = rule->flags; + frh->proto = rule->proto; if (rule->action == FR_ACT_GOTO && rcu_access_pointer(rule->ctarget) == NULL) -- cgit v1.2.3 From 5271a98cd8baae39bb0415f8002aa1bd331f9aa3 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 15 Feb 2018 12:55:31 -0500 Subject: media: add digital video decoder entity functions Add a new media entity function definition for digital TV decoders: MEDIA_ENT_F_DTV_DECODER Signed-off-by: Tim Harvey Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/mediactl/media-types.rst | 11 +++++++++++ include/uapi/linux/media.h | 5 +++++ 2 files changed, 16 insertions(+) (limited to 'include/uapi/linux') diff --git a/Documentation/media/uapi/mediactl/media-types.rst b/Documentation/media/uapi/mediactl/media-types.rst index 8d64b0c06ebc..195400e24254 100644 --- a/Documentation/media/uapi/mediactl/media-types.rst +++ b/Documentation/media/uapi/mediactl/media-types.rst @@ -321,6 +321,17 @@ Types and flags used to represent the media graph elements MIPI CSI-2, ...), and outputs them on its source pad to an output video bus of another type (eDP, MIPI CSI-2, parallel, ...). + - .. row 31 + + .. _MEDIA-ENT-F-DTV-DECODER: + + - ``MEDIA_ENT_F_DTV_DECODER`` + + - Digital video decoder. The basic function of the video decoder is + to accept digital video from a wide variety of sources + and output it in some digital video standard, with appropriate + timing signals. + .. tabularcolumns:: |p{5.5cm}|p{12.0cm}| .. _media-entity-flag: diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index b9b9446095e9..2f12328e260d 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -109,6 +109,11 @@ struct media_device_info { #define MEDIA_ENT_F_VID_MUX (MEDIA_ENT_F_BASE + 0x5001) #define MEDIA_ENT_F_VID_IF_BRIDGE (MEDIA_ENT_F_BASE + 0x5002) +/* + * Digital video decoder entities + */ +#define MEDIA_ENT_F_DTV_DECODER (MEDIA_ENT_F_BASE + 0x6001) + /* * Connectors */ -- cgit v1.2.3 From 4fe0de5b143762d327bfaf1d7be7c5b58041a18c Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Wed, 31 Jan 2018 19:04:15 -0600 Subject: uapi: Add 802.11 Preauthentication to if_ether This adds 0x88c7 protocol type to if_ether. Signed-off-by: Denis Kenzior Signed-off-by: Johannes Berg --- include/uapi/linux/if_ether.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index f8cb5760ea4f..54585906e50a 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -89,6 +89,7 @@ #define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ #define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ #define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */ +#define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */ #define ETH_P_TIPC 0x88CA /* TIPC */ #define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */ #define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ -- cgit v1.2.3 From 5f171577b4f35b44795a73bde8cf2c49b4073925 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 24 Oct 2017 16:52:32 +0100 Subject: Drop a bunch of metag references Now that arch/metag/ has been removed, drop a bunch of metag references in various codes across the whole tree: - VM_GROWSUP and __VM_ARCH_SPECIFIC_1. - MT_METAG_* ELF note types. - METAG Kconfig dependencies (FRAME_POINTER) and ranges (MAX_STACK_SIZE_MB). - metag cases in tools (checkstack.pl, recordmcount.c, perf). Signed-off-by: James Hogan Acked-by: Steven Rostedt (VMware) Acked-by: Peter Zijlstra (Intel) Reviewed-by: Guenter Roeck Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Namhyung Kim Cc: linux-mm@kvack.org Cc: linux-metag@vger.kernel.org --- include/linux/cpuhotplug.h | 1 - include/linux/mm.h | 2 -- include/trace/events/mmflags.h | 2 +- include/uapi/linux/elf.h | 3 --- lib/Kconfig.debug | 2 +- mm/Kconfig | 7 +++---- scripts/checkstack.pl | 4 ---- scripts/recordmcount.c | 20 -------------------- tools/perf/perf-sys.h | 4 ---- 9 files changed, 5 insertions(+), 40 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 5172ad0daa7c..c7a950681f3a 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -108,7 +108,6 @@ enum cpuhp_state { CPUHP_AP_PERF_X86_CQM_STARTING, CPUHP_AP_PERF_X86_CSTATE_STARTING, CPUHP_AP_PERF_XTENSA_STARTING, - CPUHP_AP_PERF_METAG_STARTING, CPUHP_AP_MIPS_OP_LOONGSON3_STARTING, CPUHP_AP_ARM_SDEI_STARTING, CPUHP_AP_ARM_VFP_STARTING, diff --git a/include/linux/mm.h b/include/linux/mm.h index ad06d42adb1a..ccac10682ce5 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -241,8 +241,6 @@ extern unsigned int kobjsize(const void *objp); # define VM_SAO VM_ARCH_1 /* Strong Access Ordering (powerpc) */ #elif defined(CONFIG_PARISC) # define VM_GROWSUP VM_ARCH_1 -#elif defined(CONFIG_METAG) -# define VM_GROWSUP VM_ARCH_1 #elif defined(CONFIG_IA64) # define VM_GROWSUP VM_ARCH_1 #elif !defined(CONFIG_MMU) diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h index dbe1bb058c09..a81cffb76d89 100644 --- a/include/trace/events/mmflags.h +++ b/include/trace/events/mmflags.h @@ -115,7 +115,7 @@ IF_HAVE_PG_IDLE(PG_idle, "idle" ) #define __VM_ARCH_SPECIFIC_1 {VM_PAT, "pat" } #elif defined(CONFIG_PPC) #define __VM_ARCH_SPECIFIC_1 {VM_SAO, "sao" } -#elif defined(CONFIG_PARISC) || defined(CONFIG_METAG) || defined(CONFIG_IA64) +#elif defined(CONFIG_PARISC) || defined(CONFIG_IA64) #define __VM_ARCH_SPECIFIC_1 {VM_GROWSUP, "growsup" } #elif !defined(CONFIG_MMU) #define __VM_ARCH_SPECIFIC_1 {VM_MAPPED_COPY,"mappedcopy" } diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index 3bf73fb58045..e2535d6dcec7 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -420,9 +420,6 @@ typedef struct elf64_shdr { #define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ #define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ #define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension registers */ -#define NT_METAG_CBUF 0x500 /* Metag catch buffer registers */ -#define NT_METAG_RPIPE 0x501 /* Metag read pipeline state */ -#define NT_METAG_TLS 0x502 /* Metag TLS pointer */ #define NT_ARC_V2 0x600 /* ARCv2 accumulator/extra registers */ /* Note header in a PT_NOTE section */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 6088408ef26c..d1c523e408e9 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -356,7 +356,7 @@ config FRAME_POINTER bool "Compile the kernel with frame pointers" depends on DEBUG_KERNEL && \ (CRIS || M68K || FRV || UML || \ - SUPERH || BLACKFIN || MN10300 || METAG) || \ + SUPERH || BLACKFIN || MN10300) || \ ARCH_WANT_FRAME_POINTERS default y if (DEBUG_INFO && UML) || ARCH_WANT_FRAME_POINTERS help diff --git a/mm/Kconfig b/mm/Kconfig index c782e8fb7235..abefa573bcd8 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -627,15 +627,14 @@ config GENERIC_EARLY_IOREMAP config MAX_STACK_SIZE_MB int "Maximum user stack size for 32-bit processes (MB)" default 80 - range 8 256 if METAG range 8 2048 depends on STACK_GROWSUP && (!64BIT || COMPAT) help This is the maximum stack size in Megabytes in the VM layout of 32-bit user processes when the stack grows upwards (currently only on parisc - and metag arch). The stack will be located at the highest memory - address minus the given value, unless the RLIMIT_STACK hard limit is - changed to a smaller value in which case that is used. + arch). The stack will be located at the highest memory address minus + the given value, unless the RLIMIT_STACK hard limit is changed to a + smaller value in which case that is used. A sane initial value is 80 MB. diff --git a/scripts/checkstack.pl b/scripts/checkstack.pl index cb993801e4b2..eeb9ac8dbcfb 100755 --- a/scripts/checkstack.pl +++ b/scripts/checkstack.pl @@ -64,10 +64,6 @@ my (@stack, $re, $dre, $x, $xs, $funcre); # 2b6c: 4e56 fb70 linkw %fp,#-1168 # 1df770: defc ffe4 addaw #-28,%sp $re = qr/.*(?:linkw %fp,|addaw )#-([0-9]{1,4})(?:,%sp)?$/o; - } elsif ($arch eq 'metag') { - #400026fc: 40 00 00 82 ADD A0StP,A0StP,#0x8 - $re = qr/.*ADD.*A0StP,A0StP,\#(0x$x{1,8})/o; - $funcre = qr/^$x* <[^\$](.*)>:$/; } elsif ($arch eq 'mips64') { #8800402c: 67bdfff0 daddiu sp,sp,-16 $re = qr/.*daddiu.*sp,sp,-(([0-9]{2}|[3-9])[0-9]{2})/o; diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 16e086dcc567..8c9691c3329e 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -33,20 +33,6 @@ #include #include -/* - * glibc synced up and added the metag number but didn't add the relocations. - * Work around this in a crude manner for now. - */ -#ifndef EM_METAG -#define EM_METAG 174 -#endif -#ifndef R_METAG_ADDR32 -#define R_METAG_ADDR32 2 -#endif -#ifndef R_METAG_NONE -#define R_METAG_NONE 3 -#endif - #ifndef EM_AARCH64 #define EM_AARCH64 183 #define R_AARCH64_NONE 0 @@ -538,12 +524,6 @@ do_file(char const *const fname) gpfx = '_'; break; case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; - case EM_METAG: reltype = R_METAG_ADDR32; - altmcount = "_mcount_wrapper"; - rel_type_nop = R_METAG_NONE; - /* We happen to have the same requirement as MIPS */ - is_fake_mcount32 = MIPS32_is_fake_mcount; - break; case EM_MIPS: /* reltype: e_class */ gpfx = '_'; break; case EM_PPC: reltype = R_PPC_ADDR32; gpfx = '_'; break; case EM_PPC64: reltype = R_PPC64_ADDR64; gpfx = '_'; break; diff --git a/tools/perf/perf-sys.h b/tools/perf/perf-sys.h index 36673f98d66b..3eb7a39169f6 100644 --- a/tools/perf/perf-sys.h +++ b/tools/perf/perf-sys.h @@ -46,10 +46,6 @@ #define CPUINFO_PROC {"Processor"} #endif -#ifdef __metag__ -#define CPUINFO_PROC {"CPU"} -#endif - #ifdef __xtensa__ #define CPUINFO_PROC {"core ID"} #endif -- cgit v1.2.3 From 1b71af6053af1bd2f849e9fda4f71c1e3f145dcf Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 23 Feb 2018 14:01:52 -0500 Subject: net: fib_rules: Add new attribute to set protocol For ages iproute2 has used `struct rtmsg` as the ancillary header for FIB rules and in the process set the protocol value to RTPROT_BOOT. Until ca56209a66 ("net: Allow a rule to track originating protocol") the kernel rules code ignored the protocol value sent from userspace and always returned 0 in notifications. To avoid incompatibility with existing iproute2, send the protocol as a new attribute. Fixes: cac56209a66 ("net: Allow a rule to track originating protocol") Signed-off-by: Donald Sharp Signed-off-by: David S. Miller --- drivers/net/vrf.c | 5 ++++- include/net/fib_rules.h | 3 ++- include/uapi/linux/fib_rules.h | 5 +++-- net/core/fib_rules.c | 15 +++++++++++---- 4 files changed, 20 insertions(+), 8 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 951a4b42cb29..9ce0182223a0 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -1145,6 +1145,7 @@ static inline size_t vrf_fib_rule_nl_size(void) sz = NLMSG_ALIGN(sizeof(struct fib_rule_hdr)); sz += nla_total_size(sizeof(u8)); /* FRA_L3MDEV */ sz += nla_total_size(sizeof(u32)); /* FRA_PRIORITY */ + sz += nla_total_size(sizeof(u8)); /* FRA_PROTOCOL */ return sz; } @@ -1174,7 +1175,9 @@ static int vrf_fib_rule(const struct net_device *dev, __u8 family, bool add_it) memset(frh, 0, sizeof(*frh)); frh->family = family; frh->action = FR_ACT_TO_TBL; - frh->proto = RTPROT_KERNEL; + + if (nla_put_u8(skb, FRA_PROTOCOL, RTPROT_KERNEL)) + goto nla_put_failure; if (nla_put_u8(skb, FRA_L3MDEV, 1)) goto nla_put_failure; diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index b166ef07e6d4..b3d216249240 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -109,7 +109,8 @@ struct fib_rule_notifier_info { [FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \ [FRA_GOTO] = { .type = NLA_U32 }, \ [FRA_L3MDEV] = { .type = NLA_U8 }, \ - [FRA_UID_RANGE] = { .len = sizeof(struct fib_rule_uid_range) } + [FRA_UID_RANGE] = { .len = sizeof(struct fib_rule_uid_range) }, \ + [FRA_PROTOCOL] = { .type = NLA_U8 } static inline void fib_rule_get(struct fib_rule *rule) { diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index 925539172d5b..77d90ae38114 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -23,8 +23,8 @@ struct fib_rule_hdr { __u8 tos; __u8 table; - __u8 proto; - __u8 res1; /* reserved */ + __u8 res1; /* reserved */ + __u8 res2; /* reserved */ __u8 action; __u32 flags; @@ -58,6 +58,7 @@ enum { FRA_PAD, FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ FRA_UID_RANGE, /* UID range */ + FRA_PROTOCOL, /* Originator of the rule */ __FRA_MAX }; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 88298f18cbae..a6aea805a0a2 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -466,11 +466,13 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, } refcount_set(&rule->refcnt, 1); rule->fr_net = net; - rule->proto = frh->proto; rule->pref = tb[FRA_PRIORITY] ? nla_get_u32(tb[FRA_PRIORITY]) : fib_default_rule_pref(ops); + rule->proto = tb[FRA_PROTOCOL] ? + nla_get_u8(tb[FRA_PROTOCOL]) : RTPROT_UNSPEC; + if (tb[FRA_IIFNAME]) { struct net_device *dev; @@ -666,7 +668,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, } list_for_each_entry(rule, &ops->rules_list, list) { - if (frh->proto && (frh->proto != rule->proto)) + if (tb[FRA_PROTOCOL] && + (rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) continue; if (frh->action && (frh->action != rule->action)) @@ -786,7 +789,8 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(4) /* FRA_FWMARK */ + nla_total_size(4) /* FRA_FWMASK */ + nla_total_size_64bit(8) /* FRA_TUN_ID */ - + nla_total_size(sizeof(struct fib_kuid_range)); + + nla_total_size(sizeof(struct fib_kuid_range)) + + nla_total_size(1); /* FRA_PROTOCOL */ if (ops->nlmsg_payload) payload += ops->nlmsg_payload(rule); @@ -813,9 +817,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, if (nla_put_u32(skb, FRA_SUPPRESS_PREFIXLEN, rule->suppress_prefixlen)) goto nla_put_failure; frh->res1 = 0; + frh->res2 = 0; frh->action = rule->action; frh->flags = rule->flags; - frh->proto = rule->proto; + + if (nla_put_u8(skb, FRA_PROTOCOL, rule->proto)) + goto nla_put_failure; if (rule->action == FR_ACT_GOTO && rcu_access_pointer(rule->ctarget) == NULL) -- cgit v1.2.3 From 0018147c964e73cb6ee0d463cad534fb4309e286 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Mon, 8 Jan 2018 12:55:23 -0500 Subject: media: v4l: doc: Clarify v4l2_mbus_fmt height definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v4l2_mbus_fmt width and height corresponds directly with the v4l2_pix_format definitions, yet the differences in documentation make it ambiguous what to do in the event of field heights. Clarify this using the same text as is provided for the v4l2_pix_format which is explicit on the matter, and by matching the terminology of 'image height' rather than the misleading 'frame height'. Signed-off-by: Kieran Bingham Acked-by: Sakari Ailus Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/subdev-formats.rst | 8 ++++++-- include/uapi/linux/v4l2-mediabus.h | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/media/uapi/v4l/subdev-formats.rst b/Documentation/media/uapi/v4l/subdev-formats.rst index b1eea44550e1..9fcabe7f9367 100644 --- a/Documentation/media/uapi/v4l/subdev-formats.rst +++ b/Documentation/media/uapi/v4l/subdev-formats.rst @@ -16,10 +16,14 @@ Media Bus Formats * - __u32 - ``width`` - - Image width, in pixels. + - Image width in pixels. * - __u32 - ``height`` - - Image height, in pixels. + - Image height in pixels. If ``field`` is one of ``V4L2_FIELD_TOP``, + ``V4L2_FIELD_BOTTOM`` or ``V4L2_FIELD_ALTERNATE`` then height + refers to the number of lines in the field, otherwise it refers to + the number of lines in the frame (which is twice the field height + for interlaced formats). * - __u32 - ``code`` - Format code, from enum diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 6e20de63ec59..123a231001a8 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -18,8 +18,8 @@ /** * struct v4l2_mbus_framefmt - frame format on the media bus - * @width: frame width - * @height: frame height + * @width: image width + * @height: image height * @code: data format code (from enum v4l2_mbus_pixelcode) * @field: used interlacing type (from enum v4l2_field) * @colorspace: colorspace of the data (from enum v4l2_colorspace) -- cgit v1.2.3 From 81e0989e2ff411b3bb40098730830f16f707bd42 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 5 Feb 2018 04:30:48 -0500 Subject: media: media.h: fix confusing typo in comment Subdevs are initialized with MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN, not MEDIA_ENT_T_V4L2_SUBDEV_UNKNOWN. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/media.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 2f12328e260d..3e8a28a5429f 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -136,7 +136,7 @@ struct media_device_info { * with the legacy v1 API.The number range is out of range by purpose: * several previously reserved numbers got excluded from this range. * - * Subdevs are initialized with MEDIA_ENT_T_V4L2_SUBDEV_UNKNOWN, + * Subdevs are initialized with MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN, * in order to preserve backward compatibility. * Drivers must change to the proper subdev type before * registering the entity. -- cgit v1.2.3 From ed3056f015de90a53127d05c34d49fe3e04675a4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 5 Feb 2018 06:19:00 -0500 Subject: media: media.h: reorganize header to make it easier to understand The media.h public header is very messy. It mixes legacy and 'new' defines and it is not easy to figure out what should and what shouldn't be used. It also contains confusing comment that are either out of date or completely uninteresting for anyone that needs to use this header. The patch groups all entity functions together, including the 'old' defines based on the old range base. The reader just wants to know about the available functions and doesn't care about what range is used. All legacy defines are moved to the end of the header, so it is easier to locate them and just ignore them. The legacy structs in the struct media_entity_desc are put under also a much more effective signal to the reader that they shouldn't be used compared to the old method of relying on '#if 1' followed by a comment. The unused MEDIA_INTF_T_ALSA_* defines are also moved to the end of the header in the legacy area. They are also dropped from intf_type() in media-entity.c. All defines are also aligned at the same tab making the header easier to read. Signed-off-by: Hans Verkuil [mchehab@s-opensource.com: removed lots of spaces before tabs; typo changes ->change ] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-entity.c | 16 -- include/uapi/linux/media.h | 347 ++++++++++++++++++++----------------------- 2 files changed, 164 insertions(+), 199 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index f7c6d64e6031..3498551e618e 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -64,22 +64,6 @@ static inline const char *intf_type(struct media_interface *intf) return "v4l-swradio"; case MEDIA_INTF_T_V4L_TOUCH: return "v4l-touch"; - case MEDIA_INTF_T_ALSA_PCM_CAPTURE: - return "alsa-pcm-capture"; - case MEDIA_INTF_T_ALSA_PCM_PLAYBACK: - return "alsa-pcm-playback"; - case MEDIA_INTF_T_ALSA_CONTROL: - return "alsa-control"; - case MEDIA_INTF_T_ALSA_COMPRESS: - return "alsa-compress"; - case MEDIA_INTF_T_ALSA_RAWMIDI: - return "alsa-rawmidi"; - case MEDIA_INTF_T_ALSA_HWDEP: - return "alsa-hwdep"; - case MEDIA_INTF_T_ALSA_SEQUENCER: - return "alsa-sequencer"; - case MEDIA_INTF_T_ALSA_TIMER: - return "alsa-timer"; default: return "unknown-intf"; } diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 3e8a28a5429f..c7e9a5cba24e 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -15,10 +15,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_MEDIA_H @@ -42,113 +38,66 @@ struct media_device_info { __u32 reserved[31]; }; -#define MEDIA_ENT_ID_FLAG_NEXT (1 << 31) - -/* - * Initial value to be used when a new entity is created - * Drivers should change it to something useful - */ -#define MEDIA_ENT_F_UNKNOWN 0x00000000 - /* * Base number ranges for entity functions * - * NOTE: those ranges and entity function number are phased just to - * make it easier to maintain this file. Userspace should not rely on - * the ranges to identify a group of function types, as newer - * functions can be added with any name within the full u32 range. + * NOTE: Userspace should not rely on these ranges to identify a group + * of function types, as newer functions can be added with any name within + * the full u32 range. + * + * Some older functions use the MEDIA_ENT_F_OLD_*_BASE range. Do not + * change this, this is for backwards compatibility. When adding new + * functions always use MEDIA_ENT_F_BASE. */ -#define MEDIA_ENT_F_BASE 0x00000000 -#define MEDIA_ENT_F_OLD_BASE 0x00010000 -#define MEDIA_ENT_F_OLD_SUBDEV_BASE 0x00020000 +#define MEDIA_ENT_F_BASE 0x00000000 +#define MEDIA_ENT_F_OLD_BASE 0x00010000 +#define MEDIA_ENT_F_OLD_SUBDEV_BASE 0x00020000 /* - * DVB entities + * Initial value to be used when a new entity is created + * Drivers should change it to something useful. */ -#define MEDIA_ENT_F_DTV_DEMOD (MEDIA_ENT_F_BASE + 0x00001) -#define MEDIA_ENT_F_TS_DEMUX (MEDIA_ENT_F_BASE + 0x00002) -#define MEDIA_ENT_F_DTV_CA (MEDIA_ENT_F_BASE + 0x00003) -#define MEDIA_ENT_F_DTV_NET_DECAP (MEDIA_ENT_F_BASE + 0x00004) +#define MEDIA_ENT_F_UNKNOWN MEDIA_ENT_F_BASE /* - * I/O entities + * Subdevs are initialized with MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN in order + * to preserve backward compatibility. Drivers must change to the proper + * subdev type before registering the entity. */ -#define MEDIA_ENT_F_IO_DTV (MEDIA_ENT_F_BASE + 0x01001) -#define MEDIA_ENT_F_IO_VBI (MEDIA_ENT_F_BASE + 0x01002) -#define MEDIA_ENT_F_IO_SWRADIO (MEDIA_ENT_F_BASE + 0x01003) +#define MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN MEDIA_ENT_F_OLD_SUBDEV_BASE /* - * Analog TV IF-PLL decoders - * - * It is a responsibility of the master/bridge drivers to create links - * for MEDIA_ENT_F_IF_VID_DECODER and MEDIA_ENT_F_IF_AUD_DECODER. + * DVB entity functions */ -#define MEDIA_ENT_F_IF_VID_DECODER (MEDIA_ENT_F_BASE + 0x02001) -#define MEDIA_ENT_F_IF_AUD_DECODER (MEDIA_ENT_F_BASE + 0x02002) +#define MEDIA_ENT_F_DTV_DEMOD (MEDIA_ENT_F_BASE + 0x00001) +#define MEDIA_ENT_F_TS_DEMUX (MEDIA_ENT_F_BASE + 0x00002) +#define MEDIA_ENT_F_DTV_CA (MEDIA_ENT_F_BASE + 0x00003) +#define MEDIA_ENT_F_DTV_NET_DECAP (MEDIA_ENT_F_BASE + 0x00004) /* - * Audio Entity Functions + * I/O entity functions */ -#define MEDIA_ENT_F_AUDIO_CAPTURE (MEDIA_ENT_F_BASE + 0x03001) -#define MEDIA_ENT_F_AUDIO_PLAYBACK (MEDIA_ENT_F_BASE + 0x03002) -#define MEDIA_ENT_F_AUDIO_MIXER (MEDIA_ENT_F_BASE + 0x03003) +#define MEDIA_ENT_F_IO_V4L (MEDIA_ENT_F_OLD_BASE + 1) +#define MEDIA_ENT_F_IO_DTV (MEDIA_ENT_F_BASE + 0x01001) +#define MEDIA_ENT_F_IO_VBI (MEDIA_ENT_F_BASE + 0x01002) +#define MEDIA_ENT_F_IO_SWRADIO (MEDIA_ENT_F_BASE + 0x01003) /* - * Processing entities + * Sensor functions */ -#define MEDIA_ENT_F_PROC_VIDEO_COMPOSER (MEDIA_ENT_F_BASE + 0x4001) -#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER (MEDIA_ENT_F_BASE + 0x4002) -#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV (MEDIA_ENT_F_BASE + 0x4003) -#define MEDIA_ENT_F_PROC_VIDEO_LUT (MEDIA_ENT_F_BASE + 0x4004) -#define MEDIA_ENT_F_PROC_VIDEO_SCALER (MEDIA_ENT_F_BASE + 0x4005) -#define MEDIA_ENT_F_PROC_VIDEO_STATISTICS (MEDIA_ENT_F_BASE + 0x4006) +#define MEDIA_ENT_F_CAM_SENSOR (MEDIA_ENT_F_OLD_SUBDEV_BASE + 1) +#define MEDIA_ENT_F_FLASH (MEDIA_ENT_F_OLD_SUBDEV_BASE + 2) +#define MEDIA_ENT_F_LENS (MEDIA_ENT_F_OLD_SUBDEV_BASE + 3) /* - * Switch and bridge entitites - */ -#define MEDIA_ENT_F_VID_MUX (MEDIA_ENT_F_BASE + 0x5001) -#define MEDIA_ENT_F_VID_IF_BRIDGE (MEDIA_ENT_F_BASE + 0x5002) - -/* - * Digital video decoder entities + * Video decoder functions */ +#define MEDIA_ENT_F_ATV_DECODER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 4) #define MEDIA_ENT_F_DTV_DECODER (MEDIA_ENT_F_BASE + 0x6001) /* - * Connectors - */ -/* It is a responsibility of the entity drivers to add connectors and links */ -#ifdef __KERNEL__ - /* - * For now, it should not be used in userspace, as some - * definitions may change - */ - -#define MEDIA_ENT_F_CONN_RF (MEDIA_ENT_F_BASE + 0x30001) -#define MEDIA_ENT_F_CONN_SVIDEO (MEDIA_ENT_F_BASE + 0x30002) -#define MEDIA_ENT_F_CONN_COMPOSITE (MEDIA_ENT_F_BASE + 0x30003) - -#endif - -/* - * Don't touch on those. The ranges MEDIA_ENT_F_OLD_BASE and - * MEDIA_ENT_F_OLD_SUBDEV_BASE are kept to keep backward compatibility - * with the legacy v1 API.The number range is out of range by purpose: - * several previously reserved numbers got excluded from this range. + * Digital TV, analog TV, radio and/or software defined radio tuner functions. * - * Subdevs are initialized with MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN, - * in order to preserve backward compatibility. - * Drivers must change to the proper subdev type before - * registering the entity. - */ - -#define MEDIA_ENT_F_IO_V4L (MEDIA_ENT_F_OLD_BASE + 1) - -#define MEDIA_ENT_F_CAM_SENSOR (MEDIA_ENT_F_OLD_SUBDEV_BASE + 1) -#define MEDIA_ENT_F_FLASH (MEDIA_ENT_F_OLD_SUBDEV_BASE + 2) -#define MEDIA_ENT_F_LENS (MEDIA_ENT_F_OLD_SUBDEV_BASE + 3) -#define MEDIA_ENT_F_ATV_DECODER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 4) -/* * It is a responsibility of the master/bridge drivers to add connectors * and links for MEDIA_ENT_F_TUNER. Please notice that some old tuners * may require the usage of separate I2C chips to decode analog TV signals, @@ -156,49 +105,46 @@ struct media_device_info { * On such cases, the IF-PLL staging is mapped via one or two entities: * MEDIA_ENT_F_IF_VID_DECODER and/or MEDIA_ENT_F_IF_AUD_DECODER. */ -#define MEDIA_ENT_F_TUNER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 5) +#define MEDIA_ENT_F_TUNER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 5) -#define MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN MEDIA_ENT_F_OLD_SUBDEV_BASE +/* + * Analog TV IF-PLL decoder functions + * + * It is a responsibility of the master/bridge drivers to create links + * for MEDIA_ENT_F_IF_VID_DECODER and MEDIA_ENT_F_IF_AUD_DECODER. + */ +#define MEDIA_ENT_F_IF_VID_DECODER (MEDIA_ENT_F_BASE + 0x02001) +#define MEDIA_ENT_F_IF_AUD_DECODER (MEDIA_ENT_F_BASE + 0x02002) -#if !defined(__KERNEL__) || defined(__NEED_MEDIA_LEGACY_API) +/* + * Audio entity functions + */ +#define MEDIA_ENT_F_AUDIO_CAPTURE (MEDIA_ENT_F_BASE + 0x03001) +#define MEDIA_ENT_F_AUDIO_PLAYBACK (MEDIA_ENT_F_BASE + 0x03002) +#define MEDIA_ENT_F_AUDIO_MIXER (MEDIA_ENT_F_BASE + 0x03003) /* - * Legacy symbols used to avoid userspace compilation breakages - * - * Those symbols map the entity function into types and should be - * used only on legacy programs for legacy hardware. Don't rely - * on those for MEDIA_IOC_G_TOPOLOGY. + * Processing entity functions */ -#define MEDIA_ENT_TYPE_SHIFT 16 -#define MEDIA_ENT_TYPE_MASK 0x00ff0000 -#define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff - -/* End of the old subdev reserved numberspace */ -#define MEDIA_ENT_T_DEVNODE_UNKNOWN (MEDIA_ENT_T_DEVNODE | \ - MEDIA_ENT_SUBTYPE_MASK) - -#define MEDIA_ENT_T_DEVNODE MEDIA_ENT_F_OLD_BASE -#define MEDIA_ENT_T_DEVNODE_V4L MEDIA_ENT_F_IO_V4L -#define MEDIA_ENT_T_DEVNODE_FB (MEDIA_ENT_T_DEVNODE + 2) -#define MEDIA_ENT_T_DEVNODE_ALSA (MEDIA_ENT_T_DEVNODE + 3) -#define MEDIA_ENT_T_DEVNODE_DVB (MEDIA_ENT_T_DEVNODE + 4) - -#define MEDIA_ENT_T_UNKNOWN MEDIA_ENT_F_UNKNOWN -#define MEDIA_ENT_T_V4L2_VIDEO MEDIA_ENT_F_IO_V4L -#define MEDIA_ENT_T_V4L2_SUBDEV MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN -#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR MEDIA_ENT_F_CAM_SENSOR -#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH MEDIA_ENT_F_FLASH -#define MEDIA_ENT_T_V4L2_SUBDEV_LENS MEDIA_ENT_F_LENS -#define MEDIA_ENT_T_V4L2_SUBDEV_DECODER MEDIA_ENT_F_ATV_DECODER -#define MEDIA_ENT_T_V4L2_SUBDEV_TUNER MEDIA_ENT_F_TUNER +#define MEDIA_ENT_F_PROC_VIDEO_COMPOSER (MEDIA_ENT_F_BASE + 0x4001) +#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER (MEDIA_ENT_F_BASE + 0x4002) +#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV (MEDIA_ENT_F_BASE + 0x4003) +#define MEDIA_ENT_F_PROC_VIDEO_LUT (MEDIA_ENT_F_BASE + 0x4004) +#define MEDIA_ENT_F_PROC_VIDEO_SCALER (MEDIA_ENT_F_BASE + 0x4005) +#define MEDIA_ENT_F_PROC_VIDEO_STATISTICS (MEDIA_ENT_F_BASE + 0x4006) -/* Obsolete symbol for media_version, no longer used in the kernel */ -#define MEDIA_API_VERSION KERNEL_VERSION(0, 1, 0) -#endif +/* + * Switch and bridge entity functions + */ +#define MEDIA_ENT_F_VID_MUX (MEDIA_ENT_F_BASE + 0x5001) +#define MEDIA_ENT_F_VID_IF_BRIDGE (MEDIA_ENT_F_BASE + 0x5002) /* Entity flags */ -#define MEDIA_ENT_FL_DEFAULT (1 << 0) -#define MEDIA_ENT_FL_CONNECTOR (1 << 1) +#define MEDIA_ENT_FL_DEFAULT (1 << 0) +#define MEDIA_ENT_FL_CONNECTOR (1 << 1) + +/* OR with the entity id value to find the next entity */ +#define MEDIA_ENT_ID_FLAG_NEXT (1 << 31) struct media_entity_desc { __u32 id; @@ -219,7 +165,7 @@ struct media_entity_desc { __u32 minor; } dev; -#if 1 +#if !defined(__KERNEL__) /* * TODO: this shouldn't have been added without * actual drivers that use this. When the first real driver @@ -230,24 +176,17 @@ struct media_entity_desc { * contain the subdevice information. In addition, struct dev * can only refer to a single device, and not to multiple (e.g. * pcm and mixer devices). - * - * So for now mark this as a to do. */ struct { __u32 card; __u32 device; __u32 subdevice; } alsa; -#endif -#if 1 /* * DEPRECATED: previous node specifications. Kept just to - * avoid breaking compilation, but media_entity_desc.dev - * should be used instead. In particular, alsa and dvb - * fields below are wrong: for all devnodes, there should - * be just major/minor inside the struct, as this is enough - * to represent any devnode, no matter what type. + * avoid breaking compilation. Use media_entity_desc.dev + * instead. */ struct { __u32 major; @@ -266,9 +205,9 @@ struct media_entity_desc { }; }; -#define MEDIA_PAD_FL_SINK (1 << 0) -#define MEDIA_PAD_FL_SOURCE (1 << 1) -#define MEDIA_PAD_FL_MUST_CONNECT (1 << 2) +#define MEDIA_PAD_FL_SINK (1 << 0) +#define MEDIA_PAD_FL_SOURCE (1 << 1) +#define MEDIA_PAD_FL_MUST_CONNECT (1 << 2) struct media_pad_desc { __u32 entity; /* entity ID */ @@ -277,13 +216,13 @@ struct media_pad_desc { __u32 reserved[2]; }; -#define MEDIA_LNK_FL_ENABLED (1 << 0) -#define MEDIA_LNK_FL_IMMUTABLE (1 << 1) -#define MEDIA_LNK_FL_DYNAMIC (1 << 2) +#define MEDIA_LNK_FL_ENABLED (1 << 0) +#define MEDIA_LNK_FL_IMMUTABLE (1 << 1) +#define MEDIA_LNK_FL_DYNAMIC (1 << 2) -#define MEDIA_LNK_FL_LINK_TYPE (0xf << 28) -# define MEDIA_LNK_FL_DATA_LINK (0 << 28) -# define MEDIA_LNK_FL_INTERFACE_LINK (1 << 28) +#define MEDIA_LNK_FL_LINK_TYPE (0xf << 28) +# define MEDIA_LNK_FL_DATA_LINK (0 << 28) +# define MEDIA_LNK_FL_INTERFACE_LINK (1 << 28) struct media_link_desc { struct media_pad_desc source; @@ -303,57 +242,47 @@ struct media_links_enum { /* Interface type ranges */ -#define MEDIA_INTF_T_DVB_BASE 0x00000100 -#define MEDIA_INTF_T_V4L_BASE 0x00000200 -#define MEDIA_INTF_T_ALSA_BASE 0x00000300 +#define MEDIA_INTF_T_DVB_BASE 0x00000100 +#define MEDIA_INTF_T_V4L_BASE 0x00000200 /* Interface types */ -#define MEDIA_INTF_T_DVB_FE (MEDIA_INTF_T_DVB_BASE) -#define MEDIA_INTF_T_DVB_DEMUX (MEDIA_INTF_T_DVB_BASE + 1) -#define MEDIA_INTF_T_DVB_DVR (MEDIA_INTF_T_DVB_BASE + 2) -#define MEDIA_INTF_T_DVB_CA (MEDIA_INTF_T_DVB_BASE + 3) -#define MEDIA_INTF_T_DVB_NET (MEDIA_INTF_T_DVB_BASE + 4) - -#define MEDIA_INTF_T_V4L_VIDEO (MEDIA_INTF_T_V4L_BASE) -#define MEDIA_INTF_T_V4L_VBI (MEDIA_INTF_T_V4L_BASE + 1) -#define MEDIA_INTF_T_V4L_RADIO (MEDIA_INTF_T_V4L_BASE + 2) -#define MEDIA_INTF_T_V4L_SUBDEV (MEDIA_INTF_T_V4L_BASE + 3) -#define MEDIA_INTF_T_V4L_SWRADIO (MEDIA_INTF_T_V4L_BASE + 4) -#define MEDIA_INTF_T_V4L_TOUCH (MEDIA_INTF_T_V4L_BASE + 5) - -#define MEDIA_INTF_T_ALSA_PCM_CAPTURE (MEDIA_INTF_T_ALSA_BASE) -#define MEDIA_INTF_T_ALSA_PCM_PLAYBACK (MEDIA_INTF_T_ALSA_BASE + 1) -#define MEDIA_INTF_T_ALSA_CONTROL (MEDIA_INTF_T_ALSA_BASE + 2) -#define MEDIA_INTF_T_ALSA_COMPRESS (MEDIA_INTF_T_ALSA_BASE + 3) -#define MEDIA_INTF_T_ALSA_RAWMIDI (MEDIA_INTF_T_ALSA_BASE + 4) -#define MEDIA_INTF_T_ALSA_HWDEP (MEDIA_INTF_T_ALSA_BASE + 5) -#define MEDIA_INTF_T_ALSA_SEQUENCER (MEDIA_INTF_T_ALSA_BASE + 6) -#define MEDIA_INTF_T_ALSA_TIMER (MEDIA_INTF_T_ALSA_BASE + 7) +#define MEDIA_INTF_T_DVB_FE (MEDIA_INTF_T_DVB_BASE) +#define MEDIA_INTF_T_DVB_DEMUX (MEDIA_INTF_T_DVB_BASE + 1) +#define MEDIA_INTF_T_DVB_DVR (MEDIA_INTF_T_DVB_BASE + 2) +#define MEDIA_INTF_T_DVB_CA (MEDIA_INTF_T_DVB_BASE + 3) +#define MEDIA_INTF_T_DVB_NET (MEDIA_INTF_T_DVB_BASE + 4) + +#define MEDIA_INTF_T_V4L_VIDEO (MEDIA_INTF_T_V4L_BASE) +#define MEDIA_INTF_T_V4L_VBI (MEDIA_INTF_T_V4L_BASE + 1) +#define MEDIA_INTF_T_V4L_RADIO (MEDIA_INTF_T_V4L_BASE + 2) +#define MEDIA_INTF_T_V4L_SUBDEV (MEDIA_INTF_T_V4L_BASE + 3) +#define MEDIA_INTF_T_V4L_SWRADIO (MEDIA_INTF_T_V4L_BASE + 4) +#define MEDIA_INTF_T_V4L_TOUCH (MEDIA_INTF_T_V4L_BASE + 5) + +#if defined(__KERNEL__) /* - * MC next gen API definitions + * Connector functions * - * NOTE: The declarations below are close to the MC RFC for the Media - * Controller, the next generation. Yet, there are a few adjustments - * to do, as we want to be able to have a functional API before - * the MC properties change. Those will be properly marked below. - * Please also notice that I removed "num_pads", "num_links", - * from the proposal, as a proper userspace application will likely - * use lists for pads/links, just as we intend to do in Kernelspace. - * The API definition should be freed from fields that are bound to - * some specific data structure. + * For now these should not be used in userspace, as some definitions may + * change. * - * FIXME: Currently, I opted to name the new types as "media_v2", as this - * won't cause any conflict with the Kernelspace namespace, nor with - * the previous kAPI media_*_desc namespace. This can be changed - * later, before the adding this API upstream. + * It is the responsibility of the entity drivers to add connectors and links. */ +#define MEDIA_ENT_F_CONN_RF (MEDIA_ENT_F_BASE + 0x30001) +#define MEDIA_ENT_F_CONN_SVIDEO (MEDIA_ENT_F_BASE + 0x30002) +#define MEDIA_ENT_F_CONN_COMPOSITE (MEDIA_ENT_F_BASE + 0x30003) +#endif + +/* + * MC next gen API definitions + */ struct media_v2_entity { __u32 id; - char name[64]; /* FIXME: move to a property? (RFC says so) */ + char name[64]; __u32 function; /* Main function of the entity */ __u32 reserved[6]; } __attribute__ ((packed)); @@ -413,10 +342,62 @@ struct media_v2_topology { /* ioctls */ -#define MEDIA_IOC_DEVICE_INFO _IOWR('|', 0x00, struct media_device_info) -#define MEDIA_IOC_ENUM_ENTITIES _IOWR('|', 0x01, struct media_entity_desc) -#define MEDIA_IOC_ENUM_LINKS _IOWR('|', 0x02, struct media_links_enum) -#define MEDIA_IOC_SETUP_LINK _IOWR('|', 0x03, struct media_link_desc) -#define MEDIA_IOC_G_TOPOLOGY _IOWR('|', 0x04, struct media_v2_topology) +#define MEDIA_IOC_DEVICE_INFO _IOWR('|', 0x00, struct media_device_info) +#define MEDIA_IOC_ENUM_ENTITIES _IOWR('|', 0x01, struct media_entity_desc) +#define MEDIA_IOC_ENUM_LINKS _IOWR('|', 0x02, struct media_links_enum) +#define MEDIA_IOC_SETUP_LINK _IOWR('|', 0x03, struct media_link_desc) +#define MEDIA_IOC_G_TOPOLOGY _IOWR('|', 0x04, struct media_v2_topology) + +#if !defined(__KERNEL__) || defined(__NEED_MEDIA_LEGACY_API) + +/* + * Legacy symbols used to avoid userspace compilation breakages. + * Do not use any of this in new applications! + * + * Those symbols map the entity function into types and should be + * used only on legacy programs for legacy hardware. Don't rely + * on those for MEDIA_IOC_G_TOPOLOGY. + */ +#define MEDIA_ENT_TYPE_SHIFT 16 +#define MEDIA_ENT_TYPE_MASK 0x00ff0000 +#define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff + +#define MEDIA_ENT_T_DEVNODE_UNKNOWN (MEDIA_ENT_F_OLD_BASE | \ + MEDIA_ENT_SUBTYPE_MASK) + +#define MEDIA_ENT_T_DEVNODE MEDIA_ENT_F_OLD_BASE +#define MEDIA_ENT_T_DEVNODE_V4L MEDIA_ENT_F_IO_V4L +#define MEDIA_ENT_T_DEVNODE_FB (MEDIA_ENT_F_OLD_BASE + 2) +#define MEDIA_ENT_T_DEVNODE_ALSA (MEDIA_ENT_F_OLD_BASE + 3) +#define MEDIA_ENT_T_DEVNODE_DVB (MEDIA_ENT_F_OLD_BASE + 4) + +#define MEDIA_ENT_T_UNKNOWN MEDIA_ENT_F_UNKNOWN +#define MEDIA_ENT_T_V4L2_VIDEO MEDIA_ENT_F_IO_V4L +#define MEDIA_ENT_T_V4L2_SUBDEV MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN +#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR MEDIA_ENT_F_CAM_SENSOR +#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH MEDIA_ENT_F_FLASH +#define MEDIA_ENT_T_V4L2_SUBDEV_LENS MEDIA_ENT_F_LENS +#define MEDIA_ENT_T_V4L2_SUBDEV_DECODER MEDIA_ENT_F_ATV_DECODER +#define MEDIA_ENT_T_V4L2_SUBDEV_TUNER MEDIA_ENT_F_TUNER + +/* + * There is still no ALSA support in the media controller. These + * defines should not have been added and we leave them here only + * in case some application tries to use these defines. + */ +#define MEDIA_INTF_T_ALSA_BASE 0x00000300 +#define MEDIA_INTF_T_ALSA_PCM_CAPTURE (MEDIA_INTF_T_ALSA_BASE) +#define MEDIA_INTF_T_ALSA_PCM_PLAYBACK (MEDIA_INTF_T_ALSA_BASE + 1) +#define MEDIA_INTF_T_ALSA_CONTROL (MEDIA_INTF_T_ALSA_BASE + 2) +#define MEDIA_INTF_T_ALSA_COMPRESS (MEDIA_INTF_T_ALSA_BASE + 3) +#define MEDIA_INTF_T_ALSA_RAWMIDI (MEDIA_INTF_T_ALSA_BASE + 4) +#define MEDIA_INTF_T_ALSA_HWDEP (MEDIA_INTF_T_ALSA_BASE + 5) +#define MEDIA_INTF_T_ALSA_SEQUENCER (MEDIA_INTF_T_ALSA_BASE + 6) +#define MEDIA_INTF_T_ALSA_TIMER (MEDIA_INTF_T_ALSA_BASE + 7) + +/* Obsolete symbol for media_version, no longer used in the kernel */ +#define MEDIA_API_VERSION KERNEL_VERSION(0, 1, 0) + +#endif #endif /* __LINUX_MEDIA_H */ -- cgit v1.2.3 From 20d60f61c58e8c937f3653819816dd203e6e3cb4 Mon Sep 17 00:00:00 2001 From: Haiyue Wang Date: Fri, 2 Feb 2018 10:16:10 +0800 Subject: ipmi: add a KCS IPMI BMC driver Provides a device driver for the KCS (Keyboard Controller Style) IPMI interface which meets the requirement of the BMC (Baseboard Management Controllers) side for handling the IPMI request from host system software. Signed-off-by: Haiyue Wang [Removed the selectability of IPMI_KCS_BMC, as it doesn't do much good to have it by itself.] Signed-off-by: Corey Minyard --- drivers/char/ipmi/Kconfig | 3 + drivers/char/ipmi/Makefile | 1 + drivers/char/ipmi/kcs_bmc.c | 464 ++++++++++++++++++++++++++++++++++++++++++ drivers/char/ipmi/kcs_bmc.h | 106 ++++++++++ include/uapi/linux/ipmi_bmc.h | 14 ++ 5 files changed, 588 insertions(+) create mode 100644 drivers/char/ipmi/kcs_bmc.c create mode 100644 drivers/char/ipmi/kcs_bmc.h create mode 100644 include/uapi/linux/ipmi_bmc.h (limited to 'include/uapi/linux') diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 3544abc0f9f9..7641b8a2f632 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -96,6 +96,9 @@ config IPMI_POWEROFF endif # IPMI_HANDLER +config IPMI_KCS_BMC + tristate + config ASPEED_BT_IPMI_BMC depends on ARCH_ASPEED || COMPILE_TEST depends on REGMAP && REGMAP_MMIO && MFD_SYSCON diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 33b899fcf14a..2abccb30016a 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -21,4 +21,5 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o +obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c new file mode 100644 index 000000000000..3a3498afa427 --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc.c @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2018, Intel Corporation. + +#define pr_fmt(fmt) "kcs-bmc: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kcs_bmc.h" + +#define KCS_MSG_BUFSIZ 1000 + +#define KCS_ZERO_DATA 0 + + +/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */ +#define KCS_STATUS_STATE(state) (state << 6) +#define KCS_STATUS_STATE_MASK GENMASK(7, 6) +#define KCS_STATUS_CMD_DAT BIT(3) +#define KCS_STATUS_SMS_ATN BIT(2) +#define KCS_STATUS_IBF BIT(1) +#define KCS_STATUS_OBF BIT(0) + +/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */ +enum kcs_states { + IDLE_STATE = 0, + READ_STATE = 1, + WRITE_STATE = 2, + ERROR_STATE = 3, +}; + +/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */ +#define KCS_CMD_GET_STATUS_ABORT 0x60 +#define KCS_CMD_WRITE_START 0x61 +#define KCS_CMD_WRITE_END 0x62 +#define KCS_CMD_READ_BYTE 0x68 + +static inline u8 read_data(struct kcs_bmc *kcs_bmc) +{ + return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr); +} + +static inline void write_data(struct kcs_bmc *kcs_bmc, u8 data) +{ + kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data); +} + +static inline u8 read_status(struct kcs_bmc *kcs_bmc) +{ + return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str); +} + +static inline void write_status(struct kcs_bmc *kcs_bmc, u8 data) +{ + kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data); +} + +static void update_status_bits(struct kcs_bmc *kcs_bmc, u8 mask, u8 val) +{ + u8 tmp = read_status(kcs_bmc); + + tmp &= ~mask; + tmp |= val & mask; + + write_status(kcs_bmc, tmp); +} + +static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state) +{ + update_status_bits(kcs_bmc, KCS_STATUS_STATE_MASK, + KCS_STATUS_STATE(state)); +} + +static void kcs_force_abort(struct kcs_bmc *kcs_bmc) +{ + set_state(kcs_bmc, ERROR_STATE); + read_data(kcs_bmc); + write_data(kcs_bmc, KCS_ZERO_DATA); + + kcs_bmc->phase = KCS_PHASE_ERROR; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; +} + +static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc) +{ + u8 data; + + switch (kcs_bmc->phase) { + case KCS_PHASE_WRITE_START: + kcs_bmc->phase = KCS_PHASE_WRITE_DATA; + + case KCS_PHASE_WRITE_DATA: + if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) { + set_state(kcs_bmc, WRITE_STATE); + write_data(kcs_bmc, KCS_ZERO_DATA); + kcs_bmc->data_in[kcs_bmc->data_in_idx++] = + read_data(kcs_bmc); + } else { + kcs_force_abort(kcs_bmc); + kcs_bmc->error = KCS_LENGTH_ERROR; + } + break; + + case KCS_PHASE_WRITE_END_CMD: + if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) { + set_state(kcs_bmc, READ_STATE); + kcs_bmc->data_in[kcs_bmc->data_in_idx++] = + read_data(kcs_bmc); + kcs_bmc->phase = KCS_PHASE_WRITE_DONE; + kcs_bmc->data_in_avail = true; + wake_up_interruptible(&kcs_bmc->queue); + } else { + kcs_force_abort(kcs_bmc); + kcs_bmc->error = KCS_LENGTH_ERROR; + } + break; + + case KCS_PHASE_READ: + if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) + set_state(kcs_bmc, IDLE_STATE); + + data = read_data(kcs_bmc); + if (data != KCS_CMD_READ_BYTE) { + set_state(kcs_bmc, ERROR_STATE); + write_data(kcs_bmc, KCS_ZERO_DATA); + break; + } + + if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) { + write_data(kcs_bmc, KCS_ZERO_DATA); + kcs_bmc->phase = KCS_PHASE_IDLE; + break; + } + + write_data(kcs_bmc, + kcs_bmc->data_out[kcs_bmc->data_out_idx++]); + break; + + case KCS_PHASE_ABORT_ERROR1: + set_state(kcs_bmc, READ_STATE); + read_data(kcs_bmc); + write_data(kcs_bmc, kcs_bmc->error); + kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2; + break; + + case KCS_PHASE_ABORT_ERROR2: + set_state(kcs_bmc, IDLE_STATE); + read_data(kcs_bmc); + write_data(kcs_bmc, KCS_ZERO_DATA); + kcs_bmc->phase = KCS_PHASE_IDLE; + break; + + default: + kcs_force_abort(kcs_bmc); + break; + } +} + +static void kcs_bmc_handle_cmd(struct kcs_bmc *kcs_bmc) +{ + u8 cmd; + + set_state(kcs_bmc, WRITE_STATE); + write_data(kcs_bmc, KCS_ZERO_DATA); + + cmd = read_data(kcs_bmc); + switch (cmd) { + case KCS_CMD_WRITE_START: + kcs_bmc->phase = KCS_PHASE_WRITE_START; + kcs_bmc->error = KCS_NO_ERROR; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; + break; + + case KCS_CMD_WRITE_END: + if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) { + kcs_force_abort(kcs_bmc); + break; + } + + kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD; + break; + + case KCS_CMD_GET_STATUS_ABORT: + if (kcs_bmc->error == KCS_NO_ERROR) + kcs_bmc->error = KCS_ABORTED_BY_COMMAND; + + kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; + break; + + default: + kcs_force_abort(kcs_bmc); + kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE; + break; + } +} + +int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc) +{ + unsigned long flags; + int ret = 0; + u8 status; + + spin_lock_irqsave(&kcs_bmc->lock, flags); + + if (!kcs_bmc->running) { + kcs_force_abort(kcs_bmc); + ret = -ENODEV; + goto out_unlock; + } + + status = read_status(kcs_bmc) & (KCS_STATUS_IBF | KCS_STATUS_CMD_DAT); + + switch (status) { + case KCS_STATUS_IBF | KCS_STATUS_CMD_DAT: + kcs_bmc_handle_cmd(kcs_bmc); + break; + + case KCS_STATUS_IBF: + kcs_bmc_handle_data(kcs_bmc); + break; + + default: + ret = -ENODATA; + break; + } + +out_unlock: + spin_unlock_irqrestore(&kcs_bmc->lock, flags); + + return ret; +} +EXPORT_SYMBOL(kcs_bmc_handle_event); + +static inline struct kcs_bmc *file_to_kcs_bmc(struct file *filp) +{ + return container_of(filp->private_data, struct kcs_bmc, miscdev); +} + +static int kcs_bmc_open(struct inode *inode, struct file *filp) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + int ret = 0; + + spin_lock_irq(&kcs_bmc->lock); + if (!kcs_bmc->running) + kcs_bmc->running = 1; + else + ret = -EBUSY; + spin_unlock_irq(&kcs_bmc->lock); + + return ret; +} + +static unsigned int kcs_bmc_poll(struct file *filp, poll_table *wait) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + unsigned int mask = 0; + + poll_wait(filp, &kcs_bmc->queue, wait); + + spin_lock_irq(&kcs_bmc->lock); + if (kcs_bmc->data_in_avail) + mask |= POLLIN; + spin_unlock_irq(&kcs_bmc->lock); + + return mask; +} + +static ssize_t kcs_bmc_read(struct file *filp, char *buf, + size_t count, loff_t *offset) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + bool data_avail; + size_t data_len; + ssize_t ret; + + if (!(filp->f_flags & O_NONBLOCK)) + wait_event_interruptible(kcs_bmc->queue, + kcs_bmc->data_in_avail); + + mutex_lock(&kcs_bmc->mutex); + + spin_lock_irq(&kcs_bmc->lock); + data_avail = kcs_bmc->data_in_avail; + if (data_avail) { + data_len = kcs_bmc->data_in_idx; + memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len); + } + spin_unlock_irq(&kcs_bmc->lock); + + if (!data_avail) { + ret = -EAGAIN; + goto out_unlock; + } + + if (count < data_len) { + pr_err("channel=%u with too large data : %zu\n", + kcs_bmc->channel, data_len); + + spin_lock_irq(&kcs_bmc->lock); + kcs_force_abort(kcs_bmc); + spin_unlock_irq(&kcs_bmc->lock); + + ret = -EOVERFLOW; + goto out_unlock; + } + + if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) { + ret = -EFAULT; + goto out_unlock; + } + + ret = data_len; + + spin_lock_irq(&kcs_bmc->lock); + if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) { + kcs_bmc->phase = KCS_PHASE_WAIT_READ; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; + } else { + ret = -EAGAIN; + } + spin_unlock_irq(&kcs_bmc->lock); + +out_unlock: + mutex_unlock(&kcs_bmc->mutex); + + return ret; +} + +static ssize_t kcs_bmc_write(struct file *filp, const char *buf, + size_t count, loff_t *offset) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + ssize_t ret; + + /* a minimum response size '3' : netfn + cmd + ccode */ + if (count < 3 || count > KCS_MSG_BUFSIZ) + return -EINVAL; + + mutex_lock(&kcs_bmc->mutex); + + if (copy_from_user(kcs_bmc->kbuffer, buf, count)) { + ret = -EFAULT; + goto out_unlock; + } + + spin_lock_irq(&kcs_bmc->lock); + if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) { + kcs_bmc->phase = KCS_PHASE_READ; + kcs_bmc->data_out_idx = 1; + kcs_bmc->data_out_len = count; + memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count); + write_data(kcs_bmc, kcs_bmc->data_out[0]); + ret = count; + } else { + ret = -EINVAL; + } + spin_unlock_irq(&kcs_bmc->lock); + +out_unlock: + mutex_unlock(&kcs_bmc->mutex); + + return ret; +} + +static long kcs_bmc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + long ret = 0; + + spin_lock_irq(&kcs_bmc->lock); + + switch (cmd) { + case IPMI_BMC_IOCTL_SET_SMS_ATN: + update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN, + KCS_STATUS_SMS_ATN); + break; + + case IPMI_BMC_IOCTL_CLEAR_SMS_ATN: + update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN, + 0); + break; + + case IPMI_BMC_IOCTL_FORCE_ABORT: + kcs_force_abort(kcs_bmc); + break; + + default: + ret = -EINVAL; + break; + } + + spin_unlock_irq(&kcs_bmc->lock); + + return ret; +} + +static int kcs_bmc_release(struct inode *inode, struct file *filp) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + + spin_lock_irq(&kcs_bmc->lock); + kcs_bmc->running = 0; + kcs_force_abort(kcs_bmc); + spin_unlock_irq(&kcs_bmc->lock); + + return 0; +} + +static const struct file_operations kcs_bmc_fops = { + .owner = THIS_MODULE, + .open = kcs_bmc_open, + .read = kcs_bmc_read, + .write = kcs_bmc_write, + .release = kcs_bmc_release, + .poll = kcs_bmc_poll, + .unlocked_ioctl = kcs_bmc_ioctl, +}; + +struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel) +{ + struct kcs_bmc *kcs_bmc; + + kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL); + if (!kcs_bmc) + return NULL; + + dev_set_name(dev, "ipmi-kcs%u", channel); + + spin_lock_init(&kcs_bmc->lock); + kcs_bmc->channel = channel; + + mutex_init(&kcs_bmc->mutex); + init_waitqueue_head(&kcs_bmc->queue); + + kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); + kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); + kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); + if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer) + return NULL; + + kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR; + kcs_bmc->miscdev.name = dev_name(dev); + kcs_bmc->miscdev.fops = &kcs_bmc_fops; + + return kcs_bmc; +} +EXPORT_SYMBOL(kcs_bmc_alloc); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Haiyue Wang "); +MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software"); diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h new file mode 100644 index 000000000000..c19501db0236 --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc.h @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2018, Intel Corporation. + +#ifndef __KCS_BMC_H__ +#define __KCS_BMC_H__ + +#include + +/* Different phases of the KCS BMC module : + * KCS_PHASE_IDLE : + * BMC should not be expecting nor sending any data. + * KCS_PHASE_WRITE_START : + * BMC is receiving a WRITE_START command from system software. + * KCS_PHASE_WRITE_DATA : + * BMC is receiving a data byte from system software. + * KCS_PHASE_WRITE_END_CMD : + * BMC is waiting a last data byte from system software. + * KCS_PHASE_WRITE_DONE : + * BMC has received the whole request from system software. + * KCS_PHASE_WAIT_READ : + * BMC is waiting the response from the upper IPMI service. + * KCS_PHASE_READ : + * BMC is transferring the response to system software. + * KCS_PHASE_ABORT_ERROR1 : + * BMC is waiting error status request from system software. + * KCS_PHASE_ABORT_ERROR2 : + * BMC is waiting for idle status afer error from system software. + * KCS_PHASE_ERROR : + * BMC has detected a protocol violation at the interface level. + */ +enum kcs_phases { + KCS_PHASE_IDLE, + + KCS_PHASE_WRITE_START, + KCS_PHASE_WRITE_DATA, + KCS_PHASE_WRITE_END_CMD, + KCS_PHASE_WRITE_DONE, + + KCS_PHASE_WAIT_READ, + KCS_PHASE_READ, + + KCS_PHASE_ABORT_ERROR1, + KCS_PHASE_ABORT_ERROR2, + KCS_PHASE_ERROR +}; + +/* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */ +enum kcs_errors { + KCS_NO_ERROR = 0x00, + KCS_ABORTED_BY_COMMAND = 0x01, + KCS_ILLEGAL_CONTROL_CODE = 0x02, + KCS_LENGTH_ERROR = 0x06, + KCS_UNSPECIFIED_ERROR = 0xFF +}; + +/* IPMI 2.0 - 9.5, KCS Interface Registers + * @idr : Input Data Register + * @odr : Output Data Register + * @str : Status Register + */ +struct kcs_ioreg { + u32 idr; + u32 odr; + u32 str; +}; + +struct kcs_bmc { + spinlock_t lock; + + u32 channel; + int running; + + /* Setup by BMC KCS controller driver */ + struct kcs_ioreg ioreg; + u8 (*io_inputb)(struct kcs_bmc *kcs_bmc, u32 reg); + void (*io_outputb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 b); + + enum kcs_phases phase; + enum kcs_errors error; + + wait_queue_head_t queue; + bool data_in_avail; + int data_in_idx; + u8 *data_in; + + int data_out_idx; + int data_out_len; + u8 *data_out; + + struct mutex mutex; + u8 *kbuffer; + + struct miscdevice miscdev; + + unsigned long priv[]; +}; + +static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc) +{ + return kcs_bmc->priv; +} + +int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc); +struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, + u32 channel); +#endif diff --git a/include/uapi/linux/ipmi_bmc.h b/include/uapi/linux/ipmi_bmc.h new file mode 100644 index 000000000000..2f9f97e6123a --- /dev/null +++ b/include/uapi/linux/ipmi_bmc.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2018, Intel Corporation. + +#ifndef _UAPI_LINUX_IPMI_BMC_H +#define _UAPI_LINUX_IPMI_BMC_H + +#include + +#define __IPMI_BMC_IOCTL_MAGIC 0xB1 +#define IPMI_BMC_IOCTL_SET_SMS_ATN _IO(__IPMI_BMC_IOCTL_MAGIC, 0x00) +#define IPMI_BMC_IOCTL_CLEAR_SMS_ATN _IO(__IPMI_BMC_IOCTL_MAGIC, 0x01) +#define IPMI_BMC_IOCTL_FORCE_ABORT _IO(__IPMI_BMC_IOCTL_MAGIC, 0x02) + +#endif /* _UAPI_LINUX_KCS_BMC_H */ -- cgit v1.2.3 From 3b6d082f0dfc2b7b9def494d2ab67fd4d3862ea1 Mon Sep 17 00:00:00 2001 From: Haiyue Wang Date: Mon, 26 Feb 2018 23:48:14 +0800 Subject: ipmi: kcs_bmc: coding-style fixes and use new poll type Many for coding-style fixes, and update the poll API with the new type '__poll_t', this is new commit from linux-4.16-rc1. Signed-off-by: Haiyue Wang Signed-off-by: Corey Minyard --- drivers/char/ipmi/kcs_bmc.c | 32 +++++++++++++++++--------------- drivers/char/ipmi/kcs_bmc.h | 36 +++++++++++++++++++----------------- drivers/char/ipmi/kcs_bmc_aspeed.c | 9 +++++---- include/uapi/linux/ipmi_bmc.h | 8 +++++--- 4 files changed, 46 insertions(+), 39 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c index 6476bfb79f44..fbfc05e3f3d1 100644 --- a/drivers/char/ipmi/kcs_bmc.c +++ b/drivers/char/ipmi/kcs_bmc.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2015-2018, Intel Corporation. +/* + * Copyright (c) 2015-2018, Intel Corporation. + */ #define pr_fmt(fmt) "kcs-bmc: " fmt @@ -242,14 +244,14 @@ out_unlock: } EXPORT_SYMBOL(kcs_bmc_handle_event); -static inline struct kcs_bmc *file_to_kcs_bmc(struct file *filp) +static inline struct kcs_bmc *to_kcs_bmc(struct file *filp) { return container_of(filp->private_data, struct kcs_bmc, miscdev); } static int kcs_bmc_open(struct inode *inode, struct file *filp) { - struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); int ret = 0; spin_lock_irq(&kcs_bmc->lock); @@ -262,25 +264,25 @@ static int kcs_bmc_open(struct inode *inode, struct file *filp) return ret; } -static unsigned int kcs_bmc_poll(struct file *filp, poll_table *wait) +static __poll_t kcs_bmc_poll(struct file *filp, poll_table *wait) { - struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); - unsigned int mask = 0; + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); + __poll_t mask = 0; poll_wait(filp, &kcs_bmc->queue, wait); spin_lock_irq(&kcs_bmc->lock); if (kcs_bmc->data_in_avail) - mask |= POLLIN; + mask |= EPOLLIN; spin_unlock_irq(&kcs_bmc->lock); return mask; } -static ssize_t kcs_bmc_read(struct file *filp, char *buf, - size_t count, loff_t *offset) +static ssize_t kcs_bmc_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) { - struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); bool data_avail; size_t data_len; ssize_t ret; @@ -339,10 +341,10 @@ out_unlock: return ret; } -static ssize_t kcs_bmc_write(struct file *filp, const char *buf, - size_t count, loff_t *offset) +static ssize_t kcs_bmc_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) { - struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); ssize_t ret; /* a minimum response size '3' : netfn + cmd + ccode */ @@ -378,7 +380,7 @@ out_unlock: static long kcs_bmc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); long ret = 0; spin_lock_irq(&kcs_bmc->lock); @@ -410,7 +412,7 @@ static long kcs_bmc_ioctl(struct file *filp, unsigned int cmd, static int kcs_bmc_release(struct inode *inode, struct file *filp) { - struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); spin_lock_irq(&kcs_bmc->lock); kcs_bmc->running = 0; diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h index c19501db0236..eb9ea4ce78b8 100644 --- a/drivers/char/ipmi/kcs_bmc.h +++ b/drivers/char/ipmi/kcs_bmc.h @@ -1,31 +1,33 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2015-2018, Intel Corporation. +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015-2018, Intel Corporation. + */ #ifndef __KCS_BMC_H__ #define __KCS_BMC_H__ #include -/* Different phases of the KCS BMC module : - * KCS_PHASE_IDLE : +/* Different phases of the KCS BMC module. + * KCS_PHASE_IDLE: * BMC should not be expecting nor sending any data. - * KCS_PHASE_WRITE_START : + * KCS_PHASE_WRITE_START: * BMC is receiving a WRITE_START command from system software. - * KCS_PHASE_WRITE_DATA : + * KCS_PHASE_WRITE_DATA: * BMC is receiving a data byte from system software. - * KCS_PHASE_WRITE_END_CMD : + * KCS_PHASE_WRITE_END_CMD: * BMC is waiting a last data byte from system software. - * KCS_PHASE_WRITE_DONE : + * KCS_PHASE_WRITE_DONE: * BMC has received the whole request from system software. - * KCS_PHASE_WAIT_READ : + * KCS_PHASE_WAIT_READ: * BMC is waiting the response from the upper IPMI service. - * KCS_PHASE_READ : + * KCS_PHASE_READ: * BMC is transferring the response to system software. - * KCS_PHASE_ABORT_ERROR1 : + * KCS_PHASE_ABORT_ERROR1: * BMC is waiting error status request from system software. - * KCS_PHASE_ABORT_ERROR2 : + * KCS_PHASE_ABORT_ERROR2: * BMC is waiting for idle status afer error from system software. - * KCS_PHASE_ERROR : + * KCS_PHASE_ERROR: * BMC has detected a protocol violation at the interface level. */ enum kcs_phases { @@ -54,9 +56,9 @@ enum kcs_errors { }; /* IPMI 2.0 - 9.5, KCS Interface Registers - * @idr : Input Data Register - * @odr : Output Data Register - * @str : Status Register + * @idr: Input Data Register + * @odr: Output Data Register + * @str: Status Register */ struct kcs_ioreg { u32 idr; @@ -103,4 +105,4 @@ static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc) int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc); struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel); -#endif +#endif /* __KCS_BMC_H__ */ diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c index 0c4d1a36dae4..3c955946e647 100644 --- a/drivers/char/ipmi/kcs_bmc_aspeed.c +++ b/drivers/char/ipmi/kcs_bmc_aspeed.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2015-2018, Intel Corporation. +/* + * Copyright (c) 2015-2018, Intel Corporation. + */ #define pr_fmt(fmt) "aspeed-kcs-bmc: " fmt @@ -301,19 +303,18 @@ static const struct of_device_id ast_kcs_bmc_match[] = { { .compatible = "aspeed,ast2500-kcs-bmc" }, { } }; +MODULE_DEVICE_TABLE(of, ast_kcs_bmc_match); static struct platform_driver ast_kcs_bmc_driver = { .driver = { .name = DEVICE_NAME, .of_match_table = ast_kcs_bmc_match, }, - .probe = aspeed_kcs_probe, + .probe = aspeed_kcs_probe, .remove = aspeed_kcs_remove, }; - module_platform_driver(ast_kcs_bmc_driver); -MODULE_DEVICE_TABLE(of, ast_kcs_bmc_match); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Haiyue Wang "); MODULE_DESCRIPTION("Aspeed device interface to the KCS BMC device"); diff --git a/include/uapi/linux/ipmi_bmc.h b/include/uapi/linux/ipmi_bmc.h index 2f9f97e6123a..1670f0944227 100644 --- a/include/uapi/linux/ipmi_bmc.h +++ b/include/uapi/linux/ipmi_bmc.h @@ -1,5 +1,7 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2015-2018, Intel Corporation. +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015-2018, Intel Corporation. + */ #ifndef _UAPI_LINUX_IPMI_BMC_H #define _UAPI_LINUX_IPMI_BMC_H @@ -11,4 +13,4 @@ #define IPMI_BMC_IOCTL_CLEAR_SMS_ATN _IO(__IPMI_BMC_IOCTL_MAGIC, 0x01) #define IPMI_BMC_IOCTL_FORCE_ABORT _IO(__IPMI_BMC_IOCTL_MAGIC, 0x02) -#endif /* _UAPI_LINUX_KCS_BMC_H */ +#endif /* _UAPI_LINUX_IPMI_BMC_H */ -- cgit v1.2.3 From 6b1aea8cf2c8618146edaf6b35775ab55f7cafe5 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Mon, 1 Jan 2018 00:00:00 +0100 Subject: batman-adv: Update copyright years for 2018 Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- include/uapi/linux/batadv_packet.h | 2 +- include/uapi/linux/batman_adv.h | 2 +- net/batman-adv/Kconfig | 2 +- net/batman-adv/Makefile | 2 +- net/batman-adv/bat_algo.c | 2 +- net/batman-adv/bat_algo.h | 2 +- net/batman-adv/bat_iv_ogm.c | 2 +- net/batman-adv/bat_iv_ogm.h | 2 +- net/batman-adv/bat_v.c | 2 +- net/batman-adv/bat_v.h | 2 +- net/batman-adv/bat_v_elp.c | 2 +- net/batman-adv/bat_v_elp.h | 2 +- net/batman-adv/bat_v_ogm.c | 2 +- net/batman-adv/bat_v_ogm.h | 2 +- net/batman-adv/bitarray.c | 2 +- net/batman-adv/bitarray.h | 2 +- net/batman-adv/bridge_loop_avoidance.c | 2 +- net/batman-adv/bridge_loop_avoidance.h | 2 +- net/batman-adv/debugfs.c | 2 +- net/batman-adv/debugfs.h | 2 +- net/batman-adv/distributed-arp-table.c | 2 +- net/batman-adv/distributed-arp-table.h | 2 +- net/batman-adv/fragmentation.c | 2 +- net/batman-adv/fragmentation.h | 2 +- net/batman-adv/gateway_client.c | 2 +- net/batman-adv/gateway_client.h | 2 +- net/batman-adv/gateway_common.c | 2 +- net/batman-adv/gateway_common.h | 2 +- net/batman-adv/hard-interface.c | 2 +- net/batman-adv/hard-interface.h | 2 +- net/batman-adv/hash.c | 2 +- net/batman-adv/hash.h | 2 +- net/batman-adv/icmp_socket.c | 2 +- net/batman-adv/icmp_socket.h | 2 +- net/batman-adv/log.c | 2 +- net/batman-adv/log.h | 2 +- net/batman-adv/main.c | 2 +- net/batman-adv/main.h | 2 +- net/batman-adv/multicast.c | 2 +- net/batman-adv/multicast.h | 2 +- net/batman-adv/netlink.c | 2 +- net/batman-adv/netlink.h | 2 +- net/batman-adv/network-coding.c | 2 +- net/batman-adv/network-coding.h | 2 +- net/batman-adv/originator.c | 2 +- net/batman-adv/originator.h | 2 +- net/batman-adv/routing.c | 2 +- net/batman-adv/routing.h | 2 +- net/batman-adv/send.c | 2 +- net/batman-adv/send.h | 2 +- net/batman-adv/soft-interface.c | 2 +- net/batman-adv/soft-interface.h | 2 +- net/batman-adv/sysfs.c | 2 +- net/batman-adv/sysfs.h | 2 +- net/batman-adv/tp_meter.c | 2 +- net/batman-adv/tp_meter.h | 2 +- net/batman-adv/translation-table.c | 2 +- net/batman-adv/translation-table.h | 2 +- net/batman-adv/tvlv.c | 2 +- net/batman-adv/tvlv.h | 2 +- net/batman-adv/types.h | 2 +- 61 files changed, 61 insertions(+), 61 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/batadv_packet.h b/include/uapi/linux/batadv_packet.h index 5cb360be2a11..daefd7283896 100644 --- a/include/uapi/linux/batadv_packet.h +++ b/include/uapi/linux/batadv_packet.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index ae00c99cbed0..56ae28934070 100644 --- a/include/uapi/linux/batman_adv.h +++ b/include/uapi/linux/batman_adv.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: MIT */ -/* Copyright (C) 2016-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2016-2018 B.A.T.M.A.N. contributors: * * Matthias Schiffer * diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig index c44f6515be5e..e4e2e02b7380 100644 --- a/net/batman-adv/Kconfig +++ b/net/batman-adv/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -# Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +# Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: # # Marek Lindner, Simon Wunderlich # diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile index 022f6e77307b..b97ba6fb8353 100644 --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -# Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +# Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: # # Marek Lindner, Simon Wunderlich # diff --git a/net/batman-adv/bat_algo.c b/net/batman-adv/bat_algo.c index 80c72c7d3cad..ea309ad06175 100644 --- a/net/batman-adv/bat_algo.c +++ b/net/batman-adv/bat_algo.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/bat_algo.h b/net/batman-adv/bat_algo.h index 029221615ba3..534b790c3753 100644 --- a/net/batman-adv/bat_algo.h +++ b/net/batman-adv/bat_algo.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2011-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Linus Lüssing * diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 79e326383726..e21aa147607b 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/bat_iv_ogm.h b/net/batman-adv/bat_iv_ogm.h index 9dc0dd5c83df..317cafd302cf 100644 --- a/net/batman-adv/bat_iv_ogm.h +++ b/net/batman-adv/bat_iv_ogm.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index 27e165ac9302..9c3a34b65b15 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2013-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2013-2018 B.A.T.M.A.N. contributors: * * Linus Lüssing, Marek Lindner * diff --git a/net/batman-adv/bat_v.h b/net/batman-adv/bat_v.h index a17ab68bbce8..ec4a2a569750 100644 --- a/net/batman-adv/bat_v.h +++ b/net/batman-adv/bat_v.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2011-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Linus Lüssing * diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index a83478c46597..28687493599f 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2011-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2018 B.A.T.M.A.N. contributors: * * Linus Lüssing, Marek Lindner * diff --git a/net/batman-adv/bat_v_elp.h b/net/batman-adv/bat_v_elp.h index 5e39d0588a48..e8c7b7fd290d 100644 --- a/net/batman-adv/bat_v_elp.h +++ b/net/batman-adv/bat_v_elp.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2013-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2013-2018 B.A.T.M.A.N. contributors: * * Linus Lüssing, Marek Lindner * diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index ba59b77c605d..2948b41b06d4 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2013-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2013-2018 B.A.T.M.A.N. contributors: * * Antonio Quartulli * diff --git a/net/batman-adv/bat_v_ogm.h b/net/batman-adv/bat_v_ogm.h index 6a4c14ccc3c6..ed36c5e79fde 100644 --- a/net/batman-adv/bat_v_ogm.h +++ b/net/batman-adv/bat_v_ogm.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2013-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2013-2018 B.A.T.M.A.N. contributors: * * Antonio Quartulli * diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c index bdc1ef06e05b..a296a4d851f5 100644 --- a/net/batman-adv/bitarray.c +++ b/net/batman-adv/bitarray.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2006-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2018 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h index ca9d0753dd6b..48f683289531 100644 --- a/net/batman-adv/bitarray.h +++ b/net/batman-adv/bitarray.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2006-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2018 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index fad47853ad3c..8ff81346ff0c 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2011-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2018 B.A.T.M.A.N. contributors: * * Simon Wunderlich * diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h index b27571abcd2f..71f95a3e4d3f 100644 --- a/net/batman-adv/bridge_loop_avoidance.h +++ b/net/batman-adv/bridge_loop_avoidance.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2011-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2018 B.A.T.M.A.N. contributors: * * Simon Wunderlich * diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index 21d1189957a7..4229b01ac7b5 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2010-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/debugfs.h b/net/batman-adv/debugfs.h index 90a08d35c501..37b069698b04 100644 --- a/net/batman-adv/debugfs.h +++ b/net/batman-adv/debugfs.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2010-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 9703c791ffc5..19b15de455ab 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2011-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2018 B.A.T.M.A.N. contributors: * * Antonio Quartulli * diff --git a/net/batman-adv/distributed-arp-table.h b/net/batman-adv/distributed-arp-table.h index 12897eb46268..e24aa9601c52 100644 --- a/net/batman-adv/distributed-arp-table.h +++ b/net/batman-adv/distributed-arp-table.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2011-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2018 B.A.T.M.A.N. contributors: * * Antonio Quartulli * diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 22dde42fd80e..d815acc13c35 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2013-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2013-2018 B.A.T.M.A.N. contributors: * * Martin Hundebøll * diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h index 138b22a1836a..944512e07782 100644 --- a/net/batman-adv/fragmentation.h +++ b/net/batman-adv/fragmentation.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2013-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2013-2018 B.A.T.M.A.N. contributors: * * Martin Hundebøll * diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 37fe9a644f22..c294f6fd43e0 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2009-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h index 981f58421a32..f0b86fcb2493 100644 --- a/net/batman-adv/gateway_client.h +++ b/net/batman-adv/gateway_client.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2009-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c index b3e156af2256..936c107f3199 100644 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2009-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/gateway_common.h b/net/batman-adv/gateway_common.h index afebd9c7edf4..80afb2793687 100644 --- a/net/batman-adv/gateway_common.h +++ b/net/batman-adv/gateway_common.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2009-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 5f186bff284a..fd4a263dd6b7 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index de5e9a374ece..d1c0f6189301 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/hash.c b/net/batman-adv/hash.c index 04d964358c98..7b49e4001778 100644 --- a/net/batman-adv/hash.c +++ b/net/batman-adv/hash.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2006-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2018 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index 4ce1b6d3ad5c..9490a7ca2ba6 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2006-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2018 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index e91f29c7c638..7d5e9abb7a65 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/icmp_socket.h b/net/batman-adv/icmp_socket.h index 84cddd01eeab..958be22beda9 100644 --- a/net/batman-adv/icmp_socket.h +++ b/net/batman-adv/icmp_socket.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/log.c b/net/batman-adv/log.c index dc9fa37ddd14..52d8a4b848c0 100644 --- a/net/batman-adv/log.c +++ b/net/batman-adv/log.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2010-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/log.h b/net/batman-adv/log.h index 35e02b2b9e72..35f4f397ed57 100644 --- a/net/batman-adv/log.h +++ b/net/batman-adv/log.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index d31c8266e244..69c0d85bceb3 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 69bfedfad174..d5d65999207e 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index cbdeb47ec3f6..6eaffe50335a 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2014-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2014-2018 B.A.T.M.A.N. contributors: * * Linus Lüssing * diff --git a/net/batman-adv/multicast.h b/net/batman-adv/multicast.h index 3ac06337ab71..6b8594e23da3 100644 --- a/net/batman-adv/multicast.h +++ b/net/batman-adv/multicast.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2014-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2014-2018 B.A.T.M.A.N. contributors: * * Linus Lüssing * diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index a823d3899bad..129af56b944d 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2016-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2016-2018 B.A.T.M.A.N. contributors: * * Matthias Schiffer * diff --git a/net/batman-adv/netlink.h b/net/batman-adv/netlink.h index 0e7e57b69b54..571d9a5ae7aa 100644 --- a/net/batman-adv/netlink.h +++ b/net/batman-adv/netlink.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2016-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2016-2018 B.A.T.M.A.N. contributors: * * Matthias Schiffer * diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index b48116bb24ef..c3578444f3cb 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2012-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2012-2018 B.A.T.M.A.N. contributors: * * Martin Hundebøll, Jeppe Ledet-Pedersen * diff --git a/net/batman-adv/network-coding.h b/net/batman-adv/network-coding.h index adaeafa4f71e..65c346812bc1 100644 --- a/net/batman-adv/network-coding.h +++ b/net/batman-adv/network-coding.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2012-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2012-2018 B.A.T.M.A.N. contributors: * * Martin Hundebøll, Jeppe Ledet-Pedersen * diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 58a7d9274435..2a51a0cbb82a 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2009-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index 8e543a3cdc6c..f3601ab0872e 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index b6891e8b741c..289df027ecdd 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h index a1289bc5f115..db54c2d9b8bf 100644 --- a/net/batman-adv/routing.h +++ b/net/batman-adv/routing.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 2a5ab6f1076d..4a35f5c2f52b 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index 1e8c79093623..64cce07b8fe6 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 900c5ce21cd4..c95e2b2677fd 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h index 075c5b5b2ce1..daf87f07fadd 100644 --- a/net/batman-adv/soft-interface.h +++ b/net/batman-adv/soft-interface.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index c1578fa0b952..f2eef43bd2ec 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2010-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/sysfs.h b/net/batman-adv/sysfs.h index bbeee61221fa..c1e3fb69952d 100644 --- a/net/batman-adv/sysfs.h +++ b/net/batman-adv/sysfs.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2010-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 8b576712d0c1..11520de96ccb 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2012-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2012-2018 B.A.T.M.A.N. contributors: * * Edo Monticelli, Antonio Quartulli * diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h index c8b8f2cb2c2b..68e600974759 100644 --- a/net/batman-adv/tp_meter.h +++ b/net/batman-adv/tp_meter.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2012-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2012-2018 B.A.T.M.A.N. contributors: * * Edo Monticelli, Antonio Quartulli * diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 7550a9ccd695..0225616d5771 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich, Antonio Quartulli * diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h index 8d9e3abec2c8..01b6c8eafaf9 100644 --- a/net/batman-adv/translation-table.h +++ b/net/batman-adv/translation-table.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich, Antonio Quartulli * diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 5ffcb45ac6ff..a637458205d1 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h index a74df33f446d..ef5867f49824 100644 --- a/net/batman-adv/tvlv.h +++ b/net/batman-adv/tvlv.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index bb1578410e0c..4a3b8837e1b5 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (C) 2007-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * -- cgit v1.2.3 From 2277c7cd75e39783eeb7512a6c35f8b4abbe1039 Mon Sep 17 00:00:00 2001 From: Richard Haines Date: Tue, 13 Feb 2018 20:56:24 +0000 Subject: sctp: Add LSM hooks Add security hooks allowing security modules to exercise access control over SCTP. Signed-off-by: Richard Haines Signed-off-by: Paul Moore --- include/net/sctp/structs.h | 10 ++++++++ include/uapi/linux/sctp.h | 1 + net/sctp/sm_make_chunk.c | 12 +++++++++ net/sctp/sm_statefuns.c | 18 ++++++++++++++ net/sctp/socket.c | 62 +++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 102 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index ead5fcedc283..7a23896cddc4 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1318,6 +1318,16 @@ struct sctp_endpoint { reconf_enable:1; __u8 strreset_enable; + + /* Security identifiers from incoming (INIT). These are set by + * security_sctp_assoc_request(). These will only be used by + * SCTP TCP type sockets and peeled off connections as they + * cause a new socket to be generated. security_sctp_sk_clone() + * will then plug these into the new socket. + */ + + u32 secid; + u32 peer_secid; }; /* Recover the outter endpoint structure. */ diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 4c4db14786bd..64736edd6726 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -126,6 +126,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_STREAM_SCHEDULER 123 #define SCTP_STREAM_SCHEDULER_VALUE 124 #define SCTP_INTERLEAVING_SUPPORTED 125 +#define SCTP_SENDMSG_CONNECT 126 /* PR-SCTP policies */ #define SCTP_PR_SCTP_NONE 0x0000 diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index d01475f5f710..70274ae5ac6f 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3071,6 +3071,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, if (af->is_any(&addr)) memcpy(&addr, &asconf->source, sizeof(addr)); + if (security_sctp_bind_connect(asoc->ep->base.sk, + SCTP_PARAM_ADD_IP, + (struct sockaddr *)&addr, + af->sockaddr_len)) + return SCTP_ERROR_REQ_REFUSED; + /* ADDIP 4.3 D9) If an endpoint receives an ADD IP address * request and does not have the local resources to add this * new address to the association, it MUST return an Error @@ -3137,6 +3143,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, if (af->is_any(&addr)) memcpy(&addr.v4, sctp_source(asconf), sizeof(addr)); + if (security_sctp_bind_connect(asoc->ep->base.sk, + SCTP_PARAM_SET_PRIMARY, + (struct sockaddr *)&addr, + af->sockaddr_len)) + return SCTP_ERROR_REQ_REFUSED; + peer = sctp_assoc_lookup_paddr(asoc, &addr); if (!peer) return SCTP_ERROR_DNS_FAILED; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index eb7905ffe5f2..42659ab68c38 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -321,6 +321,11 @@ enum sctp_disposition sctp_sf_do_5_1B_init(struct net *net, struct sctp_packet *packet; int len; + /* Update socket peer label if first association. */ + if (security_sctp_assoc_request((struct sctp_endpoint *)ep, + chunk->skb)) + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + /* 6.10 Bundling * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. @@ -908,6 +913,9 @@ enum sctp_disposition sctp_sf_do_5_1E_ca(struct net *net, */ sctp_add_cmd_sf(commands, SCTP_CMD_INIT_COUNTER_RESET, SCTP_NULL()); + /* Set peer label for connection. */ + security_inet_conn_established(ep->base.sk, chunk->skb); + /* RFC 2960 5.1 Normal Establishment of an Association * * E) Upon reception of the COOKIE ACK, endpoint "A" will move @@ -1436,6 +1444,11 @@ static enum sctp_disposition sctp_sf_do_unexpected_init( struct sctp_packet *packet; int len; + /* Update socket peer label if first association. */ + if (security_sctp_assoc_request((struct sctp_endpoint *)ep, + chunk->skb)) + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + /* 6.10 Bundling * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. @@ -2106,6 +2119,11 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook( } } + /* Update socket peer label if first association. */ + if (security_sctp_assoc_request((struct sctp_endpoint *)ep, + chunk->skb)) + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + /* Set temp so that it won't be added into hashtable */ new_asoc->temp = 1; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index eb55c63d1990..73b34a6b5b09 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1043,6 +1043,12 @@ static int sctp_setsockopt_bindx(struct sock *sk, /* Do the work. */ switch (op) { case SCTP_BINDX_ADD_ADDR: + /* Allow security module to validate bindx addresses. */ + err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_BINDX_ADD, + (struct sockaddr *)kaddrs, + addrs_size); + if (err) + goto out; err = sctp_bindx_add(sk, kaddrs, addrcnt); if (err) goto out; @@ -1252,6 +1258,7 @@ static int __sctp_connect(struct sock *sk, if (assoc_id) *assoc_id = asoc->assoc_id; + err = sctp_wait_for_connect(asoc, &timeo); /* Note: the asoc may be freed after the return of * sctp_wait_for_connect. @@ -1347,7 +1354,16 @@ static int __sctp_setsockopt_connectx(struct sock *sk, if (unlikely(IS_ERR(kaddrs))) return PTR_ERR(kaddrs); + /* Allow security module to validate connectx addresses. */ + err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_CONNECTX, + (struct sockaddr *)kaddrs, + addrs_size); + if (err) + goto out_free; + err = __sctp_connect(sk, kaddrs, addrs_size, assoc_id); + +out_free: kvfree(kaddrs); return err; @@ -1615,6 +1631,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) struct sctp_transport *transport, *chunk_tp; struct sctp_chunk *chunk; union sctp_addr to; + struct sctp_af *af; struct sockaddr *msg_name = NULL; struct sctp_sndrcvinfo default_sinfo; struct sctp_sndrcvinfo *sinfo; @@ -1844,6 +1861,24 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) } scope = sctp_scope(&to); + + /* Label connection socket for first association 1-to-many + * style for client sequence socket()->sendmsg(). This + * needs to be done before sctp_assoc_add_peer() as that will + * set up the initial packet that needs to account for any + * security ip options (CIPSO/CALIPSO) added to the packet. + */ + af = sctp_get_af_specific(to.sa.sa_family); + if (!af) { + err = -EINVAL; + goto out_unlock; + } + err = security_sctp_bind_connect(sk, SCTP_SENDMSG_CONNECT, + (struct sockaddr *)&to, + af->sockaddr_len); + if (err < 0) + goto out_unlock; + new_asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL); if (!new_asoc) { err = -ENOMEM; @@ -2909,6 +2944,8 @@ static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval, { struct sctp_prim prim; struct sctp_transport *trans; + struct sctp_af *af; + int err; if (optlen != sizeof(struct sctp_prim)) return -EINVAL; @@ -2916,6 +2953,17 @@ static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval, if (copy_from_user(&prim, optval, sizeof(struct sctp_prim))) return -EFAULT; + /* Allow security module to validate address but need address len. */ + af = sctp_get_af_specific(prim.ssp_addr.ss_family); + if (!af) + return -EINVAL; + + err = security_sctp_bind_connect(sk, SCTP_PRIMARY_ADDR, + (struct sockaddr *)&prim.ssp_addr, + af->sockaddr_len); + if (err) + return err; + trans = sctp_addr_id2transport(sk, &prim.ssp_addr, prim.ssp_assoc_id); if (!trans) return -EINVAL; @@ -3247,6 +3295,13 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva if (!sctp_assoc_lookup_laddr(asoc, (union sctp_addr *)&prim.sspp_addr)) return -EADDRNOTAVAIL; + /* Allow security module to validate address. */ + err = security_sctp_bind_connect(sk, SCTP_SET_PEER_PRIMARY_ADDR, + (struct sockaddr *)&prim.sspp_addr, + af->sockaddr_len); + if (err) + return err; + /* Create an ASCONF chunk with SET_PRIMARY parameter */ chunk = sctp_make_asconf_set_prim(asoc, (union sctp_addr *)&prim.sspp_addr); @@ -8346,6 +8401,8 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk, { struct inet_sock *inet = inet_sk(sk); struct inet_sock *newinet; + struct sctp_sock *sp = sctp_sk(sk); + struct sctp_endpoint *ep = sp->ep; newsk->sk_type = sk->sk_type; newsk->sk_bound_dev_if = sk->sk_bound_dev_if; @@ -8388,7 +8445,10 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk, if (newsk->sk_flags & SK_FLAGS_TIMESTAMP) net_enable_timestamp(); - security_sk_clone(sk, newsk); + /* Set newsk security attributes from orginal sk and connection + * security attribute from ep. + */ + security_sctp_sk_clone(ep, sk, newsk); } static inline void sctp_copy_descendant(struct sock *sk_to, -- cgit v1.2.3 From a163dc22d515d17844435c8217ff66193d35b3fa Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 24 Jan 2018 12:21:37 +0100 Subject: batman-adv: always assume 2-byte packet alignment NIC drivers generally try to ensure that the "network header" is aligned to a 4-byte boundary. This is not always possible: When Ethernet frames are encapsulated in other packets with 4-byte aligned headers, the inner Ethernet header will have 4-byte alignment, and in consequence, the inner network header is aligned to 2, but not to 4 bytes. Most parts of batman-adv only care about 2-byte alignment; in particular, no unaligned accesses occur in performance-critical paths that handle actual payload data. This is not true for OGM handling: the seqno and crc fields are accessed as 32-bit values. To avoid these unaligned accesses, this patch reduces the expected packet alignment to 2 bytes for all of batadv's packet types. As no unaligned accesses existed on the performance-critical paths anyways, this chance does have any (positive or negative) effect on performance, but it still makes sense to avoid these accesses to prevent log noise when examining other unaligned accesses in the kernel while batman-adv is active. Signed-off-by: Matthias Schiffer Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- include/uapi/linux/batadv_packet.h | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/batadv_packet.h b/include/uapi/linux/batadv_packet.h index daefd7283896..894d8d2f713d 100644 --- a/include/uapi/linux/batadv_packet.h +++ b/include/uapi/linux/batadv_packet.h @@ -196,8 +196,6 @@ struct batadv_bla_claim_dst { __be16 group; /* group id */ }; -#pragma pack() - /** * struct batadv_ogm_packet - ogm (routing protocol) packet * @packet_type: batman-adv packet type, part of the general header @@ -222,9 +220,6 @@ struct batadv_ogm_packet { __u8 reserved; __u8 tq; __be16 tvlv_len; - /* __packed is not needed as the struct size is divisible by 4, - * and the largest data type in this struct has a size of 4. - */ }; #define BATADV_OGM_HLEN sizeof(struct batadv_ogm_packet) @@ -249,9 +244,6 @@ struct batadv_ogm2_packet { __u8 orig[ETH_ALEN]; __be16 tvlv_len; __be32 throughput; - /* __packed is not needed as the struct size is divisible by 4, - * and the largest data type in this struct has a size of 4. - */ }; #define BATADV_OGM2_HLEN sizeof(struct batadv_ogm2_packet) @@ -405,7 +397,6 @@ struct batadv_icmp_packet_rr { * misalignment of the payload after the ethernet header. It may also lead to * leakage of information when the padding it not initialized before sending. */ -#pragma pack(2) /** * struct batadv_unicast_packet - unicast packet for network payload @@ -533,8 +524,6 @@ struct batadv_coded_packet { __be16 coded_len; }; -#pragma pack() - /** * struct batadv_unicast_tvlv_packet - generic unicast packet with tvlv payload * @packet_type: batman-adv packet type, part of the general header @@ -641,4 +630,6 @@ struct batadv_tvlv_mcast_data { __u8 reserved[3]; }; +#pragma pack() + #endif /* _UAPI_LINUX_BATADV_PACKET_H_ */ -- cgit v1.2.3 From 243ac21035176ac9692c1308a9f3b8f6a4e5d733 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 20 Feb 2018 07:30:22 -0600 Subject: ipmi: Add or fix SPDX-License-Identifier in all files And get rid of the license text that is no longer necessary. Signed-off-by: Corey Minyard Cc: Kees Cook Cc: Alistair Popple Cc: Jeremy Kerr Cc: Joel Stanley Cc: Rocky Craig --- drivers/char/ipmi/bt-bmc.c | 6 +----- drivers/char/ipmi/ipmi_bt_sm.c | 22 ++-------------------- drivers/char/ipmi/ipmi_devintf.c | 22 +--------------------- drivers/char/ipmi/ipmi_dmi.c | 2 +- drivers/char/ipmi/ipmi_dmi.h | 2 +- drivers/char/ipmi/ipmi_kcs_sm.c | 22 +--------------------- drivers/char/ipmi/ipmi_msghandler.c | 22 +--------------------- drivers/char/ipmi/ipmi_powernv.c | 6 +----- drivers/char/ipmi/ipmi_poweroff.c | 22 +--------------------- drivers/char/ipmi/ipmi_si.h | 1 + drivers/char/ipmi/ipmi_si_hardcode.c | 1 + drivers/char/ipmi/ipmi_si_hotmod.c | 1 + drivers/char/ipmi/ipmi_si_intf.c | 22 +--------------------- drivers/char/ipmi/ipmi_si_mem_io.c | 1 + drivers/char/ipmi/ipmi_si_parisc.c | 1 + drivers/char/ipmi/ipmi_si_pci.c | 1 + drivers/char/ipmi/ipmi_si_platform.c | 1 + drivers/char/ipmi/ipmi_si_port_io.c | 1 + drivers/char/ipmi/ipmi_si_sm.h | 22 +--------------------- drivers/char/ipmi/ipmi_smic_sm.c | 24 ++---------------------- drivers/char/ipmi/ipmi_ssif.c | 6 +----- drivers/char/ipmi/ipmi_watchdog.c | 22 +--------------------- include/linux/ipmi-fru.h | 3 +-- include/linux/ipmi.h | 21 +-------------------- include/linux/ipmi_smi.h | 21 +-------------------- include/uapi/linux/ipmi.h | 20 -------------------- include/uapi/linux/ipmi_msgdefs.h | 20 -------------------- 27 files changed, 27 insertions(+), 288 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c index c95b93b7598b..40b9927c072c 100644 --- a/drivers/char/ipmi/bt-bmc.c +++ b/drivers/char/ipmi/bt-bmc.c @@ -1,10 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2015-2016, IBM Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. */ #include diff --git a/drivers/char/ipmi/ipmi_bt_sm.c b/drivers/char/ipmi/ipmi_bt_sm.c index feafdab734ae..fd4ea8d87d4b 100644 --- a/drivers/char/ipmi/ipmi_bt_sm.c +++ b/drivers/char/ipmi/ipmi_bt_sm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_bt_sm.c * @@ -5,26 +6,7 @@ * of the driver architecture at http://sourceforge.net/projects/openipmi * * Author: Rocky Craig - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ + */ #include /* For printk. */ #include diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 5f1bc9174735..8ecfd47806fa 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_devintf.c * @@ -8,27 +9,6 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c index f1df63bc859a..e2c143861b1e 100644 --- a/drivers/char/ipmi/ipmi_dmi.c +++ b/drivers/char/ipmi/ipmi_dmi.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0+ /* * A hack to create a platform device from a DMI entry. This will * allow autoloading of the IPMI drive based on SMBIOS entries. diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h index 6c21018e3668..8d2b094db8e6 100644 --- a/drivers/char/ipmi/ipmi_dmi.h +++ b/drivers/char/ipmi/ipmi_dmi.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * DMI defines for use by IPMI */ diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c index 1da61af7f576..f4ea9f47230a 100644 --- a/drivers/char/ipmi/ipmi_kcs_sm.c +++ b/drivers/char/ipmi/ipmi_kcs_sm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_kcs_sm.c * @@ -8,27 +9,6 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ /* diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index e0b0d7e2d976..361148938801 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_msghandler.c * @@ -8,27 +9,6 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c index bcf493d8e238..e96500372ce2 100644 --- a/drivers/char/ipmi/ipmi_powernv.c +++ b/drivers/char/ipmi/ipmi_powernv.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * PowerNV OPAL IPMI driver * * Copyright 2014 IBM Corp. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. */ #define pr_fmt(fmt) "ipmi-powernv: " fmt diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 38e6af1c8e38..07fa366bc8f0 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_poweroff.c * @@ -9,27 +10,6 @@ * source@mvista.com * * Copyright 2002,2004 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h index 17ce5f7b89ab..52f6152d1fcb 100644 --- a/drivers/char/ipmi/ipmi_si.h +++ b/drivers/char/ipmi/ipmi_si.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * ipmi_si.h * diff --git a/drivers/char/ipmi/ipmi_si_hardcode.c b/drivers/char/ipmi/ipmi_si_hardcode.c index fa9a4780de36..10219f24546b 100644 --- a/drivers/char/ipmi/ipmi_si_hardcode.c +++ b/drivers/char/ipmi/ipmi_si_hardcode.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ #include #include "ipmi_si.h" diff --git a/drivers/char/ipmi/ipmi_si_hotmod.c b/drivers/char/ipmi/ipmi_si_hotmod.c index fc03b9be2f3d..a98ca42a50b1 100644 --- a/drivers/char/ipmi/ipmi_si_hotmod.c +++ b/drivers/char/ipmi/ipmi_si_hotmod.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_si_hotmod.c * diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 6768cb2dd740..5141ccf0b958 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_si.c * @@ -10,27 +11,6 @@ * * Copyright 2002 MontaVista Software Inc. * Copyright 2006 IBM Corp., Christian Krafft - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ /* diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c index 8796396ecd0f..1b869d530884 100644 --- a/drivers/char/ipmi/ipmi_si_mem_io.c +++ b/drivers/char/ipmi/ipmi_si_mem_io.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ #include #include "ipmi_si.h" diff --git a/drivers/char/ipmi/ipmi_si_parisc.c b/drivers/char/ipmi/ipmi_si_parisc.c index 6b10f0e18a95..f3c99820f564 100644 --- a/drivers/char/ipmi/ipmi_si_parisc.c +++ b/drivers/char/ipmi/ipmi_si_parisc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ #include #include /* for register_parisc_driver() stuff */ diff --git a/drivers/char/ipmi/ipmi_si_pci.c b/drivers/char/ipmi/ipmi_si_pci.c index ad4e20b94c08..b1c055540b26 100644 --- a/drivers/char/ipmi/ipmi_si_pci.c +++ b/drivers/char/ipmi/ipmi_si_pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_si_pci.c * diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c index f4214870d726..3d45bf1ee5bc 100644 --- a/drivers/char/ipmi/ipmi_si_platform.c +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_si_platform.c * diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c index e5ce174fbeeb..ef6dffcea9fa 100644 --- a/drivers/char/ipmi/ipmi_si_port_io.c +++ b/drivers/char/ipmi/ipmi_si_port_io.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ #include #include "ipmi_si.h" diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h index aa8d88ab4433..aaddf047d923 100644 --- a/drivers/char/ipmi/ipmi_si_sm.h +++ b/drivers/char/ipmi/ipmi_si_sm.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * ipmi_si_sm.h * @@ -11,27 +12,6 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include diff --git a/drivers/char/ipmi/ipmi_smic_sm.c b/drivers/char/ipmi/ipmi_smic_sm.c index 8f7c73ff58f2..466a5aac5298 100644 --- a/drivers/char/ipmi/ipmi_smic_sm.c +++ b/drivers/char/ipmi/ipmi_smic_sm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_smic_sm.c * @@ -18,28 +19,7 @@ * copyright notice: * (c) Copyright 2001 Grant Grundler (c) Copyright * 2001 Hewlett-Packard Company - * - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ + */ #include /* For printk. */ #include diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index f929e72bdac8..9d3b0fa27560 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_ssif.c * @@ -13,11 +14,6 @@ * * Copyright 2003 Intel Corporation * Copyright 2005 MontaVista Software - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ /* diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index a58acdcf7414..22bc287eac2d 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_watchdog.c * @@ -8,27 +9,6 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include diff --git a/include/linux/ipmi-fru.h b/include/linux/ipmi-fru.h index 4d3a76380e32..05c9422624c6 100644 --- a/include/linux/ipmi-fru.h +++ b/include/linux/ipmi-fru.h @@ -1,9 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2012 CERN (www.cern.ch) * Author: Alessandro Rubini * - * Released according to the GNU GPL, version 2 or any later version. - * * This work is part of the White Rabbit project, a research effort led * by CERN, the European Institute for Nuclear Research. */ diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h index f4ffacf4fe9d..8b0626cec980 100644 --- a/include/linux/ipmi.h +++ b/include/linux/ipmi.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * ipmi.h * @@ -9,26 +10,6 @@ * * Copyright 2002 MontaVista Software Inc. * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __LINUX_IPMI_H #define __LINUX_IPMI_H diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index 5be51281e14d..af457b5a689e 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * ipmi_smi.h * @@ -9,26 +10,6 @@ * * Copyright 2002 MontaVista Software Inc. * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __LINUX_IPMI_SMI_H diff --git a/include/uapi/linux/ipmi.h b/include/uapi/linux/ipmi.h index b076f7a47407..32d148309b16 100644 --- a/include/uapi/linux/ipmi.h +++ b/include/uapi/linux/ipmi.h @@ -10,26 +10,6 @@ * * Copyright 2002 MontaVista Software Inc. * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _UAPI__LINUX_IPMI_H diff --git a/include/uapi/linux/ipmi_msgdefs.h b/include/uapi/linux/ipmi_msgdefs.h index 17f349459587..c2b23a9fdf3d 100644 --- a/include/uapi/linux/ipmi_msgdefs.h +++ b/include/uapi/linux/ipmi_msgdefs.h @@ -10,26 +10,6 @@ * * Copyright 2002 MontaVista Software Inc. * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __LINUX_IPMI_MSGDEFS_H -- cgit v1.2.3 From 401910db4cd425899832a093539222b6174f92a2 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Tue, 27 Feb 2018 09:52:43 -0800 Subject: rds: deliver zerocopy completion notification with data This commit is an optimization over commit 01883eda72bd ("rds: support for zcopy completion notification") for PF_RDS sockets. RDS applications are predominantly request-response transactions, so it is more efficient to reduce the number of system calls and have zerocopy completion notification delivered as ancillary data on the POLLIN channel. Cookies are passed up as ancillary data (at level SOL_RDS) in a struct rds_zcopy_cookies when the returned value of recvmsg() is greater than, or equal to, 0. A max of RDS_MAX_ZCOOKIES may be passed with each message. This commit removes support for zerocopy completion notification on MSG_ERRQUEUE for PF_RDS sockets. Signed-off-by: Sowmini Varadhan Acked-by: Willem de Bruijn Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- include/uapi/linux/errqueue.h | 2 -- include/uapi/linux/rds.h | 7 +++++++ net/rds/af_rds.c | 7 +++++-- net/rds/message.c | 38 ++++++++++++++++---------------------- net/rds/rds.h | 2 ++ net/rds/recv.c | 31 ++++++++++++++++++++++++++++++- 6 files changed, 60 insertions(+), 27 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/errqueue.h b/include/uapi/linux/errqueue.h index 28812eda4209..dc64cfaf13da 100644 --- a/include/uapi/linux/errqueue.h +++ b/include/uapi/linux/errqueue.h @@ -20,13 +20,11 @@ struct sock_extended_err { #define SO_EE_ORIGIN_ICMP6 3 #define SO_EE_ORIGIN_TXSTATUS 4 #define SO_EE_ORIGIN_ZEROCOPY 5 -#define SO_EE_ORIGIN_ZCOOKIE 6 #define SO_EE_ORIGIN_TIMESTAMPING SO_EE_ORIGIN_TXSTATUS #define SO_EE_OFFENDER(ee) ((struct sockaddr*)((ee)+1)) #define SO_EE_CODE_ZEROCOPY_COPIED 1 -#define SO_EE_ORIGIN_MAX_ZCOOKIES 8 /** * struct scm_timestamping - timestamps exposed through cmsg diff --git a/include/uapi/linux/rds.h b/include/uapi/linux/rds.h index 12e3bca32cad..a66b213de3d7 100644 --- a/include/uapi/linux/rds.h +++ b/include/uapi/linux/rds.h @@ -104,6 +104,7 @@ #define RDS_CMSG_MASKED_ATOMIC_CSWP 9 #define RDS_CMSG_RXPATH_LATENCY 11 #define RDS_CMSG_ZCOPY_COOKIE 12 +#define RDS_CMSG_ZCOPY_COMPLETION 13 #define RDS_INFO_FIRST 10000 #define RDS_INFO_COUNTERS 10000 @@ -317,6 +318,12 @@ struct rds_rdma_notify { #define RDS_RDMA_DROPPED 3 #define RDS_RDMA_OTHER_ERROR 4 +#define RDS_MAX_ZCOOKIES 8 +struct rds_zcopy_cookies { + __u32 num; + __u32 cookies[RDS_MAX_ZCOOKIES]; +}; + /* * Common set of flags for all RDMA related structs */ diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index a937f18896ae..f7126108a811 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -77,6 +77,7 @@ static int rds_release(struct socket *sock) rds_send_drop_to(rs, NULL); rds_rdma_drop_keys(rs); rds_notify_queue_get(rs, NULL); + __skb_queue_purge(&rs->rs_zcookie_queue); spin_lock_bh(&rds_sock_lock); list_del_init(&rs->rs_item); @@ -144,7 +145,7 @@ static int rds_getname(struct socket *sock, struct sockaddr *uaddr, * - to signal that a previously congested destination may have become * uncongested * - A notification has been queued to the socket (this can be a congestion - * update, or a RDMA completion). + * update, or a RDMA completion, or a MSG_ZEROCOPY completion). * * EPOLLOUT is asserted if there is room on the send queue. This does not mean * however, that the next sendmsg() call will succeed. If the application tries @@ -178,7 +179,8 @@ static __poll_t rds_poll(struct file *file, struct socket *sock, spin_unlock(&rs->rs_lock); } if (!list_empty(&rs->rs_recv_queue) || - !list_empty(&rs->rs_notify_queue)) + !list_empty(&rs->rs_notify_queue) || + !skb_queue_empty(&rs->rs_zcookie_queue)) mask |= (EPOLLIN | EPOLLRDNORM); if (rs->rs_snd_bytes < rds_sk_sndbuf(rs)) mask |= (EPOLLOUT | EPOLLWRNORM); @@ -513,6 +515,7 @@ static int __rds_create(struct socket *sock, struct sock *sk, int protocol) INIT_LIST_HEAD(&rs->rs_recv_queue); INIT_LIST_HEAD(&rs->rs_notify_queue); INIT_LIST_HEAD(&rs->rs_cong_list); + skb_queue_head_init(&rs->rs_zcookie_queue); spin_lock_init(&rs->rs_rdma_lock); rs->rs_rdma_keys = RB_ROOT; rs->rs_rx_traces = 0; diff --git a/net/rds/message.c b/net/rds/message.c index 651834513481..116cf87ccb89 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -58,32 +58,26 @@ EXPORT_SYMBOL_GPL(rds_message_addref); static inline bool skb_zcookie_add(struct sk_buff *skb, u32 cookie) { - struct sock_exterr_skb *serr = SKB_EXT_ERR(skb); - int ncookies; - u32 *ptr; + struct rds_zcopy_cookies *ck = (struct rds_zcopy_cookies *)skb->cb; + int ncookies = ck->num; - if (serr->ee.ee_origin != SO_EE_ORIGIN_ZCOOKIE) + if (ncookies == RDS_MAX_ZCOOKIES) return false; - ncookies = serr->ee.ee_data; - if (ncookies == SO_EE_ORIGIN_MAX_ZCOOKIES) - return false; - ptr = skb_put(skb, sizeof(u32)); - *ptr = cookie; - serr->ee.ee_data = ++ncookies; + ck->cookies[ncookies] = cookie; + ck->num = ++ncookies; return true; } static void rds_rm_zerocopy_callback(struct rds_sock *rs, struct rds_znotifier *znotif) { - struct sock *sk = rds_rs_to_sk(rs); struct sk_buff *skb, *tail; - struct sock_exterr_skb *serr; unsigned long flags; struct sk_buff_head *q; u32 cookie = znotif->z_cookie; + struct rds_zcopy_cookies *ck; - q = &sk->sk_error_queue; + q = &rs->rs_zcookie_queue; spin_lock_irqsave(&q->lock, flags); tail = skb_peek_tail(q); @@ -91,22 +85,19 @@ static void rds_rm_zerocopy_callback(struct rds_sock *rs, spin_unlock_irqrestore(&q->lock, flags); mm_unaccount_pinned_pages(&znotif->z_mmp); consume_skb(rds_skb_from_znotifier(znotif)); - sk->sk_error_report(sk); + /* caller invokes rds_wake_sk_sleep() */ return; } skb = rds_skb_from_znotifier(znotif); - serr = SKB_EXT_ERR(skb); - memset(&serr->ee, 0, sizeof(serr->ee)); - serr->ee.ee_errno = 0; - serr->ee.ee_origin = SO_EE_ORIGIN_ZCOOKIE; - serr->ee.ee_info = 0; + ck = (struct rds_zcopy_cookies *)skb->cb; + memset(ck, 0, sizeof(*ck)); WARN_ON(!skb_zcookie_add(skb, cookie)); __skb_queue_tail(q, skb); spin_unlock_irqrestore(&q->lock, flags); - sk->sk_error_report(sk); + /* caller invokes rds_wake_sk_sleep() */ mm_unaccount_pinned_pages(&znotif->z_mmp); } @@ -129,6 +120,7 @@ static void rds_message_purge(struct rds_message *rm) if (rm->data.op_mmp_znotifier) { zcopy = true; rds_rm_zerocopy_callback(rs, rm->data.op_mmp_znotifier); + rds_wake_sk_sleep(rs); rm->data.op_mmp_znotifier = NULL; } sock_put(rds_rs_to_sk(rs)); @@ -362,10 +354,12 @@ int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from, int total_copied = 0; struct sk_buff *skb; - skb = alloc_skb(SO_EE_ORIGIN_MAX_ZCOOKIES * sizeof(u32), - GFP_KERNEL); + skb = alloc_skb(0, GFP_KERNEL); if (!skb) return -ENOMEM; + BUILD_BUG_ON(sizeof(skb->cb) < + max_t(int, sizeof(struct rds_znotifier), + sizeof(struct rds_zcopy_cookies))); rm->data.op_mmp_znotifier = RDS_ZCOPY_SKB(skb); if (mm_account_pinned_pages(&rm->data.op_mmp_znotifier->z_mmp, length)) { diff --git a/net/rds/rds.h b/net/rds/rds.h index 31cd38852050..33b16353d8f3 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -603,6 +603,8 @@ struct rds_sock { /* Socket receive path trace points*/ u8 rs_rx_traces; u8 rs_rx_trace[RDS_MSG_RX_DGRAM_TRACE_MAX]; + + struct sk_buff_head rs_zcookie_queue; }; static inline struct rds_sock *rds_sk_to_rs(const struct sock *sk) diff --git a/net/rds/recv.c b/net/rds/recv.c index b080961464df..d50747725221 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -577,6 +577,32 @@ out: return ret; } +static bool rds_recvmsg_zcookie(struct rds_sock *rs, struct msghdr *msg) +{ + struct sk_buff *skb; + struct sk_buff_head *q = &rs->rs_zcookie_queue; + struct rds_zcopy_cookies *done; + + if (!msg->msg_control) + return false; + + if (!sock_flag(rds_rs_to_sk(rs), SOCK_ZEROCOPY) || + msg->msg_controllen < CMSG_SPACE(sizeof(*done))) + return false; + + skb = skb_dequeue(q); + if (!skb) + return false; + done = (struct rds_zcopy_cookies *)skb->cb; + if (put_cmsg(msg, SOL_RDS, RDS_CMSG_ZCOPY_COMPLETION, sizeof(*done), + done)) { + skb_queue_head(q, skb); + return false; + } + consume_skb(skb); + return true; +} + int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int msg_flags) { @@ -611,7 +637,9 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, if (!rds_next_incoming(rs, &inc)) { if (nonblock) { - ret = -EAGAIN; + bool reaped = rds_recvmsg_zcookie(rs, msg); + + ret = reaped ? 0 : -EAGAIN; break; } @@ -660,6 +688,7 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, ret = -EFAULT; goto out; } + rds_recvmsg_zcookie(rs, msg); rds_stats_inc(s_recv_delivered); -- cgit v1.2.3 From bfff4862653bb96001ab57c1edd6d03f48e5f035 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Wed, 28 Feb 2018 22:40:16 -0500 Subject: net: fib_rules: support for match on ip_proto, sport and dport uapi for ip_proto, sport and dport range match in fib rules. Signed-off-by: Roopa Prabhu Acked-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/net/fib_rules.h | 36 ++++++++++++++++- include/uapi/linux/fib_rules.h | 8 ++++ net/core/fib_rules.c | 92 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 133 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index b3d216249240..6dd0a00653ae 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -27,7 +27,7 @@ struct fib_rule { u8 action; u8 l3mdev; u8 proto; - /* 1 byte hole, try to use */ + u8 ip_proto; u32 target; __be64 tun_id; struct fib_rule __rcu *ctarget; @@ -40,6 +40,8 @@ struct fib_rule { char iifname[IFNAMSIZ]; char oifname[IFNAMSIZ]; struct fib_kuid_range uid_range; + struct fib_rule_port_range sport_range; + struct fib_rule_port_range dport_range; struct rcu_head rcu; }; @@ -144,6 +146,38 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) return frh->table; } +static inline bool fib_rule_port_range_set(const struct fib_rule_port_range *range) +{ + return range->start != 0 && range->end != 0; +} + +static inline bool fib_rule_port_inrange(const struct fib_rule_port_range *a, + __be16 port) +{ + return ntohs(port) >= a->start && + ntohs(port) <= a->end; +} + +static inline bool fib_rule_port_range_valid(const struct fib_rule_port_range *a) +{ + return a->start != 0 && a->end != 0 && a->end < 0xffff && + a->start <= a->end; +} + +static inline bool fib_rule_port_range_compare(struct fib_rule_port_range *a, + struct fib_rule_port_range *b) +{ + return a->start == b->start && + a->end == b->end; +} + +static inline bool fib_rule_requires_fldissect(struct fib_rule *rule) +{ + return rule->ip_proto || + fib_rule_port_range_set(&rule->sport_range) || + fib_rule_port_range_set(&rule->dport_range); +} + struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, struct net *); void fib_rules_unregister(struct fib_rules_ops *); diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index 77d90ae38114..232df14e1287 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -35,6 +35,11 @@ struct fib_rule_uid_range { __u32 end; }; +struct fib_rule_port_range { + __u16 start; + __u16 end; +}; + enum { FRA_UNSPEC, FRA_DST, /* destination address */ @@ -59,6 +64,9 @@ enum { FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ FRA_UID_RANGE, /* UID range */ FRA_PROTOCOL, /* Originator of the rule */ + FRA_IP_PROTO, /* ip proto */ + FRA_SPORT_RANGE, /* sport */ + FRA_DPORT_RANGE, /* dport */ __FRA_MAX }; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index a6aea805a0a2..f6f04fc0f629 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -33,6 +33,10 @@ bool fib_rule_matchall(const struct fib_rule *rule) if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) return false; + if (fib_rule_port_range_set(&rule->sport_range)) + return false; + if (fib_rule_port_range_set(&rule->dport_range)) + return false; return true; } EXPORT_SYMBOL_GPL(fib_rule_matchall); @@ -221,6 +225,26 @@ static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range) return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out); } +static int nla_get_port_range(struct nlattr *pattr, + struct fib_rule_port_range *port_range) +{ + const struct fib_rule_port_range *pr = nla_data(pattr); + + if (!fib_rule_port_range_valid(pr)) + return -EINVAL; + + port_range->start = pr->start; + port_range->end = pr->end; + + return 0; +} + +static int nla_put_port_range(struct sk_buff *skb, int attrtype, + struct fib_rule_port_range *range) +{ + return nla_put(skb, attrtype, sizeof(*range), range); +} + static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, struct flowi *fl, int flags, struct fib_lookup_arg *arg) @@ -425,6 +449,17 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, !uid_eq(r->uid_range.end, rule->uid_range.end)) continue; + if (r->ip_proto != rule->ip_proto) + continue; + + if (!fib_rule_port_range_compare(&r->sport_range, + &rule->sport_range)) + continue; + + if (!fib_rule_port_range_compare(&r->dport_range, + &rule->dport_range)) + continue; + if (!ops->compare(r, frh, tb)) continue; return 1; @@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, rule->uid_range = fib_kuid_range_unset; } + if (tb[FRA_IP_PROTO]) + rule->ip_proto = nla_get_u8(tb[FRA_IP_PROTO]); + + if (tb[FRA_SPORT_RANGE]) { + err = nla_get_port_range(tb[FRA_SPORT_RANGE], + &rule->sport_range); + if (err) + goto errout_free; + } + + if (tb[FRA_DPORT_RANGE]) { + err = nla_get_port_range(tb[FRA_DPORT_RANGE], + &rule->dport_range); + if (err) + goto errout_free; + } + if ((nlh->nlmsg_flags & NLM_F_EXCL) && rule_exists(ops, frh, tb, rule)) { err = -EEXIST; @@ -634,6 +686,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, { struct net *net = sock_net(skb->sk); struct fib_rule_hdr *frh = nlmsg_data(nlh); + struct fib_rule_port_range sprange = {0, 0}; + struct fib_rule_port_range dprange = {0, 0}; struct fib_rules_ops *ops = NULL; struct fib_rule *rule, *r; struct nlattr *tb[FRA_MAX+1]; @@ -667,6 +721,20 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, range = fib_kuid_range_unset; } + if (tb[FRA_SPORT_RANGE]) { + err = nla_get_port_range(tb[FRA_SPORT_RANGE], + &sprange); + if (err) + goto errout; + } + + if (tb[FRA_DPORT_RANGE]) { + err = nla_get_port_range(tb[FRA_DPORT_RANGE], + &dprange); + if (err) + goto errout; + } + list_for_each_entry(rule, &ops->rules_list, list) { if (tb[FRA_PROTOCOL] && (rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) @@ -712,6 +780,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, !uid_eq(rule->uid_range.end, range.end))) continue; + if (tb[FRA_IP_PROTO] && + (rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO]))) + continue; + + if (fib_rule_port_range_set(&sprange) && + !fib_rule_port_range_compare(&rule->sport_range, &sprange)) + continue; + + if (fib_rule_port_range_set(&dprange) && + !fib_rule_port_range_compare(&rule->dport_range, &dprange)) + continue; + if (!ops->compare(rule, frh, tb)) continue; @@ -790,7 +870,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(4) /* FRA_FWMASK */ + nla_total_size_64bit(8) /* FRA_TUN_ID */ + nla_total_size(sizeof(struct fib_kuid_range)) - + nla_total_size(1); /* FRA_PROTOCOL */ + + nla_total_size(1) /* FRA_PROTOCOL */ + + nla_total_size(1) /* FRA_IP_PROTO */ + + nla_total_size(sizeof(struct fib_rule_port_range)) /* FRA_SPORT_RANGE */ + + nla_total_size(sizeof(struct fib_rule_port_range)); /* FRA_DPORT_RANGE */ if (ops->nlmsg_payload) payload += ops->nlmsg_payload(rule); @@ -855,7 +938,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, (rule->l3mdev && nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || (uid_range_set(&rule->uid_range) && - nla_put_uid_range(skb, &rule->uid_range))) + nla_put_uid_range(skb, &rule->uid_range)) || + (fib_rule_port_range_set(&rule->sport_range) && + nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) || + (fib_rule_port_range_set(&rule->dport_range) && + nla_put_port_range(skb, FRA_DPORT_RANGE, &rule->dport_range)) || + (rule->ip_proto && nla_put_u8(skb, FRA_IP_PROTO, rule->ip_proto))) goto nla_put_failure; if (rule->suppress_ifgroup != -1) { -- cgit v1.2.3 From 77a5196a804e34ce5e215ef84d5e1de332e9c529 Mon Sep 17 00:00:00 2001 From: William Tu Date: Thu, 1 Mar 2018 13:49:57 -0800 Subject: gre: add sequence number for collect md mode. Currently GRE sequence number can only be used in native tunnel mode. This patch adds sequence number support for gre collect metadata mode. RFC2890 defines GRE sequence number to be specific to the traffic flow identified by the key. However, this patch does not implement per-key seqno. The sequence number is shared in the same tunnel device. That is, different tunnel keys using the same collect_md tunnel share single sequence number. Signed-off-by: William Tu Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 4 +++- net/ipv4/ip_gre.c | 7 +++++-- net/ipv6/ip6_gre.c | 13 ++++++++----- 4 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index db6bdc375126..2a66769e5875 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -800,6 +800,7 @@ enum bpf_func_id { /* BPF_FUNC_skb_set_tunnel_key flags. */ #define BPF_F_ZERO_CSUM_TX (1ULL << 1) #define BPF_F_DONT_FRAGMENT (1ULL << 2) +#define BPF_F_SEQ_NUMBER (1ULL << 3) /* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and * BPF_FUNC_perf_event_read_value flags. diff --git a/net/core/filter.c b/net/core/filter.c index 0c121adbdbaa..33edfa8372fd 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2991,7 +2991,7 @@ BPF_CALL_4(bpf_skb_set_tunnel_key, struct sk_buff *, skb, struct ip_tunnel_info *info; if (unlikely(flags & ~(BPF_F_TUNINFO_IPV6 | BPF_F_ZERO_CSUM_TX | - BPF_F_DONT_FRAGMENT))) + BPF_F_DONT_FRAGMENT | BPF_F_SEQ_NUMBER))) return -EINVAL; if (unlikely(size != sizeof(struct bpf_tunnel_key))) { switch (size) { @@ -3025,6 +3025,8 @@ BPF_CALL_4(bpf_skb_set_tunnel_key, struct sk_buff *, skb, info->key.tun_flags |= TUNNEL_DONT_FRAGMENT; if (flags & BPF_F_ZERO_CSUM_TX) info->key.tun_flags &= ~TUNNEL_CSUM; + if (flags & BPF_F_SEQ_NUMBER) + info->key.tun_flags |= TUNNEL_SEQ; info->key.tun_id = cpu_to_be64(from->tunnel_id); info->key.tos = from->tunnel_tos; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 0fe1d69b5df4..95fd225f402e 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -522,6 +522,7 @@ err_free_skb: static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev, __be16 proto) { + struct ip_tunnel *tunnel = netdev_priv(dev); struct ip_tunnel_info *tun_info; const struct ip_tunnel_key *key; struct rtable *rt = NULL; @@ -545,9 +546,11 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev, if (gre_handle_offloads(skb, !!(tun_info->key.tun_flags & TUNNEL_CSUM))) goto err_free_rt; - flags = tun_info->key.tun_flags & (TUNNEL_CSUM | TUNNEL_KEY); + flags = tun_info->key.tun_flags & + (TUNNEL_CSUM | TUNNEL_KEY | TUNNEL_SEQ); gre_build_header(skb, tunnel_hlen, flags, proto, - tunnel_id_to_key32(tun_info->key.tun_id), 0); + tunnel_id_to_key32(tun_info->key.tun_id), + (flags | TUNNEL_SEQ) ? htonl(tunnel->o_seqno++) : 0); df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 83c7766c8c75..18a3dfbd0300 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -695,9 +695,6 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb, else fl6->daddr = tunnel->parms.raddr; - if (tunnel->parms.o_flags & TUNNEL_SEQ) - tunnel->o_seqno++; - /* Push GRE header. */ protocol = (dev->type == ARPHRD_ETHER) ? htons(ETH_P_TEB) : proto; @@ -720,14 +717,20 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb, fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL); dsfield = key->tos; - flags = key->tun_flags & (TUNNEL_CSUM | TUNNEL_KEY); + flags = key->tun_flags & + (TUNNEL_CSUM | TUNNEL_KEY | TUNNEL_SEQ); tunnel->tun_hlen = gre_calc_hlen(flags); gre_build_header(skb, tunnel->tun_hlen, flags, protocol, - tunnel_id_to_key32(tun_info->key.tun_id), 0); + tunnel_id_to_key32(tun_info->key.tun_id), + (flags | TUNNEL_SEQ) ? htonl(tunnel->o_seqno++) + : 0); } else { + if (tunnel->parms.o_flags & TUNNEL_SEQ) + tunnel->o_seqno++; + gre_build_header(skb, tunnel->tun_hlen, tunnel->parms.o_flags, protocol, tunnel->parms.o_key, htonl(tunnel->o_seqno)); -- cgit v1.2.3 From 2fe4c22c53fc2e3f35be2cd0033cb3d15ebd41b1 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 27 Feb 2018 08:03:56 -0500 Subject: media: rc: lirc does not use LIRC_CAN_SEND_SCANCODE feature Since commit 02d742f4b209 ("media: lirc: lirc daemon fails to detect raw IR device"), the feature LIRC_CAN_SEND_SCANCODE is no longer used as it tripped up lircd. The ability to send scancodes for IR Tx is implied by LIRC_CAN_SEND_PULSE (i.e. any device that can send can use IR Tx encoders). So, remove LIRC_CAN_SEND_SCANCODE since it never used. This fixes: Documentation/output/lirc.h.rst:6: WARNING: undefined label: lirc-can-send-scancode (if the link has no caption the label must precede a section header As this flag was added for kernel 4.16, let's remove it, while not too late. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/lirc.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/lirc.h b/include/uapi/linux/lirc.h index 4fe580d36e41..f5bf06ecd87d 100644 --- a/include/uapi/linux/lirc.h +++ b/include/uapi/linux/lirc.h @@ -54,7 +54,6 @@ #define LIRC_CAN_SEND_RAW LIRC_MODE2SEND(LIRC_MODE_RAW) #define LIRC_CAN_SEND_PULSE LIRC_MODE2SEND(LIRC_MODE_PULSE) #define LIRC_CAN_SEND_MODE2 LIRC_MODE2SEND(LIRC_MODE_MODE2) -#define LIRC_CAN_SEND_SCANCODE LIRC_MODE2SEND(LIRC_MODE_SCANCODE) #define LIRC_CAN_SEND_LIRCCODE LIRC_MODE2SEND(LIRC_MODE_LIRCCODE) #define LIRC_CAN_SEND_MASK 0x0000003f -- cgit v1.2.3 From 87ecc95d81d951b0984f2eb9c5c118cb68d0dce8 Mon Sep 17 00:00:00 2001 From: Priyaranjan Jha Date: Sun, 4 Mar 2018 10:38:35 -0800 Subject: tcp: add send queue size stat in SCM_TIMESTAMPING_OPT_STATS This patch adds TCP_NLA_SENDQ_SIZE stat into SCM_TIMESTAMPING_OPT_STATS. It reports no. of bytes present in send queue, when timestamp is generated. Signed-off-by: Priyaranjan Jha Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Signed-off-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller --- include/uapi/linux/tcp.h | 1 + net/ipv4/tcp.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index b4a4f64635fa..93bad2128ef6 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -241,6 +241,7 @@ enum { TCP_NLA_MIN_RTT, /* minimum RTT */ TCP_NLA_RECUR_RETRANS, /* Recurring retransmits for the current pkt */ TCP_NLA_DELIVERY_RATE_APP_LMT, /* delivery rate application limited ? */ + TCP_NLA_SNDQ_SIZE, /* Data (bytes) pending in send queue */ }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index a33539798bf6..162ba4227446 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3031,7 +3031,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk) u32 rate; stats = alloc_skb(7 * nla_total_size_64bit(sizeof(u64)) + - 3 * nla_total_size(sizeof(u32)) + + 4 * nla_total_size(sizeof(u32)) + 2 * nla_total_size(sizeof(u8)), GFP_ATOMIC); if (!stats) return NULL; @@ -3061,6 +3061,8 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk) nla_put_u8(stats, TCP_NLA_RECUR_RETRANS, inet_csk(sk)->icsk_retransmits); nla_put_u8(stats, TCP_NLA_DELIVERY_RATE_APP_LMT, !!tp->rate_app_limited); + + nla_put_u32(stats, TCP_NLA_SNDQ_SIZE, tp->write_seq - tp->snd_una); return stats; } -- cgit v1.2.3 From be631892948060f44b1ceee3132be1266932071e Mon Sep 17 00:00:00 2001 From: Priyaranjan Jha Date: Sun, 4 Mar 2018 10:38:36 -0800 Subject: tcp: add ca_state stat in SCM_TIMESTAMPING_OPT_STATS This patch adds TCP_NLA_CA_STATE stat into SCM_TIMESTAMPING_OPT_STATS. It reports ca_state of socket, when timestamp is generated. Signed-off-by: Priyaranjan Jha Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Signed-off-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller --- include/uapi/linux/tcp.h | 1 + net/ipv4/tcp.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 93bad2128ef6..4c0ae0faf7ca 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -242,6 +242,7 @@ enum { TCP_NLA_RECUR_RETRANS, /* Recurring retransmits for the current pkt */ TCP_NLA_DELIVERY_RATE_APP_LMT, /* delivery rate application limited ? */ TCP_NLA_SNDQ_SIZE, /* Data (bytes) pending in send queue */ + TCP_NLA_CA_STATE, /* ca_state of socket */ }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 162ba4227446..fb350f740f69 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3032,7 +3032,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk) stats = alloc_skb(7 * nla_total_size_64bit(sizeof(u64)) + 4 * nla_total_size(sizeof(u32)) + - 2 * nla_total_size(sizeof(u8)), GFP_ATOMIC); + 3 * nla_total_size(sizeof(u8)), GFP_ATOMIC); if (!stats) return NULL; @@ -3063,6 +3063,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk) nla_put_u8(stats, TCP_NLA_DELIVERY_RATE_APP_LMT, !!tp->rate_app_limited); nla_put_u32(stats, TCP_NLA_SNDQ_SIZE, tp->write_seq - tp->snd_una); + nla_put_u8(stats, TCP_NLA_CA_STATE, inet_csk(sk)->icsk_ca_state); return stats; } -- cgit v1.2.3 From 955dc68cb9b23b42999cafe6df3684309bc686c6 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Mon, 5 Mar 2018 11:39:05 +1100 Subject: net/ncsi: Add generic netlink family Add a generic netlink family for NCSI. This supports three commands; NCSI_CMD_PKG_INFO which returns information on packages and their associated channels, NCSI_CMD_SET_INTERFACE which allows a specific package or package/channel combination to be set as the preferred choice, and NCSI_CMD_CLEAR_INTERFACE which clears any preferred setting. Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David S. Miller --- include/uapi/linux/ncsi.h | 115 +++++++++++++ net/ncsi/Makefile | 2 +- net/ncsi/internal.h | 3 + net/ncsi/ncsi-manage.c | 30 +++- net/ncsi/ncsi-netlink.c | 421 ++++++++++++++++++++++++++++++++++++++++++++++ net/ncsi/ncsi-netlink.h | 20 +++ 6 files changed, 586 insertions(+), 5 deletions(-) create mode 100644 include/uapi/linux/ncsi.h create mode 100644 net/ncsi/ncsi-netlink.c create mode 100644 net/ncsi/ncsi-netlink.h (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/ncsi.h b/include/uapi/linux/ncsi.h new file mode 100644 index 000000000000..4c292ecbb748 --- /dev/null +++ b/include/uapi/linux/ncsi.h @@ -0,0 +1,115 @@ +/* + * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __UAPI_NCSI_NETLINK_H__ +#define __UAPI_NCSI_NETLINK_H__ + +/** + * enum ncsi_nl_commands - supported NCSI commands + * + * @NCSI_CMD_UNSPEC: unspecified command to catch errors + * @NCSI_CMD_PKG_INFO: list package and channel attributes. Requires + * NCSI_ATTR_IFINDEX. If NCSI_ATTR_PACKAGE_ID is specified returns the + * specific package and its channels - otherwise a dump request returns + * all packages and their associated channels. + * @NCSI_CMD_SET_INTERFACE: set preferred package and channel combination. + * Requires NCSI_ATTR_IFINDEX and the preferred NCSI_ATTR_PACKAGE_ID and + * optionally the preferred NCSI_ATTR_CHANNEL_ID. + * @NCSI_CMD_CLEAR_INTERFACE: clear any preferred package/channel combination. + * Requires NCSI_ATTR_IFINDEX. + * @NCSI_CMD_MAX: highest command number + */ +enum ncsi_nl_commands { + NCSI_CMD_UNSPEC, + NCSI_CMD_PKG_INFO, + NCSI_CMD_SET_INTERFACE, + NCSI_CMD_CLEAR_INTERFACE, + + __NCSI_CMD_AFTER_LAST, + NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1 +}; + +/** + * enum ncsi_nl_attrs - General NCSI netlink attributes + * + * @NCSI_ATTR_UNSPEC: unspecified attributes to catch errors + * @NCSI_ATTR_IFINDEX: ifindex of network device using NCSI + * @NCSI_ATTR_PACKAGE_LIST: nested array of NCSI_PKG_ATTR attributes + * @NCSI_ATTR_PACKAGE_ID: package ID + * @NCSI_ATTR_CHANNEL_ID: channel ID + * @NCSI_ATTR_MAX: highest attribute number + */ +enum ncsi_nl_attrs { + NCSI_ATTR_UNSPEC, + NCSI_ATTR_IFINDEX, + NCSI_ATTR_PACKAGE_LIST, + NCSI_ATTR_PACKAGE_ID, + NCSI_ATTR_CHANNEL_ID, + + __NCSI_ATTR_AFTER_LAST, + NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1 +}; + +/** + * enum ncsi_nl_pkg_attrs - NCSI netlink package-specific attributes + * + * @NCSI_PKG_ATTR_UNSPEC: unspecified attributes to catch errors + * @NCSI_PKG_ATTR: nested array of package attributes + * @NCSI_PKG_ATTR_ID: package ID + * @NCSI_PKG_ATTR_FORCED: flag signifying a package has been set as preferred + * @NCSI_PKG_ATTR_CHANNEL_LIST: nested array of NCSI_CHANNEL_ATTR attributes + * @NCSI_PKG_ATTR_MAX: highest attribute number + */ +enum ncsi_nl_pkg_attrs { + NCSI_PKG_ATTR_UNSPEC, + NCSI_PKG_ATTR, + NCSI_PKG_ATTR_ID, + NCSI_PKG_ATTR_FORCED, + NCSI_PKG_ATTR_CHANNEL_LIST, + + __NCSI_PKG_ATTR_AFTER_LAST, + NCSI_PKG_ATTR_MAX = __NCSI_PKG_ATTR_AFTER_LAST - 1 +}; + +/** + * enum ncsi_nl_channel_attrs - NCSI netlink channel-specific attributes + * + * @NCSI_CHANNEL_ATTR_UNSPEC: unspecified attributes to catch errors + * @NCSI_CHANNEL_ATTR: nested array of channel attributes + * @NCSI_CHANNEL_ATTR_ID: channel ID + * @NCSI_CHANNEL_ATTR_VERSION_MAJOR: channel major version number + * @NCSI_CHANNEL_ATTR_VERSION_MINOR: channel minor version number + * @NCSI_CHANNEL_ATTR_VERSION_STR: channel version string + * @NCSI_CHANNEL_ATTR_LINK_STATE: channel link state flags + * @NCSI_CHANNEL_ATTR_ACTIVE: channels with this flag are in + * NCSI_CHANNEL_ACTIVE state + * @NCSI_CHANNEL_ATTR_FORCED: flag signifying a channel has been set as + * preferred + * @NCSI_CHANNEL_ATTR_VLAN_LIST: nested array of NCSI_CHANNEL_ATTR_VLAN_IDs + * @NCSI_CHANNEL_ATTR_VLAN_ID: VLAN ID being filtered on this channel + * @NCSI_CHANNEL_ATTR_MAX: highest attribute number + */ +enum ncsi_nl_channel_attrs { + NCSI_CHANNEL_ATTR_UNSPEC, + NCSI_CHANNEL_ATTR, + NCSI_CHANNEL_ATTR_ID, + NCSI_CHANNEL_ATTR_VERSION_MAJOR, + NCSI_CHANNEL_ATTR_VERSION_MINOR, + NCSI_CHANNEL_ATTR_VERSION_STR, + NCSI_CHANNEL_ATTR_LINK_STATE, + NCSI_CHANNEL_ATTR_ACTIVE, + NCSI_CHANNEL_ATTR_FORCED, + NCSI_CHANNEL_ATTR_VLAN_LIST, + NCSI_CHANNEL_ATTR_VLAN_ID, + + __NCSI_CHANNEL_ATTR_AFTER_LAST, + NCSI_CHANNEL_ATTR_MAX = __NCSI_CHANNEL_ATTR_AFTER_LAST - 1 +}; + +#endif /* __UAPI_NCSI_NETLINK_H__ */ diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile index dd12b564f2e7..436ef68331f2 100644 --- a/net/ncsi/Makefile +++ b/net/ncsi/Makefile @@ -1,4 +1,4 @@ # # Makefile for NCSI API # -obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-aen.o ncsi-manage.o +obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-aen.o ncsi-manage.o ncsi-netlink.o diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h index d30f7bd741d0..8da84312cd3b 100644 --- a/net/ncsi/internal.h +++ b/net/ncsi/internal.h @@ -276,6 +276,8 @@ struct ncsi_dev_priv { unsigned int package_num; /* Number of packages */ struct list_head packages; /* List of packages */ struct ncsi_channel *hot_channel; /* Channel was ever active */ + struct ncsi_package *force_package; /* Force a specific package */ + struct ncsi_channel *force_channel; /* Force a specific channel */ struct ncsi_request requests[256]; /* Request table */ unsigned int request_id; /* Last used request ID */ #define NCSI_REQ_START_IDX 1 @@ -318,6 +320,7 @@ extern spinlock_t ncsi_dev_lock; list_for_each_entry_rcu(nc, &np->channels, node) /* Resources */ +u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index); int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data); int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data); int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index); diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c index c989211bbabc..c3695ba0cf94 100644 --- a/net/ncsi/ncsi-manage.c +++ b/net/ncsi/ncsi-manage.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -23,6 +22,7 @@ #include "internal.h" #include "ncsi-pkt.h" +#include "ncsi-netlink.h" LIST_HEAD(ncsi_dev_list); DEFINE_SPINLOCK(ncsi_dev_lock); @@ -38,7 +38,7 @@ static inline int ncsi_filter_size(int table) return sizes[table]; } -static u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index) +u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index) { struct ncsi_channel_filter *ncf; int size; @@ -965,20 +965,37 @@ error: static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) { - struct ncsi_package *np; - struct ncsi_channel *nc, *found, *hot_nc; + struct ncsi_package *np, *force_package; + struct ncsi_channel *nc, *found, *hot_nc, *force_channel; struct ncsi_channel_mode *ncm; unsigned long flags; spin_lock_irqsave(&ndp->lock, flags); hot_nc = ndp->hot_channel; + force_channel = ndp->force_channel; + force_package = ndp->force_package; spin_unlock_irqrestore(&ndp->lock, flags); + /* Force a specific channel whether or not it has link if we have been + * configured to do so + */ + if (force_package && force_channel) { + found = force_channel; + ncm = &found->modes[NCSI_MODE_LINK]; + if (!(ncm->data[2] & 0x1)) + netdev_info(ndp->ndev.dev, + "NCSI: Channel %u forced, but it is link down\n", + found->id); + goto out; + } + /* The search is done once an inactive channel with up * link is found. */ found = NULL; NCSI_FOR_EACH_PACKAGE(ndp, np) { + if (ndp->force_package && np != ndp->force_package) + continue; NCSI_FOR_EACH_CHANNEL(np, nc) { spin_lock_irqsave(&nc->lock, flags); @@ -1594,6 +1611,9 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev, ndp->ptype.dev = dev; dev_add_pack(&ndp->ptype); + /* Set up generic netlink interface */ + ncsi_init_netlink(dev); + return nd; } EXPORT_SYMBOL_GPL(ncsi_register_dev); @@ -1673,6 +1693,8 @@ void ncsi_unregister_dev(struct ncsi_dev *nd) #endif spin_unlock_irqrestore(&ncsi_dev_lock, flags); + ncsi_unregister_netlink(nd->dev); + kfree(ndp); } EXPORT_SYMBOL_GPL(ncsi_unregister_dev); diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c new file mode 100644 index 000000000000..d4201665a580 --- /dev/null +++ b/net/ncsi/ncsi-netlink.c @@ -0,0 +1,421 @@ +/* + * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" +#include "ncsi-netlink.h" + +static struct genl_family ncsi_genl_family; + +static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = { + [NCSI_ATTR_IFINDEX] = { .type = NLA_U32 }, + [NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED }, + [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, + [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, +}; + +static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) +{ + struct ncsi_dev_priv *ndp; + struct net_device *dev; + struct ncsi_dev *nd; + struct ncsi_dev; + + if (!net) + return NULL; + + dev = dev_get_by_index(net, ifindex); + if (!dev) { + pr_err("NCSI netlink: No device for ifindex %u\n", ifindex); + return NULL; + } + + nd = ncsi_find_dev(dev); + ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL; + + dev_put(dev); + return ndp; +} + +static int ncsi_write_channel_info(struct sk_buff *skb, + struct ncsi_dev_priv *ndp, + struct ncsi_channel *nc) +{ + struct nlattr *vid_nest; + struct ncsi_channel_filter *ncf; + struct ncsi_channel_mode *m; + u32 *data; + int i; + + nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id); + m = &nc->modes[NCSI_MODE_LINK]; + nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); + if (nc->state == NCSI_CHANNEL_ACTIVE) + nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); + if (ndp->force_channel == nc) + nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); + + nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); + nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2); + nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name); + + vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST); + if (!vid_nest) + return -ENOMEM; + ncf = nc->filters[NCSI_FILTER_VLAN]; + i = -1; + if (ncf) { + while ((i = find_next_bit((void *)&ncf->bitmap, ncf->total, + i + 1)) < ncf->total) { + data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, i); + /* Uninitialised channels will have 'zero' vlan ids */ + if (!data || !*data) + continue; + nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID, + *(u16 *)data); + } + } + nla_nest_end(skb, vid_nest); + + return 0; +} + +static int ncsi_write_package_info(struct sk_buff *skb, + struct ncsi_dev_priv *ndp, unsigned int id) +{ + struct nlattr *pnest, *cnest, *nest; + struct ncsi_package *np; + struct ncsi_channel *nc; + bool found; + int rc; + + if (id > ndp->package_num) { + netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id); + return -ENODEV; + } + + found = false; + NCSI_FOR_EACH_PACKAGE(ndp, np) { + if (np->id != id) + continue; + pnest = nla_nest_start(skb, NCSI_PKG_ATTR); + if (!pnest) + return -ENOMEM; + nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); + if (ndp->force_package == np) + nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); + cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); + if (!cnest) { + nla_nest_cancel(skb, pnest); + return -ENOMEM; + } + NCSI_FOR_EACH_CHANNEL(np, nc) { + nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR); + if (!nest) { + nla_nest_cancel(skb, cnest); + nla_nest_cancel(skb, pnest); + return -ENOMEM; + } + rc = ncsi_write_channel_info(skb, ndp, nc); + if (rc) { + nla_nest_cancel(skb, nest); + nla_nest_cancel(skb, cnest); + nla_nest_cancel(skb, pnest); + return rc; + } + nla_nest_end(skb, nest); + } + nla_nest_end(skb, cnest); + nla_nest_end(skb, pnest); + found = true; + } + + if (!found) + return -ENODEV; + + return 0; +} + +static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info) +{ + struct ncsi_dev_priv *ndp; + unsigned int package_id; + struct sk_buff *skb; + struct nlattr *attr; + void *hdr; + int rc; + + if (!info || !info->attrs) + return -EINVAL; + + if (!info->attrs[NCSI_ATTR_IFINDEX]) + return -EINVAL; + + if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) + return -EINVAL; + + ndp = ndp_from_ifindex(genl_info_net(info), + nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); + if (!ndp) + return -ENODEV; + + skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, + &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO); + if (!hdr) { + kfree(skb); + return -EMSGSIZE; + } + + package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); + + attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST); + rc = ncsi_write_package_info(skb, ndp, package_id); + + if (rc) { + nla_nest_cancel(skb, attr); + goto err; + } + + nla_nest_end(skb, attr); + + genlmsg_end(skb, hdr); + return genlmsg_reply(skb, info); + +err: + genlmsg_cancel(skb, hdr); + kfree(skb); + return rc; +} + +static int ncsi_pkg_info_all_nl(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct nlattr *attrs[NCSI_ATTR_MAX]; + struct ncsi_package *np, *package; + struct ncsi_dev_priv *ndp; + unsigned int package_id; + struct nlattr *attr; + void *hdr; + int rc; + + rc = genlmsg_parse(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX, + ncsi_genl_policy, NULL); + if (rc) + return rc; + + if (!attrs[NCSI_ATTR_IFINDEX]) + return -EINVAL; + + ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)), + nla_get_u32(attrs[NCSI_ATTR_IFINDEX])); + + if (!ndp) + return -ENODEV; + + package_id = cb->args[0]; + package = NULL; + NCSI_FOR_EACH_PACKAGE(ndp, np) + if (np->id == package_id) + package = np; + + if (!package) + return 0; /* done */ + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO); + if (!hdr) { + rc = -EMSGSIZE; + goto err; + } + + attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST); + rc = ncsi_write_package_info(skb, ndp, package->id); + if (rc) { + nla_nest_cancel(skb, attr); + goto err; + } + + nla_nest_end(skb, attr); + genlmsg_end(skb, hdr); + + cb->args[0] = package_id + 1; + + return skb->len; +err: + genlmsg_cancel(skb, hdr); + return rc; +} + +static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) +{ + struct ncsi_package *np, *package; + struct ncsi_channel *nc, *channel; + u32 package_id, channel_id; + struct ncsi_dev_priv *ndp; + unsigned long flags; + + if (!info || !info->attrs) + return -EINVAL; + + if (!info->attrs[NCSI_ATTR_IFINDEX]) + return -EINVAL; + + if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) + return -EINVAL; + + ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), + nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); + if (!ndp) + return -ENODEV; + + package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); + package = NULL; + + spin_lock_irqsave(&ndp->lock, flags); + + NCSI_FOR_EACH_PACKAGE(ndp, np) + if (np->id == package_id) + package = np; + if (!package) { + /* The user has set a package that does not exist */ + return -ERANGE; + } + + channel = NULL; + if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { + /* Allow any channel */ + channel_id = NCSI_RESERVED_CHANNEL; + } else { + channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); + NCSI_FOR_EACH_CHANNEL(package, nc) + if (nc->id == channel_id) + channel = nc; + } + + if (channel_id != NCSI_RESERVED_CHANNEL && !channel) { + /* The user has set a channel that does not exist on this + * package + */ + netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n", + channel_id); + return -ERANGE; + } + + ndp->force_package = package; + ndp->force_channel = channel; + spin_unlock_irqrestore(&ndp->lock, flags); + + netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n", + package_id, channel_id, + channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : ""); + + /* Bounce the NCSI channel to set changes */ + ncsi_stop_dev(&ndp->ndev); + ncsi_start_dev(&ndp->ndev); + + return 0; +} + +static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) +{ + struct ncsi_dev_priv *ndp; + unsigned long flags; + + if (!info || !info->attrs) + return -EINVAL; + + if (!info->attrs[NCSI_ATTR_IFINDEX]) + return -EINVAL; + + ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), + nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); + if (!ndp) + return -ENODEV; + + /* Clear any override */ + spin_lock_irqsave(&ndp->lock, flags); + ndp->force_package = NULL; + ndp->force_channel = NULL; + spin_unlock_irqrestore(&ndp->lock, flags); + netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); + + /* Bounce the NCSI channel to set changes */ + ncsi_stop_dev(&ndp->ndev); + ncsi_start_dev(&ndp->ndev); + + return 0; +} + +static const struct genl_ops ncsi_ops[] = { + { + .cmd = NCSI_CMD_PKG_INFO, + .policy = ncsi_genl_policy, + .doit = ncsi_pkg_info_nl, + .dumpit = ncsi_pkg_info_all_nl, + .flags = 0, + }, + { + .cmd = NCSI_CMD_SET_INTERFACE, + .policy = ncsi_genl_policy, + .doit = ncsi_set_interface_nl, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NCSI_CMD_CLEAR_INTERFACE, + .policy = ncsi_genl_policy, + .doit = ncsi_clear_interface_nl, + .flags = GENL_ADMIN_PERM, + }, +}; + +static struct genl_family ncsi_genl_family __ro_after_init = { + .name = "NCSI", + .version = 0, + .maxattr = NCSI_ATTR_MAX, + .module = THIS_MODULE, + .ops = ncsi_ops, + .n_ops = ARRAY_SIZE(ncsi_ops), +}; + +int ncsi_init_netlink(struct net_device *dev) +{ + int rc; + + rc = genl_register_family(&ncsi_genl_family); + if (rc) + netdev_err(dev, "ncsi: failed to register netlink family\n"); + + return rc; +} + +int ncsi_unregister_netlink(struct net_device *dev) +{ + int rc; + + rc = genl_unregister_family(&ncsi_genl_family); + if (rc) + netdev_err(dev, "ncsi: failed to unregister netlink family\n"); + + return rc; +} diff --git a/net/ncsi/ncsi-netlink.h b/net/ncsi/ncsi-netlink.h new file mode 100644 index 000000000000..91a5c256f8c4 --- /dev/null +++ b/net/ncsi/ncsi-netlink.h @@ -0,0 +1,20 @@ +/* + * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __NCSI_NETLINK_H__ +#define __NCSI_NETLINK_H__ + +#include + +#include "internal.h" + +int ncsi_init_netlink(struct net_device *dev); +int ncsi_unregister_netlink(struct net_device *dev); + +#endif /* __NCSI_NETLINK_H__ */ -- cgit v1.2.3 From faeb7833eee0d6afe0ecb6bdfa6042556c2c352e Mon Sep 17 00:00:00 2001 From: Roman Kagan Date: Thu, 1 Feb 2018 16:48:32 +0300 Subject: kvm: x86: hyperv: guest->host event signaling via eventfd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Hyper-V, the fast guest->host notification mechanism is the SIGNAL_EVENT hypercall, with a single parameter of the connection ID to signal. Currently this hypercall incurs a user exit and requires the userspace to decode the parameters and trigger the notification of the potentially different I/O context. To avoid the costly user exit, process this hypercall and signal the corresponding eventfd in KVM, similar to ioeventfd. The association between the connection id and the eventfd is established via the newly introduced KVM_HYPERV_EVENTFD ioctl, and maintained in an (srcu-protected) IDR. Signed-off-by: Roman Kagan Reviewed-by: David Hildenbrand [asm/hyperv.h changes approved by KY Srinivasan. - Radim] Signed-off-by: Radim Krčmář --- Documentation/virtual/kvm/api.txt | 32 ++++++++++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/include/uapi/asm/hyperv.h | 2 + arch/x86/kvm/hyperv.c | 103 ++++++++++++++++++++++++++++++++++++- arch/x86/kvm/hyperv.h | 1 + arch/x86/kvm/x86.c | 10 ++++ include/uapi/linux/kvm.h | 15 ++++++ 7 files changed, 164 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index d6b3ff51a14f..db992e036bdf 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -3516,6 +3516,38 @@ Returns: 0 on success; -1 on error This ioctl can be used to unregister the guest memory region registered with KVM_MEMORY_ENCRYPT_REG_REGION ioctl above. +4.113 KVM_HYPERV_EVENTFD + +Capability: KVM_CAP_HYPERV_EVENTFD +Architectures: x86 +Type: vm ioctl +Parameters: struct kvm_hyperv_eventfd (in) + +This ioctl (un)registers an eventfd to receive notifications from the guest on +the specified Hyper-V connection id through the SIGNAL_EVENT hypercall, without +causing a user exit. SIGNAL_EVENT hypercall with non-zero event flag number +(bits 24-31) still triggers a KVM_EXIT_HYPERV_HCALL user exit. + +struct kvm_hyperv_eventfd { + __u32 conn_id; + __s32 fd; + __u32 flags; + __u32 padding[3]; +}; + +The conn_id field should fit within 24 bits: + +#define KVM_HYPERV_CONN_ID_MASK 0x00ffffff + +The acceptable values for the flags field are: + +#define KVM_HYPERV_EVENTFD_DEASSIGN (1 << 0) + +Returns: 0 on success, + -EINVAL if conn_id or flags is outside the allowed range + -ENOENT on deassign if the conn_id isn't registered + -EEXIST on assign if the conn_id is already registered + 5. The kvm_run structure ------------------------ diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index b605a5b6a30c..df6720fc57e6 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -754,6 +754,8 @@ struct kvm_hv { u64 hv_crash_ctl; HV_REFERENCE_TSC_PAGE tsc_ref; + + struct idr conn_to_evt; }; enum kvm_irqchip_mode { diff --git a/arch/x86/include/uapi/asm/hyperv.h b/arch/x86/include/uapi/asm/hyperv.h index 099414345865..31d7a0a91f50 100644 --- a/arch/x86/include/uapi/asm/hyperv.h +++ b/arch/x86/include/uapi/asm/hyperv.h @@ -303,7 +303,9 @@ enum HV_GENERIC_SET_FORMAT { #define HV_STATUS_INVALID_HYPERCALL_CODE 2 #define HV_STATUS_INVALID_HYPERCALL_INPUT 3 #define HV_STATUS_INVALID_ALIGNMENT 4 +#define HV_STATUS_INVALID_PARAMETER 5 #define HV_STATUS_INSUFFICIENT_MEMORY 11 +#define HV_STATUS_INVALID_PORT_ID 17 #define HV_STATUS_INVALID_CONNECTION_ID 18 #define HV_STATUS_INSUFFICIENT_BUFFERS 19 diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index 015fb06c7522..53bd1913b6fd 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -1226,6 +1227,43 @@ static int kvm_hv_hypercall_complete_userspace(struct kvm_vcpu *vcpu) return 1; } +static u16 kvm_hvcall_signal_event(struct kvm_vcpu *vcpu, bool fast, u64 param) +{ + struct eventfd_ctx *eventfd; + + if (unlikely(!fast)) { + int ret; + gpa_t gpa = param; + + if ((gpa & (__alignof__(param) - 1)) || + offset_in_page(gpa) + sizeof(param) > PAGE_SIZE) + return HV_STATUS_INVALID_ALIGNMENT; + + ret = kvm_vcpu_read_guest(vcpu, gpa, ¶m, sizeof(param)); + if (ret < 0) + return HV_STATUS_INVALID_ALIGNMENT; + } + + /* + * Per spec, bits 32-47 contain the extra "flag number". However, we + * have no use for it, and in all known usecases it is zero, so just + * report lookup failure if it isn't. + */ + if (param & 0xffff00000000ULL) + return HV_STATUS_INVALID_PORT_ID; + /* remaining bits are reserved-zero */ + if (param & ~KVM_HYPERV_CONN_ID_MASK) + return HV_STATUS_INVALID_HYPERCALL_INPUT; + + /* conn_to_evt is protected by vcpu->kvm->srcu */ + eventfd = idr_find(&vcpu->kvm->arch.hyperv.conn_to_evt, param); + if (!eventfd) + return HV_STATUS_INVALID_PORT_ID; + + eventfd_signal(eventfd, 1); + return HV_STATUS_SUCCESS; +} + int kvm_hv_hypercall(struct kvm_vcpu *vcpu) { u64 param, ingpa, outgpa, ret; @@ -1276,8 +1314,12 @@ int kvm_hv_hypercall(struct kvm_vcpu *vcpu) case HVCALL_NOTIFY_LONG_SPIN_WAIT: kvm_vcpu_on_spin(vcpu, true); break; - case HVCALL_POST_MESSAGE: case HVCALL_SIGNAL_EVENT: + res = kvm_hvcall_signal_event(vcpu, fast, ingpa); + if (res != HV_STATUS_INVALID_PORT_ID) + break; + /* maybe userspace knows this conn_id: fall through */ + case HVCALL_POST_MESSAGE: /* don't bother userspace if it has no way to handle it */ if (!vcpu_to_synic(vcpu)->active) { res = HV_STATUS_INVALID_HYPERCALL_CODE; @@ -1305,8 +1347,67 @@ set_result: void kvm_hv_init_vm(struct kvm *kvm) { mutex_init(&kvm->arch.hyperv.hv_lock); + idr_init(&kvm->arch.hyperv.conn_to_evt); } void kvm_hv_destroy_vm(struct kvm *kvm) { + struct eventfd_ctx *eventfd; + int i; + + idr_for_each_entry(&kvm->arch.hyperv.conn_to_evt, eventfd, i) + eventfd_ctx_put(eventfd); + idr_destroy(&kvm->arch.hyperv.conn_to_evt); +} + +static int kvm_hv_eventfd_assign(struct kvm *kvm, u32 conn_id, int fd) +{ + struct kvm_hv *hv = &kvm->arch.hyperv; + struct eventfd_ctx *eventfd; + int ret; + + eventfd = eventfd_ctx_fdget(fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + mutex_lock(&hv->hv_lock); + ret = idr_alloc(&hv->conn_to_evt, eventfd, conn_id, conn_id + 1, + GFP_KERNEL); + mutex_unlock(&hv->hv_lock); + + if (ret >= 0) + return 0; + + if (ret == -ENOSPC) + ret = -EEXIST; + eventfd_ctx_put(eventfd); + return ret; +} + +static int kvm_hv_eventfd_deassign(struct kvm *kvm, u32 conn_id) +{ + struct kvm_hv *hv = &kvm->arch.hyperv; + struct eventfd_ctx *eventfd; + + mutex_lock(&hv->hv_lock); + eventfd = idr_remove(&hv->conn_to_evt, conn_id); + mutex_unlock(&hv->hv_lock); + + if (!eventfd) + return -ENOENT; + + synchronize_srcu(&kvm->srcu); + eventfd_ctx_put(eventfd); + return 0; +} + +int kvm_vm_ioctl_hv_eventfd(struct kvm *kvm, struct kvm_hyperv_eventfd *args) +{ + if ((args->flags & ~KVM_HYPERV_EVENTFD_DEASSIGN) || + (args->conn_id & ~KVM_HYPERV_CONN_ID_MASK)) + return -EINVAL; + + if (args->flags == KVM_HYPERV_EVENTFD_DEASSIGN) + return kvm_hv_eventfd_deassign(kvm, args->conn_id); + return kvm_hv_eventfd_assign(kvm, args->conn_id, args->fd); } diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h index cc2468244ca2..837465d69c6d 100644 --- a/arch/x86/kvm/hyperv.h +++ b/arch/x86/kvm/hyperv.h @@ -90,5 +90,6 @@ void kvm_hv_setup_tsc_page(struct kvm *kvm, void kvm_hv_init_vm(struct kvm *kvm); void kvm_hv_destroy_vm(struct kvm *kvm); +int kvm_vm_ioctl_hv_eventfd(struct kvm *kvm, struct kvm_hyperv_eventfd *args); #endif diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index fee833c4a132..3b6b7ee9fa8f 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2809,6 +2809,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_HYPERV_SYNIC: case KVM_CAP_HYPERV_SYNIC2: case KVM_CAP_HYPERV_VP_INDEX: + case KVM_CAP_HYPERV_EVENTFD: case KVM_CAP_PCI_SEGMENT: case KVM_CAP_DEBUGREGS: case KVM_CAP_X86_ROBUST_SINGLESTEP: @@ -4482,6 +4483,15 @@ set_identity_unlock: r = kvm_x86_ops->mem_enc_unreg_region(kvm, ®ion); break; } + case KVM_HYPERV_EVENTFD: { + struct kvm_hyperv_eventfd hvevfd; + + r = -EFAULT; + if (copy_from_user(&hvevfd, argp, sizeof(hvevfd))) + goto out; + r = kvm_vm_ioctl_hv_eventfd(kvm, &hvevfd); + break; + } default: r = -ENOTTY; } diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 7b26d4b0b052..2d2d926113ba 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -936,6 +936,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_PPC_GET_CPU_CHAR 151 #define KVM_CAP_S390_BPB 152 #define KVM_CAP_GET_MSR_FEATURES 153 +#define KVM_CAP_HYPERV_EVENTFD 154 #ifdef KVM_CAP_IRQ_ROUTING @@ -1375,6 +1376,10 @@ struct kvm_enc_region { #define KVM_MEMORY_ENCRYPT_REG_REGION _IOR(KVMIO, 0xbb, struct kvm_enc_region) #define KVM_MEMORY_ENCRYPT_UNREG_REGION _IOR(KVMIO, 0xbc, struct kvm_enc_region) +/* Available with KVM_CAP_HYPERV_EVENTFD */ +#define KVM_HYPERV_EVENTFD _IOW(KVMIO, 0xbd, struct kvm_hyperv_eventfd) + + /* Secure Encrypted Virtualization command */ enum sev_cmd_id { /* Guest initialization commands */ @@ -1515,4 +1520,14 @@ struct kvm_assigned_msix_entry { #define KVM_ARM_DEV_EL1_PTIMER (1 << 1) #define KVM_ARM_DEV_PMU (1 << 2) +struct kvm_hyperv_eventfd { + __u32 conn_id; + __s32 fd; + __u32 flags; + __u32 padding[3]; +}; + +#define KVM_HYPERV_CONN_ID_MASK 0x00ffffff +#define KVM_HYPERV_EVENTFD_DEASSIGN (1 << 0) + #endif /* __LINUX_KVM_H */ -- cgit v1.2.3 From 7b7e39522a61f402d41dd9a67f3fa2133ef9d4e8 Mon Sep 17 00:00:00 2001 From: Ken Hofsass Date: Wed, 31 Jan 2018 16:03:35 -0800 Subject: KVM: x86: add SYNC_REGS_SIZE_BYTES #define. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hardcoded padding size value for struct kvm_sync_regs with #define SYNC_REGS_SIZE_BYTES. Also update the value specified in api.txt from outdated hardcoded value to SYNC_REGS_SIZE_BYTES. Signed-off-by: Ken Hofsass Reviewed-by: David Hildenbrand Acked-by: Christian Borntraeger Signed-off-by: Radim Krčmář --- Documentation/virtual/kvm/api.txt | 2 +- include/uapi/linux/kvm.h | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index db992e036bdf..55867a2a460c 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -3905,7 +3905,7 @@ in userspace. __u64 kvm_dirty_regs; union { struct kvm_sync_regs regs; - char padding[1024]; + char padding[SYNC_REGS_SIZE_BYTES]; } s; If KVM_CAP_SYNC_REGS is defined, these fields allow userspace to access diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 2d2d926113ba..088c2c92db55 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -396,6 +396,10 @@ struct kvm_run { char padding[256]; }; + /* 2048 is the size of the char array used to bound/pad the size + * of the union that holds sync regs. + */ + #define SYNC_REGS_SIZE_BYTES 2048 /* * shared registers between kvm and userspace. * kvm_valid_regs specifies the register classes set by the host @@ -407,7 +411,7 @@ struct kvm_run { __u64 kvm_dirty_regs; union { struct kvm_sync_regs regs; - char padding[2048]; + char padding[SYNC_REGS_SIZE_BYTES]; } s; }; -- cgit v1.2.3 From ed63afb8a318f6b3558d76afba7809daee4f28e5 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 5 Mar 2018 20:44:18 +0800 Subject: sctp: add support for PR-SCTP Information for sendmsg This patch is to add support for PR-SCTP Information for sendmsg, as described in section 5.3.7 of RFC6458. With this option, you can specify pr_policy and pr_value for user data in sendmsg. It's also a necessary send info for sctp_sendv. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 1 + include/uapi/linux/sctp.h | 15 +++++++++++++++ net/sctp/socket.c | 31 ++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 03e92dda1813..d40a2a329888 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -2112,6 +2112,7 @@ struct sctp_cmsgs { struct sctp_initmsg *init; struct sctp_sndrcvinfo *srinfo; struct sctp_sndinfo *sinfo; + struct sctp_prinfo *prinfo; }; /* Structure for tracking memory objects */ diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 4c4db14786bd..0dd1f82a4fa8 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -260,6 +260,19 @@ struct sctp_nxtinfo { sctp_assoc_t nxt_assoc_id; }; +/* 5.3.7 SCTP PR-SCTP Information Structure (SCTP_PRINFO) + * + * This cmsghdr structure specifies SCTP options for sendmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_PRINFO struct sctp_prinfo + */ +struct sctp_prinfo { + __u16 pr_policy; + __u32 pr_value; +}; + /* * sinfo_flags: 16 bits (unsigned integer) * @@ -293,6 +306,8 @@ typedef enum sctp_cmsg_type { #define SCTP_RCVINFO SCTP_RCVINFO SCTP_NXTINFO, /* 5.3.6 SCTP Next Receive Information Structure */ #define SCTP_NXTINFO SCTP_NXTINFO + SCTP_PRINFO, /* 5.3.7 SCTP PR-SCTP Information Structure */ +#define SCTP_PRINFO SCTP_PRINFO } sctp_cmsg_t; /* diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 7fa76031bb08..fdde697b37e7 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1644,6 +1644,12 @@ static int sctp_sendmsg_parse(struct sock *sk, struct sctp_cmsgs *cmsgs, srinfo->sinfo_assoc_id = cmsgs->sinfo->snd_assoc_id; } + if (cmsgs->prinfo) { + srinfo->sinfo_timetolive = cmsgs->prinfo->pr_value; + SCTP_PR_SET_POLICY(srinfo->sinfo_flags, + cmsgs->prinfo->pr_policy); + } + sflags = srinfo->sinfo_flags; if (!sflags && msg_len) return 0; @@ -1901,9 +1907,12 @@ static void sctp_sendmsg_update_sinfo(struct sctp_association *asoc, sinfo->sinfo_ppid = asoc->default_ppid; sinfo->sinfo_context = asoc->default_context; sinfo->sinfo_assoc_id = sctp_assoc2id(asoc); + + if (!cmsgs->prinfo) + sinfo->sinfo_flags = asoc->default_flags; } - if (!cmsgs->srinfo) + if (!cmsgs->srinfo && !cmsgs->prinfo) sinfo->sinfo_timetolive = asoc->default_timetolive; } @@ -7749,6 +7758,26 @@ static int sctp_msghdr_parse(const struct msghdr *msg, struct sctp_cmsgs *cmsgs) SCTP_ABORT | SCTP_EOF)) return -EINVAL; break; + case SCTP_PRINFO: + /* SCTP Socket API Extension + * 5.3.7 SCTP PR-SCTP Information Structure (SCTP_PRINFO) + * + * This cmsghdr structure specifies SCTP options for sendmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ --------------------- + * IPPROTO_SCTP SCTP_PRINFO struct sctp_prinfo + */ + if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_prinfo))) + return -EINVAL; + + cmsgs->prinfo = CMSG_DATA(cmsg); + if (cmsgs->prinfo->pr_policy & ~SCTP_PR_SCTP_MASK) + return -EINVAL; + + if (cmsgs->prinfo->pr_policy == SCTP_PR_SCTP_NONE) + cmsgs->prinfo->pr_value = 0; + break; default: return -EINVAL; } -- cgit v1.2.3 From 2c0dbaa0c43d04d8d6daf52adb724c5789676b15 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 5 Mar 2018 20:44:19 +0800 Subject: sctp: add support for SCTP_DSTADDRV4/6 Information for sendmsg This patch is to add support for Destination IPv4/6 Address options for sendmsg, as described in section 5.3.9/10 of RFC6458. With this option, you can provide more than one destination addrs to sendmsg when creating asoc, like sctp_connectx. It's also a necessary send info for sctp_sendv. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 1 + include/uapi/linux/sctp.h | 6 ++++ net/sctp/socket.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index d40a2a329888..ec6e46b7e119 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -2113,6 +2113,7 @@ struct sctp_cmsgs { struct sctp_sndrcvinfo *srinfo; struct sctp_sndinfo *sinfo; struct sctp_prinfo *prinfo; + struct msghdr *addrs_msg; }; /* Structure for tracking memory objects */ diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 0dd1f82a4fa8..a1bc35098033 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -308,6 +308,12 @@ typedef enum sctp_cmsg_type { #define SCTP_NXTINFO SCTP_NXTINFO SCTP_PRINFO, /* 5.3.7 SCTP PR-SCTP Information Structure */ #define SCTP_PRINFO SCTP_PRINFO + SCTP_AUTHINFO, /* 5.3.8 SCTP AUTH Information Structure (RESERVED) */ +#define SCTP_AUTHINFO SCTP_AUTHINFO + SCTP_DSTADDRV4, /* 5.3.9 SCTP Destination IPv4 Address Structure */ +#define SCTP_DSTADDRV4 SCTP_DSTADDRV4 + SCTP_DSTADDRV6, /* 5.3.10 SCTP Destination IPv6 Address Structure */ +#define SCTP_DSTADDRV6 SCTP_DSTADDRV6 } sctp_cmsg_t; /* diff --git a/net/sctp/socket.c b/net/sctp/socket.c index fdde697b37e7..067b57a330b1 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1676,6 +1676,7 @@ static int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags, struct net *net = sock_net(sk); struct sctp_association *asoc; enum sctp_scope scope; + struct cmsghdr *cmsg; int err = -EINVAL; *tp = NULL; @@ -1741,6 +1742,67 @@ static int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags, goto free; } + if (!cmsgs->addrs_msg) + return 0; + + /* sendv addr list parse */ + for_each_cmsghdr(cmsg, cmsgs->addrs_msg) { + struct sctp_transport *transport; + struct sctp_association *old; + union sctp_addr _daddr; + int dlen; + + if (cmsg->cmsg_level != IPPROTO_SCTP || + (cmsg->cmsg_type != SCTP_DSTADDRV4 && + cmsg->cmsg_type != SCTP_DSTADDRV6)) + continue; + + daddr = &_daddr; + memset(daddr, 0, sizeof(*daddr)); + dlen = cmsg->cmsg_len - sizeof(struct cmsghdr); + if (cmsg->cmsg_type == SCTP_DSTADDRV4) { + if (dlen < sizeof(struct in_addr)) + goto free; + + dlen = sizeof(struct in_addr); + daddr->v4.sin_family = AF_INET; + daddr->v4.sin_port = htons(asoc->peer.port); + memcpy(&daddr->v4.sin_addr, CMSG_DATA(cmsg), dlen); + } else { + if (dlen < sizeof(struct in6_addr)) + goto free; + + dlen = sizeof(struct in6_addr); + daddr->v6.sin6_family = AF_INET6; + daddr->v6.sin6_port = htons(asoc->peer.port); + memcpy(&daddr->v6.sin6_addr, CMSG_DATA(cmsg), dlen); + } + err = sctp_verify_addr(sk, daddr, sizeof(*daddr)); + if (err) + goto free; + + old = sctp_endpoint_lookup_assoc(ep, daddr, &transport); + if (old && old != asoc) { + if (old->state >= SCTP_STATE_ESTABLISHED) + err = -EISCONN; + else + err = -EALREADY; + goto free; + } + + if (sctp_endpoint_is_peeled_off(ep, daddr)) { + err = -EADDRNOTAVAIL; + goto free; + } + + transport = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL, + SCTP_UNKNOWN); + if (!transport) { + err = -ENOMEM; + goto free; + } + } + return 0; free: @@ -7778,6 +7840,21 @@ static int sctp_msghdr_parse(const struct msghdr *msg, struct sctp_cmsgs *cmsgs) if (cmsgs->prinfo->pr_policy == SCTP_PR_SCTP_NONE) cmsgs->prinfo->pr_value = 0; break; + case SCTP_DSTADDRV4: + case SCTP_DSTADDRV6: + /* SCTP Socket API Extension + * 5.3.9/10 SCTP Destination IPv4/6 Address Structure (SCTP_DSTADDRV4/6) + * + * This cmsghdr structure specifies SCTP options for sendmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ --------------------- + * IPPROTO_SCTP SCTP_DSTADDRV4 struct in_addr + * ------------ ------------ --------------------- + * IPPROTO_SCTP SCTP_DSTADDRV6 struct in6_addr + */ + cmsgs->addrs_msg = my_msg; + break; default: return -EINVAL; } -- cgit v1.2.3 From 4910280503f3af2857d5aa77e35b22d93a8960a8 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 5 Mar 2018 20:44:20 +0800 Subject: sctp: add support for snd flag SCTP_SENDALL process in sendmsg This patch is to add support for snd flag SCTP_SENDALL process in sendmsg, as described in section 5.3.4 of RFC6458. With this flag, you can send the same data to all the asocs of this sk once. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/uapi/linux/sctp.h | 2 ++ net/sctp/socket.c | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index a1bc35098033..e94b6d297ad9 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -284,6 +284,8 @@ enum sctp_sinfo_flags { SCTP_ADDR_OVER = (1 << 1), /* Override the primary destination. */ SCTP_ABORT = (1 << 2), /* Send an ABORT message to the peer. */ SCTP_SACK_IMMEDIATELY = (1 << 3), /* SACK should be sent without delay. */ + /* 2 bits here have been used by SCTP_PR_SCTP_MASK */ + SCTP_SENDALL = (1 << 6), SCTP_NOTIFICATION = MSG_NOTIFICATION, /* Next message is not user msg but notification. */ SCTP_EOF = MSG_FIN, /* Initiate graceful shutdown process. */ }; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 067b57a330b1..7d3476a4860d 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1820,6 +1820,10 @@ static int sctp_sendmsg_check_sflags(struct sctp_association *asoc, if (sctp_state(asoc, CLOSED) && sctp_style(sk, TCP)) return -EPIPE; + if ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP) && + !sctp_state(asoc, ESTABLISHED)) + return 0; + if (sflags & SCTP_EOF) { pr_debug("%s: shutting down association:%p\n", __func__, asoc); sctp_primitive_SHUTDOWN(net, asoc, NULL); @@ -2007,6 +2011,29 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) lock_sock(sk); + /* SCTP_SENDALL process */ + if ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP)) { + list_for_each_entry(asoc, &ep->asocs, asocs) { + err = sctp_sendmsg_check_sflags(asoc, sflags, msg, + msg_len); + if (err == 0) + continue; + if (err < 0) + goto out_unlock; + + sctp_sendmsg_update_sinfo(asoc, sinfo, &cmsgs); + + err = sctp_sendmsg_to_asoc(asoc, msg, msg_len, + NULL, sinfo); + if (err < 0) + goto out_unlock; + + iov_iter_revert(&msg->msg_iter, err); + } + + goto out_unlock; + } + /* Get and check or create asoc */ if (daddr) { asoc = sctp_endpoint_lookup_assoc(ep, daddr, &transport); @@ -7792,8 +7819,8 @@ static int sctp_msghdr_parse(const struct msghdr *msg, struct sctp_cmsgs *cmsgs) if (cmsgs->srinfo->sinfo_flags & ~(SCTP_UNORDERED | SCTP_ADDR_OVER | - SCTP_SACK_IMMEDIATELY | SCTP_PR_SCTP_MASK | - SCTP_ABORT | SCTP_EOF)) + SCTP_SACK_IMMEDIATELY | SCTP_SENDALL | + SCTP_PR_SCTP_MASK | SCTP_ABORT | SCTP_EOF)) return -EINVAL; break; @@ -7816,8 +7843,8 @@ static int sctp_msghdr_parse(const struct msghdr *msg, struct sctp_cmsgs *cmsgs) if (cmsgs->sinfo->snd_flags & ~(SCTP_UNORDERED | SCTP_ADDR_OVER | - SCTP_SACK_IMMEDIATELY | SCTP_PR_SCTP_MASK | - SCTP_ABORT | SCTP_EOF)) + SCTP_SACK_IMMEDIATELY | SCTP_SENDALL | + SCTP_PR_SCTP_MASK | SCTP_ABORT | SCTP_EOF)) return -EINVAL; break; case SCTP_PRINFO: -- cgit v1.2.3 From 95da0cdb723260362fc126a563285ac66a193da7 Mon Sep 17 00:00:00 2001 From: Teng Qin Date: Tue, 6 Mar 2018 10:55:01 -0800 Subject: bpf: add support to read sample address in bpf program This commit adds new field "addr" to bpf_perf_event_data which could be read and used by bpf programs attached to perf events. The value of the field is copied from bpf_perf_event_data_kern.addr and contains the address value recorded by specifying sample_type with PERF_SAMPLE_ADDR when calling perf_event_open. Signed-off-by: Teng Qin Signed-off-by: Daniel Borkmann --- include/uapi/linux/bpf_perf_event.h | 1 + kernel/trace/bpf_trace.c | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/bpf_perf_event.h b/include/uapi/linux/bpf_perf_event.h index 8f95303f9d80..eb1b9d21250c 100644 --- a/include/uapi/linux/bpf_perf_event.h +++ b/include/uapi/linux/bpf_perf_event.h @@ -13,6 +13,7 @@ struct bpf_perf_event_data { bpf_user_pt_regs_t regs; __u64 sample_period; + __u64 addr; }; #endif /* _UAPI__LINUX_BPF_PERF_EVENT_H__ */ diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index c0a9e310d715..c634e093951f 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -726,8 +726,7 @@ const struct bpf_prog_ops tracepoint_prog_ops = { static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type, struct bpf_insn_access_aux *info) { - const int size_sp = FIELD_SIZEOF(struct bpf_perf_event_data, - sample_period); + const int size_u64 = sizeof(u64); if (off < 0 || off >= sizeof(struct bpf_perf_event_data)) return false; @@ -738,8 +737,13 @@ static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type switch (off) { case bpf_ctx_range(struct bpf_perf_event_data, sample_period): - bpf_ctx_record_field_size(info, size_sp); - if (!bpf_ctx_narrow_access_ok(off, size, size_sp)) + bpf_ctx_record_field_size(info, size_u64); + if (!bpf_ctx_narrow_access_ok(off, size, size_u64)) + return false; + break; + case bpf_ctx_range(struct bpf_perf_event_data, addr): + bpf_ctx_record_field_size(info, size_u64); + if (!bpf_ctx_narrow_access_ok(off, size, size_u64)) return false; break; default: @@ -766,6 +770,14 @@ static u32 pe_prog_convert_ctx_access(enum bpf_access_type type, bpf_target_off(struct perf_sample_data, period, 8, target_size)); break; + case offsetof(struct bpf_perf_event_data, addr): + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_perf_event_data_kern, + data), si->dst_reg, si->src_reg, + offsetof(struct bpf_perf_event_data_kern, data)); + *insn++ = BPF_LDX_MEM(BPF_DW, si->dst_reg, si->dst_reg, + bpf_target_off(struct perf_sample_data, addr, 8, + target_size)); + break; default: *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_perf_event_data_kern, regs), si->dst_reg, si->src_reg, -- cgit v1.2.3 From 459d153d9916ea48b1550bbb6f2959dc03bff011 Mon Sep 17 00:00:00 2001 From: Pieter Jansen van Vuuren Date: Tue, 6 Mar 2018 18:11:14 +0100 Subject: net/sched: cls_flower: Add support to handle first frag as match field Allow setting firstfrag as matching option in tc flower classifier. # tc filter add dev eth0 protocol ip parent ffff: \ flower indev eth0 \ ip_flags firstfrag action mirred egress redirect dev eth1 Signed-off-by: Pieter Jansen van Vuuren Signed-off-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/uapi/linux/pkt_cls.h | 1 + net/sched/cls_flower.c | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 7cafb26df555..be05e66c167b 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -475,6 +475,7 @@ enum { enum { TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0), + TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1), }; /* Match-all classifier */ diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 7d0ce2c40f93..d964e60c730e 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -511,6 +511,9 @@ static int fl_set_key_flags(struct nlattr **tb, fl_set_key_flag(key, mask, flags_key, flags_mask, TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); + fl_set_key_flag(key, mask, flags_key, flags_mask, + TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST, + FLOW_DIS_FIRST_FRAG); return 0; } @@ -1130,6 +1133,9 @@ static int fl_dump_key_flags(struct sk_buff *skb, u32 flags_key, u32 flags_mask) fl_get_key_flag(flags_key, flags_mask, &key, &mask, TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); + fl_get_key_flag(flags_key, flags_mask, &key, &mask, + TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST, + FLOW_DIS_FIRST_FRAG); _key = cpu_to_be32(key); _mask = cpu_to_be32(mask); -- cgit v1.2.3 From 84a1d9c4820080bebcbd413a845076dcb62f45fa Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 8 Mar 2018 15:45:03 +0000 Subject: net: ethtool: extend RXNFC API to support RSS spreading of filter matches We use a two-step process to configure a filter with RSS spreading. First, the RSS context is allocated and configured using ETHTOOL_SRSSH; this returns an identifier (rss_context) which can then be passed to subsequent invocations of ETHTOOL_SRXCLSRLINS to specify that the offset from the RSS indirection table lookup should be added to the queue number (ring_cookie) when delivering the packet. Drivers for devices which can only use the indirection table entry directly (not add it to a base queue number) should reject rule insertions combining RSS with a nonzero ring_cookie. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- include/linux/ethtool.h | 5 ++++ include/uapi/linux/ethtool.h | 32 +++++++++++++++++----- net/core/ethtool.c | 64 +++++++++++++++++++++++++++++++++----------- 3 files changed, 80 insertions(+), 21 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 2ec41a7eb54f..ebe41811ed34 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -371,6 +371,11 @@ struct ethtool_ops { u8 *hfunc); int (*set_rxfh)(struct net_device *, const u32 *indir, const u8 *key, const u8 hfunc); + int (*get_rxfh_context)(struct net_device *, u32 *indir, u8 *key, + u8 *hfunc, u32 rss_context); + int (*set_rxfh_context)(struct net_device *, const u32 *indir, + const u8 *key, const u8 hfunc, + u32 *rss_context, bool delete); void (*get_channels)(struct net_device *, struct ethtool_channels *); int (*set_channels)(struct net_device *, struct ethtool_channels *); int (*get_dump_flag)(struct net_device *, struct ethtool_dump *); diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 44a0b675a6bc..20da156aaf64 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -914,12 +914,15 @@ static inline __u64 ethtool_get_flow_spec_ring_vf(__u64 ring_cookie) * @flow_type: Type of flow to be affected, e.g. %TCP_V4_FLOW * @data: Command-dependent value * @fs: Flow classification rule + * @rss_context: RSS context to be affected * @rule_cnt: Number of rules to be affected * @rule_locs: Array of used rule locations * * For %ETHTOOL_GRXFH and %ETHTOOL_SRXFH, @data is a bitmask indicating * the fields included in the flow hash, e.g. %RXH_IP_SRC. The following - * structure fields must not be used. + * structure fields must not be used, except that if @flow_type includes + * the %FLOW_RSS flag, then @rss_context determines which RSS context to + * act on. * * For %ETHTOOL_GRXRINGS, @data is set to the number of RX rings/queues * on return. @@ -931,7 +934,9 @@ static inline __u64 ethtool_get_flow_spec_ring_vf(__u64 ring_cookie) * set in @data then special location values should not be used. * * For %ETHTOOL_GRXCLSRULE, @fs.@location specifies the location of an - * existing rule on entry and @fs contains the rule on return. + * existing rule on entry and @fs contains the rule on return; if + * @fs.@flow_type includes the %FLOW_RSS flag, then @rss_context is + * filled with the RSS context ID associated with the rule. * * For %ETHTOOL_GRXCLSRLALL, @rule_cnt specifies the array size of the * user buffer for @rule_locs on entry. On return, @data is the size @@ -942,7 +947,11 @@ static inline __u64 ethtool_get_flow_spec_ring_vf(__u64 ring_cookie) * For %ETHTOOL_SRXCLSRLINS, @fs specifies the rule to add or update. * @fs.@location either specifies the location to use or is a special * location value with %RX_CLS_LOC_SPECIAL flag set. On return, - * @fs.@location is the actual rule location. + * @fs.@location is the actual rule location. If @fs.@flow_type + * includes the %FLOW_RSS flag, @rss_context is the RSS context ID to + * use for flow spreading traffic which matches this rule. The value + * from the rxfh indirection table will be added to @fs.@ring_cookie + * to choose which ring to deliver to. * * For %ETHTOOL_SRXCLSRLDEL, @fs.@location specifies the location of an * existing rule on entry. @@ -963,7 +972,10 @@ struct ethtool_rxnfc { __u32 flow_type; __u64 data; struct ethtool_rx_flow_spec fs; - __u32 rule_cnt; + union { + __u32 rule_cnt; + __u32 rss_context; + }; __u32 rule_locs[0]; }; @@ -990,7 +1002,11 @@ struct ethtool_rxfh_indir { /** * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key. * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH - * @rss_context: RSS context identifier. + * @rss_context: RSS context identifier. Context 0 is the default for normal + * traffic; other contexts can be referenced as the destination for RX flow + * classification rules. %ETH_RXFH_CONTEXT_ALLOC is used with command + * %ETHTOOL_SRSSH to allocate a new RSS context; on return this field will + * contain the ID of the newly allocated context. * @indir_size: On entry, the array size of the user buffer for the * indirection table, which may be zero, or (for %ETHTOOL_SRSSH), * %ETH_RXFH_INDIR_NO_CHANGE. On return from %ETHTOOL_GRSSH, @@ -1009,7 +1025,8 @@ struct ethtool_rxfh_indir { * size should be returned. For %ETHTOOL_SRSSH, an @indir_size of * %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested * and a @indir_size of zero means the indir table should be reset to default - * values. An hfunc of zero means that hash function setting is not requested. + * values (if @rss_context == 0) or that the RSS context should be deleted. + * An hfunc of zero means that hash function setting is not requested. */ struct ethtool_rxfh { __u32 cmd; @@ -1021,6 +1038,7 @@ struct ethtool_rxfh { __u32 rsvd32; __u32 rss_config[0]; }; +#define ETH_RXFH_CONTEXT_ALLOC 0xffffffff #define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff /** @@ -1635,6 +1653,8 @@ static inline int ethtool_validate_duplex(__u8 duplex) /* Flag to enable additional fields in struct ethtool_rx_flow_spec */ #define FLOW_EXT 0x80000000 #define FLOW_MAC_EXT 0x40000000 +/* Flag to enable RSS spreading of traffic matching rule (nfc only) */ +#define FLOW_RSS 0x20000000 /* L3-L4 network traffic flow hash options */ #define RXH_L2DA (1 << 1) diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 3f89c76d5c24..157cd9efa4be 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -1022,6 +1022,15 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, if (copy_from_user(&info, useraddr, info_size)) return -EFAULT; + /* If FLOW_RSS was requested then user-space must be using the + * new definition, as FLOW_RSS is newer. + */ + if (cmd == ETHTOOL_GRXFH && info.flow_type & FLOW_RSS) { + info_size = sizeof(info); + if (copy_from_user(&info, useraddr, info_size)) + return -EFAULT; + } + if (info.cmd == ETHTOOL_GRXCLSRLALL) { if (info.rule_cnt > 0) { if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32)) @@ -1251,9 +1260,11 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev, user_key_size = rxfh.key_size; /* Check that reserved fields are 0 for now */ - if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] || - rxfh.rsvd8[2] || rxfh.rsvd32) + if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32) return -EINVAL; + /* Most drivers don't handle rss_context, check it's 0 as well */ + if (rxfh.rss_context && !ops->get_rxfh_context) + return -EOPNOTSUPP; rxfh.indir_size = dev_indir_size; rxfh.key_size = dev_key_size; @@ -1276,7 +1287,12 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev, if (user_key_size) hkey = rss_config + indir_bytes; - ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey, &dev_hfunc); + if (rxfh.rss_context) + ret = dev->ethtool_ops->get_rxfh_context(dev, indir, hkey, + &dev_hfunc, + rxfh.rss_context); + else + ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey, &dev_hfunc); if (ret) goto out; @@ -1306,6 +1322,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, u8 *hkey = NULL; u8 *rss_config; u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]); + bool delete = false; if (!ops->get_rxnfc || !ops->set_rxfh) return -EOPNOTSUPP; @@ -1319,9 +1336,11 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, return -EFAULT; /* Check that reserved fields are 0 for now */ - if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] || - rxfh.rsvd8[2] || rxfh.rsvd32) + if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32) return -EINVAL; + /* Most drivers don't handle rss_context, check it's 0 as well */ + if (rxfh.rss_context && !ops->set_rxfh_context) + return -EOPNOTSUPP; /* If either indir, hash key or function is valid, proceed further. * Must request at least one change: indir size, hash key or function. @@ -1346,7 +1365,8 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, if (ret) goto out; - /* rxfh.indir_size == 0 means reset the indir table to default. + /* rxfh.indir_size == 0 means reset the indir table to default (master + * context) or delete the context (other RSS contexts). * rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged. */ if (rxfh.indir_size && @@ -1359,9 +1379,13 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, if (ret) goto out; } else if (rxfh.indir_size == 0) { - indir = (u32 *)rss_config; - for (i = 0; i < dev_indir_size; i++) - indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); + if (rxfh.rss_context == 0) { + indir = (u32 *)rss_config; + for (i = 0; i < dev_indir_size; i++) + indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); + } else { + delete = true; + } } if (rxfh.key_size) { @@ -1374,15 +1398,25 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, } } - ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc); + if (rxfh.rss_context) + ret = ops->set_rxfh_context(dev, indir, hkey, rxfh.hfunc, + &rxfh.rss_context, delete); + else + ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc); if (ret) goto out; - /* indicate whether rxfh was set to default */ - if (rxfh.indir_size == 0) - dev->priv_flags &= ~IFF_RXFH_CONFIGURED; - else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) - dev->priv_flags |= IFF_RXFH_CONFIGURED; + if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, rss_context), + &rxfh.rss_context, sizeof(rxfh.rss_context))) + ret = -EFAULT; + + if (!rxfh.rss_context) { + /* indicate whether rxfh was set to default */ + if (rxfh.indir_size == 0) + dev->priv_flags &= ~IFF_RXFH_CONFIGURED; + else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) + dev->priv_flags |= IFF_RXFH_CONFIGURED; + } out: kfree(rss_config); -- cgit v1.2.3 From f597fbce38d230af95384f4a04e0a13a1d0ad45d Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 5 Mar 2018 22:17:38 +1030 Subject: serial: 8250: Add Nuvoton NPCM UART The Nuvoton UART is almost compatible with the 8250 driver when probed via the 8250_of driver, however it requires some extra configuration at startup. Reviewed-by: Rob Herring Signed-off-by: Joel Stanley Cc: stable Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/8250.txt | 1 + drivers/tty/serial/8250/8250_of.c | 1 + drivers/tty/serial/8250/8250_port.c | 33 +++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 +++ 4 files changed, 38 insertions(+) (limited to 'include/uapi/linux') diff --git a/Documentation/devicetree/bindings/serial/8250.txt b/Documentation/devicetree/bindings/serial/8250.txt index dad3b2ec66d4..aeb6db4e35c3 100644 --- a/Documentation/devicetree/bindings/serial/8250.txt +++ b/Documentation/devicetree/bindings/serial/8250.txt @@ -24,6 +24,7 @@ Required properties: - "ti,da830-uart" - "aspeed,ast2400-vuart" - "aspeed,ast2500-vuart" + - "nuvoton,npcm750-uart" - "serial" if the port type is unknown. - reg : offset and length of the register set for the device. - interrupts : should contain uart interrupt. diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 160b8906d9b9..9835b1c1cbe1 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -316,6 +316,7 @@ static const struct of_device_id of_platform_serial_table[] = { { .compatible = "mrvl,mmp-uart", .data = (void *)PORT_XSCALE, }, { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, + { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, }, { /* end of list */ }, }; MODULE_DEVICE_TABLE(of, of_platform_serial_table); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index ffbb955d1c06..95833cbc4338 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -47,6 +47,10 @@ #define UART_EXAR_SLEEP 0x8b /* Sleep mode */ #define UART_EXAR_DVID 0x8d /* Device identification */ +/* Nuvoton NPCM timeout register */ +#define UART_NPCM_TOR 7 +#define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */ + /* * Debugging. */ @@ -293,6 +297,15 @@ static const struct serial8250_config uart_config[] = { UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, .flags = UART_CAP_FIFO, }, + [PORT_NPCM] = { + .name = "Nuvoton 16550", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO, + }, }; /* Uart divisor latch read */ @@ -2141,6 +2154,15 @@ int serial8250_do_startup(struct uart_port *port) UART_DA830_PWREMU_MGMT_FREE); } + if (port->type == PORT_NPCM) { + /* + * Nuvoton calls the scratch register 'UART_TOR' (timeout + * register). Enable it, and set TIOC (timeout interrupt + * comparator) to be 0x20 for correct operation. + */ + serial_port_out(port, UART_NPCM_TOR, UART_NPCM_TOIE | 0x20); + } + #ifdef CONFIG_SERIAL_8250_RSA /* * If this is an RSA port, see if we can kick it up to the @@ -2463,6 +2485,15 @@ static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up, return quot_16 >> 4; } +/* Nuvoton NPCM UARTs have a custom divisor calculation */ +static unsigned int npcm_get_divisor(struct uart_8250_port *up, + unsigned int baud) +{ + struct uart_port *port = &up->port; + + return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2; +} + static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned int baud, unsigned int *frac) @@ -2483,6 +2514,8 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up, quot = 0x8002; else if (up->port.type == PORT_XR17V35X) quot = xr17v35x_get_divisor(up, baud, frac); + else if (up->port.type == PORT_NPCM) + quot = npcm_get_divisor(up, baud); else quot = uart_get_divisor(port, baud); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 1c8413f93e3d..dce5f9dae121 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -76,6 +76,9 @@ #define PORT_SUNZILOG 38 #define PORT_SUNSAB 39 +/* Nuvoton UART */ +#define PORT_NPCM 40 + /* Intel EG20 */ #define PORT_PCH_8LINE 44 #define PORT_PCH_2LINE 45 -- cgit v1.2.3 From 72199320d49dbafa1a99f94f1cd60dc90035c154 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 1 Mar 2018 17:33:32 +0100 Subject: timekeeping: Add the new CLOCK_MONOTONIC_ACTIVE clock The planned change to unify the behaviour of the MONOTONIC and BOOTTIME clocks vs. suspend removes the ability to retrieve the active non-suspended time of a system. Provide a new CLOCK_MONOTONIC_ACTIVE clock which returns the active non-suspended time of the system via clock_gettime(). This preserves the old behaviour of CLOCK_MONOTONIC before the BOOTTIME/MONOTONIC unification. This new clock also allows applications to detect programmatically that the MONOTONIC and BOOTTIME clocks are identical. Signed-off-by: Thomas Gleixner Cc: Dmitry Torokhov Cc: John Stultz Cc: Jonathan Corbet Cc: Kevin Easton Cc: Linus Torvalds Cc: Mark Salyzyn Cc: Michael Kerrisk Cc: Peter Zijlstra Cc: Petr Mladek Cc: Prarit Bhargava Cc: Sergey Senozhatsky Cc: Steven Rostedt Link: http://lkml.kernel.org/r/20180301165149.965235774@linutronix.de Signed-off-by: Ingo Molnar --- include/linux/timekeeper_internal.h | 2 ++ include/linux/timekeeping.h | 1 + include/uapi/linux/time.h | 1 + kernel/time/posix-stubs.c | 2 ++ kernel/time/posix-timers.c | 13 +++++++++++++ kernel/time/timekeeping.c | 36 ++++++++++++++++++++++++++++++++++++ 6 files changed, 55 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 7acb953298a7..4b3dca173e89 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -52,6 +52,7 @@ struct tk_read_base { * @offs_real: Offset clock monotonic -> clock realtime * @offs_boot: Offset clock monotonic -> clock boottime * @offs_tai: Offset clock monotonic -> clock tai + * @time_suspended: Accumulated suspend time * @tai_offset: The current UTC to TAI offset in seconds * @clock_was_set_seq: The sequence number of clock was set events * @cs_was_changed_seq: The sequence number of clocksource change events @@ -94,6 +95,7 @@ struct timekeeper { ktime_t offs_real; ktime_t offs_boot; ktime_t offs_tai; + ktime_t time_suspended; s32 tai_offset; unsigned int clock_was_set_seq; u8 cs_was_changed_seq; diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index b17bcce58bc4..440b1935d3a5 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -32,6 +32,7 @@ extern void getrawmonotonic64(struct timespec64 *ts); extern void ktime_get_ts64(struct timespec64 *ts); extern time64_t ktime_get_seconds(void); extern time64_t ktime_get_real_seconds(void); +extern void ktime_get_active_ts64(struct timespec64 *ts); extern int __getnstimeofday64(struct timespec64 *tv); extern void getnstimeofday64(struct timespec64 *tv); diff --git a/include/uapi/linux/time.h b/include/uapi/linux/time.h index 53f8dd84beb5..61a187df8da2 100644 --- a/include/uapi/linux/time.h +++ b/include/uapi/linux/time.h @@ -61,6 +61,7 @@ struct itimerval { */ #define CLOCK_SGI_CYCLE 10 #define CLOCK_TAI 11 +#define CLOCK_MONOTONIC_ACTIVE 12 #define MAX_CLOCKS 16 #define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC) diff --git a/kernel/time/posix-stubs.c b/kernel/time/posix-stubs.c index b258bee13b02..6259dbc0191a 100644 --- a/kernel/time/posix-stubs.c +++ b/kernel/time/posix-stubs.c @@ -73,6 +73,8 @@ int do_clock_gettime(clockid_t which_clock, struct timespec64 *tp) case CLOCK_BOOTTIME: get_monotonic_boottime64(tp); break; + case CLOCK_MONOTONIC_ACTIVE: + ktime_get_active_ts64(tp); default: return -EINVAL; } diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 75043046914e..556fe02a47a4 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -263,6 +263,13 @@ static int posix_get_tai(clockid_t which_clock, struct timespec64 *tp) return 0; } +static int posix_get_monotonic_active(clockid_t which_clock, + struct timespec64 *tp) +{ + ktime_get_active_ts64(tp); + return 0; +} + static int posix_get_hrtimer_res(clockid_t which_clock, struct timespec64 *tp) { tp->tv_sec = 0; @@ -1330,6 +1337,11 @@ static const struct k_clock clock_boottime = { .timer_arm = common_hrtimer_arm, }; +static const struct k_clock clock_monotonic_active = { + .clock_getres = posix_get_hrtimer_res, + .clock_get = posix_get_monotonic_active, +}; + static const struct k_clock * const posix_clocks[] = { [CLOCK_REALTIME] = &clock_realtime, [CLOCK_MONOTONIC] = &clock_monotonic, @@ -1342,6 +1354,7 @@ static const struct k_clock * const posix_clocks[] = { [CLOCK_REALTIME_ALARM] = &alarm_clock, [CLOCK_BOOTTIME_ALARM] = &alarm_clock, [CLOCK_TAI] = &clock_tai, + [CLOCK_MONOTONIC_ACTIVE] = &clock_monotonic_active, }; static const struct k_clock *clockid_to_kclock(const clockid_t id) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index e11760121cb2..a2b7f583e64e 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -139,6 +139,9 @@ static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec64 wtm) static inline void tk_update_sleep_time(struct timekeeper *tk, ktime_t delta) { tk->offs_boot = ktime_add(tk->offs_boot, delta); + + /* Accumulate time spent in suspend */ + tk->time_suspended += delta; } /* @@ -886,6 +889,39 @@ void ktime_get_ts64(struct timespec64 *ts) } EXPORT_SYMBOL_GPL(ktime_get_ts64); +/** + * ktime_get_active_ts64 - Get the active non-suspended monotonic clock + * @ts: pointer to timespec variable + * + * The function calculates the monotonic clock from the realtime clock and + * the wall_to_monotonic offset, subtracts the accumulated suspend time and + * stores the result in normalized timespec64 format in the variable + * pointed to by @ts. + */ +void ktime_get_active_ts64(struct timespec64 *ts) +{ + struct timekeeper *tk = &tk_core.timekeeper; + struct timespec64 tomono, tsusp; + u64 nsec, nssusp; + unsigned int seq; + + WARN_ON(timekeeping_suspended); + + do { + seq = read_seqcount_begin(&tk_core.seq); + ts->tv_sec = tk->xtime_sec; + nsec = timekeeping_get_ns(&tk->tkr_mono); + tomono = tk->wall_to_monotonic; + nssusp = tk->time_suspended; + } while (read_seqcount_retry(&tk_core.seq, seq)); + + ts->tv_sec += tomono.tv_sec; + ts->tv_nsec = 0; + timespec64_add_ns(ts, nsec + tomono.tv_nsec); + tsusp = ns_to_timespec64(nssusp); + *ts = timespec64_sub(*ts, tsusp); +} + /** * ktime_get_seconds - Get the seconds portion of CLOCK_MONOTONIC * -- cgit v1.2.3 From 32ff77e8cc9e66cc4fb38098f64fd54cc8f54573 Mon Sep 17 00:00:00 2001 From: Milind Chabbi Date: Mon, 12 Mar 2018 14:45:47 +0100 Subject: perf/core: Implement fast breakpoint modification via _IOC_MODIFY_ATTRIBUTES Problem and motivation: Once a breakpoint perf event (PERF_TYPE_BREAKPOINT) is created, there is no flexibility to change the breakpoint type (bp_type), breakpoint address (bp_addr), or breakpoint length (bp_len). The only option is to close the perf event and configure a new breakpoint event. This inflexibility has a significant performance overhead. For example, sampling-based, lightweight performance profilers (and also concurrency bug detection tools), monitor different addresses for a short duration using PERF_TYPE_BREAKPOINT and change the address (bp_addr) to another address or change the kind of breakpoint (bp_type) from "write" to a "read" or vice-versa or change the length (bp_len) of the address being monitored. The cost of these modifications is prohibitive since it involves unmapping the circular buffer associated with the perf event, closing the perf event, opening another perf event and mmaping another circular buffer. Solution: The new ioctl flag for perf events, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, introduced in this patch takes a pointer to a struct perf_event_attr as an argument to update an old breakpoint event with new address, type, and size. This facility allows retaining a previous mmaped perf events ring buffer and avoids having to close and reopen another perf event. This patch supports only changing PERF_TYPE_BREAKPOINT event type; future implementations can extend this feature. The patch replicates some of its functionality of modify_user_hw_breakpoint() in kernel/events/hw_breakpoint.c. modify_user_hw_breakpoint cannot be called directly since perf_event_ctx_lock() is already held in _perf_ioctl(). Evidence: Experiments show that the baseline (not able to modify an already created breakpoint) costs an order of magnitude (~10x) more than the suggested optimization (having the ability to dynamically modifying a configured breakpoint via ioctl). When the breakpoints typically do not trap, the speedup due to the suggested optimization is ~10x; even when the breakpoints always trap, the speedup is ~4x due to the suggested optimization. Testing: tests posted at https://github.com/linux-contrib/perf_event_modify_bp demonstrate the performance significance of this patch. Tests also check the functional correctness of the patch. Signed-off-by: Milind Chabbi [ Using modify_user_hw_breakpoint_check function. ] [ Reformated PERF_EVENT_IOC_*, so the values are all in one column. ] Signed-off-by: Jiri Olsa Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Frederic Weisbecker Cc: Hari Bathini Cc: Jin Yao Cc: Jiri Olsa Cc: Kan Liang Cc: Linus Torvalds Cc: Michael Ellerman Cc: Namhyung Kim Cc: Oleg Nesterov Cc: Peter Zijlstra Cc: Sukadev Bhattiprolu Cc: Thomas Gleixner Cc: Will Deacon Link: http://lkml.kernel.org/r/20180312134548.31532-8-jolsa@kernel.org Signed-off-by: Ingo Molnar --- include/linux/hw_breakpoint.h | 7 +++++ include/uapi/linux/perf_event.h | 23 +++++++++-------- kernel/events/core.c | 48 +++++++++++++++++++++++++++++++++++ kernel/events/hw_breakpoint.c | 2 +- tools/include/uapi/linux/perf_event.h | 23 +++++++++-------- 5 files changed, 80 insertions(+), 23 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h index cf045885a499..6058c3844a76 100644 --- a/include/linux/hw_breakpoint.h +++ b/include/linux/hw_breakpoint.h @@ -53,6 +53,9 @@ register_user_hw_breakpoint(struct perf_event_attr *attr, /* FIXME: only change from the attr, and don't unregister */ extern int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr); +extern int +modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *attr, + bool check); /* * Kernel breakpoints are not associated with any particular thread. @@ -97,6 +100,10 @@ register_user_hw_breakpoint(struct perf_event_attr *attr, static inline int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr) { return -ENOSYS; } +static inline int +modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *attr, + bool check) { return -ENOSYS; } + static inline struct perf_event * register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr, perf_overflow_handler_t triggered, diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 6f873503552d..912b85b52344 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -448,17 +448,18 @@ struct perf_event_query_bpf { /* * Ioctls that can be done on a perf event fd: */ -#define PERF_EVENT_IOC_ENABLE _IO ('$', 0) -#define PERF_EVENT_IOC_DISABLE _IO ('$', 1) -#define PERF_EVENT_IOC_REFRESH _IO ('$', 2) -#define PERF_EVENT_IOC_RESET _IO ('$', 3) -#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64) -#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) -#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) -#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) -#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32) -#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32) -#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *) +#define PERF_EVENT_IOC_ENABLE _IO ('$', 0) +#define PERF_EVENT_IOC_DISABLE _IO ('$', 1) +#define PERF_EVENT_IOC_REFRESH _IO ('$', 2) +#define PERF_EVENT_IOC_RESET _IO ('$', 3) +#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64) +#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) +#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) +#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) +#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32) +#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32) +#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *) +#define PERF_EVENT_IOC_MODIFY_ATTRIBUTES _IOW('$', 11, struct perf_event_attr *) enum perf_event_ioc_flags { PERF_IOC_FLAG_GROUP = 1U << 0, diff --git a/kernel/events/core.c b/kernel/events/core.c index ee145bdee6ed..3b4c7792a6ac 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -2846,6 +2846,41 @@ int perf_event_refresh(struct perf_event *event, int refresh) } EXPORT_SYMBOL_GPL(perf_event_refresh); +static int perf_event_modify_breakpoint(struct perf_event *bp, + struct perf_event_attr *attr) +{ + int err; + + _perf_event_disable(bp); + + err = modify_user_hw_breakpoint_check(bp, attr, true); + if (err) { + if (!bp->attr.disabled) + _perf_event_enable(bp); + + return err; + } + + if (!attr->disabled) + _perf_event_enable(bp); + return 0; +} + +static int perf_event_modify_attr(struct perf_event *event, + struct perf_event_attr *attr) +{ + if (event->attr.type != attr->type) + return -EINVAL; + + switch (event->attr.type) { + case PERF_TYPE_BREAKPOINT: + return perf_event_modify_breakpoint(event, attr); + default: + /* Place holder for future additions. */ + return -EOPNOTSUPP; + } +} + static void ctx_sched_out(struct perf_event_context *ctx, struct perf_cpu_context *cpuctx, enum event_type_t event_type) @@ -4952,6 +4987,8 @@ static int perf_event_set_output(struct perf_event *event, struct perf_event *output_event); static int perf_event_set_filter(struct perf_event *event, void __user *arg); static int perf_event_set_bpf_prog(struct perf_event *event, u32 prog_fd); +static int perf_copy_attr(struct perf_event_attr __user *uattr, + struct perf_event_attr *attr); static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned long arg) { @@ -5024,6 +5061,17 @@ static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned lon case PERF_EVENT_IOC_QUERY_BPF: return perf_event_query_prog_array(event, (void __user *)arg); + + case PERF_EVENT_IOC_MODIFY_ATTRIBUTES: { + struct perf_event_attr new_attr; + int err = perf_copy_attr((struct perf_event_attr __user *)arg, + &new_attr); + + if (err) + return err; + + return perf_event_modify_attr(event, &new_attr); + } default: return -ENOTTY; } diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c index 0c82663395f7..6253d5519cd8 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c @@ -456,7 +456,7 @@ register_user_hw_breakpoint(struct perf_event_attr *attr, } EXPORT_SYMBOL_GPL(register_user_hw_breakpoint); -static int +int modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *attr, bool check) { diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h index 6f873503552d..912b85b52344 100644 --- a/tools/include/uapi/linux/perf_event.h +++ b/tools/include/uapi/linux/perf_event.h @@ -448,17 +448,18 @@ struct perf_event_query_bpf { /* * Ioctls that can be done on a perf event fd: */ -#define PERF_EVENT_IOC_ENABLE _IO ('$', 0) -#define PERF_EVENT_IOC_DISABLE _IO ('$', 1) -#define PERF_EVENT_IOC_REFRESH _IO ('$', 2) -#define PERF_EVENT_IOC_RESET _IO ('$', 3) -#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64) -#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) -#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) -#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) -#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32) -#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32) -#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *) +#define PERF_EVENT_IOC_ENABLE _IO ('$', 0) +#define PERF_EVENT_IOC_DISABLE _IO ('$', 1) +#define PERF_EVENT_IOC_REFRESH _IO ('$', 2) +#define PERF_EVENT_IOC_RESET _IO ('$', 3) +#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64) +#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) +#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) +#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) +#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32) +#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32) +#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *) +#define PERF_EVENT_IOC_MODIFY_ATTRIBUTES _IOW('$', 11, struct perf_event_attr *) enum perf_event_ioc_flags { PERF_IOC_FLAG_GROUP = 1U << 0, -- cgit v1.2.3 From 41aeefcc38a2643a62db46818a70a781efb40d99 Mon Sep 17 00:00:00 2001 From: Linus Lüssing Date: Tue, 13 Mar 2018 11:41:12 +0100 Subject: batman-adv: add DAT cache netlink support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dump the list of DAT cache entries via the netlink socket. Signed-off-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- include/uapi/linux/batman_adv.h | 20 +++++ net/batman-adv/distributed-arp-table.c | 152 +++++++++++++++++++++++++++++++++ net/batman-adv/distributed-arp-table.h | 8 ++ net/batman-adv/netlink.c | 76 ++++++++++------- 4 files changed, 223 insertions(+), 33 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index 56ae28934070..95ab5dbd09fa 100644 --- a/include/uapi/linux/batman_adv.h +++ b/include/uapi/linux/batman_adv.h @@ -272,6 +272,21 @@ enum batadv_nl_attrs { */ BATADV_ATTR_BLA_CRC, + /** + * @BATADV_ATTR_DAT_CACHE_IP4ADDRESS: Client IPv4 address + */ + BATADV_ATTR_DAT_CACHE_IP4ADDRESS, + + /** + * @BATADV_ATTR_DAT_CACHE_HWADDRESS: Client MAC address + */ + BATADV_ATTR_DAT_CACHE_HWADDRESS, + + /** + * @BATADV_ATTR_DAT_CACHE_VID: VLAN ID + */ + BATADV_ATTR_DAT_CACHE_VID, + /* add attributes above here, update the policy in netlink.c */ /** @@ -361,6 +376,11 @@ enum batadv_nl_commands { */ BATADV_CMD_GET_BLA_BACKBONE, + /** + * @BATADV_CMD_GET_DAT_CACHE: Query list of DAT cache entries + */ + BATADV_CMD_GET_DAT_CACHE, + /* add new commands above here */ /** diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 4469dcc1558f..75dda9454ccf 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -43,13 +44,19 @@ #include #include #include +#include +#include +#include +#include #include "bridge_loop_avoidance.h" #include "hard-interface.h" #include "hash.h" #include "log.h" +#include "netlink.h" #include "originator.h" #include "send.h" +#include "soft-interface.h" #include "translation-table.h" #include "tvlv.h" @@ -851,6 +858,151 @@ out: } #endif +/** + * batadv_dat_cache_dump_entry() - dump one entry of the DAT cache table to a + * netlink socket + * @msg: buffer for the message + * @portid: netlink port + * @seq: Sequence number of netlink message + * @dat_entry: entry to dump + * + * Return: 0 or error code. + */ +static int +batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid, u32 seq, + struct batadv_dat_entry *dat_entry) +{ + int msecs; + void *hdr; + + hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, + NLM_F_MULTI, BATADV_CMD_GET_DAT_CACHE); + if (!hdr) + return -ENOBUFS; + + msecs = jiffies_to_msecs(jiffies - dat_entry->last_update); + + if (nla_put_in_addr(msg, BATADV_ATTR_DAT_CACHE_IP4ADDRESS, + dat_entry->ip) || + nla_put(msg, BATADV_ATTR_DAT_CACHE_HWADDRESS, ETH_ALEN, + dat_entry->mac_addr) || + nla_put_u16(msg, BATADV_ATTR_DAT_CACHE_VID, dat_entry->vid) || + nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, msecs)) { + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; + } + + genlmsg_end(msg, hdr); + return 0; +} + +/** + * batadv_dat_cache_dump_bucket() - dump one bucket of the DAT cache table to + * a netlink socket + * @msg: buffer for the message + * @portid: netlink port + * @seq: Sequence number of netlink message + * @head: bucket to dump + * @idx_skip: How many entries to skip + * + * Return: 0 or error code. + */ +static int +batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq, + struct hlist_head *head, int *idx_skip) +{ + struct batadv_dat_entry *dat_entry; + int idx = 0; + + rcu_read_lock(); + hlist_for_each_entry_rcu(dat_entry, head, hash_entry) { + if (idx < *idx_skip) + goto skip; + + if (batadv_dat_cache_dump_entry(msg, portid, seq, + dat_entry)) { + rcu_read_unlock(); + *idx_skip = idx; + + return -EMSGSIZE; + } + +skip: + idx++; + } + rcu_read_unlock(); + + return 0; +} + +/** + * batadv_dat_cache_dump() - dump DAT cache table to a netlink socket + * @msg: buffer for the message + * @cb: callback structure containing arguments + * + * Return: message length. + */ +int batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb) +{ + struct batadv_hard_iface *primary_if = NULL; + int portid = NETLINK_CB(cb->skb).portid; + struct net *net = sock_net(cb->skb->sk); + struct net_device *soft_iface; + struct batadv_hashtable *hash; + struct batadv_priv *bat_priv; + int bucket = cb->args[0]; + struct hlist_head *head; + int idx = cb->args[1]; + int ifindex; + int ret = 0; + + ifindex = batadv_netlink_get_ifindex(cb->nlh, + BATADV_ATTR_MESH_IFINDEX); + if (!ifindex) + return -EINVAL; + + soft_iface = dev_get_by_index(net, ifindex); + if (!soft_iface || !batadv_softif_is_valid(soft_iface)) { + ret = -ENODEV; + goto out; + } + + bat_priv = netdev_priv(soft_iface); + hash = bat_priv->dat.hash; + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) { + ret = -ENOENT; + goto out; + } + + while (bucket < hash->size) { + head = &hash->table[bucket]; + + if (batadv_dat_cache_dump_bucket(msg, portid, + cb->nlh->nlmsg_seq, head, + &idx)) + break; + + bucket++; + idx = 0; + } + + cb->args[0] = bucket; + cb->args[1] = idx; + + ret = msg->len; + +out: + if (primary_if) + batadv_hardif_put(primary_if); + + if (soft_iface) + dev_put(soft_iface); + + return ret; +} + /** * batadv_arp_get_type() - parse an ARP packet and gets the type * @bat_priv: the bat priv with all the soft interface information diff --git a/net/batman-adv/distributed-arp-table.h b/net/batman-adv/distributed-arp-table.h index e24aa9601c52..a04596028337 100644 --- a/net/batman-adv/distributed-arp-table.h +++ b/net/batman-adv/distributed-arp-table.h @@ -28,6 +28,7 @@ #include "originator.h" +struct netlink_callback; struct seq_file; struct sk_buff; @@ -81,6 +82,7 @@ batadv_dat_init_own_addr(struct batadv_priv *bat_priv, int batadv_dat_init(struct batadv_priv *bat_priv); void batadv_dat_free(struct batadv_priv *bat_priv); int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset); +int batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb); /** * batadv_dat_inc_counter() - increment the correct DAT packet counter @@ -169,6 +171,12 @@ static inline void batadv_dat_free(struct batadv_priv *bat_priv) { } +static inline int +batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb) +{ + return -EOPNOTSUPP; +} + static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv, u8 subtype) { diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 129af56b944d..2b3e7c3f87fa 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -45,6 +45,7 @@ #include "bat_algo.h" #include "bridge_loop_avoidance.h" +#include "distributed-arp-table.h" #include "gateway_client.h" #include "hard-interface.h" #include "originator.h" @@ -64,39 +65,42 @@ static const struct genl_multicast_group batadv_netlink_mcgrps[] = { }; static const struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = { - [BATADV_ATTR_VERSION] = { .type = NLA_STRING }, - [BATADV_ATTR_ALGO_NAME] = { .type = NLA_STRING }, - [BATADV_ATTR_MESH_IFINDEX] = { .type = NLA_U32 }, - [BATADV_ATTR_MESH_IFNAME] = { .type = NLA_STRING }, - [BATADV_ATTR_MESH_ADDRESS] = { .len = ETH_ALEN }, - [BATADV_ATTR_HARD_IFINDEX] = { .type = NLA_U32 }, - [BATADV_ATTR_HARD_IFNAME] = { .type = NLA_STRING }, - [BATADV_ATTR_HARD_ADDRESS] = { .len = ETH_ALEN }, - [BATADV_ATTR_ORIG_ADDRESS] = { .len = ETH_ALEN }, - [BATADV_ATTR_TPMETER_RESULT] = { .type = NLA_U8 }, - [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NLA_U32 }, - [BATADV_ATTR_TPMETER_BYTES] = { .type = NLA_U64 }, - [BATADV_ATTR_TPMETER_COOKIE] = { .type = NLA_U32 }, - [BATADV_ATTR_ACTIVE] = { .type = NLA_FLAG }, - [BATADV_ATTR_TT_ADDRESS] = { .len = ETH_ALEN }, - [BATADV_ATTR_TT_TTVN] = { .type = NLA_U8 }, - [BATADV_ATTR_TT_LAST_TTVN] = { .type = NLA_U8 }, - [BATADV_ATTR_TT_CRC32] = { .type = NLA_U32 }, - [BATADV_ATTR_TT_VID] = { .type = NLA_U16 }, - [BATADV_ATTR_TT_FLAGS] = { .type = NLA_U32 }, - [BATADV_ATTR_FLAG_BEST] = { .type = NLA_FLAG }, - [BATADV_ATTR_LAST_SEEN_MSECS] = { .type = NLA_U32 }, - [BATADV_ATTR_NEIGH_ADDRESS] = { .len = ETH_ALEN }, - [BATADV_ATTR_TQ] = { .type = NLA_U8 }, - [BATADV_ATTR_THROUGHPUT] = { .type = NLA_U32 }, - [BATADV_ATTR_BANDWIDTH_UP] = { .type = NLA_U32 }, - [BATADV_ATTR_BANDWIDTH_DOWN] = { .type = NLA_U32 }, - [BATADV_ATTR_ROUTER] = { .len = ETH_ALEN }, - [BATADV_ATTR_BLA_OWN] = { .type = NLA_FLAG }, - [BATADV_ATTR_BLA_ADDRESS] = { .len = ETH_ALEN }, - [BATADV_ATTR_BLA_VID] = { .type = NLA_U16 }, - [BATADV_ATTR_BLA_BACKBONE] = { .len = ETH_ALEN }, - [BATADV_ATTR_BLA_CRC] = { .type = NLA_U16 }, + [BATADV_ATTR_VERSION] = { .type = NLA_STRING }, + [BATADV_ATTR_ALGO_NAME] = { .type = NLA_STRING }, + [BATADV_ATTR_MESH_IFINDEX] = { .type = NLA_U32 }, + [BATADV_ATTR_MESH_IFNAME] = { .type = NLA_STRING }, + [BATADV_ATTR_MESH_ADDRESS] = { .len = ETH_ALEN }, + [BATADV_ATTR_HARD_IFINDEX] = { .type = NLA_U32 }, + [BATADV_ATTR_HARD_IFNAME] = { .type = NLA_STRING }, + [BATADV_ATTR_HARD_ADDRESS] = { .len = ETH_ALEN }, + [BATADV_ATTR_ORIG_ADDRESS] = { .len = ETH_ALEN }, + [BATADV_ATTR_TPMETER_RESULT] = { .type = NLA_U8 }, + [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NLA_U32 }, + [BATADV_ATTR_TPMETER_BYTES] = { .type = NLA_U64 }, + [BATADV_ATTR_TPMETER_COOKIE] = { .type = NLA_U32 }, + [BATADV_ATTR_ACTIVE] = { .type = NLA_FLAG }, + [BATADV_ATTR_TT_ADDRESS] = { .len = ETH_ALEN }, + [BATADV_ATTR_TT_TTVN] = { .type = NLA_U8 }, + [BATADV_ATTR_TT_LAST_TTVN] = { .type = NLA_U8 }, + [BATADV_ATTR_TT_CRC32] = { .type = NLA_U32 }, + [BATADV_ATTR_TT_VID] = { .type = NLA_U16 }, + [BATADV_ATTR_TT_FLAGS] = { .type = NLA_U32 }, + [BATADV_ATTR_FLAG_BEST] = { .type = NLA_FLAG }, + [BATADV_ATTR_LAST_SEEN_MSECS] = { .type = NLA_U32 }, + [BATADV_ATTR_NEIGH_ADDRESS] = { .len = ETH_ALEN }, + [BATADV_ATTR_TQ] = { .type = NLA_U8 }, + [BATADV_ATTR_THROUGHPUT] = { .type = NLA_U32 }, + [BATADV_ATTR_BANDWIDTH_UP] = { .type = NLA_U32 }, + [BATADV_ATTR_BANDWIDTH_DOWN] = { .type = NLA_U32 }, + [BATADV_ATTR_ROUTER] = { .len = ETH_ALEN }, + [BATADV_ATTR_BLA_OWN] = { .type = NLA_FLAG }, + [BATADV_ATTR_BLA_ADDRESS] = { .len = ETH_ALEN }, + [BATADV_ATTR_BLA_VID] = { .type = NLA_U16 }, + [BATADV_ATTR_BLA_BACKBONE] = { .len = ETH_ALEN }, + [BATADV_ATTR_BLA_CRC] = { .type = NLA_U16 }, + [BATADV_ATTR_DAT_CACHE_IP4ADDRESS] = { .type = NLA_U32 }, + [BATADV_ATTR_DAT_CACHE_HWADDRESS] = { .len = ETH_ALEN }, + [BATADV_ATTR_DAT_CACHE_VID] = { .type = NLA_U16 }, }; /** @@ -604,6 +608,12 @@ static const struct genl_ops batadv_netlink_ops[] = { .policy = batadv_netlink_policy, .dumpit = batadv_bla_backbone_dump, }, + { + .cmd = BATADV_CMD_GET_DAT_CACHE, + .flags = GENL_ADMIN_PERM, + .policy = batadv_netlink_policy, + .dumpit = batadv_dat_cache_dump, + }, }; -- cgit v1.2.3 From 53dd9a68ba683986ec90497586f94b941bb748a0 Mon Sep 17 00:00:00 2001 From: Linus Lüssing Date: Tue, 13 Mar 2018 11:41:13 +0100 Subject: batman-adv: add multicast flags netlink support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dump the list of multicast flags entries via the netlink socket. Signed-off-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- include/uapi/linux/batman_adv.h | 62 +++++++++++ net/batman-adv/multicast.c | 237 ++++++++++++++++++++++++++++++++++++++++ net/batman-adv/multicast.h | 18 +++ net/batman-adv/netlink.c | 12 ++ 4 files changed, 329 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index 95ab5dbd09fa..324a0e1143e7 100644 --- a/include/uapi/linux/batman_adv.h +++ b/include/uapi/linux/batman_adv.h @@ -91,6 +91,53 @@ enum batadv_tt_client_flags { BATADV_TT_CLIENT_TEMP = (1 << 11), }; +/** + * enum batadv_mcast_flags_priv - Private, own multicast flags + * + * These are internal, multicast related flags. Currently they describe certain + * multicast related attributes of the segment this originator bridges into the + * mesh. + * + * Those attributes are used to determine the public multicast flags this + * originator is going to announce via TT. + * + * For netlink, if BATADV_MCAST_FLAGS_BRIDGED is unset then all querier + * related flags are undefined. + */ +enum batadv_mcast_flags_priv { + /** + * @BATADV_MCAST_FLAGS_BRIDGED: There is a bridge on top of the mesh + * interface. + */ + BATADV_MCAST_FLAGS_BRIDGED = (1 << 0), + + /** + * @BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS: Whether an IGMP querier + * exists in the mesh + */ + BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS = (1 << 1), + + /** + * @BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS: Whether an MLD querier + * exists in the mesh + */ + BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS = (1 << 2), + + /** + * @BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING: If an IGMP querier + * exists, whether it is potentially shadowing multicast listeners + * (i.e. querier is behind our own bridge segment) + */ + BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING = (1 << 3), + + /** + * @BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING: If an MLD querier + * exists, whether it is potentially shadowing multicast listeners + * (i.e. querier is behind our own bridge segment) + */ + BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING = (1 << 4), +}; + /** * enum batadv_nl_attrs - batman-adv netlink attributes */ @@ -287,6 +334,16 @@ enum batadv_nl_attrs { */ BATADV_ATTR_DAT_CACHE_VID, + /** + * @BATADV_ATTR_MCAST_FLAGS: Per originator multicast flags + */ + BATADV_ATTR_MCAST_FLAGS, + + /** + * @BATADV_ATTR_MCAST_FLAGS_PRIV: Private, own multicast flags + */ + BATADV_ATTR_MCAST_FLAGS_PRIV, + /* add attributes above here, update the policy in netlink.c */ /** @@ -381,6 +438,11 @@ enum batadv_nl_commands { */ BATADV_CMD_GET_DAT_CACHE, + /** + * @BATADV_CMD_GET_MCAST_FLAGS: Query list of multicast flags + */ + BATADV_CMD_GET_MCAST_FLAGS, + /* add new commands above here */ /** diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index c7a1305ca7e7..5615b6abea6f 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -52,14 +53,20 @@ #include #include #include +#include #include #include #include +#include +#include #include +#include #include "hard-interface.h" #include "hash.h" #include "log.h" +#include "netlink.h" +#include "soft-interface.h" #include "translation-table.h" #include "tvlv.h" @@ -1333,6 +1340,236 @@ int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset) } #endif +/** + * batadv_mcast_mesh_info_put() - put multicast info into a netlink message + * @msg: buffer for the message + * @bat_priv: the bat priv with all the soft interface information + * + * Return: 0 or error code. + */ +int batadv_mcast_mesh_info_put(struct sk_buff *msg, + struct batadv_priv *bat_priv) +{ + u32 flags = bat_priv->mcast.flags; + u32 flags_priv = BATADV_NO_FLAGS; + + if (bat_priv->mcast.bridged) { + flags_priv |= BATADV_MCAST_FLAGS_BRIDGED; + + if (bat_priv->mcast.querier_ipv4.exists) + flags_priv |= BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS; + if (bat_priv->mcast.querier_ipv6.exists) + flags_priv |= BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS; + if (bat_priv->mcast.querier_ipv4.shadowing) + flags_priv |= BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING; + if (bat_priv->mcast.querier_ipv6.shadowing) + flags_priv |= BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING; + } + + if (nla_put_u32(msg, BATADV_ATTR_MCAST_FLAGS, flags) || + nla_put_u32(msg, BATADV_ATTR_MCAST_FLAGS_PRIV, flags_priv)) + return -EMSGSIZE; + + return 0; +} + +/** + * batadv_mcast_flags_dump_entry() - dump one entry of the multicast flags table + * to a netlink socket + * @msg: buffer for the message + * @portid: netlink port + * @seq: Sequence number of netlink message + * @orig_node: originator to dump the multicast flags of + * + * Return: 0 or error code. + */ +static int +batadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid, u32 seq, + struct batadv_orig_node *orig_node) +{ + void *hdr; + + hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, + NLM_F_MULTI, BATADV_CMD_GET_MCAST_FLAGS); + if (!hdr) + return -ENOBUFS; + + if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, + orig_node->orig)) { + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; + } + + if (test_bit(BATADV_ORIG_CAPA_HAS_MCAST, + &orig_node->capabilities)) { + if (nla_put_u32(msg, BATADV_ATTR_MCAST_FLAGS, + orig_node->mcast_flags)) { + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; + } + } + + genlmsg_end(msg, hdr); + return 0; +} + +/** + * batadv_mcast_flags_dump_bucket() - dump one bucket of the multicast flags + * table to a netlink socket + * @msg: buffer for the message + * @portid: netlink port + * @seq: Sequence number of netlink message + * @head: bucket to dump + * @idx_skip: How many entries to skip + * + * Return: 0 or error code. + */ +static int +batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq, + struct hlist_head *head, long *idx_skip) +{ + struct batadv_orig_node *orig_node; + long idx = 0; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, head, hash_entry) { + if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST, + &orig_node->capa_initialized)) + continue; + + if (idx < *idx_skip) + goto skip; + + if (batadv_mcast_flags_dump_entry(msg, portid, seq, + orig_node)) { + rcu_read_unlock(); + *idx_skip = idx; + + return -EMSGSIZE; + } + +skip: + idx++; + } + rcu_read_unlock(); + + return 0; +} + +/** + * __batadv_mcast_flags_dump() - dump multicast flags table to a netlink socket + * @msg: buffer for the message + * @portid: netlink port + * @seq: Sequence number of netlink message + * @bat_priv: the bat priv with all the soft interface information + * @bucket: current bucket to dump + * @idx: index in current bucket to the next entry to dump + * + * Return: 0 or error code. + */ +static int +__batadv_mcast_flags_dump(struct sk_buff *msg, u32 portid, u32 seq, + struct batadv_priv *bat_priv, long *bucket, long *idx) +{ + struct batadv_hashtable *hash = bat_priv->orig_hash; + long bucket_tmp = *bucket; + struct hlist_head *head; + long idx_tmp = *idx; + + while (bucket_tmp < hash->size) { + head = &hash->table[bucket_tmp]; + + if (batadv_mcast_flags_dump_bucket(msg, portid, seq, head, + &idx_tmp)) + break; + + bucket_tmp++; + idx_tmp = 0; + } + + *bucket = bucket_tmp; + *idx = idx_tmp; + + return msg->len; +} + +/** + * batadv_mcast_netlink_get_primary() - get primary interface from netlink + * callback + * @cb: netlink callback structure + * @primary_if: the primary interface pointer to return the result in + * + * Return: 0 or error code. + */ +static int +batadv_mcast_netlink_get_primary(struct netlink_callback *cb, + struct batadv_hard_iface **primary_if) +{ + struct batadv_hard_iface *hard_iface = NULL; + struct net *net = sock_net(cb->skb->sk); + struct net_device *soft_iface; + struct batadv_priv *bat_priv; + int ifindex; + int ret = 0; + + ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX); + if (!ifindex) + return -EINVAL; + + soft_iface = dev_get_by_index(net, ifindex); + if (!soft_iface || !batadv_softif_is_valid(soft_iface)) { + ret = -ENODEV; + goto out; + } + + bat_priv = netdev_priv(soft_iface); + + hard_iface = batadv_primary_if_get_selected(bat_priv); + if (!hard_iface || hard_iface->if_status != BATADV_IF_ACTIVE) { + ret = -ENOENT; + goto out; + } + +out: + if (soft_iface) + dev_put(soft_iface); + + if (!ret && primary_if) + *primary_if = hard_iface; + else + batadv_hardif_put(hard_iface); + + return ret; +} + +/** + * batadv_mcast_flags_dump() - dump multicast flags table to a netlink socket + * @msg: buffer for the message + * @cb: callback structure containing arguments + * + * Return: message length. + */ +int batadv_mcast_flags_dump(struct sk_buff *msg, struct netlink_callback *cb) +{ + struct batadv_hard_iface *primary_if = NULL; + int portid = NETLINK_CB(cb->skb).portid; + struct batadv_priv *bat_priv; + long *bucket = &cb->args[0]; + long *idx = &cb->args[1]; + int ret; + + ret = batadv_mcast_netlink_get_primary(cb, &primary_if); + if (ret) + return ret; + + bat_priv = netdev_priv(primary_if->soft_iface); + ret = __batadv_mcast_flags_dump(msg, portid, cb->nlh->nlmsg_seq, + bat_priv, bucket, idx); + + batadv_hardif_put(primary_if); + return ret; +} + /** * batadv_mcast_free() - free the multicast optimizations structures * @bat_priv: the bat priv with all the soft interface information diff --git a/net/batman-adv/multicast.h b/net/batman-adv/multicast.h index 6b8594e23da3..3b04ab13f0eb 100644 --- a/net/batman-adv/multicast.h +++ b/net/batman-adv/multicast.h @@ -21,6 +21,7 @@ #include "main.h" +struct netlink_callback; struct seq_file; struct sk_buff; @@ -54,6 +55,11 @@ void batadv_mcast_init(struct batadv_priv *bat_priv); int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset); +int batadv_mcast_mesh_info_put(struct sk_buff *msg, + struct batadv_priv *bat_priv); + +int batadv_mcast_flags_dump(struct sk_buff *msg, struct netlink_callback *cb); + void batadv_mcast_free(struct batadv_priv *bat_priv); void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node); @@ -72,6 +78,18 @@ static inline int batadv_mcast_init(struct batadv_priv *bat_priv) return 0; } +static inline int +batadv_mcast_mesh_info_put(struct sk_buff *msg, struct batadv_priv *bat_priv) +{ + return 0; +} + +static inline int batadv_mcast_flags_dump(struct sk_buff *msg, + struct netlink_callback *cb) +{ + return -EOPNOTSUPP; +} + static inline void batadv_mcast_free(struct batadv_priv *bat_priv) { } diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 2b3e7c3f87fa..0d9459b69bdb 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -48,6 +48,7 @@ #include "distributed-arp-table.h" #include "gateway_client.h" #include "hard-interface.h" +#include "multicast.h" #include "originator.h" #include "soft-interface.h" #include "tp_meter.h" @@ -101,6 +102,8 @@ static const struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = { [BATADV_ATTR_DAT_CACHE_IP4ADDRESS] = { .type = NLA_U32 }, [BATADV_ATTR_DAT_CACHE_HWADDRESS] = { .len = ETH_ALEN }, [BATADV_ATTR_DAT_CACHE_VID] = { .type = NLA_U16 }, + [BATADV_ATTR_MCAST_FLAGS] = { .type = NLA_U32 }, + [BATADV_ATTR_MCAST_FLAGS_PRIV] = { .type = NLA_U32 }, }; /** @@ -151,6 +154,9 @@ batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface) goto out; #endif + if (batadv_mcast_mesh_info_put(msg, bat_priv)) + goto out; + primary_if = batadv_primary_if_get_selected(bat_priv); if (primary_if && primary_if->if_status == BATADV_IF_ACTIVE) { hard_iface = primary_if->net_dev; @@ -614,6 +620,12 @@ static const struct genl_ops batadv_netlink_ops[] = { .policy = batadv_netlink_policy, .dumpit = batadv_dat_cache_dump, }, + { + .cmd = BATADV_CMD_GET_MCAST_FLAGS, + .flags = GENL_ADMIN_PERM, + .policy = batadv_netlink_policy, + .dumpit = batadv_mcast_flags_dump, + }, }; -- cgit v1.2.3 From d64c2a76123f0300b08d0557ad56e9d599872a36 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Mar 2018 13:12:26 +0100 Subject: staging: irda: remove the irda network stack and drivers No one has publicly stepped up to maintain this broken codebase for devices that no one uses anymore, so let's just drop the whole thing. If someone really wants/needs it, we can revert this and they can fix the code up to work properly. Cc: David S. Miller Signed-off-by: Greg Kroah-Hartman --- Documentation/networking/irda.txt | 10 - drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 2 - drivers/staging/irda/TODO | 4 - drivers/staging/irda/drivers/Kconfig | 398 --- drivers/staging/irda/drivers/Makefile | 44 - drivers/staging/irda/drivers/act200l-sir.c | 250 -- drivers/staging/irda/drivers/actisys-sir.c | 245 -- drivers/staging/irda/drivers/ali-ircc.c | 2217 -------------- drivers/staging/irda/drivers/ali-ircc.h | 227 -- drivers/staging/irda/drivers/au1k_ir.c | 985 ------- drivers/staging/irda/drivers/bfin_sir.c | 819 ------ drivers/staging/irda/drivers/bfin_sir.h | 93 - drivers/staging/irda/drivers/donauboe.c | 1732 ----------- drivers/staging/irda/drivers/donauboe.h | 362 --- drivers/staging/irda/drivers/esi-sir.c | 157 - drivers/staging/irda/drivers/girbil-sir.c | 252 -- drivers/staging/irda/drivers/irda-usb.c | 1906 ------------ drivers/staging/irda/drivers/irda-usb.h | 175 -- drivers/staging/irda/drivers/irtty-sir.c | 570 ---- drivers/staging/irda/drivers/irtty-sir.h | 34 - drivers/staging/irda/drivers/kingsun-sir.c | 634 ---- drivers/staging/irda/drivers/ks959-sir.c | 912 ------ drivers/staging/irda/drivers/ksdazzle-sir.c | 813 ------ drivers/staging/irda/drivers/litelink-sir.c | 199 -- drivers/staging/irda/drivers/ma600-sir.c | 253 -- drivers/staging/irda/drivers/mcp2120-sir.c | 224 -- drivers/staging/irda/drivers/mcs7780.c | 990 ------- drivers/staging/irda/drivers/mcs7780.h | 165 -- drivers/staging/irda/drivers/nsc-ircc.c | 2410 ---------------- drivers/staging/irda/drivers/nsc-ircc.h | 281 -- drivers/staging/irda/drivers/old_belkin-sir.c | 146 - drivers/staging/irda/drivers/pxaficp_ir.c | 1075 ------- drivers/staging/irda/drivers/sa1100_ir.c | 1150 -------- drivers/staging/irda/drivers/sh_sir.c | 810 ------ drivers/staging/irda/drivers/sir-dev.h | 191 -- drivers/staging/irda/drivers/sir_dev.c | 987 ------- drivers/staging/irda/drivers/sir_dongle.c | 133 - drivers/staging/irda/drivers/smsc-ircc2.c | 3026 -------------------- drivers/staging/irda/drivers/smsc-ircc2.h | 191 -- drivers/staging/irda/drivers/smsc-sio.h | 100 - drivers/staging/irda/drivers/stir4200.c | 1134 -------- drivers/staging/irda/drivers/tekram-sir.c | 225 -- drivers/staging/irda/drivers/toim3232-sir.c | 358 --- drivers/staging/irda/drivers/via-ircc.c | 1593 ----------- drivers/staging/irda/drivers/via-ircc.h | 846 ------ drivers/staging/irda/drivers/vlsi_ir.c | 1872 ------------ drivers/staging/irda/drivers/vlsi_ir.h | 757 ----- drivers/staging/irda/drivers/w83977af.h | 53 - drivers/staging/irda/drivers/w83977af_ir.c | 1285 --------- drivers/staging/irda/drivers/w83977af_ir.h | 198 -- drivers/staging/irda/include/net/irda/af_irda.h | 87 - drivers/staging/irda/include/net/irda/crc.h | 29 - drivers/staging/irda/include/net/irda/discovery.h | 95 - .../staging/irda/include/net/irda/ircomm_core.h | 106 - .../staging/irda/include/net/irda/ircomm_event.h | 83 - drivers/staging/irda/include/net/irda/ircomm_lmp.h | 36 - .../staging/irda/include/net/irda/ircomm_param.h | 147 - drivers/staging/irda/include/net/irda/ircomm_ttp.h | 37 - drivers/staging/irda/include/net/irda/ircomm_tty.h | 121 - .../irda/include/net/irda/ircomm_tty_attach.h | 92 - drivers/staging/irda/include/net/irda/irda.h | 115 - .../staging/irda/include/net/irda/irda_device.h | 285 -- drivers/staging/irda/include/net/irda/iriap.h | 108 - .../staging/irda/include/net/irda/iriap_event.h | 85 - .../staging/irda/include/net/irda/irias_object.h | 108 - .../staging/irda/include/net/irda/irlan_client.h | 42 - .../staging/irda/include/net/irda/irlan_common.h | 230 -- drivers/staging/irda/include/net/irda/irlan_eth.h | 32 - .../staging/irda/include/net/irda/irlan_event.h | 81 - .../staging/irda/include/net/irda/irlan_filter.h | 35 - .../staging/irda/include/net/irda/irlan_provider.h | 52 - drivers/staging/irda/include/net/irda/irlap.h | 311 -- .../staging/irda/include/net/irda/irlap_event.h | 129 - .../staging/irda/include/net/irda/irlap_frame.h | 167 -- drivers/staging/irda/include/net/irda/irlmp.h | 295 -- .../staging/irda/include/net/irda/irlmp_event.h | 98 - .../staging/irda/include/net/irda/irlmp_frame.h | 62 - drivers/staging/irda/include/net/irda/irmod.h | 109 - drivers/staging/irda/include/net/irda/irqueue.h | 96 - drivers/staging/irda/include/net/irda/irttp.h | 210 -- drivers/staging/irda/include/net/irda/parameters.h | 100 - drivers/staging/irda/include/net/irda/qos.h | 101 - drivers/staging/irda/include/net/irda/timer.h | 102 - drivers/staging/irda/include/net/irda/wrapper.h | 58 - drivers/staging/irda/net/Kconfig | 96 - drivers/staging/irda/net/Makefile | 17 - drivers/staging/irda/net/af_irda.c | 2694 ----------------- drivers/staging/irda/net/discovery.c | 417 --- drivers/staging/irda/net/ircomm/Kconfig | 12 - drivers/staging/irda/net/ircomm/Makefile | 8 - drivers/staging/irda/net/ircomm/ircomm_core.c | 563 ---- drivers/staging/irda/net/ircomm/ircomm_event.c | 246 -- drivers/staging/irda/net/ircomm/ircomm_lmp.c | 350 --- drivers/staging/irda/net/ircomm/ircomm_param.c | 501 ---- drivers/staging/irda/net/ircomm/ircomm_ttp.c | 350 --- drivers/staging/irda/net/ircomm/ircomm_tty.c | 1329 --------- .../staging/irda/net/ircomm/ircomm_tty_attach.c | 987 ------- drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c | 291 -- drivers/staging/irda/net/irda_device.c | 316 -- drivers/staging/irda/net/iriap.c | 1085 ------- drivers/staging/irda/net/iriap_event.c | 496 ---- drivers/staging/irda/net/irias_object.c | 555 ---- drivers/staging/irda/net/irlan/Kconfig | 14 - drivers/staging/irda/net/irlan/Makefile | 7 - drivers/staging/irda/net/irlan/irlan_client.c | 559 ---- .../staging/irda/net/irlan/irlan_client_event.c | 511 ---- drivers/staging/irda/net/irlan/irlan_common.c | 1176 -------- drivers/staging/irda/net/irlan/irlan_eth.c | 340 --- drivers/staging/irda/net/irlan/irlan_event.c | 60 - drivers/staging/irda/net/irlan/irlan_filter.c | 240 -- drivers/staging/irda/net/irlan/irlan_provider.c | 408 --- .../staging/irda/net/irlan/irlan_provider_event.c | 233 -- drivers/staging/irda/net/irlap.c | 1207 -------- drivers/staging/irda/net/irlap_event.c | 2316 --------------- drivers/staging/irda/net/irlap_frame.c | 1407 --------- drivers/staging/irda/net/irlmp.c | 1996 ------------- drivers/staging/irda/net/irlmp_event.c | 886 ------ drivers/staging/irda/net/irlmp_frame.c | 476 --- drivers/staging/irda/net/irmod.c | 199 -- drivers/staging/irda/net/irnet/Kconfig | 13 - drivers/staging/irda/net/irnet/Makefile | 7 - drivers/staging/irda/net/irnet/irnet.h | 522 ---- drivers/staging/irda/net/irnet/irnet_irda.c | 1885 ------------ drivers/staging/irda/net/irnet/irnet_irda.h | 178 -- drivers/staging/irda/net/irnet/irnet_ppp.c | 1189 -------- drivers/staging/irda/net/irnet/irnet_ppp.h | 116 - drivers/staging/irda/net/irnetlink.c | 162 -- drivers/staging/irda/net/irproc.c | 96 - drivers/staging/irda/net/irqueue.c | 912 ------ drivers/staging/irda/net/irsysctl.c | 258 -- drivers/staging/irda/net/irttp.c | 1886 ------------ drivers/staging/irda/net/parameters.c | 584 ---- drivers/staging/irda/net/qos.c | 771 ----- drivers/staging/irda/net/timer.c | 231 -- drivers/staging/irda/net/wrapper.c | 492 ---- include/uapi/linux/irda.h | 252 -- 137 files changed, 69241 deletions(-) delete mode 100644 Documentation/networking/irda.txt delete mode 100644 drivers/staging/irda/TODO delete mode 100644 drivers/staging/irda/drivers/Kconfig delete mode 100644 drivers/staging/irda/drivers/Makefile delete mode 100644 drivers/staging/irda/drivers/act200l-sir.c delete mode 100644 drivers/staging/irda/drivers/actisys-sir.c delete mode 100644 drivers/staging/irda/drivers/ali-ircc.c delete mode 100644 drivers/staging/irda/drivers/ali-ircc.h delete mode 100644 drivers/staging/irda/drivers/au1k_ir.c delete mode 100644 drivers/staging/irda/drivers/bfin_sir.c delete mode 100644 drivers/staging/irda/drivers/bfin_sir.h delete mode 100644 drivers/staging/irda/drivers/donauboe.c delete mode 100644 drivers/staging/irda/drivers/donauboe.h delete mode 100644 drivers/staging/irda/drivers/esi-sir.c delete mode 100644 drivers/staging/irda/drivers/girbil-sir.c delete mode 100644 drivers/staging/irda/drivers/irda-usb.c delete mode 100644 drivers/staging/irda/drivers/irda-usb.h delete mode 100644 drivers/staging/irda/drivers/irtty-sir.c delete mode 100644 drivers/staging/irda/drivers/irtty-sir.h delete mode 100644 drivers/staging/irda/drivers/kingsun-sir.c delete mode 100644 drivers/staging/irda/drivers/ks959-sir.c delete mode 100644 drivers/staging/irda/drivers/ksdazzle-sir.c delete mode 100644 drivers/staging/irda/drivers/litelink-sir.c delete mode 100644 drivers/staging/irda/drivers/ma600-sir.c delete mode 100644 drivers/staging/irda/drivers/mcp2120-sir.c delete mode 100644 drivers/staging/irda/drivers/mcs7780.c delete mode 100644 drivers/staging/irda/drivers/mcs7780.h delete mode 100644 drivers/staging/irda/drivers/nsc-ircc.c delete mode 100644 drivers/staging/irda/drivers/nsc-ircc.h delete mode 100644 drivers/staging/irda/drivers/old_belkin-sir.c delete mode 100644 drivers/staging/irda/drivers/pxaficp_ir.c delete mode 100644 drivers/staging/irda/drivers/sa1100_ir.c delete mode 100644 drivers/staging/irda/drivers/sh_sir.c delete mode 100644 drivers/staging/irda/drivers/sir-dev.h delete mode 100644 drivers/staging/irda/drivers/sir_dev.c delete mode 100644 drivers/staging/irda/drivers/sir_dongle.c delete mode 100644 drivers/staging/irda/drivers/smsc-ircc2.c delete mode 100644 drivers/staging/irda/drivers/smsc-ircc2.h delete mode 100644 drivers/staging/irda/drivers/smsc-sio.h delete mode 100644 drivers/staging/irda/drivers/stir4200.c delete mode 100644 drivers/staging/irda/drivers/tekram-sir.c delete mode 100644 drivers/staging/irda/drivers/toim3232-sir.c delete mode 100644 drivers/staging/irda/drivers/via-ircc.c delete mode 100644 drivers/staging/irda/drivers/via-ircc.h delete mode 100644 drivers/staging/irda/drivers/vlsi_ir.c delete mode 100644 drivers/staging/irda/drivers/vlsi_ir.h delete mode 100644 drivers/staging/irda/drivers/w83977af.h delete mode 100644 drivers/staging/irda/drivers/w83977af_ir.c delete mode 100644 drivers/staging/irda/drivers/w83977af_ir.h delete mode 100644 drivers/staging/irda/include/net/irda/af_irda.h delete mode 100644 drivers/staging/irda/include/net/irda/crc.h delete mode 100644 drivers/staging/irda/include/net/irda/discovery.h delete mode 100644 drivers/staging/irda/include/net/irda/ircomm_core.h delete mode 100644 drivers/staging/irda/include/net/irda/ircomm_event.h delete mode 100644 drivers/staging/irda/include/net/irda/ircomm_lmp.h delete mode 100644 drivers/staging/irda/include/net/irda/ircomm_param.h delete mode 100644 drivers/staging/irda/include/net/irda/ircomm_ttp.h delete mode 100644 drivers/staging/irda/include/net/irda/ircomm_tty.h delete mode 100644 drivers/staging/irda/include/net/irda/ircomm_tty_attach.h delete mode 100644 drivers/staging/irda/include/net/irda/irda.h delete mode 100644 drivers/staging/irda/include/net/irda/irda_device.h delete mode 100644 drivers/staging/irda/include/net/irda/iriap.h delete mode 100644 drivers/staging/irda/include/net/irda/iriap_event.h delete mode 100644 drivers/staging/irda/include/net/irda/irias_object.h delete mode 100644 drivers/staging/irda/include/net/irda/irlan_client.h delete mode 100644 drivers/staging/irda/include/net/irda/irlan_common.h delete mode 100644 drivers/staging/irda/include/net/irda/irlan_eth.h delete mode 100644 drivers/staging/irda/include/net/irda/irlan_event.h delete mode 100644 drivers/staging/irda/include/net/irda/irlan_filter.h delete mode 100644 drivers/staging/irda/include/net/irda/irlan_provider.h delete mode 100644 drivers/staging/irda/include/net/irda/irlap.h delete mode 100644 drivers/staging/irda/include/net/irda/irlap_event.h delete mode 100644 drivers/staging/irda/include/net/irda/irlap_frame.h delete mode 100644 drivers/staging/irda/include/net/irda/irlmp.h delete mode 100644 drivers/staging/irda/include/net/irda/irlmp_event.h delete mode 100644 drivers/staging/irda/include/net/irda/irlmp_frame.h delete mode 100644 drivers/staging/irda/include/net/irda/irmod.h delete mode 100644 drivers/staging/irda/include/net/irda/irqueue.h delete mode 100644 drivers/staging/irda/include/net/irda/irttp.h delete mode 100644 drivers/staging/irda/include/net/irda/parameters.h delete mode 100644 drivers/staging/irda/include/net/irda/qos.h delete mode 100644 drivers/staging/irda/include/net/irda/timer.h delete mode 100644 drivers/staging/irda/include/net/irda/wrapper.h delete mode 100644 drivers/staging/irda/net/Kconfig delete mode 100644 drivers/staging/irda/net/Makefile delete mode 100644 drivers/staging/irda/net/af_irda.c delete mode 100644 drivers/staging/irda/net/discovery.c delete mode 100644 drivers/staging/irda/net/ircomm/Kconfig delete mode 100644 drivers/staging/irda/net/ircomm/Makefile delete mode 100644 drivers/staging/irda/net/ircomm/ircomm_core.c delete mode 100644 drivers/staging/irda/net/ircomm/ircomm_event.c delete mode 100644 drivers/staging/irda/net/ircomm/ircomm_lmp.c delete mode 100644 drivers/staging/irda/net/ircomm/ircomm_param.c delete mode 100644 drivers/staging/irda/net/ircomm/ircomm_ttp.c delete mode 100644 drivers/staging/irda/net/ircomm/ircomm_tty.c delete mode 100644 drivers/staging/irda/net/ircomm/ircomm_tty_attach.c delete mode 100644 drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c delete mode 100644 drivers/staging/irda/net/irda_device.c delete mode 100644 drivers/staging/irda/net/iriap.c delete mode 100644 drivers/staging/irda/net/iriap_event.c delete mode 100644 drivers/staging/irda/net/irias_object.c delete mode 100644 drivers/staging/irda/net/irlan/Kconfig delete mode 100644 drivers/staging/irda/net/irlan/Makefile delete mode 100644 drivers/staging/irda/net/irlan/irlan_client.c delete mode 100644 drivers/staging/irda/net/irlan/irlan_client_event.c delete mode 100644 drivers/staging/irda/net/irlan/irlan_common.c delete mode 100644 drivers/staging/irda/net/irlan/irlan_eth.c delete mode 100644 drivers/staging/irda/net/irlan/irlan_event.c delete mode 100644 drivers/staging/irda/net/irlan/irlan_filter.c delete mode 100644 drivers/staging/irda/net/irlan/irlan_provider.c delete mode 100644 drivers/staging/irda/net/irlan/irlan_provider_event.c delete mode 100644 drivers/staging/irda/net/irlap.c delete mode 100644 drivers/staging/irda/net/irlap_event.c delete mode 100644 drivers/staging/irda/net/irlap_frame.c delete mode 100644 drivers/staging/irda/net/irlmp.c delete mode 100644 drivers/staging/irda/net/irlmp_event.c delete mode 100644 drivers/staging/irda/net/irlmp_frame.c delete mode 100644 drivers/staging/irda/net/irmod.c delete mode 100644 drivers/staging/irda/net/irnet/Kconfig delete mode 100644 drivers/staging/irda/net/irnet/Makefile delete mode 100644 drivers/staging/irda/net/irnet/irnet.h delete mode 100644 drivers/staging/irda/net/irnet/irnet_irda.c delete mode 100644 drivers/staging/irda/net/irnet/irnet_irda.h delete mode 100644 drivers/staging/irda/net/irnet/irnet_ppp.c delete mode 100644 drivers/staging/irda/net/irnet/irnet_ppp.h delete mode 100644 drivers/staging/irda/net/irnetlink.c delete mode 100644 drivers/staging/irda/net/irproc.c delete mode 100644 drivers/staging/irda/net/irqueue.c delete mode 100644 drivers/staging/irda/net/irsysctl.c delete mode 100644 drivers/staging/irda/net/irttp.c delete mode 100644 drivers/staging/irda/net/parameters.c delete mode 100644 drivers/staging/irda/net/qos.c delete mode 100644 drivers/staging/irda/net/timer.c delete mode 100644 drivers/staging/irda/net/wrapper.c delete mode 100644 include/uapi/linux/irda.h (limited to 'include/uapi/linux') diff --git a/Documentation/networking/irda.txt b/Documentation/networking/irda.txt deleted file mode 100644 index bff26c138be6..000000000000 --- a/Documentation/networking/irda.txt +++ /dev/null @@ -1,10 +0,0 @@ -To use the IrDA protocols within Linux you will need to get a suitable copy -of the IrDA Utilities. More detailed information about these and associated -programs can be found on http://irda.sourceforge.net/ - -For more information about how to use the IrDA protocol stack, see the -Linux Infrared HOWTO by Werner Heuser : - - -There is an active mailing list for discussing Linux-IrDA matters called - irda-users@lists.sourceforge.net diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 7802c262e91c..b2209b95639a 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -24,8 +24,6 @@ menuconfig STAGING if STAGING -source "drivers/staging/irda/net/Kconfig" - source "drivers/staging/ipx/Kconfig" source "drivers/staging/ncpfs/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 56afa218e285..1075c13eb456 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -5,8 +5,6 @@ obj-y += media/ obj-y += typec/ obj-$(CONFIG_IPX) += ipx/ obj-$(CONFIG_NCP_FS) += ncpfs/ -obj-$(CONFIG_IRDA) += irda/net/ -obj-$(CONFIG_IRDA) += irda/drivers/ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ obj-$(CONFIG_COMEDI) += comedi/ obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/ diff --git a/drivers/staging/irda/TODO b/drivers/staging/irda/TODO deleted file mode 100644 index 7d98a5cffaff..000000000000 --- a/drivers/staging/irda/TODO +++ /dev/null @@ -1,4 +0,0 @@ -The irda code will be removed soon from the kernel tree as it is old and -obsolete and broken. - -Don't worry about fixing up anything here, it's not needed. diff --git a/drivers/staging/irda/drivers/Kconfig b/drivers/staging/irda/drivers/Kconfig deleted file mode 100644 index e070e1222733..000000000000 --- a/drivers/staging/irda/drivers/Kconfig +++ /dev/null @@ -1,398 +0,0 @@ -menu "Infrared-port device drivers" - depends on IRDA!=n - -comment "SIR device drivers" - -config IRTTY_SIR - tristate "IrTTY (uses Linux serial driver)" - depends on IRDA && TTY - help - Say Y here if you want to build support for the IrTTY line - discipline. To compile it as a module, choose M here: the module - will be called irtty-sir. IrTTY makes it possible to use Linux's - own serial driver for all IrDA ports that are 16550 compatible. - Most IrDA chips are 16550 compatible so you should probably say Y - to this option. Using IrTTY will however limit the speed of the - connection to 115200 bps (IrDA SIR mode). - - If unsure, say Y. - -config BFIN_SIR - tristate "Blackfin SIR on UART" - depends on BLACKFIN && IRDA - default n - help - Say Y here if your want to enable SIR function on Blackfin UART - devices. - - To activate this driver you can start irattach like: - "irattach irda0 -s" - - Saying M, it will be built as a module named bfin_sir. - - Note that you need to turn off one of the serial drivers for SIR - to use that UART. - -config BFIN_SIR0 - bool "Blackfin SIR on UART0" - depends on BFIN_SIR && !SERIAL_BFIN_UART0 - -config BFIN_SIR1 - bool "Blackfin SIR on UART1" - depends on BFIN_SIR && !SERIAL_BFIN_UART1 && (!BF531 && !BF532 && !BF533 && !BF561) - -config BFIN_SIR2 - bool "Blackfin SIR on UART2" - depends on BFIN_SIR && !SERIAL_BFIN_UART2 && (BF54x || BF538 || BF539) - -config BFIN_SIR3 - bool "Blackfin SIR on UART3" - depends on BFIN_SIR && !SERIAL_BFIN_UART3 && (BF54x) - -choice - prompt "SIR Mode" - depends on BFIN_SIR - default SIR_BFIN_DMA - -config SIR_BFIN_DMA - bool "DMA mode" - depends on !DMA_UNCACHED_NONE - -config SIR_BFIN_PIO - bool "PIO mode" -endchoice - -config SH_SIR - tristate "SuperH SIR on UART" - depends on IRDA && SUPERH && \ - (CPU_SUBTYPE_SH7722 || CPU_SUBTYPE_SH7723 || \ - CPU_SUBTYPE_SH7724) - default n - help - Say Y here if your want to enable SIR function on SuperH UART - devices. - -comment "Dongle support" - -config DONGLE - bool "Serial dongle support" - depends on IRTTY_SIR - help - Say Y here if you have an infrared device that connects to your - computer's serial port. These devices are called dongles. Then say Y - or M to the driver for your particular dongle below. - - 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 serial dongles. - -config ESI_DONGLE - tristate "ESI JetEye PC dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Extended Systems - JetEye PC dongle. To compile it as a module, choose M here. The ESI - dongle attaches to the normal 9-pin serial port connector, and can - currently only be used by IrTTY. To activate support for ESI - dongles you will have to start irattach like this: - "irattach -d esi". - -config ACTISYS_DONGLE - tristate "ACTiSYS IR-220L and IR220L+ dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the ACTiSYS IR-220L and - IR220L+ dongles. To compile it as a module, choose M here. The - ACTiSYS dongles attaches to the normal 9-pin serial port connector, - and can currently only be used by IrTTY. To activate support for - ACTiSYS dongles you will have to start irattach like this: - "irattach -d actisys" or "irattach -d actisys+". - -config TEKRAM_DONGLE - tristate "Tekram IrMate 210B dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Tekram IrMate 210B - dongle. To compile it as a module, choose M here. The Tekram dongle - attaches to the normal 9-pin serial port connector, and can - currently only be used by IrTTY. To activate support for Tekram - dongles you will have to start irattach like this: - "irattach -d tekram". - -config TOIM3232_DONGLE - tristate "TOIM3232 IrDa dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Vishay/Temic - TOIM3232 and TOIM4232 based dongles. - To compile it as a module, choose M here. - -config LITELINK_DONGLE - tristate "Parallax LiteLink dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Parallax Litelink - dongle. To compile it as a module, choose M here. The Parallax - dongle attaches to the normal 9-pin serial port connector, and can - currently only be used by IrTTY. To activate support for Parallax - dongles you will have to start irattach like this: - "irattach -d litelink". - -config MA600_DONGLE - tristate "Mobile Action MA600 dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Mobile Action MA600 - dongle. To compile it as a module, choose M here. The MA600 dongle - attaches to the normal 9-pin serial port connector, and can - currently only be used by IrTTY. The driver should also support - the MA620 USB version of the dongle, if the integrated USB-to-RS232 - converter is supported by usbserial. To activate support for - MA600 dongle you will have to start irattach like this: - "irattach -d ma600". - -config GIRBIL_DONGLE - tristate "Greenwich GIrBIL dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Greenwich GIrBIL - dongle. If you want to compile it as a module, choose M here. - The Greenwich dongle attaches to the normal 9-pin serial port - connector, and can currently only be used by IrTTY. To activate - support for Greenwich dongles you will have to start irattach - like this: "irattach -d girbil". - -config MCP2120_DONGLE - tristate "Microchip MCP2120" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Microchip MCP2120 - dongle. If you want to compile it as a module, choose M here. - The MCP2120 dongle attaches to the normal 9-pin serial port - connector, and can currently only be used by IrTTY. To activate - support for MCP2120 dongles you will have to start irattach - like this: "irattach -d mcp2120". - - You must build this dongle yourself. For more information see: - - -config OLD_BELKIN_DONGLE - tristate "Old Belkin dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the Adaptec Airport 1000 - and 2000 dongles. If you want to compile it as a module, choose - M here. Some information is contained in the comments - at the top of . - -config ACT200L_DONGLE - tristate "ACTiSYS IR-200L dongle" - depends on IRTTY_SIR && DONGLE && IRDA - help - Say Y here if you want to build support for the ACTiSYS IR-200L - dongle. If you want to compile it as a module, choose M here. - The ACTiSYS IR-200L dongle attaches to the normal 9-pin serial - port connector, and can currently only be used by IrTTY. - To activate support for ACTiSYS IR-200L dongle you will have to - start irattach like this: "irattach -d act200l". - -config KINGSUN_DONGLE - tristate "KingSun/DonShine DS-620 IrDA-USB dongle" - depends on IRDA && USB - help - Say Y or M here if you want to build support for the KingSun/DonShine - DS-620 IrDA-USB bridge device driver. - - This USB bridge does not conform to the IrDA-USB device class - specification, and therefore needs its own specific driver. This - dongle supports SIR speed only (9600 bps). - - To compile it as a module, choose M here: the module will be called - kingsun-sir. - -config KSDAZZLE_DONGLE - tristate "KingSun Dazzle IrDA-USB dongle" - depends on IRDA && USB - help - Say Y or M here if you want to build support for the KingSun Dazzle - IrDA-USB bridge device driver. - - This USB bridge does not conform to the IrDA-USB device class - specification, and therefore needs its own specific driver. This - dongle supports SIR speeds only (9600 through 115200 bps). - - To compile it as a module, choose M here: the module will be called - ksdazzle-sir. - -config KS959_DONGLE - tristate "KingSun KS-959 IrDA-USB dongle" - depends on IRDA && USB - help - Say Y or M here if you want to build support for the KingSun KS-959 - IrDA-USB bridge device driver. - - This USB bridge does not conform to the IrDA-USB device class - specification, and therefore needs its own specific driver. This - dongle supports SIR speeds only (9600 through 57600 bps). - - To compile it as a module, choose M here: the module will be called - ks959-sir. - -comment "FIR device drivers" - -config USB_IRDA - tristate "IrDA USB dongles" - depends on IRDA && USB - select FW_LOADER - ---help--- - Say Y here if you want to build support for the USB IrDA FIR Dongle - device driver. To compile it as a module, choose M here: the module - will be called irda-usb. IrDA-USB support the various IrDA USB - dongles available and most of their peculiarities. Those dongles - plug in the USB port of your computer, are plug and play, and - support SIR and FIR (4Mbps) speeds. On the other hand, those - dongles tend to be less efficient than a FIR chipset. - - Please note that the driver is still experimental. And of course, - you will need both USB and IrDA support in your kernel... - -config SIGMATEL_FIR - tristate "SigmaTel STIr4200 bridge" - depends on IRDA && USB - select CRC32 - ---help--- - Say Y here if you want to build support for the SigmaTel STIr4200 - USB IrDA FIR bridge device driver. - - USB bridge based on the SigmaTel STIr4200 don't conform to the - IrDA-USB device class specification, and therefore need their - own specific driver. Those dongles support SIR and FIR (4Mbps) - speeds. - - To compile it as a module, choose M here: the module will be called - stir4200. - -config NSC_FIR - tristate "NSC PC87108/PC87338" - depends on IRDA && ISA_DMA_API - help - Say Y here if you want to build support for the NSC PC87108 and - PC87338 IrDA chipsets. This driver supports SIR, - MIR and FIR (4Mbps) speeds. - - To compile it as a module, choose M here: the module will be called - nsc-ircc. - -config WINBOND_FIR - tristate "Winbond W83977AF (IR)" - depends on IRDA && ISA_DMA_API - help - Say Y here if you want to build IrDA support for the Winbond - W83977AF super-io chipset. This driver should be used for the IrDA - chipset in the Corel NetWinder. The driver supports SIR, MIR and - FIR (4Mbps) speeds. - - To compile it as a module, choose M here: the module will be called - w83977af_ir. - -config TOSHIBA_FIR - tristate "Toshiba Type-O IR Port" - depends on IRDA && PCI && !64BIT && VIRT_TO_BUS - help - Say Y here if you want to build support for the Toshiba Type-O IR - and Donau oboe chipsets. These chipsets are used by the Toshiba - Libretto 100/110CT, Tecra 8100, Portege 7020 and many more laptops. - To compile it as a module, choose M here: the module will be called - donauboe. - -config AU1000_FIR - tristate "Alchemy IrDA SIR/FIR" - depends on IRDA && MIPS_ALCHEMY - help - Say Y/M here to build support the IrDA peripheral on the - Alchemy Au1000 and Au1100 SoCs. - Say M to build a module; it will be called au1k_ir.ko - -config SMC_IRCC_FIR - tristate "SMSC IrCC" - depends on IRDA && ISA_DMA_API - help - Say Y here if you want to build support for the SMC Infrared - Communications Controller. It is used in a wide variety of - laptops (Fujitsu, Sony, Compaq and some Toshiba). - To compile it as a module, choose M here: the module will be called - smsc-ircc2.o. - -config ALI_FIR - tristate "ALi M5123 FIR" - depends on IRDA && ISA_DMA_API - help - Say Y here if you want to build support for the ALi M5123 FIR - Controller. The ALi M5123 FIR Controller is embedded in ALi M1543C, - M1535, M1535D, M1535+, M1535D South Bridge. This driver supports - SIR, MIR and FIR (4Mbps) speeds. - - To compile it as a module, choose M here: the module will be called - ali-ircc. - -config VLSI_FIR - tristate "VLSI 82C147 SIR/MIR/FIR" - depends on IRDA && PCI - help - Say Y here if you want to build support for the VLSI 82C147 - PCI-IrDA Controller. This controller is used by the HP OmniBook 800 - and 5500 notebooks. The driver provides support for SIR, MIR and - FIR (4Mbps) speeds. - - To compile it as a module, choose M here: the module will be called - vlsi_ir. - -config SA1100_FIR - tristate "SA1100 Internal IR" - depends on ARCH_SA1100 && IRDA && DMA_SA11X0 - -config VIA_FIR - tristate "VIA VT8231/VT1211 SIR/MIR/FIR" - depends on IRDA && ISA_DMA_API - help - Say Y here if you want to build support for the VIA VT8231 - and VIA VT1211 IrDA controllers, found on the motherboards using - those VIA chipsets. To use this controller, you will need - to plug a specific 5 pins FIR IrDA dongle in the specific - motherboard connector. The driver provides support for SIR, MIR - and FIR (4Mbps) speeds. - - You will need to specify the 'dongle_id' module parameter to - indicate the FIR dongle attached to the controller. - - To compile it as a module, choose M here: the module will be called - via-ircc. - -config PXA_FICP - tristate "Intel PXA2xx Internal FICP" - depends on ARCH_PXA && IRDA - help - Say Y or M here if you want to build support for the PXA2xx - built-in IRDA interface which can support both SIR and FIR. - This driver relies on platform specific helper routines so - available capabilities may vary from one PXA2xx target to - another. - -config MCS_FIR - tristate "MosChip MCS7780 IrDA-USB dongle" - depends on IRDA && USB - select CRC32 - help - Say Y or M here if you want to build support for the MosChip - MCS7780 IrDA-USB bridge device driver. - - USB bridge based on the MosChip MCS7780 don't conform to the - IrDA-USB device class specification, and therefore need their - own specific driver. Those dongles support SIR and FIR (4Mbps) - speeds. - - To compile it as a module, choose M here: the module will be called - mcs7780. - -endmenu - diff --git a/drivers/staging/irda/drivers/Makefile b/drivers/staging/irda/drivers/Makefile deleted file mode 100644 index e2901b135528..000000000000 --- a/drivers/staging/irda/drivers/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -# -# Makefile for the Linux IrDA infrared port device drivers. -# -# 9 Aug 2000, Christoph Hellwig -# Rewritten to use lists instead of if-statements. -# - -subdir-ccflags-y += -I$(srctree)/drivers/staging/irda/include - -# FIR drivers -obj-$(CONFIG_USB_IRDA) += irda-usb.o -obj-$(CONFIG_SIGMATEL_FIR) += stir4200.o -obj-$(CONFIG_NSC_FIR) += nsc-ircc.o -obj-$(CONFIG_WINBOND_FIR) += w83977af_ir.o -obj-$(CONFIG_SA1100_FIR) += sa1100_ir.o -obj-$(CONFIG_TOSHIBA_FIR) += donauboe.o -obj-$(CONFIG_SMC_IRCC_FIR) += smsc-ircc2.o -obj-$(CONFIG_ALI_FIR) += ali-ircc.o -obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o -obj-$(CONFIG_VIA_FIR) += via-ircc.o -obj-$(CONFIG_PXA_FICP) += pxaficp_ir.o -obj-$(CONFIG_MCS_FIR) += mcs7780.o -obj-$(CONFIG_AU1000_FIR) += au1k_ir.o -# SIR drivers -obj-$(CONFIG_IRTTY_SIR) += irtty-sir.o sir-dev.o -obj-$(CONFIG_BFIN_SIR) += bfin_sir.o -obj-$(CONFIG_SH_SIR) += sh_sir.o -# dongle drivers for SIR drivers -obj-$(CONFIG_ESI_DONGLE) += esi-sir.o -obj-$(CONFIG_TEKRAM_DONGLE) += tekram-sir.o -obj-$(CONFIG_ACTISYS_DONGLE) += actisys-sir.o -obj-$(CONFIG_LITELINK_DONGLE) += litelink-sir.o -obj-$(CONFIG_GIRBIL_DONGLE) += girbil-sir.o -obj-$(CONFIG_OLD_BELKIN_DONGLE) += old_belkin-sir.o -obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o -obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o -obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o -obj-$(CONFIG_TOIM3232_DONGLE) += toim3232-sir.o -obj-$(CONFIG_KINGSUN_DONGLE) += kingsun-sir.o -obj-$(CONFIG_KSDAZZLE_DONGLE) += ksdazzle-sir.o -obj-$(CONFIG_KS959_DONGLE) += ks959-sir.o - -# The SIR helper module -sir-dev-objs := sir_dev.o sir_dongle.o diff --git a/drivers/staging/irda/drivers/act200l-sir.c b/drivers/staging/irda/drivers/act200l-sir.c deleted file mode 100644 index e8917511e1aa..000000000000 --- a/drivers/staging/irda/drivers/act200l-sir.c +++ /dev/null @@ -1,250 +0,0 @@ -/********************************************************************* - * - * Filename: act200l.c - * Version: 0.8 - * Description: Implementation for the ACTiSYS ACT-IR200L dongle - * Status: Experimental. - * Author: SHIMIZU Takuya - * Created at: Fri Aug 3 17:35:42 2001 - * Modified at: Fri Aug 17 10:22:40 2001 - * Modified by: SHIMIZU Takuya - * - * Copyright (c) 2001 SHIMIZU Takuya, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int act200l_reset(struct sir_dev *dev); -static int act200l_open(struct sir_dev *dev); -static int act200l_close(struct sir_dev *dev); -static int act200l_change_speed(struct sir_dev *dev, unsigned speed); - -/* Regsiter 0: Control register #1 */ -#define ACT200L_REG0 0x00 -#define ACT200L_TXEN 0x01 /* Enable transmitter */ -#define ACT200L_RXEN 0x02 /* Enable receiver */ - -/* Register 1: Control register #2 */ -#define ACT200L_REG1 0x10 -#define ACT200L_LODB 0x01 /* Load new baud rate count value */ -#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */ - -/* Register 4: Output Power register */ -#define ACT200L_REG4 0x40 -#define ACT200L_OP0 0x01 /* Enable LED1C output */ -#define ACT200L_OP1 0x02 /* Enable LED2C output */ -#define ACT200L_BLKR 0x04 - -/* Register 5: Receive Mode register */ -#define ACT200L_REG5 0x50 -#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */ - -/* Register 6: Receive Sensitivity register #1 */ -#define ACT200L_REG6 0x60 -#define ACT200L_RS0 0x01 /* receive threshold bit 0 */ -#define ACT200L_RS1 0x02 /* receive threshold bit 1 */ - -/* Register 7: Receive Sensitivity register #2 */ -#define ACT200L_REG7 0x70 -#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */ - -/* Register 8,9: Baud Rate Dvider register #1,#2 */ -#define ACT200L_REG8 0x80 -#define ACT200L_REG9 0x90 - -#define ACT200L_2400 0x5f -#define ACT200L_9600 0x17 -#define ACT200L_19200 0x0b -#define ACT200L_38400 0x05 -#define ACT200L_57600 0x03 -#define ACT200L_115200 0x01 - -/* Register 13: Control register #3 */ -#define ACT200L_REG13 0xd0 -#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */ - -/* Register 15: Status register */ -#define ACT200L_REG15 0xf0 - -/* Register 21: Control register #4 */ -#define ACT200L_REG21 0x50 -#define ACT200L_EXCK 0x02 /* Disable clock output driver */ -#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */ - -static struct dongle_driver act200l = { - .owner = THIS_MODULE, - .driver_name = "ACTiSYS ACT-IR200L", - .type = IRDA_ACT200L_DONGLE, - .open = act200l_open, - .close = act200l_close, - .reset = act200l_reset, - .set_speed = act200l_change_speed, -}; - -static int __init act200l_sir_init(void) -{ - return irda_register_dongle(&act200l); -} - -static void __exit act200l_sir_cleanup(void) -{ - irda_unregister_dongle(&act200l); -} - -static int act200l_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Power on the dongle */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Set the speeds we can accept */ - qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - qos->min_turn_time.bits = 0x03; - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int act200l_close(struct sir_dev *dev) -{ - /* Power off the dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function act200l_change_speed (dev, speed) - * - * Set the speed for the ACTiSYS ACT-IR200L type dongle. - * - */ -static int act200l_change_speed(struct sir_dev *dev, unsigned speed) -{ - u8 control[3]; - int ret = 0; - - /* Clear DTR and set RTS to enter command mode */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - switch (speed) { - default: - ret = -EINVAL; - /* fall through */ - case 9600: - control[0] = ACT200L_REG8 | (ACT200L_9600 & 0x0f); - control[1] = ACT200L_REG9 | ((ACT200L_9600 >> 4) & 0x0f); - break; - case 19200: - control[0] = ACT200L_REG8 | (ACT200L_19200 & 0x0f); - control[1] = ACT200L_REG9 | ((ACT200L_19200 >> 4) & 0x0f); - break; - case 38400: - control[0] = ACT200L_REG8 | (ACT200L_38400 & 0x0f); - control[1] = ACT200L_REG9 | ((ACT200L_38400 >> 4) & 0x0f); - break; - case 57600: - control[0] = ACT200L_REG8 | (ACT200L_57600 & 0x0f); - control[1] = ACT200L_REG9 | ((ACT200L_57600 >> 4) & 0x0f); - break; - case 115200: - control[0] = ACT200L_REG8 | (ACT200L_115200 & 0x0f); - control[1] = ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f); - break; - } - control[2] = ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE; - - /* Write control bytes */ - sirdev_raw_write(dev, control, 3); - msleep(5); - - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - dev->speed = speed; - return ret; -} - -/* - * Function act200l_reset (driver) - * - * Reset the ACTiSYS ACT-IR200L type dongle. - */ - -#define ACT200L_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1) -#define ACT200L_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2) - -static int act200l_reset(struct sir_dev *dev) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - static const u8 control[9] = { - ACT200L_REG15, - ACT200L_REG13 | ACT200L_SHDW, - ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL, - ACT200L_REG13, - ACT200L_REG7 | ACT200L_ENPOS, - ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1, - ACT200L_REG5 | ACT200L_RWIDL, - ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR, - ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN - }; - int ret = 0; - - switch (state) { - case SIRDEV_STATE_DONGLE_RESET: - /* Reset the dongle : set RTS low for 25 ms */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - state = ACT200L_STATE_WAIT1_RESET; - delay = 50; - break; - - case ACT200L_STATE_WAIT1_RESET: - /* Clear DTR and set RTS to enter command mode */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - udelay(25); /* better wait for some short while */ - - /* Write control bytes */ - sirdev_raw_write(dev, control, sizeof(control)); - state = ACT200L_STATE_WAIT2_RESET; - delay = 15; - break; - - case ACT200L_STATE_WAIT2_RESET: - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - dev->speed = 9600; - break; - default: - net_err_ratelimited("%s(), unknown state %d\n", - __func__, state); - ret = -1; - break; - } - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -MODULE_AUTHOR("SHIMIZU Takuya "); -MODULE_DESCRIPTION("ACTiSYS ACT-IR200L dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-10"); /* IRDA_ACT200L_DONGLE */ - -module_init(act200l_sir_init); -module_exit(act200l_sir_cleanup); diff --git a/drivers/staging/irda/drivers/actisys-sir.c b/drivers/staging/irda/drivers/actisys-sir.c deleted file mode 100644 index e224b8b99517..000000000000 --- a/drivers/staging/irda/drivers/actisys-sir.c +++ /dev/null @@ -1,245 +0,0 @@ -/********************************************************************* - * - * Filename: actisys.c - * Version: 1.1 - * Description: Implementation for the ACTiSYS IR-220L and IR-220L+ - * dongles - * Status: Beta. - * Authors: Dag Brattli (initially) - * Jean Tourrilhes (new version) - * Martin Diehl (new version for sir_dev) - * Created at: Wed Oct 21 20:02:35 1998 - * Modified at: Sun Oct 27 22:02:13 2002 - * Modified by: Martin Diehl - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 1999 Jean Tourrilhes - * Copyright (c) 2002 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -/* - * Changelog - * - * 0.8 -> 0.9999 - Jean - * o New initialisation procedure : much safer and correct - * o New procedure the change speed : much faster and simpler - * o Other cleanups & comments - * Thanks to Lichen Wang @ Actisys for his excellent help... - * - * 1.0 -> 1.1 - Martin Diehl - * modified for new sir infrastructure - */ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -/* - * Define the timing of the pulses we send to the dongle (to reset it, and - * to toggle speeds). Basically, the limit here is the propagation speed of - * the signals through the serial port, the dongle being much faster. Any - * serial port support 115 kb/s, so we are sure that pulses 8.5 us wide can - * go through cleanly . If you are on the wild side, you can try to lower - * this value (Actisys recommended me 2 us, and 0 us work for me on a P233!) - */ -#define MIN_DELAY 10 /* 10 us to be on the conservative side */ - -static int actisys_open(struct sir_dev *); -static int actisys_close(struct sir_dev *); -static int actisys_change_speed(struct sir_dev *, unsigned); -static int actisys_reset(struct sir_dev *); - -/* These are the baudrates supported, in the order available */ -/* Note : the 220L doesn't support 38400, but we will fix that below */ -static unsigned baud_rates[] = { 9600, 19200, 57600, 115200, 38400 }; - -#define MAX_SPEEDS ARRAY_SIZE(baud_rates) - -static struct dongle_driver act220l = { - .owner = THIS_MODULE, - .driver_name = "Actisys ACT-220L", - .type = IRDA_ACTISYS_DONGLE, - .open = actisys_open, - .close = actisys_close, - .reset = actisys_reset, - .set_speed = actisys_change_speed, -}; - -static struct dongle_driver act220l_plus = { - .owner = THIS_MODULE, - .driver_name = "Actisys ACT-220L+", - .type = IRDA_ACTISYS_PLUS_DONGLE, - .open = actisys_open, - .close = actisys_close, - .reset = actisys_reset, - .set_speed = actisys_change_speed, -}; - -static int __init actisys_sir_init(void) -{ - int ret; - - /* First, register an Actisys 220L dongle */ - ret = irda_register_dongle(&act220l); - if (ret < 0) - return ret; - - /* Now, register an Actisys 220L+ dongle */ - ret = irda_register_dongle(&act220l_plus); - if (ret < 0) { - irda_unregister_dongle(&act220l); - return ret; - } - return 0; -} - -static void __exit actisys_sir_cleanup(void) -{ - /* We have to remove both dongles */ - irda_unregister_dongle(&act220l_plus); - irda_unregister_dongle(&act220l); -} - -static int actisys_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Set the speeds we can accept */ - qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - - /* Remove support for 38400 if this is not a 220L+ dongle */ - if (dev->dongle_drv->type == IRDA_ACTISYS_DONGLE) - qos->baud_rate.bits &= ~IR_38400; - - qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int actisys_close(struct sir_dev *dev) -{ - /* Power off the dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function actisys_change_speed (task) - * - * Change speed of the ACTiSYS IR-220L and IR-220L+ type IrDA dongles. - * To cycle through the available baud rates, pulse RTS low for a few us. - * - * First, we reset the dongle to always start from a known state. - * Then, we cycle through the speeds by pulsing RTS low and then up. - * The dongle allow us to pulse quite fast, se we can set speed in one go, - * which is must faster ( < 100 us) and less complex than what is found - * in some other dongle drivers... - * Note that even if the new speed is the same as the current speed, - * we reassert the speed. This make sure that things are all right, - * and it's fast anyway... - * By the way, this function will work for both type of dongles, - * because the additional speed is at the end of the sequence... - */ -static int actisys_change_speed(struct sir_dev *dev, unsigned speed) -{ - int ret = 0; - int i = 0; - - pr_debug("%s(), speed=%d (was %d)\n", __func__, speed, dev->speed); - - /* dongle was already resetted from irda_request state machine, - * we are in known state (dongle default) - */ - - /* - * Now, we can set the speed requested. Send RTS pulses until we - * reach the target speed - */ - for (i = 0; i < MAX_SPEEDS; i++) { - if (speed == baud_rates[i]) { - dev->speed = speed; - break; - } - /* Set RTS low for 10 us */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - udelay(MIN_DELAY); - - /* Set RTS high for 10 us */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - udelay(MIN_DELAY); - } - - /* Check if life is sweet... */ - if (i >= MAX_SPEEDS) { - actisys_reset(dev); - ret = -EINVAL; /* This should not happen */ - } - - /* Basta lavoro, on se casse d'ici... */ - return ret; -} - -/* - * Function actisys_reset (task) - * - * Reset the Actisys type dongle. Warning, this function must only be - * called with a process context! - * - * We need to do two things in this function : - * o first make sure that the dongle is in a state where it can operate - * o second put the dongle in a know state - * - * The dongle is powered of the RTS and DTR lines. In the dongle, there - * is a big capacitor to accommodate the current spikes. This capacitor - * takes a least 50 ms to be charged. In theory, the Bios set those lines - * up, so by the time we arrive here we should be set. It doesn't hurt - * to be on the conservative side, so we will wait... - * - * Then, we set the speed to 9600 b/s to get in a known state (see in - * change_speed for details). It is needed because the IrDA stack - * has tried to set the speed immediately after our first return, - * so before we can be sure the dongle is up and running. - */ - -static int actisys_reset(struct sir_dev *dev) -{ - /* Reset the dongle : set DTR low for 10 us */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - udelay(MIN_DELAY); - - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - dev->speed = 9600; /* That's the default */ - - return 0; -} - -MODULE_AUTHOR("Dag Brattli - Jean Tourrilhes "); -MODULE_DESCRIPTION("ACTiSYS IR-220L and IR-220L+ dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-2"); /* IRDA_ACTISYS_DONGLE */ -MODULE_ALIAS("irda-dongle-3"); /* IRDA_ACTISYS_PLUS_DONGLE */ - -module_init(actisys_sir_init); -module_exit(actisys_sir_cleanup); diff --git a/drivers/staging/irda/drivers/ali-ircc.c b/drivers/staging/irda/drivers/ali-ircc.c deleted file mode 100644 index 589cd01797f4..000000000000 --- a/drivers/staging/irda/drivers/ali-ircc.c +++ /dev/null @@ -1,2217 +0,0 @@ -/********************************************************************* - * - * Filename: ali-ircc.h - * Version: 0.5 - * Description: Driver for the ALI M1535D and M1543C FIR Controller - * Status: Experimental. - * Author: Benjamin Kong - * Created at: 2000/10/16 03:46PM - * Modified at: 2001/1/3 02:55PM - * Modified by: Benjamin Kong - * Modified at: 2003/11/6 and support for ALi south-bridge chipsets M1563 - * Modified by: Clear Zhang - * - * Copyright (c) 2000 Benjamin Kong - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "ali-ircc.h" - -#define CHIP_IO_EXTENT 8 -#define BROKEN_DONGLE_ID - -#define ALI_IRCC_DRIVER_NAME "ali-ircc" - -/* Power Management */ -static int ali_ircc_suspend(struct platform_device *dev, pm_message_t state); -static int ali_ircc_resume(struct platform_device *dev); - -static struct platform_driver ali_ircc_driver = { - .suspend = ali_ircc_suspend, - .resume = ali_ircc_resume, - .driver = { - .name = ALI_IRCC_DRIVER_NAME, - }, -}; - -/* Module parameters */ -static int qos_mtt_bits = 0x07; /* 1 ms or more */ - -/* Use BIOS settions by default, but user may supply module parameters */ -static unsigned int io[] = { ~0, ~0, ~0, ~0 }; -static unsigned int irq[] = { 0, 0, 0, 0 }; -static unsigned int dma[] = { 0, 0, 0, 0 }; - -static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info); -static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info); -static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info); - -/* These are the currently known ALi south-bridge chipsets, the only one difference - * is that M1543C doesn't support HP HDSL-3600 - */ -static ali_chip_t chips[] = -{ - { "M1543", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x43, ali_ircc_probe_53, ali_ircc_init_43 }, - { "M1535", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x53, ali_ircc_probe_53, ali_ircc_init_53 }, - { "M1563", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x63, ali_ircc_probe_53, ali_ircc_init_53 }, - { NULL } -}; - -/* Max 4 instances for now */ -static struct ali_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL }; - -/* Dongle Types */ -static char *dongle_types[] = { - "TFDS6000", - "HP HSDL-3600", - "HP HSDL-1100", - "No dongle connected", -}; - -/* Some prototypes */ -static int ali_ircc_open(int i, chipio_t *info); - -static int ali_ircc_close(struct ali_ircc_cb *self); - -static int ali_ircc_setup(chipio_t *info); -static int ali_ircc_is_receiving(struct ali_ircc_cb *self); -static int ali_ircc_net_open(struct net_device *dev); -static int ali_ircc_net_close(struct net_device *dev); -static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud); - -/* SIR function */ -static netdev_tx_t ali_ircc_sir_hard_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self); -static void ali_ircc_sir_receive(struct ali_ircc_cb *self); -static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self); -static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len); -static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed); - -/* FIR function */ -static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb, - struct net_device *dev); -static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 speed); -static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self); -static int ali_ircc_dma_receive(struct ali_ircc_cb *self); -static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self); -static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self); -static void ali_ircc_dma_xmit(struct ali_ircc_cb *self); - -/* My Function */ -static int ali_ircc_read_dongle_id (int i, chipio_t *info); -static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed); - -/* ALi chip function */ -static void SIR2FIR(int iobase); -static void FIR2SIR(int iobase); -static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable); - -/* - * Function ali_ircc_init () - * - * Initialize chip. Find out whay kinds of chips we are dealing with - * and their configuration registers address - */ -static int __init ali_ircc_init(void) -{ - ali_chip_t *chip; - chipio_t info; - int ret; - int cfg, cfg_base; - int reg, revision; - int i = 0; - - ret = platform_driver_register(&ali_ircc_driver); - if (ret) { - net_err_ratelimited("%s, Can't register driver!\n", - ALI_IRCC_DRIVER_NAME); - return ret; - } - - ret = -ENODEV; - - /* Probe for all the ALi chipsets we know about */ - for (chip= chips; chip->name; chip++, i++) - { - pr_debug("%s(), Probing for %s ...\n", __func__, chip->name); - - /* Try all config registers for this chip */ - for (cfg=0; cfg<2; cfg++) - { - cfg_base = chip->cfg[cfg]; - if (!cfg_base) - continue; - - memset(&info, 0, sizeof(chipio_t)); - info.cfg_base = cfg_base; - info.fir_base = io[i]; - info.dma = dma[i]; - info.irq = irq[i]; - - - /* Enter Configuration */ - outb(chip->entr1, cfg_base); - outb(chip->entr2, cfg_base); - - /* Select Logical Device 5 Registers (UART2) */ - outb(0x07, cfg_base); - outb(0x05, cfg_base+1); - - /* Read Chip Identification Register */ - outb(chip->cid_index, cfg_base); - reg = inb(cfg_base+1); - - if (reg == chip->cid_value) - { - pr_debug("%s(), Chip found at 0x%03x\n", - __func__, cfg_base); - - outb(0x1F, cfg_base); - revision = inb(cfg_base+1); - pr_debug("%s(), Found %s chip, revision=%d\n", - __func__, chip->name, revision); - - /* - * If the user supplies the base address, then - * we init the chip, if not we probe the values - * set by the BIOS - */ - if (io[i] < 2000) - { - chip->init(chip, &info); - } - else - { - chip->probe(chip, &info); - } - - if (ali_ircc_open(i, &info) == 0) - ret = 0; - i++; - } - else - { - pr_debug("%s(), No %s chip at 0x%03x\n", - __func__, chip->name, cfg_base); - } - /* Exit configuration */ - outb(0xbb, cfg_base); - } - } - - if (ret) - platform_driver_unregister(&ali_ircc_driver); - - return ret; -} - -/* - * Function ali_ircc_cleanup () - * - * Close all configured chips - * - */ -static void __exit ali_ircc_cleanup(void) -{ - int i; - - for (i=0; i < ARRAY_SIZE(dev_self); i++) { - if (dev_self[i]) - ali_ircc_close(dev_self[i]); - } - - platform_driver_unregister(&ali_ircc_driver); - -} - -static const struct net_device_ops ali_ircc_sir_ops = { - .ndo_open = ali_ircc_net_open, - .ndo_stop = ali_ircc_net_close, - .ndo_start_xmit = ali_ircc_sir_hard_xmit, - .ndo_do_ioctl = ali_ircc_net_ioctl, -}; - -static const struct net_device_ops ali_ircc_fir_ops = { - .ndo_open = ali_ircc_net_open, - .ndo_stop = ali_ircc_net_close, - .ndo_start_xmit = ali_ircc_fir_hard_xmit, - .ndo_do_ioctl = ali_ircc_net_ioctl, -}; - -/* - * Function ali_ircc_open (int i, chipio_t *inf) - * - * Open driver instance - * - */ -static int ali_ircc_open(int i, chipio_t *info) -{ - struct net_device *dev; - struct ali_ircc_cb *self; - int dongle_id; - int err; - - if (i >= ARRAY_SIZE(dev_self)) { - net_err_ratelimited("%s(), maximum number of supported chips reached!\n", - __func__); - return -ENOMEM; - } - - /* Set FIR FIFO and DMA Threshold */ - if ((ali_ircc_setup(info)) == -1) - return -1; - - dev = alloc_irdadev(sizeof(*self)); - if (dev == NULL) { - net_err_ratelimited("%s(), can't allocate memory for control block!\n", - __func__); - return -ENOMEM; - } - - self = netdev_priv(dev); - self->netdev = dev; - spin_lock_init(&self->lock); - - /* Need to store self somewhere */ - dev_self[i] = self; - self->index = i; - - /* Initialize IO */ - self->io.cfg_base = info->cfg_base; /* In ali_ircc_probe_53 assign */ - self->io.fir_base = info->fir_base; /* info->sir_base = info->fir_base */ - self->io.sir_base = info->sir_base; /* ALi SIR and FIR use the same address */ - self->io.irq = info->irq; - self->io.fir_ext = CHIP_IO_EXTENT; - self->io.dma = info->dma; - self->io.fifo_size = 16; /* SIR: 16, FIR: 32 Benjamin 2000/11/1 */ - - /* Reserve the ioports that we need */ - if (!request_region(self->io.fir_base, self->io.fir_ext, - ALI_IRCC_DRIVER_NAME)) { - net_warn_ratelimited("%s(), can't get iobase of 0x%03x\n", - __func__, self->io.fir_base); - err = -ENODEV; - goto err_out1; - } - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - /* The only value we must override it the baudrate */ - self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| - IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); // benjamin 2000/11/8 05:27PM - - self->qos.min_turn_time.bits = qos_mtt_bits; - - irda_qos_bits_to_value(&self->qos); - - /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ - self->rx_buff.truesize = 14384; - self->tx_buff.truesize = 14384; - - /* Allocate memory if needed */ - self->rx_buff.head = - dma_zalloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL); - if (self->rx_buff.head == NULL) { - err = -ENOMEM; - goto err_out2; - } - - self->tx_buff.head = - dma_zalloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL); - if (self->tx_buff.head == NULL) { - err = -ENOMEM; - goto err_out3; - } - - self->rx_buff.in_frame = FALSE; - self->rx_buff.state = OUTSIDE_FRAME; - self->tx_buff.data = self->tx_buff.head; - self->rx_buff.data = self->rx_buff.head; - - /* Reset Tx queue info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - - /* Override the network functions we need to use */ - dev->netdev_ops = &ali_ircc_sir_ops; - - err = register_netdev(dev); - if (err) { - net_err_ratelimited("%s(), register_netdev() failed!\n", - __func__); - goto err_out4; - } - net_info_ratelimited("IrDA: Registered device %s\n", dev->name); - - /* Check dongle id */ - dongle_id = ali_ircc_read_dongle_id(i, info); - net_info_ratelimited("%s(), %s, Found dongle: %s\n", - __func__, ALI_IRCC_DRIVER_NAME, - dongle_types[dongle_id]); - - self->io.dongle_id = dongle_id; - - - return 0; - - err_out4: - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - err_out3: - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - err_out2: - release_region(self->io.fir_base, self->io.fir_ext); - err_out1: - dev_self[i] = NULL; - free_netdev(dev); - return err; -} - - -/* - * Function ali_ircc_close (self) - * - * Close driver instance - * - */ -static int __exit ali_ircc_close(struct ali_ircc_cb *self) -{ - int iobase; - - IRDA_ASSERT(self != NULL, return -1;); - - iobase = self->io.fir_base; - - /* Remove netdevice */ - unregister_netdev(self->netdev); - - /* Release the PORT that this driver is using */ - pr_debug("%s(), Releasing Region %03x\n", __func__, self->io.fir_base); - release_region(self->io.fir_base, self->io.fir_ext); - - if (self->tx_buff.head) - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - - if (self->rx_buff.head) - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - - dev_self[self->index] = NULL; - free_netdev(self->netdev); - - - return 0; -} - -/* - * Function ali_ircc_init_43 (chip, info) - * - * Initialize the ALi M1543 chip. - */ -static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info) -{ - /* All controller information like I/O address, DMA channel, IRQ - * are set by BIOS - */ - - return 0; -} - -/* - * Function ali_ircc_init_53 (chip, info) - * - * Initialize the ALi M1535 chip. - */ -static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info) -{ - /* All controller information like I/O address, DMA channel, IRQ - * are set by BIOS - */ - - return 0; -} - -/* - * Function ali_ircc_probe_53 (chip, info) - * - * Probes for the ALi M1535D or M1535 - */ -static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - int hi, low, reg; - - - /* Enter Configuration */ - outb(chip->entr1, cfg_base); - outb(chip->entr2, cfg_base); - - /* Select Logical Device 5 Registers (UART2) */ - outb(0x07, cfg_base); - outb(0x05, cfg_base+1); - - /* Read address control register */ - outb(0x60, cfg_base); - hi = inb(cfg_base+1); - outb(0x61, cfg_base); - low = inb(cfg_base+1); - info->fir_base = (hi<<8) + low; - - info->sir_base = info->fir_base; - - pr_debug("%s(), probing fir_base=0x%03x\n", __func__, info->fir_base); - - /* Read IRQ control register */ - outb(0x70, cfg_base); - reg = inb(cfg_base+1); - info->irq = reg & 0x0f; - pr_debug("%s(), probing irq=%d\n", __func__, info->irq); - - /* Read DMA channel */ - outb(0x74, cfg_base); - reg = inb(cfg_base+1); - info->dma = reg & 0x07; - - if(info->dma == 0x04) - net_warn_ratelimited("%s(), No DMA channel assigned !\n", - __func__); - else - pr_debug("%s(), probing dma=%d\n", __func__, info->dma); - - /* Read Enabled Status */ - outb(0x30, cfg_base); - reg = inb(cfg_base+1); - info->enabled = (reg & 0x80) && (reg & 0x01); - pr_debug("%s(), probing enabled=%d\n", __func__, info->enabled); - - /* Read Power Status */ - outb(0x22, cfg_base); - reg = inb(cfg_base+1); - info->suspended = (reg & 0x20); - pr_debug("%s(), probing suspended=%d\n", __func__, info->suspended); - - /* Exit configuration */ - outb(0xbb, cfg_base); - - - return 0; -} - -/* - * Function ali_ircc_setup (info) - * - * Set FIR FIFO and DMA Threshold - * Returns non-negative on success. - * - */ -static int ali_ircc_setup(chipio_t *info) -{ - unsigned char tmp; - int version; - int iobase = info->fir_base; - - - /* Locking comments : - * Most operations here need to be protected. We are called before - * the device instance is created in ali_ircc_open(), therefore - * nobody can bother us - Jean II */ - - /* Switch to FIR space */ - SIR2FIR(iobase); - - /* Master Reset */ - outb(0x40, iobase+FIR_MCR); // benjamin 2000/11/30 11:45AM - - /* Read FIR ID Version Register */ - switch_bank(iobase, BANK3); - version = inb(iobase+FIR_ID_VR); - - /* Should be 0x00 in the M1535/M1535D */ - if(version != 0x00) - { - net_err_ratelimited("%s, Wrong chip version %02x\n", - ALI_IRCC_DRIVER_NAME, version); - return -1; - } - - /* Set FIR FIFO Threshold Register */ - switch_bank(iobase, BANK1); - outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR); - - /* Set FIR DMA Threshold Register */ - outb(RX_DMA_Threshold, iobase+FIR_DMA_TR); - - /* CRC enable */ - switch_bank(iobase, BANK2); - outb(inb(iobase+FIR_IRDA_CR) | IRDA_CR_CRC, iobase+FIR_IRDA_CR); - - /* NDIS driver set TX Length here BANK2 Alias 3, Alias4*/ - - /* Switch to Bank 0 */ - switch_bank(iobase, BANK0); - - tmp = inb(iobase+FIR_LCR_B); - tmp &=~0x20; // disable SIP - tmp |= 0x80; // these two steps make RX mode - tmp &= 0xbf; - outb(tmp, iobase+FIR_LCR_B); - - /* Disable Interrupt */ - outb(0x00, iobase+FIR_IER); - - - /* Switch to SIR space */ - FIR2SIR(iobase); - - net_info_ratelimited("%s, driver loaded (Benjamin Kong)\n", - ALI_IRCC_DRIVER_NAME); - - /* Enable receive interrupts */ - // outb(UART_IER_RDI, iobase+UART_IER); //benjamin 2000/11/23 01:25PM - // Turn on the interrupts in ali_ircc_net_open - - - return 0; -} - -/* - * Function ali_ircc_read_dongle_id (int index, info) - * - * Try to read dongle identification. This procedure needs to be executed - * once after power-on/reset. It also needs to be used whenever you suspect - * that the user may have plugged/unplugged the IrDA Dongle. - */ -static int ali_ircc_read_dongle_id (int i, chipio_t *info) -{ - int dongle_id, reg; - int cfg_base = info->cfg_base; - - - /* Enter Configuration */ - outb(chips[i].entr1, cfg_base); - outb(chips[i].entr2, cfg_base); - - /* Select Logical Device 5 Registers (UART2) */ - outb(0x07, cfg_base); - outb(0x05, cfg_base+1); - - /* Read Dongle ID */ - outb(0xf0, cfg_base); - reg = inb(cfg_base+1); - dongle_id = ((reg>>6)&0x02) | ((reg>>5)&0x01); - pr_debug("%s(), probing dongle_id=%d, dongle_types=%s\n", - __func__, dongle_id, dongle_types[dongle_id]); - - /* Exit configuration */ - outb(0xbb, cfg_base); - - - return dongle_id; -} - -/* - * Function ali_ircc_interrupt (irq, dev_id, regs) - * - * An interrupt from the chip has arrived. Time to do some work - * - */ -static irqreturn_t ali_ircc_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct ali_ircc_cb *self; - int ret; - - - self = netdev_priv(dev); - - spin_lock(&self->lock); - - /* Dispatch interrupt handler for the current speed */ - if (self->io.speed > 115200) - ret = ali_ircc_fir_interrupt(self); - else - ret = ali_ircc_sir_interrupt(self); - - spin_unlock(&self->lock); - - return ret; -} -/* - * Function ali_ircc_fir_interrupt(irq, struct ali_ircc_cb *self) - * - * Handle MIR/FIR interrupt - * - */ -static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self) -{ - __u8 eir, OldMessageCount; - int iobase, tmp; - - - iobase = self->io.fir_base; - - switch_bank(iobase, BANK0); - self->InterruptID = inb(iobase+FIR_IIR); - self->BusStatus = inb(iobase+FIR_BSR); - - OldMessageCount = (self->LineStatus + 1) & 0x07; - self->LineStatus = inb(iobase+FIR_LSR); - //self->ier = inb(iobase+FIR_IER); 2000/12/1 04:32PM - eir = self->InterruptID & self->ier; /* Mask out the interesting ones */ - - pr_debug("%s(), self->InterruptID = %x\n", __func__, self->InterruptID); - pr_debug("%s(), self->LineStatus = %x\n", __func__, self->LineStatus); - pr_debug("%s(), self->ier = %x\n", __func__, self->ier); - pr_debug("%s(), eir = %x\n", __func__, eir); - - /* Disable interrupts */ - SetCOMInterrupts(self, FALSE); - - /* Tx or Rx Interrupt */ - - if (eir & IIR_EOM) - { - if (self->io.direction == IO_XMIT) /* TX */ - { - pr_debug("%s(), ******* IIR_EOM (Tx) *******\n", - __func__); - - if(ali_ircc_dma_xmit_complete(self)) - { - if (irda_device_txqueue_empty(self->netdev)) - { - /* Prepare for receive */ - ali_ircc_dma_receive(self); - self->ier = IER_EOM; - } - } - else - { - self->ier = IER_EOM; - } - - } - else /* RX */ - { - pr_debug("%s(), ******* IIR_EOM (Rx) *******\n", - __func__); - - if(OldMessageCount > ((self->LineStatus+1) & 0x07)) - { - self->rcvFramesOverflow = TRUE; - pr_debug("%s(), ******* self->rcvFramesOverflow = TRUE ********\n", - __func__); - } - - if (ali_ircc_dma_receive_complete(self)) - { - pr_debug("%s(), ******* receive complete ********\n", - __func__); - - self->ier = IER_EOM; - } - else - { - pr_debug("%s(), ******* Not receive complete ********\n", - __func__); - - self->ier = IER_EOM | IER_TIMER; - } - - } - } - /* Timer Interrupt */ - else if (eir & IIR_TIMER) - { - if(OldMessageCount > ((self->LineStatus+1) & 0x07)) - { - self->rcvFramesOverflow = TRUE; - pr_debug("%s(), ******* self->rcvFramesOverflow = TRUE *******\n", - __func__); - } - /* Disable Timer */ - switch_bank(iobase, BANK1); - tmp = inb(iobase+FIR_CR); - outb( tmp& ~CR_TIMER_EN, iobase+FIR_CR); - - /* Check if this is a Tx timer interrupt */ - if (self->io.direction == IO_XMIT) - { - ali_ircc_dma_xmit(self); - - /* Interrupt on EOM */ - self->ier = IER_EOM; - - } - else /* Rx */ - { - if(ali_ircc_dma_receive_complete(self)) - { - self->ier = IER_EOM; - } - else - { - self->ier = IER_EOM | IER_TIMER; - } - } - } - - /* Restore Interrupt */ - SetCOMInterrupts(self, TRUE); - - return IRQ_RETVAL(eir); -} - -/* - * Function ali_ircc_sir_interrupt (irq, self, eir) - * - * Handle SIR interrupt - * - */ -static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self) -{ - int iobase; - int iir, lsr; - - - iobase = self->io.sir_base; - - iir = inb(iobase+UART_IIR) & UART_IIR_ID; - if (iir) { - /* Clear interrupt */ - lsr = inb(iobase+UART_LSR); - - pr_debug("%s(), iir=%02x, lsr=%02x, iobase=%#x\n", - __func__, iir, lsr, iobase); - - switch (iir) - { - case UART_IIR_RLSI: - pr_debug("%s(), RLSI\n", __func__); - break; - case UART_IIR_RDI: - /* Receive interrupt */ - ali_ircc_sir_receive(self); - break; - case UART_IIR_THRI: - if (lsr & UART_LSR_THRE) - { - /* Transmitter ready for data */ - ali_ircc_sir_write_wakeup(self); - } - break; - default: - pr_debug("%s(), unhandled IIR=%#x\n", - __func__, iir); - break; - } - - } - - - return IRQ_RETVAL(iir); -} - - -/* - * Function ali_ircc_sir_receive (self) - * - * Receive one frame from the infrared port - * - */ -static void ali_ircc_sir_receive(struct ali_ircc_cb *self) -{ - int boguscount = 0; - int iobase; - - IRDA_ASSERT(self != NULL, return;); - - iobase = self->io.sir_base; - - /* - * Receive all characters in Rx FIFO, unwrap and unstuff them. - * async_unwrap_char will deliver all found frames - */ - do { - async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, - inb(iobase+UART_RX)); - - /* Make sure we don't stay here too long */ - if (boguscount++ > 32) { - pr_debug("%s(), breaking!\n", __func__); - break; - } - } while (inb(iobase+UART_LSR) & UART_LSR_DR); - -} - -/* - * Function ali_ircc_sir_write_wakeup (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 ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self) -{ - int actual = 0; - int iobase; - - IRDA_ASSERT(self != NULL, return;); - - - iobase = self->io.sir_base; - - /* Finished with frame? */ - if (self->tx_buff.len > 0) - { - /* Write data left in transmit buffer */ - actual = ali_ircc_sir_write(iobase, self->io.fifo_size, - self->tx_buff.data, self->tx_buff.len); - self->tx_buff.data += actual; - self->tx_buff.len -= actual; - } - else - { - if (self->new_speed) - { - /* We must wait until all data are gone */ - while(!(inb(iobase+UART_LSR) & UART_LSR_TEMT)) - pr_debug("%s(), UART_LSR_THRE\n", __func__); - - pr_debug("%s(), Changing speed! self->new_speed = %d\n", - __func__, self->new_speed); - ali_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - - // benjamin 2000/11/10 06:32PM - if (self->io.speed > 115200) - { - pr_debug("%s(), ali_ircc_change_speed from UART_LSR_TEMT\n", - __func__); - - self->ier = IER_EOM; - // SetCOMInterrupts(self, TRUE); - return; - } - } - else - { - netif_wake_queue(self->netdev); - } - - self->netdev->stats.tx_packets++; - - /* Turn on receive interrupts */ - outb(UART_IER_RDI, iobase+UART_IER); - } - -} - -static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud) -{ - struct net_device *dev = self->netdev; - int iobase; - - - pr_debug("%s(), setting speed = %d\n", __func__, baud); - - /* This function *must* be called with irq off and spin-lock. - * - Jean II */ - - iobase = self->io.fir_base; - - SetCOMInterrupts(self, FALSE); // 2000/11/24 11:43AM - - /* Go to MIR, FIR Speed */ - if (baud > 115200) - { - - - ali_ircc_fir_change_speed(self, baud); - - /* Install FIR xmit handler*/ - dev->netdev_ops = &ali_ircc_fir_ops; - - /* Enable Interuupt */ - self->ier = IER_EOM; // benjamin 2000/11/20 07:24PM - - /* Be ready for incoming frames */ - ali_ircc_dma_receive(self); // benajmin 2000/11/8 07:46PM not complete - } - /* Go to SIR Speed */ - else - { - ali_ircc_sir_change_speed(self, baud); - - /* Install SIR xmit handler*/ - dev->netdev_ops = &ali_ircc_sir_ops; - } - - - SetCOMInterrupts(self, TRUE); // 2000/11/24 11:43AM - - netif_wake_queue(self->netdev); - -} - -static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 baud) -{ - - int iobase; - struct ali_ircc_cb *self = priv; - struct net_device *dev; - - - IRDA_ASSERT(self != NULL, return;); - - dev = self->netdev; - iobase = self->io.fir_base; - - pr_debug("%s(), self->io.speed = %d, change to speed = %d\n", - __func__, self->io.speed, baud); - - /* Come from SIR speed */ - if(self->io.speed <=115200) - { - SIR2FIR(iobase); - } - - /* Update accounting for new speed */ - self->io.speed = baud; - - // Set Dongle Speed mode - ali_ircc_change_dongle_speed(self, baud); - -} - -/* - * Function ali_sir_change_speed (self, speed) - * - * Set speed of IrDA port to specified baudrate - * - */ -static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed) -{ - struct ali_ircc_cb *self = priv; - int iobase; - int fcr; /* FIFO control reg */ - int lcr; /* Line control reg */ - int divisor; - - - pr_debug("%s(), Setting speed to: %d\n", __func__, speed); - - IRDA_ASSERT(self != NULL, return;); - - iobase = self->io.sir_base; - - /* Come from MIR or FIR speed */ - if(self->io.speed >115200) - { - // Set Dongle Speed mode first - ali_ircc_change_dongle_speed(self, speed); - - FIR2SIR(iobase); - } - - // Clear Line and Auxiluary status registers 2000/11/24 11:47AM - - inb(iobase+UART_LSR); - inb(iobase+UART_SCR); - - /* Update accounting for new speed */ - self->io.speed = speed; - - divisor = 115200/speed; - - fcr = UART_FCR_ENABLE_FIFO; - - /* - * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and - * almost 1,7 ms at 19200 bps. At speeds above that we can just forget - * about this timeout since it will always be fast enough. - */ - if (self->io.speed < 38400) - fcr |= UART_FCR_TRIGGER_1; - else - fcr |= UART_FCR_TRIGGER_14; - - /* IrDA ports use 8N1 */ - lcr = UART_LCR_WLEN8; - - outb(UART_LCR_DLAB | lcr, iobase+UART_LCR); /* Set DLAB */ - outb(divisor & 0xff, iobase+UART_DLL); /* Set speed */ - outb(divisor >> 8, iobase+UART_DLM); - outb(lcr, iobase+UART_LCR); /* Set 8N1 */ - outb(fcr, iobase+UART_FCR); /* Enable FIFO's */ - - /* without this, the connection will be broken after come back from FIR speed, - but with this, the SIR connection is harder to established */ - outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR); -} - -static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed) -{ - - struct ali_ircc_cb *self = priv; - int iobase,dongle_id; - int tmp = 0; - - - iobase = self->io.fir_base; /* or iobase = self->io.sir_base; */ - dongle_id = self->io.dongle_id; - - /* We are already locked, no need to do it again */ - - pr_debug("%s(), Set Speed for %s , Speed = %d\n", - __func__, dongle_types[dongle_id], speed); - - switch_bank(iobase, BANK2); - tmp = inb(iobase+FIR_IRDA_CR); - - /* IBM type dongle */ - if(dongle_id == 0) - { - if(speed == 4000000) - { - // __ __ - // SD/MODE __| |__ __ - // __ __ - // IRTX __ __| |__ - // T1 T2 T3 T4 T5 - - tmp &= ~IRDA_CR_HDLC; // HDLC=0 - tmp |= IRDA_CR_CRC; // CRC=1 - - switch_bank(iobase, BANK2); - outb(tmp, iobase+FIR_IRDA_CR); - - // T1 -> SD/MODE:0 IRTX:0 - tmp &= ~0x09; - tmp |= 0x02; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // T2 -> SD/MODE:1 IRTX:0 - tmp &= ~0x01; - tmp |= 0x0a; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // T3 -> SD/MODE:1 IRTX:1 - tmp |= 0x0b; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // T4 -> SD/MODE:0 IRTX:1 - tmp &= ~0x08; - tmp |= 0x03; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // T5 -> SD/MODE:0 IRTX:0 - tmp &= ~0x09; - tmp |= 0x02; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // reset -> Normal TX output Signal - outb(tmp & ~0x02, iobase+FIR_IRDA_CR); - } - else /* speed <=1152000 */ - { - // __ - // SD/MODE __| |__ - // - // IRTX ________ - // T1 T2 T3 - - /* MIR 115200, 57600 */ - if (speed==1152000) - { - tmp |= 0xA0; //HDLC=1, 1.152Mbps=1 - } - else - { - tmp &=~0x80; //HDLC 0.576Mbps - tmp |= 0x20; //HDLC=1, - } - - tmp |= IRDA_CR_CRC; // CRC=1 - - switch_bank(iobase, BANK2); - outb(tmp, iobase+FIR_IRDA_CR); - - /* MIR 115200, 57600 */ - - //switch_bank(iobase, BANK2); - // T1 -> SD/MODE:0 IRTX:0 - tmp &= ~0x09; - tmp |= 0x02; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // T2 -> SD/MODE:1 IRTX:0 - tmp &= ~0x01; - tmp |= 0x0a; - outb(tmp, iobase+FIR_IRDA_CR); - - // T3 -> SD/MODE:0 IRTX:0 - tmp &= ~0x09; - tmp |= 0x02; - outb(tmp, iobase+FIR_IRDA_CR); - udelay(2); - - // reset -> Normal TX output Signal - outb(tmp & ~0x02, iobase+FIR_IRDA_CR); - } - } - else if (dongle_id == 1) /* HP HDSL-3600 */ - { - switch(speed) - { - case 4000000: - tmp &= ~IRDA_CR_HDLC; // HDLC=0 - break; - - case 1152000: - tmp |= 0xA0; // HDLC=1, 1.152Mbps=1 - break; - - case 576000: - tmp &=~0x80; // HDLC 0.576Mbps - tmp |= 0x20; // HDLC=1, - break; - } - - tmp |= IRDA_CR_CRC; // CRC=1 - - switch_bank(iobase, BANK2); - outb(tmp, iobase+FIR_IRDA_CR); - } - else /* HP HDSL-1100 */ - { - if(speed <= 115200) /* SIR */ - { - - tmp &= ~IRDA_CR_FIR_SIN; // HP sin select = 0 - - switch_bank(iobase, BANK2); - outb(tmp, iobase+FIR_IRDA_CR); - } - else /* MIR FIR */ - { - - switch(speed) - { - case 4000000: - tmp &= ~IRDA_CR_HDLC; // HDLC=0 - break; - - case 1152000: - tmp |= 0xA0; // HDLC=1, 1.152Mbps=1 - break; - - case 576000: - tmp &=~0x80; // HDLC 0.576Mbps - tmp |= 0x20; // HDLC=1, - break; - } - - tmp |= IRDA_CR_CRC; // CRC=1 - tmp |= IRDA_CR_FIR_SIN; // HP sin select = 1 - - switch_bank(iobase, BANK2); - outb(tmp, iobase+FIR_IRDA_CR); - } - } - - switch_bank(iobase, BANK0); - -} - -/* - * Function ali_ircc_sir_write (driver) - * - * Fill Tx FIFO with transmit data - * - */ -static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len) -{ - int actual = 0; - - - /* Tx FIFO should be empty! */ - if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) { - pr_debug("%s(), failed, fifo not empty!\n", __func__); - return 0; - } - - /* Fill FIFO with current frame */ - while ((fifo_size-- > 0) && (actual < len)) { - /* Transmit next byte */ - outb(buf[actual], iobase+UART_TX); - - actual++; - } - - return actual; -} - -/* - * Function ali_ircc_net_open (dev) - * - * Start the device - * - */ -static int ali_ircc_net_open(struct net_device *dev) -{ - struct ali_ircc_cb *self; - int iobase; - char hwname[32]; - - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return 0;); - - iobase = self->io.fir_base; - - /* Request IRQ and install Interrupt Handler */ - if (request_irq(self->io.irq, ali_ircc_interrupt, 0, dev->name, dev)) - { - net_warn_ratelimited("%s, unable to allocate irq=%d\n", - ALI_IRCC_DRIVER_NAME, self->io.irq); - return -EAGAIN; - } - - /* - * Always allocate the DMA channel after the IRQ, and clean up on - * failure. - */ - if (request_dma(self->io.dma, dev->name)) { - net_warn_ratelimited("%s, unable to allocate dma=%d\n", - ALI_IRCC_DRIVER_NAME, self->io.dma); - free_irq(self->io.irq, dev); - return -EAGAIN; - } - - /* Turn on interrups */ - outb(UART_IER_RDI , iobase+UART_IER); - - /* Ready to play! */ - netif_start_queue(dev); //benjamin by irport - - /* Give self a hardware name */ - sprintf(hwname, "ALI-FIR @ 0x%03x", self->io.fir_base); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - self->irlap = irlap_open(dev, &self->qos, hwname); - - - return 0; -} - -/* - * Function ali_ircc_net_close (dev) - * - * Stop the device - * - */ -static int ali_ircc_net_close(struct net_device *dev) -{ - - struct ali_ircc_cb *self; - //int iobase; - - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); - - /* Stop device */ - netif_stop_queue(dev); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - - disable_dma(self->io.dma); - - /* Disable interrupts */ - SetCOMInterrupts(self, FALSE); - - free_irq(self->io.irq, dev); - free_dma(self->io.dma); - - - return 0; -} - -/* - * Function ali_ircc_fir_hard_xmit (skb, dev) - * - * Transmit the frame - * - */ -static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct ali_ircc_cb *self; - unsigned long flags; - int iobase; - __u32 speed; - int mtt, diff; - - - self = netdev_priv(dev); - iobase = self->io.fir_base; - - netif_stop_queue(dev); - - /* Make sure tests *& speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Note : you should make sure that speed changes are not going - * to corrupt any outgoing frame. Look at nsc-ircc for the gory - * details - Jean II */ - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame */ - if (!skb->len) { - ali_ircc_change_speed(self, speed); - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else - self->new_speed = speed; - } - - /* Register and copy this frame to DMA memory */ - self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; - self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; - self->tx_fifo.tail += skb->len; - - dev->stats.tx_bytes += skb->len; - - skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, - skb->len); - self->tx_fifo.len++; - self->tx_fifo.free++; - - /* Start transmit only if there is currently no transmit going on */ - if (self->tx_fifo.len == 1) - { - /* Check if we must wait the min turn time or not */ - mtt = irda_get_mtt(skb); - - if (mtt) - { - /* Check how much time we have used already */ - diff = ktime_us_delta(ktime_get(), self->stamp); - /* self->stamp is set from ali_ircc_dma_receive_complete() */ - - pr_debug("%s(), ******* diff = %d *******\n", - __func__, diff); - - /* Check if the mtt is larger than the time we have - * already used by all the protocol processing - */ - if (mtt > diff) - { - mtt -= diff; - - /* - * Use timer if delay larger than 1000 us, and - * use udelay for smaller values which should - * be acceptable - */ - if (mtt > 500) - { - /* Adjust for timer resolution */ - mtt = (mtt+250) / 500; /* 4 discard, 5 get advanced, Let's round off */ - - pr_debug("%s(), ************** mtt = %d ***********\n", - __func__, mtt); - - /* Setup timer */ - if (mtt == 1) /* 500 us */ - { - switch_bank(iobase, BANK1); - outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); - } - else if (mtt == 2) /* 1 ms */ - { - switch_bank(iobase, BANK1); - outb(TIMER_IIR_1ms, iobase+FIR_TIMER_IIR); - } - else /* > 2ms -> 4ms */ - { - switch_bank(iobase, BANK1); - outb(TIMER_IIR_2ms, iobase+FIR_TIMER_IIR); - } - - - /* Start timer */ - outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR); - self->io.direction = IO_XMIT; - - /* Enable timer interrupt */ - self->ier = IER_TIMER; - SetCOMInterrupts(self, TRUE); - - /* Timer will take care of the rest */ - goto out; - } - else - udelay(mtt); - } // if (if (mtt > diff) - }// if (mtt) - - /* Enable EOM interrupt */ - self->ier = IER_EOM; - SetCOMInterrupts(self, TRUE); - - /* Transmit frame */ - ali_ircc_dma_xmit(self); - } // if (self->tx_fifo.len == 1) - - out: - - /* Not busy transmitting anymore if window is not full */ - if (self->tx_fifo.free < MAX_TX_WINDOW) - netif_wake_queue(self->netdev); - - /* Restore bank register */ - switch_bank(iobase, BANK0); - - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - - -static void ali_ircc_dma_xmit(struct ali_ircc_cb *self) -{ - int iobase, tmp; - unsigned char FIFO_OPTI, Hi, Lo; - - - - iobase = self->io.fir_base; - - /* FIFO threshold , this method comes from NDIS5 code */ - - if(self->tx_fifo.queue[self->tx_fifo.ptr].len < TX_FIFO_Threshold) - FIFO_OPTI = self->tx_fifo.queue[self->tx_fifo.ptr].len-1; - else - FIFO_OPTI = TX_FIFO_Threshold; - - /* Disable DMA */ - switch_bank(iobase, BANK1); - outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); - - self->io.direction = IO_XMIT; - - irda_setup_dma(self->io.dma, - ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - - self->tx_buff.head) + self->tx_buff_dma, - self->tx_fifo.queue[self->tx_fifo.ptr].len, - DMA_TX_MODE); - - /* Reset Tx FIFO */ - switch_bank(iobase, BANK0); - outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A); - - /* Set Tx FIFO threshold */ - if (self->fifo_opti_buf!=FIFO_OPTI) - { - switch_bank(iobase, BANK1); - outb(FIFO_OPTI, iobase+FIR_FIFO_TR) ; - self->fifo_opti_buf=FIFO_OPTI; - } - - /* Set Tx DMA threshold */ - switch_bank(iobase, BANK1); - outb(TX_DMA_Threshold, iobase+FIR_DMA_TR); - - /* Set max Tx frame size */ - Hi = (self->tx_fifo.queue[self->tx_fifo.ptr].len >> 8) & 0x0f; - Lo = self->tx_fifo.queue[self->tx_fifo.ptr].len & 0xff; - switch_bank(iobase, BANK2); - outb(Hi, iobase+FIR_TX_DSR_HI); - outb(Lo, iobase+FIR_TX_DSR_LO); - - /* Disable SIP , Disable Brick Wall (we don't support in TX mode), Change to TX mode */ - switch_bank(iobase, BANK0); - tmp = inb(iobase+FIR_LCR_B); - tmp &= ~0x20; // Disable SIP - outb(((unsigned char)(tmp & 0x3f) | LCR_B_TX_MODE) & ~LCR_B_BW, iobase+FIR_LCR_B); - pr_debug("%s(), *** Change to TX mode: FIR_LCR_B = 0x%x ***\n", - __func__, inb(iobase + FIR_LCR_B)); - - outb(0, iobase+FIR_LSR); - - /* Enable DMA and Burst Mode */ - switch_bank(iobase, BANK1); - outb(inb(iobase+FIR_CR) | CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR); - - switch_bank(iobase, BANK0); - -} - -static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self) -{ - int iobase; - int ret = TRUE; - - - iobase = self->io.fir_base; - - /* Disable DMA */ - switch_bank(iobase, BANK1); - outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); - - /* Check for underrun! */ - switch_bank(iobase, BANK0); - if((inb(iobase+FIR_LSR) & LSR_FRAME_ABORT) == LSR_FRAME_ABORT) - - { - net_err_ratelimited("%s(), ********* LSR_FRAME_ABORT *********\n", - __func__); - self->netdev->stats.tx_errors++; - self->netdev->stats.tx_fifo_errors++; - } - else - { - self->netdev->stats.tx_packets++; - } - - /* Check if we need to change the speed */ - if (self->new_speed) - { - ali_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - } - - /* Finished with this frame, so prepare for next */ - self->tx_fifo.ptr++; - self->tx_fifo.len--; - - /* Any frames to be sent back-to-back? */ - if (self->tx_fifo.len) - { - ali_ircc_dma_xmit(self); - - /* Not finished yet! */ - ret = FALSE; - } - else - { /* Reset Tx FIFO info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - } - - /* Make sure we have room for more frames */ - if (self->tx_fifo.free < MAX_TX_WINDOW) { - /* Not busy transmitting anymore */ - /* Tell the network layer, that we can accept more frames */ - netif_wake_queue(self->netdev); - } - - switch_bank(iobase, BANK0); - - return ret; -} - -/* - * Function ali_ircc_dma_receive (self) - * - * Get ready for receiving a frame. The device will initiate a DMA - * if it starts to receive a frame. - * - */ -static int ali_ircc_dma_receive(struct ali_ircc_cb *self) -{ - int iobase, tmp; - - - iobase = self->io.fir_base; - - /* Reset Tx FIFO info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - - /* Disable DMA */ - switch_bank(iobase, BANK1); - outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); - - /* Reset Message Count */ - switch_bank(iobase, BANK0); - outb(0x07, iobase+FIR_LSR); - - self->rcvFramesOverflow = FALSE; - - self->LineStatus = inb(iobase+FIR_LSR) ; - - /* Reset Rx FIFO info */ - self->io.direction = IO_RECV; - self->rx_buff.data = self->rx_buff.head; - - /* Reset Rx FIFO */ - // switch_bank(iobase, BANK0); - outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A); - - self->st_fifo.len = self->st_fifo.pending_bytes = 0; - self->st_fifo.tail = self->st_fifo.head = 0; - - irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, - DMA_RX_MODE); - - /* Set Receive Mode,Brick Wall */ - //switch_bank(iobase, BANK0); - tmp = inb(iobase+FIR_LCR_B); - outb((unsigned char)(tmp &0x3f) | LCR_B_RX_MODE | LCR_B_BW , iobase + FIR_LCR_B); // 2000/12/1 05:16PM - pr_debug("%s(), *** Change To RX mode: FIR_LCR_B = 0x%x ***\n", - __func__, inb(iobase + FIR_LCR_B)); - - /* Set Rx Threshold */ - switch_bank(iobase, BANK1); - outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR); - outb(RX_DMA_Threshold, iobase+FIR_DMA_TR); - - /* Enable DMA and Burst Mode */ - // switch_bank(iobase, BANK1); - outb(CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR); - - switch_bank(iobase, BANK0); - return 0; -} - -static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) -{ - struct st_fifo *st_fifo; - struct sk_buff *skb; - __u8 status, MessageCount; - int len, i, iobase, val; - - st_fifo = &self->st_fifo; - iobase = self->io.fir_base; - - switch_bank(iobase, BANK0); - MessageCount = inb(iobase+ FIR_LSR)&0x07; - - if (MessageCount > 0) - pr_debug("%s(), Message count = %d\n", __func__, MessageCount); - - for (i=0; i<=MessageCount; i++) - { - /* Bank 0 */ - switch_bank(iobase, BANK0); - status = inb(iobase+FIR_LSR); - - switch_bank(iobase, BANK2); - len = inb(iobase+FIR_RX_DSR_HI) & 0x0f; - len = len << 8; - len |= inb(iobase+FIR_RX_DSR_LO); - - pr_debug("%s(), RX Length = 0x%.2x,\n", __func__ , len); - pr_debug("%s(), RX Status = 0x%.2x,\n", __func__ , status); - - if (st_fifo->tail >= MAX_RX_WINDOW) { - pr_debug("%s(), window is full!\n", __func__); - continue; - } - - st_fifo->entries[st_fifo->tail].status = status; - st_fifo->entries[st_fifo->tail].len = len; - st_fifo->pending_bytes += len; - st_fifo->tail++; - st_fifo->len++; - } - - for (i=0; i<=MessageCount; i++) - { - /* Get first entry */ - status = st_fifo->entries[st_fifo->head].status; - len = st_fifo->entries[st_fifo->head].len; - st_fifo->pending_bytes -= len; - st_fifo->head++; - st_fifo->len--; - - /* Check for errors */ - if ((status & 0xd8) || self->rcvFramesOverflow || (len==0)) - { - pr_debug("%s(), ************* RX Errors ************\n", - __func__); - - /* Skip frame */ - self->netdev->stats.rx_errors++; - - self->rx_buff.data += len; - - if (status & LSR_FIFO_UR) - { - self->netdev->stats.rx_frame_errors++; - pr_debug("%s(), ************* FIFO Errors ************\n", - __func__); - } - if (status & LSR_FRAME_ERROR) - { - self->netdev->stats.rx_frame_errors++; - pr_debug("%s(), ************* FRAME Errors ************\n", - __func__); - } - - if (status & LSR_CRC_ERROR) - { - self->netdev->stats.rx_crc_errors++; - pr_debug("%s(), ************* CRC Errors ************\n", - __func__); - } - - if(self->rcvFramesOverflow) - { - self->netdev->stats.rx_frame_errors++; - pr_debug("%s(), ************* Overran DMA buffer ************\n", - __func__); - } - if(len == 0) - { - self->netdev->stats.rx_frame_errors++; - pr_debug("%s(), ********** Receive Frame Size = 0 *********\n", - __func__); - } - } - else - { - - if (st_fifo->pending_bytes < 32) - { - switch_bank(iobase, BANK0); - val = inb(iobase+FIR_BSR); - if ((val& BSR_FIFO_NOT_EMPTY)== 0x80) - { - pr_debug("%s(), ************* BSR_FIFO_NOT_EMPTY ************\n", - __func__); - - /* Put this entry back in fifo */ - st_fifo->head--; - st_fifo->len++; - st_fifo->pending_bytes += len; - st_fifo->entries[st_fifo->head].status = status; - st_fifo->entries[st_fifo->head].len = len; - - /* - * DMA not finished yet, so try again - * later, set timer value, resolution - * 500 us - */ - - switch_bank(iobase, BANK1); - outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); // 2001/1/2 05:07PM - - /* Enable Timer */ - outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR); - - return FALSE; /* I'll be back! */ - } - } - - /* - * Remember the time we received this frame, so we can - * reduce the min turn time a bit since we will know - * how much time we have used for protocol processing - */ - self->stamp = ktime_get(); - - skb = dev_alloc_skb(len+1); - if (!skb) { - self->netdev->stats.rx_dropped++; - - return FALSE; - } - - /* Make sure IP header gets aligned */ - skb_reserve(skb, 1); - - /* Copy frame without CRC, CRC is removed by hardware*/ - skb_put(skb, len); - skb_copy_to_linear_data(skb, self->rx_buff.data, len); - - /* Move to next frame */ - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - } - } - - switch_bank(iobase, BANK0); - - return TRUE; -} - - - -/* - * Function ali_ircc_sir_hard_xmit (skb, dev) - * - * Transmit the frame! - * - */ -static netdev_tx_t ali_ircc_sir_hard_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct ali_ircc_cb *self; - unsigned long flags; - int iobase; - __u32 speed; - - - IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); - - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); - - iobase = self->io.sir_base; - - netif_stop_queue(dev); - - /* Make sure tests *& speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Note : you should make sure that speed changes are not going - * to corrupt any outgoing frame. Look at nsc-ircc for the gory - * details - Jean II */ - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame */ - if (!skb->len) { - ali_ircc_change_speed(self, speed); - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else - self->new_speed = speed; - } - - /* Init tx buffer */ - self->tx_buff.data = self->tx_buff.head; - - /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - self->netdev->stats.tx_bytes += self->tx_buff.len; - - /* Turn on transmit finished interrupt. Will fire immediately! */ - outb(UART_IER_THRI, iobase+UART_IER); - - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - - dev_kfree_skb(skb); - - - return NETDEV_TX_OK; -} - - -/* - * Function ali_ircc_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct ali_ircc_cb *self; - unsigned long flags; - int ret = 0; - - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return -1;); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__ , dev->name, cmd); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - pr_debug("%s(), SIOCSBANDWIDTH\n", __func__); - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) - return -EPERM; - - spin_lock_irqsave(&self->lock, flags); - ali_ircc_change_speed(self, irq->ifr_baudrate); - spin_unlock_irqrestore(&self->lock, flags); - break; - case SIOCSMEDIABUSY: /* Set media busy */ - pr_debug("%s(), SIOCSMEDIABUSY\n", __func__); - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - pr_debug("%s(), SIOCGRECEIVING\n", __func__); - /* This is protected */ - irq->ifr_receiving = ali_ircc_is_receiving(self); - break; - default: - ret = -EOPNOTSUPP; - } - - - return ret; -} - -/* - * Function ali_ircc_is_receiving (self) - * - * Return TRUE is we are currently receiving a frame - * - */ -static int ali_ircc_is_receiving(struct ali_ircc_cb *self) -{ - unsigned long flags; - int status = FALSE; - int iobase; - - - IRDA_ASSERT(self != NULL, return FALSE;); - - spin_lock_irqsave(&self->lock, flags); - - if (self->io.speed > 115200) - { - iobase = self->io.fir_base; - - switch_bank(iobase, BANK1); - if((inb(iobase+FIR_FIFO_FR) & 0x3f) != 0) - { - /* We are receiving something */ - pr_debug("%s(), We are receiving something\n", - __func__); - status = TRUE; - } - switch_bank(iobase, BANK0); - } - else - { - status = (self->rx_buff.state != OUTSIDE_FRAME); - } - - spin_unlock_irqrestore(&self->lock, flags); - - - return status; -} - -static int ali_ircc_suspend(struct platform_device *dev, pm_message_t state) -{ - struct ali_ircc_cb *self = platform_get_drvdata(dev); - - net_info_ratelimited("%s, Suspending\n", ALI_IRCC_DRIVER_NAME); - - if (self->io.suspended) - return 0; - - ali_ircc_net_close(self->netdev); - - self->io.suspended = 1; - - return 0; -} - -static int ali_ircc_resume(struct platform_device *dev) -{ - struct ali_ircc_cb *self = platform_get_drvdata(dev); - - if (!self->io.suspended) - return 0; - - ali_ircc_net_open(self->netdev); - - net_info_ratelimited("%s, Waking up\n", ALI_IRCC_DRIVER_NAME); - - self->io.suspended = 0; - - return 0; -} - -/* ALi Chip Function */ - -static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable) -{ - - unsigned char newMask; - - int iobase = self->io.fir_base; /* or sir_base */ - - pr_debug("%s(), -------- Start -------- ( Enable = %d )\n", - __func__, enable); - - /* Enable the interrupt which we wish to */ - if (enable){ - if (self->io.direction == IO_XMIT) - { - if (self->io.speed > 115200) /* FIR, MIR */ - { - newMask = self->ier; - } - else /* SIR */ - { - newMask = UART_IER_THRI | UART_IER_RDI; - } - } - else { - if (self->io.speed > 115200) /* FIR, MIR */ - { - newMask = self->ier; - } - else /* SIR */ - { - newMask = UART_IER_RDI; - } - } - } - else /* Disable all the interrupts */ - { - newMask = 0x00; - - } - - //SIR and FIR has different registers - if (self->io.speed > 115200) - { - switch_bank(iobase, BANK0); - outb(newMask, iobase+FIR_IER); - } - else - outb(newMask, iobase+UART_IER); - -} - -static void SIR2FIR(int iobase) -{ - //unsigned char tmp; - - - /* Already protected (change_speed() or setup()), no need to lock. - * Jean II */ - - outb(0x28, iobase+UART_MCR); - outb(0x68, iobase+UART_MCR); - outb(0x88, iobase+UART_MCR); - - outb(0x60, iobase+FIR_MCR); /* Master Reset */ - outb(0x20, iobase+FIR_MCR); /* Master Interrupt Enable */ - - //tmp = inb(iobase+FIR_LCR_B); /* SIP enable */ - //tmp |= 0x20; - //outb(tmp, iobase+FIR_LCR_B); - -} - -static void FIR2SIR(int iobase) -{ - unsigned char val; - - - /* Already protected (change_speed() or setup()), no need to lock. - * Jean II */ - - outb(0x20, iobase+FIR_MCR); /* IRQ to low */ - outb(0x00, iobase+UART_IER); - - outb(0xA0, iobase+FIR_MCR); /* Don't set master reset */ - outb(0x00, iobase+UART_FCR); - outb(0x07, iobase+UART_FCR); - - val = inb(iobase+UART_RX); - val = inb(iobase+UART_LSR); - val = inb(iobase+UART_MSR); - -} - -MODULE_AUTHOR("Benjamin Kong "); -MODULE_DESCRIPTION("ALi FIR Controller Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" ALI_IRCC_DRIVER_NAME); - - -module_param_hw_array(io, int, ioport, NULL, 0); -MODULE_PARM_DESC(io, "Base I/O addresses"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "IRQ lines"); -module_param_hw_array(dma, int, dma, NULL, 0); -MODULE_PARM_DESC(dma, "DMA channels"); - -module_init(ali_ircc_init); -module_exit(ali_ircc_cleanup); diff --git a/drivers/staging/irda/drivers/ali-ircc.h b/drivers/staging/irda/drivers/ali-ircc.h deleted file mode 100644 index c2d9747a5108..000000000000 --- a/drivers/staging/irda/drivers/ali-ircc.h +++ /dev/null @@ -1,227 +0,0 @@ -/********************************************************************* - * - * Filename: ali-ircc.h - * Version: 0.5 - * Description: Driver for the ALI M1535D and M1543C FIR Controller - * Status: Experimental. - * Author: Benjamin Kong - * Created at: 2000/10/16 03:46PM - * Modified at: 2001/1/3 02:56PM - * Modified by: Benjamin Kong - * - * Copyright (c) 2000 Benjamin Kong - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#ifndef ALI_IRCC_H -#define ALI_IRCC_H - -#include - -#include -#include -#include -#include - -/* SIR Register */ -/* Usr definition of linux/serial_reg.h */ - -/* FIR Register */ -#define BANK0 0x20 -#define BANK1 0x21 -#define BANK2 0x22 -#define BANK3 0x23 - -#define FIR_MCR 0x07 /* Master Control Register */ - -/* Bank 0 */ -#define FIR_DR 0x00 /* Alias 0, FIR Data Register (R/W) */ -#define FIR_IER 0x01 /* Alias 1, FIR Interrupt Enable Register (R/W) */ -#define FIR_IIR 0x02 /* Alias 2, FIR Interrupt Identification Register (Read only) */ -#define FIR_LCR_A 0x03 /* Alias 3, FIR Line Control Register A (R/W) */ -#define FIR_LCR_B 0x04 /* Alias 4, FIR Line Control Register B (R/W) */ -#define FIR_LSR 0x05 /* Alias 5, FIR Line Status Register (R/W) */ -#define FIR_BSR 0x06 /* Alias 6, FIR Bus Status Register (Read only) */ - - - /* Alias 1 */ - #define IER_FIFO 0x10 /* FIR FIFO Interrupt Enable */ - #define IER_TIMER 0x20 /* Timer Interrupt Enable */ - #define IER_EOM 0x40 /* End of Message Interrupt Enable */ - #define IER_ACT 0x80 /* Active Frame Interrupt Enable */ - - /* Alias 2 */ - #define IIR_FIFO 0x10 /* FIR FIFO Interrupt */ - #define IIR_TIMER 0x20 /* Timer Interrupt */ - #define IIR_EOM 0x40 /* End of Message Interrupt */ - #define IIR_ACT 0x80 /* Active Frame Interrupt */ - - /* Alias 3 */ - #define LCR_A_FIFO_RESET 0x80 /* FIFO Reset */ - - /* Alias 4 */ - #define LCR_B_BW 0x10 /* Brick Wall */ - #define LCR_B_SIP 0x20 /* SIP Enable */ - #define LCR_B_TX_MODE 0x40 /* Transmit Mode */ - #define LCR_B_RX_MODE 0x80 /* Receive Mode */ - - /* Alias 5 */ - #define LSR_FIR_LSA 0x00 /* FIR Line Status Address */ - #define LSR_FRAME_ABORT 0x08 /* Frame Abort */ - #define LSR_CRC_ERROR 0x10 /* CRC Error */ - #define LSR_SIZE_ERROR 0x20 /* Size Error */ - #define LSR_FRAME_ERROR 0x40 /* Frame Error */ - #define LSR_FIFO_UR 0x80 /* FIFO Underrun */ - #define LSR_FIFO_OR 0x80 /* FIFO Overrun */ - - /* Alias 6 */ - #define BSR_FIFO_NOT_EMPTY 0x80 /* FIFO Not Empty */ - -/* Bank 1 */ -#define FIR_CR 0x00 /* Alias 0, FIR Configuration Register (R/W) */ -#define FIR_FIFO_TR 0x01 /* Alias 1, FIR FIFO Threshold Register (R/W) */ -#define FIR_DMA_TR 0x02 /* Alias 2, FIR DMA Threshold Register (R/W) */ -#define FIR_TIMER_IIR 0x03 /* Alias 3, FIR Timer interrupt interval register (W/O) */ -#define FIR_FIFO_FR 0x03 /* Alias 3, FIR FIFO Flag register (R/O) */ -#define FIR_FIFO_RAR 0x04 /* Alias 4, FIR FIFO Read Address register (R/O) */ -#define FIR_FIFO_WAR 0x05 /* Alias 5, FIR FIFO Write Address register (R/O) */ -#define FIR_TR 0x06 /* Alias 6, Test REgister (W/O) */ - - /* Alias 0 */ - #define CR_DMA_EN 0x01 /* DMA Enable */ - #define CR_DMA_BURST 0x02 /* DMA Burst Mode */ - #define CR_TIMER_EN 0x08 /* Timer Enable */ - - /* Alias 3 */ - #define TIMER_IIR_500 0x00 /* 500 us */ - #define TIMER_IIR_1ms 0x01 /* 1 ms */ - #define TIMER_IIR_2ms 0x02 /* 2 ms */ - #define TIMER_IIR_4ms 0x03 /* 4 ms */ - -/* Bank 2 */ -#define FIR_IRDA_CR 0x00 /* Alias 0, IrDA Control Register (R/W) */ -#define FIR_BOF_CR 0x01 /* Alias 1, BOF Count Register (R/W) */ -#define FIR_BW_CR 0x02 /* Alias 2, Brick Wall Count Register (R/W) */ -#define FIR_TX_DSR_HI 0x03 /* Alias 3, TX Data Size Register (high) (R/W) */ -#define FIR_TX_DSR_LO 0x04 /* Alias 4, TX Data Size Register (low) (R/W) */ -#define FIR_RX_DSR_HI 0x05 /* Alias 5, RX Data Size Register (high) (R/W) */ -#define FIR_RX_DSR_LO 0x06 /* Alias 6, RX Data Size Register (low) (R/W) */ - - /* Alias 0 */ - #define IRDA_CR_HDLC1152 0x80 /* 1.152Mbps HDLC Select */ - #define IRDA_CR_CRC 0X40 /* CRC Select. */ - #define IRDA_CR_HDLC 0x20 /* HDLC select. */ - #define IRDA_CR_HP_MODE 0x10 /* HP mode (read only) */ - #define IRDA_CR_SD_ST 0x08 /* SD/MODE State. */ - #define IRDA_CR_FIR_SIN 0x04 /* FIR SIN Select. */ - #define IRDA_CR_ITTX_0 0x02 /* SOUT State. IRTX force to 0 */ - #define IRDA_CR_ITTX_1 0x03 /* SOUT State. IRTX force to 1 */ - -/* Bank 3 */ -#define FIR_ID_VR 0x00 /* Alias 0, FIR ID Version Register (R/O) */ -#define FIR_MODULE_CR 0x01 /* Alias 1, FIR Module Control Register (R/W) */ -#define FIR_IO_BASE_HI 0x02 /* Alias 2, FIR Higher I/O Base Address Register (R/O) */ -#define FIR_IO_BASE_LO 0x03 /* Alias 3, FIR Lower I/O Base Address Register (R/O) */ -#define FIR_IRQ_CR 0x04 /* Alias 4, FIR IRQ Channel Register (R/O) */ -#define FIR_DMA_CR 0x05 /* Alias 5, FIR DMA Channel Register (R/O) */ - -struct ali_chip { - char *name; - int cfg[2]; - unsigned char entr1; - unsigned char entr2; - unsigned char cid_index; - unsigned char cid_value; - int (*probe)(struct ali_chip *chip, chipio_t *info); - int (*init)(struct ali_chip *chip, chipio_t *info); -}; -typedef struct ali_chip ali_chip_t; - - -/* DMA modes needed */ -#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ -#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ - -#define MAX_TX_WINDOW 7 -#define MAX_RX_WINDOW 7 - -#define TX_FIFO_Threshold 8 -#define RX_FIFO_Threshold 1 -#define TX_DMA_Threshold 1 -#define RX_DMA_Threshold 1 - -/* For storing entries in the status FIFO */ - -struct st_fifo_entry { - int status; - int len; -}; - -struct st_fifo { - struct st_fifo_entry entries[MAX_RX_WINDOW]; - int pending_bytes; - int head; - int tail; - int len; -}; - -struct frame_cb { - void *start; /* Start of frame in DMA mem */ - int len; /* Length of frame in DMA mem */ -}; - -struct tx_fifo { - struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */ - int ptr; /* Currently being sent */ - int len; /* Length of queue */ - int free; /* Next free slot */ - void *tail; /* Next free start in DMA mem */ -}; - -/* Private data for each instance */ -struct ali_ircc_cb { - - struct st_fifo st_fifo; /* Info about received frames */ - struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ - - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; /* QoS capabilities for this device */ - - chipio_t io; /* IrDA controller information */ - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - dma_addr_t tx_buff_dma; - dma_addr_t rx_buff_dma; - - __u8 ier; /* Interrupt enable register */ - - __u8 InterruptID; /* Interrupt ID */ - __u8 BusStatus; /* Bus Status */ - __u8 LineStatus; /* Line Status */ - - unsigned char rcvFramesOverflow; - - ktime_t stamp; - - spinlock_t lock; /* For serializing operations */ - - __u32 new_speed; - int index; /* Instance index */ - - unsigned char fifo_opti_buf; -}; - -static inline void switch_bank(int iobase, int bank) -{ - outb(bank, iobase+FIR_MCR); -} - -#endif /* ALI_IRCC_H */ diff --git a/drivers/staging/irda/drivers/au1k_ir.c b/drivers/staging/irda/drivers/au1k_ir.c deleted file mode 100644 index 73e3e4b041bf..000000000000 --- a/drivers/staging/irda/drivers/au1k_ir.c +++ /dev/null @@ -1,985 +0,0 @@ -/* - * Alchemy Semi Au1000 IrDA driver - * - * Copyright 2001 MontaVista Software Inc. - * Author: MontaVista Software, Inc. - * ppopov@mvista.com or source@mvista.com - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* registers */ -#define IR_RING_PTR_STATUS 0x00 -#define IR_RING_BASE_ADDR_H 0x04 -#define IR_RING_BASE_ADDR_L 0x08 -#define IR_RING_SIZE 0x0C -#define IR_RING_PROMPT 0x10 -#define IR_RING_ADDR_CMPR 0x14 -#define IR_INT_CLEAR 0x18 -#define IR_CONFIG_1 0x20 -#define IR_SIR_FLAGS 0x24 -#define IR_STATUS 0x28 -#define IR_READ_PHY_CONFIG 0x2C -#define IR_WRITE_PHY_CONFIG 0x30 -#define IR_MAX_PKT_LEN 0x34 -#define IR_RX_BYTE_CNT 0x38 -#define IR_CONFIG_2 0x3C -#define IR_ENABLE 0x40 - -/* Config1 */ -#define IR_RX_INVERT_LED (1 << 0) -#define IR_TX_INVERT_LED (1 << 1) -#define IR_ST (1 << 2) -#define IR_SF (1 << 3) -#define IR_SIR (1 << 4) -#define IR_MIR (1 << 5) -#define IR_FIR (1 << 6) -#define IR_16CRC (1 << 7) -#define IR_TD (1 << 8) -#define IR_RX_ALL (1 << 9) -#define IR_DMA_ENABLE (1 << 10) -#define IR_RX_ENABLE (1 << 11) -#define IR_TX_ENABLE (1 << 12) -#define IR_LOOPBACK (1 << 14) -#define IR_SIR_MODE (IR_SIR | IR_DMA_ENABLE | \ - IR_RX_ALL | IR_RX_ENABLE | IR_SF | \ - IR_16CRC) - -/* ir_status */ -#define IR_RX_STATUS (1 << 9) -#define IR_TX_STATUS (1 << 10) -#define IR_PHYEN (1 << 15) - -/* ir_write_phy_config */ -#define IR_BR(x) (((x) & 0x3f) << 10) /* baud rate */ -#define IR_PW(x) (((x) & 0x1f) << 5) /* pulse width */ -#define IR_P(x) ((x) & 0x1f) /* preamble bits */ - -/* Config2 */ -#define IR_MODE_INV (1 << 0) -#define IR_ONE_PIN (1 << 1) -#define IR_PHYCLK_40MHZ (0 << 2) -#define IR_PHYCLK_48MHZ (1 << 2) -#define IR_PHYCLK_56MHZ (2 << 2) -#define IR_PHYCLK_64MHZ (3 << 2) -#define IR_DP (1 << 4) -#define IR_DA (1 << 5) -#define IR_FLT_HIGH (0 << 6) -#define IR_FLT_MEDHI (1 << 6) -#define IR_FLT_MEDLO (2 << 6) -#define IR_FLT_LO (3 << 6) -#define IR_IEN (1 << 8) - -/* ir_enable */ -#define IR_HC (1 << 3) /* divide SBUS clock by 2 */ -#define IR_CE (1 << 2) /* clock enable */ -#define IR_C (1 << 1) /* coherency bit */ -#define IR_BE (1 << 0) /* set in big endian mode */ - -#define NUM_IR_DESC 64 -#define RING_SIZE_4 0x0 -#define RING_SIZE_16 0x3 -#define RING_SIZE_64 0xF -#define MAX_NUM_IR_DESC 64 -#define MAX_BUF_SIZE 2048 - -/* Ring descriptor flags */ -#define AU_OWN (1 << 7) /* tx,rx */ -#define IR_DIS_CRC (1 << 6) /* tx */ -#define IR_BAD_CRC (1 << 5) /* tx */ -#define IR_NEED_PULSE (1 << 4) /* tx */ -#define IR_FORCE_UNDER (1 << 3) /* tx */ -#define IR_DISABLE_TX (1 << 2) /* tx */ -#define IR_HW_UNDER (1 << 0) /* tx */ -#define IR_TX_ERROR (IR_DIS_CRC | IR_BAD_CRC | IR_HW_UNDER) - -#define IR_PHY_ERROR (1 << 6) /* rx */ -#define IR_CRC_ERROR (1 << 5) /* rx */ -#define IR_MAX_LEN (1 << 4) /* rx */ -#define IR_FIFO_OVER (1 << 3) /* rx */ -#define IR_SIR_ERROR (1 << 2) /* rx */ -#define IR_RX_ERROR (IR_PHY_ERROR | IR_CRC_ERROR | \ - IR_MAX_LEN | IR_FIFO_OVER | IR_SIR_ERROR) - -struct db_dest { - struct db_dest *pnext; - volatile u32 *vaddr; - dma_addr_t dma_addr; -}; - -struct ring_dest { - u8 count_0; /* 7:0 */ - u8 count_1; /* 12:8 */ - u8 reserved; - u8 flags; - u8 addr_0; /* 7:0 */ - u8 addr_1; /* 15:8 */ - u8 addr_2; /* 23:16 */ - u8 addr_3; /* 31:24 */ -}; - -/* Private data for each instance */ -struct au1k_private { - void __iomem *iobase; - int irq_rx, irq_tx; - - struct db_dest *pDBfree; - struct db_dest db[2 * NUM_IR_DESC]; - volatile struct ring_dest *rx_ring[NUM_IR_DESC]; - volatile struct ring_dest *tx_ring[NUM_IR_DESC]; - struct db_dest *rx_db_inuse[NUM_IR_DESC]; - struct db_dest *tx_db_inuse[NUM_IR_DESC]; - u32 rx_head; - u32 tx_head; - u32 tx_tail; - u32 tx_full; - - iobuff_t rx_buff; - - struct net_device *netdev; - struct qos_info qos; - struct irlap_cb *irlap; - - u8 open; - u32 speed; - u32 newspeed; - - struct resource *ioarea; - struct au1k_irda_platform_data *platdata; - struct clk *irda_clk; -}; - -static int qos_mtt_bits = 0x07; /* 1 ms or more */ - -static void au1k_irda_plat_set_phy_mode(struct au1k_private *p, int mode) -{ - if (p->platdata && p->platdata->set_phy_mode) - p->platdata->set_phy_mode(mode); -} - -static inline unsigned long irda_read(struct au1k_private *p, - unsigned long ofs) -{ - /* - * IrDA peripheral bug. You have to read the register - * twice to get the right value. - */ - (void)__raw_readl(p->iobase + ofs); - return __raw_readl(p->iobase + ofs); -} - -static inline void irda_write(struct au1k_private *p, unsigned long ofs, - unsigned long val) -{ - __raw_writel(val, p->iobase + ofs); - wmb(); -} - -/* - * Buffer allocation/deallocation routines. The buffer descriptor returned - * has the virtual and dma address of a buffer suitable for - * both, receive and transmit operations. - */ -static struct db_dest *GetFreeDB(struct au1k_private *aup) -{ - struct db_dest *db; - db = aup->pDBfree; - - if (db) - aup->pDBfree = db->pnext; - return db; -} - -/* - DMA memory allocation, derived from pci_alloc_consistent. - However, the Au1000 data cache is coherent (when programmed - so), therefore we return KSEG0 address, not KSEG1. -*/ -static void *dma_alloc(size_t size, dma_addr_t *dma_handle) -{ - void *ret; - int gfp = GFP_ATOMIC | GFP_DMA; - - ret = (void *)__get_free_pages(gfp, get_order(size)); - - if (ret != NULL) { - memset(ret, 0, size); - *dma_handle = virt_to_bus(ret); - ret = (void *)KSEG0ADDR(ret); - } - return ret; -} - -static void dma_free(void *vaddr, size_t size) -{ - vaddr = (void *)KSEG0ADDR(vaddr); - free_pages((unsigned long) vaddr, get_order(size)); -} - - -static void setup_hw_rings(struct au1k_private *aup, u32 rx_base, u32 tx_base) -{ - int i; - for (i = 0; i < NUM_IR_DESC; i++) { - aup->rx_ring[i] = (volatile struct ring_dest *) - (rx_base + sizeof(struct ring_dest) * i); - } - for (i = 0; i < NUM_IR_DESC; i++) { - aup->tx_ring[i] = (volatile struct ring_dest *) - (tx_base + sizeof(struct ring_dest) * i); - } -} - -static int au1k_irda_init_iobuf(iobuff_t *io, int size) -{ - io->head = kmalloc(size, GFP_KERNEL); - if (io->head != NULL) { - io->truesize = size; - io->in_frame = FALSE; - io->state = OUTSIDE_FRAME; - io->data = io->head; - } - return io->head ? 0 : -ENOMEM; -} - -/* - * Set the IrDA communications speed. - */ -static int au1k_irda_set_speed(struct net_device *dev, int speed) -{ - struct au1k_private *aup = netdev_priv(dev); - volatile struct ring_dest *ptxd; - unsigned long control; - int ret = 0, timeout = 10, i; - - if (speed == aup->speed) - return ret; - - /* disable PHY first */ - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF); - irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) & ~IR_PHYEN); - - /* disable RX/TX */ - irda_write(aup, IR_CONFIG_1, - irda_read(aup, IR_CONFIG_1) & ~(IR_RX_ENABLE | IR_TX_ENABLE)); - msleep(20); - while (irda_read(aup, IR_STATUS) & (IR_RX_STATUS | IR_TX_STATUS)) { - msleep(20); - if (!timeout--) { - netdev_err(dev, "rx/tx disable timeout\n"); - break; - } - } - - /* disable DMA */ - irda_write(aup, IR_CONFIG_1, - irda_read(aup, IR_CONFIG_1) & ~IR_DMA_ENABLE); - msleep(20); - - /* After we disable tx/rx. the index pointers go back to zero. */ - aup->tx_head = aup->tx_tail = aup->rx_head = 0; - for (i = 0; i < NUM_IR_DESC; i++) { - ptxd = aup->tx_ring[i]; - ptxd->flags = 0; - ptxd->count_0 = 0; - ptxd->count_1 = 0; - } - - for (i = 0; i < NUM_IR_DESC; i++) { - ptxd = aup->rx_ring[i]; - ptxd->count_0 = 0; - ptxd->count_1 = 0; - ptxd->flags = AU_OWN; - } - - if (speed == 4000000) - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_FIR); - else - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_SIR); - - switch (speed) { - case 9600: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(11) | IR_PW(12)); - irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); - break; - case 19200: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(5) | IR_PW(12)); - irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); - break; - case 38400: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(2) | IR_PW(12)); - irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); - break; - case 57600: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(1) | IR_PW(12)); - irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); - break; - case 115200: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_PW(12)); - irda_write(aup, IR_CONFIG_1, IR_SIR_MODE); - break; - case 4000000: - irda_write(aup, IR_WRITE_PHY_CONFIG, IR_P(15)); - irda_write(aup, IR_CONFIG_1, IR_FIR | IR_DMA_ENABLE | - IR_RX_ENABLE); - break; - default: - netdev_err(dev, "unsupported speed %x\n", speed); - ret = -EINVAL; - break; - } - - aup->speed = speed; - irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) | IR_PHYEN); - - control = irda_read(aup, IR_STATUS); - irda_write(aup, IR_RING_PROMPT, 0); - - if (control & (1 << 14)) { - netdev_err(dev, "configuration error\n"); - } else { - if (control & (1 << 11)) - netdev_debug(dev, "Valid SIR config\n"); - if (control & (1 << 12)) - netdev_debug(dev, "Valid MIR config\n"); - if (control & (1 << 13)) - netdev_debug(dev, "Valid FIR config\n"); - if (control & (1 << 10)) - netdev_debug(dev, "TX enabled\n"); - if (control & (1 << 9)) - netdev_debug(dev, "RX enabled\n"); - } - - return ret; -} - -static void update_rx_stats(struct net_device *dev, u32 status, u32 count) -{ - struct net_device_stats *ps = &dev->stats; - - ps->rx_packets++; - - if (status & IR_RX_ERROR) { - ps->rx_errors++; - if (status & (IR_PHY_ERROR | IR_FIFO_OVER)) - ps->rx_missed_errors++; - if (status & IR_MAX_LEN) - ps->rx_length_errors++; - if (status & IR_CRC_ERROR) - ps->rx_crc_errors++; - } else - ps->rx_bytes += count; -} - -static void update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len) -{ - struct net_device_stats *ps = &dev->stats; - - ps->tx_packets++; - ps->tx_bytes += pkt_len; - - if (status & IR_TX_ERROR) { - ps->tx_errors++; - ps->tx_aborted_errors++; - } -} - -static void au1k_tx_ack(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - volatile struct ring_dest *ptxd; - - ptxd = aup->tx_ring[aup->tx_tail]; - while (!(ptxd->flags & AU_OWN) && (aup->tx_tail != aup->tx_head)) { - update_tx_stats(dev, ptxd->flags, - (ptxd->count_1 << 8) | ptxd->count_0); - ptxd->count_0 = 0; - ptxd->count_1 = 0; - wmb(); - aup->tx_tail = (aup->tx_tail + 1) & (NUM_IR_DESC - 1); - ptxd = aup->tx_ring[aup->tx_tail]; - - if (aup->tx_full) { - aup->tx_full = 0; - netif_wake_queue(dev); - } - } - - if (aup->tx_tail == aup->tx_head) { - if (aup->newspeed) { - au1k_irda_set_speed(dev, aup->newspeed); - aup->newspeed = 0; - } else { - irda_write(aup, IR_CONFIG_1, - irda_read(aup, IR_CONFIG_1) & ~IR_TX_ENABLE); - irda_write(aup, IR_CONFIG_1, - irda_read(aup, IR_CONFIG_1) | IR_RX_ENABLE); - irda_write(aup, IR_RING_PROMPT, 0); - } - } -} - -static int au1k_irda_rx(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - volatile struct ring_dest *prxd; - struct sk_buff *skb; - struct db_dest *pDB; - u32 flags, count; - - prxd = aup->rx_ring[aup->rx_head]; - flags = prxd->flags; - - while (!(flags & AU_OWN)) { - pDB = aup->rx_db_inuse[aup->rx_head]; - count = (prxd->count_1 << 8) | prxd->count_0; - if (!(flags & IR_RX_ERROR)) { - /* good frame */ - update_rx_stats(dev, flags, count); - skb = alloc_skb(count + 1, GFP_ATOMIC); - if (skb == NULL) { - dev->stats.rx_dropped++; - continue; - } - skb_reserve(skb, 1); - if (aup->speed == 4000000) - skb_put(skb, count); - else - skb_put(skb, count - 2); - skb_copy_to_linear_data(skb, (void *)pDB->vaddr, - count - 2); - skb->dev = dev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - prxd->count_0 = 0; - prxd->count_1 = 0; - } - prxd->flags |= AU_OWN; - aup->rx_head = (aup->rx_head + 1) & (NUM_IR_DESC - 1); - irda_write(aup, IR_RING_PROMPT, 0); - - /* next descriptor */ - prxd = aup->rx_ring[aup->rx_head]; - flags = prxd->flags; - - } - return 0; -} - -static irqreturn_t au1k_irda_interrupt(int dummy, void *dev_id) -{ - struct net_device *dev = dev_id; - struct au1k_private *aup = netdev_priv(dev); - - irda_write(aup, IR_INT_CLEAR, 0); /* ack irda interrupts */ - - au1k_irda_rx(dev); - au1k_tx_ack(dev); - - return IRQ_HANDLED; -} - -static int au1k_init(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - u32 enable, ring_address, phyck; - struct clk *c; - int i; - - c = clk_get(NULL, "irda_clk"); - if (IS_ERR(c)) - return PTR_ERR(c); - i = clk_prepare_enable(c); - if (i) { - clk_put(c); - return i; - } - - switch (clk_get_rate(c)) { - case 40000000: - phyck = IR_PHYCLK_40MHZ; - break; - case 48000000: - phyck = IR_PHYCLK_48MHZ; - break; - case 56000000: - phyck = IR_PHYCLK_56MHZ; - break; - case 64000000: - phyck = IR_PHYCLK_64MHZ; - break; - default: - clk_disable_unprepare(c); - clk_put(c); - return -EINVAL; - } - aup->irda_clk = c; - - enable = IR_HC | IR_CE | IR_C; -#ifndef CONFIG_CPU_LITTLE_ENDIAN - enable |= IR_BE; -#endif - aup->tx_head = 0; - aup->tx_tail = 0; - aup->rx_head = 0; - - for (i = 0; i < NUM_IR_DESC; i++) - aup->rx_ring[i]->flags = AU_OWN; - - irda_write(aup, IR_ENABLE, enable); - msleep(20); - - /* disable PHY */ - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF); - irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) & ~IR_PHYEN); - msleep(20); - - irda_write(aup, IR_MAX_PKT_LEN, MAX_BUF_SIZE); - - ring_address = (u32)virt_to_phys((void *)aup->rx_ring[0]); - irda_write(aup, IR_RING_BASE_ADDR_H, ring_address >> 26); - irda_write(aup, IR_RING_BASE_ADDR_L, (ring_address >> 10) & 0xffff); - - irda_write(aup, IR_RING_SIZE, - (RING_SIZE_64 << 8) | (RING_SIZE_64 << 12)); - - irda_write(aup, IR_CONFIG_2, phyck | IR_ONE_PIN); - irda_write(aup, IR_RING_ADDR_CMPR, 0); - - au1k_irda_set_speed(dev, 9600); - return 0; -} - -static int au1k_irda_start(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - char hwname[32]; - int retval; - - retval = au1k_init(dev); - if (retval) { - netdev_err(dev, "error in au1k_init\n"); - return retval; - } - - retval = request_irq(aup->irq_tx, &au1k_irda_interrupt, 0, - dev->name, dev); - if (retval) { - netdev_err(dev, "unable to get IRQ %d\n", dev->irq); - return retval; - } - retval = request_irq(aup->irq_rx, &au1k_irda_interrupt, 0, - dev->name, dev); - if (retval) { - free_irq(aup->irq_tx, dev); - netdev_err(dev, "unable to get IRQ %d\n", dev->irq); - return retval; - } - - /* Give self a hardware name */ - sprintf(hwname, "Au1000 SIR/FIR"); - aup->irlap = irlap_open(dev, &aup->qos, hwname); - netif_start_queue(dev); - - /* int enable */ - irda_write(aup, IR_CONFIG_2, irda_read(aup, IR_CONFIG_2) | IR_IEN); - - /* power up */ - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_SIR); - - return 0; -} - -static int au1k_irda_stop(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - - au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF); - - /* disable interrupts */ - irda_write(aup, IR_CONFIG_2, irda_read(aup, IR_CONFIG_2) & ~IR_IEN); - irda_write(aup, IR_CONFIG_1, 0); - irda_write(aup, IR_ENABLE, 0); /* disable clock */ - - if (aup->irlap) { - irlap_close(aup->irlap); - aup->irlap = NULL; - } - - netif_stop_queue(dev); - - /* disable the interrupt */ - free_irq(aup->irq_tx, dev); - free_irq(aup->irq_rx, dev); - - clk_disable_unprepare(aup->irda_clk); - clk_put(aup->irda_clk); - - return 0; -} - -/* - * Au1000 transmit routine. - */ -static int au1k_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - int speed = irda_get_next_speed(skb); - volatile struct ring_dest *ptxd; - struct db_dest *pDB; - u32 len, flags; - - if (speed != aup->speed && speed != -1) - aup->newspeed = speed; - - if ((skb->len == 0) && (aup->newspeed)) { - if (aup->tx_tail == aup->tx_head) { - au1k_irda_set_speed(dev, speed); - aup->newspeed = 0; - } - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - ptxd = aup->tx_ring[aup->tx_head]; - flags = ptxd->flags; - - if (flags & AU_OWN) { - netdev_debug(dev, "tx_full\n"); - netif_stop_queue(dev); - aup->tx_full = 1; - return 1; - } else if (((aup->tx_head + 1) & (NUM_IR_DESC - 1)) == aup->tx_tail) { - netdev_debug(dev, "tx_full\n"); - netif_stop_queue(dev); - aup->tx_full = 1; - return 1; - } - - pDB = aup->tx_db_inuse[aup->tx_head]; - -#if 0 - if (irda_read(aup, IR_RX_BYTE_CNT) != 0) { - netdev_debug(dev, "tx warning: rx byte cnt %x\n", - irda_read(aup, IR_RX_BYTE_CNT)); - } -#endif - - if (aup->speed == 4000000) { - /* FIR */ - skb_copy_from_linear_data(skb, (void *)pDB->vaddr, skb->len); - ptxd->count_0 = skb->len & 0xff; - ptxd->count_1 = (skb->len >> 8) & 0xff; - } else { - /* SIR */ - len = async_wrap_skb(skb, (u8 *)pDB->vaddr, MAX_BUF_SIZE); - ptxd->count_0 = len & 0xff; - ptxd->count_1 = (len >> 8) & 0xff; - ptxd->flags |= IR_DIS_CRC; - } - ptxd->flags |= AU_OWN; - wmb(); - - irda_write(aup, IR_CONFIG_1, - irda_read(aup, IR_CONFIG_1) | IR_TX_ENABLE); - irda_write(aup, IR_RING_PROMPT, 0); - - dev_kfree_skb(skb); - aup->tx_head = (aup->tx_head + 1) & (NUM_IR_DESC - 1); - return NETDEV_TX_OK; -} - -/* - * The Tx ring has been full longer than the watchdog timeout - * value. The transmitter must be hung? - */ -static void au1k_tx_timeout(struct net_device *dev) -{ - u32 speed; - struct au1k_private *aup = netdev_priv(dev); - - netdev_err(dev, "tx timeout\n"); - speed = aup->speed; - aup->speed = 0; - au1k_irda_set_speed(dev, speed); - aup->tx_full = 0; - netif_wake_queue(dev); -} - -static int au1k_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) -{ - struct if_irda_req *rq = (struct if_irda_req *)ifreq; - struct au1k_private *aup = netdev_priv(dev); - int ret = -EOPNOTSUPP; - - switch (cmd) { - case SIOCSBANDWIDTH: - if (capable(CAP_NET_ADMIN)) { - /* - * We are unable to set the speed if the - * device is not running. - */ - if (aup->open) - ret = au1k_irda_set_speed(dev, - rq->ifr_baudrate); - else { - netdev_err(dev, "ioctl: !netif_running\n"); - ret = 0; - } - } - break; - - case SIOCSMEDIABUSY: - ret = -EPERM; - if (capable(CAP_NET_ADMIN)) { - irda_device_set_media_busy(dev, TRUE); - ret = 0; - } - break; - - case SIOCGRECEIVING: - rq->ifr_receiving = 0; - break; - default: - break; - } - return ret; -} - -static const struct net_device_ops au1k_irda_netdev_ops = { - .ndo_open = au1k_irda_start, - .ndo_stop = au1k_irda_stop, - .ndo_start_xmit = au1k_irda_hard_xmit, - .ndo_tx_timeout = au1k_tx_timeout, - .ndo_do_ioctl = au1k_irda_ioctl, -}; - -static int au1k_irda_net_init(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - struct db_dest *pDB, *pDBfree; - int i, err, retval = 0; - dma_addr_t temp; - - err = au1k_irda_init_iobuf(&aup->rx_buff, 14384); - if (err) - goto out1; - - dev->netdev_ops = &au1k_irda_netdev_ops; - - irda_init_max_qos_capabilies(&aup->qos); - - /* The only value we must override it the baudrate */ - aup->qos.baud_rate.bits = IR_9600 | IR_19200 | IR_38400 | - IR_57600 | IR_115200 | IR_576000 | (IR_4000000 << 8); - - aup->qos.min_turn_time.bits = qos_mtt_bits; - irda_qos_bits_to_value(&aup->qos); - - retval = -ENOMEM; - - /* Tx ring follows rx ring + 512 bytes */ - /* we need a 1k aligned buffer */ - aup->rx_ring[0] = (struct ring_dest *) - dma_alloc(2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest)), - &temp); - if (!aup->rx_ring[0]) - goto out2; - - /* allocate the data buffers */ - aup->db[0].vaddr = - dma_alloc(MAX_BUF_SIZE * 2 * NUM_IR_DESC, &temp); - if (!aup->db[0].vaddr) - goto out3; - - setup_hw_rings(aup, (u32)aup->rx_ring[0], (u32)aup->rx_ring[0] + 512); - - pDBfree = NULL; - pDB = aup->db; - for (i = 0; i < (2 * NUM_IR_DESC); i++) { - pDB->pnext = pDBfree; - pDBfree = pDB; - pDB->vaddr = - (u32 *)((unsigned)aup->db[0].vaddr + (MAX_BUF_SIZE * i)); - pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr); - pDB++; - } - aup->pDBfree = pDBfree; - - /* attach a data buffer to each descriptor */ - for (i = 0; i < NUM_IR_DESC; i++) { - pDB = GetFreeDB(aup); - if (!pDB) - goto out3; - aup->rx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff); - aup->rx_ring[i]->addr_1 = (u8)((pDB->dma_addr >> 8) & 0xff); - aup->rx_ring[i]->addr_2 = (u8)((pDB->dma_addr >> 16) & 0xff); - aup->rx_ring[i]->addr_3 = (u8)((pDB->dma_addr >> 24) & 0xff); - aup->rx_db_inuse[i] = pDB; - } - for (i = 0; i < NUM_IR_DESC; i++) { - pDB = GetFreeDB(aup); - if (!pDB) - goto out3; - aup->tx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff); - aup->tx_ring[i]->addr_1 = (u8)((pDB->dma_addr >> 8) & 0xff); - aup->tx_ring[i]->addr_2 = (u8)((pDB->dma_addr >> 16) & 0xff); - aup->tx_ring[i]->addr_3 = (u8)((pDB->dma_addr >> 24) & 0xff); - aup->tx_ring[i]->count_0 = 0; - aup->tx_ring[i]->count_1 = 0; - aup->tx_ring[i]->flags = 0; - aup->tx_db_inuse[i] = pDB; - } - - return 0; - -out3: - dma_free((void *)aup->rx_ring[0], - 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest))); -out2: - kfree(aup->rx_buff.head); -out1: - netdev_err(dev, "au1k_irda_net_init() failed. Returns %d\n"); - return retval; -} - -static int au1k_irda_probe(struct platform_device *pdev) -{ - struct au1k_private *aup; - struct net_device *dev; - struct resource *r; - struct clk *c; - int err; - - dev = alloc_irdadev(sizeof(struct au1k_private)); - if (!dev) - return -ENOMEM; - - aup = netdev_priv(dev); - - aup->platdata = pdev->dev.platform_data; - - err = -EINVAL; - r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!r) - goto out; - - aup->irq_tx = r->start; - - r = platform_get_resource(pdev, IORESOURCE_IRQ, 1); - if (!r) - goto out; - - aup->irq_rx = r->start; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - goto out; - - err = -EBUSY; - aup->ioarea = request_mem_region(r->start, resource_size(r), - pdev->name); - if (!aup->ioarea) - goto out; - - /* bail out early if clock doesn't exist */ - c = clk_get(NULL, "irda_clk"); - if (IS_ERR(c)) { - err = PTR_ERR(c); - goto out; - } - clk_put(c); - - aup->iobase = ioremap_nocache(r->start, resource_size(r)); - if (!aup->iobase) - goto out2; - - dev->irq = aup->irq_rx; - - err = au1k_irda_net_init(dev); - if (err) - goto out3; - err = register_netdev(dev); - if (err) - goto out4; - - platform_set_drvdata(pdev, dev); - - netdev_info(dev, "IrDA: Registered device\n"); - return 0; - -out4: - dma_free((void *)aup->db[0].vaddr, - MAX_BUF_SIZE * 2 * NUM_IR_DESC); - dma_free((void *)aup->rx_ring[0], - 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest))); - kfree(aup->rx_buff.head); -out3: - iounmap(aup->iobase); -out2: - release_resource(aup->ioarea); - kfree(aup->ioarea); -out: - free_netdev(dev); - return err; -} - -static int au1k_irda_remove(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct au1k_private *aup = netdev_priv(dev); - - unregister_netdev(dev); - - dma_free((void *)aup->db[0].vaddr, - MAX_BUF_SIZE * 2 * NUM_IR_DESC); - dma_free((void *)aup->rx_ring[0], - 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest))); - kfree(aup->rx_buff.head); - - iounmap(aup->iobase); - release_resource(aup->ioarea); - kfree(aup->ioarea); - - free_netdev(dev); - - return 0; -} - -static struct platform_driver au1k_irda_driver = { - .driver = { - .name = "au1000-irda", - }, - .probe = au1k_irda_probe, - .remove = au1k_irda_remove, -}; - -module_platform_driver(au1k_irda_driver); - -MODULE_AUTHOR("Pete Popov "); -MODULE_DESCRIPTION("Au1000 IrDA Device Driver"); diff --git a/drivers/staging/irda/drivers/bfin_sir.c b/drivers/staging/irda/drivers/bfin_sir.c deleted file mode 100644 index 59e409b68349..000000000000 --- a/drivers/staging/irda/drivers/bfin_sir.c +++ /dev/null @@ -1,819 +0,0 @@ -/* - * Blackfin Infra-red Driver - * - * Copyright 2006-2009 Analog Devices Inc. - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - * - */ -#include "bfin_sir.h" - -#ifdef CONFIG_SIR_BFIN_DMA -#define DMA_SIR_RX_XCNT 10 -#define DMA_SIR_RX_YCNT (PAGE_SIZE / DMA_SIR_RX_XCNT) -#define DMA_SIR_RX_FLUSH_JIFS (HZ * 4 / 250) -#endif - -#if ANOMALY_05000447 -static int max_rate = 57600; -#else -static int max_rate = 115200; -#endif - -static void bfin_sir_rx_dma_timeout(struct timer_list *t); - -static void turnaround_delay(int mtt) -{ - long ticks; - - mtt = mtt < 10000 ? 10000 : mtt; - ticks = 1 + mtt / (USEC_PER_SEC / HZ); - schedule_timeout_uninterruptible(ticks); -} - -static void bfin_sir_init_ports(struct bfin_sir_port *sp, struct platform_device *pdev) -{ - int i; - struct resource *res; - - for (i = 0; i < pdev->num_resources; i++) { - res = &pdev->resource[i]; - switch (res->flags) { - case IORESOURCE_MEM: - sp->membase = (void __iomem *)res->start; - break; - case IORESOURCE_IRQ: - sp->irq = res->start; - break; - case IORESOURCE_DMA: - sp->rx_dma_channel = res->start; - sp->tx_dma_channel = res->end; - break; - default: - break; - } - } - - sp->clk = get_sclk(); -#ifdef CONFIG_SIR_BFIN_DMA - sp->tx_done = 1; - timer_setup(&sp->rx_dma_timer, bfin_sir_rx_dma_timeout, 0); -#endif -} - -static void bfin_sir_stop_tx(struct bfin_sir_port *port) -{ -#ifdef CONFIG_SIR_BFIN_DMA - disable_dma(port->tx_dma_channel); -#endif - - while (!(UART_GET_LSR(port) & THRE)) { - cpu_relax(); - continue; - } - - UART_CLEAR_IER(port, ETBEI); -} - -static void bfin_sir_enable_tx(struct bfin_sir_port *port) -{ - UART_SET_IER(port, ETBEI); -} - -static void bfin_sir_stop_rx(struct bfin_sir_port *port) -{ - UART_CLEAR_IER(port, ERBFI); -} - -static void bfin_sir_enable_rx(struct bfin_sir_port *port) -{ - UART_SET_IER(port, ERBFI); -} - -static int bfin_sir_set_speed(struct bfin_sir_port *port, int speed) -{ - int ret = -EINVAL; - unsigned int quot; - unsigned short val, lsr, lcr; - static int utime; - int count = 10; - - lcr = WLS(8); - - switch (speed) { - case 9600: - case 19200: - case 38400: - case 57600: - case 115200: - - /* - * IRDA is not affected by anomaly 05000230, so there is no - * need to tweak the divisor like he UART driver (which will - * slightly speed up the baud rate on us). - */ - quot = (port->clk + (8 * speed)) / (16 * speed); - - do { - udelay(utime); - lsr = UART_GET_LSR(port); - } while (!(lsr & TEMT) && count--); - - /* The useconds for 1 bits to transmit */ - utime = 1000000 / speed + 1; - - /* Clear UCEN bit to reset the UART state machine - * and control registers - */ - val = UART_GET_GCTL(port); - val &= ~UCEN; - UART_PUT_GCTL(port, val); - - /* Set DLAB in LCR to Access THR RBR IER */ - UART_SET_DLAB(port); - SSYNC(); - - UART_PUT_DLL(port, quot & 0xFF); - UART_PUT_DLH(port, (quot >> 8) & 0xFF); - SSYNC(); - - /* Clear DLAB in LCR */ - UART_CLEAR_DLAB(port); - SSYNC(); - - UART_PUT_LCR(port, lcr); - - val = UART_GET_GCTL(port); - val |= UCEN; - UART_PUT_GCTL(port, val); - - ret = 0; - break; - default: - printk(KERN_WARNING "bfin_sir: Invalid speed %d\n", speed); - break; - } - - val = UART_GET_GCTL(port); - /* If not add the 'RPOLC', we can't catch the receive interrupt. - * It's related with the HW layout and the IR transiver. - */ - val |= UMOD_IRDA | RPOLC; - UART_PUT_GCTL(port, val); - return ret; -} - -static int bfin_sir_is_receiving(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - if (!(UART_GET_IER(port) & ERBFI)) - return 0; - return self->rx_buff.state != OUTSIDE_FRAME; -} - -#ifdef CONFIG_SIR_BFIN_PIO -static void bfin_sir_tx_chars(struct net_device *dev) -{ - unsigned int chr; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - if (self->tx_buff.len != 0) { - chr = *(self->tx_buff.data); - UART_PUT_CHAR(port, chr); - self->tx_buff.data++; - self->tx_buff.len--; - } else { - self->stats.tx_packets++; - self->stats.tx_bytes += self->tx_buff.data - self->tx_buff.head; - if (self->newspeed) { - bfin_sir_set_speed(port, self->newspeed); - self->speed = self->newspeed; - self->newspeed = 0; - } - bfin_sir_stop_tx(port); - bfin_sir_enable_rx(port); - /* I'm hungry! */ - netif_wake_queue(dev); - } -} - -static void bfin_sir_rx_chars(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - unsigned char ch; - - UART_CLEAR_LSR(port); - ch = UART_GET_CHAR(port); - async_unwrap_char(dev, &self->stats, &self->rx_buff, ch); -} - -static irqreturn_t bfin_sir_rx_int(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - spin_lock(&self->lock); - while ((UART_GET_LSR(port) & DR)) - bfin_sir_rx_chars(dev); - spin_unlock(&self->lock); - - return IRQ_HANDLED; -} - -static irqreturn_t bfin_sir_tx_int(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - spin_lock(&self->lock); - if (UART_GET_LSR(port) & THRE) - bfin_sir_tx_chars(dev); - spin_unlock(&self->lock); - - return IRQ_HANDLED; -} -#endif /* CONFIG_SIR_BFIN_PIO */ - -#ifdef CONFIG_SIR_BFIN_DMA -static void bfin_sir_dma_tx_chars(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - if (!port->tx_done) - return; - port->tx_done = 0; - - if (self->tx_buff.len == 0) { - self->stats.tx_packets++; - if (self->newspeed) { - bfin_sir_set_speed(port, self->newspeed); - self->speed = self->newspeed; - self->newspeed = 0; - } - bfin_sir_enable_rx(port); - port->tx_done = 1; - netif_wake_queue(dev); - return; - } - - blackfin_dcache_flush_range((unsigned long)(self->tx_buff.data), - (unsigned long)(self->tx_buff.data+self->tx_buff.len)); - set_dma_config(port->tx_dma_channel, - set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP, - INTR_ON_BUF, DIMENSION_LINEAR, DATA_SIZE_8, - DMA_SYNC_RESTART)); - set_dma_start_addr(port->tx_dma_channel, - (unsigned long)(self->tx_buff.data)); - set_dma_x_count(port->tx_dma_channel, self->tx_buff.len); - set_dma_x_modify(port->tx_dma_channel, 1); - enable_dma(port->tx_dma_channel); -} - -static irqreturn_t bfin_sir_dma_tx_int(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - - spin_lock(&self->lock); - if (!(get_dma_curr_irqstat(port->tx_dma_channel) & DMA_RUN)) { - clear_dma_irqstat(port->tx_dma_channel); - bfin_sir_stop_tx(port); - - self->stats.tx_packets++; - self->stats.tx_bytes += self->tx_buff.len; - self->tx_buff.len = 0; - if (self->newspeed) { - bfin_sir_set_speed(port, self->newspeed); - self->speed = self->newspeed; - self->newspeed = 0; - } - bfin_sir_enable_rx(port); - /* I'm hungry! */ - netif_wake_queue(dev); - port->tx_done = 1; - } - spin_unlock(&self->lock); - - return IRQ_HANDLED; -} - -static void bfin_sir_dma_rx_chars(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - int i; - - UART_CLEAR_LSR(port); - - for (i = port->rx_dma_buf.head; i < port->rx_dma_buf.tail; i++) - async_unwrap_char(dev, &self->stats, &self->rx_buff, port->rx_dma_buf.buf[i]); -} - -static void bfin_sir_rx_dma_timeout(struct timer_list *t) -{ - struct bfin_sir_port *port = from_timer(port, t, rx_dma_timer); - struct net_device *dev = port->dev; - struct bfin_sir_self *self = netdev_priv(dev); - - int x_pos, pos; - unsigned long flags; - - spin_lock_irqsave(&self->lock, flags); - x_pos = DMA_SIR_RX_XCNT - get_dma_curr_xcount(port->rx_dma_channel); - if (x_pos == DMA_SIR_RX_XCNT) - x_pos = 0; - - pos = port->rx_dma_nrows * DMA_SIR_RX_XCNT + x_pos; - - if (pos > port->rx_dma_buf.tail) { - port->rx_dma_buf.tail = pos; - bfin_sir_dma_rx_chars(dev); - port->rx_dma_buf.head = port->rx_dma_buf.tail; - } - spin_unlock_irqrestore(&self->lock, flags); -} - -static irqreturn_t bfin_sir_dma_rx_int(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - unsigned short irqstat; - - spin_lock(&self->lock); - - port->rx_dma_nrows++; - port->rx_dma_buf.tail = DMA_SIR_RX_XCNT * port->rx_dma_nrows; - bfin_sir_dma_rx_chars(dev); - if (port->rx_dma_nrows >= DMA_SIR_RX_YCNT) { - port->rx_dma_nrows = 0; - port->rx_dma_buf.tail = 0; - } - port->rx_dma_buf.head = port->rx_dma_buf.tail; - - irqstat = get_dma_curr_irqstat(port->rx_dma_channel); - clear_dma_irqstat(port->rx_dma_channel); - spin_unlock(&self->lock); - - mod_timer(&port->rx_dma_timer, jiffies + DMA_SIR_RX_FLUSH_JIFS); - return IRQ_HANDLED; -} -#endif /* CONFIG_SIR_BFIN_DMA */ - -static int bfin_sir_startup(struct bfin_sir_port *port, struct net_device *dev) -{ -#ifdef CONFIG_SIR_BFIN_DMA - dma_addr_t dma_handle; -#endif /* CONFIG_SIR_BFIN_DMA */ - - if (request_dma(port->rx_dma_channel, "BFIN_UART_RX") < 0) { - dev_warn(&dev->dev, "Unable to attach SIR RX DMA channel\n"); - return -EBUSY; - } - - if (request_dma(port->tx_dma_channel, "BFIN_UART_TX") < 0) { - dev_warn(&dev->dev, "Unable to attach SIR TX DMA channel\n"); - free_dma(port->rx_dma_channel); - return -EBUSY; - } - -#ifdef CONFIG_SIR_BFIN_DMA - - set_dma_callback(port->rx_dma_channel, bfin_sir_dma_rx_int, dev); - set_dma_callback(port->tx_dma_channel, bfin_sir_dma_tx_int, dev); - - port->rx_dma_buf.buf = dma_alloc_coherent(NULL, PAGE_SIZE, - &dma_handle, GFP_DMA); - port->rx_dma_buf.head = 0; - port->rx_dma_buf.tail = 0; - port->rx_dma_nrows = 0; - - set_dma_config(port->rx_dma_channel, - set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO, - INTR_ON_ROW, DIMENSION_2D, - DATA_SIZE_8, DMA_SYNC_RESTART)); - set_dma_x_count(port->rx_dma_channel, DMA_SIR_RX_XCNT); - set_dma_x_modify(port->rx_dma_channel, 1); - set_dma_y_count(port->rx_dma_channel, DMA_SIR_RX_YCNT); - set_dma_y_modify(port->rx_dma_channel, 1); - set_dma_start_addr(port->rx_dma_channel, (unsigned long)port->rx_dma_buf.buf); - enable_dma(port->rx_dma_channel); - - -#else - - if (request_irq(port->irq, bfin_sir_rx_int, 0, "BFIN_SIR_RX", dev)) { - dev_warn(&dev->dev, "Unable to attach SIR RX interrupt\n"); - return -EBUSY; - } - - if (request_irq(port->irq+1, bfin_sir_tx_int, 0, "BFIN_SIR_TX", dev)) { - dev_warn(&dev->dev, "Unable to attach SIR TX interrupt\n"); - free_irq(port->irq, dev); - return -EBUSY; - } -#endif - - return 0; -} - -static void bfin_sir_shutdown(struct bfin_sir_port *port, struct net_device *dev) -{ - unsigned short val; - - bfin_sir_stop_rx(port); - - val = UART_GET_GCTL(port); - val &= ~(UCEN | UMOD_MASK | RPOLC); - UART_PUT_GCTL(port, val); - -#ifdef CONFIG_SIR_BFIN_DMA - disable_dma(port->tx_dma_channel); - disable_dma(port->rx_dma_channel); - del_timer(&(port->rx_dma_timer)); - dma_free_coherent(NULL, PAGE_SIZE, port->rx_dma_buf.buf, 0); -#else - free_irq(port->irq+1, dev); - free_irq(port->irq, dev); -#endif - free_dma(port->tx_dma_channel); - free_dma(port->rx_dma_channel); -} - -#ifdef CONFIG_PM -static int bfin_sir_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct bfin_sir_port *sir_port; - struct net_device *dev; - struct bfin_sir_self *self; - - sir_port = platform_get_drvdata(pdev); - if (!sir_port) - return 0; - - dev = sir_port->dev; - self = netdev_priv(dev); - if (self->open) { - flush_work(&self->work); - bfin_sir_shutdown(self->sir_port, dev); - netif_device_detach(dev); - } - - return 0; -} -static int bfin_sir_resume(struct platform_device *pdev) -{ - struct bfin_sir_port *sir_port; - struct net_device *dev; - struct bfin_sir_self *self; - struct bfin_sir_port *port; - - sir_port = platform_get_drvdata(pdev); - if (!sir_port) - return 0; - - dev = sir_port->dev; - self = netdev_priv(dev); - port = self->sir_port; - if (self->open) { - if (self->newspeed) { - self->speed = self->newspeed; - self->newspeed = 0; - } - bfin_sir_startup(port, dev); - bfin_sir_set_speed(port, 9600); - bfin_sir_enable_rx(port); - netif_device_attach(dev); - } - return 0; -} -#else -#define bfin_sir_suspend NULL -#define bfin_sir_resume NULL -#endif - -static void bfin_sir_send_work(struct work_struct *work) -{ - struct bfin_sir_self *self = container_of(work, struct bfin_sir_self, work); - struct net_device *dev = self->sir_port->dev; - struct bfin_sir_port *port = self->sir_port; - unsigned short val; - int tx_cnt = 10; - - while (bfin_sir_is_receiving(dev) && --tx_cnt) - turnaround_delay(self->mtt); - - bfin_sir_stop_rx(port); - - /* To avoid losting RX interrupt, we reset IR function before - * sending data. We also can set the speed, which will - * reset all the UART. - */ - val = UART_GET_GCTL(port); - val &= ~(UMOD_MASK | RPOLC); - UART_PUT_GCTL(port, val); - SSYNC(); - val |= UMOD_IRDA | RPOLC; - UART_PUT_GCTL(port, val); - SSYNC(); - /* bfin_sir_set_speed(port, self->speed); */ - -#ifdef CONFIG_SIR_BFIN_DMA - bfin_sir_dma_tx_chars(dev); -#endif - bfin_sir_enable_tx(port); - netif_trans_update(dev); -} - -static int bfin_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - int speed = irda_get_next_speed(skb); - - netif_stop_queue(dev); - - self->mtt = irda_get_mtt(skb); - - if (speed != self->speed && speed != -1) - self->newspeed = speed; - - self->tx_buff.data = self->tx_buff.head; - if (skb->len == 0) - self->tx_buff.len = 0; - else - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); - - schedule_work(&self->work); - dev_kfree_skb(skb); - - return 0; -} - -static int bfin_sir_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) -{ - struct if_irda_req *rq = (struct if_irda_req *)ifreq; - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: - if (capable(CAP_NET_ADMIN)) { - if (self->open) { - ret = bfin_sir_set_speed(port, rq->ifr_baudrate); - bfin_sir_enable_rx(port); - } else { - dev_warn(&dev->dev, "SIOCSBANDWIDTH: !netif_running\n"); - ret = 0; - } - } - break; - - case SIOCSMEDIABUSY: - ret = -EPERM; - if (capable(CAP_NET_ADMIN)) { - irda_device_set_media_busy(dev, TRUE); - ret = 0; - } - break; - - case SIOCGRECEIVING: - rq->ifr_receiving = bfin_sir_is_receiving(dev); - break; - - default: - ret = -EOPNOTSUPP; - break; - } - - return ret; -} - -static struct net_device_stats *bfin_sir_stats(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - - return &self->stats; -} - -static int bfin_sir_open(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - struct bfin_sir_port *port = self->sir_port; - int err; - - self->newspeed = 0; - self->speed = 9600; - - spin_lock_init(&self->lock); - - err = bfin_sir_startup(port, dev); - if (err) - goto err_startup; - - bfin_sir_set_speed(port, 9600); - - self->irlap = irlap_open(dev, &self->qos, DRIVER_NAME); - if (!self->irlap) { - err = -ENOMEM; - goto err_irlap; - } - - INIT_WORK(&self->work, bfin_sir_send_work); - - /* - * Now enable the interrupt then start the queue - */ - self->open = 1; - bfin_sir_enable_rx(port); - - netif_start_queue(dev); - - return 0; - -err_irlap: - self->open = 0; - bfin_sir_shutdown(port, dev); -err_startup: - return err; -} - -static int bfin_sir_stop(struct net_device *dev) -{ - struct bfin_sir_self *self = netdev_priv(dev); - - flush_work(&self->work); - bfin_sir_shutdown(self->sir_port, dev); - - if (self->rxskb) { - dev_kfree_skb(self->rxskb); - self->rxskb = NULL; - } - - /* Stop IrLAP */ - if (self->irlap) { - irlap_close(self->irlap); - self->irlap = NULL; - } - - netif_stop_queue(dev); - self->open = 0; - - return 0; -} - -static int bfin_sir_init_iobuf(iobuff_t *io, int size) -{ - io->head = kmalloc(size, GFP_KERNEL); - if (!io->head) - return -ENOMEM; - io->truesize = size; - io->in_frame = FALSE; - io->state = OUTSIDE_FRAME; - io->data = io->head; - return 0; -} - -static const struct net_device_ops bfin_sir_ndo = { - .ndo_open = bfin_sir_open, - .ndo_stop = bfin_sir_stop, - .ndo_start_xmit = bfin_sir_hard_xmit, - .ndo_do_ioctl = bfin_sir_ioctl, - .ndo_get_stats = bfin_sir_stats, -}; - -static int bfin_sir_probe(struct platform_device *pdev) -{ - struct net_device *dev; - struct bfin_sir_self *self; - unsigned int baudrate_mask; - struct bfin_sir_port *sir_port; - int err; - - if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(per) && \ - per[pdev->id][3] == pdev->id) { - err = peripheral_request_list(per[pdev->id], DRIVER_NAME); - if (err) - return err; - } else { - dev_err(&pdev->dev, "Invalid pdev id, please check board file\n"); - return -ENODEV; - } - - err = -ENOMEM; - sir_port = kmalloc(sizeof(*sir_port), GFP_KERNEL); - if (!sir_port) - goto err_mem_0; - - bfin_sir_init_ports(sir_port, pdev); - - dev = alloc_irdadev(sizeof(*self)); - if (!dev) - goto err_mem_1; - - self = netdev_priv(dev); - self->dev = &pdev->dev; - self->sir_port = sir_port; - sir_port->dev = dev; - - err = bfin_sir_init_iobuf(&self->rx_buff, IRDA_SKB_MAX_MTU); - if (err) - goto err_mem_2; - err = bfin_sir_init_iobuf(&self->tx_buff, IRDA_SIR_MAX_FRAME); - if (err) - goto err_mem_3; - - dev->netdev_ops = &bfin_sir_ndo; - dev->irq = sir_port->irq; - - irda_init_max_qos_capabilies(&self->qos); - - baudrate_mask = IR_9600; - - switch (max_rate) { - case 115200: - baudrate_mask |= IR_115200; - case 57600: - baudrate_mask |= IR_57600; - case 38400: - baudrate_mask |= IR_38400; - case 19200: - baudrate_mask |= IR_19200; - case 9600: - break; - default: - dev_warn(&pdev->dev, "Invalid maximum baud rate, using 9600\n"); - } - - self->qos.baud_rate.bits &= baudrate_mask; - - self->qos.min_turn_time.bits = 1; /* 10 ms or more */ - - irda_qos_bits_to_value(&self->qos); - - err = register_netdev(dev); - - if (err) { - kfree(self->tx_buff.head); -err_mem_3: - kfree(self->rx_buff.head); -err_mem_2: - free_netdev(dev); -err_mem_1: - kfree(sir_port); -err_mem_0: - peripheral_free_list(per[pdev->id]); - } else - platform_set_drvdata(pdev, sir_port); - - return err; -} - -static int bfin_sir_remove(struct platform_device *pdev) -{ - struct bfin_sir_port *sir_port; - struct net_device *dev = NULL; - struct bfin_sir_self *self; - - sir_port = platform_get_drvdata(pdev); - if (!sir_port) - return 0; - dev = sir_port->dev; - self = netdev_priv(dev); - unregister_netdev(dev); - kfree(self->tx_buff.head); - kfree(self->rx_buff.head); - free_netdev(dev); - kfree(sir_port); - - return 0; -} - -static struct platform_driver bfin_ir_driver = { - .probe = bfin_sir_probe, - .remove = bfin_sir_remove, - .suspend = bfin_sir_suspend, - .resume = bfin_sir_resume, - .driver = { - .name = DRIVER_NAME, - }, -}; - -module_platform_driver(bfin_ir_driver); - -module_param(max_rate, int, 0); -MODULE_PARM_DESC(max_rate, "Maximum baud rate (115200, 57600, 38400, 19200, 9600)"); - -MODULE_AUTHOR("Graf Yang "); -MODULE_DESCRIPTION("Blackfin IrDA driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/bfin_sir.h b/drivers/staging/irda/drivers/bfin_sir.h deleted file mode 100644 index d47cf14bb4a5..000000000000 --- a/drivers/staging/irda/drivers/bfin_sir.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Blackfin Infra-red Driver - * - * Copyright 2006-2009 Analog Devices Inc. - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#undef DRIVER_NAME - -#ifdef CONFIG_SIR_BFIN_DMA -struct dma_rx_buf { - char *buf; - int head; - int tail; -}; -#endif - -struct bfin_sir_port { - unsigned char __iomem *membase; - unsigned int irq; - unsigned int lsr; - unsigned long clk; - struct net_device *dev; -#ifdef CONFIG_SIR_BFIN_DMA - int tx_done; - struct dma_rx_buf rx_dma_buf; - struct timer_list rx_dma_timer; - int rx_dma_nrows; -#endif - unsigned int tx_dma_channel; - unsigned int rx_dma_channel; -}; - -struct bfin_sir_port_res { - unsigned long base_addr; - int irq; - unsigned int rx_dma_channel; - unsigned int tx_dma_channel; -}; - -struct bfin_sir_self { - struct bfin_sir_port *sir_port; - spinlock_t lock; - unsigned int open; - int speed; - int newspeed; - - struct sk_buff *txskb; - struct sk_buff *rxskb; - struct net_device_stats stats; - struct device *dev; - struct irlap_cb *irlap; - struct qos_info qos; - - iobuff_t tx_buff; - iobuff_t rx_buff; - - struct work_struct work; - int mtt; -}; - -#define DRIVER_NAME "bfin_sir" - -#include - -static const unsigned short per[][4] = { - /* rx pin tx pin NULL uart_number */ - {P_UART0_RX, P_UART0_TX, 0, 0}, - {P_UART1_RX, P_UART1_TX, 0, 1}, - {P_UART2_RX, P_UART2_TX, 0, 2}, - {P_UART3_RX, P_UART3_TX, 0, 3}, -}; diff --git a/drivers/staging/irda/drivers/donauboe.c b/drivers/staging/irda/drivers/donauboe.c deleted file mode 100644 index b337e6d23a88..000000000000 --- a/drivers/staging/irda/drivers/donauboe.c +++ /dev/null @@ -1,1732 +0,0 @@ -/***************************************************************** - * - * Filename: donauboe.c - * Version: 2.17 - * Description: Driver for the Toshiba OBOE (or type-O or 701) - * FIR Chipset, also supports the DONAUOBOE (type-DO - * or d01) FIR chipset which as far as I know is - * register compatible. - * Documentation: http://libxg.free.fr/irda/lib-irda.html - * Status: Experimental. - * Author: James McKenzie - * Created at: Sat May 8 12:35:27 1999 - * Modified: Paul Bristow - * Modified: Mon Nov 11 19:10:05 1999 - * Modified: James McKenzie - * Modified: Thu Mar 16 12:49:00 2000 (Substantial rewrite) - * Modified: Sat Apr 29 00:23:03 2000 (Added DONAUOBOE support) - * Modified: Wed May 24 23:45:02 2000 (Fixed chipio_t structure) - * Modified: 2.13 Christian Gennerat - * Modified: 2.13 dim jan 07 21:57:39 2001 (tested with kernel 2.4 & irnet/ppp) - * Modified: 2.14 Christian Gennerat - * Modified: 2.14 lun fev 05 17:55:59 2001 (adapted to patch-2.4.1-pre8-irda1) - * Modified: 2.15 Martin Lucina - * Modified: 2.15 Fri Jun 21 20:40:59 2002 (sync with 2.4.18, substantial fixes) - * Modified: 2.16 Martin Lucina - * Modified: 2.16 Sat Jun 22 18:54:29 2002 (fix freeregion, default to verbose) - * Modified: 2.17 Christian Gennerat - * Modified: 2.17 jeu sep 12 08:50:20 2002 (save_flags();cli(); replaced by spinlocks) - * Modified: 2.18 Christian Gennerat - * Modified: 2.18 ven jan 10 03:14:16 2003 Change probe default options - * - * Copyright (c) 1999 James McKenzie, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither James McKenzie nor Cambridge University admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - * Applicable Models : Libretto 100/110CT and many more. - * Toshiba refers to this chip as the type-O IR port, - * or the type-DO IR port. - * - ********************************************************************/ - -/* Look at toshoboe.h (currently in include/net/irda) for details of */ -/* Where to get documentation on the chip */ - -/* See below for a description of the logic in this driver */ - -/* User servicable parts */ -/* USE_PROBE Create the code which probes the chip and does a few tests */ -/* do_probe module parameter Enable this code */ -/* Probe code is very useful for understanding how the hardware works */ -/* Use it with various combinations of TT_LEN, RX_LEN */ -/* Strongly recommended, disable if the probe fails on your machine */ -/* and send me the output of dmesg */ -#define USE_PROBE 1 -#undef USE_PROBE - -/* Trace Transmit ring, interrupts, Receive ring or not ? */ -#define PROBE_VERBOSE 1 - -/* Debug option, examine sent and received raw data */ -/* Irdadump is better, but does not see all packets. enable it if you want. */ -#undef DUMP_PACKETS - -/* MIR mode has not been tested. Some behaviour is different */ -/* Seems to work against an Ericsson R520 for me. -Martin */ -#define USE_MIR - -/* Schedule back to back hardware transmits wherever possible, otherwise */ -/* we need an interrupt for every frame, unset if oboe works for a bit and */ -/* then hangs */ -#define OPTIMIZE_TX - -/* Set the number of slots in the rings */ -/* If you get rx/tx fifo overflows at high bitrates, you can try increasing */ -/* these */ - -#define RING_SIZE (OBOE_RING_SIZE_RX8 | OBOE_RING_SIZE_TX8) -#define TX_SLOTS 8 -#define RX_SLOTS 8 - - -/* Less user servicable parts below here */ - -/* Test, Transmit and receive buffer sizes, adjust at your peril */ -/* remarks: nfs usually needs 1k blocks */ -/* remarks: in SIR mode, CRC is received, -> RX_LEN=TX_LEN+2 */ -/* remarks: test accepts large blocks. Standard is 0x80 */ -/* When TT_LEN > RX_LEN (SIR mode) data is stored in successive slots. */ -/* When 3 or more slots are needed for each test packet, */ -/* data received in the first slots is overwritten, even */ -/* if OBOE_CTL_RX_HW_OWNS is not set, without any error! */ -#define TT_LEN 0x80 -#define TX_LEN 0xc00 -#define RX_LEN 0xc04 -/* Real transmitted length (SIR mode) is about 14+(2%*TX_LEN) more */ -/* long than user-defined length (see async_wrap_skb) and is less then 4K */ -/* Real received length is (max RX_LEN) differs from user-defined */ -/* length only b the CRC (2 or 4 bytes) */ -#define BUF_SAFETY 0x7a -#define RX_BUF_SZ (RX_LEN) -#define TX_BUF_SZ (TX_LEN+BUF_SAFETY) - - -/* Logic of the netdev part of this driver */ - -/* The RX ring is filled with buffers, when a packet arrives */ -/* it is DMA'd into the buffer which is marked used and RxDone called */ -/* RxDone forms an skb (and checks the CRC if in SIR mode) and ships */ -/* the packet off upstairs */ - -/* The transmitter on the oboe chip can work in one of two modes */ -/* for each ring->tx[] the transmitter can either */ -/* a) transmit the packet, leave the trasmitter enabled and proceed to */ -/* the next ring */ -/* OR */ -/* b) transmit the packet, switch off the transmitter and issue TxDone */ - -/* All packets are entered into the ring in mode b), if the ring was */ -/* empty the transmitter is started. */ - -/* If OPTIMIZE_TX is defined then in TxDone if the ring contains */ -/* more than one packet, all but the last are set to mode a) [HOWEVER */ -/* the hardware may not notice this, this is why we start in mode b) ] */ -/* then restart the transmitter */ - -/* If OPTIMIZE_TX is not defined then we just restart the transmitter */ -/* if the ring isn't empty */ - -/* Speed changes are delayed until the TxRing is empty */ -/* mtt is handled by generating packets with bad CRCs, before the data */ - -/* TODO: */ -/* check the mtt works ok */ -/* finish the watchdog */ - -/* No user servicable parts below here */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -//#include -//#include -#include -#include - -#include "donauboe.h" - -#define INB(port) inb_p(port) -#define OUTB(val,port) outb_p(val,port) -#define OUTBP(val,port) outb_p(val,port) - -#define PROMPT OUTB(OBOE_PROMPT_BIT,OBOE_PROMPT); - -#if PROBE_VERBOSE -#define PROBE_DEBUG(args...) (printk (args)) -#else -#define PROBE_DEBUG(args...) ; -#endif - -/* Set the DMA to be byte at a time */ -#define CONFIG0H_DMA_OFF OBOE_CONFIG0H_RCVANY -#define CONFIG0H_DMA_ON_NORX CONFIG0H_DMA_OFF| OBOE_CONFIG0H_ENDMAC -#define CONFIG0H_DMA_ON CONFIG0H_DMA_ON_NORX | OBOE_CONFIG0H_ENRX - -static const struct pci_device_id toshoboe_pci_tbl[] = { - { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIR701, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIRD01, PCI_ANY_ID, PCI_ANY_ID, }, - { } /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(pci, toshoboe_pci_tbl); - -#define DRIVER_NAME "toshoboe" -static char *driver_name = DRIVER_NAME; - -static int max_baud = 4000000; -#ifdef USE_PROBE -static bool do_probe = false; -#endif - - -/**********************************************************************/ -static int -toshoboe_checkfcs (unsigned char *buf, int len) -{ - int i; - union - { - __u16 value; - __u8 bytes[2]; - } - fcs; - - fcs.value = INIT_FCS; - - for (i = 0; i < len; ++i) - fcs.value = irda_fcs (fcs.value, *(buf++)); - - return fcs.value == GOOD_FCS; -} - -/***********************************************************************/ -/* Generic chip handling code */ -#ifdef DUMP_PACKETS -static unsigned char dump[50]; -static void -_dumpbufs (unsigned char *data, int len, char tete) -{ -int i,j; -char head=tete; -for (i=0;iint_tx, self->int_rx, self->int_txunder, self->int_rxover, - self->int_sip); - printk (KERN_ERR "RX %02x TX %02x RingBase %08x\n", - INB (OBOE_RXSLOT), INB (OBOE_TXSLOT), ringbase); - printk (KERN_ERR "RING_SIZE %02x IER %02x ISR %02x\n", - INB (OBOE_RING_SIZE), INB (OBOE_IER), INB (OBOE_ISR)); - printk (KERN_ERR "CONFIG1 %02x STATUS %02x\n", - INB (OBOE_CONFIG1), INB (OBOE_STATUS)); - printk (KERN_ERR "CONFIG0 %02x%02x ENABLE %02x%02x\n", - INB (OBOE_CONFIG0H), INB (OBOE_CONFIG0L), - INB (OBOE_ENABLEH), INB (OBOE_ENABLEL)); - printk (KERN_ERR "NEW_PCONFIG %02x%02x CURR_PCONFIG %02x%02x\n", - INB (OBOE_NEW_PCONFIGH), INB (OBOE_NEW_PCONFIGL), - INB (OBOE_CURR_PCONFIGH), INB (OBOE_CURR_PCONFIGL)); - printk (KERN_ERR "MAXLEN %02x%02x RXCOUNT %02x%02x\n", - INB (OBOE_MAXLENH), INB (OBOE_MAXLENL), - INB (OBOE_RXCOUNTL), INB (OBOE_RXCOUNTH)); - - if (self->ring) - { - int i; - ringbase = virt_to_bus (self->ring); - printk (KERN_ERR "Ring at %08x:\n", ringbase); - printk (KERN_ERR "RX:"); - for (i = 0; i < RX_SLOTS; ++i) - printk (" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control); - printk ("\n"); - printk (KERN_ERR "TX:"); - for (i = 0; i < RX_SLOTS; ++i) - printk (" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control); - printk ("\n"); - } -} -#endif - -/*Don't let the chip look at memory */ -static void -toshoboe_disablebm (struct toshoboe_cb *self) -{ - __u8 command; - pci_read_config_byte (self->pdev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MASTER; - pci_write_config_byte (self->pdev, PCI_COMMAND, command); - -} - -/* Shutdown the chip and point the taskfile reg somewhere else */ -static void -toshoboe_stopchip (struct toshoboe_cb *self) -{ - /*Disable interrupts */ - OUTB (0x0, OBOE_IER); - /*Disable DMA, Disable Rx, Disable Tx */ - OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); - /*Disable SIR MIR FIR, Tx and Rx */ - OUTB (0x00, OBOE_ENABLEH); - /*Point the ring somewhere safe */ - OUTB (0x3f, OBOE_RING_BASE2); - OUTB (0xff, OBOE_RING_BASE1); - OUTB (0xff, OBOE_RING_BASE0); - - OUTB (RX_LEN >> 8, OBOE_MAXLENH); - OUTB (RX_LEN & 0xff, OBOE_MAXLENL); - - /*Acknoledge any pending interrupts */ - OUTB (0xff, OBOE_ISR); - - /*Why */ - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - - /*switch it off */ - OUTB (OBOE_CONFIG1_OFF, OBOE_CONFIG1); - - toshoboe_disablebm (self); -} - -/* Transmitter initialization */ -static void -toshoboe_start_DMA (struct toshoboe_cb *self, int opts) -{ - OUTB (0x0, OBOE_ENABLEH); - OUTB (CONFIG0H_DMA_ON | opts, OBOE_CONFIG0H); - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - PROMPT; -} - -/*Set the baud rate */ -static void -toshoboe_setbaud (struct toshoboe_cb *self) -{ - __u16 pconfig = 0; - __u8 config0l = 0; - - pr_debug("%s(%d/%d)\n", __func__, self->speed, self->io.speed); - - switch (self->speed) - { - case 2400: - case 4800: - case 9600: - case 19200: - case 38400: - case 57600: - case 115200: -#ifdef USE_MIR - case 1152000: -#endif - case 4000000: - break; - default: - - printk (KERN_ERR DRIVER_NAME ": switch to unsupported baudrate %d\n", - self->speed); - return; - } - - switch (self->speed) - { - /* For SIR the preamble is done by adding XBOFs */ - /* to the packet */ - /* set to filtered SIR mode, filter looks for BOF and EOF */ - case 2400: - pconfig |= 47 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 4800: - pconfig |= 23 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 9600: - pconfig |= 11 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 19200: - pconfig |= 5 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 38400: - pconfig |= 2 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 57600: - pconfig |= 1 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - case 115200: - pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; - break; - default: - /*Set to packet based reception */ - OUTB (RX_LEN >> 8, OBOE_MAXLENH); - OUTB (RX_LEN & 0xff, OBOE_MAXLENL); - break; - } - - switch (self->speed) - { - case 2400: - case 4800: - case 9600: - case 19200: - case 38400: - case 57600: - case 115200: - config0l = OBOE_CONFIG0L_ENSIR; - if (self->async) - { - /*Set to character based reception */ - /*System will lock if MAXLEN=0 */ - /*so have to be careful */ - OUTB (0x01, OBOE_MAXLENH); - OUTB (0x01, OBOE_MAXLENL); - OUTB (0x00, OBOE_MAXLENH); - } - else - { - /*Set to packet based reception */ - config0l |= OBOE_CONFIG0L_ENSIRF; - OUTB (RX_LEN >> 8, OBOE_MAXLENH); - OUTB (RX_LEN & 0xff, OBOE_MAXLENL); - } - break; - -#ifdef USE_MIR - /* MIR mode */ - /* Set for 16 bit CRC and enable MIR */ - /* Preamble now handled by the chip */ - case 1152000: - pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; - pconfig |= 8 << OBOE_PCONFIG_WIDTHSHIFT; - pconfig |= 1 << OBOE_PCONFIG_PREAMBLESHIFT; - config0l = OBOE_CONFIG0L_CRC16 | OBOE_CONFIG0L_ENMIR; - break; -#endif - /* FIR mode */ - /* Set for 32 bit CRC and enable FIR */ - /* Preamble handled by the chip */ - case 4000000: - pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; - /* Documentation says 14, but toshiba use 15 in their drivers */ - pconfig |= 15 << OBOE_PCONFIG_PREAMBLESHIFT; - config0l = OBOE_CONFIG0L_ENFIR; - break; - } - - /* Copy into new PHY config buffer */ - OUTBP (pconfig >> 8, OBOE_NEW_PCONFIGH); - OUTB (pconfig & 0xff, OBOE_NEW_PCONFIGL); - OUTB (config0l, OBOE_CONFIG0L); - - /* Now make OBOE copy from new PHY to current PHY */ - OUTB (0x0, OBOE_ENABLEH); - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - PROMPT; - - /* speed change executed */ - self->new_speed = 0; - self->io.speed = self->speed; -} - -/*Let the chip look at memory */ -static void -toshoboe_enablebm (struct toshoboe_cb *self) -{ - pci_set_master (self->pdev); -} - -/*setup the ring */ -static void -toshoboe_initring (struct toshoboe_cb *self) -{ - int i; - - for (i = 0; i < TX_SLOTS; ++i) - { - self->ring->tx[i].len = 0; - self->ring->tx[i].control = 0x00; - self->ring->tx[i].address = virt_to_bus (self->tx_bufs[i]); - } - - for (i = 0; i < RX_SLOTS; ++i) - { - self->ring->rx[i].len = RX_LEN; - self->ring->rx[i].len = 0; - self->ring->rx[i].address = virt_to_bus (self->rx_bufs[i]); - self->ring->rx[i].control = OBOE_CTL_RX_HW_OWNS; - } -} - -static void -toshoboe_resetptrs (struct toshoboe_cb *self) -{ - /* Can reset pointers by twidling DMA */ - OUTB (0x0, OBOE_ENABLEH); - OUTBP (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - - self->rxs = inb_p (OBOE_RXSLOT) & OBOE_SLOT_MASK; - self->txs = inb_p (OBOE_TXSLOT) & OBOE_SLOT_MASK; -} - -/* Called in locked state */ -static void -toshoboe_initptrs (struct toshoboe_cb *self) -{ - - /* spin_lock_irqsave(self->spinlock, flags); */ - /* save_flags (flags); */ - - /* Can reset pointers by twidling DMA */ - toshoboe_resetptrs (self); - - OUTB (0x0, OBOE_ENABLEH); - OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H); - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - - self->txpending = 0; - - /* spin_unlock_irqrestore(self->spinlock, flags); */ - /* restore_flags (flags); */ -} - -/* Wake the chip up and get it looking at the rings */ -/* Called in locked state */ -static void -toshoboe_startchip (struct toshoboe_cb *self) -{ - __u32 physaddr; - - toshoboe_initring (self); - toshoboe_enablebm (self); - OUTBP (OBOE_CONFIG1_RESET, OBOE_CONFIG1); - OUTBP (OBOE_CONFIG1_ON, OBOE_CONFIG1); - - /* Stop the clocks */ - OUTB (0, OBOE_ENABLEH); - - /*Set size of rings */ - OUTB (RING_SIZE, OBOE_RING_SIZE); - - /*Acknoledge any pending interrupts */ - OUTB (0xff, OBOE_ISR); - - /*Enable ints */ - OUTB (OBOE_INT_TXDONE | OBOE_INT_RXDONE | - OBOE_INT_TXUNDER | OBOE_INT_RXOVER | OBOE_INT_SIP , OBOE_IER); - - /*Acknoledge any pending interrupts */ - OUTB (0xff, OBOE_ISR); - - /*Set the maximum packet length to 0xfff (4095) */ - OUTB (RX_LEN >> 8, OBOE_MAXLENH); - OUTB (RX_LEN & 0xff, OBOE_MAXLENL); - - /*Shutdown DMA */ - OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); - - /*Find out where the rings live */ - physaddr = virt_to_bus (self->ring); - - IRDA_ASSERT ((physaddr & 0x3ff) == 0, - printk (KERN_ERR DRIVER_NAME "ring not correctly aligned\n"); - return;); - - OUTB ((physaddr >> 10) & 0xff, OBOE_RING_BASE0); - OUTB ((physaddr >> 18) & 0xff, OBOE_RING_BASE1); - OUTB ((physaddr >> 26) & 0x3f, OBOE_RING_BASE2); - - /*Enable DMA controller in byte mode and RX */ - OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H); - - /* Start up the clocks */ - OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); - - /*set to sensible speed */ - self->speed = 9600; - toshoboe_setbaud (self); - toshoboe_initptrs (self); -} - -static void -toshoboe_isntstuck (struct toshoboe_cb *self) -{ -} - -static void -toshoboe_checkstuck (struct toshoboe_cb *self) -{ - unsigned long flags; - - if (0) - { - spin_lock_irqsave(&self->spinlock, flags); - - /* This will reset the chip completely */ - printk (KERN_ERR DRIVER_NAME ": Resetting chip\n"); - - toshoboe_stopchip (self); - toshoboe_startchip (self); - spin_unlock_irqrestore(&self->spinlock, flags); - } -} - -/*Generate packet of about mtt us long */ -static int -toshoboe_makemttpacket (struct toshoboe_cb *self, void *buf, int mtt) -{ - int xbofs; - - xbofs = ((int) (mtt/100)) * (int) (self->speed); - xbofs=xbofs/80000; /*Eight bits per byte, and mtt is in us*/ - xbofs++; - - pr_debug(DRIVER_NAME ": generated mtt of %d bytes for %d us at %d baud\n", - xbofs, mtt, self->speed); - - if (xbofs > TX_LEN) - { - printk (KERN_ERR DRIVER_NAME ": wanted %d bytes MTT but TX_LEN is %d\n", - xbofs, TX_LEN); - xbofs = TX_LEN; - } - - /*xbofs will do for SIR, MIR and FIR,SIR mode doesn't generate a checksum anyway */ - memset (buf, XBOF, xbofs); - - return xbofs; -} - -#ifdef USE_PROBE -/***********************************************************************/ -/* Probe code */ - -static void -toshoboe_dumptx (struct toshoboe_cb *self) -{ - int i; - PROBE_DEBUG(KERN_WARNING "TX:"); - for (i = 0; i < RX_SLOTS; ++i) - PROBE_DEBUG(" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control); - PROBE_DEBUG(" [%d]\n",self->speed); -} - -static void -toshoboe_dumprx (struct toshoboe_cb *self, int score) -{ - int i; - PROBE_DEBUG(" %d\nRX:",score); - for (i = 0; i < RX_SLOTS; ++i) - PROBE_DEBUG(" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control); - PROBE_DEBUG("\n"); -} - -static inline int -stuff_byte (__u8 byte, __u8 * buf) -{ - switch (byte) - { - case BOF: /* FALLTHROUGH */ - case EOF: /* FALLTHROUGH */ - case CE: - /* Insert transparently coded */ - buf[0] = CE; /* Send link escape */ - buf[1] = byte ^ IRDA_TRANS; /* Complement bit 5 */ - return 2; - /* break; */ - default: - /* Non-special value, no transparency required */ - buf[0] = byte; - return 1; - /* break; */ - } -} - -static irqreturn_t -toshoboe_probeinterrupt (int irq, void *dev_id) -{ - struct toshoboe_cb *self = dev_id; - __u8 irqstat; - - irqstat = INB (OBOE_ISR); - -/* was it us */ - if (!(irqstat & OBOE_INT_MASK)) - return IRQ_NONE; - -/* Ack all the interrupts */ - OUTB (irqstat, OBOE_ISR); - - if (irqstat & OBOE_INT_TXDONE) - { - int txp; - - self->int_tx++; - PROBE_DEBUG("T"); - - txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK; - if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS) - { - self->int_tx+=100; - PROBE_DEBUG("S"); - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); - } - } - - if (irqstat & OBOE_INT_RXDONE) { - self->int_rx++; - PROBE_DEBUG("R"); } - if (irqstat & OBOE_INT_TXUNDER) { - self->int_txunder++; - PROBE_DEBUG("U"); } - if (irqstat & OBOE_INT_RXOVER) { - self->int_rxover++; - PROBE_DEBUG("O"); } - if (irqstat & OBOE_INT_SIP) { - self->int_sip++; - PROBE_DEBUG("I"); } - return IRQ_HANDLED; -} - -static int -toshoboe_maketestpacket (unsigned char *buf, int badcrc, int fir) -{ - int i; - int len = 0; - union - { - __u16 value; - __u8 bytes[2]; - } - fcs; - - if (fir) - { - memset (buf, 0, TT_LEN); - return TT_LEN; - } - - fcs.value = INIT_FCS; - - memset (buf, XBOF, 10); - len += 10; - buf[len++] = BOF; - - for (i = 0; i < TT_LEN; ++i) - { - len += stuff_byte (i, buf + len); - fcs.value = irda_fcs (fcs.value, i); - } - - len += stuff_byte (fcs.bytes[0] ^ badcrc, buf + len); - len += stuff_byte (fcs.bytes[1] ^ badcrc, buf + len); - buf[len++] = EOF; - len++; - return len; -} - -static int -toshoboe_probefail (struct toshoboe_cb *self, char *msg) -{ - printk (KERN_ERR DRIVER_NAME "probe(%d) failed %s\n",self-> speed, msg); - toshoboe_dumpregs (self); - toshoboe_stopchip (self); - free_irq (self->io.irq, (void *) self); - return 0; -} - -static int -toshoboe_numvalidrcvs (struct toshoboe_cb *self) -{ - int i, ret = 0; - for (i = 0; i < RX_SLOTS; ++i) - if ((self->ring->rx[i].control & 0xe0) == 0) - ret++; - - return ret; -} - -static int -toshoboe_numrcvs (struct toshoboe_cb *self) -{ - int i, ret = 0; - for (i = 0; i < RX_SLOTS; ++i) - if (!(self->ring->rx[i].control & OBOE_CTL_RX_HW_OWNS)) - ret++; - - return ret; -} - -static int -toshoboe_probe (struct toshoboe_cb *self) -{ - int i, j, n; -#ifdef USE_MIR - static const int bauds[] = { 9600, 115200, 4000000, 1152000 }; -#else - static const int bauds[] = { 9600, 115200, 4000000 }; -#endif - unsigned long flags; - - if (request_irq (self->io.irq, toshoboe_probeinterrupt, - self->io.irqflags, "toshoboe", (void *) self)) - { - printk (KERN_ERR DRIVER_NAME ": probe failed to allocate irq %d\n", - self->io.irq); - return 0; - } - - /* test 1: SIR filter and back to back */ - - for (j = 0; j < ARRAY_SIZE(bauds); ++j) - { - int fir = (j > 1); - toshoboe_stopchip (self); - - - spin_lock_irqsave(&self->spinlock, flags); - /*Address is already setup */ - toshoboe_startchip (self); - self->int_rx = self->int_tx = 0; - self->speed = bauds[j]; - toshoboe_setbaud (self); - toshoboe_initptrs (self); - spin_unlock_irqrestore(&self->spinlock, flags); - - self->ring->tx[self->txs].control = -/* (FIR only) OBOE_CTL_TX_SIP needed for switching to next slot */ -/* MIR: all received data is stored in one slot */ - (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX - : OBOE_CTL_TX_HW_OWNS ; - self->ring->tx[self->txs].len = - toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); - self->txs++; - self->txs %= TX_SLOTS; - - self->ring->tx[self->txs].control = - (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_SIP - : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ; - self->ring->tx[self->txs].len = - toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); - self->txs++; - self->txs %= TX_SLOTS; - - self->ring->tx[self->txs].control = - (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX - : OBOE_CTL_TX_HW_OWNS ; - self->ring->tx[self->txs].len = - toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); - self->txs++; - self->txs %= TX_SLOTS; - - self->ring->tx[self->txs].control = - (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX - | OBOE_CTL_TX_SIP | OBOE_CTL_TX_BAD_CRC - : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ; - self->ring->tx[self->txs].len = - toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); - self->txs++; - self->txs %= TX_SLOTS; - - toshoboe_dumptx (self); - /* Turn on TX and RX and loopback */ - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); - - i = 0; - n = fir ? 1 : 4; - while (toshoboe_numvalidrcvs (self) != n) - { - if (i > 4800) - return toshoboe_probefail (self, "filter test"); - udelay ((9600*(TT_LEN+16))/self->speed); - i++; - } - - n = fir ? 203 : 102; - while ((toshoboe_numrcvs(self) != self->int_rx) || (self->int_tx != n)) - { - if (i > 4800) - return toshoboe_probefail (self, "interrupt test"); - udelay ((9600*(TT_LEN+16))/self->speed); - i++; - } - toshoboe_dumprx (self,i); - - } - - /* test 2: SIR in char at a time */ - - toshoboe_stopchip (self); - self->int_rx = self->int_tx = 0; - - spin_lock_irqsave(&self->spinlock, flags); - toshoboe_startchip (self); - spin_unlock_irqrestore(&self->spinlock, flags); - - self->async = 1; - self->speed = 115200; - toshoboe_setbaud (self); - self->ring->tx[self->txs].control = - OBOE_CTL_TX_RTCENTX | OBOE_CTL_TX_HW_OWNS; - self->ring->tx[self->txs].len = 4; - - ((unsigned char *) self->tx_bufs[self->txs])[0] = 'f'; - ((unsigned char *) self->tx_bufs[self->txs])[1] = 'i'; - ((unsigned char *) self->tx_bufs[self->txs])[2] = 's'; - ((unsigned char *) self->tx_bufs[self->txs])[3] = 'h'; - toshoboe_dumptx (self); - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); - - i = 0; - while (toshoboe_numvalidrcvs (self) != 4) - { - if (i > 100) - return toshoboe_probefail (self, "Async test"); - udelay (100); - i++; - } - - while ((toshoboe_numrcvs (self) != self->int_rx) || (self->int_tx != 1)) - { - if (i > 100) - return toshoboe_probefail (self, "Async interrupt test"); - udelay (100); - i++; - } - toshoboe_dumprx (self,i); - - self->async = 0; - self->speed = 9600; - toshoboe_setbaud (self); - toshoboe_stopchip (self); - - free_irq (self->io.irq, (void *) self); - - printk (KERN_WARNING DRIVER_NAME ": Self test passed ok\n"); - - return 1; -} -#endif - -/******************************************************************/ -/* Netdev style code */ - -/* Transmit something */ -static netdev_tx_t -toshoboe_hard_xmit (struct sk_buff *skb, struct net_device *dev) -{ - struct toshoboe_cb *self; - __s32 speed; - int mtt, len, ctl; - unsigned long flags; - struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; - - self = netdev_priv(dev); - - IRDA_ASSERT (self != NULL, return NETDEV_TX_OK; ); - - pr_debug("%s.tx:%x(%x)%x\n", - __func__, skb->len, self->txpending, INB(OBOE_ENABLEH)); - if (!cb->magic) { - pr_debug("%s.Not IrLAP:%x\n", __func__, cb->magic); -#ifdef DUMP_PACKETS - _dumpbufs(skb->data,skb->len,'>'); -#endif - } - - /* change speed pending, wait for its execution */ - if (self->new_speed) - return NETDEV_TX_BUSY; - - /* device stopped (apm) wait for restart */ - if (self->stopped) - return NETDEV_TX_BUSY; - - toshoboe_checkstuck (self); - - /* Check if we need to change the speed */ - /* But not now. Wait after transmission if mtt not required */ - speed=irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) - { - spin_lock_irqsave(&self->spinlock, flags); - - if (self->txpending || skb->len) - { - self->new_speed = speed; - pr_debug("%s: Queued TxDone scheduled speed change %d\n" , - __func__, speed); - /* if no data, that's all! */ - if (!skb->len) - { - spin_unlock_irqrestore(&self->spinlock, flags); - dev_kfree_skb (skb); - return NETDEV_TX_OK; - } - /* True packet, go on, but */ - /* do not accept anything before change speed execution */ - netif_stop_queue(dev); - /* ready to process TxDone interrupt */ - spin_unlock_irqrestore(&self->spinlock, flags); - } - else - { - /* idle and no data, change speed now */ - self->speed = speed; - toshoboe_setbaud (self); - spin_unlock_irqrestore(&self->spinlock, flags); - dev_kfree_skb (skb); - return NETDEV_TX_OK; - } - - } - - if ((mtt = irda_get_mtt(skb))) - { - /* This is fair since the queue should be empty anyway */ - spin_lock_irqsave(&self->spinlock, flags); - - if (self->txpending) - { - spin_unlock_irqrestore(&self->spinlock, flags); - return NETDEV_TX_BUSY; - } - - /* If in SIR mode we need to generate a string of XBOFs */ - /* In MIR and FIR we need to generate a string of data */ - /* which we will add a wrong checksum to */ - - mtt = toshoboe_makemttpacket (self, self->tx_bufs[self->txs], mtt); - pr_debug("%s.mtt:%x(%x)%d\n", __func__, skb->len, mtt, self->txpending); - if (mtt) - { - self->ring->tx[self->txs].len = mtt & 0xfff; - - ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX; - if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON) - { - ctl |= OBOE_CTL_TX_BAD_CRC | OBOE_CTL_TX_SIP ; - } -#ifdef USE_MIR - else if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_MIRON) - { - ctl |= OBOE_CTL_TX_BAD_CRC; - } -#endif - self->ring->tx[self->txs].control = ctl; - - OUTB (0x0, OBOE_ENABLEH); - /* It is only a timer. Do not send mtt packet outside! */ - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); - - self->txpending++; - - self->txs++; - self->txs %= TX_SLOTS; - - } - else - { - printk(KERN_ERR DRIVER_NAME ": problem with mtt packet - ignored\n"); - } - spin_unlock_irqrestore(&self->spinlock, flags); - } - -#ifdef DUMP_PACKETS -dumpbufs(skb->data,skb->len,'>'); -#endif - - spin_lock_irqsave(&self->spinlock, flags); - - if (self->ring->tx[self->txs].control & OBOE_CTL_TX_HW_OWNS) - { - pr_debug("%s.ful:%x(%x)%x\n", - __func__, skb->len, self->ring->tx[self->txs].control, - self->txpending); - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); - spin_unlock_irqrestore(&self->spinlock, flags); - return NETDEV_TX_BUSY; - } - - if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_SIRON) - { - len = async_wrap_skb (skb, self->tx_bufs[self->txs], TX_BUF_SZ); - } - else - { - len = skb->len; - skb_copy_from_linear_data(skb, self->tx_bufs[self->txs], len); - } - self->ring->tx[self->txs].len = len & 0x0fff; - - /*Sometimes the HW doesn't see us assert RTCENTX in the interrupt code */ - /*later this plays safe, we garuntee the last packet to be transmitted */ - /*has RTCENTX set */ - - ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX; - if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON) - { - ctl |= OBOE_CTL_TX_SIP ; - } - self->ring->tx[self->txs].control = ctl; - - /* If transmitter is idle start in one-shot mode */ - - if (!self->txpending) - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); - - self->txpending++; - - self->txs++; - self->txs %= TX_SLOTS; - - spin_unlock_irqrestore(&self->spinlock, flags); - dev_kfree_skb (skb); - - return NETDEV_TX_OK; -} - -/*interrupt handler */ -static irqreturn_t -toshoboe_interrupt (int irq, void *dev_id) -{ - struct toshoboe_cb *self = dev_id; - __u8 irqstat; - struct sk_buff *skb = NULL; - - irqstat = INB (OBOE_ISR); - -/* was it us */ - if (!(irqstat & OBOE_INT_MASK)) - return IRQ_NONE; - -/* Ack all the interrupts */ - OUTB (irqstat, OBOE_ISR); - - toshoboe_isntstuck (self); - -/* Txdone */ - if (irqstat & OBOE_INT_TXDONE) - { - int txp, txpc; - int i; - - txp = self->txpending; - self->txpending = 0; - - for (i = 0; i < TX_SLOTS; ++i) - { - if (self->ring->tx[i].control & OBOE_CTL_TX_HW_OWNS) - self->txpending++; - } - pr_debug("%s.txd(%x)%x/%x\n", __func__, irqstat, txp, self->txpending); - - txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK; - - /* Got anything queued ? start it together */ - if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS) - { - txpc = txp; -#ifdef OPTIMIZE_TX - while (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS) - { - txp = txpc; - txpc++; - txpc %= TX_SLOTS; - self->netdev->stats.tx_packets++; - if (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS) - self->ring->tx[txp].control &= ~OBOE_CTL_TX_RTCENTX; - } - self->netdev->stats.tx_packets--; -#else - self->netdev->stats.tx_packets++; -#endif - toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); - } - - if ((!self->txpending) && (self->new_speed)) - { - self->speed = self->new_speed; - pr_debug("%s: Executed TxDone scheduled speed change %d\n", - __func__, self->speed); - toshoboe_setbaud (self); - } - - /* Tell network layer that we want more frames */ - if (!self->new_speed) - netif_wake_queue(self->netdev); - } - - if (irqstat & OBOE_INT_RXDONE) - { - while (!(self->ring->rx[self->rxs].control & OBOE_CTL_RX_HW_OWNS)) - { - int len = self->ring->rx[self->rxs].len; - skb = NULL; - pr_debug("%s.rcv:%x(%x)\n", __func__ - , len, self->ring->rx[self->rxs].control); - -#ifdef DUMP_PACKETS -dumpbufs(self->rx_bufs[self->rxs],len,'<'); -#endif - - if (self->ring->rx[self->rxs].control == 0) - { - __u8 enable = INB (OBOE_ENABLEH); - - /* In SIR mode we need to check the CRC as this */ - /* hasn't been done by the hardware */ - if (enable & OBOE_ENABLEH_SIRON) - { - if (!toshoboe_checkfcs (self->rx_bufs[self->rxs], len)) - len = 0; - /*Trim off the CRC */ - if (len > 1) - len -= 2; - else - len = 0; - pr_debug("%s.SIR:%x(%x)\n", __func__, len, enable); - } - -#ifdef USE_MIR - else if (enable & OBOE_ENABLEH_MIRON) - { - if (len > 1) - len -= 2; - else - len = 0; - pr_debug("%s.MIR:%x(%x)\n", __func__, len, enable); - } -#endif - else if (enable & OBOE_ENABLEH_FIRON) - { - if (len > 3) - len -= 4; /*FIXME: check this */ - else - len = 0; - pr_debug("%s.FIR:%x(%x)\n", __func__, len, enable); - } - else - pr_debug("%s.?IR:%x(%x)\n", __func__, len, enable); - - if (len) - { - skb = dev_alloc_skb (len + 1); - if (skb) - { - skb_reserve (skb, 1); - - skb_put (skb, len); - skb_copy_to_linear_data(skb, self->rx_bufs[self->rxs], - len); - self->netdev->stats.rx_packets++; - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons (ETH_P_IRDA); - } - else - { - printk (KERN_INFO - "%s(), memory squeeze, dropping frame.\n", - __func__); - } - } - } - else - { - /* TODO: =========================================== */ - /* if OBOE_CTL_RX_LENGTH, our buffers are too small */ - /* (MIR or FIR) data is lost. */ - /* (SIR) data is splitted in several slots. */ - /* we have to join all the received buffers received */ - /*in a large buffer before checking CRC. */ - pr_debug("%s.err:%x(%x)\n", __func__ - , len, self->ring->rx[self->rxs].control); - } - - self->ring->rx[self->rxs].len = 0x0; - self->ring->rx[self->rxs].control = OBOE_CTL_RX_HW_OWNS; - - self->rxs++; - self->rxs %= RX_SLOTS; - - if (skb) - netif_rx (skb); - - } - } - - if (irqstat & OBOE_INT_TXUNDER) - { - printk (KERN_WARNING DRIVER_NAME ": tx fifo underflow\n"); - } - if (irqstat & OBOE_INT_RXOVER) - { - printk (KERN_WARNING DRIVER_NAME ": rx fifo overflow\n"); - } -/* This must be useful for something... */ - if (irqstat & OBOE_INT_SIP) - { - self->int_sip++; - pr_debug("%s.sip:%x(%x)%x\n", - __func__, self->int_sip, irqstat, self->txpending); - } - return IRQ_HANDLED; -} - - -static int -toshoboe_net_open (struct net_device *dev) -{ - struct toshoboe_cb *self; - unsigned long flags; - int rc; - - self = netdev_priv(dev); - - if (self->async) - return -EBUSY; - - if (self->stopped) - return 0; - - rc = request_irq (self->io.irq, toshoboe_interrupt, - IRQF_SHARED, dev->name, self); - if (rc) - return rc; - - spin_lock_irqsave(&self->spinlock, flags); - toshoboe_startchip (self); - spin_unlock_irqrestore(&self->spinlock, flags); - - /* Ready to play! */ - netif_start_queue(dev); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - self->irlap = irlap_open (dev, &self->qos, driver_name); - - self->irdad = 1; - - return 0; -} - -static int -toshoboe_net_close (struct net_device *dev) -{ - struct toshoboe_cb *self; - - IRDA_ASSERT (dev != NULL, return -1; ); - self = netdev_priv(dev); - - /* Stop device */ - netif_stop_queue(dev); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close (self->irlap); - self->irlap = NULL; - - self->irdad = 0; - - free_irq (self->io.irq, (void *) self); - - if (!self->stopped) - { - toshoboe_stopchip (self); - } - - return 0; -} - -/* - * Function toshoboe_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int -toshoboe_net_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct toshoboe_cb *self; - unsigned long flags; - int ret = 0; - - IRDA_ASSERT (dev != NULL, return -1; ); - - self = netdev_priv(dev); - - IRDA_ASSERT (self != NULL, return -1; ); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - - /* Disable interrupts & save flags */ - spin_lock_irqsave(&self->spinlock, flags); - - switch (cmd) - { - case SIOCSBANDWIDTH: /* Set bandwidth */ - /* This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - pr_debug("%s(BANDWIDTH), %s, (%X/%ld\n", - __func__, dev->name, INB(OBOE_STATUS), irq->ifr_baudrate); - if (!in_interrupt () && !capable (CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - - /* self->speed=irq->ifr_baudrate; */ - /* toshoboe_setbaud(self); */ - /* Just change speed once - inserted by Paul Bristow */ - self->new_speed = irq->ifr_baudrate; - break; - case SIOCSMEDIABUSY: /* Set media busy */ - pr_debug("%s(MEDIABUSY), %s, (%X/%x)\n", - __func__, dev->name, - INB(OBOE_STATUS), capable(CAP_NET_ADMIN)); - if (!capable (CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - irda_device_set_media_busy (self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = (INB (OBOE_STATUS) & OBOE_STATUS_RXBUSY) ? 1 : 0; - pr_debug("%s(RECEIVING), %s, (%X/%x)\n", - __func__, dev->name, INB(OBOE_STATUS), irq->ifr_receiving); - break; - default: - pr_debug("%s(?), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - ret = -EOPNOTSUPP; - } -out: - spin_unlock_irqrestore(&self->spinlock, flags); - return ret; - -} - -MODULE_DESCRIPTION("Toshiba OBOE IrDA Device Driver"); -MODULE_AUTHOR("James McKenzie "); -MODULE_LICENSE("GPL"); - -module_param (max_baud, int, 0); -MODULE_PARM_DESC(max_baud, "Maximum baud rate"); - -#ifdef USE_PROBE -module_param (do_probe, bool, 0); -MODULE_PARM_DESC(do_probe, "Enable/disable chip probing and self-test"); -#endif - -static void -toshoboe_close (struct pci_dev *pci_dev) -{ - int i; - struct toshoboe_cb *self = pci_get_drvdata(pci_dev); - - IRDA_ASSERT (self != NULL, return; ); - - if (!self->stopped) - { - toshoboe_stopchip (self); - } - - release_region (self->io.fir_base, self->io.fir_ext); - - for (i = 0; i < TX_SLOTS; ++i) - { - kfree (self->tx_bufs[i]); - self->tx_bufs[i] = NULL; - } - - for (i = 0; i < RX_SLOTS; ++i) - { - kfree (self->rx_bufs[i]); - self->rx_bufs[i] = NULL; - } - - unregister_netdev(self->netdev); - - kfree (self->ringbuf); - self->ringbuf = NULL; - self->ring = NULL; - - free_netdev(self->netdev); -} - -static const struct net_device_ops toshoboe_netdev_ops = { - .ndo_open = toshoboe_net_open, - .ndo_stop = toshoboe_net_close, - .ndo_start_xmit = toshoboe_hard_xmit, - .ndo_do_ioctl = toshoboe_net_ioctl, -}; - -static int -toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid) -{ - struct toshoboe_cb *self; - struct net_device *dev; - int i = 0; - int ok = 0; - int err; - - if ((err=pci_enable_device(pci_dev))) - return err; - - dev = alloc_irdadev(sizeof (struct toshoboe_cb)); - if (dev == NULL) - { - printk (KERN_ERR DRIVER_NAME ": can't allocate memory for " - "IrDA control block\n"); - return -ENOMEM; - } - - self = netdev_priv(dev); - self->netdev = dev; - self->pdev = pci_dev; - self->base = pci_resource_start(pci_dev,0); - - self->io.fir_base = self->base; - self->io.fir_ext = OBOE_IO_EXTENT; - self->io.irq = pci_dev->irq; - self->io.irqflags = IRQF_SHARED; - - self->speed = self->io.speed = 9600; - self->async = 0; - - /* Lock the port that we need */ - if (NULL==request_region (self->io.fir_base, self->io.fir_ext, driver_name)) - { - printk (KERN_ERR DRIVER_NAME ": can't get iobase of 0x%03x\n" - ,self->io.fir_base); - err = -EBUSY; - goto freeself; - } - - spin_lock_init(&self->spinlock); - - irda_init_max_qos_capabilies (&self->qos); - self->qos.baud_rate.bits = 0; - - if (max_baud >= 2400) - self->qos.baud_rate.bits |= IR_2400; - /*if (max_baud>=4800) idev->qos.baud_rate.bits|=IR_4800; */ - if (max_baud >= 9600) - self->qos.baud_rate.bits |= IR_9600; - if (max_baud >= 19200) - self->qos.baud_rate.bits |= IR_19200; - if (max_baud >= 115200) - self->qos.baud_rate.bits |= IR_115200; -#ifdef USE_MIR - if (max_baud >= 1152000) - { - self->qos.baud_rate.bits |= IR_1152000; - } -#endif - if (max_baud >= 4000000) - { - self->qos.baud_rate.bits |= (IR_4000000 << 8); - } - - /*FIXME: work this out... */ - self->qos.min_turn_time.bits = 0xff; - - irda_qos_bits_to_value (&self->qos); - - /* Allocate twice the size to guarantee alignment */ - self->ringbuf = kmalloc(OBOE_RING_LEN << 1, GFP_KERNEL); - if (!self->ringbuf) - { - err = -ENOMEM; - goto freeregion; - } - -#if (BITS_PER_LONG == 64) -#error broken on 64-bit: casts pointer to 32-bit, and then back to pointer. -#endif - - /*We need to align the taskfile on a taskfile size boundary */ - { - unsigned long addr; - - addr = (__u32) self->ringbuf; - addr &= ~(OBOE_RING_LEN - 1); - addr += OBOE_RING_LEN; - self->ring = (struct OboeRing *) addr; - } - - memset (self->ring, 0, OBOE_RING_LEN); - self->io.mem_base = (__u32) self->ring; - - ok = 1; - for (i = 0; i < TX_SLOTS; ++i) - { - self->tx_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL); - if (!self->tx_bufs[i]) - ok = 0; - } - - for (i = 0; i < RX_SLOTS; ++i) - { - self->rx_bufs[i] = kmalloc (RX_BUF_SZ, GFP_KERNEL); - if (!self->rx_bufs[i]) - ok = 0; - } - - if (!ok) - { - err = -ENOMEM; - goto freebufs; - } - - -#ifdef USE_PROBE - if (do_probe) - if (!toshoboe_probe (self)) - { - err = -ENODEV; - goto freebufs; - } -#endif - - SET_NETDEV_DEV(dev, &pci_dev->dev); - dev->netdev_ops = &toshoboe_netdev_ops; - - err = register_netdev(dev); - if (err) - { - printk (KERN_ERR DRIVER_NAME ": register_netdev() failed\n"); - err = -ENOMEM; - goto freebufs; - } - printk (KERN_INFO "IrDA: Registered device %s\n", dev->name); - - pci_set_drvdata(pci_dev,self); - - printk (KERN_INFO DRIVER_NAME ": Using multiple tasks\n"); - - return 0; - -freebufs: - for (i = 0; i < TX_SLOTS; ++i) - kfree (self->tx_bufs[i]); - for (i = 0; i < RX_SLOTS; ++i) - kfree (self->rx_bufs[i]); - kfree(self->ringbuf); - -freeregion: - release_region (self->io.fir_base, self->io.fir_ext); - -freeself: - free_netdev(dev); - - return err; -} - -static int -toshoboe_gotosleep (struct pci_dev *pci_dev, pm_message_t crap) -{ - struct toshoboe_cb *self = pci_get_drvdata(pci_dev); - unsigned long flags; - int i = 10; - - if (!self || self->stopped) - return 0; - - if ((!self->irdad) && (!self->async)) - return 0; - -/* Flush all packets */ - while ((i--) && (self->txpending)) - msleep(10); - - spin_lock_irqsave(&self->spinlock, flags); - - toshoboe_stopchip (self); - self->stopped = 1; - self->txpending = 0; - - spin_unlock_irqrestore(&self->spinlock, flags); - return 0; -} - -static int -toshoboe_wakeup (struct pci_dev *pci_dev) -{ - struct toshoboe_cb *self = pci_get_drvdata(pci_dev); - unsigned long flags; - - if (!self || !self->stopped) - return 0; - - if ((!self->irdad) && (!self->async)) - return 0; - - spin_lock_irqsave(&self->spinlock, flags); - - toshoboe_startchip (self); - self->stopped = 0; - - netif_wake_queue(self->netdev); - spin_unlock_irqrestore(&self->spinlock, flags); - return 0; -} - -static struct pci_driver donauboe_pci_driver = { - .name = "donauboe", - .id_table = toshoboe_pci_tbl, - .probe = toshoboe_open, - .remove = toshoboe_close, - .suspend = toshoboe_gotosleep, - .resume = toshoboe_wakeup -}; - -module_pci_driver(donauboe_pci_driver); diff --git a/drivers/staging/irda/drivers/donauboe.h b/drivers/staging/irda/drivers/donauboe.h deleted file mode 100644 index d92d54e839b9..000000000000 --- a/drivers/staging/irda/drivers/donauboe.h +++ /dev/null @@ -1,362 +0,0 @@ -/********************************************************************* - * - * Filename: toshoboe.h - * Version: 2.16 - * Description: Driver for the Toshiba OBOE (or type-O or 701) - * FIR Chipset, also supports the DONAUOBOE (type-DO - * or d01) FIR chipset which as far as I know is - * register compatible. - * Status: Experimental. - * Author: James McKenzie - * Created at: Sat May 8 12:35:27 1999 - * Modified: 2.16 Martin Lucina - * Modified: 2.16 Sat Jun 22 18:54:29 2002 (sync headers) - * Modified: 2.17 Christian Gennerat - * Modified: 2.17 jeu sep 12 08:50:20 2002 (add lock to be used by spinlocks) - * - * Copyright (c) 1999 James McKenzie, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither James McKenzie nor Cambridge University admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - * Applicable Models : Libretto 100/110CT and many more. - * Toshiba refers to this chip as the type-O IR port, - * or the type-DO IR port. - * - * IrDA chip set list from Toshiba Computer Engineering Corp. - * model method maker controller Version - * Portege 320CT FIR,SIR Toshiba Oboe(Triangle) - * Portege 3010CT FIR,SIR Toshiba Oboe(Sydney) - * Portege 3015CT FIR,SIR Toshiba Oboe(Sydney) - * Portege 3020CT FIR,SIR Toshiba Oboe(Sydney) - * Portege 7020CT FIR,SIR ? ? - * - * Satell. 4090XCDT FIR,SIR ? ? - * - * Libretto 100CT FIR,SIR Toshiba Oboe - * Libretto 1000CT FIR,SIR Toshiba Oboe - * - * TECRA750DVD FIR,SIR Toshiba Oboe(Triangle) REV ID=14h - * TECRA780 FIR,SIR Toshiba Oboe(Sandlot) REV ID=32h,33h - * TECRA750CDT FIR,SIR Toshiba Oboe(Triangle) REV ID=13h,14h - * TECRA8000 FIR,SIR Toshiba Oboe(ISKUR) REV ID=23h - * - ********************************************************************/ - -/* The documentation for this chip is allegedly released */ -/* However I have not seen it, not have I managed to contact */ -/* anyone who has. HOWEVER the chip bears a striking resemblance */ -/* to the IrDA controller in the Toshiba RISC TMPR3922 chip */ -/* the documentation for this is freely available at */ -/* http://www.madingley.org/james/resources/toshoboe/TMPR3922.pdf */ -/* The mapping between the registers in that document and the */ -/* Registers in the 701 oboe chip are as follows */ - - -/* 3922 reg 701 regs, by bit numbers */ -/* 7- 0 15- 8 24-16 31-25 */ -/* $28 0x0 0x1 */ -/* $2c SEE NOTE 1 */ -/* $30 0x6 0x7 */ -/* $34 0x8 0x9 SEE NOTE 2 */ -/* $38 0x10 0x11 */ -/* $3C 0xe SEE NOTE 3 */ -/* $40 0x12 0x13 */ -/* $44 0x14 0x15 */ -/* $48 0x16 0x17 */ -/* $4c 0x18 0x19 */ -/* $50 0x1a 0x1b */ - -/* FIXME: could be 0x1b 0x1a here */ - -/* $54 0x1d 0x1c */ -/* $5C 0xf SEE NOTE 4 */ -/* $130 SEE NOTE 5 */ -/* $134 SEE NOTE 6 */ -/* */ -/* NOTES: */ -/* 1. The pointer to ring is packed in most unceremoniusly */ -/* 701 Register Address bits (A9-A0 must be zero) */ -/* 0x4: A17 A16 A15 A14 A13 A12 A11 A10 */ -/* 0x5: A25 A24 A23 A22 A21 A20 A19 A18 */ -/* 0x2: 0 0 A31 A30 A29 A28 A27 A26 */ -/* */ -/* 2. The M$ drivers do a write 0x1 to 0x9, however the 3922 */ -/* documentation would suggest that a write of 0x1 to 0x8 */ -/* would be more appropriate. */ -/* */ -/* 3. This assignment is tenuous at best, register 0xe seems to */ -/* have bits arranged 0 0 0 R/W R/W R/W R/W R/W */ -/* if either of the lower two bits are set the chip seems to */ -/* switch off */ -/* */ -/* 4. Bits 7-4 seem to be different 4 seems just to be generic */ -/* receiver busy flag */ -/* */ -/* 5. and 6. The IER and ISR have a different bit assignment */ -/* The lower three bits of both read back as ones */ -/* ISR is register 0xc, IER is register 0xd */ -/* 7 6 5 4 3 2 1 0 */ -/* 0xc: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */ -/* 0xd: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */ -/* TxDone xmitt done (generated only if generate interrupt bit */ -/* is set in the ring) */ -/* RxDone recv completed (or other recv condition if you set it */ -/* up */ -/* TxUnder underflow in Transmit FIFO */ -/* RxOver overflow in Recv FIFO */ -/* SipRcv received serial gap (or other condition you set) */ -/* Interrupts are enabled by writing a one to the IER register */ -/* Interrupts are cleared by writing a one to the ISR register */ -/* */ -/* 6. The remaining registers: 0x6 and 0x3 appear to be */ -/* reserved parts of 16 or 32 bit registersthe remainder */ -/* 0xa 0xb 0x1e 0x1f could possibly be (by their behaviour) */ -/* the Unicast Filter register at $58. */ -/* */ -/* 7. While the core obviously expects 32 bit accesses all the */ -/* M$ drivers do 8 bit accesses, infact the Miniport ones */ -/* write and read back the byte serveral times (why?) */ - - -#ifndef TOSHOBOE_H -#define TOSHOBOE_H - -/* Registers */ - -#define OBOE_IO_EXTENT 0x1f - -/*Receive and transmit slot pointers */ -#define OBOE_REG(i) (i+(self->base)) -#define OBOE_RXSLOT OBOE_REG(0x0) -#define OBOE_TXSLOT OBOE_REG(0x1) -#define OBOE_SLOT_MASK 0x3f - -#define OBOE_TXRING_OFFSET 0x200 -#define OBOE_TXRING_OFFSET_IN_SLOTS 0x40 - -/*pointer to the ring */ -#define OBOE_RING_BASE0 OBOE_REG(0x4) -#define OBOE_RING_BASE1 OBOE_REG(0x5) -#define OBOE_RING_BASE2 OBOE_REG(0x2) -#define OBOE_RING_BASE3 OBOE_REG(0x3) - -/*Number of slots in the ring */ -#define OBOE_RING_SIZE OBOE_REG(0x7) -#define OBOE_RING_SIZE_RX4 0x00 -#define OBOE_RING_SIZE_RX8 0x01 -#define OBOE_RING_SIZE_RX16 0x03 -#define OBOE_RING_SIZE_RX32 0x07 -#define OBOE_RING_SIZE_RX64 0x0f -#define OBOE_RING_SIZE_TX4 0x00 -#define OBOE_RING_SIZE_TX8 0x10 -#define OBOE_RING_SIZE_TX16 0x30 -#define OBOE_RING_SIZE_TX32 0x70 -#define OBOE_RING_SIZE_TX64 0xf0 - -#define OBOE_RING_MAX_SIZE 64 - -/*Causes the gubbins to re-examine the ring */ -#define OBOE_PROMPT OBOE_REG(0x9) -#define OBOE_PROMPT_BIT 0x1 - -/* Interrupt Status Register */ -#define OBOE_ISR OBOE_REG(0xc) -/* Interrupt Enable Register */ -#define OBOE_IER OBOE_REG(0xd) -/* Interrupt bits for IER and ISR */ -#define OBOE_INT_TXDONE 0x80 -#define OBOE_INT_RXDONE 0x40 -#define OBOE_INT_TXUNDER 0x20 -#define OBOE_INT_RXOVER 0x10 -#define OBOE_INT_SIP 0x08 -#define OBOE_INT_MASK 0xf8 - -/*Reset Register */ -#define OBOE_CONFIG1 OBOE_REG(0xe) -#define OBOE_CONFIG1_RST 0x01 -#define OBOE_CONFIG1_DISABLE 0x02 -#define OBOE_CONFIG1_4 0x08 -#define OBOE_CONFIG1_8 0x08 - -#define OBOE_CONFIG1_ON 0x8 -#define OBOE_CONFIG1_RESET 0xf -#define OBOE_CONFIG1_OFF 0xe - -#define OBOE_STATUS OBOE_REG(0xf) -#define OBOE_STATUS_RXBUSY 0x10 -#define OBOE_STATUS_FIRRX 0x04 -#define OBOE_STATUS_MIRRX 0x02 -#define OBOE_STATUS_SIRRX 0x01 - - -/*Speed control registers */ -#define OBOE_CONFIG0L OBOE_REG(0x10) -#define OBOE_CONFIG0H OBOE_REG(0x11) - -#define OBOE_CONFIG0H_TXONLOOP 0x80 /*Transmit when looping (dangerous) */ -#define OBOE_CONFIG0H_LOOP 0x40 /*Loopback Tx->Rx */ -#define OBOE_CONFIG0H_ENTX 0x10 /*Enable Tx */ -#define OBOE_CONFIG0H_ENRX 0x08 /*Enable Rx */ -#define OBOE_CONFIG0H_ENDMAC 0x04 /*Enable/reset* the DMA controller */ -#define OBOE_CONFIG0H_RCVANY 0x02 /*DMA mode 1=bytes, 0=dwords */ - -#define OBOE_CONFIG0L_CRC16 0x80 /*CRC 1=16 bit 0=32 bit */ -#define OBOE_CONFIG0L_ENFIR 0x40 /*Enable FIR */ -#define OBOE_CONFIG0L_ENMIR 0x20 /*Enable MIR */ -#define OBOE_CONFIG0L_ENSIR 0x10 /*Enable SIR */ -#define OBOE_CONFIG0L_ENSIRF 0x08 /*Enable SIR framer */ -#define OBOE_CONFIG0L_SIRTEST 0x04 /*Enable SIR framer in MIR and FIR */ -#define OBOE_CONFIG0L_INVERTTX 0x02 /*Invert Tx Line */ -#define OBOE_CONFIG0L_INVERTRX 0x01 /*Invert Rx Line */ - -#define OBOE_BOF OBOE_REG(0x12) -#define OBOE_EOF OBOE_REG(0x13) - -#define OBOE_ENABLEL OBOE_REG(0x14) -#define OBOE_ENABLEH OBOE_REG(0x15) - -#define OBOE_ENABLEH_PHYANDCLOCK 0x80 /*Toggle low to copy config in */ -#define OBOE_ENABLEH_CONFIGERR 0x40 -#define OBOE_ENABLEH_FIRON 0x20 -#define OBOE_ENABLEH_MIRON 0x10 -#define OBOE_ENABLEH_SIRON 0x08 -#define OBOE_ENABLEH_ENTX 0x04 -#define OBOE_ENABLEH_ENRX 0x02 -#define OBOE_ENABLEH_CRC16 0x01 - -#define OBOE_ENABLEL_BROADCAST 0x01 - -#define OBOE_CURR_PCONFIGL OBOE_REG(0x16) /*Current config */ -#define OBOE_CURR_PCONFIGH OBOE_REG(0x17) - -#define OBOE_NEW_PCONFIGL OBOE_REG(0x18) -#define OBOE_NEW_PCONFIGH OBOE_REG(0x19) - -#define OBOE_PCONFIGH_BAUDMASK 0xfc -#define OBOE_PCONFIGH_WIDTHMASK 0x04 -#define OBOE_PCONFIGL_WIDTHMASK 0xe0 -#define OBOE_PCONFIGL_PREAMBLEMASK 0x1f - -#define OBOE_PCONFIG_BAUDMASK 0xfc00 -#define OBOE_PCONFIG_BAUDSHIFT 10 -#define OBOE_PCONFIG_WIDTHMASK 0x04e0 -#define OBOE_PCONFIG_WIDTHSHIFT 5 -#define OBOE_PCONFIG_PREAMBLEMASK 0x001f -#define OBOE_PCONFIG_PREAMBLESHIFT 0 - -#define OBOE_MAXLENL OBOE_REG(0x1a) -#define OBOE_MAXLENH OBOE_REG(0x1b) - -#define OBOE_RXCOUNTH OBOE_REG(0x1c) /*Reset on recipt */ -#define OBOE_RXCOUNTL OBOE_REG(0x1d) /*of whole packet */ - -/* The PCI ID of the OBOE chip */ -#ifndef PCI_DEVICE_ID_FIR701 -#define PCI_DEVICE_ID_FIR701 0x0701 -#endif - -#ifndef PCI_DEVICE_ID_FIRD01 -#define PCI_DEVICE_ID_FIRD01 0x0d01 -#endif - -struct OboeSlot -{ - __u16 len; /*Tweleve bits of packet length */ - __u8 unused; - __u8 control; /*Slot control/status see below */ - __u32 address; /*Slot buffer address */ -} -__packed; - -#define OBOE_NTASKS OBOE_TXRING_OFFSET_IN_SLOTS - -struct OboeRing -{ - struct OboeSlot rx[OBOE_NTASKS]; - struct OboeSlot tx[OBOE_NTASKS]; -}; - -#define OBOE_RING_LEN (sizeof(struct OboeRing)) - - -#define OBOE_CTL_TX_HW_OWNS 0x80 /*W/R This slot owned by the hardware */ -#define OBOE_CTL_TX_DISTX_CRC 0x40 /*W Disable CRC generation for [FM]IR */ -#define OBOE_CTL_TX_BAD_CRC 0x20 /*W Generate bad CRC */ -#define OBOE_CTL_TX_SIP 0x10 /*W Generate an SIP after xmittion */ -#define OBOE_CTL_TX_MKUNDER 0x08 /*W Generate an underrun error */ -#define OBOE_CTL_TX_RTCENTX 0x04 /*W Enable receiver and generate TXdone */ - /* After this slot is processed */ -#define OBOE_CTL_TX_UNDER 0x01 /*R Set by hardware to indicate underrun */ - - -#define OBOE_CTL_RX_HW_OWNS 0x80 /*W/R This slot owned by hardware */ -#define OBOE_CTL_RX_PHYERR 0x40 /*R Decoder error on receiption */ -#define OBOE_CTL_RX_CRCERR 0x20 /*R CRC error only set for [FM]IR */ -#define OBOE_CTL_RX_LENGTH 0x10 /*R Packet > max Rx length */ -#define OBOE_CTL_RX_OVER 0x08 /*R set to indicate an overflow */ -#define OBOE_CTL_RX_SIRBAD 0x04 /*R SIR had BOF in packet or ABORT sequence */ -#define OBOE_CTL_RX_RXEOF 0x02 /*R Finished receiving on this slot */ - - -struct toshoboe_cb -{ - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - struct tty_driver ttydev; - - struct irlap_cb *irlap; /* The link layer we are binded to */ - - chipio_t io; /* IrDA controller information */ - struct qos_info qos; /* QoS capabilities for this device */ - - __u32 flags; /* Interface flags */ - - struct pci_dev *pdev; /*PCI device */ - int base; /*IO base */ - - - int txpending; /*how many tx's are pending */ - int txs, rxs; /*Which slots are we at */ - - int irdad; /*Driver under control of netdev end */ - int async; /*Driver under control of async end */ - - - int stopped; /*Stopped by some or other APM stuff */ - - int filter; /*In SIR mode do we want to receive - frames or byte ranges */ - - void *ringbuf; /*The ring buffer */ - struct OboeRing *ring; /*The ring */ - - void *tx_bufs[OBOE_RING_MAX_SIZE]; /*The buffers */ - void *rx_bufs[OBOE_RING_MAX_SIZE]; - - - int speed; /*Current setting of the speed */ - int new_speed; /*Set to request a speed change */ - -/* The spinlock protect critical parts of the driver. - * Locking is done like this : - * spin_lock_irqsave(&self->spinlock, flags); - * Releasing the lock : - * spin_unlock_irqrestore(&self->spinlock, flags); - */ - spinlock_t spinlock; - /* Used for the probe and diagnostics code */ - int int_rx; - int int_tx; - int int_txunder; - int int_rxover; - int int_sip; -}; - - -#endif diff --git a/drivers/staging/irda/drivers/esi-sir.c b/drivers/staging/irda/drivers/esi-sir.c deleted file mode 100644 index eb7aa6430bea..000000000000 --- a/drivers/staging/irda/drivers/esi-sir.c +++ /dev/null @@ -1,157 +0,0 @@ -/********************************************************************* - * - * Filename: esi.c - * Version: 1.6 - * Description: Driver for the Extended Systems JetEye PC dongle - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Feb 21 18:54:38 1998 - * Modified at: Sun Oct 27 22:01:04 2002 - * Modified by: Martin Diehl - * - * Copyright (c) 1999 Dag Brattli, , - * Copyright (c) 1998 Thomas Davis, , - * Copyright (c) 2002 Martin Diehl, , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int esi_open(struct sir_dev *); -static int esi_close(struct sir_dev *); -static int esi_change_speed(struct sir_dev *, unsigned); -static int esi_reset(struct sir_dev *); - -static struct dongle_driver esi = { - .owner = THIS_MODULE, - .driver_name = "JetEye PC ESI-9680 PC", - .type = IRDA_ESI_DONGLE, - .open = esi_open, - .close = esi_close, - .reset = esi_reset, - .set_speed = esi_change_speed, -}; - -static int __init esi_sir_init(void) -{ - return irda_register_dongle(&esi); -} - -static void __exit esi_sir_cleanup(void) -{ - irda_unregister_dongle(&esi); -} - -static int esi_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Power up and set dongle to 9600 baud */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200; - qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int esi_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function esi_change_speed (task) - * - * Set the speed for the Extended Systems JetEye PC ESI-9680 type dongle - * Apparently (see old esi-driver) no delays are needed here... - * - */ -static int esi_change_speed(struct sir_dev *dev, unsigned speed) -{ - int ret = 0; - int dtr, rts; - - switch (speed) { - case 19200: - dtr = TRUE; - rts = FALSE; - break; - case 115200: - dtr = rts = TRUE; - break; - default: - ret = -EINVAL; - speed = 9600; - /* fall through */ - case 9600: - dtr = FALSE; - rts = TRUE; - break; - } - - /* Change speed of dongle */ - sirdev_set_dtr_rts(dev, dtr, rts); - dev->speed = speed; - - return ret; -} - -/* - * Function esi_reset (task) - * - * Reset dongle; - * - */ -static int esi_reset(struct sir_dev *dev) -{ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - /* Hm, the old esi-driver left the dongle unpowered relying on - * the following speed change to repower. This might work for - * the esi because we only need the modem lines. However, now the - * general rule is reset must bring the dongle to some working - * well-known state because speed change might write to registers. - * The old esi-driver didn't any delay here - let's hope it' fine. - */ - - sirdev_set_dtr_rts(dev, FALSE, TRUE); - dev->speed = 9600; - - return 0; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-1"); /* IRDA_ESI_DONGLE */ - -module_init(esi_sir_init); -module_exit(esi_sir_cleanup); - diff --git a/drivers/staging/irda/drivers/girbil-sir.c b/drivers/staging/irda/drivers/girbil-sir.c deleted file mode 100644 index 7e0a5b8c6d53..000000000000 --- a/drivers/staging/irda/drivers/girbil-sir.c +++ /dev/null @@ -1,252 +0,0 @@ -/********************************************************************* - * - * Filename: girbil.c - * Version: 1.2 - * Description: Implementation for the Greenwich GIrBIL dongle - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Feb 6 21:02:33 1999 - * Modified at: Fri Dec 17 09:13:20 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int girbil_reset(struct sir_dev *dev); -static int girbil_open(struct sir_dev *dev); -static int girbil_close(struct sir_dev *dev); -static int girbil_change_speed(struct sir_dev *dev, unsigned speed); - -/* Control register 1 */ -#define GIRBIL_TXEN 0x01 /* Enable transmitter */ -#define GIRBIL_RXEN 0x02 /* Enable receiver */ -#define GIRBIL_ECAN 0x04 /* Cancel self emitted data */ -#define GIRBIL_ECHO 0x08 /* Echo control characters */ - -/* LED Current Register (0x2) */ -#define GIRBIL_HIGH 0x20 -#define GIRBIL_MEDIUM 0x21 -#define GIRBIL_LOW 0x22 - -/* Baud register (0x3) */ -#define GIRBIL_2400 0x30 -#define GIRBIL_4800 0x31 -#define GIRBIL_9600 0x32 -#define GIRBIL_19200 0x33 -#define GIRBIL_38400 0x34 -#define GIRBIL_57600 0x35 -#define GIRBIL_115200 0x36 - -/* Mode register (0x4) */ -#define GIRBIL_IRDA 0x40 -#define GIRBIL_ASK 0x41 - -/* Control register 2 (0x5) */ -#define GIRBIL_LOAD 0x51 /* Load the new baud rate value */ - -static struct dongle_driver girbil = { - .owner = THIS_MODULE, - .driver_name = "Greenwich GIrBIL", - .type = IRDA_GIRBIL_DONGLE, - .open = girbil_open, - .close = girbil_close, - .reset = girbil_reset, - .set_speed = girbil_change_speed, -}; - -static int __init girbil_sir_init(void) -{ - return irda_register_dongle(&girbil); -} - -static void __exit girbil_sir_cleanup(void) -{ - irda_unregister_dongle(&girbil); -} - -static int girbil_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Power on dongle */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - qos->min_turn_time.bits = 0x03; - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int girbil_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function girbil_change_speed (dev, speed) - * - * Set the speed for the Girbil type dongle. - * - */ - -#define GIRBIL_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) - -static int girbil_change_speed(struct sir_dev *dev, unsigned speed) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - u8 control[2]; - static int ret = 0; - - /* dongle alread reset - port and dongle at default speed */ - - switch(state) { - - case SIRDEV_STATE_DONGLE_SPEED: - - /* Set DTR and Clear RTS to enter command mode */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - udelay(25); /* better wait a little while */ - - ret = 0; - switch (speed) { - default: - ret = -EINVAL; - /* fall through */ - case 9600: - control[0] = GIRBIL_9600; - break; - case 19200: - control[0] = GIRBIL_19200; - break; - case 34800: - control[0] = GIRBIL_38400; - break; - case 57600: - control[0] = GIRBIL_57600; - break; - case 115200: - control[0] = GIRBIL_115200; - break; - } - control[1] = GIRBIL_LOAD; - - /* Write control bytes */ - sirdev_raw_write(dev, control, 2); - - dev->speed = speed; - - state = GIRBIL_STATE_WAIT_SPEED; - delay = 100; - break; - - case GIRBIL_STATE_WAIT_SPEED: - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - udelay(25); /* better wait a little while */ - break; - - default: - net_err_ratelimited("%s - undefined state %d\n", - __func__, state); - ret = -EINVAL; - break; - } - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -/* - * Function girbil_reset (driver) - * - * This function resets the girbil dongle. - * - * Algorithm: - * 0. set RTS, and wait at least 5 ms - * 1. clear RTS - */ - - -#define GIRBIL_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET + 1) -#define GIRBIL_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET + 2) -#define GIRBIL_STATE_WAIT3_RESET (SIRDEV_STATE_DONGLE_RESET + 3) - -static int girbil_reset(struct sir_dev *dev) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - u8 control = GIRBIL_TXEN | GIRBIL_RXEN; - int ret = 0; - - switch (state) { - case SIRDEV_STATE_DONGLE_RESET: - /* Reset dongle */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - /* Sleep at least 5 ms */ - delay = 20; - state = GIRBIL_STATE_WAIT1_RESET; - break; - - case GIRBIL_STATE_WAIT1_RESET: - /* Set DTR and clear RTS to enter command mode */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - delay = 20; - state = GIRBIL_STATE_WAIT2_RESET; - break; - - case GIRBIL_STATE_WAIT2_RESET: - /* Write control byte */ - sirdev_raw_write(dev, &control, 1); - delay = 20; - state = GIRBIL_STATE_WAIT3_RESET; - break; - - case GIRBIL_STATE_WAIT3_RESET: - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - dev->speed = 9600; - break; - - default: - net_err_ratelimited("%s(), undefined state %d\n", - __func__, state); - ret = -1; - break; - } - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("Greenwich GIrBIL dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-4"); /* IRDA_GIRBIL_DONGLE */ - -module_init(girbil_sir_init); -module_exit(girbil_sir_cleanup); diff --git a/drivers/staging/irda/drivers/irda-usb.c b/drivers/staging/irda/drivers/irda-usb.c deleted file mode 100644 index bda6bdc6c70b..000000000000 --- a/drivers/staging/irda/drivers/irda-usb.c +++ /dev/null @@ -1,1906 +0,0 @@ -/***************************************************************************** - * - * Filename: irda-usb.c - * Version: 0.10 - * Description: IrDA-USB Driver - * Status: Experimental - * Author: Dag Brattli - * - * Copyright (C) 2000, Roman Weissgaerber - * Copyright (C) 2001, Dag Brattli - * Copyright (C) 2001, Jean Tourrilhes - * Copyright (C) 2004, SigmaTel, Inc. - * Copyright (C) 2005, Milan Beno - * Copyright (C) 2006, Nick Fedchik - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - *****************************************************************************/ - -/* - * IMPORTANT NOTE - * -------------- - * - * As of kernel 2.5.20, this is the state of compliance and testing of - * this driver (irda-usb) with regards to the USB low level drivers... - * - * This driver has been tested SUCCESSFULLY with the following drivers : - * o usb-uhci-hcd (For Intel/Via USB controllers) - * o uhci-hcd (Alternate/JE driver for Intel/Via USB controllers) - * o ohci-hcd (For other USB controllers) - * - * This driver has NOT been tested with the following drivers : - * o ehci-hcd (USB 2.0 controllers) - * - * Note that all HCD drivers do URB_ZERO_PACKET and timeout properly, - * so we don't have to worry about that anymore. - * One common problem is the failure to set the address on the dongle, - * but this happens before the driver gets loaded... - * - * Jean II - */ - -/*------------------------------------------------------------------*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "irda-usb.h" - -/*------------------------------------------------------------------*/ - -static int qos_mtt_bits = 0; - -/* These are the currently known IrDA USB dongles. Add new dongles here */ -static const struct usb_device_id dongles[] = { - /* ACTiSYS Corp., ACT-IR2000U FIR-USB Adapter */ - { USB_DEVICE(0x9c4, 0x011), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, - /* Look like ACTiSYS, Report : IBM Corp., IBM UltraPort IrDA */ - { USB_DEVICE(0x4428, 0x012), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, - /* KC Technology Inc., KC-180 USB IrDA Device */ - { USB_DEVICE(0x50f, 0x180), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, - /* Extended Systems, Inc., XTNDAccess IrDA USB (ESI-9685) */ - { USB_DEVICE(0x8e9, 0x100), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, - /* SigmaTel STIR4210/4220/4116 USB IrDA (VFIR) Bridge */ - { USB_DEVICE(0x66f, 0x4210), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, - { USB_DEVICE(0x66f, 0x4220), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, - { USB_DEVICE(0x66f, 0x4116), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, - { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .bInterfaceClass = USB_CLASS_APP_SPEC, - .bInterfaceSubClass = USB_CLASS_IRDA, - .driver_info = IUC_DEFAULT, }, - { }, /* The end */ -}; - -/* - * Important note : - * Devices based on the SigmaTel chipset (0x66f, 0x4200) are not designed - * using the "USB-IrDA specification" (yes, there exist such a thing), and - * therefore not supported by this driver (don't add them above). - * There is a Linux driver, stir4200, that support those USB devices. - * Jean II - */ - -MODULE_DEVICE_TABLE(usb, dongles); - -/*------------------------------------------------------------------*/ - -static void irda_usb_init_qos(struct irda_usb_cb *self) ; -static struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf); -static void irda_usb_disconnect(struct usb_interface *intf); -static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self); -static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb, - struct net_device *dev); -static int irda_usb_open(struct irda_usb_cb *self); -static void irda_usb_close(struct irda_usb_cb *self); -static void speed_bulk_callback(struct urb *urb); -static void write_bulk_callback(struct urb *urb); -static void irda_usb_receive(struct urb *urb); -static void irda_usb_rx_defer_expired(struct timer_list *t); -static int irda_usb_net_open(struct net_device *dev); -static int irda_usb_net_close(struct net_device *dev); -static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void irda_usb_net_timeout(struct net_device *dev); - -/************************ TRANSMIT ROUTINES ************************/ -/* - * Receive packets from the IrDA stack and send them on the USB pipe. - * Handle speed change, timeout and lot's of ugliness... - */ - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_build_header(self, skb, header) - * - * Builds USB-IrDA outbound header - * - * When we send an IrDA frame over an USB pipe, we add to it a 1 byte - * header. This function create this header with the proper values. - * - * Important note : the USB-IrDA spec 1.0 say very clearly in chapter 5.4.2.2 - * that the setting of the link speed and xbof number in this outbound header - * should be applied *AFTER* the frame has been sent. - * Unfortunately, some devices are not compliant with that... It seems that - * reading the spec is far too difficult... - * Jean II - */ -static void irda_usb_build_header(struct irda_usb_cb *self, - __u8 *header, - int force) -{ - /* Here we check if we have an STIR421x chip, - * and if either speed or xbofs (or both) needs - * to be changed. - */ - if (self->capability & IUC_STIR421X && - ((self->new_speed != -1) || (self->new_xbofs != -1))) { - - /* With STIR421x, speed and xBOFs must be set at the same - * time, even if only one of them changes. - */ - if (self->new_speed == -1) - self->new_speed = self->speed ; - - if (self->new_xbofs == -1) - self->new_xbofs = self->xbofs ; - } - - /* Set the link speed */ - if (self->new_speed != -1) { - /* Hum... Ugly hack :-( - * Some device are not compliant with the spec and change - * parameters *before* sending the frame. - Jean II - */ - if ((self->capability & IUC_SPEED_BUG) && - (!force) && (self->speed != -1)) { - /* No speed and xbofs change here - * (we'll do it later in the write callback) */ - pr_debug("%s(), not changing speed yet\n", __func__); - *header = 0; - return; - } - - pr_debug("%s(), changing speed to %d\n", - __func__, self->new_speed); - self->speed = self->new_speed; - /* We will do ` self->new_speed = -1; ' in the completion - * handler just in case the current URB fail - Jean II */ - - switch (self->speed) { - case 2400: - *header = SPEED_2400; - break; - default: - case 9600: - *header = SPEED_9600; - break; - case 19200: - *header = SPEED_19200; - break; - case 38400: - *header = SPEED_38400; - break; - case 57600: - *header = SPEED_57600; - break; - case 115200: - *header = SPEED_115200; - break; - case 576000: - *header = SPEED_576000; - break; - case 1152000: - *header = SPEED_1152000; - break; - case 4000000: - *header = SPEED_4000000; - self->new_xbofs = 0; - break; - case 16000000: - *header = SPEED_16000000; - self->new_xbofs = 0; - break; - } - } else - /* No change */ - *header = 0; - - /* Set the negotiated additional XBOFS */ - if (self->new_xbofs != -1) { - pr_debug("%s(), changing xbofs to %d\n", - __func__, self->new_xbofs); - self->xbofs = self->new_xbofs; - /* We will do ` self->new_xbofs = -1; ' in the completion - * handler just in case the current URB fail - Jean II */ - - switch (self->xbofs) { - case 48: - *header |= 0x10; - break; - case 28: - case 24: /* USB spec 1.0 says 24 */ - *header |= 0x20; - break; - default: - case 12: - *header |= 0x30; - break; - case 5: /* Bug in IrLAP spec? (should be 6) */ - case 6: - *header |= 0x40; - break; - case 3: - *header |= 0x50; - break; - case 2: - *header |= 0x60; - break; - case 1: - *header |= 0x70; - break; - case 0: - *header |= 0x80; - break; - } - } -} - -/* -* calculate turnaround time for SigmaTel header -*/ -static __u8 get_turnaround_time(struct sk_buff *skb) -{ - int turnaround_time = irda_get_mtt(skb); - - if ( turnaround_time == 0 ) - return 0; - else if ( turnaround_time <= 10 ) - return 1; - else if ( turnaround_time <= 50 ) - return 2; - else if ( turnaround_time <= 100 ) - return 3; - else if ( turnaround_time <= 500 ) - return 4; - else if ( turnaround_time <= 1000 ) - return 5; - else if ( turnaround_time <= 5000 ) - return 6; - else - return 7; -} - - -/*------------------------------------------------------------------*/ -/* - * Send a command to change the speed of the dongle - * Need to be called with spinlock on. - */ -static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self) -{ - __u8 *frame; - struct urb *urb; - int ret; - - pr_debug("%s(), speed=%d, xbofs=%d\n", __func__, - self->new_speed, self->new_xbofs); - - /* Grab the speed URB */ - urb = self->speed_urb; - if (urb->status != 0) { - net_warn_ratelimited("%s(), URB still in use!\n", __func__); - return; - } - - /* Allocate the fake frame */ - frame = self->speed_buff; - - /* Set the new speed and xbofs in this fake frame */ - irda_usb_build_header(self, frame, 1); - - if (self->capability & IUC_STIR421X) { - if (frame[0] == 0) return ; // do nothing if no change - frame[1] = 0; // other parameters don't change here - frame[2] = 0; - } - - /* Submit the 0 length IrDA frame to trigger new speed settings */ - usb_fill_bulk_urb(urb, self->usbdev, - usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), - frame, IRDA_USB_SPEED_MTU, - speed_bulk_callback, self); - urb->transfer_buffer_length = self->header_length; - urb->transfer_flags = 0; - - /* Irq disabled -> GFP_ATOMIC */ - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - net_warn_ratelimited("%s(), failed Speed URB\n", __func__); -} - -/*------------------------------------------------------------------*/ -/* - * Speed URB callback - * Now, we can only get called for the speed URB. - */ -static void speed_bulk_callback(struct urb *urb) -{ - struct irda_usb_cb *self = urb->context; - - /* We should always have a context */ - IRDA_ASSERT(self != NULL, return;); - /* We should always be called for the speed URB */ - IRDA_ASSERT(urb == self->speed_urb, return;); - - /* Check for timeout and other USB nasties */ - if (urb->status != 0) { - /* I get a lot of -ECONNABORTED = -103 here - Jean II */ - pr_debug("%s(), URB complete status %d, transfer_flags 0x%04X\n", - __func__, urb->status, urb->transfer_flags); - - /* Don't do anything here, that might confuse the USB layer. - * Instead, we will wait for irda_usb_net_timeout(), the - * network layer watchdog, to fix the situation. - * Jean II */ - /* A reset of the dongle might be welcomed here - Jean II */ - return; - } - - /* urb is now available */ - //urb->status = 0; -> tested above - - /* New speed and xbof is now committed in hardware */ - self->new_speed = -1; - self->new_xbofs = -1; - - /* Allow the stack to send more packets */ - netif_wake_queue(self->netdev); -} - -/*------------------------------------------------------------------*/ -/* - * Send an IrDA frame to the USB dongle (for transmission) - */ -static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct irda_usb_cb *self = netdev_priv(netdev); - struct urb *urb = self->tx_urb; - unsigned long flags; - s32 speed; - s16 xbofs; - int res, mtt; - - pr_debug("%s() on %s\n", __func__, netdev->name); - - netif_stop_queue(netdev); - - /* Protect us from USB callbacks, net watchdog and else. */ - spin_lock_irqsave(&self->lock, flags); - - /* Check if the device is still there. - * We need to check self->present under the spinlock because - * of irda_usb_disconnect() is synchronous - Jean II */ - if (!self->present) { - pr_debug("%s(), Device is gone...\n", __func__); - goto drop; - } - - /* Check if we need to change the number of xbofs */ - xbofs = irda_get_next_xbofs(skb); - if ((xbofs != self->xbofs) && (xbofs != -1)) { - self->new_xbofs = xbofs; - } - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->speed) && (speed != -1)) { - /* Set the desired speed */ - self->new_speed = speed; - - /* Check for empty frame */ - if (!skb->len) { - /* IrLAP send us an empty frame to make us change the - * speed. Changing speed with the USB adapter is in - * fact sending an empty frame to the adapter, so we - * could just let the present function do its job. - * However, we would wait for min turn time, - * do an extra memcpy and increment packet counters... - * Jean II */ - irda_usb_change_speed_xbofs(self); - netif_trans_update(netdev); - /* Will netif_wake_queue() in callback */ - goto drop; - } - } - - if (urb->status != 0) { - net_warn_ratelimited("%s(), URB still in use!\n", __func__); - goto drop; - } - - skb_copy_from_linear_data(skb, self->tx_buff + self->header_length, skb->len); - - /* Change setting for next frame */ - if (self->capability & IUC_STIR421X) { - __u8 turnaround_time; - __u8* frame = self->tx_buff; - turnaround_time = get_turnaround_time( skb ); - irda_usb_build_header(self, frame, 0); - frame[2] = turnaround_time; - if ((skb->len != 0) && - ((skb->len % 128) == 0) && - ((skb->len % 512) != 0)) { - /* add extra byte for special SigmaTel feature */ - frame[1] = 1; - skb_put(skb, 1); - } else { - frame[1] = 0; - } - } else { - irda_usb_build_header(self, self->tx_buff, 0); - } - - /* FIXME: Make macro out of this one */ - ((struct irda_skb_cb *)skb->cb)->context = self; - - usb_fill_bulk_urb(urb, self->usbdev, - usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), - self->tx_buff, skb->len + self->header_length, - write_bulk_callback, skb); - - /* This flag (URB_ZERO_PACKET) indicates that what we send is not - * a continuous stream of data but separate packets. - * In this case, the USB layer will insert an empty USB frame (TD) - * after each of our packets that is exact multiple of the frame size. - * This is how the dongle will detect the end of packet - Jean II */ - urb->transfer_flags = URB_ZERO_PACKET; - - /* Generate min turn time. FIXME: can we do better than this? */ - /* Trying to a turnaround time at this level is trying to measure - * processor clock cycle with a wrist-watch, approximate at best... - * - * What we know is the last time we received a frame over USB. - * Due to latency over USB that depend on the USB load, we don't - * know when this frame was received over IrDA (a few ms before ?) - * Then, same story for our outgoing frame... - * - * In theory, the USB dongle is supposed to handle the turnaround - * by itself (spec 1.0, chater 4, page 6). Who knows ??? That's - * why this code is enabled only for dongles that doesn't meet - * the spec. - * Jean II */ - if (self->capability & IUC_NO_TURN) { - mtt = irda_get_mtt(skb); - if (mtt) { - int diff; - diff = ktime_us_delta(ktime_get(), self->stamp); -#ifdef IU_USB_MIN_RTT - /* Factor in USB delays -> Get rid of udelay() that - * would be lost in the noise - Jean II */ - diff += IU_USB_MIN_RTT; -#endif /* IU_USB_MIN_RTT */ - - /* Check if the mtt is larger than the time we have - * already used by all the protocol processing - */ - if (mtt > diff) { - mtt -= diff; - if (mtt > 1000) - mdelay(mtt/1000); - else - udelay(mtt); - } - } - } - - /* Ask USB to send the packet - Irq disabled -> GFP_ATOMIC */ - if ((res = usb_submit_urb(urb, GFP_ATOMIC))) { - net_warn_ratelimited("%s(), failed Tx URB\n", __func__); - netdev->stats.tx_errors++; - /* Let USB recover : We will catch that in the watchdog */ - /*netif_start_queue(netdev);*/ - } else { - /* Increment packet stats */ - netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; - - netif_trans_update(netdev); - } - spin_unlock_irqrestore(&self->lock, flags); - - return NETDEV_TX_OK; - -drop: - /* Drop silently the skb and exit */ - dev_kfree_skb(skb); - spin_unlock_irqrestore(&self->lock, flags); - return NETDEV_TX_OK; -} - -/*------------------------------------------------------------------*/ -/* - * Note : this function will be called only for tx_urb... - */ -static void write_bulk_callback(struct urb *urb) -{ - unsigned long flags; - struct sk_buff *skb = urb->context; - struct irda_usb_cb *self = ((struct irda_skb_cb *) skb->cb)->context; - - /* We should always have a context */ - IRDA_ASSERT(self != NULL, return;); - /* We should always be called for the speed URB */ - IRDA_ASSERT(urb == self->tx_urb, return;); - - /* Free up the skb */ - dev_kfree_skb_any(skb); - urb->context = NULL; - - /* Check for timeout and other USB nasties */ - if (urb->status != 0) { - /* I get a lot of -ECONNABORTED = -103 here - Jean II */ - pr_debug("%s(), URB complete status %d, transfer_flags 0x%04X\n", - __func__, urb->status, urb->transfer_flags); - - /* Don't do anything here, that might confuse the USB layer, - * and we could go in recursion and blow the kernel stack... - * Instead, we will wait for irda_usb_net_timeout(), the - * network layer watchdog, to fix the situation. - * Jean II */ - /* A reset of the dongle might be welcomed here - Jean II */ - return; - } - - /* urb is now available */ - //urb->status = 0; -> tested above - - /* Make sure we read self->present properly */ - spin_lock_irqsave(&self->lock, flags); - - /* If the network is closed, stop everything */ - if ((!self->netopen) || (!self->present)) { - pr_debug("%s(), Network is gone...\n", __func__); - spin_unlock_irqrestore(&self->lock, flags); - return; - } - - /* If changes to speed or xbofs is pending... */ - if ((self->new_speed != -1) || (self->new_xbofs != -1)) { - if ((self->new_speed != self->speed) || - (self->new_xbofs != self->xbofs)) { - /* We haven't changed speed yet (because of - * IUC_SPEED_BUG), so do it now - Jean II */ - pr_debug("%s(), Changing speed now...\n", __func__); - irda_usb_change_speed_xbofs(self); - } else { - /* New speed and xbof is now committed in hardware */ - self->new_speed = -1; - self->new_xbofs = -1; - /* Done, waiting for next packet */ - netif_wake_queue(self->netdev); - } - } else { - /* Otherwise, allow the stack to send more packets */ - netif_wake_queue(self->netdev); - } - spin_unlock_irqrestore(&self->lock, flags); -} - -/*------------------------------------------------------------------*/ -/* - * Watchdog timer from the network layer. - * After a predetermined timeout, if we don't give confirmation that - * the packet has been sent (i.e. no call to netif_wake_queue()), - * the network layer will call this function. - * Note that URB that we submit have also a timeout. When the URB timeout - * expire, the normal URB callback is called (write_bulk_callback()). - */ -static void irda_usb_net_timeout(struct net_device *netdev) -{ - unsigned long flags; - struct irda_usb_cb *self = netdev_priv(netdev); - struct urb *urb; - int done = 0; /* If we have made any progress */ - - pr_debug("%s(), Network layer thinks we timed out!\n", __func__); - IRDA_ASSERT(self != NULL, return;); - - /* Protect us from USB callbacks, net Tx and else. */ - spin_lock_irqsave(&self->lock, flags); - - /* self->present *MUST* be read under spinlock */ - if (!self->present) { - net_warn_ratelimited("%s(), device not present!\n", __func__); - netif_stop_queue(netdev); - spin_unlock_irqrestore(&self->lock, flags); - return; - } - - /* Check speed URB */ - urb = self->speed_urb; - if (urb->status != 0) { - pr_debug("%s: Speed change timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", - netdev->name, urb->status, urb->transfer_flags); - - switch (urb->status) { - case -EINPROGRESS: - usb_unlink_urb(urb); - /* Note : above will *NOT* call netif_wake_queue() - * in completion handler, we will come back here. - * Jean II */ - done = 1; - break; - case -ECONNRESET: - case -ENOENT: /* urb unlinked by us */ - default: /* ??? - Play safe */ - urb->status = 0; - netif_wake_queue(self->netdev); - done = 1; - break; - } - } - - /* Check Tx URB */ - urb = self->tx_urb; - if (urb->status != 0) { - struct sk_buff *skb = urb->context; - - pr_debug("%s: Tx timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", - netdev->name, urb->status, urb->transfer_flags); - - /* Increase error count */ - netdev->stats.tx_errors++; - -#ifdef IU_BUG_KICK_TIMEOUT - /* Can't be a bad idea to reset the speed ;-) - Jean II */ - if(self->new_speed == -1) - self->new_speed = self->speed; - if(self->new_xbofs == -1) - self->new_xbofs = self->xbofs; - irda_usb_change_speed_xbofs(self); -#endif /* IU_BUG_KICK_TIMEOUT */ - - switch (urb->status) { - case -EINPROGRESS: - usb_unlink_urb(urb); - /* Note : above will *NOT* call netif_wake_queue() - * in completion handler, because urb->status will - * be -ENOENT. We will fix that at the next watchdog, - * leaving more time to USB to recover... - * Jean II */ - done = 1; - break; - case -ECONNRESET: - case -ENOENT: /* urb unlinked by us */ - default: /* ??? - Play safe */ - if(skb != NULL) { - dev_kfree_skb_any(skb); - urb->context = NULL; - } - urb->status = 0; - netif_wake_queue(self->netdev); - done = 1; - break; - } - } - spin_unlock_irqrestore(&self->lock, flags); - - /* Maybe we need a reset */ - /* Note : Some drivers seem to use a usb_set_interface() when they - * need to reset the hardware. Hum... - */ - - /* if(done == 0) */ -} - -/************************* RECEIVE ROUTINES *************************/ -/* - * Receive packets from the USB layer stack and pass them to the IrDA stack. - * Try to work around USB failures... - */ - -/* - * Note : - * Some of you may have noticed that most dongle have an interrupt in pipe - * that we don't use. Here is the little secret... - * When we hang a Rx URB on the bulk in pipe, it generates some USB traffic - * in every USB frame. This is unnecessary overhead. - * The interrupt in pipe will generate an event every time a packet is - * received. Reading an interrupt pipe adds minimal overhead, but has some - * latency (~1ms). - * If we are connected (speed != 9600), we want to minimise latency, so - * we just always hang the Rx URB and ignore the interrupt. - * If we are not connected (speed == 9600), there is usually no Rx traffic, - * and we want to minimise the USB overhead. In this case we should wait - * on the interrupt pipe and hang the Rx URB only when an interrupt is - * received. - * Jean II - * - * Note : don't read the above as what we are currently doing, but as - * something we could do with KC dongle. Also don't forget that the - * interrupt pipe is not part of the original standard, so this would - * need to be optional... - * Jean II - */ - -/*------------------------------------------------------------------*/ -/* - * Submit a Rx URB to the USB layer to handle reception of a frame - * Mostly called by the completion callback of the previous URB. - * - * Jean II - */ -static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, struct urb *urb) -{ - struct irda_skb_cb *cb; - int ret; - - /* This should never happen */ - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(urb != NULL, return;); - - /* Save ourselves in the skb */ - cb = (struct irda_skb_cb *) skb->cb; - cb->context = self; - - /* Reinitialize URB */ - usb_fill_bulk_urb(urb, self->usbdev, - usb_rcvbulkpipe(self->usbdev, self->bulk_in_ep), - skb->data, IRDA_SKB_MAX_MTU, - irda_usb_receive, skb); - urb->status = 0; - - /* Can be called from irda_usb_receive (irq handler) -> GFP_ATOMIC */ - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) { - /* If this ever happen, we are in deep s***. - * Basically, the Rx path will stop... */ - net_warn_ratelimited("%s(), Failed to submit Rx URB %d\n", - __func__, ret); - } -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_receive(urb) - * - * Called by the USB subsystem when a frame has been received - * - */ -static void irda_usb_receive(struct urb *urb) -{ - struct sk_buff *skb = (struct sk_buff *) urb->context; - struct irda_usb_cb *self; - struct irda_skb_cb *cb; - struct sk_buff *newskb; - struct sk_buff *dataskb; - struct urb *next_urb; - unsigned int len, docopy; - - pr_debug("%s(), len=%d\n", __func__, urb->actual_length); - - /* Find ourselves */ - cb = (struct irda_skb_cb *) skb->cb; - IRDA_ASSERT(cb != NULL, return;); - self = (struct irda_usb_cb *) cb->context; - IRDA_ASSERT(self != NULL, return;); - - /* If the network is closed or the device gone, stop everything */ - if ((!self->netopen) || (!self->present)) { - pr_debug("%s(), Network is gone!\n", __func__); - /* Don't re-submit the URB : will stall the Rx path */ - return; - } - - /* Check the status */ - if (urb->status != 0) { - switch (urb->status) { - case -EILSEQ: - self->netdev->stats.rx_crc_errors++; - /* Also precursor to a hot-unplug on UHCI. */ - /* Fallthrough... */ - case -ECONNRESET: - /* Random error, if I remember correctly */ - /* uhci_cleanup_unlink() is going to kill the Rx - * URB just after we return. No problem, at this - * point the URB will be idle ;-) - Jean II */ - case -ESHUTDOWN: - /* That's usually a hot-unplug. Submit will fail... */ - case -ETIME: - /* Usually precursor to a hot-unplug on OHCI. */ - default: - self->netdev->stats.rx_errors++; - pr_debug("%s(), RX status %d, transfer_flags 0x%04X\n", - __func__, urb->status, urb->transfer_flags); - break; - } - /* If we received an error, we don't want to resubmit the - * Rx URB straight away but to give the USB layer a little - * bit of breathing room. - * We are in the USB thread context, therefore there is a - * danger of recursion (new URB we submit fails, we come - * back here). - * With recent USB stack (2.6.15+), I'm seeing that on - * hot unplug of the dongle... - * Lowest effective timer is 10ms... - * Jean II */ - self->rx_defer_timer_urb = urb; - mod_timer(&self->rx_defer_timer, - jiffies + msecs_to_jiffies(10)); - - return; - } - - /* Check for empty frames */ - if (urb->actual_length <= self->header_length) { - net_warn_ratelimited("%s(), empty frame!\n", __func__); - goto done; - } - - /* - * Remember the time we received this frame, so we can - * reduce the min turn time a bit since we will know - * how much time we have used for protocol processing - */ - self->stamp = ktime_get(); - - /* Check if we need to copy the data to a new skb or not. - * For most frames, we use ZeroCopy and pass the already - * allocated skb up the stack. - * If the frame is small, it is more efficient to copy it - * to save memory (copy will be fast anyway - that's - * called Rx-copy-break). Jean II */ - docopy = (urb->actual_length < IRDA_RX_COPY_THRESHOLD); - - /* Allocate a new skb */ - if (self->capability & IUC_STIR421X) - newskb = dev_alloc_skb(docopy ? urb->actual_length : - IRDA_SKB_MAX_MTU + - USB_IRDA_STIR421X_HEADER); - else - newskb = dev_alloc_skb(docopy ? urb->actual_length : - IRDA_SKB_MAX_MTU); - - if (!newskb) { - self->netdev->stats.rx_dropped++; - /* We could deliver the current skb, but this would stall - * the Rx path. Better drop the packet... Jean II */ - goto done; - } - - /* Make sure IP header get aligned (IrDA header is 5 bytes) */ - /* But IrDA-USB header is 1 byte. Jean II */ - //skb_reserve(newskb, USB_IRDA_HEADER - 1); - - if(docopy) { - /* Copy packet, so we can recycle the original */ - skb_copy_from_linear_data(skb, newskb->data, urb->actual_length); - /* Deliver this new skb */ - dataskb = newskb; - /* And hook the old skb to the URB - * Note : we don't need to "clean up" the old skb, - * as we never touched it. Jean II */ - } else { - /* We are using ZeroCopy. Deliver old skb */ - dataskb = skb; - /* And hook the new skb to the URB */ - skb = newskb; - } - - /* Set proper length on skb & remove USB-IrDA header */ - skb_put(dataskb, urb->actual_length); - skb_pull(dataskb, self->header_length); - - /* Ask the networking layer to queue the packet for the IrDA stack */ - dataskb->dev = self->netdev; - skb_reset_mac_header(dataskb); - dataskb->protocol = htons(ETH_P_IRDA); - len = dataskb->len; - netif_rx(dataskb); - - /* Keep stats up to date */ - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - -done: - /* Note : at this point, the URB we've just received (urb) - * is still referenced by the USB layer. For example, if we - * have received a -ECONNRESET, uhci_cleanup_unlink() will - * continue to process it (in fact, cleaning it up). - * If we were to submit this URB, disaster would ensue. - * Therefore, we submit our idle URB, and put this URB in our - * idle slot.... - * Jean II */ - /* Note : with this scheme, we could submit the idle URB before - * processing the Rx URB. I don't think it would buy us anything as - * we are running in the USB thread context. Jean II */ - next_urb = self->idle_rx_urb; - - /* Recycle Rx URB : Now, the idle URB is the present one */ - urb->context = NULL; - self->idle_rx_urb = urb; - - /* Submit the idle URB to replace the URB we've just received. - * Do it last to avoid race conditions... Jean II */ - irda_usb_submit(self, skb, next_urb); -} - -/*------------------------------------------------------------------*/ -/* - * In case of errors, we want the USB layer to have time to recover. - * Now, it is time to resubmit ouur Rx URB... - */ -static void irda_usb_rx_defer_expired(struct timer_list *t) -{ - struct irda_usb_cb *self = from_timer(self, t, rx_defer_timer); - struct urb *urb = self->rx_defer_timer_urb; - struct sk_buff *skb = (struct sk_buff *) urb->context; - struct urb *next_urb; - - /* Same stuff as when Rx is done, see above... */ - next_urb = self->idle_rx_urb; - urb->context = NULL; - self->idle_rx_urb = urb; - irda_usb_submit(self, skb, next_urb); -} - -/*------------------------------------------------------------------*/ -/* - * Callbak from IrDA layer. IrDA wants to know if we have - * started receiving anything. - */ -static int irda_usb_is_receiving(struct irda_usb_cb *self) -{ - /* Note : because of the way UHCI works, it's almost impossible - * to get this info. The Controller DMA directly to memory and - * signal only when the whole frame is finished. To know if the - * first TD of the URB has been filled or not seems hard work... - * - * The other solution would be to use the "receiving" command - * on the default decriptor with a usb_control_msg(), but that - * would add USB traffic and would return result only in the - * next USB frame (~1ms). - * - * I've been told that current dongles send status info on their - * interrupt endpoint, and that's what the Windows driver uses - * to know this info. Unfortunately, this is not yet in the spec... - * - * Jean II - */ - - return 0; /* For now */ -} - -#define STIR421X_PATCH_PRODUCT_VER "Product Version: " -#define STIR421X_PATCH_STMP_TAG "STMP" -#define STIR421X_PATCH_CODE_OFFSET 512 /* patch image starts before here */ -/* marks end of patch file header (PC DOS text file EOF character) */ -#define STIR421X_PATCH_END_OF_HDR_TAG 0x1A -#define STIR421X_PATCH_BLOCK_SIZE 1023 - -/* - * Function stir421x_fwupload (struct irda_usb_cb *self, - * unsigned char *patch, - * const unsigned int patch_len) - * - * Upload firmware code to SigmaTel 421X IRDA-USB dongle - */ -static int stir421x_fw_upload(struct irda_usb_cb *self, - const unsigned char *patch, - const unsigned int patch_len) -{ - int ret = -ENOMEM; - int actual_len = 0; - unsigned int i; - unsigned int block_size = 0; - unsigned char *patch_block; - - patch_block = kzalloc(STIR421X_PATCH_BLOCK_SIZE, GFP_KERNEL); - if (patch_block == NULL) - return -ENOMEM; - - /* break up patch into 1023-byte sections */ - for (i = 0; i < patch_len; i += block_size) { - block_size = patch_len - i; - - if (block_size > STIR421X_PATCH_BLOCK_SIZE) - block_size = STIR421X_PATCH_BLOCK_SIZE; - - /* upload the patch section */ - memcpy(patch_block, patch + i, block_size); - - ret = usb_bulk_msg(self->usbdev, - usb_sndbulkpipe(self->usbdev, - self->bulk_out_ep), - patch_block, block_size, - &actual_len, msecs_to_jiffies(500)); - pr_debug("%s(): Bulk send %u bytes, ret=%d\n", - __func__, actual_len, ret); - - if (ret < 0) - break; - - mdelay(10); - } - - kfree(patch_block); - - return ret; - } - -/* - * Function stir421x_patch_device(struct irda_usb_cb *self) - * - * Get a firmware code from userspase using hotplug request_firmware() call - */ -static int stir421x_patch_device(struct irda_usb_cb *self) -{ - unsigned int i; - int ret; - char stir421x_fw_name[12]; - const struct firmware *fw; - const unsigned char *fw_version_ptr; /* pointer to version string */ - unsigned long fw_version = 0; - - /* - * Known firmware patch file names for STIR421x dongles - * are "42101001.sb" or "42101002.sb" - */ - sprintf(stir421x_fw_name, "4210%4X.sb", - le16_to_cpu(self->usbdev->descriptor.bcdDevice)); - ret = request_firmware(&fw, stir421x_fw_name, &self->usbdev->dev); - if (ret < 0) - return ret; - - /* We get a patch from userspace */ - net_info_ratelimited("%s(): Received firmware %s (%zu bytes)\n", - __func__, stir421x_fw_name, fw->size); - - ret = -EINVAL; - - /* Get the bcd product version */ - if (!memcmp(fw->data, STIR421X_PATCH_PRODUCT_VER, - sizeof(STIR421X_PATCH_PRODUCT_VER) - 1)) { - fw_version_ptr = fw->data + - sizeof(STIR421X_PATCH_PRODUCT_VER) - 1; - - /* Let's check if the product version is dotted */ - if (fw_version_ptr[3] == '.' && - fw_version_ptr[7] == '.') { - unsigned long major, minor, build; - major = simple_strtoul(fw_version_ptr, NULL, 10); - minor = simple_strtoul(fw_version_ptr + 4, NULL, 10); - build = simple_strtoul(fw_version_ptr + 8, NULL, 10); - - fw_version = (major << 12) - + (minor << 8) - + ((build / 10) << 4) - + (build % 10); - - pr_debug("%s(): Firmware Product version %ld\n", - __func__, fw_version); - } - } - - if (self->usbdev->descriptor.bcdDevice == cpu_to_le16(fw_version)) { - /* - * If we're here, we've found a correct patch - * The actual image starts after the "STMP" keyword - * so forward to the firmware header tag - */ - for (i = 0; i < fw->size && fw->data[i] != - STIR421X_PATCH_END_OF_HDR_TAG; i++) ; - /* here we check for the out of buffer case */ - if (i < STIR421X_PATCH_CODE_OFFSET && i < fw->size && - STIR421X_PATCH_END_OF_HDR_TAG == fw->data[i]) { - if (!memcmp(fw->data + i + 1, STIR421X_PATCH_STMP_TAG, - sizeof(STIR421X_PATCH_STMP_TAG) - 1)) { - - /* We can upload the patch to the target */ - i += sizeof(STIR421X_PATCH_STMP_TAG); - ret = stir421x_fw_upload(self, &fw->data[i], - fw->size - i); - } - } - } - - release_firmware(fw); - - return ret; -} - - -/********************** IRDA DEVICE CALLBACKS **********************/ -/* - * Main calls from the IrDA/Network subsystem. - * Mostly registering a new irda-usb device and removing it.... - * We only deal with the IrDA side of the business, the USB side will - * be dealt with below... - */ - - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_net_open (dev) - * - * Network device is taken up. Usually this is done by "ifconfig irda0 up" - * - * Note : don't mess with self->netopen - Jean II - */ -static int irda_usb_net_open(struct net_device *netdev) -{ - struct irda_usb_cb *self; - unsigned long flags; - char hwname[16]; - int i; - - IRDA_ASSERT(netdev != NULL, return -1;); - self = netdev_priv(netdev); - IRDA_ASSERT(self != NULL, return -1;); - - spin_lock_irqsave(&self->lock, flags); - /* Can only open the device if it's there */ - if(!self->present) { - spin_unlock_irqrestore(&self->lock, flags); - net_warn_ratelimited("%s(), device not present!\n", __func__); - return -1; - } - - if(self->needspatch) { - spin_unlock_irqrestore(&self->lock, flags); - net_warn_ratelimited("%s(), device needs patch\n", __func__); - return -EIO ; - } - - /* Initialise default speed and xbofs value - * (IrLAP will change that soon) */ - self->speed = -1; - self->xbofs = -1; - self->new_speed = -1; - self->new_xbofs = -1; - - /* To do *before* submitting Rx urbs and starting net Tx queue - * Jean II */ - self->netopen = 1; - spin_unlock_irqrestore(&self->lock, flags); - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - * Note : will send immediately a speed change... - */ - sprintf(hwname, "usb#%d", self->usbdev->devnum); - self->irlap = irlap_open(netdev, &self->qos, hwname); - IRDA_ASSERT(self->irlap != NULL, return -1;); - - /* Allow IrLAP to send data to us */ - netif_start_queue(netdev); - - /* We submit all the Rx URB except for one that we keep idle. - * Need to be initialised before submitting other USBs, because - * in some cases as soon as we submit the URBs the USB layer - * will trigger a dummy receive - Jean II */ - self->idle_rx_urb = self->rx_urb[IU_MAX_ACTIVE_RX_URBS]; - self->idle_rx_urb->context = NULL; - - /* Now that we can pass data to IrLAP, allow the USB layer - * to send us some data... */ - for (i = 0; i < IU_MAX_ACTIVE_RX_URBS; i++) { - struct sk_buff *skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!skb) { - /* If this ever happen, we are in deep s***. - * Basically, we can't start the Rx path... */ - return -1; - } - //skb_reserve(newskb, USB_IRDA_HEADER - 1); - irda_usb_submit(self, skb, self->rx_urb[i]); - } - - /* Ready to play !!! */ - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_net_close (self) - * - * Network device is taken down. Usually this is done by - * "ifconfig irda0 down" - */ -static int irda_usb_net_close(struct net_device *netdev) -{ - struct irda_usb_cb *self; - int i; - - IRDA_ASSERT(netdev != NULL, return -1;); - self = netdev_priv(netdev); - IRDA_ASSERT(self != NULL, return -1;); - - /* Clear this flag *before* unlinking the urbs and *before* - * stopping the network Tx queue - Jean II */ - self->netopen = 0; - - /* Stop network Tx queue */ - netif_stop_queue(netdev); - - /* Kill defered Rx URB */ - del_timer(&self->rx_defer_timer); - - /* Deallocate all the Rx path buffers (URBs and skb) */ - for (i = 0; i < self->max_rx_urb; i++) { - struct urb *urb = self->rx_urb[i]; - struct sk_buff *skb = (struct sk_buff *) urb->context; - /* Cancel the receive command */ - usb_kill_urb(urb); - /* The skb is ours, free it */ - if(skb) { - dev_kfree_skb(skb); - urb->context = NULL; - } - } - /* Cancel Tx and speed URB - need to be synchronous to avoid races */ - usb_kill_urb(self->tx_urb); - usb_kill_urb(self->speed_urb); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * IOCTLs : Extra out-of-band network commands... - */ -static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - unsigned long flags; - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct irda_usb_cb *self; - int ret = 0; - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return -1;); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - /* Protect us from USB callbacks, net watchdog and else. */ - spin_lock_irqsave(&self->lock, flags); - /* Check if the device is still there */ - if(self->present) { - /* Set the desired speed */ - self->new_speed = irq->ifr_baudrate; - irda_usb_change_speed_xbofs(self); - } - spin_unlock_irqrestore(&self->lock, flags); - break; - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - /* Check if the IrDA stack is still there */ - if(self->netopen) - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = irda_usb_is_receiving(self); - break; - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -/*------------------------------------------------------------------*/ - -/********************* IRDA CONFIG SUBROUTINES *********************/ -/* - * Various subroutines dealing with IrDA and network stuff we use to - * configure and initialise each irda-usb instance. - * These functions are used below in the main calls of the driver... - */ - -/*------------------------------------------------------------------*/ -/* - * Set proper values in the IrDA QOS structure - */ -static inline void irda_usb_init_qos(struct irda_usb_cb *self) -{ - struct irda_class_desc *desc; - - - desc = self->irda_desc; - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - /* See spec section 7.2 for meaning. - * Values are little endian (as most USB stuff), the IrDA stack - * use it in native order (see parameters.c). - Jean II */ - self->qos.baud_rate.bits = le16_to_cpu(desc->wBaudRate); - self->qos.min_turn_time.bits = desc->bmMinTurnaroundTime; - self->qos.additional_bofs.bits = desc->bmAdditionalBOFs; - self->qos.window_size.bits = desc->bmWindowSize; - self->qos.data_size.bits = desc->bmDataSize; - - pr_debug("%s(), dongle says speed=0x%X, size=0x%X, window=0x%X, bofs=0x%X, turn=0x%X\n", - __func__, self->qos.baud_rate.bits, self->qos.data_size.bits, - self->qos.window_size.bits, self->qos.additional_bofs.bits, - self->qos.min_turn_time.bits); - - /* Don't always trust what the dongle tell us */ - if(self->capability & IUC_SIR_ONLY) - self->qos.baud_rate.bits &= 0x00ff; - if(self->capability & IUC_SMALL_PKT) - self->qos.data_size.bits = 0x07; - if(self->capability & IUC_NO_WINDOW) - self->qos.window_size.bits = 0x01; - if(self->capability & IUC_MAX_WINDOW) - self->qos.window_size.bits = 0x7f; - if(self->capability & IUC_MAX_XBOFS) - self->qos.additional_bofs.bits = 0x01; - -#if 1 - /* Module parameter can override the rx window size */ - if (qos_mtt_bits) - self->qos.min_turn_time.bits = qos_mtt_bits; -#endif - /* - * Note : most of those values apply only for the receive path, - * the transmit path will be set differently - Jean II - */ - irda_qos_bits_to_value(&self->qos); -} - -/*------------------------------------------------------------------*/ -static const struct net_device_ops irda_usb_netdev_ops = { - .ndo_open = irda_usb_net_open, - .ndo_stop = irda_usb_net_close, - .ndo_do_ioctl = irda_usb_net_ioctl, - .ndo_start_xmit = irda_usb_hard_xmit, - .ndo_tx_timeout = irda_usb_net_timeout, -}; - -/* - * Initialise the network side of the irda-usb instance - * Called when a new USB instance is registered in irda_usb_probe() - */ -static inline int irda_usb_open(struct irda_usb_cb *self) -{ - struct net_device *netdev = self->netdev; - - netdev->netdev_ops = &irda_usb_netdev_ops; - - irda_usb_init_qos(self); - - return register_netdev(netdev); -} - -/*------------------------------------------------------------------*/ -/* - * Cleanup the network side of the irda-usb instance - * Called when a USB instance is removed in irda_usb_disconnect() - */ -static inline void irda_usb_close(struct irda_usb_cb *self) -{ - /* Remove netdevice */ - unregister_netdev(self->netdev); - - /* Remove the speed buffer */ - kfree(self->speed_buff); - self->speed_buff = NULL; - - kfree(self->tx_buff); - self->tx_buff = NULL; -} - -/********************** USB CONFIG SUBROUTINES **********************/ -/* - * Various subroutines dealing with USB stuff we use to configure and - * initialise each irda-usb instance. - * These functions are used below in the main calls of the driver... - */ - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_parse_endpoints(dev, ifnum) - * - * Parse the various endpoints and find the one we need. - * - * The endpoint are the pipes used to communicate with the USB device. - * The spec defines 2 endpoints of type bulk transfer, one in, and one out. - * These are used to pass frames back and forth with the dongle. - * Most dongle have also an interrupt endpoint, that will be probably - * documented in the next spec... - */ -static inline int irda_usb_parse_endpoints(struct irda_usb_cb *self, struct usb_host_endpoint *endpoint, int ennum) -{ - int i; /* Endpoint index in table */ - - /* Init : no endpoints */ - self->bulk_in_ep = 0; - self->bulk_out_ep = 0; - self->bulk_int_ep = 0; - - /* Let's look at all those endpoints */ - for(i = 0; i < ennum; i++) { - /* All those variables will get optimised by the compiler, - * so let's aim for clarity... - Jean II */ - __u8 ep; /* Endpoint address */ - __u8 dir; /* Endpoint direction */ - __u8 attr; /* Endpoint attribute */ - __u16 psize; /* Endpoint max packet size in bytes */ - - /* Get endpoint address, direction and attribute */ - ep = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - dir = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK; - attr = endpoint[i].desc.bmAttributes; - psize = le16_to_cpu(endpoint[i].desc.wMaxPacketSize); - - /* Is it a bulk endpoint ??? */ - if(attr == USB_ENDPOINT_XFER_BULK) { - /* We need to find an IN and an OUT */ - if(dir == USB_DIR_IN) { - /* This is our Rx endpoint */ - self->bulk_in_ep = ep; - } else { - /* This is our Tx endpoint */ - self->bulk_out_ep = ep; - self->bulk_out_mtu = psize; - } - } else { - if((attr == USB_ENDPOINT_XFER_INT) && - (dir == USB_DIR_IN)) { - /* This is our interrupt endpoint */ - self->bulk_int_ep = ep; - } else { - net_err_ratelimited("%s(), Unrecognised endpoint %02X\n", - __func__, ep); - } - } - } - - pr_debug("%s(), And our endpoints are : in=%02X, out=%02X (%d), int=%02X\n", - __func__, self->bulk_in_ep, self->bulk_out_ep, - self->bulk_out_mtu, self->bulk_int_ep); - - return (self->bulk_in_ep != 0) && (self->bulk_out_ep != 0); -} - -#ifdef IU_DUMP_CLASS_DESC -/*------------------------------------------------------------------*/ -/* - * Function usb_irda_dump_class_desc(desc) - * - * Prints out the contents of the IrDA class descriptor - * - */ -static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) -{ - /* Values are little endian */ - printk("bLength=%x\n", desc->bLength); - printk("bDescriptorType=%x\n", desc->bDescriptorType); - printk("bcdSpecRevision=%x\n", le16_to_cpu(desc->bcdSpecRevision)); - printk("bmDataSize=%x\n", desc->bmDataSize); - printk("bmWindowSize=%x\n", desc->bmWindowSize); - printk("bmMinTurnaroundTime=%d\n", desc->bmMinTurnaroundTime); - printk("wBaudRate=%x\n", le16_to_cpu(desc->wBaudRate)); - printk("bmAdditionalBOFs=%x\n", desc->bmAdditionalBOFs); - printk("bIrdaRateSniff=%x\n", desc->bIrdaRateSniff); - printk("bMaxUnicastList=%x\n", desc->bMaxUnicastList); -} -#endif /* IU_DUMP_CLASS_DESC */ - -/*------------------------------------------------------------------*/ -/* - * Function irda_usb_find_class_desc(intf) - * - * Returns instance of IrDA class descriptor, or NULL if not found - * - * The class descriptor is some extra info that IrDA USB devices will - * offer to us, describing their IrDA characteristics. We will use that in - * irda_usb_init_qos() - */ -static inline struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf) -{ - struct usb_device *dev = interface_to_usbdev (intf); - struct irda_class_desc *desc; - int ret; - - desc = kzalloc(sizeof(*desc), GFP_KERNEL); - if (!desc) - return NULL; - - /* USB-IrDA class spec 1.0: - * 6.1.3: Standard "Get Descriptor" Device Request is not - * appropriate to retrieve class-specific descriptor - * 6.2.5: Class Specific "Get Class Descriptor" Interface Request - * is mandatory and returns the USB-IrDA class descriptor - */ - - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), - IU_REQ_GET_CLASS_DESC, - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, intf->altsetting->desc.bInterfaceNumber, desc, - sizeof(*desc), 500); - - pr_debug("%s(), ret=%d\n", __func__, ret); - if (ret < sizeof(*desc)) { - net_warn_ratelimited("usb-irda: class_descriptor read %s (%d)\n", - ret < 0 ? "failed" : "too short", ret); - } - else if (desc->bDescriptorType != USB_DT_IRDA) { - net_warn_ratelimited("usb-irda: bad class_descriptor type\n"); - } - else { -#ifdef IU_DUMP_CLASS_DESC - irda_usb_dump_class_desc(desc); -#endif /* IU_DUMP_CLASS_DESC */ - - return desc; - } - kfree(desc); - return NULL; -} - -/*********************** USB DEVICE CALLBACKS ***********************/ -/* - * Main calls from the USB subsystem. - * Mostly registering a new irda-usb device and removing it.... - */ - -/*------------------------------------------------------------------*/ -/* - * This routine is called by the USB subsystem for each new device - * in the system. We need to check if the device is ours, and in - * this case start handling it. - * The USB layer protect us from reentrancy (via BKL), so we don't need - * to spinlock in there... Jean II - */ -static int irda_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct net_device *net; - struct usb_device *dev = interface_to_usbdev(intf); - struct irda_usb_cb *self; - struct usb_host_interface *interface; - struct irda_class_desc *irda_desc; - int ret = -ENOMEM; - int i; /* Driver instance index / Rx URB index */ - - /* Note : the probe make sure to call us only for devices that - * matches the list of dongle (top of the file). So, we - * don't need to check if the dongle is really ours. - * Jean II */ - - net_info_ratelimited("IRDA-USB found at address %d, Vendor: %x, Product: %x\n", - dev->devnum, le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - net = alloc_irdadev(sizeof(*self)); - if (!net) - goto err_out; - - SET_NETDEV_DEV(net, &intf->dev); - self = netdev_priv(net); - self->netdev = net; - spin_lock_init(&self->lock); - timer_setup(&self->rx_defer_timer, irda_usb_rx_defer_expired, 0); - - self->capability = id->driver_info; - self->needspatch = ((self->capability & IUC_STIR421X) != 0); - - /* Create all of the needed urbs */ - if (self->capability & IUC_STIR421X) { - self->max_rx_urb = IU_SIGMATEL_MAX_RX_URBS; - self->header_length = USB_IRDA_STIR421X_HEADER; - } else { - self->max_rx_urb = IU_MAX_RX_URBS; - self->header_length = USB_IRDA_HEADER; - } - - self->rx_urb = kcalloc(self->max_rx_urb, sizeof(struct urb *), - GFP_KERNEL); - if (!self->rx_urb) - goto err_free_net; - - for (i = 0; i < self->max_rx_urb; i++) { - self->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); - if (!self->rx_urb[i]) { - goto err_out_1; - } - } - self->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!self->tx_urb) { - goto err_out_1; - } - self->speed_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!self->speed_urb) { - goto err_out_2; - } - - /* Is this really necessary? (no, except maybe for broken devices) */ - if (usb_reset_configuration (dev) < 0) { - dev_err(&intf->dev, "reset_configuration failed\n"); - ret = -EIO; - goto err_out_3; - } - - /* Is this really necessary? */ - /* Note : some driver do hardcode the interface number, some others - * specify an alternate, but very few driver do like this. - * Jean II */ - ret = usb_set_interface(dev, intf->altsetting->desc.bInterfaceNumber, 0); - pr_debug("usb-irda: set interface %d result %d\n", - intf->altsetting->desc.bInterfaceNumber, ret); - switch (ret) { - case 0: - break; - case -EPIPE: /* -EPIPE = -32 */ - /* Martin Diehl says if we get a -EPIPE we should - * be fine and we don't need to do a usb_clear_halt(). - * - Jean II */ - pr_debug("%s(), Received -EPIPE, ignoring...\n", - __func__); - break; - default: - pr_debug("%s(), Unknown error %d\n", __func__, ret); - ret = -EIO; - goto err_out_3; - } - - /* Find our endpoints */ - interface = intf->cur_altsetting; - if(!irda_usb_parse_endpoints(self, interface->endpoint, - interface->desc.bNumEndpoints)) { - net_err_ratelimited("%s(), Bogus endpoints...\n", __func__); - ret = -EIO; - goto err_out_3; - } - - self->usbdev = dev; - - /* Find IrDA class descriptor */ - irda_desc = irda_usb_find_class_desc(intf); - ret = -ENODEV; - if (!irda_desc) - goto err_out_3; - - if (self->needspatch) { - ret = usb_control_msg (self->usbdev, usb_sndctrlpipe (self->usbdev, 0), - 0x02, 0x40, 0, 0, NULL, 0, 500); - if (ret < 0) { - pr_debug("usb_control_msg failed %d\n", ret); - goto err_out_3; - } else { - mdelay(10); - } - } - - self->irda_desc = irda_desc; - self->present = 1; - self->netopen = 0; - self->usbintf = intf; - - /* Allocate the buffer for speed changes */ - /* Don't change this buffer size and allocation without doing - * some heavy and complete testing. Don't ask why :-( - * Jean II */ - ret = -ENOMEM; - self->speed_buff = kzalloc(IRDA_USB_SPEED_MTU, GFP_KERNEL); - if (!self->speed_buff) - goto err_out_3; - - self->tx_buff = kzalloc(IRDA_SKB_MAX_MTU + self->header_length, - GFP_KERNEL); - if (!self->tx_buff) - goto err_out_4; - - ret = irda_usb_open(self); - if (ret) - goto err_out_5; - - net_info_ratelimited("IrDA: Registered device %s\n", net->name); - usb_set_intfdata(intf, self); - - if (self->needspatch) { - /* Now we fetch and upload the firmware patch */ - ret = stir421x_patch_device(self); - self->needspatch = (ret < 0); - if (self->needspatch) { - net_err_ratelimited("STIR421X: Couldn't upload patch\n"); - goto err_out_6; - } - - /* replace IrDA class descriptor with what patched device is now reporting */ - irda_desc = irda_usb_find_class_desc (self->usbintf); - if (!irda_desc) { - ret = -ENODEV; - goto err_out_6; - } - kfree(self->irda_desc); - self->irda_desc = irda_desc; - irda_usb_init_qos(self); - } - - return 0; -err_out_6: - unregister_netdev(self->netdev); -err_out_5: - kfree(self->tx_buff); -err_out_4: - kfree(self->speed_buff); -err_out_3: - /* Free all urbs that we may have created */ - usb_free_urb(self->speed_urb); -err_out_2: - usb_free_urb(self->tx_urb); -err_out_1: - for (i = 0; i < self->max_rx_urb; i++) - usb_free_urb(self->rx_urb[i]); - kfree(self->rx_urb); -err_free_net: - free_netdev(net); -err_out: - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * The current irda-usb device is removed, the USB layer tell us - * to shut it down... - * One of the constraints is that when we exit this function, - * we cannot use the usb_device no more. Gone. Destroyed. kfree(). - * Most other subsystem allow you to destroy the instance at a time - * when it's convenient to you, to postpone it to a later date, but - * not the USB subsystem. - * So, we must make bloody sure that everything gets deactivated. - * Jean II - */ -static void irda_usb_disconnect(struct usb_interface *intf) -{ - unsigned long flags; - struct irda_usb_cb *self = usb_get_intfdata(intf); - int i; - - usb_set_intfdata(intf, NULL); - if (!self) - return; - - /* Make sure that the Tx path is not executing. - Jean II */ - spin_lock_irqsave(&self->lock, flags); - - /* Oups ! We are not there any more. - * This will stop/desactivate the Tx path. - Jean II */ - self->present = 0; - - /* Kill defered Rx URB */ - del_timer(&self->rx_defer_timer); - - /* We need to have irq enabled to unlink the URBs. That's OK, - * at this point the Tx path is gone - Jean II */ - spin_unlock_irqrestore(&self->lock, flags); - - /* Hum... Check if networking is still active (avoid races) */ - if((self->netopen) || (self->irlap)) { - /* Accept no more transmissions */ - /*netif_device_detach(self->netdev);*/ - netif_stop_queue(self->netdev); - /* Stop all the receive URBs. Must be synchronous. */ - for (i = 0; i < self->max_rx_urb; i++) - usb_kill_urb(self->rx_urb[i]); - /* Cancel Tx and speed URB. - * Make sure it's synchronous to avoid races. */ - usb_kill_urb(self->tx_urb); - usb_kill_urb(self->speed_urb); - } - - /* Cleanup the device stuff */ - irda_usb_close(self); - /* No longer attached to USB bus */ - self->usbdev = NULL; - self->usbintf = NULL; - - /* Clean up our urbs */ - for (i = 0; i < self->max_rx_urb; i++) - usb_free_urb(self->rx_urb[i]); - kfree(self->rx_urb); - /* Clean up Tx and speed URB */ - usb_free_urb(self->tx_urb); - usb_free_urb(self->speed_urb); - - /* Free self and network device */ - free_netdev(self->netdev); - pr_debug("%s(), USB IrDA Disconnected\n", __func__); -} - -#ifdef CONFIG_PM -/* USB suspend, so power off the transmitter/receiver */ -static int irda_usb_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct irda_usb_cb *self = usb_get_intfdata(intf); - int i; - - netif_device_detach(self->netdev); - - if (self->tx_urb != NULL) - usb_kill_urb(self->tx_urb); - if (self->speed_urb != NULL) - usb_kill_urb(self->speed_urb); - for (i = 0; i < self->max_rx_urb; i++) { - if (self->rx_urb[i] != NULL) - usb_kill_urb(self->rx_urb[i]); - } - return 0; -} - -/* Coming out of suspend, so reset hardware */ -static int irda_usb_resume(struct usb_interface *intf) -{ - struct irda_usb_cb *self = usb_get_intfdata(intf); - int i; - - for (i = 0; i < self->max_rx_urb; i++) { - if (self->rx_urb[i] != NULL) - usb_submit_urb(self->rx_urb[i], GFP_KERNEL); - } - - netif_device_attach(self->netdev); - return 0; -} -#endif - -/*------------------------------------------------------------------*/ -/* - * USB device callbacks - */ -static struct usb_driver irda_driver = { - .name = "irda-usb", - .probe = irda_usb_probe, - .disconnect = irda_usb_disconnect, - .id_table = dongles, -#ifdef CONFIG_PM - .suspend = irda_usb_suspend, - .resume = irda_usb_resume, -#endif -}; - -module_usb_driver(irda_driver); - -/* - * Module parameters - */ -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); -MODULE_AUTHOR("Roman Weissgaerber , Dag Brattli , Jean Tourrilhes and Nick Fedchik "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/irda-usb.h b/drivers/staging/irda/drivers/irda-usb.h deleted file mode 100644 index 56ee8c16c5e2..000000000000 --- a/drivers/staging/irda/drivers/irda-usb.h +++ /dev/null @@ -1,175 +0,0 @@ -/***************************************************************************** - * - * Filename: irda-usb.h - * Version: 0.10 - * Description: IrDA-USB Driver - * Status: Experimental - * Author: Dag Brattli - * - * Copyright (C) 2001, Roman Weissgaerber - * Copyright (C) 2000, Dag Brattli - * Copyright (C) 2001, Jean Tourrilhes - * Copyright (C) 2004, SigmaTel, Inc. - * Copyright (C) 2005, Milan Beno - * Copyright (C) 2006, Nick FEdchik - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - *****************************************************************************/ - -#include - -#include -#include /* struct irlap_cb */ - -#define RX_COPY_THRESHOLD 200 -#define IRDA_USB_MAX_MTU 2051 -#define IRDA_USB_SPEED_MTU 64 /* Weird, but work like this */ - -/* Maximum number of active URB on the Rx path - * This is the amount of buffers the we keep between the USB harware and the - * IrDA stack. - * - * Note : the network layer does also queue the packets between us and the - * IrDA stack, and is actually pretty fast and efficient in doing that. - * Therefore, we don't need to have a large number of URBs, and we can - * perfectly live happy with only one. We certainly don't need to keep the - * full IrTTP window around here... - * I repeat for those who have trouble to understand : 1 URB is plenty - * good enough to handle back-to-back (brickwalled) frames. I tried it, - * it works (it's the hardware that has trouble doing it). - * - * Having 2 URBs would allow the USB stack to process one URB while we take - * care of the other and then swap the URBs... - * On the other hand, increasing the number of URB will have penalities - * in term of latency and will interact with the link management in IrLAP... - * Jean II */ -#define IU_MAX_ACTIVE_RX_URBS 1 /* Don't touch !!! */ - -/* When a Rx URB is passed back to us, we can't reuse it immediately, - * because it may still be referenced by the USB layer. Therefore we - * need to keep one extra URB in the Rx path. - * Jean II */ -#define IU_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + 1) - -/* Various ugly stuff to try to workaround generic problems */ -/* Send speed command in case of timeout, just for trying to get things sane */ -#define IU_BUG_KICK_TIMEOUT -/* Show the USB class descriptor */ -#undef IU_DUMP_CLASS_DESC -/* Assume a minimum round trip latency for USB transfer (in us)... - * USB transfer are done in the next USB slot if there is no traffic - * (1/19 msec) and is done at 12 Mb/s : - * Waiting for slot + tx = (53us + 16us) * 2 = 137us minimum. - * Rx notification will only be done at the end of the USB frame period : - * OHCI : frame period = 1ms - * UHCI : frame period = 1ms, but notification can take 2 or 3 ms :-( - * EHCI : frame period = 125us */ -#define IU_USB_MIN_RTT 500 /* This should be safe in most cases */ - -/* Inbound header */ -#define MEDIA_BUSY 0x80 - -#define SPEED_2400 0x01 -#define SPEED_9600 0x02 -#define SPEED_19200 0x03 -#define SPEED_38400 0x04 -#define SPEED_57600 0x05 -#define SPEED_115200 0x06 -#define SPEED_576000 0x07 -#define SPEED_1152000 0x08 -#define SPEED_4000000 0x09 -#define SPEED_16000000 0x0a - -/* Basic capabilities */ -#define IUC_DEFAULT 0x00 /* Basic device compliant with 1.0 spec */ -/* Main bugs */ -#define IUC_SPEED_BUG 0x01 /* Device doesn't set speed after the frame */ -#define IUC_NO_WINDOW 0x02 /* Device doesn't behave with big Rx window */ -#define IUC_NO_TURN 0x04 /* Device doesn't do turnaround by itself */ -/* Not currently used */ -#define IUC_SIR_ONLY 0x08 /* Device doesn't behave at FIR speeds */ -#define IUC_SMALL_PKT 0x10 /* Device doesn't behave with big Rx packets */ -#define IUC_MAX_WINDOW 0x20 /* Device underestimate the Rx window */ -#define IUC_MAX_XBOFS 0x40 /* Device need more xbofs than advertised */ -#define IUC_STIR421X 0x80 /* SigmaTel 4210/4220/4116 VFIR */ - -/* USB class definitions */ -#define USB_IRDA_HEADER 0x01 -#define USB_CLASS_IRDA 0x02 /* USB_CLASS_APP_SPEC subclass */ -#define USB_DT_IRDA 0x21 -#define USB_IRDA_STIR421X_HEADER 0x03 -#define IU_SIGMATEL_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + \ - USB_IRDA_STIR421X_HEADER) - -struct irda_class_desc { - __u8 bLength; - __u8 bDescriptorType; - __le16 bcdSpecRevision; - __u8 bmDataSize; - __u8 bmWindowSize; - __u8 bmMinTurnaroundTime; - __le16 wBaudRate; - __u8 bmAdditionalBOFs; - __u8 bIrdaRateSniff; - __u8 bMaxUnicastList; -} __packed; - -/* class specific interface request to get the IrDA-USB class descriptor - * (6.2.5, USB-IrDA class spec 1.0) */ - -#define IU_REQ_GET_CLASS_DESC 0x06 -#define STIR421X_MAX_PATCH_DOWNLOAD_SIZE 1023 - -struct irda_usb_cb { - struct irda_class_desc *irda_desc; - struct usb_device *usbdev; /* init: probe_irda */ - struct usb_interface *usbintf; /* init: probe_irda */ - int netopen; /* Device is active for network */ - int present; /* Device is present on the bus */ - __u32 capability; /* Capability of the hardware */ - __u8 bulk_in_ep; /* Rx Endpoint assignments */ - __u8 bulk_out_ep; /* Tx Endpoint assignments */ - __u16 bulk_out_mtu; /* Max Tx packet size in bytes */ - __u8 bulk_int_ep; /* Interrupt Endpoint assignments */ - - __u8 max_rx_urb; - struct urb **rx_urb; /* URBs used to receive data frames */ - struct urb *idle_rx_urb; /* Pointer to idle URB in Rx path */ - struct urb *tx_urb; /* URB used to send data frames */ - struct urb *speed_urb; /* URB used to send speed commands */ - - struct net_device *netdev; /* Yes! we are some kind of netdev. */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; - char *speed_buff; /* Buffer for speed changes */ - char *tx_buff; - - ktime_t stamp; - - spinlock_t lock; /* For serializing Tx operations */ - - __u16 xbofs; /* Current xbofs setting */ - __s16 new_xbofs; /* xbofs we need to set */ - __u32 speed; /* Current speed */ - __s32 new_speed; /* speed we need to set */ - - __u8 header_length; /* USB-IrDA frame header size */ - int needspatch; /* device needs firmware patch */ - - struct timer_list rx_defer_timer; /* Wait for Rx error to clear */ - struct urb *rx_defer_timer_urb; /* URB attached to rx_defer_timer */ -}; - diff --git a/drivers/staging/irda/drivers/irtty-sir.c b/drivers/staging/irda/drivers/irtty-sir.c deleted file mode 100644 index 7a20a9a4663a..000000000000 --- a/drivers/staging/irda/drivers/irtty-sir.c +++ /dev/null @@ -1,570 +0,0 @@ -/********************************************************************* - * - * Filename: irtty-sir.c - * Version: 2.0 - * Description: IrDA line discipline implementation - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Dec 9 21:18:38 1997 - * Modified at: Sun Oct 27 22:13:30 2002 - * Modified by: Martin Diehl - * Sources: slip.c by Laurence Culhane, - * Fred N. van Kempen, - * - * Copyright (c) 1998-2000 Dag Brattli, - * Copyright (c) 2002 Martin Diehl, - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "sir-dev.h" -#include "irtty-sir.h" - -static int qos_mtt_bits = 0x03; /* 5 ms or more */ - -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); - -/* ------------------------------------------------------- */ - -/* device configuration callbacks always invoked with irda-thread context */ - -/* find out, how many chars we have in buffers below us - * this is allowed to lie, i.e. return less chars than we - * actually have. The returned value is used to determine - * how long the irdathread should wait before doing the - * real blocking wait_until_sent() - */ - -static int irtty_chars_in_buffer(struct sir_dev *dev) -{ - struct sirtty_cb *priv = dev->priv; - - IRDA_ASSERT(priv != NULL, return -1;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); - - return tty_chars_in_buffer(priv->tty); -} - -/* Wait (sleep) until underlaying hardware finished transmission - * i.e. hardware buffers are drained - * this must block and not return before all characters are really sent - * - * If the tty sits on top of a 16550A-like uart, there are typically - * up to 16 bytes in the fifo - f.e. 9600 bps 8N1 needs 16.7 msec - * - * With usbserial the uart-fifo is basically replaced by the converter's - * outgoing endpoint buffer, which can usually hold 64 bytes (at least). - * With pl2303 it appears we are safe with 60msec here. - * - * I really wish all serial drivers would provide - * correct implementation of wait_until_sent() - */ - -#define USBSERIAL_TX_DONE_DELAY 60 - -static void irtty_wait_until_sent(struct sir_dev *dev) -{ - struct sirtty_cb *priv = dev->priv; - struct tty_struct *tty; - - IRDA_ASSERT(priv != NULL, return;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); - - tty = priv->tty; - if (tty->ops->wait_until_sent) { - tty->ops->wait_until_sent(tty, msecs_to_jiffies(100)); - } - else { - msleep(USBSERIAL_TX_DONE_DELAY); - } -} - -/* - * Function irtty_change_speed (dev, speed) - * - * Change the speed of the serial port. - * - * This may sleep in set_termios (usbserial driver f.e.) and must - * not be called from interrupt/timer/tasklet therefore. - * All such invocations are deferred to kIrDAd now so we can sleep there. - */ - -static int irtty_change_speed(struct sir_dev *dev, unsigned speed) -{ - struct sirtty_cb *priv = dev->priv; - struct tty_struct *tty; - struct ktermios old_termios; - int cflag; - - IRDA_ASSERT(priv != NULL, return -1;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); - - tty = priv->tty; - - down_write(&tty->termios_rwsem); - old_termios = tty->termios; - cflag = tty->termios.c_cflag; - tty_encode_baud_rate(tty, speed, speed); - if (tty->ops->set_termios) - tty->ops->set_termios(tty, &old_termios); - priv->io.speed = speed; - up_write(&tty->termios_rwsem); - - return 0; -} - -/* - * Function irtty_set_dtr_rts (dev, dtr, rts) - * - * This function can be used by dongles etc. to set or reset the status - * of the dtr and rts lines - */ - -static int irtty_set_dtr_rts(struct sir_dev *dev, int dtr, int rts) -{ - struct sirtty_cb *priv = dev->priv; - int set = 0; - int clear = 0; - - IRDA_ASSERT(priv != NULL, return -1;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); - - if (rts) - set |= TIOCM_RTS; - else - clear |= TIOCM_RTS; - if (dtr) - set |= TIOCM_DTR; - else - clear |= TIOCM_DTR; - - /* - * We can't use ioctl() because it expects a non-null file structure, - * and we don't have that here. - * This function is not yet defined for all tty driver, so - * let's be careful... Jean II - */ - IRDA_ASSERT(priv->tty->ops->tiocmset != NULL, return -1;); - priv->tty->ops->tiocmset(priv->tty, set, clear); - - return 0; -} - -/* ------------------------------------------------------- */ - -/* called from sir_dev when there is more data to send - * context is either netdev->hard_xmit or some transmit-completion bh - * i.e. we are under spinlock here and must not sleep. - */ - -static int irtty_do_write(struct sir_dev *dev, const unsigned char *ptr, size_t len) -{ - struct sirtty_cb *priv = dev->priv; - struct tty_struct *tty; - int writelen; - - IRDA_ASSERT(priv != NULL, return -1;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); - - tty = priv->tty; - if (!tty->ops->write) - return 0; - set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - writelen = tty_write_room(tty); - if (writelen > len) - writelen = len; - return tty->ops->write(tty, ptr, writelen); -} - -/* ------------------------------------------------------- */ - -/* irda line discipline callbacks */ - -/* - * Function irtty_receive_buf( tty, cp, count) - * - * Handle the 'receiver data ready' interrupt. This function is called - * by the 'tty_io' module in the kernel when a block of IrDA data has - * been received, which can now be decapsulated and delivered for - * further processing - * - * calling context depends on underlying driver and tty->port->low_latency! - * for example (low_latency: 1 / 0): - * serial.c: uart-interrupt / softint - * usbserial: urb-complete-interrupt / softint - */ - -static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - struct sir_dev *dev; - struct sirtty_cb *priv = tty->disc_data; - int i; - - IRDA_ASSERT(priv != NULL, return;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); - - if (unlikely(count==0)) /* yes, this happens */ - return; - - dev = priv->dev; - if (!dev) { - net_warn_ratelimited("%s(), not ready yet!\n", __func__); - return; - } - - for (i = 0; i < count; i++) { - /* - * Characters received with a parity error, etc? - */ - if (fp && *fp++) { - pr_debug("Framing or parity error!\n"); - sirdev_receive(dev, NULL, 0); /* notify sir_dev (updating stats) */ - return; - } - } - - sirdev_receive(dev, cp, count); -} - -/* - * Function irtty_write_wakeup (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 irtty_write_wakeup(struct tty_struct *tty) -{ - struct sirtty_cb *priv = tty->disc_data; - - IRDA_ASSERT(priv != NULL, return;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); - - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - if (priv->dev) - sirdev_write_complete(priv->dev); -} - -/* ------------------------------------------------------- */ - -/* - * Function irtty_stop_receiver (tty, stop) - * - */ - -static inline void irtty_stop_receiver(struct tty_struct *tty, int stop) -{ - struct ktermios old_termios; - int cflag; - - down_write(&tty->termios_rwsem); - old_termios = tty->termios; - cflag = tty->termios.c_cflag; - - if (stop) - cflag &= ~CREAD; - else - cflag |= CREAD; - - tty->termios.c_cflag = cflag; - if (tty->ops->set_termios) - tty->ops->set_termios(tty, &old_termios); - up_write(&tty->termios_rwsem); -} - -/*****************************************************************/ - -/* serialize ldisc open/close with sir_dev */ -static DEFINE_MUTEX(irtty_mutex); - -/* notifier from sir_dev when irda% device gets opened (ifup) */ - -static int irtty_start_dev(struct sir_dev *dev) -{ - struct sirtty_cb *priv; - struct tty_struct *tty; - - /* serialize with ldisc open/close */ - mutex_lock(&irtty_mutex); - - priv = dev->priv; - if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) { - mutex_unlock(&irtty_mutex); - return -ESTALE; - } - - tty = priv->tty; - - if (tty->ops->start) - tty->ops->start(tty); - /* Make sure we can receive more data */ - irtty_stop_receiver(tty, FALSE); - - mutex_unlock(&irtty_mutex); - return 0; -} - -/* notifier from sir_dev when irda% device gets closed (ifdown) */ - -static int irtty_stop_dev(struct sir_dev *dev) -{ - struct sirtty_cb *priv; - struct tty_struct *tty; - - /* serialize with ldisc open/close */ - mutex_lock(&irtty_mutex); - - priv = dev->priv; - if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) { - mutex_unlock(&irtty_mutex); - return -ESTALE; - } - - tty = priv->tty; - - /* Make sure we don't receive more data */ - irtty_stop_receiver(tty, TRUE); - if (tty->ops->stop) - tty->ops->stop(tty); - - mutex_unlock(&irtty_mutex); - - return 0; -} - -/* ------------------------------------------------------- */ - -static struct sir_driver sir_tty_drv = { - .owner = THIS_MODULE, - .driver_name = "sir_tty", - .start_dev = irtty_start_dev, - .stop_dev = irtty_stop_dev, - .do_write = irtty_do_write, - .chars_in_buffer = irtty_chars_in_buffer, - .wait_until_sent = irtty_wait_until_sent, - .set_speed = irtty_change_speed, - .set_dtr_rts = irtty_set_dtr_rts, -}; - -/* ------------------------------------------------------- */ - -/* - * Function irtty_ioctl (tty, file, cmd, arg) - * - * The Swiss army knife of system calls :-) - * - */ -static int irtty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct irtty_info { char name[6]; } info; - struct sir_dev *dev; - struct sirtty_cb *priv = tty->disc_data; - int err = 0; - - IRDA_ASSERT(priv != NULL, return -ENODEV;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EBADR;); - - pr_debug("%s(cmd=0x%X)\n", __func__, cmd); - - dev = priv->dev; - IRDA_ASSERT(dev != NULL, return -1;); - - switch (cmd) { - case IRTTY_IOCTDONGLE: - /* this call blocks for completion */ - err = sirdev_set_dongle(dev, (IRDA_DONGLE) arg); - break; - - case IRTTY_IOCGET: - IRDA_ASSERT(dev->netdev != NULL, return -1;); - - memset(&info, 0, sizeof(info)); - strncpy(info.name, dev->netdev->name, sizeof(info.name)-1); - - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - err = -EFAULT; - break; - default: - err = tty_mode_ioctl(tty, file, cmd, arg); - break; - } - return err; -} - - -/* - * Function irtty_open(tty) - * - * This function is called by the TTY module when the IrDA line - * discipline is called for. Because we are sure the tty line exists, - * we only have to link it to a free IrDA channel. - */ -static int irtty_open(struct tty_struct *tty) -{ - struct sir_dev *dev; - struct sirtty_cb *priv; - int ret = 0; - - /* Module stuff handled via irda_ldisc.owner - Jean II */ - - /* stop the underlying driver */ - irtty_stop_receiver(tty, TRUE); - if (tty->ops->stop) - tty->ops->stop(tty); - - tty_driver_flush_buffer(tty); - - /* apply mtt override */ - sir_tty_drv.qos_mtt_bits = qos_mtt_bits; - - /* get a sir device instance for this driver */ - dev = sirdev_get_instance(&sir_tty_drv, tty->name); - if (!dev) { - ret = -ENODEV; - goto out; - } - - /* allocate private device info block */ - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto out_put; - } - - priv->magic = IRTTY_MAGIC; - priv->tty = tty; - priv->dev = dev; - - /* serialize with start_dev - in case we were racing with ifup */ - mutex_lock(&irtty_mutex); - - dev->priv = priv; - tty->disc_data = priv; - tty->receive_room = 65536; - - mutex_unlock(&irtty_mutex); - - pr_debug("%s - %s: irda line discipline opened\n", __func__, tty->name); - - return 0; - -out_put: - sirdev_put_instance(dev); -out: - return ret; -} - -/* - * Function irtty_close (tty) - * - * Close down a IrDA channel. This means flushing out any pending queues, - * and then restoring the TTY line discipline to what it was before it got - * hooked to IrDA (which usually is TTY again). - */ -static void irtty_close(struct tty_struct *tty) -{ - struct sirtty_cb *priv = tty->disc_data; - - IRDA_ASSERT(priv != NULL, return;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); - - /* Hm, with a dongle attached the dongle driver wants - * to close the dongle - which requires the use of - * some tty write and/or termios or ioctl operations. - * Are we allowed to call those when already requested - * to shutdown the ldisc? - * If not, we should somehow mark the dev being staled. - * Question remains, how to close the dongle in this case... - * For now let's assume we are granted to issue tty driver calls - * until we return here from the ldisc close. I'm just wondering - * how this behaves with hotpluggable serial hardware like - * rs232-pcmcia card or usb-serial... - * - * priv->tty = NULL?; - */ - - /* we are dead now */ - tty->disc_data = NULL; - - sirdev_put_instance(priv->dev); - - /* Stop tty */ - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - if (tty->ops->stop) - tty->ops->stop(tty); - - kfree(priv); - - pr_debug("%s - %s: irda line discipline closed\n", __func__, tty->name); -} - -/* ------------------------------------------------------- */ - -static struct tty_ldisc_ops irda_ldisc = { - .magic = TTY_LDISC_MAGIC, - .name = "irda", - .flags = 0, - .open = irtty_open, - .close = irtty_close, - .read = NULL, - .write = NULL, - .ioctl = irtty_ioctl, - .poll = NULL, - .receive_buf = irtty_receive_buf, - .write_wakeup = irtty_write_wakeup, - .owner = THIS_MODULE, -}; - -/* ------------------------------------------------------- */ - -static int __init irtty_sir_init(void) -{ - int err; - - if ((err = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0) - net_err_ratelimited("IrDA: can't register line discipline (err = %d)\n", - err); - return err; -} - -static void __exit irtty_sir_cleanup(void) -{ - int err; - - if ((err = tty_unregister_ldisc(N_IRDA))) { - net_err_ratelimited("%s(), can't unregister line discipline (err = %d)\n", - __func__, err); - } -} - -module_init(irtty_sir_init); -module_exit(irtty_sir_cleanup); - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("IrDA TTY device driver"); -MODULE_ALIAS_LDISC(N_IRDA); -MODULE_LICENSE("GPL"); - diff --git a/drivers/staging/irda/drivers/irtty-sir.h b/drivers/staging/irda/drivers/irtty-sir.h deleted file mode 100644 index b132d8f6eb13..000000000000 --- a/drivers/staging/irda/drivers/irtty-sir.h +++ /dev/null @@ -1,34 +0,0 @@ -/********************************************************************* - * - * sir_tty.h: definitions for the irtty_sir client driver (former irtty) - * - * Copyright (c) 2002 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#ifndef IRTTYSIR_H -#define IRTTYSIR_H - -#include -#include // chipio_t - -#define IRTTY_IOC_MAGIC 'e' -#define IRTTY_IOCTDONGLE _IO(IRTTY_IOC_MAGIC, 1) -#define IRTTY_IOCGET _IOR(IRTTY_IOC_MAGIC, 2, struct irtty_info) -#define IRTTY_IOC_MAXNR 2 - -struct sirtty_cb { - magic_t magic; - - struct sir_dev *dev; - struct tty_struct *tty; - - chipio_t io; /* IrDA controller information */ -}; - -#endif diff --git a/drivers/staging/irda/drivers/kingsun-sir.c b/drivers/staging/irda/drivers/kingsun-sir.c deleted file mode 100644 index 4fd4ac2fe09f..000000000000 --- a/drivers/staging/irda/drivers/kingsun-sir.c +++ /dev/null @@ -1,634 +0,0 @@ -/***************************************************************************** -* -* Filename: kingsun-sir.c -* Version: 0.1.1 -* Description: Irda KingSun/DonShine USB Dongle -* Status: Experimental -* Author: Alex Villacís Lasso -* -* Based on stir4200 and mcs7780 drivers, with (strange?) differences -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - -/* - * This is my current (2007-04-25) understanding of how this dongle is supposed - * to work. This is based on reverse-engineering and examination of the packet - * data sent and received by the WinXP driver using USBSnoopy. Feel free to - * update here as more of this dongle is known: - * - * General: Unlike the other USB IrDA dongles, this particular dongle exposes, - * not two bulk (in and out) endpoints, but two *interrupt* ones. This dongle, - * like the bulk based ones (stir4200.c and mcs7780.c), requires polling in - * order to receive data. - * Transmission: Just like stir4200, this dongle uses a raw stream of data, - * which needs to be wrapped and escaped in a similar way as in stir4200.c. - * Reception: Poll-based, as in stir4200. Each read returns the contents of a - * 8-byte buffer, of which the first byte (LSB) indicates the number of bytes - * (1-7) of valid data contained within the remaining 7 bytes. For example, if - * the buffer had the following contents: - * 06 ff ff ff c0 01 04 aa - * This means that (06) there are 6 bytes of valid data. The byte 0xaa at the - * end is garbage (left over from a previous reception) and is discarded. - * If a read returns an "impossible" value as the length of valid data (such as - * 0x36) in the first byte, then the buffer is uninitialized (as is the case of - * first plug-in) and its contents should be discarded. There is currently no - * evidence that the top 5 bits of the 1st byte of the buffer can have values - * other than 0 once reception begins. - * Once valid bytes are collected, the assembled stream is a sequence of - * wrapped IrDA frames that is unwrapped and unescaped as in stir4200.c. - * BIG FAT WARNING: the dongle does *not* reset the RX buffer in any way after - * a successful read from the host, which means that in absence of further - * reception, repeated reads from the dongle will return the exact same - * contents repeatedly. Attempts to be smart and cache a previous read seem - * to result in corrupted packets, so this driver depends on the unwrap logic - * to sort out any repeated reads. - * Speed change: no commands observed so far to change speed, assumed fixed - * 9600bps (SIR). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -/* - * According to lsusb, 0x07c0 is assigned to - * "Code Mercenaries Hard- und Software GmbH" - */ -#define KING_VENDOR_ID 0x07c0 -#define KING_PRODUCT_ID 0x4200 - -/* These are the currently known USB ids */ -static const struct usb_device_id dongles[] = { - /* KingSun Co,Ltd IrDA/USB Bridge */ - { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) }, - { } -}; - -MODULE_DEVICE_TABLE(usb, dongles); - -#define KINGSUN_MTT 0x07 - -#define KINGSUN_FIFO_SIZE 4096 -#define KINGSUN_EP_IN 0 -#define KINGSUN_EP_OUT 1 - -struct kingsun_cb { - struct usb_device *usbdev; /* init: probe_irda */ - struct net_device *netdev; /* network layer */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - - struct qos_info qos; - - __u8 *in_buf; /* receive buffer */ - __u8 *out_buf; /* transmit buffer */ - __u8 max_rx; /* max. atomic read from dongle - (usually 8), also size of in_buf */ - __u8 max_tx; /* max. atomic write to dongle - (usually 8) */ - - iobuff_t rx_buff; /* receive unwrap state machine */ - spinlock_t lock; - int receiving; - - __u8 ep_in; - __u8 ep_out; - - struct urb *tx_urb; - struct urb *rx_urb; -}; - -/* Callback transmission routine */ -static void kingsun_send_irq(struct urb *urb) -{ - struct kingsun_cb *kingsun = urb->context; - struct net_device *netdev = kingsun->netdev; - - /* in process of stopping, just drop data */ - if (!netif_running(kingsun->netdev)) { - dev_err(&kingsun->usbdev->dev, - "kingsun_send_irq: Network not running!\n"); - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "kingsun_send_irq: urb asynchronously failed - %d\n", - urb->status); - } - netif_wake_queue(netdev); -} - -/* - * Called from net/core when new frame is available. - */ -static netdev_tx_t kingsun_hard_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct kingsun_cb *kingsun; - int wraplen; - int ret = 0; - - netif_stop_queue(netdev); - - /* the IRDA wrapping routines don't deal with non linear skb */ - SKB_LINEAR_ASSERT(skb); - - kingsun = netdev_priv(netdev); - - spin_lock(&kingsun->lock); - - /* Append data to the end of whatever data remains to be transmitted */ - wraplen = async_wrap_skb(skb, - kingsun->out_buf, - KINGSUN_FIFO_SIZE); - - /* Calculate how much data can be transmitted in this urb */ - usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev, - usb_sndintpipe(kingsun->usbdev, kingsun->ep_out), - kingsun->out_buf, wraplen, kingsun_send_irq, - kingsun, 1); - - if ((ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC))) { - dev_err(&kingsun->usbdev->dev, - "kingsun_hard_xmit: failed tx_urb submit: %d\n", ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - netdev->stats.tx_errors++; - netif_start_queue(netdev); - } - } else { - netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; - } - - dev_kfree_skb(skb); - spin_unlock(&kingsun->lock); - - return NETDEV_TX_OK; -} - -/* Receive callback function */ -static void kingsun_rcv_irq(struct urb *urb) -{ - struct kingsun_cb *kingsun = urb->context; - int ret; - - /* in process of stopping, just drop data */ - if (!netif_running(kingsun->netdev)) { - kingsun->receiving = 0; - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "kingsun_rcv_irq: urb asynchronously failed - %d\n", - urb->status); - kingsun->receiving = 0; - return; - } - - if (urb->actual_length == kingsun->max_rx) { - __u8 *bytes = urb->transfer_buffer; - int i; - - /* The very first byte in the buffer indicates the length of - valid data in the read. This byte must be in the range - 1..kingsun->max_rx -1 . Values outside this range indicate - an uninitialized Rx buffer when the dongle has just been - plugged in. */ - if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) { - for (i = 1; i <= bytes[0]; i++) { - async_unwrap_char(kingsun->netdev, - &kingsun->netdev->stats, - &kingsun->rx_buff, bytes[i]); - } - kingsun->receiving = - (kingsun->rx_buff.state != OUTSIDE_FRAME) - ? 1 : 0; - } - } else if (urb->actual_length > 0) { - dev_err(&kingsun->usbdev->dev, - "%s(): Unexpected response length, expected %d got %d\n", - __func__, kingsun->max_rx, urb->actual_length); - } - /* This urb has already been filled in kingsun_net_open */ - ret = usb_submit_urb(urb, GFP_ATOMIC); -} - -/* - * Function kingsun_net_open (dev) - * - * Network device is taken up. Usually this is done by "ifconfig irda0 up" - */ -static int kingsun_net_open(struct net_device *netdev) -{ - struct kingsun_cb *kingsun = netdev_priv(netdev); - int err = -ENOMEM; - char hwname[16]; - - /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */ - kingsun->receiving = 0; - - /* Initialize for SIR to copy data directly into skb. */ - kingsun->rx_buff.in_frame = FALSE; - kingsun->rx_buff.state = OUTSIDE_FRAME; - kingsun->rx_buff.truesize = IRDA_SKB_MAX_MTU; - kingsun->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!kingsun->rx_buff.skb) - goto free_mem; - - skb_reserve(kingsun->rx_buff.skb, 1); - kingsun->rx_buff.head = kingsun->rx_buff.skb->data; - - kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->rx_urb) - goto free_mem; - - kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->tx_urb) - goto free_mem; - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - */ - sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); - kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); - if (!kingsun->irlap) { - dev_err(&kingsun->usbdev->dev, "irlap_open failed\n"); - goto free_mem; - } - - /* Start first reception */ - usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev, - usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in), - kingsun->in_buf, kingsun->max_rx, - kingsun_rcv_irq, kingsun, 1); - kingsun->rx_urb->status = 0; - err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - if (err) { - dev_err(&kingsun->usbdev->dev, - "first urb-submit failed: %d\n", err); - goto close_irlap; - } - - netif_start_queue(netdev); - - /* Situation at this point: - - all work buffers allocated - - urbs allocated and ready to fill - - max rx packet known (in max_rx) - - unwrap state machine initialized, in state outside of any frame - - receive request in progress - - IrLAP layer started, about to hand over packets to send - */ - - return 0; - - close_irlap: - irlap_close(kingsun->irlap); - free_mem: - if (kingsun->tx_urb) { - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - } - if (kingsun->rx_urb) { - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - } - if (kingsun->rx_buff.skb) { - kfree_skb(kingsun->rx_buff.skb); - kingsun->rx_buff.skb = NULL; - kingsun->rx_buff.head = NULL; - } - return err; -} - -/* - * Function kingsun_net_close (kingsun) - * - * Network device is taken down. Usually this is done by - * "ifconfig irda0 down" - */ -static int kingsun_net_close(struct net_device *netdev) -{ - struct kingsun_cb *kingsun = netdev_priv(netdev); - - /* Stop transmit processing */ - netif_stop_queue(netdev); - - /* Mop up receive && transmit urb's */ - usb_kill_urb(kingsun->tx_urb); - usb_kill_urb(kingsun->rx_urb); - - usb_free_urb(kingsun->tx_urb); - usb_free_urb(kingsun->rx_urb); - - kingsun->tx_urb = NULL; - kingsun->rx_urb = NULL; - - kfree_skb(kingsun->rx_buff.skb); - kingsun->rx_buff.skb = NULL; - kingsun->rx_buff.head = NULL; - kingsun->rx_buff.in_frame = FALSE; - kingsun->rx_buff.state = OUTSIDE_FRAME; - kingsun->receiving = 0; - - /* Stop and remove instance of IrLAP */ - if (kingsun->irlap) - irlap_close(kingsun->irlap); - - kingsun->irlap = NULL; - - return 0; -} - -/* - * IOCTLs : Extra out-of-band network commands... - */ -static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq, - int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct kingsun_cb *kingsun = netdev_priv(netdev); - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the device is still there */ - if (netif_device_present(kingsun->netdev)) - /* No observed commands for speed change */ - ret = -EOPNOTSUPP; - break; - - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the IrDA stack is still there */ - if (netif_running(kingsun->netdev)) - irda_device_set_media_busy(kingsun->netdev, TRUE); - break; - - case SIOCGRECEIVING: - /* Only approximately true */ - irq->ifr_receiving = kingsun->receiving; - break; - - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -static const struct net_device_ops kingsun_ops = { - .ndo_start_xmit = kingsun_hard_xmit, - .ndo_open = kingsun_net_open, - .ndo_stop = kingsun_net_close, - .ndo_do_ioctl = kingsun_net_ioctl, -}; - -/* - * This routine is called by the USB subsystem for each new device - * in the system. We need to check if the device is ours, and in - * this case start handling it. - */ -static int kingsun_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - - struct usb_device *dev = interface_to_usbdev(intf); - struct kingsun_cb *kingsun = NULL; - struct net_device *net = NULL; - int ret = -ENOMEM; - int pipe, maxp_in, maxp_out; - __u8 ep_in; - __u8 ep_out; - - /* Check that there really are two interrupt endpoints. - Check based on the one in drivers/usb/input/usbmouse.c - */ - interface = intf->cur_altsetting; - if (interface->desc.bNumEndpoints != 2) { - dev_err(&intf->dev, - "kingsun-sir: expected 2 endpoints, found %d\n", - interface->desc.bNumEndpoints); - return -ENODEV; - } - endpoint = &interface->endpoint[KINGSUN_EP_IN].desc; - if (!usb_endpoint_is_int_in(endpoint)) { - dev_err(&intf->dev, - "kingsun-sir: endpoint 0 is not interrupt IN\n"); - return -ENODEV; - } - - ep_in = endpoint->bEndpointAddress; - pipe = usb_rcvintpipe(dev, ep_in); - maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - if (maxp_in > 255 || maxp_in <= 1) { - dev_err(&intf->dev, - "endpoint 0 has max packet size %d not in range\n", - maxp_in); - return -ENODEV; - } - - endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc; - if (!usb_endpoint_is_int_out(endpoint)) { - dev_err(&intf->dev, - "kingsun-sir: endpoint 1 is not interrupt OUT\n"); - return -ENODEV; - } - - ep_out = endpoint->bEndpointAddress; - pipe = usb_sndintpipe(dev, ep_out); - maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - /* Allocate network device container. */ - net = alloc_irdadev(sizeof(*kingsun)); - if(!net) - goto err_out1; - - SET_NETDEV_DEV(net, &intf->dev); - kingsun = netdev_priv(net); - kingsun->irlap = NULL; - kingsun->tx_urb = NULL; - kingsun->rx_urb = NULL; - kingsun->ep_in = ep_in; - kingsun->ep_out = ep_out; - kingsun->in_buf = NULL; - kingsun->out_buf = NULL; - kingsun->max_rx = (__u8)maxp_in; - kingsun->max_tx = (__u8)maxp_out; - kingsun->netdev = net; - kingsun->usbdev = dev; - kingsun->rx_buff.in_frame = FALSE; - kingsun->rx_buff.state = OUTSIDE_FRAME; - kingsun->rx_buff.skb = NULL; - kingsun->receiving = 0; - spin_lock_init(&kingsun->lock); - - /* Allocate input buffer */ - kingsun->in_buf = kmalloc(kingsun->max_rx, GFP_KERNEL); - if (!kingsun->in_buf) - goto free_mem; - - /* Allocate output buffer */ - kingsun->out_buf = kmalloc(KINGSUN_FIFO_SIZE, GFP_KERNEL); - if (!kingsun->out_buf) - goto free_mem; - - printk(KERN_INFO "KingSun/DonShine IRDA/USB found at address %d, " - "Vendor: %x, Product: %x\n", - dev->devnum, le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&kingsun->qos); - - /* That's the Rx capability. */ - kingsun->qos.baud_rate.bits &= IR_9600; - kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; - irda_qos_bits_to_value(&kingsun->qos); - - /* Override the network functions we need to use */ - net->netdev_ops = &kingsun_ops; - - ret = register_netdev(net); - if (ret != 0) - goto free_mem; - - dev_info(&net->dev, "IrDA: Registered KingSun/DonShine device %s\n", - net->name); - - usb_set_intfdata(intf, kingsun); - - /* Situation at this point: - - all work buffers allocated - - urbs not allocated, set to NULL - - max rx packet known (in max_rx) - - unwrap state machine (partially) initialized, but skb == NULL - */ - - return 0; - -free_mem: - kfree(kingsun->out_buf); - kfree(kingsun->in_buf); - free_netdev(net); -err_out1: - return ret; -} - -/* - * The current device is removed, the USB layer tell us to shut it down... - */ -static void kingsun_disconnect(struct usb_interface *intf) -{ - struct kingsun_cb *kingsun = usb_get_intfdata(intf); - - if (!kingsun) - return; - - unregister_netdev(kingsun->netdev); - - /* Mop up receive && transmit urb's */ - if (kingsun->tx_urb != NULL) { - usb_kill_urb(kingsun->tx_urb); - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - } - if (kingsun->rx_urb != NULL) { - usb_kill_urb(kingsun->rx_urb); - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - } - - kfree(kingsun->out_buf); - kfree(kingsun->in_buf); - free_netdev(kingsun->netdev); - - usb_set_intfdata(intf, NULL); -} - -#ifdef CONFIG_PM -/* USB suspend, so power off the transmitter/receiver */ -static int kingsun_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct kingsun_cb *kingsun = usb_get_intfdata(intf); - - netif_device_detach(kingsun->netdev); - if (kingsun->tx_urb != NULL) usb_kill_urb(kingsun->tx_urb); - if (kingsun->rx_urb != NULL) usb_kill_urb(kingsun->rx_urb); - return 0; -} - -/* Coming out of suspend, so reset hardware */ -static int kingsun_resume(struct usb_interface *intf) -{ - struct kingsun_cb *kingsun = usb_get_intfdata(intf); - - if (kingsun->rx_urb != NULL) - usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - netif_device_attach(kingsun->netdev); - - return 0; -} -#endif - -/* - * USB device callbacks - */ -static struct usb_driver irda_driver = { - .name = "kingsun-sir", - .probe = kingsun_probe, - .disconnect = kingsun_disconnect, - .id_table = dongles, -#ifdef CONFIG_PM - .suspend = kingsun_suspend, - .resume = kingsun_resume, -#endif -}; - -module_usb_driver(irda_driver); - -MODULE_AUTHOR("Alex Villacís Lasso "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun/DonShine"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/ks959-sir.c b/drivers/staging/irda/drivers/ks959-sir.c deleted file mode 100644 index 8025741e7586..000000000000 --- a/drivers/staging/irda/drivers/ks959-sir.c +++ /dev/null @@ -1,912 +0,0 @@ -/***************************************************************************** -* -* Filename: ks959-sir.c -* Version: 0.1.2 -* Description: Irda KingSun KS-959 USB Dongle -* Status: Experimental -* Author: Alex Villacís Lasso -* with help from Domen Puncer -* -* Based on stir4200, mcs7780, kingsun-sir drivers. -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - -/* - * Following is my most current (2007-07-17) understanding of how the Kingsun - * KS-959 dongle is supposed to work. This information was deduced by - * reverse-engineering and examining the USB traffic captured with USBSnoopy - * from the WinXP driver. Feel free to update here as more of the dongle is - * known. - * - * My most sincere thanks must go to Domen Puncer for - * invaluable help in cracking the obfuscation and padding required for this - * dongle. - * - * General: This dongle exposes one interface with one interrupt IN endpoint. - * However, the interrupt endpoint is NOT used at all for this dongle. Instead, - * this dongle uses control transfers for everything, including sending and - * receiving the IrDA frame data. Apparently the interrupt endpoint is just a - * dummy to ensure the dongle has a valid interface to present to the PC.And I - * thought the DonShine dongle was weird... In addition, this dongle uses - * obfuscation (?!?!), applied at the USB level, to hide the traffic, both sent - * and received, from the dongle. I call it obfuscation because the XOR keying - * and padding required to produce an USB traffic acceptable for the dongle can - * not be explained by any other technical requirement. - * - * Transmission: To transmit an IrDA frame, the driver must prepare a control - * URB with the following as a setup packet: - * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - * bRequest 0x09 - * wValue - * wIndex 0x0000 - * wLength - * The payload packet must be manually wrapped and escaped (as in stir4200.c), - * then padded and obfuscated before being sent. Both padding and obfuscation - * are implemented in the procedure obfuscate_tx_buffer(). Suffice to say, the - * designer/programmer of the dongle used his name as a source for the - * obfuscation. WTF?! - * Apparently the dongle cannot handle payloads larger than 256 bytes. The - * driver has to perform fragmentation in order to send anything larger than - * this limit. - * - * Reception: To receive data, the driver must poll the dongle regularly (like - * kingsun-sir.c) with control URBs and the following as a setup packet: - * bRequestType USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE - * bRequest 0x01 - * wValue 0x0200 - * wIndex 0x0000 - * wLength 0x0800 (size of available buffer) - * If there is data to be read, it will be returned as the response payload. - * This data is (apparently) not padded, but it is obfuscated. To de-obfuscate - * it, the driver must XOR every byte, in sequence, with a value that starts at - * 1 and is incremented with each byte processed, and then with 0x55. The value - * incremented with each byte processed overflows as an unsigned char. The - * resulting bytes form a wrapped SIR frame that is unwrapped and unescaped - * as in stir4200.c The incremented value is NOT reset with each frame, but is - * kept across the entire session with the dongle. Also, the dongle inserts an - * extra garbage byte with value 0x95 (after decoding) every 0xff bytes, which - * must be skipped. - * - * Speed change: To change the speed of the dongle, the driver prepares a - * control URB with the following as a setup packet: - * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - * bRequest 0x09 - * wValue 0x0200 - * wIndex 0x0001 - * wLength 0x0008 (length of the payload) - * The payload is a 8-byte record, apparently identical to the one used in - * drivers/usb/serial/cypress_m8.c to change speed: - * __u32 baudSpeed; - * unsigned int dataBits : 2; // 0 - 5 bits 3 - 8 bits - * unsigned int : 1; - * unsigned int stopBits : 1; - * unsigned int parityEnable : 1; - * unsigned int parityType : 1; - * unsigned int : 1; - * unsigned int reset : 1; - * unsigned char reserved[3]; // set to 0 - * - * For now only SIR speeds have been observed with this dongle. Therefore, - * nothing is known on what changes (if any) must be done to frame wrapping / - * unwrapping for higher than SIR speeds. This driver assumes no change is - * necessary and announces support for all the way to 57600 bps. Although the - * package announces support for up to 4MBps, tests with a Sony Ericcson K300 - * phone show corruption when receiving large frames at 115200 bps, the highest - * speed announced by the phone. However, transmission at 115200 bps is OK. Go - * figure. Since I don't know whether the phone or the dongle is at fault, max - * announced speed is 57600 bps until someone produces a device that can run - * at higher speeds with this dongle. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#define KS959_VENDOR_ID 0x07d0 -#define KS959_PRODUCT_ID 0x4959 - -/* These are the currently known USB ids */ -static const struct usb_device_id dongles[] = { - /* KingSun Co,Ltd IrDA/USB Bridge */ - {USB_DEVICE(KS959_VENDOR_ID, KS959_PRODUCT_ID)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, dongles); - -#define KINGSUN_MTT 0x07 -#define KINGSUN_REQ_RECV 0x01 -#define KINGSUN_REQ_SEND 0x09 - -#define KINGSUN_RCV_FIFO_SIZE 2048 /* Max length we can receive */ -#define KINGSUN_SND_FIFO_SIZE 2048 /* Max packet we can send */ -#define KINGSUN_SND_PACKET_SIZE 256 /* Max packet dongle can handle */ - -struct ks959_speedparams { - __le32 baudrate; /* baud rate, little endian */ - __u8 flags; - __u8 reserved[3]; -} __packed; - -#define KS_DATA_5_BITS 0x00 -#define KS_DATA_6_BITS 0x01 -#define KS_DATA_7_BITS 0x02 -#define KS_DATA_8_BITS 0x03 - -#define KS_STOP_BITS_1 0x00 -#define KS_STOP_BITS_2 0x08 - -#define KS_PAR_DISABLE 0x00 -#define KS_PAR_EVEN 0x10 -#define KS_PAR_ODD 0x30 -#define KS_RESET 0x80 - -struct ks959_cb { - struct usb_device *usbdev; /* init: probe_irda */ - struct net_device *netdev; /* network layer */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - - struct qos_info qos; - - struct usb_ctrlrequest *tx_setuprequest; - struct urb *tx_urb; - __u8 *tx_buf_clear; - unsigned int tx_buf_clear_used; - unsigned int tx_buf_clear_sent; - __u8 *tx_buf_xored; - - struct usb_ctrlrequest *rx_setuprequest; - struct urb *rx_urb; - __u8 *rx_buf; - __u8 rx_variable_xormask; - iobuff_t rx_unwrap_buff; - - struct usb_ctrlrequest *speed_setuprequest; - struct urb *speed_urb; - struct ks959_speedparams speedparams; - unsigned int new_speed; - - spinlock_t lock; - int receiving; -}; - -/* Procedure to perform the obfuscation/padding expected by the dongle - * - * buf_cleartext (IN) Cleartext version of the IrDA frame to transmit - * len_cleartext (IN) Length of the cleartext version of IrDA frame - * buf_xoredtext (OUT) Obfuscated version of frame built by proc - * len_maxbuf (OUT) Maximum space available at buf_xoredtext - * - * (return) length of obfuscated frame with padding - * - * If not enough space (as indicated by len_maxbuf vs. required padding), - * zero is returned - * - * The value of lookup_string is actually a required portion of the algorithm. - * Seems the designer of the dongle wanted to state who exactly is responsible - * for implementing obfuscation. Send your best (or other) wishes to him ]:-) - */ -static unsigned int obfuscate_tx_buffer(const __u8 * buf_cleartext, - unsigned int len_cleartext, - __u8 * buf_xoredtext, - unsigned int len_maxbuf) -{ - unsigned int len_xoredtext; - - /* Calculate required length with padding, check for necessary space */ - len_xoredtext = ((len_cleartext + 7) & ~0x7) + 0x10; - if (len_xoredtext <= len_maxbuf) { - static const __u8 lookup_string[] = "wangshuofei19710"; - __u8 xor_mask; - - /* Unlike the WinXP driver, we *do* clear out the padding */ - memset(buf_xoredtext, 0, len_xoredtext); - - xor_mask = lookup_string[(len_cleartext & 0x0f) ^ 0x06] ^ 0x55; - - while (len_cleartext-- > 0) { - *buf_xoredtext++ = *buf_cleartext++ ^ xor_mask; - } - } else { - len_xoredtext = 0; - } - return len_xoredtext; -} - -/* Callback transmission routine */ -static void ks959_speed_irq(struct urb *urb) -{ - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&urb->dev->dev, - "ks959_speed_irq: urb asynchronously failed - %d\n", - urb->status); - } -} - -/* Send a control request to change speed of the dongle */ -static int ks959_change_speed(struct ks959_cb *kingsun, unsigned speed) -{ - static unsigned int supported_speeds[] = { 2400, 9600, 19200, 38400, - 57600, 115200, 576000, 1152000, 4000000, 0 - }; - int err; - unsigned int i; - - if (kingsun->speed_setuprequest == NULL || kingsun->speed_urb == NULL) - return -ENOMEM; - - /* Check that requested speed is among the supported ones */ - for (i = 0; supported_speeds[i] && supported_speeds[i] != speed; i++) ; - if (supported_speeds[i] == 0) - return -EOPNOTSUPP; - - memset(&(kingsun->speedparams), 0, sizeof(struct ks959_speedparams)); - kingsun->speedparams.baudrate = cpu_to_le32(speed); - kingsun->speedparams.flags = KS_DATA_8_BITS; - - /* speed_setuprequest pre-filled in ks959_probe */ - usb_fill_control_urb(kingsun->speed_urb, kingsun->usbdev, - usb_sndctrlpipe(kingsun->usbdev, 0), - (unsigned char *)kingsun->speed_setuprequest, - &(kingsun->speedparams), - sizeof(struct ks959_speedparams), ks959_speed_irq, - kingsun); - kingsun->speed_urb->status = 0; - err = usb_submit_urb(kingsun->speed_urb, GFP_ATOMIC); - - return err; -} - -/* Submit one fragment of an IrDA frame to the dongle */ -static void ks959_send_irq(struct urb *urb); -static int ks959_submit_tx_fragment(struct ks959_cb *kingsun) -{ - unsigned int padlen; - unsigned int wraplen; - int ret; - - /* Check whether current plaintext can produce a padded buffer that fits - within the range handled by the dongle */ - wraplen = (KINGSUN_SND_PACKET_SIZE & ~0x7) - 0x10; - if (wraplen > kingsun->tx_buf_clear_used) - wraplen = kingsun->tx_buf_clear_used; - - /* Perform dongle obfuscation. Also remove the portion of the frame that - was just obfuscated and will now be sent to the dongle. */ - padlen = obfuscate_tx_buffer(kingsun->tx_buf_clear, wraplen, - kingsun->tx_buf_xored, - KINGSUN_SND_PACKET_SIZE); - - /* Calculate how much data can be transmitted in this urb */ - kingsun->tx_setuprequest->wValue = cpu_to_le16(wraplen); - kingsun->tx_setuprequest->wLength = cpu_to_le16(padlen); - /* Rest of the fields were filled in ks959_probe */ - usb_fill_control_urb(kingsun->tx_urb, kingsun->usbdev, - usb_sndctrlpipe(kingsun->usbdev, 0), - (unsigned char *)kingsun->tx_setuprequest, - kingsun->tx_buf_xored, padlen, - ks959_send_irq, kingsun); - kingsun->tx_urb->status = 0; - ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC); - - /* Remember how much data was sent, in order to update at callback */ - kingsun->tx_buf_clear_sent = (ret == 0) ? wraplen : 0; - return ret; -} - -/* Callback transmission routine */ -static void ks959_send_irq(struct urb *urb) -{ - struct ks959_cb *kingsun = urb->context; - struct net_device *netdev = kingsun->netdev; - int ret = 0; - - /* in process of stopping, just drop data */ - if (!netif_running(kingsun->netdev)) { - dev_err(&kingsun->usbdev->dev, - "ks959_send_irq: Network not running!\n"); - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "ks959_send_irq: urb asynchronously failed - %d\n", - urb->status); - return; - } - - if (kingsun->tx_buf_clear_used > 0) { - /* Update data remaining to be sent */ - if (kingsun->tx_buf_clear_sent < kingsun->tx_buf_clear_used) { - memmove(kingsun->tx_buf_clear, - kingsun->tx_buf_clear + - kingsun->tx_buf_clear_sent, - kingsun->tx_buf_clear_used - - kingsun->tx_buf_clear_sent); - } - kingsun->tx_buf_clear_used -= kingsun->tx_buf_clear_sent; - kingsun->tx_buf_clear_sent = 0; - - if (kingsun->tx_buf_clear_used > 0) { - /* There is more data to be sent */ - if ((ret = ks959_submit_tx_fragment(kingsun)) != 0) { - dev_err(&kingsun->usbdev->dev, - "ks959_send_irq: failed tx_urb submit: %d\n", - ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - netdev->stats.tx_errors++; - netif_start_queue(netdev); - } - } - } else { - /* All data sent, send next speed && wake network queue */ - if (kingsun->new_speed != -1 && - cpu_to_le32(kingsun->new_speed) != - kingsun->speedparams.baudrate) - ks959_change_speed(kingsun, kingsun->new_speed); - - netif_wake_queue(netdev); - } - } -} - -/* - * Called from net/core when new frame is available. - */ -static netdev_tx_t ks959_hard_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct ks959_cb *kingsun; - unsigned int wraplen; - int ret = 0; - - netif_stop_queue(netdev); - - /* the IRDA wrapping routines don't deal with non linear skb */ - SKB_LINEAR_ASSERT(skb); - - kingsun = netdev_priv(netdev); - - spin_lock(&kingsun->lock); - kingsun->new_speed = irda_get_next_speed(skb); - - /* Append data to the end of whatever data remains to be transmitted */ - wraplen = - async_wrap_skb(skb, kingsun->tx_buf_clear, KINGSUN_SND_FIFO_SIZE); - kingsun->tx_buf_clear_used = wraplen; - - if ((ret = ks959_submit_tx_fragment(kingsun)) != 0) { - dev_err(&kingsun->usbdev->dev, - "ks959_hard_xmit: failed tx_urb submit: %d\n", ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - netdev->stats.tx_errors++; - netif_start_queue(netdev); - } - } else { - netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; - - } - - dev_kfree_skb(skb); - spin_unlock(&kingsun->lock); - - return NETDEV_TX_OK; -} - -/* Receive callback function */ -static void ks959_rcv_irq(struct urb *urb) -{ - struct ks959_cb *kingsun = urb->context; - int ret; - - /* in process of stopping, just drop data */ - if (!netif_running(kingsun->netdev)) { - kingsun->receiving = 0; - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "kingsun_rcv_irq: urb asynchronously failed - %d\n", - urb->status); - kingsun->receiving = 0; - return; - } - - if (urb->actual_length > 0) { - __u8 *bytes = urb->transfer_buffer; - unsigned int i; - - for (i = 0; i < urb->actual_length; i++) { - /* De-obfuscation implemented here: variable portion of - xormask is incremented, and then used with the encoded - byte for the XOR. The result of the operation is used - to unwrap the SIR frame. */ - kingsun->rx_variable_xormask++; - bytes[i] = - bytes[i] ^ kingsun->rx_variable_xormask ^ 0x55u; - - /* rx_variable_xormask doubles as an index counter so we - can skip the byte at 0xff (wrapped around to 0). - */ - if (kingsun->rx_variable_xormask != 0) { - async_unwrap_char(kingsun->netdev, - &kingsun->netdev->stats, - &kingsun->rx_unwrap_buff, - bytes[i]); - } - } - kingsun->receiving = - (kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0; - } - - /* This urb has already been filled in kingsun_net_open. Setup - packet must be re-filled, but it is assumed that urb keeps the - pointer to the initial setup packet, as well as the payload buffer. - Setup packet is already pre-filled at ks959_probe. - */ - urb->status = 0; - ret = usb_submit_urb(urb, GFP_ATOMIC); -} - -/* - * Function kingsun_net_open (dev) - * - * Network device is taken up. Usually this is done by "ifconfig irda0 up" - */ -static int ks959_net_open(struct net_device *netdev) -{ - struct ks959_cb *kingsun = netdev_priv(netdev); - int err = -ENOMEM; - char hwname[16]; - - /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */ - kingsun->receiving = 0; - - /* Initialize for SIR to copy data directly into skb. */ - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->rx_unwrap_buff.truesize = IRDA_SKB_MAX_MTU; - kingsun->rx_unwrap_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!kingsun->rx_unwrap_buff.skb) - goto free_mem; - - skb_reserve(kingsun->rx_unwrap_buff.skb, 1); - kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data; - - kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->rx_urb) - goto free_mem; - - kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->tx_urb) - goto free_mem; - - kingsun->speed_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->speed_urb) - goto free_mem; - - /* Initialize speed for dongle */ - kingsun->new_speed = 9600; - err = ks959_change_speed(kingsun, 9600); - if (err < 0) - goto free_mem; - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - */ - sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); - kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); - if (!kingsun->irlap) { - err = -ENOMEM; - dev_err(&kingsun->usbdev->dev, "irlap_open failed\n"); - goto free_mem; - } - - /* Start reception. Setup request already pre-filled in ks959_probe */ - usb_fill_control_urb(kingsun->rx_urb, kingsun->usbdev, - usb_rcvctrlpipe(kingsun->usbdev, 0), - (unsigned char *)kingsun->rx_setuprequest, - kingsun->rx_buf, KINGSUN_RCV_FIFO_SIZE, - ks959_rcv_irq, kingsun); - kingsun->rx_urb->status = 0; - err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - if (err) { - dev_err(&kingsun->usbdev->dev, - "first urb-submit failed: %d\n", err); - goto close_irlap; - } - - netif_start_queue(netdev); - - /* Situation at this point: - - all work buffers allocated - - urbs allocated and ready to fill - - max rx packet known (in max_rx) - - unwrap state machine initialized, in state outside of any frame - - receive request in progress - - IrLAP layer started, about to hand over packets to send - */ - - return 0; - - close_irlap: - irlap_close(kingsun->irlap); - free_mem: - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - if (kingsun->rx_unwrap_buff.skb) { - kfree_skb(kingsun->rx_unwrap_buff.skb); - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->rx_unwrap_buff.head = NULL; - } - return err; -} - -/* - * Function kingsun_net_close (kingsun) - * - * Network device is taken down. Usually this is done by - * "ifconfig irda0 down" - */ -static int ks959_net_close(struct net_device *netdev) -{ - struct ks959_cb *kingsun = netdev_priv(netdev); - - /* Stop transmit processing */ - netif_stop_queue(netdev); - - /* Mop up receive && transmit urb's */ - usb_kill_urb(kingsun->tx_urb); - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - - usb_kill_urb(kingsun->speed_urb); - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - - usb_kill_urb(kingsun->rx_urb); - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - - kfree_skb(kingsun->rx_unwrap_buff.skb); - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->rx_unwrap_buff.head = NULL; - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->receiving = 0; - - /* Stop and remove instance of IrLAP */ - if (kingsun->irlap) - irlap_close(kingsun->irlap); - - kingsun->irlap = NULL; - - return 0; -} - -/* - * IOCTLs : Extra out-of-band network commands... - */ -static int ks959_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *)rq; - struct ks959_cb *kingsun = netdev_priv(netdev); - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the device is still there */ - if (netif_device_present(kingsun->netdev)) - return ks959_change_speed(kingsun, irq->ifr_baudrate); - break; - - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the IrDA stack is still there */ - if (netif_running(kingsun->netdev)) - irda_device_set_media_busy(kingsun->netdev, TRUE); - break; - - case SIOCGRECEIVING: - /* Only approximately true */ - irq->ifr_receiving = kingsun->receiving; - break; - - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -static const struct net_device_ops ks959_ops = { - .ndo_start_xmit = ks959_hard_xmit, - .ndo_open = ks959_net_open, - .ndo_stop = ks959_net_close, - .ndo_do_ioctl = ks959_net_ioctl, -}; -/* - * This routine is called by the USB subsystem for each new device - * in the system. We need to check if the device is ours, and in - * this case start handling it. - */ -static int ks959_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct ks959_cb *kingsun = NULL; - struct net_device *net = NULL; - int ret = -ENOMEM; - - /* Allocate network device container. */ - net = alloc_irdadev(sizeof(*kingsun)); - if (!net) - goto err_out1; - - SET_NETDEV_DEV(net, &intf->dev); - kingsun = netdev_priv(net); - kingsun->netdev = net; - kingsun->usbdev = dev; - kingsun->irlap = NULL; - kingsun->tx_setuprequest = NULL; - kingsun->tx_urb = NULL; - kingsun->tx_buf_clear = NULL; - kingsun->tx_buf_xored = NULL; - kingsun->tx_buf_clear_used = 0; - kingsun->tx_buf_clear_sent = 0; - - kingsun->rx_setuprequest = NULL; - kingsun->rx_urb = NULL; - kingsun->rx_buf = NULL; - kingsun->rx_variable_xormask = 0; - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->receiving = 0; - spin_lock_init(&kingsun->lock); - - kingsun->speed_setuprequest = NULL; - kingsun->speed_urb = NULL; - kingsun->speedparams.baudrate = 0; - - /* Allocate input buffer */ - kingsun->rx_buf = kmalloc(KINGSUN_RCV_FIFO_SIZE, GFP_KERNEL); - if (!kingsun->rx_buf) - goto free_mem; - - /* Allocate input setup packet */ - kingsun->rx_setuprequest = - kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); - if (!kingsun->rx_setuprequest) - goto free_mem; - kingsun->rx_setuprequest->bRequestType = - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - kingsun->rx_setuprequest->bRequest = KINGSUN_REQ_RECV; - kingsun->rx_setuprequest->wValue = cpu_to_le16(0x0200); - kingsun->rx_setuprequest->wIndex = 0; - kingsun->rx_setuprequest->wLength = cpu_to_le16(KINGSUN_RCV_FIFO_SIZE); - - /* Allocate output buffer */ - kingsun->tx_buf_clear = kmalloc(KINGSUN_SND_FIFO_SIZE, GFP_KERNEL); - if (!kingsun->tx_buf_clear) - goto free_mem; - kingsun->tx_buf_xored = kmalloc(KINGSUN_SND_PACKET_SIZE, GFP_KERNEL); - if (!kingsun->tx_buf_xored) - goto free_mem; - - /* Allocate and initialize output setup packet */ - kingsun->tx_setuprequest = - kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); - if (!kingsun->tx_setuprequest) - goto free_mem; - kingsun->tx_setuprequest->bRequestType = - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - kingsun->tx_setuprequest->bRequest = KINGSUN_REQ_SEND; - kingsun->tx_setuprequest->wValue = 0; - kingsun->tx_setuprequest->wIndex = 0; - kingsun->tx_setuprequest->wLength = 0; - - /* Allocate and initialize speed setup packet */ - kingsun->speed_setuprequest = - kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); - if (!kingsun->speed_setuprequest) - goto free_mem; - kingsun->speed_setuprequest->bRequestType = - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - kingsun->speed_setuprequest->bRequest = KINGSUN_REQ_SEND; - kingsun->speed_setuprequest->wValue = cpu_to_le16(0x0200); - kingsun->speed_setuprequest->wIndex = cpu_to_le16(0x0001); - kingsun->speed_setuprequest->wLength = - cpu_to_le16(sizeof(struct ks959_speedparams)); - - printk(KERN_INFO "KingSun KS-959 IRDA/USB found at address %d, " - "Vendor: %x, Product: %x\n", - dev->devnum, le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&kingsun->qos); - - /* Baud rates known to be supported. Please uncomment if devices (other - than a SonyEriccson K300 phone) can be shown to support higher speed - with this dongle. - */ - kingsun->qos.baud_rate.bits = - IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600; - kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; - irda_qos_bits_to_value(&kingsun->qos); - - /* Override the network functions we need to use */ - net->netdev_ops = &ks959_ops; - - ret = register_netdev(net); - if (ret != 0) - goto free_mem; - - dev_info(&net->dev, "IrDA: Registered KingSun KS-959 device %s\n", - net->name); - - usb_set_intfdata(intf, kingsun); - - /* Situation at this point: - - all work buffers allocated - - setup requests pre-filled - - urbs not allocated, set to NULL - - max rx packet known (is KINGSUN_FIFO_SIZE) - - unwrap state machine (partially) initialized, but skb == NULL - */ - - return 0; - - free_mem: - kfree(kingsun->speed_setuprequest); - kfree(kingsun->tx_setuprequest); - kfree(kingsun->tx_buf_xored); - kfree(kingsun->tx_buf_clear); - kfree(kingsun->rx_setuprequest); - kfree(kingsun->rx_buf); - free_netdev(net); - err_out1: - return ret; -} - -/* - * The current device is removed, the USB layer tell us to shut it down... - */ -static void ks959_disconnect(struct usb_interface *intf) -{ - struct ks959_cb *kingsun = usb_get_intfdata(intf); - - if (!kingsun) - return; - - unregister_netdev(kingsun->netdev); - - /* Mop up receive && transmit urb's */ - if (kingsun->speed_urb != NULL) { - usb_kill_urb(kingsun->speed_urb); - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - } - if (kingsun->tx_urb != NULL) { - usb_kill_urb(kingsun->tx_urb); - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - } - if (kingsun->rx_urb != NULL) { - usb_kill_urb(kingsun->rx_urb); - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - } - - kfree(kingsun->speed_setuprequest); - kfree(kingsun->tx_setuprequest); - kfree(kingsun->tx_buf_xored); - kfree(kingsun->tx_buf_clear); - kfree(kingsun->rx_setuprequest); - kfree(kingsun->rx_buf); - free_netdev(kingsun->netdev); - - usb_set_intfdata(intf, NULL); -} - -#ifdef CONFIG_PM -/* USB suspend, so power off the transmitter/receiver */ -static int ks959_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct ks959_cb *kingsun = usb_get_intfdata(intf); - - netif_device_detach(kingsun->netdev); - if (kingsun->speed_urb != NULL) - usb_kill_urb(kingsun->speed_urb); - if (kingsun->tx_urb != NULL) - usb_kill_urb(kingsun->tx_urb); - if (kingsun->rx_urb != NULL) - usb_kill_urb(kingsun->rx_urb); - return 0; -} - -/* Coming out of suspend, so reset hardware */ -static int ks959_resume(struct usb_interface *intf) -{ - struct ks959_cb *kingsun = usb_get_intfdata(intf); - - if (kingsun->rx_urb != NULL) { - /* Setup request already filled in ks959_probe */ - usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - } - netif_device_attach(kingsun->netdev); - - return 0; -} -#endif - -/* - * USB device callbacks - */ -static struct usb_driver irda_driver = { - .name = "ks959-sir", - .probe = ks959_probe, - .disconnect = ks959_disconnect, - .id_table = dongles, -#ifdef CONFIG_PM - .suspend = ks959_suspend, - .resume = ks959_resume, -#endif -}; - -module_usb_driver(irda_driver); - -MODULE_AUTHOR("Alex Villacís Lasso "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun KS-959"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/ksdazzle-sir.c b/drivers/staging/irda/drivers/ksdazzle-sir.c deleted file mode 100644 index d2a0755df596..000000000000 --- a/drivers/staging/irda/drivers/ksdazzle-sir.c +++ /dev/null @@ -1,813 +0,0 @@ -/***************************************************************************** -* -* Filename: ksdazzle.c -* Version: 0.1.2 -* Description: Irda KingSun Dazzle USB Dongle -* Status: Experimental -* Author: Alex Villacís Lasso -* -* Based on stir4200, mcs7780, kingsun-sir drivers. -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - -/* - * Following is my most current (2007-07-26) understanding of how the Kingsun - * 07D0:4100 dongle (sometimes known as the MA-660) is supposed to work. This - * information was deduced by examining the USB traffic captured with USBSnoopy - * from the WinXP driver. Feel free to update here as more of the dongle is - * known. - * - * General: This dongle exposes one interface with two interrupt endpoints, one - * IN and one OUT. In this regard, it is similar to what the Kingsun/Donshine - * dongle (07c0:4200) exposes. Traffic is raw and needs to be wrapped and - * unwrapped manually as in stir4200, kingsun-sir, and ks959-sir. - * - * Transmission: To transmit an IrDA frame, it is necessary to wrap it, then - * split it into multiple segments of up to 7 bytes each, and transmit each in - * sequence. It seems that sending a single big block (like kingsun-sir does) - * won't work with this dongle. Each segment needs to be prefixed with a value - * equal to (unsigned char)0xF8 + , inside a payload - * of exactly 8 bytes. For example, a segment of 1 byte gets prefixed by 0xF9, - * and one of 7 bytes gets prefixed by 0xFF. The bytes at the end of the - * payload, not considered by the prefix, are ignored (set to 0 by this - * implementation). - * - * Reception: To receive data, the driver must poll the dongle regularly (like - * kingsun-sir.c) with interrupt URBs. If data is available, it will be returned - * in payloads from 0 to 8 bytes long. When concatenated, these payloads form - * a raw IrDA stream that needs to be unwrapped as in stir4200 and kingsun-sir - * - * Speed change: To change the speed of the dongle, the driver prepares a - * control URB with the following as a setup packet: - * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - * bRequest 0x09 - * wValue 0x0200 - * wIndex 0x0001 - * wLength 0x0008 (length of the payload) - * The payload is a 8-byte record, apparently identical to the one used in - * drivers/usb/serial/cypress_m8.c to change speed: - * __u32 baudSpeed; - * unsigned int dataBits : 2; // 0 - 5 bits 3 - 8 bits - * unsigned int : 1; - * unsigned int stopBits : 1; - * unsigned int parityEnable : 1; - * unsigned int parityType : 1; - * unsigned int : 1; - * unsigned int reset : 1; - * unsigned char reserved[3]; // set to 0 - * - * For now only SIR speeds have been observed with this dongle. Therefore, - * nothing is known on what changes (if any) must be done to frame wrapping / - * unwrapping for higher than SIR speeds. This driver assumes no change is - * necessary and announces support for all the way to 115200 bps. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#define KSDAZZLE_VENDOR_ID 0x07d0 -#define KSDAZZLE_PRODUCT_ID 0x4100 - -/* These are the currently known USB ids */ -static const struct usb_device_id dongles[] = { - /* KingSun Co,Ltd IrDA/USB Bridge */ - {USB_DEVICE(KSDAZZLE_VENDOR_ID, KSDAZZLE_PRODUCT_ID)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, dongles); - -#define KINGSUN_MTT 0x07 -#define KINGSUN_REQ_RECV 0x01 -#define KINGSUN_REQ_SEND 0x09 - -#define KINGSUN_SND_FIFO_SIZE 2048 /* Max packet we can send */ -#define KINGSUN_RCV_MAX 2048 /* Max transfer we can receive */ - -struct ksdazzle_speedparams { - __le32 baudrate; /* baud rate, little endian */ - __u8 flags; - __u8 reserved[3]; -} __packed; - -#define KS_DATA_5_BITS 0x00 -#define KS_DATA_6_BITS 0x01 -#define KS_DATA_7_BITS 0x02 -#define KS_DATA_8_BITS 0x03 - -#define KS_STOP_BITS_1 0x00 -#define KS_STOP_BITS_2 0x08 - -#define KS_PAR_DISABLE 0x00 -#define KS_PAR_EVEN 0x10 -#define KS_PAR_ODD 0x30 -#define KS_RESET 0x80 - -#define KINGSUN_EP_IN 0 -#define KINGSUN_EP_OUT 1 - -struct ksdazzle_cb { - struct usb_device *usbdev; /* init: probe_irda */ - struct net_device *netdev; /* network layer */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - - struct qos_info qos; - - struct urb *tx_urb; - __u8 *tx_buf_clear; - unsigned int tx_buf_clear_used; - unsigned int tx_buf_clear_sent; - __u8 tx_payload[8]; - - struct urb *rx_urb; - __u8 *rx_buf; - iobuff_t rx_unwrap_buff; - - struct usb_ctrlrequest *speed_setuprequest; - struct urb *speed_urb; - struct ksdazzle_speedparams speedparams; - unsigned int new_speed; - - __u8 ep_in; - __u8 ep_out; - - spinlock_t lock; - int receiving; -}; - -/* Callback transmission routine */ -static void ksdazzle_speed_irq(struct urb *urb) -{ - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) - dev_err(&urb->dev->dev, - "ksdazzle_speed_irq: urb asynchronously failed - %d\n", - urb->status); -} - -/* Send a control request to change speed of the dongle */ -static int ksdazzle_change_speed(struct ksdazzle_cb *kingsun, unsigned speed) -{ - static unsigned int supported_speeds[] = { 2400, 9600, 19200, 38400, - 57600, 115200, 576000, 1152000, 4000000, 0 - }; - int err; - unsigned int i; - - if (kingsun->speed_setuprequest == NULL || kingsun->speed_urb == NULL) - return -ENOMEM; - - /* Check that requested speed is among the supported ones */ - for (i = 0; supported_speeds[i] && supported_speeds[i] != speed; i++) ; - if (supported_speeds[i] == 0) - return -EOPNOTSUPP; - - memset(&(kingsun->speedparams), 0, sizeof(struct ksdazzle_speedparams)); - kingsun->speedparams.baudrate = cpu_to_le32(speed); - kingsun->speedparams.flags = KS_DATA_8_BITS; - - /* speed_setuprequest pre-filled in ksdazzle_probe */ - usb_fill_control_urb(kingsun->speed_urb, kingsun->usbdev, - usb_sndctrlpipe(kingsun->usbdev, 0), - (unsigned char *)kingsun->speed_setuprequest, - &(kingsun->speedparams), - sizeof(struct ksdazzle_speedparams), - ksdazzle_speed_irq, kingsun); - kingsun->speed_urb->status = 0; - err = usb_submit_urb(kingsun->speed_urb, GFP_ATOMIC); - - return err; -} - -/* Submit one fragment of an IrDA frame to the dongle */ -static void ksdazzle_send_irq(struct urb *urb); -static int ksdazzle_submit_tx_fragment(struct ksdazzle_cb *kingsun) -{ - unsigned int wraplen; - int ret; - - /* We can send at most 7 bytes of payload at a time */ - wraplen = 7; - if (wraplen > kingsun->tx_buf_clear_used) - wraplen = kingsun->tx_buf_clear_used; - - /* Prepare payload prefix with used length */ - memset(kingsun->tx_payload, 0, 8); - kingsun->tx_payload[0] = (unsigned char)0xf8 + wraplen; - memcpy(kingsun->tx_payload + 1, kingsun->tx_buf_clear, wraplen); - - usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev, - usb_sndintpipe(kingsun->usbdev, kingsun->ep_out), - kingsun->tx_payload, 8, ksdazzle_send_irq, kingsun, 1); - kingsun->tx_urb->status = 0; - ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC); - - /* Remember how much data was sent, in order to update at callback */ - kingsun->tx_buf_clear_sent = (ret == 0) ? wraplen : 0; - return ret; -} - -/* Callback transmission routine */ -static void ksdazzle_send_irq(struct urb *urb) -{ - struct ksdazzle_cb *kingsun = urb->context; - struct net_device *netdev = kingsun->netdev; - int ret = 0; - - /* in process of stopping, just drop data */ - if (!netif_running(kingsun->netdev)) { - dev_err(&kingsun->usbdev->dev, - "ksdazzle_send_irq: Network not running!\n"); - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "ksdazzle_send_irq: urb asynchronously failed - %d\n", - urb->status); - return; - } - - if (kingsun->tx_buf_clear_used > 0) { - /* Update data remaining to be sent */ - if (kingsun->tx_buf_clear_sent < kingsun->tx_buf_clear_used) { - memmove(kingsun->tx_buf_clear, - kingsun->tx_buf_clear + - kingsun->tx_buf_clear_sent, - kingsun->tx_buf_clear_used - - kingsun->tx_buf_clear_sent); - } - kingsun->tx_buf_clear_used -= kingsun->tx_buf_clear_sent; - kingsun->tx_buf_clear_sent = 0; - - if (kingsun->tx_buf_clear_used > 0) { - /* There is more data to be sent */ - if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) { - dev_err(&kingsun->usbdev->dev, - "ksdazzle_send_irq: failed tx_urb submit: %d\n", - ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - netdev->stats.tx_errors++; - netif_start_queue(netdev); - } - } - } else { - /* All data sent, send next speed && wake network queue */ - if (kingsun->new_speed != -1 && - cpu_to_le32(kingsun->new_speed) != - kingsun->speedparams.baudrate) - ksdazzle_change_speed(kingsun, - kingsun->new_speed); - - netif_wake_queue(netdev); - } - } -} - -/* - * Called from net/core when new frame is available. - */ -static netdev_tx_t ksdazzle_hard_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct ksdazzle_cb *kingsun; - unsigned int wraplen; - int ret = 0; - - netif_stop_queue(netdev); - - /* the IRDA wrapping routines don't deal with non linear skb */ - SKB_LINEAR_ASSERT(skb); - - kingsun = netdev_priv(netdev); - - spin_lock(&kingsun->lock); - kingsun->new_speed = irda_get_next_speed(skb); - - /* Append data to the end of whatever data remains to be transmitted */ - wraplen = - async_wrap_skb(skb, kingsun->tx_buf_clear, KINGSUN_SND_FIFO_SIZE); - kingsun->tx_buf_clear_used = wraplen; - - if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) { - dev_err(&kingsun->usbdev->dev, - "ksdazzle_hard_xmit: failed tx_urb submit: %d\n", ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - netdev->stats.tx_errors++; - netif_start_queue(netdev); - } - } else { - netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; - - } - - dev_kfree_skb(skb); - spin_unlock(&kingsun->lock); - - return NETDEV_TX_OK; -} - -/* Receive callback function */ -static void ksdazzle_rcv_irq(struct urb *urb) -{ - struct ksdazzle_cb *kingsun = urb->context; - struct net_device *netdev = kingsun->netdev; - - /* in process of stopping, just drop data */ - if (!netif_running(netdev)) { - kingsun->receiving = 0; - return; - } - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) { - dev_err(&kingsun->usbdev->dev, - "ksdazzle_rcv_irq: urb asynchronously failed - %d\n", - urb->status); - kingsun->receiving = 0; - return; - } - - if (urb->actual_length > 0) { - __u8 *bytes = urb->transfer_buffer; - unsigned int i; - - for (i = 0; i < urb->actual_length; i++) { - async_unwrap_char(netdev, &netdev->stats, - &kingsun->rx_unwrap_buff, bytes[i]); - } - kingsun->receiving = - (kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0; - } - - /* This urb has already been filled in ksdazzle_net_open. It is assumed that - urb keeps the pointer to the payload buffer. - */ - urb->status = 0; - usb_submit_urb(urb, GFP_ATOMIC); -} - -/* - * Function ksdazzle_net_open (dev) - * - * Network device is taken up. Usually this is done by "ifconfig irda0 up" - */ -static int ksdazzle_net_open(struct net_device *netdev) -{ - struct ksdazzle_cb *kingsun = netdev_priv(netdev); - int err = -ENOMEM; - char hwname[16]; - - /* At this point, urbs are NULL, and skb is NULL (see ksdazzle_probe) */ - kingsun->receiving = 0; - - /* Initialize for SIR to copy data directly into skb. */ - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->rx_unwrap_buff.truesize = IRDA_SKB_MAX_MTU; - kingsun->rx_unwrap_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!kingsun->rx_unwrap_buff.skb) - goto free_mem; - - skb_reserve(kingsun->rx_unwrap_buff.skb, 1); - kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data; - - kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->rx_urb) - goto free_mem; - - kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->tx_urb) - goto free_mem; - - kingsun->speed_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!kingsun->speed_urb) - goto free_mem; - - /* Initialize speed for dongle */ - kingsun->new_speed = 9600; - err = ksdazzle_change_speed(kingsun, 9600); - if (err < 0) - goto free_mem; - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - */ - sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); - kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); - if (!kingsun->irlap) { - err = -ENOMEM; - dev_err(&kingsun->usbdev->dev, "irlap_open failed\n"); - goto free_mem; - } - - /* Start reception. */ - usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev, - usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in), - kingsun->rx_buf, KINGSUN_RCV_MAX, ksdazzle_rcv_irq, - kingsun, 1); - kingsun->rx_urb->status = 0; - err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - if (err) { - dev_err(&kingsun->usbdev->dev, "first urb-submit failed: %d\n", err); - goto close_irlap; - } - - netif_start_queue(netdev); - - /* Situation at this point: - - all work buffers allocated - - urbs allocated and ready to fill - - max rx packet known (in max_rx) - - unwrap state machine initialized, in state outside of any frame - - receive request in progress - - IrLAP layer started, about to hand over packets to send - */ - - return 0; - - close_irlap: - irlap_close(kingsun->irlap); - free_mem: - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - if (kingsun->rx_unwrap_buff.skb) { - kfree_skb(kingsun->rx_unwrap_buff.skb); - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->rx_unwrap_buff.head = NULL; - } - return err; -} - -/* - * Function ksdazzle_net_close (dev) - * - * Network device is taken down. Usually this is done by - * "ifconfig irda0 down" - */ -static int ksdazzle_net_close(struct net_device *netdev) -{ - struct ksdazzle_cb *kingsun = netdev_priv(netdev); - - /* Stop transmit processing */ - netif_stop_queue(netdev); - - /* Mop up receive && transmit urb's */ - usb_kill_urb(kingsun->tx_urb); - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - - usb_kill_urb(kingsun->speed_urb); - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - - usb_kill_urb(kingsun->rx_urb); - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - - kfree_skb(kingsun->rx_unwrap_buff.skb); - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->rx_unwrap_buff.head = NULL; - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->receiving = 0; - - /* Stop and remove instance of IrLAP */ - irlap_close(kingsun->irlap); - - kingsun->irlap = NULL; - - return 0; -} - -/* - * IOCTLs : Extra out-of-band network commands... - */ -static int ksdazzle_net_ioctl(struct net_device *netdev, struct ifreq *rq, - int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *)rq; - struct ksdazzle_cb *kingsun = netdev_priv(netdev); - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the device is still there */ - if (netif_device_present(kingsun->netdev)) - return ksdazzle_change_speed(kingsun, - irq->ifr_baudrate); - break; - - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the IrDA stack is still there */ - if (netif_running(kingsun->netdev)) - irda_device_set_media_busy(kingsun->netdev, TRUE); - break; - - case SIOCGRECEIVING: - /* Only approximately true */ - irq->ifr_receiving = kingsun->receiving; - break; - - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -static const struct net_device_ops ksdazzle_ops = { - .ndo_start_xmit = ksdazzle_hard_xmit, - .ndo_open = ksdazzle_net_open, - .ndo_stop = ksdazzle_net_close, - .ndo_do_ioctl = ksdazzle_net_ioctl, -}; - -/* - * This routine is called by the USB subsystem for each new device - * in the system. We need to check if the device is ours, and in - * this case start handling it. - */ -static int ksdazzle_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - - struct usb_device *dev = interface_to_usbdev(intf); - struct ksdazzle_cb *kingsun = NULL; - struct net_device *net = NULL; - int ret = -ENOMEM; - int pipe, maxp_in, maxp_out; - __u8 ep_in; - __u8 ep_out; - - /* Check that there really are two interrupt endpoints. Check based on the - one in drivers/usb/input/usbmouse.c - */ - interface = intf->cur_altsetting; - if (interface->desc.bNumEndpoints != 2) { - dev_err(&intf->dev, "ksdazzle: expected 2 endpoints, found %d\n", - interface->desc.bNumEndpoints); - return -ENODEV; - } - endpoint = &interface->endpoint[KINGSUN_EP_IN].desc; - if (!usb_endpoint_is_int_in(endpoint)) { - dev_err(&intf->dev, - "ksdazzle: endpoint 0 is not interrupt IN\n"); - return -ENODEV; - } - - ep_in = endpoint->bEndpointAddress; - pipe = usb_rcvintpipe(dev, ep_in); - maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - if (maxp_in > 255 || maxp_in <= 1) { - dev_err(&intf->dev, - "ksdazzle: endpoint 0 has max packet size %d not in range [2..255]\n", - maxp_in); - return -ENODEV; - } - - endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc; - if (!usb_endpoint_is_int_out(endpoint)) { - dev_err(&intf->dev, - "ksdazzle: endpoint 1 is not interrupt OUT\n"); - return -ENODEV; - } - - ep_out = endpoint->bEndpointAddress; - pipe = usb_sndintpipe(dev, ep_out); - maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - /* Allocate network device container. */ - net = alloc_irdadev(sizeof(*kingsun)); - if (!net) - goto err_out1; - - SET_NETDEV_DEV(net, &intf->dev); - kingsun = netdev_priv(net); - kingsun->netdev = net; - kingsun->usbdev = dev; - kingsun->ep_in = ep_in; - kingsun->ep_out = ep_out; - kingsun->irlap = NULL; - kingsun->tx_urb = NULL; - kingsun->tx_buf_clear = NULL; - kingsun->tx_buf_clear_used = 0; - kingsun->tx_buf_clear_sent = 0; - - kingsun->rx_urb = NULL; - kingsun->rx_buf = NULL; - kingsun->rx_unwrap_buff.in_frame = FALSE; - kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME; - kingsun->rx_unwrap_buff.skb = NULL; - kingsun->receiving = 0; - spin_lock_init(&kingsun->lock); - - kingsun->speed_setuprequest = NULL; - kingsun->speed_urb = NULL; - kingsun->speedparams.baudrate = 0; - - /* Allocate input buffer */ - kingsun->rx_buf = kmalloc(KINGSUN_RCV_MAX, GFP_KERNEL); - if (!kingsun->rx_buf) - goto free_mem; - - /* Allocate output buffer */ - kingsun->tx_buf_clear = kmalloc(KINGSUN_SND_FIFO_SIZE, GFP_KERNEL); - if (!kingsun->tx_buf_clear) - goto free_mem; - - /* Allocate and initialize speed setup packet */ - kingsun->speed_setuprequest = - kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); - if (!kingsun->speed_setuprequest) - goto free_mem; - kingsun->speed_setuprequest->bRequestType = - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - kingsun->speed_setuprequest->bRequest = KINGSUN_REQ_SEND; - kingsun->speed_setuprequest->wValue = cpu_to_le16(0x0200); - kingsun->speed_setuprequest->wIndex = cpu_to_le16(0x0001); - kingsun->speed_setuprequest->wLength = - cpu_to_le16(sizeof(struct ksdazzle_speedparams)); - - printk(KERN_INFO "KingSun/Dazzle IRDA/USB found at address %d, " - "Vendor: %x, Product: %x\n", - dev->devnum, le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&kingsun->qos); - - /* Baud rates known to be supported. Please uncomment if devices (other - than a SonyEriccson K300 phone) can be shown to support higher speeds - with this dongle. - */ - kingsun->qos.baud_rate.bits = - IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200; - kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; - irda_qos_bits_to_value(&kingsun->qos); - - /* Override the network functions we need to use */ - net->netdev_ops = &ksdazzle_ops; - - ret = register_netdev(net); - if (ret != 0) - goto free_mem; - - dev_info(&net->dev, "IrDA: Registered KingSun/Dazzle device %s\n", - net->name); - - usb_set_intfdata(intf, kingsun); - - /* Situation at this point: - - all work buffers allocated - - setup requests pre-filled - - urbs not allocated, set to NULL - - max rx packet known (is KINGSUN_FIFO_SIZE) - - unwrap state machine (partially) initialized, but skb == NULL - */ - - return 0; - - free_mem: - kfree(kingsun->speed_setuprequest); - kfree(kingsun->tx_buf_clear); - kfree(kingsun->rx_buf); - free_netdev(net); - err_out1: - return ret; -} - -/* - * The current device is removed, the USB layer tell us to shut it down... - */ -static void ksdazzle_disconnect(struct usb_interface *intf) -{ - struct ksdazzle_cb *kingsun = usb_get_intfdata(intf); - - if (!kingsun) - return; - - unregister_netdev(kingsun->netdev); - - /* Mop up receive && transmit urb's */ - usb_kill_urb(kingsun->speed_urb); - usb_free_urb(kingsun->speed_urb); - kingsun->speed_urb = NULL; - - usb_kill_urb(kingsun->tx_urb); - usb_free_urb(kingsun->tx_urb); - kingsun->tx_urb = NULL; - - usb_kill_urb(kingsun->rx_urb); - usb_free_urb(kingsun->rx_urb); - kingsun->rx_urb = NULL; - - kfree(kingsun->speed_setuprequest); - kfree(kingsun->tx_buf_clear); - kfree(kingsun->rx_buf); - free_netdev(kingsun->netdev); - - usb_set_intfdata(intf, NULL); -} - -#ifdef CONFIG_PM -/* USB suspend, so power off the transmitter/receiver */ -static int ksdazzle_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct ksdazzle_cb *kingsun = usb_get_intfdata(intf); - - netif_device_detach(kingsun->netdev); - if (kingsun->speed_urb != NULL) - usb_kill_urb(kingsun->speed_urb); - if (kingsun->tx_urb != NULL) - usb_kill_urb(kingsun->tx_urb); - if (kingsun->rx_urb != NULL) - usb_kill_urb(kingsun->rx_urb); - return 0; -} - -/* Coming out of suspend, so reset hardware */ -static int ksdazzle_resume(struct usb_interface *intf) -{ - struct ksdazzle_cb *kingsun = usb_get_intfdata(intf); - - if (kingsun->rx_urb != NULL) { - /* Setup request already filled in ksdazzle_probe */ - usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); - } - netif_device_attach(kingsun->netdev); - - return 0; -} -#endif - -/* - * USB device callbacks - */ -static struct usb_driver irda_driver = { - .name = "ksdazzle-sir", - .probe = ksdazzle_probe, - .disconnect = ksdazzle_disconnect, - .id_table = dongles, -#ifdef CONFIG_PM - .suspend = ksdazzle_suspend, - .resume = ksdazzle_resume, -#endif -}; - -module_usb_driver(irda_driver); - -MODULE_AUTHOR("Alex Villacís Lasso "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun Dazzle"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/litelink-sir.c b/drivers/staging/irda/drivers/litelink-sir.c deleted file mode 100644 index 8eefcb44bac3..000000000000 --- a/drivers/staging/irda/drivers/litelink-sir.c +++ /dev/null @@ -1,199 +0,0 @@ -/********************************************************************* - * - * Filename: litelink.c - * Version: 1.1 - * Description: Driver for the Parallax LiteLink dongle - * Status: Stable - * Author: Dag Brattli - * Created at: Fri May 7 12:50:33 1999 - * Modified at: Fri Dec 17 09:14:23 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -/* - * Modified at: Thu Jan 15 2003 - * Modified by: Eugene Crosser - * - * Convert to "new" IRDA infrastructure for kernel 2.6 - */ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -#define MIN_DELAY 25 /* 15 us, but wait a little more to be sure */ -#define MAX_DELAY 10000 /* 1 ms */ - -static int litelink_open(struct sir_dev *dev); -static int litelink_close(struct sir_dev *dev); -static int litelink_change_speed(struct sir_dev *dev, unsigned speed); -static int litelink_reset(struct sir_dev *dev); - -/* These are the baudrates supported - 9600 must be last one! */ -static unsigned baud_rates[] = { 115200, 57600, 38400, 19200, 9600 }; - -static struct dongle_driver litelink = { - .owner = THIS_MODULE, - .driver_name = "Parallax LiteLink", - .type = IRDA_LITELINK_DONGLE, - .open = litelink_open, - .close = litelink_close, - .reset = litelink_reset, - .set_speed = litelink_change_speed, -}; - -static int __init litelink_sir_init(void) -{ - return irda_register_dongle(&litelink); -} - -static void __exit litelink_sir_cleanup(void) -{ - irda_unregister_dongle(&litelink); -} - -static int litelink_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Power up dongle */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Set the speeds we can accept */ - qos->baud_rate.bits &= IR_115200|IR_57600|IR_38400|IR_19200|IR_9600; - qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int litelink_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function litelink_change_speed (task) - * - * Change speed of the Litelink dongle. To cycle through the available - * baud rates, pulse RTS low for a few ms. - */ -static int litelink_change_speed(struct sir_dev *dev, unsigned speed) -{ - int i; - - /* dongle already reset by irda-thread - current speed (dongle and - * port) is the default speed (115200 for litelink!) - */ - - /* Cycle through avaiable baudrates until we reach the correct one */ - for (i = 0; baud_rates[i] != speed; i++) { - - /* end-of-list reached due to invalid speed request */ - if (baud_rates[i] == 9600) - break; - - /* Set DTR, clear RTS */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - /* Sleep a minimum of 15 us */ - udelay(MIN_DELAY); - - /* Set DTR, Set RTS */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Sleep a minimum of 15 us */ - udelay(MIN_DELAY); - } - - dev->speed = baud_rates[i]; - - /* invalid baudrate should not happen - but if, we return -EINVAL and - * the dongle configured for 9600 so the stack has a chance to recover - */ - - return (dev->speed == speed) ? 0 : -EINVAL; -} - -/* - * Function litelink_reset (task) - * - * Reset the Litelink type dongle. - * - */ -static int litelink_reset(struct sir_dev *dev) -{ - /* probably the power-up can be dropped here, but with only - * 15 usec delay it's not worth the risk unless somebody with - * the hardware confirms it doesn't break anything... - */ - - /* Power on dongle */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Sleep a minimum of 15 us */ - udelay(MIN_DELAY); - - /* Clear RTS to reset dongle */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - - /* Sleep a minimum of 15 us */ - udelay(MIN_DELAY); - - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Sleep a minimum of 15 us */ - udelay(MIN_DELAY); - - /* This dongles speed defaults to 115200 bps */ - dev->speed = 115200; - - return 0; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("Parallax Litelink dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-5"); /* IRDA_LITELINK_DONGLE */ - -/* - * Function init_module (void) - * - * Initialize Litelink module - * - */ -module_init(litelink_sir_init); - -/* - * Function cleanup_module (void) - * - * Cleanup Litelink module - * - */ -module_exit(litelink_sir_cleanup); diff --git a/drivers/staging/irda/drivers/ma600-sir.c b/drivers/staging/irda/drivers/ma600-sir.c deleted file mode 100644 index a764817b47f1..000000000000 --- a/drivers/staging/irda/drivers/ma600-sir.c +++ /dev/null @@ -1,253 +0,0 @@ -/********************************************************************* - * - * Filename: ma600.c - * Version: 0.1 - * Description: Implementation of the MA600 dongle - * Status: Experimental. - * Author: Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95 - * Created at: Sat Jun 10 20:02:35 2000 - * Modified at: Sat Aug 16 09:34:13 2003 - * Modified by: Martin Diehl (modified for new sir_dev) - * - * Note: very thanks to Mr. Maru Wang for providing - * information on the MA600 dongle - * - * Copyright (c) 2000 Leung, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int ma600_open(struct sir_dev *); -static int ma600_close(struct sir_dev *); -static int ma600_change_speed(struct sir_dev *, unsigned); -static int ma600_reset(struct sir_dev *); - -/* control byte for MA600 */ -#define MA600_9600 0x00 -#define MA600_19200 0x01 -#define MA600_38400 0x02 -#define MA600_57600 0x03 -#define MA600_115200 0x04 -#define MA600_DEV_ID1 0x05 -#define MA600_DEV_ID2 0x06 -#define MA600_2400 0x08 - -static struct dongle_driver ma600 = { - .owner = THIS_MODULE, - .driver_name = "MA600", - .type = IRDA_MA600_DONGLE, - .open = ma600_open, - .close = ma600_close, - .reset = ma600_reset, - .set_speed = ma600_change_speed, -}; - - -static int __init ma600_sir_init(void) -{ - return irda_register_dongle(&ma600); -} - -static void __exit ma600_sir_cleanup(void) -{ - irda_unregister_dongle(&ma600); -} - -/* - Power on: - (0) Clear RTS and DTR for 1 second - (1) Set RTS and DTR for 1 second - (2) 9600 bps now - Note: assume RTS, DTR are clear before -*/ -static int ma600_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Explicitly set the speeds we can accept */ - qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400 - |IR_57600|IR_115200; - /* Hm, 0x01 means 10ms - for >= 1ms we would need 0x07 */ - qos->min_turn_time.bits = 0x01; /* Needs at least 1 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int ma600_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -static __u8 get_control_byte(__u32 speed) -{ - __u8 byte; - - switch (speed) { - default: - case 115200: - byte = MA600_115200; - break; - case 57600: - byte = MA600_57600; - break; - case 38400: - byte = MA600_38400; - break; - case 19200: - byte = MA600_19200; - break; - case 9600: - byte = MA600_9600; - break; - case 2400: - byte = MA600_2400; - break; - } - - return byte; -} - -/* - * Function ma600_change_speed (dev, speed) - * - * Set the speed for the MA600 type dongle. - * - * The dongle has already been reset to a known state (dongle default) - * We cycle through speeds by pulsing RTS low and then high. - */ - -/* - * Function ma600_change_speed (dev, speed) - * - * Set the speed for the MA600 type dongle. - * - * Algorithm - * 1. Reset (already done by irda thread state machine) - * 2. clear RTS, set DTR and wait for 1ms - * 3. send Control Byte to the MA600 through TXD to set new baud rate - * wait until the stop bit of Control Byte is sent (for 9600 baud rate, - * it takes about 10 msec) - * 4. set RTS, set DTR (return to NORMAL Operation) - * 5. wait at least 10 ms, new setting (baud rate, etc) takes effect here - * after - */ - -/* total delays are only about 20ms - let's just sleep for now to - * avoid the state machine complexity before we get things working - */ - -static int ma600_change_speed(struct sir_dev *dev, unsigned speed) -{ - u8 byte; - - pr_debug("%s(), speed=%d (was %d)\n", __func__, - speed, dev->speed); - - /* dongle already reset, dongle and port at default speed (9600) */ - - /* Set RTS low for 1 ms */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - mdelay(1); - - /* Write control byte */ - byte = get_control_byte(speed); - sirdev_raw_write(dev, &byte, sizeof(byte)); - - /* Wait at least 10ms: fake wait_until_sent - 10 bits at 9600 baud*/ - msleep(15); /* old ma600 uses 15ms */ - -#if 1 - /* read-back of the control byte. ma600 is the first dongle driver - * which uses this so there might be some unidentified issues. - * Disable this in case of problems with readback. - */ - - sirdev_raw_read(dev, &byte, sizeof(byte)); - if (byte != get_control_byte(speed)) { - net_warn_ratelimited("%s(): bad control byte read-back %02x != %02x\n", - __func__, (unsigned)byte, - (unsigned)get_control_byte(speed)); - return -1; - } - else - pr_debug("%s() control byte write read OK\n", __func__); -#endif - - /* Set DTR, Set RTS */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Wait at least 10ms */ - msleep(10); - - /* dongle is now switched to the new speed */ - dev->speed = speed; - - return 0; -} - -/* - * Function ma600_reset (dev) - * - * This function resets the ma600 dongle. - * - * Algorithm: - * 0. DTR=0, RTS=1 and wait 10 ms - * 1. DTR=1, RTS=1 and wait 10 ms - * 2. 9600 bps now - */ - -/* total delays are only about 20ms - let's just sleep for now to - * avoid the state machine complexity before we get things working - */ - -static int ma600_reset(struct sir_dev *dev) -{ - /* Reset the dongle : set DTR low for 10 ms */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - msleep(10); - - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - msleep(10); - - dev->speed = 9600; /* That's the dongle-default */ - - return 0; -} - -MODULE_AUTHOR("Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95"); -MODULE_DESCRIPTION("MA600 dongle driver version 0.1"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-11"); /* IRDA_MA600_DONGLE */ - -module_init(ma600_sir_init); -module_exit(ma600_sir_cleanup); - diff --git a/drivers/staging/irda/drivers/mcp2120-sir.c b/drivers/staging/irda/drivers/mcp2120-sir.c deleted file mode 100644 index 2e33f91bfe8f..000000000000 --- a/drivers/staging/irda/drivers/mcp2120-sir.c +++ /dev/null @@ -1,224 +0,0 @@ -/********************************************************************* - * - * - * Filename: mcp2120.c - * Version: 1.0 - * Description: Implementation for the MCP2120 (Microchip) - * Status: Experimental. - * Author: Felix Tang (tangf@eyetap.org) - * Created at: Sun Mar 31 19:32:12 EST 2002 - * Based on code by: Dag Brattli - * - * Copyright (c) 2002 Felix Tang, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int mcp2120_reset(struct sir_dev *dev); -static int mcp2120_open(struct sir_dev *dev); -static int mcp2120_close(struct sir_dev *dev); -static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed); - -#define MCP2120_9600 0x87 -#define MCP2120_19200 0x8B -#define MCP2120_38400 0x85 -#define MCP2120_57600 0x83 -#define MCP2120_115200 0x81 - -#define MCP2120_COMMIT 0x11 - -static struct dongle_driver mcp2120 = { - .owner = THIS_MODULE, - .driver_name = "Microchip MCP2120", - .type = IRDA_MCP2120_DONGLE, - .open = mcp2120_open, - .close = mcp2120_close, - .reset = mcp2120_reset, - .set_speed = mcp2120_change_speed, -}; - -static int __init mcp2120_sir_init(void) -{ - return irda_register_dongle(&mcp2120); -} - -static void __exit mcp2120_sir_cleanup(void) -{ - irda_unregister_dongle(&mcp2120); -} - -static int mcp2120_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* seems no explicit power-on required here and reset switching it on anyway */ - - qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - qos->min_turn_time.bits = 0x01; - irda_qos_bits_to_value(qos); - - return 0; -} - -static int mcp2120_close(struct sir_dev *dev) -{ - /* Power off dongle */ - /* reset and inhibit mcp2120 */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - // sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function mcp2120_change_speed (dev, speed) - * - * Set the speed for the MCP2120. - * - */ - -#define MCP2120_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED+1) - -static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - u8 control[2]; - static int ret = 0; - - switch (state) { - case SIRDEV_STATE_DONGLE_SPEED: - /* Set DTR to enter command mode */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - udelay(500); - - ret = 0; - switch (speed) { - default: - speed = 9600; - ret = -EINVAL; - /* fall through */ - case 9600: - control[0] = MCP2120_9600; - //printk("mcp2120 9600\n"); - break; - case 19200: - control[0] = MCP2120_19200; - //printk("mcp2120 19200\n"); - break; - case 34800: - control[0] = MCP2120_38400; - //printk("mcp2120 38400\n"); - break; - case 57600: - control[0] = MCP2120_57600; - //printk("mcp2120 57600\n"); - break; - case 115200: - control[0] = MCP2120_115200; - //printk("mcp2120 115200\n"); - break; - } - control[1] = MCP2120_COMMIT; - - /* Write control bytes */ - sirdev_raw_write(dev, control, 2); - dev->speed = speed; - - state = MCP2120_STATE_WAIT_SPEED; - delay = 100; - //printk("mcp2120_change_speed: dongle_speed\n"); - break; - - case MCP2120_STATE_WAIT_SPEED: - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - //printk("mcp2120_change_speed: mcp_wait\n"); - break; - - default: - net_err_ratelimited("%s(), undefine state %d\n", - __func__, state); - ret = -EINVAL; - break; - } - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -/* - * Function mcp2120_reset (driver) - * - * This function resets the mcp2120 dongle. - * - * Info: -set RTS to reset mcp2120 - * -set DTR to set mcp2120 software command mode - * -mcp2120 defaults to 9600 baud after reset - * - * Algorithm: - * 0. Set RTS to reset mcp2120. - * 1. Clear RTS and wait for device reset timer of 30 ms (max). - * - */ - -#define MCP2120_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1) -#define MCP2120_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2) - -static int mcp2120_reset(struct sir_dev *dev) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - int ret = 0; - - switch (state) { - case SIRDEV_STATE_DONGLE_RESET: - //printk("mcp2120_reset: dongle_reset\n"); - /* Reset dongle by setting RTS*/ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - state = MCP2120_STATE_WAIT1_RESET; - delay = 50; - break; - - case MCP2120_STATE_WAIT1_RESET: - //printk("mcp2120_reset: mcp2120_wait1\n"); - /* clear RTS and wait for at least 30 ms. */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - state = MCP2120_STATE_WAIT2_RESET; - delay = 50; - break; - - case MCP2120_STATE_WAIT2_RESET: - //printk("mcp2120_reset mcp2120_wait2\n"); - /* Go back to normal mode */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - break; - - default: - net_err_ratelimited("%s(), undefined state %d\n", - __func__, state); - ret = -EINVAL; - break; - } - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -MODULE_AUTHOR("Felix Tang "); -MODULE_DESCRIPTION("Microchip MCP2120"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-9"); /* IRDA_MCP2120_DONGLE */ - -module_init(mcp2120_sir_init); -module_exit(mcp2120_sir_cleanup); diff --git a/drivers/staging/irda/drivers/mcs7780.c b/drivers/staging/irda/drivers/mcs7780.c deleted file mode 100644 index d52e9f4b9770..000000000000 --- a/drivers/staging/irda/drivers/mcs7780.c +++ /dev/null @@ -1,990 +0,0 @@ -/***************************************************************************** -* -* Filename: mcs7780.c -* Version: 0.4-alpha -* Description: Irda MosChip USB Dongle Driver -* Authors: Lukasz Stelmach -* Brian Pugh -* Judy Fischbach -* -* Based on stir4200 driver, but some things done differently. -* Based on earlier driver by Paul Stewart -* -* Copyright (C) 2000, Roman Weissgaerber -* Copyright (C) 2001, Dag Brattli -* Copyright (C) 2001, Jean Tourrilhes -* Copyright (C) 2004, Stephen Hemminger -* Copyright (C) 2005, Lukasz Stelmach -* Copyright (C) 2005, Brian Pugh -* Copyright (C) 2005, Judy Fischbach -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - -/* - * MCS7780 is a simple USB to IrDA bridge by MosChip. It is neither - * compatibile with irda-usb nor with stir4200. Although it is quite - * similar to the later as far as general idea of operation is concerned. - * That is it requires the software to do all the framing job at SIR speeds. - * The hardware does take care of the framing at MIR and FIR speeds. - * It supports all speeds from 2400 through 4Mbps - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "mcs7780.h" - -#define MCS_VENDOR_ID 0x9710 -#define MCS_PRODUCT_ID 0x7780 - -static const struct usb_device_id mcs_table[] = { - /* MosChip Corp., MCS7780 FIR-USB Adapter */ - {USB_DEVICE(MCS_VENDOR_ID, MCS_PRODUCT_ID)}, - {}, -}; - -MODULE_AUTHOR("Brian Pugh "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver for MosChip MCS7780"); -MODULE_VERSION("0.3alpha"); -MODULE_LICENSE("GPL"); - -MODULE_DEVICE_TABLE(usb, mcs_table); - -static int qos_mtt_bits = 0x07 /* > 1ms */ ; -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); - -static int receive_mode = 0x1; -module_param(receive_mode, int, 0); -MODULE_PARM_DESC(receive_mode, - "Receive mode of the device (1:fast, 0:slow, default:1)"); - -static int sir_tweak = 1; -module_param(sir_tweak, int, 0444); -MODULE_PARM_DESC(sir_tweak, - "Default pulse width (1:1.6us, 0:3/16 bit, default:1)."); - -static int transceiver_type = MCS_TSC_VISHAY; -module_param(transceiver_type, int, 0444); -MODULE_PARM_DESC(transceiver_type, "IR transceiver type, see mcs7780.h."); - -static struct usb_driver mcs_driver = { - .name = "mcs7780", - .probe = mcs_probe, - .disconnect = mcs_disconnect, - .id_table = mcs_table, -}; - -/* speed flag selection by direct addressing. -addr = (speed >> 8) & 0x0f - -0x1 57600 0x2 115200 0x4 1152000 0x5 9600 -0x6 38400 0x9 2400 0xa 576000 0xb 19200 - -4Mbps (or 2400) must be checked separately. Since it also has -to be programmed in a different manner that is not a big problem. -*/ -static __u16 mcs_speed_set[16] = { 0, - MCS_SPEED_57600, - MCS_SPEED_115200, - 0, - MCS_SPEED_1152000, - MCS_SPEED_9600, - MCS_SPEED_38400, - 0, 0, - MCS_SPEED_2400, - MCS_SPEED_576000, - MCS_SPEED_19200, - 0, 0, 0, -}; - -/* Set given 16 bit register with a 16 bit value. Send control message - * to set dongle register. */ -static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val) -{ - struct usb_device *dev = mcs->usbdev; - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, - MCS_WR_RTYPE, val, reg, NULL, 0, - msecs_to_jiffies(MCS_CTRL_TIMEOUT)); -} - -/* Get 16 bit register value. Send contol message to read dongle register. */ -static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val) -{ - struct usb_device *dev = mcs->usbdev; - void *dmabuf; - int ret; - - dmabuf = kmalloc(sizeof(__u16), GFP_KERNEL); - if (!dmabuf) - return -ENOMEM; - - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, - MCS_RD_RTYPE, 0, reg, dmabuf, 2, - msecs_to_jiffies(MCS_CTRL_TIMEOUT)); - - memcpy(val, dmabuf, sizeof(__u16)); - kfree(dmabuf); - - return ret; -} - -/* Setup a communication between mcs7780 and TFDU chips. It is described - * in more detail in the data sheet. The setup sequence puts the the - * vishay tranceiver into high speed mode. It will also receive SIR speed - * packets but at reduced sensitivity. - */ - -/* 0: OK 1:ERROR */ -static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs) -{ - int ret = 0; - __u16 rval; - - /* mcs_get_reg should read exactly two bytes from the dongle */ - ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval); - if (unlikely(ret != 2)) { - ret = -EIO; - goto error; - } - - /* The MCS_XCVR_CONF bit puts the transceiver into configuration - * mode. The MCS_MODE0 bit must start out high (1) and then - * transition to low and the MCS_STFIR and MCS_MODE1 bits must - * be low. - */ - rval |= (MCS_MODE0 | MCS_XCVR_CONF); - rval &= ~MCS_STFIR; - rval &= ~MCS_MODE1; - ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); - if (unlikely(ret)) - goto error; - - rval &= ~MCS_MODE0; - ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); - if (unlikely(ret)) - goto error; - - rval &= ~MCS_XCVR_CONF; - ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); - if (unlikely(ret)) - goto error; - - ret = 0; -error: - return ret; -} - -/* Setup a communication between mcs7780 and agilent chip. */ -static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs) -{ - net_warn_ratelimited("This transceiver type is not supported yet\n"); - return 1; -} - -/* Setup a communication between mcs7780 and sharp chip. */ -static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs) -{ - net_warn_ratelimited("This transceiver type is not supported yet\n"); - return 1; -} - -/* Common setup for all transceivers */ -static inline int mcs_setup_transceiver(struct mcs_cb *mcs) -{ - int ret = 0; - __u16 rval; - const char *msg; - - msg = "Basic transceiver setup error"; - - /* read value of MODE Register, set the DRIVER and RESET bits - * and write value back out to MODE Register - */ - ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); - if(unlikely(ret != 2)) - goto error; - rval |= MCS_DRIVER; /* put the mcs7780 into configuration mode. */ - ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); - if(unlikely(ret)) - goto error; - - rval = 0; /* set min pulse width to 0 initially. */ - ret = mcs_set_reg(mcs, MCS_MINRXPW_REG, rval); - if(unlikely(ret)) - goto error; - - ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); - if(unlikely(ret != 2)) - goto error; - - rval &= ~MCS_FIR; /* turn off fir mode. */ - if(mcs->sir_tweak) - rval |= MCS_SIR16US; /* 1.6us pulse width */ - else - rval &= ~MCS_SIR16US; /* 3/16 bit time pulse width */ - - /* make sure ask mode and back to back packets are off. */ - rval &= ~(MCS_BBTG | MCS_ASK); - - rval &= ~MCS_SPEED_MASK; - rval |= MCS_SPEED_9600; /* make sure initial speed is 9600. */ - mcs->speed = 9600; - mcs->new_speed = 0; /* new_speed is set to 0 */ - rval &= ~MCS_PLLPWDN; /* disable power down. */ - - /* make sure device determines direction and that the auto send sip - * pulse are on. - */ - rval |= MCS_DTD | MCS_SIPEN; - - ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); - if(unlikely(ret)) - goto error; - - msg = "transceiver model specific setup error"; - switch (mcs->transceiver_type) { - case MCS_TSC_VISHAY: - ret = mcs_setup_transceiver_vishay(mcs); - break; - - case MCS_TSC_SHARP: - ret = mcs_setup_transceiver_sharp(mcs); - break; - - case MCS_TSC_AGILENT: - ret = mcs_setup_transceiver_agilent(mcs); - break; - - default: - net_warn_ratelimited("Unknown transceiver type: %d\n", - mcs->transceiver_type); - ret = 1; - } - if (unlikely(ret)) - goto error; - - /* If transceiver is not SHARP, then if receive mode set - * on the RXFAST bit in the XCVR Register otherwise unset it - */ - if (mcs->transceiver_type != MCS_TSC_SHARP) { - - ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval); - if (unlikely(ret != 2)) - goto error; - if (mcs->receive_mode) - rval |= MCS_RXFAST; - else - rval &= ~MCS_RXFAST; - ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); - if (unlikely(ret)) - goto error; - } - - msg = "transceiver reset"; - - ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); - if (unlikely(ret != 2)) - goto error; - - /* reset the mcs7780 so all changes take effect. */ - rval &= ~MCS_RESET; - ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); - if (unlikely(ret)) - goto error; - else - return ret; - -error: - net_err_ratelimited("%s\n", msg); - return ret; -} - -/* Wraps the data in format for SIR */ -static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf) -{ - int wraplen; - - /* 2: full frame length, including "the length" */ - wraplen = async_wrap_skb(skb, buf + 2, 4094); - - wraplen += 2; - buf[0] = wraplen & 0xff; - buf[1] = (wraplen >> 8) & 0xff; - - return wraplen; -} - -/* Wraps the data in format for FIR */ -static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf) -{ - unsigned int len = 0; - __u32 fcs = ~(crc32_le(~0, skb->data, skb->len)); - - /* add 2 bytes for length value and 4 bytes for fcs. */ - len = skb->len + 6; - - /* The mcs7780 requires that the first two bytes are the packet - * length in little endian order. Note: the length value includes - * the two bytes for the length value itself. - */ - buf[0] = len & 0xff; - buf[1] = (len >> 8) & 0xff; - /* copy the data into the tx buffer. */ - skb_copy_from_linear_data(skb, buf + 2, skb->len); - /* put the fcs in the last four bytes in little endian order. */ - buf[len - 4] = fcs & 0xff; - buf[len - 3] = (fcs >> 8) & 0xff; - buf[len - 2] = (fcs >> 16) & 0xff; - buf[len - 1] = (fcs >> 24) & 0xff; - - return len; -} - -/* Wraps the data in format for MIR */ -static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf) -{ - __u16 fcs = 0; - int len = skb->len + 4; - - fcs = ~(irda_calc_crc16(~fcs, skb->data, skb->len)); - /* put the total packet length in first. Note: packet length - * value includes the two bytes that hold the packet length - * itself. - */ - buf[0] = len & 0xff; - buf[1] = (len >> 8) & 0xff; - /* copy the data */ - skb_copy_from_linear_data(skb, buf + 2, skb->len); - /* put the fcs in last two bytes in little endian order. */ - buf[len - 2] = fcs & 0xff; - buf[len - 1] = (fcs >> 8) & 0xff; - - return len; -} - -/* Unwrap received packets at MIR speed. A 16 bit crc_ccitt checksum is - * used for the fcs. When performed over the entire packet the result - * should be GOOD_FCS = 0xf0b8. Hands the unwrapped data off to the IrDA - * layer via a sk_buff. - */ -static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len) -{ - __u16 fcs; - int new_len; - struct sk_buff *skb; - - /* Assume that the frames are going to fill a single packet - * rather than span multiple packets. - */ - - new_len = len - 2; - if(unlikely(new_len <= 0)) { - net_err_ratelimited("%s short frame length %d\n", - mcs->netdev->name, new_len); - ++mcs->netdev->stats.rx_errors; - ++mcs->netdev->stats.rx_length_errors; - return; - } - fcs = 0; - fcs = irda_calc_crc16(~fcs, buf, len); - - if(fcs != GOOD_FCS) { - net_err_ratelimited("crc error calc 0x%x len %d\n", - fcs, new_len); - mcs->netdev->stats.rx_errors++; - mcs->netdev->stats.rx_crc_errors++; - return; - } - - skb = dev_alloc_skb(new_len + 1); - if(unlikely(!skb)) { - ++mcs->netdev->stats.rx_dropped; - return; - } - - skb_reserve(skb, 1); - skb_copy_to_linear_data(skb, buf, new_len); - skb_put(skb, new_len); - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - skb->dev = mcs->netdev; - - netif_rx(skb); - - mcs->netdev->stats.rx_packets++; - mcs->netdev->stats.rx_bytes += new_len; -} - -/* Unwrap received packets at FIR speed. A 32 bit crc_ccitt checksum is - * used for the fcs. Hands the unwrapped data off to the IrDA - * layer via a sk_buff. - */ -static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len) -{ - __u32 fcs; - int new_len; - struct sk_buff *skb; - - /* Assume that the frames are going to fill a single packet - * rather than span multiple packets. This is most likely a false - * assumption. - */ - - new_len = len - 4; - if(unlikely(new_len <= 0)) { - net_err_ratelimited("%s short frame length %d\n", - mcs->netdev->name, new_len); - ++mcs->netdev->stats.rx_errors; - ++mcs->netdev->stats.rx_length_errors; - return; - } - - fcs = ~(crc32_le(~0, buf, new_len)); - if(fcs != get_unaligned_le32(buf + new_len)) { - net_err_ratelimited("crc error calc 0x%x len %d\n", - fcs, new_len); - mcs->netdev->stats.rx_errors++; - mcs->netdev->stats.rx_crc_errors++; - return; - } - - skb = dev_alloc_skb(new_len + 1); - if(unlikely(!skb)) { - ++mcs->netdev->stats.rx_dropped; - return; - } - - skb_reserve(skb, 1); - skb_copy_to_linear_data(skb, buf, new_len); - skb_put(skb, new_len); - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - skb->dev = mcs->netdev; - - netif_rx(skb); - - mcs->netdev->stats.rx_packets++; - mcs->netdev->stats.rx_bytes += new_len; -} - - -/* Allocates urbs for both receive and transmit. - * If alloc fails return error code 0 (fail) otherwise - * return error code 1 (success). - */ -static inline int mcs_setup_urbs(struct mcs_cb *mcs) -{ - mcs->rx_urb = NULL; - - mcs->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mcs->tx_urb) - return 0; - - mcs->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mcs->rx_urb) { - usb_free_urb(mcs->tx_urb); - mcs->tx_urb = NULL; - return 0; - } - - return 1; -} - -/* Sets up state to be initially outside frame, gets receive urb, - * sets status to successful and then submits the urb to start - * receiving the data. - */ -static inline int mcs_receive_start(struct mcs_cb *mcs) -{ - mcs->rx_buff.in_frame = FALSE; - mcs->rx_buff.state = OUTSIDE_FRAME; - - usb_fill_bulk_urb(mcs->rx_urb, mcs->usbdev, - usb_rcvbulkpipe(mcs->usbdev, mcs->ep_in), - mcs->in_buf, 4096, mcs_receive_irq, mcs); - - mcs->rx_urb->status = 0; - return usb_submit_urb(mcs->rx_urb, GFP_KERNEL); -} - -/* Finds the in and out endpoints for the mcs control block */ -static inline int mcs_find_endpoints(struct mcs_cb *mcs, - struct usb_host_endpoint *ep, int epnum) -{ - int i; - int ret = 0; - - /* If no place to store the endpoints just return */ - if (!ep) - return ret; - - /* cycle through all endpoints, find the first two that are DIR_IN */ - for (i = 0; i < epnum; i++) { - if (ep[i].desc.bEndpointAddress & USB_DIR_IN) - mcs->ep_in = ep[i].desc.bEndpointAddress; - else - mcs->ep_out = ep[i].desc.bEndpointAddress; - - /* MosChip says that the chip has only two bulk - * endpoints. Find one for each direction and move on. - */ - if ((mcs->ep_in != 0) && (mcs->ep_out != 0)) { - ret = 1; - break; - } - } - - return ret; -} - -static void mcs_speed_work(struct work_struct *work) -{ - struct mcs_cb *mcs = container_of(work, struct mcs_cb, work); - struct net_device *netdev = mcs->netdev; - - mcs_speed_change(mcs); - netif_wake_queue(netdev); -} - -/* Function to change the speed of the mcs7780. Fully supports SIR, - * MIR, and FIR speeds. - */ -static int mcs_speed_change(struct mcs_cb *mcs) -{ - int ret = 0; - int rst = 0; - int cnt = 0; - __u16 nspeed; - __u16 rval; - - nspeed = mcs_speed_set[(mcs->new_speed >> 8) & 0x0f]; - - do { - mcs_get_reg(mcs, MCS_RESV_REG, &rval); - } while(cnt++ < 100 && (rval & MCS_IRINTX)); - - if (cnt > 100) { - net_err_ratelimited("unable to change speed\n"); - ret = -EIO; - goto error; - } - - mcs_get_reg(mcs, MCS_MODE_REG, &rval); - - /* MINRXPW values recommended by MosChip */ - if (mcs->new_speed <= 115200) { - rval &= ~MCS_FIR; - - rst = mcs->speed > 115200; - if (rst) - mcs_set_reg(mcs, MCS_MINRXPW_REG, 0); - - } else if (mcs->new_speed <= 1152000) { - rval &= ~MCS_FIR; - - rst = !(mcs->speed == 576000 || mcs->speed == 1152000); - if (rst) - mcs_set_reg(mcs, MCS_MINRXPW_REG, 5); - - } else { - rval |= MCS_FIR; - - rst = mcs->speed != 4000000; - if (rst) - mcs_set_reg(mcs, MCS_MINRXPW_REG, 5); - - } - - rval &= ~MCS_SPEED_MASK; - rval |= nspeed; - - ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); - if (unlikely(ret)) - goto error; - - if (rst) - switch (mcs->transceiver_type) { - case MCS_TSC_VISHAY: - ret = mcs_setup_transceiver_vishay(mcs); - break; - - case MCS_TSC_SHARP: - ret = mcs_setup_transceiver_sharp(mcs); - break; - - case MCS_TSC_AGILENT: - ret = mcs_setup_transceiver_agilent(mcs); - break; - - default: - ret = 1; - net_warn_ratelimited("Unknown transceiver type: %d\n", - mcs->transceiver_type); - } - if (unlikely(ret)) - goto error; - - mcs_get_reg(mcs, MCS_MODE_REG, &rval); - rval &= ~MCS_RESET; - ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); - - mcs->speed = mcs->new_speed; -error: - mcs->new_speed = 0; - return ret; -} - -/* Ioctl calls not supported at this time. Can be an area of future work. */ -static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -{ - /* struct if_irda_req *irq = (struct if_irda_req *)rq; */ - /* struct mcs_cb *mcs = netdev_priv(netdev); */ - int ret = 0; - - switch (cmd) { - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -/* Network device is taken down, done by "ifconfig irda0 down" */ -static int mcs_net_close(struct net_device *netdev) -{ - int ret = 0; - struct mcs_cb *mcs = netdev_priv(netdev); - - /* Stop transmit processing */ - netif_stop_queue(netdev); - - kfree_skb(mcs->rx_buff.skb); - - /* kill and free the receive and transmit URBs */ - usb_kill_urb(mcs->rx_urb); - usb_free_urb(mcs->rx_urb); - usb_kill_urb(mcs->tx_urb); - usb_free_urb(mcs->tx_urb); - - /* Stop and remove instance of IrLAP */ - if (mcs->irlap) - irlap_close(mcs->irlap); - - mcs->irlap = NULL; - return ret; -} - -/* Network device is taken up, done by "ifconfig irda0 up" */ -static int mcs_net_open(struct net_device *netdev) -{ - struct mcs_cb *mcs = netdev_priv(netdev); - char hwname[16]; - int ret = 0; - - ret = usb_clear_halt(mcs->usbdev, - usb_sndbulkpipe(mcs->usbdev, mcs->ep_in)); - if (ret) - goto error1; - ret = usb_clear_halt(mcs->usbdev, - usb_rcvbulkpipe(mcs->usbdev, mcs->ep_out)); - if (ret) - goto error1; - - ret = mcs_setup_transceiver(mcs); - if (ret) - goto error1; - - ret = -ENOMEM; - - /* Initialize for SIR/FIR to copy data directly into skb. */ - mcs->receiving = 0; - mcs->rx_buff.truesize = IRDA_SKB_MAX_MTU; - mcs->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!mcs->rx_buff.skb) - goto error1; - - skb_reserve(mcs->rx_buff.skb, 1); - mcs->rx_buff.head = mcs->rx_buff.skb->data; - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - * Note : will send immediately a speed change... - */ - sprintf(hwname, "usb#%d", mcs->usbdev->devnum); - mcs->irlap = irlap_open(netdev, &mcs->qos, hwname); - if (!mcs->irlap) { - net_err_ratelimited("mcs7780: irlap_open failed\n"); - goto error2; - } - - if (!mcs_setup_urbs(mcs)) - goto error3; - - ret = mcs_receive_start(mcs); - if (ret) - goto error4; - - netif_start_queue(netdev); - return 0; - -error4: - usb_free_urb(mcs->rx_urb); - usb_free_urb(mcs->tx_urb); -error3: - irlap_close(mcs->irlap); -error2: - kfree_skb(mcs->rx_buff.skb); -error1: - return ret; -} - -/* Receive callback function. */ -static void mcs_receive_irq(struct urb *urb) -{ - __u8 *bytes; - struct mcs_cb *mcs = urb->context; - int i; - int ret; - - if (!netif_running(mcs->netdev)) - return; - - if (urb->status) - return; - - if (urb->actual_length > 0) { - bytes = urb->transfer_buffer; - - /* MCS returns frames without BOF and EOF - * I assume it returns whole frames. - */ - /* SIR speed */ - if(mcs->speed < 576000) { - async_unwrap_char(mcs->netdev, &mcs->netdev->stats, - &mcs->rx_buff, 0xc0); - - for (i = 0; i < urb->actual_length; i++) - async_unwrap_char(mcs->netdev, &mcs->netdev->stats, - &mcs->rx_buff, bytes[i]); - - async_unwrap_char(mcs->netdev, &mcs->netdev->stats, - &mcs->rx_buff, 0xc1); - } - /* MIR speed */ - else if(mcs->speed == 576000 || mcs->speed == 1152000) { - mcs_unwrap_mir(mcs, urb->transfer_buffer, - urb->actual_length); - } - /* FIR speed */ - else { - mcs_unwrap_fir(mcs, urb->transfer_buffer, - urb->actual_length); - } - } - - ret = usb_submit_urb(urb, GFP_ATOMIC); -} - -/* Transmit callback function. */ -static void mcs_send_irq(struct urb *urb) -{ - struct mcs_cb *mcs = urb->context; - struct net_device *ndev = mcs->netdev; - - if (unlikely(mcs->new_speed)) - schedule_work(&mcs->work); - else - netif_wake_queue(ndev); -} - -/* Transmit callback function. */ -static netdev_tx_t mcs_hard_xmit(struct sk_buff *skb, - struct net_device *ndev) -{ - unsigned long flags; - struct mcs_cb *mcs; - int wraplen; - int ret = 0; - - netif_stop_queue(ndev); - mcs = netdev_priv(ndev); - - spin_lock_irqsave(&mcs->lock, flags); - - mcs->new_speed = irda_get_next_speed(skb); - if (likely(mcs->new_speed == mcs->speed)) - mcs->new_speed = 0; - - /* SIR speed */ - if(mcs->speed < 576000) { - wraplen = mcs_wrap_sir_skb(skb, mcs->out_buf); - } - /* MIR speed */ - else if(mcs->speed == 576000 || mcs->speed == 1152000) { - wraplen = mcs_wrap_mir_skb(skb, mcs->out_buf); - } - /* FIR speed */ - else { - wraplen = mcs_wrap_fir_skb(skb, mcs->out_buf); - } - usb_fill_bulk_urb(mcs->tx_urb, mcs->usbdev, - usb_sndbulkpipe(mcs->usbdev, mcs->ep_out), - mcs->out_buf, wraplen, mcs_send_irq, mcs); - - if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) { - net_err_ratelimited("failed tx_urb: %d\n", ret); - switch (ret) { - case -ENODEV: - case -EPIPE: - break; - default: - mcs->netdev->stats.tx_errors++; - netif_start_queue(ndev); - } - } else { - mcs->netdev->stats.tx_packets++; - mcs->netdev->stats.tx_bytes += skb->len; - } - - dev_kfree_skb(skb); - spin_unlock_irqrestore(&mcs->lock, flags); - return NETDEV_TX_OK; -} - -static const struct net_device_ops mcs_netdev_ops = { - .ndo_open = mcs_net_open, - .ndo_stop = mcs_net_close, - .ndo_start_xmit = mcs_hard_xmit, - .ndo_do_ioctl = mcs_net_ioctl, -}; - -/* - * This function is called by the USB subsystem for each new device in the - * system. Need to verify the device and if it is, then start handling it. - */ -static int mcs_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct net_device *ndev = NULL; - struct mcs_cb *mcs; - int ret = -ENOMEM; - - ndev = alloc_irdadev(sizeof(*mcs)); - if (!ndev) - goto error1; - - pr_debug("MCS7780 USB-IrDA bridge found at %d.\n", udev->devnum); - - SET_NETDEV_DEV(ndev, &intf->dev); - - ret = usb_reset_configuration(udev); - if (ret != 0) { - net_err_ratelimited("mcs7780: usb reset configuration failed\n"); - goto error2; - } - - mcs = netdev_priv(ndev); - mcs->usbdev = udev; - mcs->netdev = ndev; - spin_lock_init(&mcs->lock); - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&mcs->qos); - - /* That's the Rx capability. */ - mcs->qos.baud_rate.bits &= - IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 - | IR_576000 | IR_1152000 | (IR_4000000 << 8); - - - mcs->qos.min_turn_time.bits &= qos_mtt_bits; - irda_qos_bits_to_value(&mcs->qos); - - /* Speed change work initialisation*/ - INIT_WORK(&mcs->work, mcs_speed_work); - - ndev->netdev_ops = &mcs_netdev_ops; - - if (!intf->cur_altsetting) { - ret = -ENOMEM; - goto error2; - } - - ret = mcs_find_endpoints(mcs, intf->cur_altsetting->endpoint, - intf->cur_altsetting->desc.bNumEndpoints); - if (!ret) { - ret = -ENODEV; - goto error2; - } - - ret = register_netdev(ndev); - if (ret != 0) - goto error2; - - pr_debug("IrDA: Registered MosChip MCS7780 device as %s\n", - ndev->name); - - mcs->transceiver_type = transceiver_type; - mcs->sir_tweak = sir_tweak; - mcs->receive_mode = receive_mode; - - usb_set_intfdata(intf, mcs); - return 0; - -error2: - free_netdev(ndev); - -error1: - return ret; -} - -/* The current device is removed, the USB layer tells us to shut down. */ -static void mcs_disconnect(struct usb_interface *intf) -{ - struct mcs_cb *mcs = usb_get_intfdata(intf); - - if (!mcs) - return; - - cancel_work_sync(&mcs->work); - - unregister_netdev(mcs->netdev); - free_netdev(mcs->netdev); - - usb_set_intfdata(intf, NULL); - pr_debug("MCS7780 now disconnected.\n"); -} - -module_usb_driver(mcs_driver); diff --git a/drivers/staging/irda/drivers/mcs7780.h b/drivers/staging/irda/drivers/mcs7780.h deleted file mode 100644 index a6e8f7dbafc9..000000000000 --- a/drivers/staging/irda/drivers/mcs7780.h +++ /dev/null @@ -1,165 +0,0 @@ -/***************************************************************************** -* -* Filename: mcs7780.h -* Version: 0.2-alpha -* Description: Irda MosChip USB Dongle -* Status: Experimental -* Authors: Lukasz Stelmach -* Brian Pugh -* -* Copyright (C) 2005, Lukasz Stelmach -* Copyright (C) 2005, Brian Pugh -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ -#ifndef _MCS7780_H -#define _MCS7780_H - -#define MCS_MODE_SIR 0 -#define MCS_MODE_MIR 1 -#define MCS_MODE_FIR 2 - -#define MCS_CTRL_TIMEOUT 500 -#define MCS_XMIT_TIMEOUT 500 -/* Possible transceiver types */ -#define MCS_TSC_VISHAY 0 /* Vishay TFD, default choice */ -#define MCS_TSC_AGILENT 1 /* Agilent 3602/3600 */ -#define MCS_TSC_SHARP 2 /* Sharp GP2W1000YP */ - -/* Requests */ -#define MCS_RD_RTYPE 0xC0 -#define MCS_WR_RTYPE 0x40 -#define MCS_RDREQ 0x0F -#define MCS_WRREQ 0x0E - -/* Register 0x00 */ -#define MCS_MODE_REG 0 -#define MCS_FIR ((__u16)0x0001) -#define MCS_SIR16US ((__u16)0x0002) -#define MCS_BBTG ((__u16)0x0004) -#define MCS_ASK ((__u16)0x0008) -#define MCS_PARITY ((__u16)0x0010) - -/* SIR/MIR speed constants */ -#define MCS_SPEED_SHIFT 5 -#define MCS_SPEED_MASK ((__u16)0x00E0) -#define MCS_SPEED(x) ((x & MCS_SPEED_MASK) >> MCS_SPEED_SHIFT) -#define MCS_SPEED_2400 ((0 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_9600 ((1 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_19200 ((2 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_38400 ((3 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_57600 ((4 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_115200 ((5 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_576000 ((6 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) -#define MCS_SPEED_1152000 ((7 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) - -#define MCS_PLLPWDN ((__u16)0x0100) -#define MCS_DRIVER ((__u16)0x0200) -#define MCS_DTD ((__u16)0x0400) -#define MCS_DIR ((__u16)0x0800) -#define MCS_SIPEN ((__u16)0x1000) -#define MCS_SENDSIP ((__u16)0x2000) -#define MCS_CHGDIR ((__u16)0x4000) -#define MCS_RESET ((__u16)0x8000) - -/* Register 0x02 */ -#define MCS_XCVR_REG 2 -#define MCS_MODE0 ((__u16)0x0001) -#define MCS_STFIR ((__u16)0x0002) -#define MCS_XCVR_CONF ((__u16)0x0004) -#define MCS_RXFAST ((__u16)0x0008) -/* TXCUR [6:4] */ -#define MCS_TXCUR_SHIFT 4 -#define MCS_TXCUR_MASK ((__u16)0x0070) -#define MCS_TXCUR(x) ((x & MCS_TXCUR_MASK) >> MCS_TXCUR_SHIFT) -#define MCS_SETTXCUR(x,y) \ - ((x & ~MCS_TXCUR_MASK) | (y << MCS_TXCUR_SHIFT) & MCS_TXCUR_MASK) - -#define MCS_MODE1 ((__u16)0x0080) -#define MCS_SMODE0 ((__u16)0x0100) -#define MCS_SMODE1 ((__u16)0x0200) -#define MCS_INVTX ((__u16)0x0400) -#define MCS_INVRX ((__u16)0x0800) - -#define MCS_MINRXPW_REG 4 - -#define MCS_RESV_REG 7 -#define MCS_IRINTX ((__u16)0x0001) -#define MCS_IRINRX ((__u16)0x0002) - -struct mcs_cb { - struct usb_device *usbdev; /* init: probe_irda */ - struct net_device *netdev; /* network layer */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; - unsigned int speed; /* Current speed */ - unsigned int new_speed; /* new speed */ - - struct work_struct work; /* Change speed work */ - - struct sk_buff *tx_pending; - char in_buf[4096]; /* transmit/receive buffer */ - char out_buf[4096]; /* transmit/receive buffer */ - __u8 *fifo_status; - - iobuff_t rx_buff; /* receive unwrap state machine */ - spinlock_t lock; - int receiving; - - __u8 ep_in; - __u8 ep_out; - - struct urb *rx_urb; - struct urb *tx_urb; - - int transceiver_type; - int sir_tweak; - int receive_mode; -}; - -static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val); -static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val); - -static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs); -static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs); -static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs); -static inline int mcs_setup_transceiver(struct mcs_cb *mcs); -static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf); -static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf); -static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf); -static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len); -static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len); -static inline int mcs_setup_urbs(struct mcs_cb *mcs); -static inline int mcs_receive_start(struct mcs_cb *mcs); -static inline int mcs_find_endpoints(struct mcs_cb *mcs, - struct usb_host_endpoint *ep, int epnum); - -static int mcs_speed_change(struct mcs_cb *mcs); - -static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd); -static int mcs_net_close(struct net_device *netdev); -static int mcs_net_open(struct net_device *netdev); - -static void mcs_receive_irq(struct urb *urb); -static void mcs_send_irq(struct urb *urb); -static netdev_tx_t mcs_hard_xmit(struct sk_buff *skb, - struct net_device *netdev); - -static int mcs_probe(struct usb_interface *intf, - const struct usb_device_id *id); -static void mcs_disconnect(struct usb_interface *intf); - -#endif /* _MCS7780_H */ diff --git a/drivers/staging/irda/drivers/nsc-ircc.c b/drivers/staging/irda/drivers/nsc-ircc.c deleted file mode 100644 index 7beae147be11..000000000000 --- a/drivers/staging/irda/drivers/nsc-ircc.c +++ /dev/null @@ -1,2410 +0,0 @@ -/********************************************************************* - * - * Filename: nsc-ircc.c - * Version: 1.0 - * Description: Driver for the NSC PC'108 and PC'338 IrDA chipsets - * Status: Stable. - * Author: Dag Brattli - * Created at: Sat Nov 7 21:43:15 1998 - * Modified at: Wed Mar 1 11:29:34 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli - * Copyright (c) 1998 Lichen Wang, - * Copyright (c) 1998 Actisys Corp., www.actisys.com - * Copyright (c) 2000-2004 Jean Tourrilhes - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - * Notice that all functions that needs to access the chip in _any_ - * way, must save BSR register on entry, and restore it on exit. - * It is _very_ important to follow this policy! - * - * __u8 bank; - * - * bank = inb(iobase+BSR); - * - * do_your_stuff_here(); - * - * outb(bank, iobase+BSR); - * - * If you find bugs in this file, its very likely that the same bug - * will also be in w83977af_ir.c since the implementations are quite - * similar. - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "nsc-ircc.h" - -#define CHIP_IO_EXTENT 8 -#define BROKEN_DONGLE_ID - -static char *driver_name = "nsc-ircc"; - -/* Power Management */ -#define NSC_IRCC_DRIVER_NAME "nsc-ircc" -static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state); -static int nsc_ircc_resume(struct platform_device *dev); - -static struct platform_driver nsc_ircc_driver = { - .suspend = nsc_ircc_suspend, - .resume = nsc_ircc_resume, - .driver = { - .name = NSC_IRCC_DRIVER_NAME, - }, -}; - -/* Module parameters */ -static int qos_mtt_bits = 0x07; /* 1 ms or more */ -static int dongle_id; - -/* Use BIOS settions by default, but user may supply module parameters */ -static unsigned int io[] = { ~0, ~0, ~0, ~0, ~0 }; -static unsigned int irq[] = { 0, 0, 0, 0, 0 }; -static unsigned int dma[] = { 0, 0, 0, 0, 0 }; - -static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info); -static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info); -static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info); -static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info); -static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info); -static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info); -#ifdef CONFIG_PNP -static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id); -#endif - -/* These are the known NSC chips */ -static nsc_chip_t chips[] = { -/* Name, {cfg registers}, chip id index reg, chip id expected value, revision mask */ - { "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0, - nsc_ircc_probe_108, nsc_ircc_init_108 }, - { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8, - nsc_ircc_probe_338, nsc_ircc_init_338 }, - /* Contributed by Steffen Pingel - IBM X40 */ - { "PC8738x", { 0x164e, 0x4e, 0x2e }, 0x20, 0xf4, 0xff, - nsc_ircc_probe_39x, nsc_ircc_init_39x }, - /* Contributed by Jan Frey - IBM A30/A31 */ - { "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff, - nsc_ircc_probe_39x, nsc_ircc_init_39x }, - /* IBM ThinkPads using PC8738x (T60/X60/Z60) */ - { "IBM-PC8738x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff, - nsc_ircc_probe_39x, nsc_ircc_init_39x }, - /* IBM ThinkPads using PC8394T (T43/R52/?) */ - { "IBM-PC8394T", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf9, 0xff, - nsc_ircc_probe_39x, nsc_ircc_init_39x }, - { NULL } -}; - -static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL, NULL }; - -static char *dongle_types[] = { - "Differential serial interface", - "Differential serial interface", - "Reserved", - "Reserved", - "Sharp RY5HD01", - "Reserved", - "Single-ended serial interface", - "Consumer-IR only", - "HP HSDL-2300, HP HSDL-3600/HSDL-3610", - "IBM31T1100 or Temic TFDS6000/TFDS6500", - "Reserved", - "Reserved", - "HP HSDL-1100/HSDL-2100", - "HP HSDL-1100/HSDL-2100", - "Supports SIR Mode only", - "No dongle connected", -}; - -/* PNP probing */ -static chipio_t pnp_info; -static const struct pnp_device_id nsc_ircc_pnp_table[] = { - { .id = "NSC6001", .driver_data = 0 }, - { .id = "HWPC224", .driver_data = 0 }, - { .id = "IBM0071", .driver_data = NSC_FORCE_DONGLE_TYPE9 }, - { } -}; - -MODULE_DEVICE_TABLE(pnp, nsc_ircc_pnp_table); - -static struct pnp_driver nsc_ircc_pnp_driver = { -#ifdef CONFIG_PNP - .name = "nsc-ircc", - .id_table = nsc_ircc_pnp_table, - .probe = nsc_ircc_pnp_probe, -#endif -}; - -/* Some prototypes */ -static int nsc_ircc_open(chipio_t *info); -static int nsc_ircc_close(struct nsc_ircc_cb *self); -static int nsc_ircc_setup(chipio_t *info); -static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self); -static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self); -static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase); -static netdev_tx_t nsc_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev); -static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev); -static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size); -static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase); -static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 baud); -static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self); -static int nsc_ircc_read_dongle_id (int iobase); -static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id); - -static int nsc_ircc_net_open(struct net_device *dev); -static int nsc_ircc_net_close(struct net_device *dev); -static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); - -/* Globals */ -static int pnp_registered; -static int pnp_succeeded; - -/* - * Function nsc_ircc_init () - * - * Initialize chip. Just try to find out how many chips we are dealing with - * and where they are - */ -static int __init nsc_ircc_init(void) -{ - chipio_t info; - nsc_chip_t *chip; - int ret; - int cfg_base; - int cfg, id; - int reg; - int i = 0; - - ret = platform_driver_register(&nsc_ircc_driver); - if (ret) { - net_err_ratelimited("%s, Can't register driver!\n", - driver_name); - return ret; - } - - /* Register with PnP subsystem to detect disable ports */ - ret = pnp_register_driver(&nsc_ircc_pnp_driver); - - if (!ret) - pnp_registered = 1; - - ret = -ENODEV; - - /* Probe for all the NSC chipsets we know about */ - for (chip = chips; chip->name ; chip++) { - pr_debug("%s(), Probing for %s ...\n", __func__, - chip->name); - - /* Try all config registers for this chip */ - for (cfg = 0; cfg < ARRAY_SIZE(chip->cfg); cfg++) { - cfg_base = chip->cfg[cfg]; - if (!cfg_base) - continue; - - /* Read index register */ - reg = inb(cfg_base); - if (reg == 0xff) { - pr_debug("%s() no chip at 0x%03x\n", - __func__, cfg_base); - continue; - } - - /* Read chip identification register */ - outb(chip->cid_index, cfg_base); - id = inb(cfg_base+1); - if ((id & chip->cid_mask) == chip->cid_value) { - pr_debug("%s() Found %s chip, revision=%d\n", - __func__, chip->name, - id & ~chip->cid_mask); - - /* - * If we found a correct PnP setting, - * we first try it. - */ - if (pnp_succeeded) { - memset(&info, 0, sizeof(chipio_t)); - info.cfg_base = cfg_base; - info.fir_base = pnp_info.fir_base; - info.dma = pnp_info.dma; - info.irq = pnp_info.irq; - - if (info.fir_base < 0x2000) { - net_info_ratelimited("%s, chip->init\n", - driver_name); - chip->init(chip, &info); - } else - chip->probe(chip, &info); - - if (nsc_ircc_open(&info) >= 0) - ret = 0; - } - - /* - * Opening based on PnP values failed. - * Let's fallback to user values, or probe - * the chip. - */ - if (ret) { - pr_debug("%s, PnP init failed\n", - driver_name); - memset(&info, 0, sizeof(chipio_t)); - info.cfg_base = cfg_base; - info.fir_base = io[i]; - info.dma = dma[i]; - info.irq = irq[i]; - - /* - * If the user supplies the base address, then - * we init the chip, if not we probe the values - * set by the BIOS - */ - if (io[i] < 0x2000) { - chip->init(chip, &info); - } else - chip->probe(chip, &info); - - if (nsc_ircc_open(&info) >= 0) - ret = 0; - } - i++; - } else { - pr_debug("%s(), Wrong chip id=0x%02x\n", - __func__, id); - } - } - } - - if (ret) { - platform_driver_unregister(&nsc_ircc_driver); - pnp_unregister_driver(&nsc_ircc_pnp_driver); - pnp_registered = 0; - } - - return ret; -} - -/* - * Function nsc_ircc_cleanup () - * - * Close all configured chips - * - */ -static void __exit nsc_ircc_cleanup(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dev_self); i++) { - if (dev_self[i]) - nsc_ircc_close(dev_self[i]); - } - - platform_driver_unregister(&nsc_ircc_driver); - - if (pnp_registered) - pnp_unregister_driver(&nsc_ircc_pnp_driver); - - pnp_registered = 0; -} - -static const struct net_device_ops nsc_ircc_sir_ops = { - .ndo_open = nsc_ircc_net_open, - .ndo_stop = nsc_ircc_net_close, - .ndo_start_xmit = nsc_ircc_hard_xmit_sir, - .ndo_do_ioctl = nsc_ircc_net_ioctl, -}; - -static const struct net_device_ops nsc_ircc_fir_ops = { - .ndo_open = nsc_ircc_net_open, - .ndo_stop = nsc_ircc_net_close, - .ndo_start_xmit = nsc_ircc_hard_xmit_fir, - .ndo_do_ioctl = nsc_ircc_net_ioctl, -}; - -/* - * Function nsc_ircc_open (iobase, irq) - * - * Open driver instance - * - */ -static int __init nsc_ircc_open(chipio_t *info) -{ - struct net_device *dev; - struct nsc_ircc_cb *self; - void *ret; - int err, chip_index; - - for (chip_index = 0; chip_index < ARRAY_SIZE(dev_self); chip_index++) { - if (!dev_self[chip_index]) - break; - } - - if (chip_index == ARRAY_SIZE(dev_self)) { - net_err_ratelimited("%s(), maximum number of supported chips reached!\n", - __func__); - return -ENOMEM; - } - - net_info_ratelimited("%s, Found chip at base=0x%03x\n", - driver_name, info->cfg_base); - - if ((nsc_ircc_setup(info)) == -1) - return -1; - - net_info_ratelimited("%s, driver loaded (Dag Brattli)\n", driver_name); - - dev = alloc_irdadev(sizeof(struct nsc_ircc_cb)); - if (dev == NULL) { - net_err_ratelimited("%s(), can't allocate memory for control block!\n", - __func__); - return -ENOMEM; - } - - self = netdev_priv(dev); - self->netdev = dev; - spin_lock_init(&self->lock); - - /* Need to store self somewhere */ - dev_self[chip_index] = self; - self->index = chip_index; - - /* Initialize IO */ - self->io.cfg_base = info->cfg_base; - self->io.fir_base = info->fir_base; - self->io.irq = info->irq; - self->io.fir_ext = CHIP_IO_EXTENT; - self->io.dma = info->dma; - self->io.fifo_size = 32; - - /* Reserve the ioports that we need */ - ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name); - if (!ret) { - net_warn_ratelimited("%s(), can't get iobase of 0x%03x\n", - __func__, self->io.fir_base); - err = -ENODEV; - goto out1; - } - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - /* The only value we must override it the baudrate */ - self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| - IR_115200|IR_576000|IR_1152000 |(IR_4000000 << 8); - - self->qos.min_turn_time.bits = qos_mtt_bits; - irda_qos_bits_to_value(&self->qos); - - /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ - self->rx_buff.truesize = 14384; - self->tx_buff.truesize = 14384; - - /* Allocate memory if needed */ - self->rx_buff.head = - dma_zalloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL); - if (self->rx_buff.head == NULL) { - err = -ENOMEM; - goto out2; - - } - - self->tx_buff.head = - dma_zalloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL); - if (self->tx_buff.head == NULL) { - err = -ENOMEM; - goto out3; - } - - self->rx_buff.in_frame = FALSE; - self->rx_buff.state = OUTSIDE_FRAME; - self->tx_buff.data = self->tx_buff.head; - self->rx_buff.data = self->rx_buff.head; - - /* Reset Tx queue info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - - /* Override the network functions we need to use */ - dev->netdev_ops = &nsc_ircc_sir_ops; - - err = register_netdev(dev); - if (err) { - net_err_ratelimited("%s(), register_netdev() failed!\n", - __func__); - goto out4; - } - net_info_ratelimited("IrDA: Registered device %s\n", dev->name); - - /* Check if user has supplied a valid dongle id or not */ - if ((dongle_id <= 0) || - (dongle_id >= ARRAY_SIZE(dongle_types))) { - dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base); - - net_info_ratelimited("%s, Found dongle: %s\n", - driver_name, dongle_types[dongle_id]); - } else { - net_info_ratelimited("%s, Using dongle: %s\n", - driver_name, dongle_types[dongle_id]); - } - - self->io.dongle_id = dongle_id; - nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id); - - self->pldev = platform_device_register_simple(NSC_IRCC_DRIVER_NAME, - self->index, NULL, 0); - if (IS_ERR(self->pldev)) { - err = PTR_ERR(self->pldev); - goto out5; - } - platform_set_drvdata(self->pldev, self); - - return chip_index; - - out5: - unregister_netdev(dev); - out4: - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - out3: - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - out2: - release_region(self->io.fir_base, self->io.fir_ext); - out1: - free_netdev(dev); - dev_self[chip_index] = NULL; - return err; -} - -/* - * Function nsc_ircc_close (self) - * - * Close driver instance - * - */ -static int __exit nsc_ircc_close(struct nsc_ircc_cb *self) -{ - int iobase; - - IRDA_ASSERT(self != NULL, return -1;); - - iobase = self->io.fir_base; - - platform_device_unregister(self->pldev); - - /* Remove netdevice */ - unregister_netdev(self->netdev); - - /* Release the PORT that this driver is using */ - pr_debug("%s(), Releasing Region %03x\n", - __func__, self->io.fir_base); - release_region(self->io.fir_base, self->io.fir_ext); - - if (self->tx_buff.head) - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - - if (self->rx_buff.head) - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - - dev_self[self->index] = NULL; - free_netdev(self->netdev); - - return 0; -} - -/* - * Function nsc_ircc_init_108 (iobase, cfg_base, irq, dma) - * - * Initialize the NSC '108 chip - * - */ -static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - __u8 temp=0; - - outb(2, cfg_base); /* Mode Control Register (MCTL) */ - outb(0x00, cfg_base+1); /* Disable device */ - - /* Base Address and Interrupt Control Register (BAIC) */ - outb(CFG_108_BAIC, cfg_base); - switch (info->fir_base) { - case 0x3e8: outb(0x14, cfg_base+1); break; - case 0x2e8: outb(0x15, cfg_base+1); break; - case 0x3f8: outb(0x16, cfg_base+1); break; - case 0x2f8: outb(0x17, cfg_base+1); break; - default: net_err_ratelimited("%s(), invalid base_address\n", __func__); - } - - /* Control Signal Routing Register (CSRT) */ - switch (info->irq) { - case 3: temp = 0x01; break; - case 4: temp = 0x02; break; - case 5: temp = 0x03; break; - case 7: temp = 0x04; break; - case 9: temp = 0x05; break; - case 11: temp = 0x06; break; - case 15: temp = 0x07; break; - default: net_err_ratelimited("%s(), invalid irq\n", __func__); - } - outb(CFG_108_CSRT, cfg_base); - - switch (info->dma) { - case 0: outb(0x08+temp, cfg_base+1); break; - case 1: outb(0x10+temp, cfg_base+1); break; - case 3: outb(0x18+temp, cfg_base+1); break; - default: net_err_ratelimited("%s(), invalid dma\n", __func__); - } - - outb(CFG_108_MCTL, cfg_base); /* Mode Control Register (MCTL) */ - outb(0x03, cfg_base+1); /* Enable device */ - - return 0; -} - -/* - * Function nsc_ircc_probe_108 (chip, info) - * - * - * - */ -static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - int reg; - - /* Read address and interrupt control register (BAIC) */ - outb(CFG_108_BAIC, cfg_base); - reg = inb(cfg_base+1); - - switch (reg & 0x03) { - case 0: - info->fir_base = 0x3e8; - break; - case 1: - info->fir_base = 0x2e8; - break; - case 2: - info->fir_base = 0x3f8; - break; - case 3: - info->fir_base = 0x2f8; - break; - } - info->sir_base = info->fir_base; - pr_debug("%s(), probing fir_base=0x%03x\n", __func__, - info->fir_base); - - /* Read control signals routing register (CSRT) */ - outb(CFG_108_CSRT, cfg_base); - reg = inb(cfg_base+1); - - switch (reg & 0x07) { - case 0: - info->irq = -1; - break; - case 1: - info->irq = 3; - break; - case 2: - info->irq = 4; - break; - case 3: - info->irq = 5; - break; - case 4: - info->irq = 7; - break; - case 5: - info->irq = 9; - break; - case 6: - info->irq = 11; - break; - case 7: - info->irq = 15; - break; - } - pr_debug("%s(), probing irq=%d\n", __func__, info->irq); - - /* Currently we only read Rx DMA but it will also be used for Tx */ - switch ((reg >> 3) & 0x03) { - case 0: - info->dma = -1; - break; - case 1: - info->dma = 0; - break; - case 2: - info->dma = 1; - break; - case 3: - info->dma = 3; - break; - } - pr_debug("%s(), probing dma=%d\n", __func__, info->dma); - - /* Read mode control register (MCTL) */ - outb(CFG_108_MCTL, cfg_base); - reg = inb(cfg_base+1); - - info->enabled = reg & 0x01; - info->suspended = !((reg >> 1) & 0x01); - - return 0; -} - -/* - * Function nsc_ircc_init_338 (chip, info) - * - * Initialize the NSC '338 chip. Remember that the 87338 needs two - * consecutive writes to the data registers while CPU interrupts are - * disabled. The 97338 does not require this, but shouldn't be any - * harm if we do it anyway. - */ -static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info) -{ - /* No init yet */ - - return 0; -} - -/* - * Function nsc_ircc_probe_338 (chip, info) - * - * - * - */ -static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - int reg, com = 0; - int pnp; - - /* Read function enable register (FER) */ - outb(CFG_338_FER, cfg_base); - reg = inb(cfg_base+1); - - info->enabled = (reg >> 2) & 0x01; - - /* Check if we are in Legacy or PnP mode */ - outb(CFG_338_PNP0, cfg_base); - reg = inb(cfg_base+1); - - pnp = (reg >> 3) & 0x01; - if (pnp) { - pr_debug("(), Chip is in PnP mode\n"); - outb(0x46, cfg_base); - reg = (inb(cfg_base+1) & 0xfe) << 2; - - outb(0x47, cfg_base); - reg |= ((inb(cfg_base+1) & 0xfc) << 8); - - info->fir_base = reg; - } else { - /* Read function address register (FAR) */ - outb(CFG_338_FAR, cfg_base); - reg = inb(cfg_base+1); - - switch ((reg >> 4) & 0x03) { - case 0: - info->fir_base = 0x3f8; - break; - case 1: - info->fir_base = 0x2f8; - break; - case 2: - com = 3; - break; - case 3: - com = 4; - break; - } - - if (com) { - switch ((reg >> 6) & 0x03) { - case 0: - if (com == 3) - info->fir_base = 0x3e8; - else - info->fir_base = 0x2e8; - break; - case 1: - if (com == 3) - info->fir_base = 0x338; - else - info->fir_base = 0x238; - break; - case 2: - if (com == 3) - info->fir_base = 0x2e8; - else - info->fir_base = 0x2e0; - break; - case 3: - if (com == 3) - info->fir_base = 0x220; - else - info->fir_base = 0x228; - break; - } - } - } - info->sir_base = info->fir_base; - - /* Read PnP register 1 (PNP1) */ - outb(CFG_338_PNP1, cfg_base); - reg = inb(cfg_base+1); - - info->irq = reg >> 4; - - /* Read PnP register 3 (PNP3) */ - outb(CFG_338_PNP3, cfg_base); - reg = inb(cfg_base+1); - - info->dma = (reg & 0x07) - 1; - - /* Read power and test register (PTR) */ - outb(CFG_338_PTR, cfg_base); - reg = inb(cfg_base+1); - - info->suspended = reg & 0x01; - - return 0; -} - - -/* - * Function nsc_ircc_init_39x (chip, info) - * - * Now that we know it's a '39x (see probe below), we need to - * configure it so we can use it. - * - * The NSC '338 chip is a Super I/O chip with a "bank" architecture, - * the configuration of the different functionality (serial, parallel, - * floppy...) are each in a different bank (Logical Device Number). - * The base address, irq and dma configuration registers are common - * to all functionalities (index 0x30 to 0x7F). - * There is only one configuration register specific to the - * serial port, CFG_39X_SPC. - * JeanII - * - * Note : this code was written by Jan Frey - */ -static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - int enabled; - - /* User is sure about his config... accept it. */ - pr_debug("%s(): nsc_ircc_init_39x (user settings): io=0x%04x, irq=%d, dma=%d\n", - __func__, info->fir_base, info->irq, info->dma); - - /* Access bank for SP2 */ - outb(CFG_39X_LDN, cfg_base); - outb(0x02, cfg_base+1); - - /* Configure SP2 */ - - /* We want to enable the device if not enabled */ - outb(CFG_39X_ACT, cfg_base); - enabled = inb(cfg_base+1) & 0x01; - - if (!enabled) { - /* Enable the device */ - outb(CFG_39X_SIOCF1, cfg_base); - outb(0x01, cfg_base+1); - /* May want to update info->enabled. Jean II */ - } - - /* Enable UART bank switching (bit 7) ; Sets the chip to normal - * power mode (wake up from sleep mode) (bit 1) */ - outb(CFG_39X_SPC, cfg_base); - outb(0x82, cfg_base+1); - - return 0; -} - -/* - * Function nsc_ircc_probe_39x (chip, info) - * - * Test if we really have a '39x chip at the given address - * - * Note : this code was written by Jan Frey - */ -static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info) -{ - int cfg_base = info->cfg_base; - int reg1, reg2, irq, irqt, dma1, dma2; - int enabled, susp; - - pr_debug("%s(), nsc_ircc_probe_39x, base=%d\n", - __func__, cfg_base); - - /* This function should be executed with irq off to avoid - * another driver messing with the Super I/O bank - Jean II */ - - /* Access bank for SP2 */ - outb(CFG_39X_LDN, cfg_base); - outb(0x02, cfg_base+1); - - /* Read infos about SP2 ; store in info struct */ - outb(CFG_39X_BASEH, cfg_base); - reg1 = inb(cfg_base+1); - outb(CFG_39X_BASEL, cfg_base); - reg2 = inb(cfg_base+1); - info->fir_base = (reg1 << 8) | reg2; - - outb(CFG_39X_IRQNUM, cfg_base); - irq = inb(cfg_base+1); - outb(CFG_39X_IRQSEL, cfg_base); - irqt = inb(cfg_base+1); - info->irq = irq; - - outb(CFG_39X_DMA0, cfg_base); - dma1 = inb(cfg_base+1); - outb(CFG_39X_DMA1, cfg_base); - dma2 = inb(cfg_base+1); - info->dma = dma1 -1; - - outb(CFG_39X_ACT, cfg_base); - info->enabled = enabled = inb(cfg_base+1) & 0x01; - - outb(CFG_39X_SPC, cfg_base); - susp = 1 - ((inb(cfg_base+1) & 0x02) >> 1); - - pr_debug("%s(): io=0x%02x%02x, irq=%d (type %d), rxdma=%d, txdma=%d, enabled=%d (suspended=%d)\n", - __func__, reg1, reg2, irq, irqt, dma1, dma2, enabled, susp); - - /* Configure SP2 */ - - /* We want to enable the device if not enabled */ - outb(CFG_39X_ACT, cfg_base); - enabled = inb(cfg_base+1) & 0x01; - - if (!enabled) { - /* Enable the device */ - outb(CFG_39X_SIOCF1, cfg_base); - outb(0x01, cfg_base+1); - /* May want to update info->enabled. Jean II */ - } - - /* Enable UART bank switching (bit 7) ; Sets the chip to normal - * power mode (wake up from sleep mode) (bit 1) */ - outb(CFG_39X_SPC, cfg_base); - outb(0x82, cfg_base+1); - - return 0; -} - -#ifdef CONFIG_PNP -/* PNP probing */ -static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id) -{ - memset(&pnp_info, 0, sizeof(chipio_t)); - pnp_info.irq = -1; - pnp_info.dma = -1; - pnp_succeeded = 1; - - if (id->driver_data & NSC_FORCE_DONGLE_TYPE9) - dongle_id = 0x9; - - /* There doesn't seem to be any way of getting the cfg_base. - * On my box, cfg_base is in the PnP descriptor of the - * motherboard. Oh well... Jean II */ - - if (pnp_port_valid(dev, 0) && - !(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) - pnp_info.fir_base = pnp_port_start(dev, 0); - - if (pnp_irq_valid(dev, 0) && - !(pnp_irq_flags(dev, 0) & IORESOURCE_DISABLED)) - pnp_info.irq = pnp_irq(dev, 0); - - if (pnp_dma_valid(dev, 0) && - !(pnp_dma_flags(dev, 0) & IORESOURCE_DISABLED)) - pnp_info.dma = pnp_dma(dev, 0); - - pr_debug("%s() : From PnP, found firbase 0x%03X ; irq %d ; dma %d.\n", - __func__, pnp_info.fir_base, pnp_info.irq, pnp_info.dma); - - if((pnp_info.fir_base == 0) || - (pnp_info.irq == -1) || (pnp_info.dma == -1)) { - /* Returning an error will disable the device. Yuck ! */ - //return -EINVAL; - pnp_succeeded = 0; - } - - return 0; -} -#endif - -/* - * Function nsc_ircc_setup (info) - * - * Returns non-negative on success. - * - */ -static int nsc_ircc_setup(chipio_t *info) -{ - int version; - int iobase = info->fir_base; - - /* Read the Module ID */ - switch_bank(iobase, BANK3); - version = inb(iobase+MID); - - pr_debug("%s() Driver %s Found chip version %02x\n", - __func__, driver_name, version); - - /* Should be 0x2? */ - if (0x20 != (version & 0xf0)) { - net_err_ratelimited("%s, Wrong chip version %02x\n", - driver_name, version); - return -1; - } - - /* Switch to advanced mode */ - switch_bank(iobase, BANK2); - outb(ECR1_EXT_SL, iobase+ECR1); - switch_bank(iobase, BANK0); - - /* Set FIFO threshold to TX17, RX16, reset and enable FIFO's */ - switch_bank(iobase, BANK0); - outb(FCR_RXTH|FCR_TXTH|FCR_TXSR|FCR_RXSR|FCR_FIFO_EN, iobase+FCR); - - outb(0x03, iobase+LCR); /* 8 bit word length */ - outb(MCR_SIR, iobase+MCR); /* Start at SIR-mode, also clears LSR*/ - - /* Set FIFO size to 32 */ - switch_bank(iobase, BANK2); - outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2); - - /* IRCR2: FEND_MD is not set */ - switch_bank(iobase, BANK5); - outb(0x02, iobase+4); - - /* Make sure that some defaults are OK */ - switch_bank(iobase, BANK6); - outb(0x20, iobase+0); /* Set 32 bits FIR CRC */ - outb(0x0a, iobase+1); /* Set MIR pulse width */ - outb(0x0d, iobase+2); /* Set SIR pulse width to 1.6us */ - outb(0x2a, iobase+4); /* Set beginning frag, and preamble length */ - - /* Enable receive interrupts */ - switch_bank(iobase, BANK0); - outb(IER_RXHDL_IE, iobase+IER); - - return 0; -} - -/* - * Function nsc_ircc_read_dongle_id (void) - * - * Try to read dongle identification. This procedure needs to be executed - * once after power-on/reset. It also needs to be used whenever you suspect - * that the user may have plugged/unplugged the IrDA Dongle. - */ -static int nsc_ircc_read_dongle_id (int iobase) -{ - int dongle_id; - __u8 bank; - - bank = inb(iobase+BSR); - - /* Select Bank 7 */ - switch_bank(iobase, BANK7); - - /* IRCFG4: IRSL0_DS and IRSL21_DS are cleared */ - outb(0x00, iobase+7); - - /* ID0, 1, and 2 are pulled up/down very slowly */ - udelay(50); - - /* IRCFG1: read the ID bits */ - dongle_id = inb(iobase+4) & 0x0f; - -#ifdef BROKEN_DONGLE_ID - if (dongle_id == 0x0a) - dongle_id = 0x09; -#endif - /* Go back to bank 0 before returning */ - switch_bank(iobase, BANK0); - - outb(bank, iobase+BSR); - - return dongle_id; -} - -/* - * Function nsc_ircc_init_dongle_interface (iobase, dongle_id) - * - * This function initializes the dongle for the transceiver that is - * used. This procedure needs to be executed once after - * power-on/reset. It also needs to be used whenever you suspect that - * the dongle is changed. - */ -static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id) -{ - int bank; - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Select Bank 7 */ - switch_bank(iobase, BANK7); - - /* IRCFG4: set according to dongle_id */ - switch (dongle_id) { - case 0x00: /* same as */ - case 0x01: /* Differential serial interface */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x02: /* same as */ - case 0x03: /* Reserved */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x04: /* Sharp RY5HD01 */ - break; - case 0x05: /* Reserved, but this is what the Thinkpad reports */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x06: /* Single-ended serial interface */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x07: /* Consumer-IR only */ - pr_debug("%s(), %s is not for IrDA mode\n", - __func__, dongle_types[dongle_id]); - break; - case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ - pr_debug("%s(), %s\n", - __func__, dongle_types[dongle_id]); - break; - case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ - outb(0x28, iobase+7); /* Set irsl[0-2] as output */ - break; - case 0x0A: /* same as */ - case 0x0B: /* Reserved */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x0C: /* same as */ - case 0x0D: /* HP HSDL-1100/HSDL-2100 */ - /* - * Set irsl0 as input, irsl[1-2] as output, and separate - * inputs are used for SIR and MIR/FIR - */ - outb(0x48, iobase+7); - break; - case 0x0E: /* Supports SIR Mode only */ - outb(0x28, iobase+7); /* Set irsl[0-2] as output */ - break; - case 0x0F: /* No dongle connected */ - pr_debug("%s(), %s\n", - __func__, dongle_types[dongle_id]); - - switch_bank(iobase, BANK0); - outb(0x62, iobase+MCR); - break; - default: - pr_debug("%s(), invalid dongle_id %#x", - __func__, dongle_id); - } - - /* IRCFG1: IRSL1 and 2 are set to IrDA mode */ - outb(0x00, iobase+4); - - /* Restore bank register */ - outb(bank, iobase+BSR); - -} /* set_up_dongle_interface */ - -/* - * Function nsc_ircc_change_dongle_speed (iobase, speed, dongle_id) - * - * Change speed of the attach dongle - * - */ -static void nsc_ircc_change_dongle_speed(int iobase, int speed, int dongle_id) -{ - __u8 bank; - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Select Bank 7 */ - switch_bank(iobase, BANK7); - - /* IRCFG1: set according to dongle_id */ - switch (dongle_id) { - case 0x00: /* same as */ - case 0x01: /* Differential serial interface */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x02: /* same as */ - case 0x03: /* Reserved */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x04: /* Sharp RY5HD01 */ - break; - case 0x05: /* Reserved */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x06: /* Single-ended serial interface */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x07: /* Consumer-IR only */ - pr_debug("%s(), %s is not for IrDA mode\n", - __func__, dongle_types[dongle_id]); - break; - case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ - pr_debug("%s(), %s\n", - __func__, dongle_types[dongle_id]); - outb(0x00, iobase+4); - if (speed > 115200) - outb(0x01, iobase+4); - break; - case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ - outb(0x01, iobase+4); - - if (speed == 4000000) { - /* There was a cli() there, but we now are already - * under spin_lock_irqsave() - JeanII */ - outb(0x81, iobase+4); - outb(0x80, iobase+4); - } else - outb(0x00, iobase+4); - break; - case 0x0A: /* same as */ - case 0x0B: /* Reserved */ - pr_debug("%s(), %s not defined by irda yet\n", - __func__, dongle_types[dongle_id]); - break; - case 0x0C: /* same as */ - case 0x0D: /* HP HSDL-1100/HSDL-2100 */ - break; - case 0x0E: /* Supports SIR Mode only */ - break; - case 0x0F: /* No dongle connected */ - pr_debug("%s(), %s is not for IrDA mode\n", - __func__, dongle_types[dongle_id]); - - switch_bank(iobase, BANK0); - outb(0x62, iobase+MCR); - break; - default: - pr_debug("%s(), invalid data_rate\n", __func__); - } - /* Restore bank register */ - outb(bank, iobase+BSR); -} - -/* - * Function nsc_ircc_change_speed (self, baud) - * - * Change the speed of the device - * - * This function *must* be called with irq off and spin-lock. - */ -static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed) -{ - struct net_device *dev; - __u8 mcr = MCR_SIR; - int iobase; - __u8 bank; - __u8 ier; /* Interrupt enable register */ - - pr_debug("%s(), speed=%d\n", __func__, speed); - - IRDA_ASSERT(self != NULL, return 0;); - - dev = self->netdev; - iobase = self->io.fir_base; - - /* Update accounting for new speed */ - self->io.speed = speed; - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Disable interrupts */ - switch_bank(iobase, BANK0); - outb(0, iobase+IER); - - /* Select Bank 2 */ - switch_bank(iobase, BANK2); - - outb(0x00, iobase+BGDH); - switch (speed) { - case 9600: outb(0x0c, iobase+BGDL); break; - case 19200: outb(0x06, iobase+BGDL); break; - case 38400: outb(0x03, iobase+BGDL); break; - case 57600: outb(0x02, iobase+BGDL); break; - case 115200: outb(0x01, iobase+BGDL); break; - case 576000: - switch_bank(iobase, BANK5); - - /* IRCR2: MDRS is set */ - outb(inb(iobase+4) | 0x04, iobase+4); - - mcr = MCR_MIR; - pr_debug("%s(), handling baud of 576000\n", __func__); - break; - case 1152000: - mcr = MCR_MIR; - pr_debug("%s(), handling baud of 1152000\n", __func__); - break; - case 4000000: - mcr = MCR_FIR; - pr_debug("%s(), handling baud of 4000000\n", __func__); - break; - default: - mcr = MCR_FIR; - pr_debug("%s(), unknown baud rate of %d\n", - __func__, speed); - break; - } - - /* Set appropriate speed mode */ - switch_bank(iobase, BANK0); - outb(mcr | MCR_TX_DFR, iobase+MCR); - - /* Give some hits to the transceiver */ - nsc_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id); - - /* Set FIFO threshold to TX17, RX16 */ - switch_bank(iobase, BANK0); - outb(0x00, iobase+FCR); - outb(FCR_FIFO_EN, iobase+FCR); - outb(FCR_RXTH| /* Set Rx FIFO threshold */ - FCR_TXTH| /* Set Tx FIFO threshold */ - FCR_TXSR| /* Reset Tx FIFO */ - FCR_RXSR| /* Reset Rx FIFO */ - FCR_FIFO_EN, /* Enable FIFOs */ - iobase+FCR); - - /* Set FIFO size to 32 */ - switch_bank(iobase, BANK2); - outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2); - - /* Enable some interrupts so we can receive frames */ - switch_bank(iobase, BANK0); - if (speed > 115200) { - /* Install FIR xmit handler */ - dev->netdev_ops = &nsc_ircc_fir_ops; - ier = IER_SFIF_IE; - nsc_ircc_dma_receive(self); - } else { - /* Install SIR xmit handler */ - dev->netdev_ops = &nsc_ircc_sir_ops; - ier = IER_RXHDL_IE; - } - /* Set our current interrupt mask */ - outb(ier, iobase+IER); - - /* Restore BSR */ - outb(bank, iobase+BSR); - - /* Make sure interrupt handlers keep the proper interrupt mask */ - return ier; -} - -/* - * Function nsc_ircc_hard_xmit (skb, dev) - * - * Transmit the frame! - * - */ -static netdev_tx_t nsc_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev) -{ - struct nsc_ircc_cb *self; - unsigned long flags; - int iobase; - __s32 speed; - __u8 bank; - - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); - - iobase = self->io.fir_base; - - netif_stop_queue(dev); - - /* Make sure tests *& speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame. */ - if (!skb->len) { - /* If we just sent a frame, we get called before - * the last bytes get out (because of the SIR FIFO). - * If this is the case, let interrupt handler change - * the speed itself... Jean II */ - if (self->io.direction == IO_RECV) { - nsc_ircc_change_speed(self, speed); - /* TODO : For SIR->SIR, the next packet - * may get corrupted - Jean II */ - netif_wake_queue(dev); - } else { - self->new_speed = speed; - /* Queue will be restarted after speed change - * to make sure packets gets through the - * proper xmit handler - Jean II */ - } - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else - self->new_speed = speed; - } - - /* Save current bank */ - bank = inb(iobase+BSR); - - self->tx_buff.data = self->tx_buff.head; - - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - dev->stats.tx_bytes += self->tx_buff.len; - - /* Add interrupt on tx low level (will fire immediately) */ - switch_bank(iobase, BANK0); - outb(IER_TXLDL_IE, iobase+IER); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev) -{ - struct nsc_ircc_cb *self; - unsigned long flags; - int iobase; - __s32 speed; - __u8 bank; - int mtt, diff; - - self = netdev_priv(dev); - iobase = self->io.fir_base; - - netif_stop_queue(dev); - - /* Make sure tests *& speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame. */ - if (!skb->len) { - /* If we are currently transmitting, defer to - * interrupt handler. - Jean II */ - if(self->tx_fifo.len == 0) { - nsc_ircc_change_speed(self, speed); - netif_wake_queue(dev); - } else { - self->new_speed = speed; - /* Keep queue stopped : - * the speed change operation may change the - * xmit handler, and we want to make sure - * the next packet get through the proper - * Tx path, so block the Tx queue until - * the speed change has been done. - * Jean II */ - } - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else { - /* Change speed after current frame */ - self->new_speed = speed; - } - } - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Register and copy this frame to DMA memory */ - self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; - self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; - self->tx_fifo.tail += skb->len; - - dev->stats.tx_bytes += skb->len; - - skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, - skb->len); - self->tx_fifo.len++; - self->tx_fifo.free++; - - /* Start transmit only if there is currently no transmit going on */ - if (self->tx_fifo.len == 1) { - /* Check if we must wait the min turn time or not */ - mtt = irda_get_mtt(skb); - if (mtt) { - /* Check how much time we have used already */ - diff = ktime_us_delta(ktime_get(), self->stamp); - - /* Check if the mtt is larger than the time we have - * already used by all the protocol processing - */ - if (mtt > diff) { - mtt -= diff; - - /* - * Use timer if delay larger than 125 us, and - * use udelay for smaller values which should - * be acceptable - */ - if (mtt > 125) { - /* Adjust for timer resolution */ - mtt = mtt / 125; - - /* Setup timer */ - switch_bank(iobase, BANK4); - outb(mtt & 0xff, iobase+TMRL); - outb((mtt >> 8) & 0x0f, iobase+TMRH); - - /* Start timer */ - outb(IRCR1_TMR_EN, iobase+IRCR1); - self->io.direction = IO_XMIT; - - /* Enable timer interrupt */ - switch_bank(iobase, BANK0); - outb(IER_TMR_IE, iobase+IER); - - /* Timer will take care of the rest */ - goto out; - } else - udelay(mtt); - } - } - /* Enable DMA interrupt */ - switch_bank(iobase, BANK0); - outb(IER_DMA_IE, iobase+IER); - - /* Transmit frame */ - nsc_ircc_dma_xmit(self, iobase); - } - out: - /* Not busy transmitting anymore if window is not full, - * and if we don't need to change speed */ - if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0)) - netif_wake_queue(self->netdev); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* - * Function nsc_ircc_dma_xmit (self, iobase) - * - * Transmit data using DMA - * - */ -static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase) -{ - int bsr; - - /* Save current bank */ - bsr = inb(iobase+BSR); - - /* Disable DMA */ - switch_bank(iobase, BANK0); - outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); - - self->io.direction = IO_XMIT; - - /* Choose transmit DMA channel */ - switch_bank(iobase, BANK2); - outb(ECR1_DMASWP|ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1); - - irda_setup_dma(self->io.dma, - ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - - self->tx_buff.head) + self->tx_buff_dma, - self->tx_fifo.queue[self->tx_fifo.ptr].len, - DMA_TX_MODE); - - /* Enable DMA and SIR interaction pulse */ - switch_bank(iobase, BANK0); - outb(inb(iobase+MCR)|MCR_TX_DFR|MCR_DMA_EN|MCR_IR_PLS, iobase+MCR); - - /* Restore bank register */ - outb(bsr, iobase+BSR); -} - -/* - * Function nsc_ircc_pio_xmit (self, iobase) - * - * Transmit data using PIO. Returns the number of bytes that actually - * got transferred - * - */ -static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size) -{ - int actual = 0; - __u8 bank; - - /* Save current bank */ - bank = inb(iobase+BSR); - - switch_bank(iobase, BANK0); - if (!(inb_p(iobase+LSR) & LSR_TXEMP)) { - pr_debug("%s(), warning, FIFO not empty yet!\n", - __func__); - - /* FIFO may still be filled to the Tx interrupt threshold */ - fifo_size -= 17; - } - - /* Fill FIFO with current frame */ - while ((fifo_size-- > 0) && (actual < len)) { - /* Transmit next byte */ - outb(buf[actual++], iobase+TXD); - } - - pr_debug("%s(), fifo_size %d ; %d sent of %d\n", - __func__, fifo_size, actual, len); - - /* Restore bank */ - outb(bank, iobase+BSR); - - return actual; -} - -/* - * Function nsc_ircc_dma_xmit_complete (self) - * - * The transfer of a frame in finished. This function will only be called - * by the interrupt handler - * - */ -static int nsc_ircc_dma_xmit_complete(struct nsc_ircc_cb *self) -{ - int iobase; - __u8 bank; - int ret = TRUE; - - iobase = self->io.fir_base; - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Disable DMA */ - switch_bank(iobase, BANK0); - outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); - - /* Check for underrun! */ - if (inb(iobase+ASCR) & ASCR_TXUR) { - self->netdev->stats.tx_errors++; - self->netdev->stats.tx_fifo_errors++; - - /* Clear bit, by writing 1 into it */ - outb(ASCR_TXUR, iobase+ASCR); - } else { - self->netdev->stats.tx_packets++; - } - - /* Finished with this frame, so prepare for next */ - self->tx_fifo.ptr++; - self->tx_fifo.len--; - - /* Any frames to be sent back-to-back? */ - if (self->tx_fifo.len) { - nsc_ircc_dma_xmit(self, iobase); - - /* Not finished yet! */ - ret = FALSE; - } else { - /* Reset Tx FIFO info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - } - - /* Make sure we have room for more frames and - * that we don't need to change speed */ - if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0)) { - /* Not busy transmitting anymore */ - /* Tell the network layer, that we can accept more frames */ - netif_wake_queue(self->netdev); - } - - /* Restore bank */ - outb(bank, iobase+BSR); - - return ret; -} - -/* - * Function nsc_ircc_dma_receive (self) - * - * Get ready for receiving a frame. The device will initiate a DMA - * if it starts to receive a frame. - * - */ -static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self) -{ - int iobase; - __u8 bsr; - - iobase = self->io.fir_base; - - /* Reset Tx FIFO info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - - /* Save current bank */ - bsr = inb(iobase+BSR); - - /* Disable DMA */ - switch_bank(iobase, BANK0); - outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); - - /* Choose DMA Rx, DMA Fairness, and Advanced mode */ - switch_bank(iobase, BANK2); - outb(ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1); - - self->io.direction = IO_RECV; - self->rx_buff.data = self->rx_buff.head; - - /* Reset Rx FIFO. This will also flush the ST_FIFO */ - switch_bank(iobase, BANK0); - outb(FCR_RXSR|FCR_FIFO_EN, iobase+FCR); - - self->st_fifo.len = self->st_fifo.pending_bytes = 0; - self->st_fifo.tail = self->st_fifo.head = 0; - - irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, - DMA_RX_MODE); - - /* Enable DMA */ - switch_bank(iobase, BANK0); - outb(inb(iobase+MCR)|MCR_DMA_EN, iobase+MCR); - - /* Restore bank register */ - outb(bsr, iobase+BSR); - - return 0; -} - -/* - * Function nsc_ircc_dma_receive_complete (self) - * - * Finished with receiving frames - * - * - */ -static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) -{ - struct st_fifo *st_fifo; - struct sk_buff *skb; - __u8 status; - __u8 bank; - int len; - - st_fifo = &self->st_fifo; - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Read all entries in status FIFO */ - switch_bank(iobase, BANK5); - while ((status = inb(iobase+FRM_ST)) & FRM_ST_VLD) { - /* We must empty the status FIFO no matter what */ - len = inb(iobase+RFLFL) | ((inb(iobase+RFLFH) & 0x1f) << 8); - - if (st_fifo->tail >= MAX_RX_WINDOW) { - pr_debug("%s(), window is full!\n", __func__); - continue; - } - - st_fifo->entries[st_fifo->tail].status = status; - st_fifo->entries[st_fifo->tail].len = len; - st_fifo->pending_bytes += len; - st_fifo->tail++; - st_fifo->len++; - } - /* Try to process all entries in status FIFO */ - while (st_fifo->len > 0) { - /* Get first entry */ - status = st_fifo->entries[st_fifo->head].status; - len = st_fifo->entries[st_fifo->head].len; - st_fifo->pending_bytes -= len; - st_fifo->head++; - st_fifo->len--; - - /* Check for errors */ - if (status & FRM_ST_ERR_MSK) { - if (status & FRM_ST_LOST_FR) { - /* Add number of lost frames to stats */ - self->netdev->stats.rx_errors += len; - } else { - /* Skip frame */ - self->netdev->stats.rx_errors++; - - self->rx_buff.data += len; - - if (status & FRM_ST_MAX_LEN) - self->netdev->stats.rx_length_errors++; - - if (status & FRM_ST_PHY_ERR) - self->netdev->stats.rx_frame_errors++; - - if (status & FRM_ST_BAD_CRC) - self->netdev->stats.rx_crc_errors++; - } - /* The errors below can be reported in both cases */ - if (status & FRM_ST_OVR1) - self->netdev->stats.rx_fifo_errors++; - - if (status & FRM_ST_OVR2) - self->netdev->stats.rx_fifo_errors++; - } else { - /* - * First we must make sure that the frame we - * want to deliver is all in main memory. If we - * cannot tell, then we check if the Rx FIFO is - * empty. If not then we will have to take a nap - * and try again later. - */ - if (st_fifo->pending_bytes < self->io.fifo_size) { - switch_bank(iobase, BANK0); - if (inb(iobase+LSR) & LSR_RXDA) { - /* Put this entry back in fifo */ - st_fifo->head--; - st_fifo->len++; - st_fifo->pending_bytes += len; - st_fifo->entries[st_fifo->head].status = status; - st_fifo->entries[st_fifo->head].len = len; - /* - * DMA not finished yet, so try again - * later, set timer value, resolution - * 125 us - */ - switch_bank(iobase, BANK4); - outb(0x02, iobase+TMRL); /* x 125 us */ - outb(0x00, iobase+TMRH); - - /* Start timer */ - outb(IRCR1_TMR_EN, iobase+IRCR1); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - return FALSE; /* I'll be back! */ - } - } - - /* - * Remember the time we received this frame, so we can - * reduce the min turn time a bit since we will know - * how much time we have used for protocol processing - */ - self->stamp = ktime_get(); - - skb = dev_alloc_skb(len+1); - if (skb == NULL) { - self->netdev->stats.rx_dropped++; - - /* Restore bank register */ - outb(bank, iobase+BSR); - - return FALSE; - } - - /* Make sure IP header gets aligned */ - skb_reserve(skb, 1); - - /* Copy frame without CRC */ - if (self->io.speed < 4000000) { - skb_put(skb, len-2); - skb_copy_to_linear_data(skb, - self->rx_buff.data, - len - 2); - } else { - skb_put(skb, len-4); - skb_copy_to_linear_data(skb, - self->rx_buff.data, - len - 4); - } - - /* Move to next frame */ - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - } - } - /* Restore bank register */ - outb(bank, iobase+BSR); - - return TRUE; -} - -/* - * Function nsc_ircc_pio_receive (self) - * - * Receive all data in receiver FIFO - * - */ -static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self) -{ - __u8 byte; - int iobase; - - iobase = self->io.fir_base; - - /* Receive all characters in Rx FIFO */ - do { - byte = inb(iobase+RXD); - async_unwrap_char(self->netdev, &self->netdev->stats, - &self->rx_buff, byte); - } while (inb(iobase+LSR) & LSR_RXDA); /* Data available */ -} - -/* - * Function nsc_ircc_sir_interrupt (self, eir) - * - * Handle SIR interrupt - * - */ -static void nsc_ircc_sir_interrupt(struct nsc_ircc_cb *self, int eir) -{ - int actual; - - /* Check if transmit FIFO is low on data */ - if (eir & EIR_TXLDL_EV) { - /* Write data left in transmit buffer */ - actual = nsc_ircc_pio_write(self->io.fir_base, - self->tx_buff.data, - self->tx_buff.len, - self->io.fifo_size); - self->tx_buff.data += actual; - self->tx_buff.len -= actual; - - self->io.direction = IO_XMIT; - - /* Check if finished */ - if (self->tx_buff.len > 0) - self->ier = IER_TXLDL_IE; - else { - - self->netdev->stats.tx_packets++; - netif_wake_queue(self->netdev); - self->ier = IER_TXEMP_IE; - } - - } - /* Check if transmission has completed */ - if (eir & EIR_TXEMP_EV) { - /* Turn around and get ready to receive some data */ - self->io.direction = IO_RECV; - self->ier = IER_RXHDL_IE; - /* Check if we need to change the speed? - * Need to be after self->io.direction to avoid race with - * nsc_ircc_hard_xmit_sir() - Jean II */ - if (self->new_speed) { - pr_debug("%s(), Changing speed!\n", __func__); - self->ier = nsc_ircc_change_speed(self, - self->new_speed); - self->new_speed = 0; - netif_wake_queue(self->netdev); - - /* Check if we are going to FIR */ - if (self->io.speed > 115200) { - /* No need to do anymore SIR stuff */ - return; - } - } - } - - /* Rx FIFO threshold or timeout */ - if (eir & EIR_RXHDL_EV) { - nsc_ircc_pio_receive(self); - - /* Keep receiving */ - self->ier = IER_RXHDL_IE; - } -} - -/* - * Function nsc_ircc_fir_interrupt (self, eir) - * - * Handle MIR/FIR interrupt - * - */ -static void nsc_ircc_fir_interrupt(struct nsc_ircc_cb *self, int iobase, - int eir) -{ - __u8 bank; - - bank = inb(iobase+BSR); - - /* Status FIFO event*/ - if (eir & EIR_SFIF_EV) { - /* Check if DMA has finished */ - if (nsc_ircc_dma_receive_complete(self, iobase)) { - /* Wait for next status FIFO interrupt */ - self->ier = IER_SFIF_IE; - } else { - self->ier = IER_SFIF_IE | IER_TMR_IE; - } - } else if (eir & EIR_TMR_EV) { /* Timer finished */ - /* Disable timer */ - switch_bank(iobase, BANK4); - outb(0, iobase+IRCR1); - - /* Clear timer event */ - switch_bank(iobase, BANK0); - outb(ASCR_CTE, iobase+ASCR); - - /* Check if this is a Tx timer interrupt */ - if (self->io.direction == IO_XMIT) { - nsc_ircc_dma_xmit(self, iobase); - - /* Interrupt on DMA */ - self->ier = IER_DMA_IE; - } else { - /* Check (again) if DMA has finished */ - if (nsc_ircc_dma_receive_complete(self, iobase)) { - self->ier = IER_SFIF_IE; - } else { - self->ier = IER_SFIF_IE | IER_TMR_IE; - } - } - } else if (eir & EIR_DMA_EV) { - /* Finished with all transmissions? */ - if (nsc_ircc_dma_xmit_complete(self)) { - if(self->new_speed != 0) { - /* As we stop the Tx queue, the speed change - * need to be done when the Tx fifo is - * empty. Ask for a Tx done interrupt */ - self->ier = IER_TXEMP_IE; - } else { - /* Check if there are more frames to be - * transmitted */ - if (irda_device_txqueue_empty(self->netdev)) { - /* Prepare for receive */ - nsc_ircc_dma_receive(self); - self->ier = IER_SFIF_IE; - } else - net_warn_ratelimited("%s(), potential Tx queue lockup !\n", - __func__); - } - } else { - /* Not finished yet, so interrupt on DMA again */ - self->ier = IER_DMA_IE; - } - } else if (eir & EIR_TXEMP_EV) { - /* The Tx FIFO has totally drained out, so now we can change - * the speed... - Jean II */ - self->ier = nsc_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - netif_wake_queue(self->netdev); - /* Note : nsc_ircc_change_speed() restarted Rx fifo */ - } - - outb(bank, iobase+BSR); -} - -/* - * Function nsc_ircc_interrupt (irq, dev_id, regs) - * - * An interrupt from the chip has arrived. Time to do some work - * - */ -static irqreturn_t nsc_ircc_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct nsc_ircc_cb *self; - __u8 bsr, eir; - int iobase; - - self = netdev_priv(dev); - - spin_lock(&self->lock); - - iobase = self->io.fir_base; - - bsr = inb(iobase+BSR); /* Save current bank */ - - switch_bank(iobase, BANK0); - self->ier = inb(iobase+IER); - eir = inb(iobase+EIR) & self->ier; /* Mask out the interesting ones */ - - outb(0, iobase+IER); /* Disable interrupts */ - - if (eir) { - /* Dispatch interrupt handler for the current speed */ - if (self->io.speed > 115200) - nsc_ircc_fir_interrupt(self, iobase, eir); - else - nsc_ircc_sir_interrupt(self, eir); - } - - outb(self->ier, iobase+IER); /* Restore interrupts */ - outb(bsr, iobase+BSR); /* Restore bank register */ - - spin_unlock(&self->lock); - return IRQ_RETVAL(eir); -} - -/* - * Function nsc_ircc_is_receiving (self) - * - * Return TRUE is we are currently receiving a frame - * - */ -static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self) -{ - unsigned long flags; - int status = FALSE; - int iobase; - __u8 bank; - - IRDA_ASSERT(self != NULL, return FALSE;); - - spin_lock_irqsave(&self->lock, flags); - - if (self->io.speed > 115200) { - iobase = self->io.fir_base; - - /* Check if rx FIFO is not empty */ - bank = inb(iobase+BSR); - switch_bank(iobase, BANK2); - if ((inb(iobase+RXFLV) & 0x3f) != 0) { - /* We are receiving something */ - status = TRUE; - } - outb(bank, iobase+BSR); - } else - status = (self->rx_buff.state != OUTSIDE_FRAME); - - spin_unlock_irqrestore(&self->lock, flags); - - return status; -} - -/* - * Function nsc_ircc_net_open (dev) - * - * Start the device - * - */ -static int nsc_ircc_net_open(struct net_device *dev) -{ - struct nsc_ircc_cb *self; - int iobase; - char hwname[32]; - __u8 bank; - - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return 0;); - - iobase = self->io.fir_base; - - if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, dev->name, dev)) { - net_warn_ratelimited("%s, unable to allocate irq=%d\n", - driver_name, self->io.irq); - return -EAGAIN; - } - /* - * Always allocate the DMA channel after the IRQ, and clean up on - * failure. - */ - if (request_dma(self->io.dma, dev->name)) { - net_warn_ratelimited("%s, unable to allocate dma=%d\n", - driver_name, self->io.dma); - free_irq(self->io.irq, dev); - return -EAGAIN; - } - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* turn on interrupts */ - switch_bank(iobase, BANK0); - outb(IER_LS_IE | IER_RXHDL_IE, iobase+IER); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - /* Ready to play! */ - netif_start_queue(dev); - - /* Give self a hardware name */ - sprintf(hwname, "NSC-FIR @ 0x%03x", self->io.fir_base); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - self->irlap = irlap_open(dev, &self->qos, hwname); - - return 0; -} - -/* - * Function nsc_ircc_net_close (dev) - * - * Stop the device - * - */ -static int nsc_ircc_net_close(struct net_device *dev) -{ - struct nsc_ircc_cb *self; - int iobase; - __u8 bank; - - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); - - /* Stop device */ - netif_stop_queue(dev); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - - iobase = self->io.fir_base; - - disable_dma(self->io.dma); - - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Disable interrupts */ - switch_bank(iobase, BANK0); - outb(0, iobase+IER); - - free_irq(self->io.irq, dev); - free_dma(self->io.dma); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - return 0; -} - -/* - * Function nsc_ircc_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct nsc_ircc_cb *self; - unsigned long flags; - int ret = 0; - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return -1;); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - spin_lock_irqsave(&self->lock, flags); - nsc_ircc_change_speed(self, irq->ifr_baudrate); - spin_unlock_irqrestore(&self->lock, flags); - break; - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - /* This is already protected */ - irq->ifr_receiving = nsc_ircc_is_receiving(self); - break; - default: - ret = -EOPNOTSUPP; - } - return ret; -} - -static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state) -{ - struct nsc_ircc_cb *self = platform_get_drvdata(dev); - int bank; - unsigned long flags; - int iobase = self->io.fir_base; - - if (self->io.suspended) - return 0; - - pr_debug("%s, Suspending\n", driver_name); - - rtnl_lock(); - if (netif_running(self->netdev)) { - netif_device_detach(self->netdev); - spin_lock_irqsave(&self->lock, flags); - /* Save current bank */ - bank = inb(iobase+BSR); - - /* Disable interrupts */ - switch_bank(iobase, BANK0); - outb(0, iobase+IER); - - /* Restore bank register */ - outb(bank, iobase+BSR); - - spin_unlock_irqrestore(&self->lock, flags); - free_irq(self->io.irq, self->netdev); - disable_dma(self->io.dma); - } - self->io.suspended = 1; - rtnl_unlock(); - - return 0; -} - -static int nsc_ircc_resume(struct platform_device *dev) -{ - struct nsc_ircc_cb *self = platform_get_drvdata(dev); - unsigned long flags; - - if (!self->io.suspended) - return 0; - - pr_debug("%s, Waking up\n", driver_name); - - rtnl_lock(); - nsc_ircc_setup(&self->io); - nsc_ircc_init_dongle_interface(self->io.fir_base, self->io.dongle_id); - - if (netif_running(self->netdev)) { - if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, - self->netdev->name, self->netdev)) { - net_warn_ratelimited("%s, unable to allocate irq=%d\n", - driver_name, self->io.irq); - - /* - * Don't fail resume process, just kill this - * network interface - */ - unregister_netdevice(self->netdev); - } else { - spin_lock_irqsave(&self->lock, flags); - nsc_ircc_change_speed(self, self->io.speed); - spin_unlock_irqrestore(&self->lock, flags); - netif_device_attach(self->netdev); - } - - } else { - spin_lock_irqsave(&self->lock, flags); - nsc_ircc_change_speed(self, 9600); - spin_unlock_irqrestore(&self->lock, flags); - } - self->io.suspended = 0; - rtnl_unlock(); - - return 0; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("NSC IrDA Device Driver"); -MODULE_LICENSE("GPL"); - - -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); -module_param_hw_array(io, int, ioport, NULL, 0); -MODULE_PARM_DESC(io, "Base I/O addresses"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "IRQ lines"); -module_param_hw_array(dma, int, dma, NULL, 0); -MODULE_PARM_DESC(dma, "DMA channels"); -module_param(dongle_id, int, 0); -MODULE_PARM_DESC(dongle_id, "Type-id of used dongle"); - -module_init(nsc_ircc_init); -module_exit(nsc_ircc_cleanup); - diff --git a/drivers/staging/irda/drivers/nsc-ircc.h b/drivers/staging/irda/drivers/nsc-ircc.h deleted file mode 100644 index 7be5acb56532..000000000000 --- a/drivers/staging/irda/drivers/nsc-ircc.h +++ /dev/null @@ -1,281 +0,0 @@ -/********************************************************************* - * - * Filename: nsc-ircc.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Fri Nov 13 14:37:40 1998 - * Modified at: Sun Jan 23 17:47:00 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli - * Copyright (c) 1998 Lichen Wang, - * Copyright (c) 1998 Actisys Corp., www.actisys.com - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef NSC_IRCC_H -#define NSC_IRCC_H - -#include - -#include -#include -#include -#include - -/* Features for chips (set in driver_data) */ -#define NSC_FORCE_DONGLE_TYPE9 0x00000001 - -/* DMA modes needed */ -#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ -#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ - -/* Config registers for the '108 */ -#define CFG_108_BAIC 0x00 -#define CFG_108_CSRT 0x01 -#define CFG_108_MCTL 0x02 - -/* Config registers for the '338 */ -#define CFG_338_FER 0x00 -#define CFG_338_FAR 0x01 -#define CFG_338_PTR 0x02 -#define CFG_338_PNP0 0x1b -#define CFG_338_PNP1 0x1c -#define CFG_338_PNP3 0x4f - -/* Config registers for the '39x (in the logical device bank) */ -#define CFG_39X_LDN 0x07 /* Logical device number (Super I/O bank) */ -#define CFG_39X_SIOCF1 0x21 /* SuperI/O Config */ -#define CFG_39X_ACT 0x30 /* Device activation */ -#define CFG_39X_BASEH 0x60 /* Device base address (high bits) */ -#define CFG_39X_BASEL 0x61 /* Device base address (low bits) */ -#define CFG_39X_IRQNUM 0x70 /* Interrupt number & wake up enable */ -#define CFG_39X_IRQSEL 0x71 /* Interrupt select (edge/level + polarity) */ -#define CFG_39X_DMA0 0x74 /* DMA 0 configuration */ -#define CFG_39X_DMA1 0x75 /* DMA 1 configuration */ -#define CFG_39X_SPC 0xF0 /* Serial port configuration register */ - -/* Flags for configuration register CRF0 */ -#define APEDCRC 0x02 -#define ENBNKSEL 0x01 - -/* Set 0 */ -#define TXD 0x00 /* Transmit data port */ -#define RXD 0x00 /* Receive data port */ - -/* Register 1 */ -#define IER 0x01 /* Interrupt Enable Register*/ -#define IER_RXHDL_IE 0x01 /* Receiver high data level interrupt */ -#define IER_TXLDL_IE 0x02 /* Transeiver low data level interrupt */ -#define IER_LS_IE 0x04//* Link Status Interrupt */ -#define IER_ETXURI 0x04 /* Tx underrun */ -#define IER_DMA_IE 0x10 /* DMA finished interrupt */ -#define IER_TXEMP_IE 0x20 -#define IER_SFIF_IE 0x40 /* Frame status FIFO intr */ -#define IER_TMR_IE 0x80 /* Timer event */ - -#define FCR 0x02 /* (write only) */ -#define FCR_FIFO_EN 0x01 /* Enable FIFO's */ -#define FCR_RXSR 0x02 /* Rx FIFO soft reset */ -#define FCR_TXSR 0x04 /* Tx FIFO soft reset */ -#define FCR_RXTH 0x40 /* Rx FIFO threshold (set to 16) */ -#define FCR_TXTH 0x20 /* Tx FIFO threshold (set to 17) */ - -#define EIR 0x02 /* (read only) */ -#define EIR_RXHDL_EV 0x01 -#define EIR_TXLDL_EV 0x02 -#define EIR_LS_EV 0x04 -#define EIR_DMA_EV 0x10 -#define EIR_TXEMP_EV 0x20 -#define EIR_SFIF_EV 0x40 -#define EIR_TMR_EV 0x80 - -#define LCR 0x03 /* Link control register */ -#define LCR_WLS_8 0x03 /* 8 bits */ - -#define BSR 0x03 /* Bank select register */ -#define BSR_BKSE 0x80 -#define BANK0 LCR_WLS_8 /* Must make sure that we set 8N1 */ -#define BANK1 0x80 -#define BANK2 0xe0 -#define BANK3 0xe4 -#define BANK4 0xe8 -#define BANK5 0xec -#define BANK6 0xf0 -#define BANK7 0xf4 - -#define MCR 0x04 /* Mode Control Register */ -#define MCR_MODE_MASK ~(0xd0) -#define MCR_UART 0x00 -#define MCR_RESERVED 0x20 -#define MCR_SHARP_IR 0x40 -#define MCR_SIR 0x60 -#define MCR_MIR 0x80 -#define MCR_FIR 0xa0 -#define MCR_CEIR 0xb0 -#define MCR_IR_PLS 0x10 -#define MCR_DMA_EN 0x04 -#define MCR_EN_IRQ 0x08 -#define MCR_TX_DFR 0x08 - -#define LSR 0x05 /* Link status register */ -#define LSR_RXDA 0x01 /* Receiver data available */ -#define LSR_TXRDY 0x20 /* Transmitter ready */ -#define LSR_TXEMP 0x40 /* Transmitter empty */ - -#define ASCR 0x07 /* Auxiliary Status and Control Register */ -#define ASCR_RXF_TOUT 0x01 /* Rx FIFO timeout */ -#define ASCR_FEND_INF 0x02 /* Frame end bytes in rx FIFO */ -#define ASCR_S_EOT 0x04 /* Set end of transmission */ -#define ASCT_RXBSY 0x20 /* Rx busy */ -#define ASCR_TXUR 0x40 /* Transeiver underrun */ -#define ASCR_CTE 0x80 /* Clear timer event */ - -/* Bank 2 */ -#define BGDL 0x00 /* Baud Generator Divisor Port (Low Byte) */ -#define BGDH 0x01 /* Baud Generator Divisor Port (High Byte) */ - -#define ECR1 0x02 /* Extended Control Register 1 */ -#define ECR1_EXT_SL 0x01 /* Extended Mode Select */ -#define ECR1_DMANF 0x02 /* DMA Fairness */ -#define ECR1_DMATH 0x04 /* DMA Threshold */ -#define ECR1_DMASWP 0x08 /* DMA Swap */ - -#define EXCR2 0x04 -#define EXCR2_TFSIZ 0x01 /* Rx FIFO size = 32 */ -#define EXCR2_RFSIZ 0x04 /* Tx FIFO size = 32 */ - -#define TXFLV 0x06 /* Tx FIFO level */ -#define RXFLV 0x07 /* Rx FIFO level */ - -/* Bank 3 */ -#define MID 0x00 - -/* Bank 4 */ -#define TMRL 0x00 /* Timer low byte */ -#define TMRH 0x01 /* Timer high byte */ -#define IRCR1 0x02 /* Infrared control register 1 */ -#define IRCR1_TMR_EN 0x01 /* Timer enable */ - -#define TFRLL 0x04 -#define TFRLH 0x05 -#define RFRLL 0x06 -#define RFRLH 0x07 - -/* Bank 5 */ -#define IRCR2 0x04 /* Infrared control register 2 */ -#define IRCR2_MDRS 0x04 /* MIR data rate select */ -#define IRCR2_FEND_MD 0x20 /* */ - -#define FRM_ST 0x05 /* Frame status FIFO */ -#define FRM_ST_VLD 0x80 /* Frame status FIFO data valid */ -#define FRM_ST_ERR_MSK 0x5f -#define FRM_ST_LOST_FR 0x40 /* Frame lost */ -#define FRM_ST_MAX_LEN 0x10 /* Max frame len exceeded */ -#define FRM_ST_PHY_ERR 0x08 /* Physical layer error */ -#define FRM_ST_BAD_CRC 0x04 -#define FRM_ST_OVR1 0x02 /* Rx FIFO overrun */ -#define FRM_ST_OVR2 0x01 /* Frame status FIFO overrun */ - -#define RFLFL 0x06 -#define RFLFH 0x07 - -/* Bank 6 */ -#define IR_CFG2 0x00 -#define IR_CFG2_DIS_CRC 0x02 - -/* Bank 7 */ -#define IRM_CR 0x07 /* Infrared module control register */ -#define IRM_CR_IRX_MSL 0x40 -#define IRM_CR_AF_MNT 0x80 /* Automatic format */ - -/* NSC chip information */ -struct nsc_chip { - char *name; /* Name of chipset */ - int cfg[3]; /* Config registers */ - u_int8_t cid_index; /* Chip identification index reg */ - u_int8_t cid_value; /* Chip identification expected value */ - u_int8_t cid_mask; /* Chip identification revision mask */ - - /* Functions for probing and initializing the specific chip */ - int (*probe)(struct nsc_chip *chip, chipio_t *info); - int (*init)(struct nsc_chip *chip, chipio_t *info); -}; -typedef struct nsc_chip nsc_chip_t; - -/* For storing entries in the status FIFO */ -struct st_fifo_entry { - int status; - int len; -}; - -#define MAX_TX_WINDOW 7 -#define MAX_RX_WINDOW 7 - -struct st_fifo { - struct st_fifo_entry entries[MAX_RX_WINDOW]; - int pending_bytes; - int head; - int tail; - int len; -}; - -struct frame_cb { - void *start; /* Start of frame in DMA mem */ - int len; /* Length of frame in DMA mem */ -}; - -struct tx_fifo { - struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */ - int ptr; /* Currently being sent */ - int len; /* Length of queue */ - int free; /* Next free slot */ - void *tail; /* Next free start in DMA mem */ -}; - -/* Private data for each instance */ -struct nsc_ircc_cb { - struct st_fifo st_fifo; /* Info about received frames */ - struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ - - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; /* QoS capabilities for this device */ - - chipio_t io; /* IrDA controller information */ - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - dma_addr_t tx_buff_dma; - dma_addr_t rx_buff_dma; - - __u8 ier; /* Interrupt enable register */ - - ktime_t stamp; - - spinlock_t lock; /* For serializing operations */ - - __u32 new_speed; - int index; /* Instance index */ - - struct platform_device *pldev; -}; - -static inline void switch_bank(int iobase, int bank) -{ - outb(bank, iobase+BSR); -} - -#endif /* NSC_IRCC_H */ diff --git a/drivers/staging/irda/drivers/old_belkin-sir.c b/drivers/staging/irda/drivers/old_belkin-sir.c deleted file mode 100644 index a7c2e990ae69..000000000000 --- a/drivers/staging/irda/drivers/old_belkin-sir.c +++ /dev/null @@ -1,146 +0,0 @@ -/********************************************************************* - * - * Filename: old_belkin.c - * Version: 1.1 - * Description: Driver for the Belkin (old) SmartBeam dongle - * Status: Experimental... - * Author: Jean Tourrilhes - * Created at: 22/11/99 - * Modified at: Fri Dec 17 09:13:32 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Jean Tourrilhes, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include - -#include -// #include - -#include "sir-dev.h" - -/* - * Belkin is selling a dongle called the SmartBeam. - * In fact, there is two hardware version of this dongle, of course with - * the same name and looking the exactly same (grrr...). - * I guess that I've got the old one, because inside I don't have - * a jumper for IrDA/ASK... - * - * As far as I can make it from info on their web site, the old dongle - * support only 9600 b/s, which make our life much simpler as far as - * the driver is concerned, but you might not like it very much ;-) - * The new SmartBeam does 115 kb/s, and I've not tested it... - * - * Belkin claim that the correct driver for the old dongle (in Windows) - * is the generic Parallax 9500a driver, but the Linux LiteLink driver - * fails for me (probably because Linux-IrDA doesn't rate fallback), - * so I created this really dumb driver... - * - * In fact, this driver doesn't do much. The only thing it does is to - * prevent Linux-IrDA to use any other speed than 9600 b/s ;-) This - * driver is called "old_belkin" so that when the new SmartBeam is supported - * its driver can be called "belkin" instead of "new_belkin". - * - * Note : this driver was written without any info/help from Belkin, - * so a lot of info here might be totally wrong. Blame me ;-) - */ - -static int old_belkin_open(struct sir_dev *dev); -static int old_belkin_close(struct sir_dev *dev); -static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed); -static int old_belkin_reset(struct sir_dev *dev); - -static struct dongle_driver old_belkin = { - .owner = THIS_MODULE, - .driver_name = "Old Belkin SmartBeam", - .type = IRDA_OLD_BELKIN_DONGLE, - .open = old_belkin_open, - .close = old_belkin_close, - .reset = old_belkin_reset, - .set_speed = old_belkin_change_speed, -}; - -static int __init old_belkin_sir_init(void) -{ - return irda_register_dongle(&old_belkin); -} - -static void __exit old_belkin_sir_cleanup(void) -{ - irda_unregister_dongle(&old_belkin); -} - -static int old_belkin_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Power on dongle */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Not too fast, please... */ - qos->baud_rate.bits &= IR_9600; - /* Needs at least 10 ms (totally wild guess, can do probably better) */ - qos->min_turn_time.bits = 0x01; - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int old_belkin_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function old_belkin_change_speed (task) - * - * With only one speed available, not much to do... - */ -static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed) -{ - dev->speed = 9600; - return (speed==dev->speed) ? 0 : -EINVAL; -} - -/* - * Function old_belkin_reset (task) - * - * Reset the Old-Belkin type dongle. - * - */ -static int old_belkin_reset(struct sir_dev *dev) -{ - /* This dongles speed "defaults" to 9600 bps ;-) */ - dev->speed = 9600; - - return 0; -} - -MODULE_AUTHOR("Jean Tourrilhes "); -MODULE_DESCRIPTION("Belkin (old) SmartBeam dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-7"); /* IRDA_OLD_BELKIN_DONGLE */ - -module_init(old_belkin_sir_init); -module_exit(old_belkin_sir_cleanup); diff --git a/drivers/staging/irda/drivers/pxaficp_ir.c b/drivers/staging/irda/drivers/pxaficp_ir.c deleted file mode 100644 index 2ea00a6531f9..000000000000 --- a/drivers/staging/irda/drivers/pxaficp_ir.c +++ /dev/null @@ -1,1075 +0,0 @@ -/* - * linux/drivers/net/irda/pxaficp_ir.c - * - * Based on sa1100_ir.c by Russell King - * - * Changes copyright (C) 2003-2005 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Infra-red driver (SIR/FIR) for the PXA2xx embedded microprocessor - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#undef __REG -#define __REG(x) ((x) & 0xffff) -#include - -#define ICCR0 0x0000 /* ICP Control Register 0 */ -#define ICCR1 0x0004 /* ICP Control Register 1 */ -#define ICCR2 0x0008 /* ICP Control Register 2 */ -#define ICDR 0x000c /* ICP Data Register */ -#define ICSR0 0x0014 /* ICP Status Register 0 */ -#define ICSR1 0x0018 /* ICP Status Register 1 */ - -#define ICCR0_AME (1 << 7) /* Address match enable */ -#define ICCR0_TIE (1 << 6) /* Transmit FIFO interrupt enable */ -#define ICCR0_RIE (1 << 5) /* Receive FIFO interrupt enable */ -#define ICCR0_RXE (1 << 4) /* Receive enable */ -#define ICCR0_TXE (1 << 3) /* Transmit enable */ -#define ICCR0_TUS (1 << 2) /* Transmit FIFO underrun select */ -#define ICCR0_LBM (1 << 1) /* Loopback mode */ -#define ICCR0_ITR (1 << 0) /* IrDA transmission */ - -#define ICCR2_RXP (1 << 3) /* Receive Pin Polarity select */ -#define ICCR2_TXP (1 << 2) /* Transmit Pin Polarity select */ -#define ICCR2_TRIG (3 << 0) /* Receive FIFO Trigger threshold */ -#define ICCR2_TRIG_8 (0 << 0) /* >= 8 bytes */ -#define ICCR2_TRIG_16 (1 << 0) /* >= 16 bytes */ -#define ICCR2_TRIG_32 (2 << 0) /* >= 32 bytes */ - -#define ICSR0_EOC (1 << 6) /* DMA End of Descriptor Chain */ -#define ICSR0_FRE (1 << 5) /* Framing error */ -#define ICSR0_RFS (1 << 4) /* Receive FIFO service request */ -#define ICSR0_TFS (1 << 3) /* Transnit FIFO service request */ -#define ICSR0_RAB (1 << 2) /* Receiver abort */ -#define ICSR0_TUR (1 << 1) /* Trunsmit FIFO underun */ -#define ICSR0_EIF (1 << 0) /* End/Error in FIFO */ - -#define ICSR1_ROR (1 << 6) /* Receiver FIFO underrun */ -#define ICSR1_CRE (1 << 5) /* CRC error */ -#define ICSR1_EOF (1 << 4) /* End of frame */ -#define ICSR1_TNF (1 << 3) /* Transmit FIFO not full */ -#define ICSR1_RNE (1 << 2) /* Receive FIFO not empty */ -#define ICSR1_TBY (1 << 1) /* Tramsmiter busy flag */ -#define ICSR1_RSY (1 << 0) /* Recevier synchronized flag */ - -#define IrSR_RXPL_NEG_IS_ZERO (1<<4) -#define IrSR_RXPL_POS_IS_ZERO 0x0 -#define IrSR_TXPL_NEG_IS_ZERO (1<<3) -#define IrSR_TXPL_POS_IS_ZERO 0x0 -#define IrSR_XMODE_PULSE_1_6 (1<<2) -#define IrSR_XMODE_PULSE_3_16 0x0 -#define IrSR_RCVEIR_IR_MODE (1<<1) -#define IrSR_RCVEIR_UART_MODE 0x0 -#define IrSR_XMITIR_IR_MODE (1<<0) -#define IrSR_XMITIR_UART_MODE 0x0 - -#define IrSR_IR_RECEIVE_ON (\ - IrSR_RXPL_NEG_IS_ZERO | \ - IrSR_TXPL_POS_IS_ZERO | \ - IrSR_XMODE_PULSE_3_16 | \ - IrSR_RCVEIR_IR_MODE | \ - IrSR_XMITIR_UART_MODE) - -#define IrSR_IR_TRANSMIT_ON (\ - IrSR_RXPL_NEG_IS_ZERO | \ - IrSR_TXPL_POS_IS_ZERO | \ - IrSR_XMODE_PULSE_3_16 | \ - IrSR_RCVEIR_UART_MODE | \ - IrSR_XMITIR_IR_MODE) - -/* macros for registers read/write */ -#define ficp_writel(irda, val, off) \ - do { \ - dev_vdbg(irda->dev, \ - "%s():%d ficp_writel(0x%x, %s)\n", \ - __func__, __LINE__, (val), #off); \ - writel_relaxed((val), (irda)->irda_base + (off)); \ - } while (0) - -#define ficp_readl(irda, off) \ - ({ \ - unsigned int _v; \ - _v = readl_relaxed((irda)->irda_base + (off)); \ - dev_vdbg(irda->dev, \ - "%s():%d ficp_readl(%s): 0x%x\n", \ - __func__, __LINE__, #off, _v); \ - _v; \ - }) - -#define stuart_writel(irda, val, off) \ - do { \ - dev_vdbg(irda->dev, \ - "%s():%d stuart_writel(0x%x, %s)\n", \ - __func__, __LINE__, (val), #off); \ - writel_relaxed((val), (irda)->stuart_base + (off)); \ - } while (0) - -#define stuart_readl(irda, off) \ - ({ \ - unsigned int _v; \ - _v = readl_relaxed((irda)->stuart_base + (off)); \ - dev_vdbg(irda->dev, \ - "%s():%d stuart_readl(%s): 0x%x\n", \ - __func__, __LINE__, #off, _v); \ - _v; \ - }) - -struct pxa_irda { - int speed; - int newspeed; - unsigned long long last_clk; - - void __iomem *stuart_base; - void __iomem *irda_base; - unsigned char *dma_rx_buff; - unsigned char *dma_tx_buff; - dma_addr_t dma_rx_buff_phy; - dma_addr_t dma_tx_buff_phy; - unsigned int dma_tx_buff_len; - struct dma_chan *txdma; - struct dma_chan *rxdma; - dma_cookie_t rx_cookie; - dma_cookie_t tx_cookie; - int drcmr_rx; - int drcmr_tx; - - int uart_irq; - int icp_irq; - - struct irlap_cb *irlap; - struct qos_info qos; - - iobuff_t tx_buff; - iobuff_t rx_buff; - - struct device *dev; - struct pxaficp_platform_data *pdata; - struct clk *fir_clk; - struct clk *sir_clk; - struct clk *cur_clk; -}; - -static int pxa_irda_set_speed(struct pxa_irda *si, int speed); - -static inline void pxa_irda_disable_clk(struct pxa_irda *si) -{ - if (si->cur_clk) - clk_disable_unprepare(si->cur_clk); - si->cur_clk = NULL; -} - -static inline void pxa_irda_enable_firclk(struct pxa_irda *si) -{ - si->cur_clk = si->fir_clk; - clk_prepare_enable(si->fir_clk); -} - -static inline void pxa_irda_enable_sirclk(struct pxa_irda *si) -{ - si->cur_clk = si->sir_clk; - clk_prepare_enable(si->sir_clk); -} - - -#define IS_FIR(si) ((si)->speed >= 4000000) -#define IRDA_FRAME_SIZE_LIMIT 2047 - -static void pxa_irda_fir_dma_rx_irq(void *data); -static void pxa_irda_fir_dma_tx_irq(void *data); - -inline static void pxa_irda_fir_dma_rx_start(struct pxa_irda *si) -{ - struct dma_async_tx_descriptor *tx; - - tx = dmaengine_prep_slave_single(si->rxdma, si->dma_rx_buff_phy, - IRDA_FRAME_SIZE_LIMIT, DMA_FROM_DEVICE, - DMA_PREP_INTERRUPT); - if (!tx) { - dev_err(si->dev, "prep_slave_sg() failed\n"); - return; - } - tx->callback = pxa_irda_fir_dma_rx_irq; - tx->callback_param = si; - si->rx_cookie = dmaengine_submit(tx); - dma_async_issue_pending(si->rxdma); -} - -inline static void pxa_irda_fir_dma_tx_start(struct pxa_irda *si) -{ - struct dma_async_tx_descriptor *tx; - - tx = dmaengine_prep_slave_single(si->txdma, si->dma_tx_buff_phy, - si->dma_tx_buff_len, DMA_TO_DEVICE, - DMA_PREP_INTERRUPT); - if (!tx) { - dev_err(si->dev, "prep_slave_sg() failed\n"); - return; - } - tx->callback = pxa_irda_fir_dma_tx_irq; - tx->callback_param = si; - si->tx_cookie = dmaengine_submit(tx); - dma_async_issue_pending(si->rxdma); -} - -/* - * Set the IrDA communications mode. - */ -static void pxa_irda_set_mode(struct pxa_irda *si, int mode) -{ - if (si->pdata->transceiver_mode) - si->pdata->transceiver_mode(si->dev, mode); - else { - if (gpio_is_valid(si->pdata->gpio_pwdown)) - gpio_set_value(si->pdata->gpio_pwdown, - !(mode & IR_OFF) ^ - !si->pdata->gpio_pwdown_inverted); - pxa2xx_transceiver_mode(si->dev, mode); - } -} - -/* - * Set the IrDA communications speed. - */ -static int pxa_irda_set_speed(struct pxa_irda *si, int speed) -{ - unsigned long flags; - unsigned int divisor; - - switch (speed) { - case 9600: case 19200: case 38400: - case 57600: case 115200: - - /* refer to PXA250/210 Developer's Manual 10-7 */ - /* BaudRate = 14.7456 MHz / (16*Divisor) */ - divisor = 14745600 / (16 * speed); - - local_irq_save(flags); - - if (IS_FIR(si)) { - /* stop RX DMA */ - dmaengine_terminate_all(si->rxdma); - /* disable FICP */ - ficp_writel(si, 0, ICCR0); - pxa_irda_disable_clk(si); - - /* set board transceiver to SIR mode */ - pxa_irda_set_mode(si, IR_SIRMODE); - - /* enable the STUART clock */ - pxa_irda_enable_sirclk(si); - } - - /* disable STUART first */ - stuart_writel(si, 0, STIER); - - /* access DLL & DLH */ - stuart_writel(si, stuart_readl(si, STLCR) | LCR_DLAB, STLCR); - stuart_writel(si, divisor & 0xff, STDLL); - stuart_writel(si, divisor >> 8, STDLH); - stuart_writel(si, stuart_readl(si, STLCR) & ~LCR_DLAB, STLCR); - - si->speed = speed; - stuart_writel(si, IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6, - STISR); - stuart_writel(si, IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE, - STIER); - - local_irq_restore(flags); - break; - - case 4000000: - local_irq_save(flags); - - /* disable STUART */ - stuart_writel(si, 0, STIER); - stuart_writel(si, 0, STISR); - pxa_irda_disable_clk(si); - - /* disable FICP first */ - ficp_writel(si, 0, ICCR0); - - /* set board transceiver to FIR mode */ - pxa_irda_set_mode(si, IR_FIRMODE); - - /* enable the FICP clock */ - pxa_irda_enable_firclk(si); - - si->speed = speed; - pxa_irda_fir_dma_rx_start(si); - ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); - - local_irq_restore(flags); - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* SIR interrupt service routine. */ -static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct pxa_irda *si = netdev_priv(dev); - int iir, lsr, data; - - iir = stuart_readl(si, STIIR); - - switch (iir & 0x0F) { - case 0x06: /* Receiver Line Status */ - lsr = stuart_readl(si, STLSR); - while (lsr & LSR_FIFOE) { - data = stuart_readl(si, STRBR); - if (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) { - printk(KERN_DEBUG "pxa_ir: sir receiving error\n"); - dev->stats.rx_errors++; - if (lsr & LSR_FE) - dev->stats.rx_frame_errors++; - if (lsr & LSR_OE) - dev->stats.rx_fifo_errors++; - } else { - dev->stats.rx_bytes++; - async_unwrap_char(dev, &dev->stats, - &si->rx_buff, data); - } - lsr = stuart_readl(si, STLSR); - } - si->last_clk = sched_clock(); - break; - - case 0x04: /* Received Data Available */ - /* forth through */ - - case 0x0C: /* Character Timeout Indication */ - do { - dev->stats.rx_bytes++; - async_unwrap_char(dev, &dev->stats, &si->rx_buff, - stuart_readl(si, STRBR)); - } while (stuart_readl(si, STLSR) & LSR_DR); - si->last_clk = sched_clock(); - break; - - case 0x02: /* Transmit FIFO Data Request */ - while ((si->tx_buff.len) && - (stuart_readl(si, STLSR) & LSR_TDRQ)) { - stuart_writel(si, *si->tx_buff.data++, STTHR); - si->tx_buff.len -= 1; - } - - if (si->tx_buff.len == 0) { - dev->stats.tx_packets++; - dev->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head; - - /* We need to ensure that the transmitter has finished. */ - while ((stuart_readl(si, STLSR) & LSR_TEMT) == 0) - cpu_relax(); - si->last_clk = sched_clock(); - - /* - * Ok, we've finished transmitting. Now enable - * the receiver. Sometimes we get a receive IRQ - * immediately after a transmit... - */ - if (si->newspeed) { - pxa_irda_set_speed(si, si->newspeed); - si->newspeed = 0; - } else { - /* enable IR Receiver, disable IR Transmitter */ - stuart_writel(si, IrSR_IR_RECEIVE_ON | - IrSR_XMODE_PULSE_1_6, STISR); - /* enable STUART and receive interrupts */ - stuart_writel(si, IER_UUE | IER_RLSE | - IER_RAVIE | IER_RTIOE, STIER); - } - /* I'm hungry! */ - netif_wake_queue(dev); - } - break; - } - - return IRQ_HANDLED; -} - -/* FIR Receive DMA interrupt handler */ -static void pxa_irda_fir_dma_rx_irq(void *data) -{ - struct net_device *dev = data; - struct pxa_irda *si = netdev_priv(dev); - - dmaengine_terminate_all(si->rxdma); - netdev_dbg(dev, "pxa_ir: fir rx dma bus error\n"); -} - -/* FIR Transmit DMA interrupt handler */ -static void pxa_irda_fir_dma_tx_irq(void *data) -{ - struct net_device *dev = data; - struct pxa_irda *si = netdev_priv(dev); - - dmaengine_terminate_all(si->txdma); - if (dmaengine_tx_status(si->txdma, si->tx_cookie, NULL) == DMA_ERROR) { - dev->stats.tx_errors++; - } else { - dev->stats.tx_packets++; - dev->stats.tx_bytes += si->dma_tx_buff_len; - } - - while (ficp_readl(si, ICSR1) & ICSR1_TBY) - cpu_relax(); - si->last_clk = sched_clock(); - - /* - * HACK: It looks like the TBY bit is dropped too soon. - * Without this delay things break. - */ - udelay(120); - - if (si->newspeed) { - pxa_irda_set_speed(si, si->newspeed); - si->newspeed = 0; - } else { - int i = 64; - - ficp_writel(si, 0, ICCR0); - pxa_irda_fir_dma_rx_start(si); - while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--) - ficp_readl(si, ICDR); - ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); - - if (i < 0) - printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n"); - } - netif_wake_queue(dev); -} - -/* EIF(Error in FIFO/End in Frame) handler for FIR */ -static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, int icsr0) -{ - unsigned int len, stat, data; - struct dma_tx_state state; - - /* Get the current data position. */ - - dmaengine_tx_status(si->rxdma, si->rx_cookie, &state); - len = IRDA_FRAME_SIZE_LIMIT - state.residue; - - do { - /* Read Status, and then Data. */ - stat = ficp_readl(si, ICSR1); - rmb(); - data = ficp_readl(si, ICDR); - - if (stat & (ICSR1_CRE | ICSR1_ROR)) { - dev->stats.rx_errors++; - if (stat & ICSR1_CRE) { - printk(KERN_DEBUG "pxa_ir: fir receive CRC error\n"); - dev->stats.rx_crc_errors++; - } - if (stat & ICSR1_ROR) { - printk(KERN_DEBUG "pxa_ir: fir receive overrun\n"); - dev->stats.rx_over_errors++; - } - } else { - si->dma_rx_buff[len++] = data; - } - /* If we hit the end of frame, there's no point in continuing. */ - if (stat & ICSR1_EOF) - break; - } while (ficp_readl(si, ICSR0) & ICSR0_EIF); - - if (stat & ICSR1_EOF) { - /* end of frame. */ - struct sk_buff *skb; - - if (icsr0 & ICSR0_FRE) { - printk(KERN_ERR "pxa_ir: dropping erroneous frame\n"); - dev->stats.rx_dropped++; - return; - } - - skb = alloc_skb(len+1,GFP_ATOMIC); - if (!skb) { - printk(KERN_ERR "pxa_ir: fir out of memory for receive skb\n"); - dev->stats.rx_dropped++; - return; - } - - /* Align IP header to 20 bytes */ - skb_reserve(skb, 1); - skb_copy_to_linear_data(skb, si->dma_rx_buff, len); - skb_put(skb, len); - - /* Feed it to IrLAP */ - skb->dev = dev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - } -} - -/* FIR interrupt handler */ -static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct pxa_irda *si = netdev_priv(dev); - int icsr0, i = 64; - - /* stop RX DMA */ - dmaengine_terminate_all(si->rxdma); - si->last_clk = sched_clock(); - icsr0 = ficp_readl(si, ICSR0); - - if (icsr0 & (ICSR0_FRE | ICSR0_RAB)) { - if (icsr0 & ICSR0_FRE) { - printk(KERN_DEBUG "pxa_ir: fir receive frame error\n"); - dev->stats.rx_frame_errors++; - } else { - printk(KERN_DEBUG "pxa_ir: fir receive abort\n"); - dev->stats.rx_errors++; - } - ficp_writel(si, icsr0 & (ICSR0_FRE | ICSR0_RAB), ICSR0); - } - - if (icsr0 & ICSR0_EIF) { - /* An error in FIFO occurred, or there is a end of frame */ - pxa_irda_fir_irq_eif(si, dev, icsr0); - } - - ficp_writel(si, 0, ICCR0); - pxa_irda_fir_dma_rx_start(si); - while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--) - ficp_readl(si, ICDR); - ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); - - if (i < 0) - printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n"); - - return IRQ_HANDLED; -} - -/* hard_xmit interface of irda device */ -static int pxa_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct pxa_irda *si = netdev_priv(dev); - int speed = irda_get_next_speed(skb); - - /* - * Does this packet contain a request to change the interface - * speed? If so, remember it until we complete the transmission - * of this frame. - */ - if (speed != si->speed && speed != -1) - si->newspeed = speed; - - /* - * If this is an empty frame, we can bypass a lot. - */ - if (skb->len == 0) { - if (si->newspeed) { - si->newspeed = 0; - pxa_irda_set_speed(si, speed); - } - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - netif_stop_queue(dev); - - if (!IS_FIR(si)) { - si->tx_buff.data = si->tx_buff.head; - si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, si->tx_buff.truesize); - - /* Disable STUART interrupts and switch to transmit mode. */ - stuart_writel(si, 0, STIER); - stuart_writel(si, IrSR_IR_TRANSMIT_ON | IrSR_XMODE_PULSE_1_6, - STISR); - - /* enable STUART and transmit interrupts */ - stuart_writel(si, IER_UUE | IER_TIE, STIER); - } else { - unsigned long mtt = irda_get_mtt(skb); - - si->dma_tx_buff_len = skb->len; - skb_copy_from_linear_data(skb, si->dma_tx_buff, skb->len); - - if (mtt) - while ((sched_clock() - si->last_clk) * 1000 < mtt) - cpu_relax(); - - /* stop RX DMA, disable FICP */ - dmaengine_terminate_all(si->rxdma); - ficp_writel(si, 0, ICCR0); - - pxa_irda_fir_dma_tx_start(si); - ficp_writel(si, ICCR0_ITR | ICCR0_TXE, ICCR0); - } - - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -static int pxa_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) -{ - struct if_irda_req *rq = (struct if_irda_req *)ifreq; - struct pxa_irda *si = netdev_priv(dev); - int ret; - - switch (cmd) { - case SIOCSBANDWIDTH: - ret = -EPERM; - if (capable(CAP_NET_ADMIN)) { - /* - * We are unable to set the speed if the - * device is not running. - */ - if (netif_running(dev)) { - ret = pxa_irda_set_speed(si, - rq->ifr_baudrate); - } else { - printk(KERN_INFO "pxa_ir: SIOCSBANDWIDTH: !netif_running\n"); - ret = 0; - } - } - break; - - case SIOCSMEDIABUSY: - ret = -EPERM; - if (capable(CAP_NET_ADMIN)) { - irda_device_set_media_busy(dev, TRUE); - ret = 0; - } - break; - - case SIOCGRECEIVING: - ret = 0; - rq->ifr_receiving = IS_FIR(si) ? 0 - : si->rx_buff.state != OUTSIDE_FRAME; - break; - - default: - ret = -EOPNOTSUPP; - break; - } - - return ret; -} - -static void pxa_irda_startup(struct pxa_irda *si) -{ - /* Disable STUART interrupts */ - stuart_writel(si, 0, STIER); - /* enable STUART interrupt to the processor */ - stuart_writel(si, MCR_OUT2, STMCR); - /* configure SIR frame format: StartBit - Data 7 ... Data 0 - Stop Bit */ - stuart_writel(si, LCR_WLS0 | LCR_WLS1, STLCR); - /* enable FIFO, we use FIFO to improve performance */ - stuart_writel(si, FCR_TRFIFOE | FCR_ITL_32, STFCR); - - /* disable FICP */ - ficp_writel(si, 0, ICCR0); - /* configure FICP ICCR2 */ - ficp_writel(si, ICCR2_TXP | ICCR2_TRIG_32, ICCR2); - - /* force SIR reinitialization */ - si->speed = 4000000; - pxa_irda_set_speed(si, 9600); - - printk(KERN_DEBUG "pxa_ir: irda startup\n"); -} - -static void pxa_irda_shutdown(struct pxa_irda *si) -{ - unsigned long flags; - - local_irq_save(flags); - - /* disable STUART and interrupt */ - stuart_writel(si, 0, STIER); - /* disable STUART SIR mode */ - stuart_writel(si, 0, STISR); - - /* disable DMA */ - dmaengine_terminate_all(si->rxdma); - dmaengine_terminate_all(si->txdma); - /* disable FICP */ - ficp_writel(si, 0, ICCR0); - - /* disable the STUART or FICP clocks */ - pxa_irda_disable_clk(si); - - local_irq_restore(flags); - - /* power off board transceiver */ - pxa_irda_set_mode(si, IR_OFF); - - printk(KERN_DEBUG "pxa_ir: irda shutdown\n"); -} - -static int pxa_irda_start(struct net_device *dev) -{ - struct pxa_irda *si = netdev_priv(dev); - dma_cap_mask_t mask; - struct dma_slave_config config; - struct pxad_param param; - int err; - - si->speed = 9600; - - err = request_irq(si->uart_irq, pxa_irda_sir_irq, 0, dev->name, dev); - if (err) - goto err_irq1; - - err = request_irq(si->icp_irq, pxa_irda_fir_irq, 0, dev->name, dev); - if (err) - goto err_irq2; - - /* - * The interrupt must remain disabled for now. - */ - disable_irq(si->uart_irq); - disable_irq(si->icp_irq); - - err = -EBUSY; - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - param.prio = PXAD_PRIO_LOWEST; - - memset(&config, 0, sizeof(config)); - config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - config.src_addr = (dma_addr_t)si->irda_base + ICDR; - config.dst_addr = (dma_addr_t)si->irda_base + ICDR; - config.src_maxburst = 32; - config.dst_maxburst = 32; - - param.drcmr = si->drcmr_rx; - si->rxdma = dma_request_slave_channel_compat(mask, pxad_filter_fn, - ¶m, &dev->dev, "rx"); - if (!si->rxdma) - goto err_rx_dma; - - param.drcmr = si->drcmr_tx; - si->txdma = dma_request_slave_channel_compat(mask, pxad_filter_fn, - ¶m, &dev->dev, "tx"); - if (!si->txdma) - goto err_tx_dma; - - err = dmaengine_slave_config(si->rxdma, &config); - if (err) - goto err_dma_rx_buff; - err = dmaengine_slave_config(si->txdma, &config); - if (err) - goto err_dma_rx_buff; - - err = -ENOMEM; - si->dma_rx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, - &si->dma_rx_buff_phy, GFP_KERNEL); - if (!si->dma_rx_buff) - goto err_dma_rx_buff; - - si->dma_tx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, - &si->dma_tx_buff_phy, GFP_KERNEL); - if (!si->dma_tx_buff) - goto err_dma_tx_buff; - - /* Setup the serial port for the initial speed. */ - pxa_irda_startup(si); - - /* - * Open a new IrLAP layer instance. - */ - si->irlap = irlap_open(dev, &si->qos, "pxa"); - err = -ENOMEM; - if (!si->irlap) - goto err_irlap; - - /* - * Now enable the interrupt and start the queue - */ - enable_irq(si->uart_irq); - enable_irq(si->icp_irq); - netif_start_queue(dev); - - printk(KERN_DEBUG "pxa_ir: irda driver opened\n"); - - return 0; - -err_irlap: - pxa_irda_shutdown(si); - dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_tx_buff, si->dma_tx_buff_phy); -err_dma_tx_buff: - dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_rx_buff, si->dma_rx_buff_phy); -err_dma_rx_buff: - dma_release_channel(si->txdma); -err_tx_dma: - dma_release_channel(si->rxdma); -err_rx_dma: - free_irq(si->icp_irq, dev); -err_irq2: - free_irq(si->uart_irq, dev); -err_irq1: - - return err; -} - -static int pxa_irda_stop(struct net_device *dev) -{ - struct pxa_irda *si = netdev_priv(dev); - - netif_stop_queue(dev); - - pxa_irda_shutdown(si); - - /* Stop IrLAP */ - if (si->irlap) { - irlap_close(si->irlap); - si->irlap = NULL; - } - - free_irq(si->uart_irq, dev); - free_irq(si->icp_irq, dev); - - dmaengine_terminate_all(si->rxdma); - dmaengine_terminate_all(si->txdma); - dma_release_channel(si->rxdma); - dma_release_channel(si->txdma); - - if (si->dma_rx_buff) - dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_tx_buff, si->dma_tx_buff_phy); - if (si->dma_tx_buff) - dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_rx_buff, si->dma_rx_buff_phy); - - printk(KERN_DEBUG "pxa_ir: irda driver closed\n"); - return 0; -} - -static int pxa_irda_suspend(struct platform_device *_dev, pm_message_t state) -{ - struct net_device *dev = platform_get_drvdata(_dev); - struct pxa_irda *si; - - if (dev && netif_running(dev)) { - si = netdev_priv(dev); - netif_device_detach(dev); - pxa_irda_shutdown(si); - } - - return 0; -} - -static int pxa_irda_resume(struct platform_device *_dev) -{ - struct net_device *dev = platform_get_drvdata(_dev); - struct pxa_irda *si; - - if (dev && netif_running(dev)) { - si = netdev_priv(dev); - pxa_irda_startup(si); - netif_device_attach(dev); - netif_wake_queue(dev); - } - - return 0; -} - - -static int pxa_irda_init_iobuf(iobuff_t *io, int size) -{ - io->head = kmalloc(size, GFP_KERNEL | GFP_DMA); - if (io->head != NULL) { - io->truesize = size; - io->in_frame = FALSE; - io->state = OUTSIDE_FRAME; - io->data = io->head; - } - return io->head ? 0 : -ENOMEM; -} - -static const struct net_device_ops pxa_irda_netdev_ops = { - .ndo_open = pxa_irda_start, - .ndo_stop = pxa_irda_stop, - .ndo_start_xmit = pxa_irda_hard_xmit, - .ndo_do_ioctl = pxa_irda_ioctl, -}; - -static int pxa_irda_probe(struct platform_device *pdev) -{ - struct net_device *dev; - struct resource *res; - struct pxa_irda *si; - void __iomem *ficp, *stuart; - unsigned int baudrate_mask; - int err; - - if (!pdev->dev.platform_data) - return -ENODEV; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ficp = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(ficp)) { - dev_err(&pdev->dev, "resource ficp not defined\n"); - return PTR_ERR(ficp); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - stuart = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(stuart)) { - dev_err(&pdev->dev, "resource stuart not defined\n"); - return PTR_ERR(stuart); - } - - dev = alloc_irdadev(sizeof(struct pxa_irda)); - if (!dev) { - err = -ENOMEM; - goto err_mem_1; - } - - SET_NETDEV_DEV(dev, &pdev->dev); - si = netdev_priv(dev); - si->dev = &pdev->dev; - si->pdata = pdev->dev.platform_data; - - si->irda_base = ficp; - si->stuart_base = stuart; - si->uart_irq = platform_get_irq(pdev, 0); - si->icp_irq = platform_get_irq(pdev, 1); - - si->sir_clk = devm_clk_get(&pdev->dev, "UARTCLK"); - si->fir_clk = devm_clk_get(&pdev->dev, "FICPCLK"); - if (IS_ERR(si->sir_clk) || IS_ERR(si->fir_clk)) { - err = PTR_ERR(IS_ERR(si->sir_clk) ? si->sir_clk : si->fir_clk); - goto err_mem_4; - } - - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (res) - si->drcmr_rx = res->start; - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (res) - si->drcmr_tx = res->start; - - /* - * Initialise the SIR buffers - */ - err = pxa_irda_init_iobuf(&si->rx_buff, 14384); - if (err) - goto err_mem_4; - err = pxa_irda_init_iobuf(&si->tx_buff, 4000); - if (err) - goto err_mem_5; - - if (gpio_is_valid(si->pdata->gpio_pwdown)) { - err = gpio_request(si->pdata->gpio_pwdown, "IrDA switch"); - if (err) - goto err_startup; - err = gpio_direction_output(si->pdata->gpio_pwdown, - !si->pdata->gpio_pwdown_inverted); - if (err) { - gpio_free(si->pdata->gpio_pwdown); - goto err_startup; - } - } - - if (si->pdata->startup) { - err = si->pdata->startup(si->dev); - if (err) - goto err_startup; - } - - if (gpio_is_valid(si->pdata->gpio_pwdown) && si->pdata->startup) - dev_warn(si->dev, "gpio_pwdown and startup() both defined!\n"); - - dev->netdev_ops = &pxa_irda_netdev_ops; - - irda_init_max_qos_capabilies(&si->qos); - - baudrate_mask = 0; - if (si->pdata->transceiver_cap & IR_SIRMODE) - baudrate_mask |= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - if (si->pdata->transceiver_cap & IR_FIRMODE) - baudrate_mask |= IR_4000000 << 8; - - si->qos.baud_rate.bits &= baudrate_mask; - si->qos.min_turn_time.bits = 7; /* 1ms or more */ - - irda_qos_bits_to_value(&si->qos); - - err = register_netdev(dev); - - if (err == 0) - platform_set_drvdata(pdev, dev); - - if (err) { - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); -err_startup: - kfree(si->tx_buff.head); -err_mem_5: - kfree(si->rx_buff.head); -err_mem_4: - free_netdev(dev); - } -err_mem_1: - return err; -} - -static int pxa_irda_remove(struct platform_device *_dev) -{ - struct net_device *dev = platform_get_drvdata(_dev); - - if (dev) { - struct pxa_irda *si = netdev_priv(dev); - unregister_netdev(dev); - if (gpio_is_valid(si->pdata->gpio_pwdown)) - gpio_free(si->pdata->gpio_pwdown); - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); - kfree(si->tx_buff.head); - kfree(si->rx_buff.head); - free_netdev(dev); - } - - return 0; -} - -static struct platform_driver pxa_ir_driver = { - .driver = { - .name = "pxa2xx-ir", - }, - .probe = pxa_irda_probe, - .remove = pxa_irda_remove, - .suspend = pxa_irda_suspend, - .resume = pxa_irda_resume, -}; - -module_platform_driver(pxa_ir_driver); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pxa2xx-ir"); diff --git a/drivers/staging/irda/drivers/sa1100_ir.c b/drivers/staging/irda/drivers/sa1100_ir.c deleted file mode 100644 index b6e44ff4e373..000000000000 --- a/drivers/staging/irda/drivers/sa1100_ir.c +++ /dev/null @@ -1,1150 +0,0 @@ -/* - * linux/drivers/net/irda/sa1100_ir.c - * - * Copyright (C) 2000-2001 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Infra-red driver for the StrongARM SA1100 embedded microprocessor - * - * Note that we don't have to worry about the SA1111's DMA bugs in here, - * so we use the straight forward dma_map_* functions with a null pointer. - * - * This driver takes one kernel command line parameter, sa1100ir=, with - * the following options: - * max_rate:baudrate - set the maximum baud rate - * power_level:level - set the transmitter power level - * tx_lpm:0|1 - set transmit low power mode - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -static int power_level = 3; -static int tx_lpm; -static int max_rate = 4000000; - -struct sa1100_buf { - struct device *dev; - struct sk_buff *skb; - struct scatterlist sg; - struct dma_chan *chan; - dma_cookie_t cookie; -}; - -struct sa1100_irda { - unsigned char utcr4; - unsigned char power; - unsigned char open; - - int speed; - int newspeed; - - struct sa1100_buf dma_rx; - struct sa1100_buf dma_tx; - - struct device *dev; - struct irda_platform_data *pdata; - struct irlap_cb *irlap; - struct qos_info qos; - - iobuff_t tx_buff; - iobuff_t rx_buff; - - int (*tx_start)(struct sk_buff *, struct net_device *, struct sa1100_irda *); - irqreturn_t (*irq)(struct net_device *, struct sa1100_irda *); -}; - -static int sa1100_irda_set_speed(struct sa1100_irda *, int); - -#define IS_FIR(si) ((si)->speed >= 4000000) - -#define HPSIR_MAX_RXLEN 2047 - -static struct dma_slave_config sa1100_irda_sir_tx = { - .direction = DMA_TO_DEVICE, - .dst_addr = __PREG(Ser2UTDR), - .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, - .dst_maxburst = 4, -}; - -static struct dma_slave_config sa1100_irda_fir_rx = { - .direction = DMA_FROM_DEVICE, - .src_addr = __PREG(Ser2HSDR), - .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, - .src_maxburst = 8, -}; - -static struct dma_slave_config sa1100_irda_fir_tx = { - .direction = DMA_TO_DEVICE, - .dst_addr = __PREG(Ser2HSDR), - .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, - .dst_maxburst = 8, -}; - -static unsigned sa1100_irda_dma_xferred(struct sa1100_buf *buf) -{ - struct dma_chan *chan = buf->chan; - struct dma_tx_state state; - enum dma_status status; - - status = chan->device->device_tx_status(chan, buf->cookie, &state); - if (status != DMA_PAUSED) - return 0; - - return sg_dma_len(&buf->sg) - state.residue; -} - -static int sa1100_irda_dma_request(struct device *dev, struct sa1100_buf *buf, - const char *name, struct dma_slave_config *cfg) -{ - dma_cap_mask_t m; - int ret; - - dma_cap_zero(m); - dma_cap_set(DMA_SLAVE, m); - - buf->chan = dma_request_channel(m, sa11x0_dma_filter_fn, (void *)name); - if (!buf->chan) { - dev_err(dev, "unable to request DMA channel for %s\n", - name); - return -ENOENT; - } - - ret = dmaengine_slave_config(buf->chan, cfg); - if (ret) - dev_warn(dev, "DMA slave_config for %s returned %d\n", - name, ret); - - buf->dev = buf->chan->device->dev; - - return 0; -} - -static void sa1100_irda_dma_start(struct sa1100_buf *buf, - enum dma_transfer_direction dir, dma_async_tx_callback cb, void *cb_p) -{ - struct dma_async_tx_descriptor *desc; - struct dma_chan *chan = buf->chan; - - desc = dmaengine_prep_slave_sg(chan, &buf->sg, 1, dir, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (desc) { - desc->callback = cb; - desc->callback_param = cb_p; - buf->cookie = dmaengine_submit(desc); - dma_async_issue_pending(chan); - } -} - -/* - * Allocate and map the receive buffer, unless it is already allocated. - */ -static int sa1100_irda_rx_alloc(struct sa1100_irda *si) -{ - if (si->dma_rx.skb) - return 0; - - si->dma_rx.skb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC); - if (!si->dma_rx.skb) { - printk(KERN_ERR "sa1100_ir: out of memory for RX SKB\n"); - return -ENOMEM; - } - - /* - * Align any IP headers that may be contained - * within the frame. - */ - skb_reserve(si->dma_rx.skb, 1); - - sg_set_buf(&si->dma_rx.sg, si->dma_rx.skb->data, HPSIR_MAX_RXLEN); - if (dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE) == 0) { - dev_kfree_skb_any(si->dma_rx.skb); - return -ENOMEM; - } - - return 0; -} - -/* - * We want to get here as soon as possible, and get the receiver setup. - * We use the existing buffer. - */ -static void sa1100_irda_rx_dma_start(struct sa1100_irda *si) -{ - if (!si->dma_rx.skb) { - printk(KERN_ERR "sa1100_ir: rx buffer went missing\n"); - return; - } - - /* - * First empty receive FIFO - */ - Ser2HSCR0 = HSCR0_HSSP; - - /* - * Enable the DMA, receiver and receive interrupt. - */ - dmaengine_terminate_all(si->dma_rx.chan); - sa1100_irda_dma_start(&si->dma_rx, DMA_DEV_TO_MEM, NULL, NULL); - - Ser2HSCR0 = HSCR0_HSSP | HSCR0_RXE; -} - -static void sa1100_irda_check_speed(struct sa1100_irda *si) -{ - if (si->newspeed) { - sa1100_irda_set_speed(si, si->newspeed); - si->newspeed = 0; - } -} - -/* - * HP-SIR format support. - */ -static void sa1100_irda_sirtxdma_irq(void *id) -{ - struct net_device *dev = id; - struct sa1100_irda *si = netdev_priv(dev); - - dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE); - dev_kfree_skb(si->dma_tx.skb); - si->dma_tx.skb = NULL; - - dev->stats.tx_packets++; - dev->stats.tx_bytes += sg_dma_len(&si->dma_tx.sg); - - /* We need to ensure that the transmitter has finished. */ - do - rmb(); - while (Ser2UTSR1 & UTSR1_TBY); - - /* - * Ok, we've finished transmitting. Now enable the receiver. - * Sometimes we get a receive IRQ immediately after a transmit... - */ - Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; - Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; - - sa1100_irda_check_speed(si); - - /* I'm hungry! */ - netif_wake_queue(dev); -} - -static int sa1100_irda_sir_tx_start(struct sk_buff *skb, struct net_device *dev, - struct sa1100_irda *si) -{ - si->tx_buff.data = si->tx_buff.head; - si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, - si->tx_buff.truesize); - - si->dma_tx.skb = skb; - sg_set_buf(&si->dma_tx.sg, si->tx_buff.data, si->tx_buff.len); - if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) { - si->dma_tx.skb = NULL; - netif_wake_queue(dev); - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - - sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_sirtxdma_irq, dev); - - /* - * The mean turn-around time is enforced by XBOF padding, - * so we don't have to do anything special here. - */ - Ser2UTCR3 = UTCR3_TXE; - - return NETDEV_TX_OK; -} - -static irqreturn_t sa1100_irda_sir_irq(struct net_device *dev, struct sa1100_irda *si) -{ - int status; - - status = Ser2UTSR0; - - /* - * Deal with any receive errors first. The bytes in error may be - * the only bytes in the receive FIFO, so we do this first. - */ - while (status & UTSR0_EIF) { - int stat, data; - - stat = Ser2UTSR1; - data = Ser2UTDR; - - if (stat & (UTSR1_FRE | UTSR1_ROR)) { - dev->stats.rx_errors++; - if (stat & UTSR1_FRE) - dev->stats.rx_frame_errors++; - if (stat & UTSR1_ROR) - dev->stats.rx_fifo_errors++; - } else - async_unwrap_char(dev, &dev->stats, &si->rx_buff, data); - - status = Ser2UTSR0; - } - - /* - * We must clear certain bits. - */ - Ser2UTSR0 = status & (UTSR0_RID | UTSR0_RBB | UTSR0_REB); - - if (status & UTSR0_RFS) { - /* - * There are at least 4 bytes in the FIFO. Read 3 bytes - * and leave the rest to the block below. - */ - async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); - async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); - async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); - } - - if (status & (UTSR0_RFS | UTSR0_RID)) { - /* - * Fifo contains more than 1 character. - */ - do { - async_unwrap_char(dev, &dev->stats, &si->rx_buff, - Ser2UTDR); - } while (Ser2UTSR1 & UTSR1_RNE); - - } - - return IRQ_HANDLED; -} - -/* - * FIR format support. - */ -static void sa1100_irda_firtxdma_irq(void *id) -{ - struct net_device *dev = id; - struct sa1100_irda *si = netdev_priv(dev); - struct sk_buff *skb; - - /* - * Wait for the transmission to complete. Unfortunately, - * the hardware doesn't give us an interrupt to indicate - * "end of frame". - */ - do - rmb(); - while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY); - - /* - * Clear the transmit underrun bit. - */ - Ser2HSSR0 = HSSR0_TUR; - - /* - * Do we need to change speed? Note that we're lazy - * here - we don't free the old dma_rx.skb. We don't need - * to allocate a buffer either. - */ - sa1100_irda_check_speed(si); - - /* - * Start reception. This disables the transmitter for - * us. This will be using the existing RX buffer. - */ - sa1100_irda_rx_dma_start(si); - - /* Account and free the packet. */ - skb = si->dma_tx.skb; - if (skb) { - dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, - DMA_TO_DEVICE); - dev->stats.tx_packets ++; - dev->stats.tx_bytes += skb->len; - dev_kfree_skb_irq(skb); - si->dma_tx.skb = NULL; - } - - /* - * Make sure that the TX queue is available for sending - * (for retries). TX has priority over RX at all times. - */ - netif_wake_queue(dev); -} - -static int sa1100_irda_fir_tx_start(struct sk_buff *skb, struct net_device *dev, - struct sa1100_irda *si) -{ - int mtt = irda_get_mtt(skb); - - si->dma_tx.skb = skb; - sg_set_buf(&si->dma_tx.sg, skb->data, skb->len); - if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) { - si->dma_tx.skb = NULL; - netif_wake_queue(dev); - dev->stats.tx_dropped++; - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_firtxdma_irq, dev); - - /* - * If we have a mean turn-around time, impose the specified - * specified delay. We could shorten this by timing from - * the point we received the packet. - */ - if (mtt) - udelay(mtt); - - Ser2HSCR0 = HSCR0_HSSP | HSCR0_TXE; - - return NETDEV_TX_OK; -} - -static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev) -{ - struct sk_buff *skb = si->dma_rx.skb; - unsigned int len, stat, data; - - if (!skb) { - printk(KERN_ERR "sa1100_ir: SKB is NULL!\n"); - return; - } - - /* - * Get the current data position. - */ - len = sa1100_irda_dma_xferred(&si->dma_rx); - if (len > HPSIR_MAX_RXLEN) - len = HPSIR_MAX_RXLEN; - dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE); - - do { - /* - * Read Status, and then Data. - */ - stat = Ser2HSSR1; - rmb(); - data = Ser2HSDR; - - if (stat & (HSSR1_CRE | HSSR1_ROR)) { - dev->stats.rx_errors++; - if (stat & HSSR1_CRE) - dev->stats.rx_crc_errors++; - if (stat & HSSR1_ROR) - dev->stats.rx_frame_errors++; - } else - skb->data[len++] = data; - - /* - * If we hit the end of frame, there's - * no point in continuing. - */ - if (stat & HSSR1_EOF) - break; - } while (Ser2HSSR0 & HSSR0_EIF); - - if (stat & HSSR1_EOF) { - si->dma_rx.skb = NULL; - - skb_put(skb, len); - skb->dev = dev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - - /* - * Before we pass the buffer up, allocate a new one. - */ - sa1100_irda_rx_alloc(si); - - netif_rx(skb); - } else { - /* - * Remap the buffer - it was previously mapped, and we - * hope that this succeeds. - */ - dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE); - } -} - -/* - * We only have to handle RX events here; transmit events go via the TX - * DMA handler. We disable RX, process, and the restart RX. - */ -static irqreturn_t sa1100_irda_fir_irq(struct net_device *dev, struct sa1100_irda *si) -{ - /* - * Stop RX DMA - */ - dmaengine_pause(si->dma_rx.chan); - - /* - * Framing error - we throw away the packet completely. - * Clearing RXE flushes the error conditions and data - * from the fifo. - */ - if (Ser2HSSR0 & (HSSR0_FRE | HSSR0_RAB)) { - dev->stats.rx_errors++; - - if (Ser2HSSR0 & HSSR0_FRE) - dev->stats.rx_frame_errors++; - - /* - * Clear out the DMA... - */ - Ser2HSCR0 = HSCR0_HSSP; - - /* - * Clear selected status bits now, so we - * don't miss them next time around. - */ - Ser2HSSR0 = HSSR0_FRE | HSSR0_RAB; - } - - /* - * Deal with any receive errors. The any of the lowest - * 8 bytes in the FIFO may contain an error. We must read - * them one by one. The "error" could even be the end of - * packet! - */ - if (Ser2HSSR0 & HSSR0_EIF) - sa1100_irda_fir_error(si, dev); - - /* - * No matter what happens, we must restart reception. - */ - sa1100_irda_rx_dma_start(si); - - return IRQ_HANDLED; -} - -/* - * Set the IrDA communications speed. - */ -static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed) -{ - unsigned long flags; - int brd, ret = -EINVAL; - - switch (speed) { - case 9600: case 19200: case 38400: - case 57600: case 115200: - brd = 3686400 / (16 * speed) - 1; - - /* Stop the receive DMA, and configure transmit. */ - if (IS_FIR(si)) { - dmaengine_terminate_all(si->dma_rx.chan); - dmaengine_slave_config(si->dma_tx.chan, - &sa1100_irda_sir_tx); - } - - local_irq_save(flags); - - Ser2UTCR3 = 0; - Ser2HSCR0 = HSCR0_UART; - - Ser2UTCR1 = brd >> 8; - Ser2UTCR2 = brd; - - /* - * Clear status register - */ - Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; - Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; - - if (si->pdata->set_speed) - si->pdata->set_speed(si->dev, speed); - - si->speed = speed; - si->tx_start = sa1100_irda_sir_tx_start; - si->irq = sa1100_irda_sir_irq; - - local_irq_restore(flags); - ret = 0; - break; - - case 4000000: - if (!IS_FIR(si)) - dmaengine_slave_config(si->dma_tx.chan, - &sa1100_irda_fir_tx); - - local_irq_save(flags); - - Ser2HSSR0 = 0xff; - Ser2HSCR0 = HSCR0_HSSP; - Ser2UTCR3 = 0; - - si->speed = speed; - si->tx_start = sa1100_irda_fir_tx_start; - si->irq = sa1100_irda_fir_irq; - - if (si->pdata->set_speed) - si->pdata->set_speed(si->dev, speed); - - sa1100_irda_rx_alloc(si); - sa1100_irda_rx_dma_start(si); - - local_irq_restore(flags); - - break; - - default: - break; - } - - return ret; -} - -/* - * Control the power state of the IrDA transmitter. - * State: - * 0 - off - * 1 - short range, lowest power - * 2 - medium range, medium power - * 3 - maximum range, high power - * - * Currently, only assabet is known to support this. - */ -static int -__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state) -{ - int ret = 0; - if (si->pdata->set_power) - ret = si->pdata->set_power(si->dev, state); - return ret; -} - -static inline int -sa1100_set_power(struct sa1100_irda *si, unsigned int state) -{ - int ret; - - ret = __sa1100_irda_set_power(si, state); - if (ret == 0) - si->power = state; - - return ret; -} - -static irqreturn_t sa1100_irda_irq(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct sa1100_irda *si = netdev_priv(dev); - - return si->irq(dev, si); -} - -static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct sa1100_irda *si = netdev_priv(dev); - int speed = irda_get_next_speed(skb); - - /* - * Does this packet contain a request to change the interface - * speed? If so, remember it until we complete the transmission - * of this frame. - */ - if (speed != si->speed && speed != -1) - si->newspeed = speed; - - /* If this is an empty frame, we can bypass a lot. */ - if (skb->len == 0) { - sa1100_irda_check_speed(si); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - netif_stop_queue(dev); - - /* We must not already have a skb to transmit... */ - BUG_ON(si->dma_tx.skb); - - return si->tx_start(skb, dev, si); -} - -static int -sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) -{ - struct if_irda_req *rq = (struct if_irda_req *)ifreq; - struct sa1100_irda *si = netdev_priv(dev); - int ret = -EOPNOTSUPP; - - switch (cmd) { - case SIOCSBANDWIDTH: - if (capable(CAP_NET_ADMIN)) { - /* - * We are unable to set the speed if the - * device is not running. - */ - if (si->open) { - ret = sa1100_irda_set_speed(si, - rq->ifr_baudrate); - } else { - printk("sa1100_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n"); - ret = 0; - } - } - break; - - case SIOCSMEDIABUSY: - ret = -EPERM; - if (capable(CAP_NET_ADMIN)) { - irda_device_set_media_busy(dev, TRUE); - ret = 0; - } - break; - - case SIOCGRECEIVING: - rq->ifr_receiving = IS_FIR(si) ? 0 - : si->rx_buff.state != OUTSIDE_FRAME; - break; - - default: - break; - } - - return ret; -} - -static int sa1100_irda_startup(struct sa1100_irda *si) -{ - int ret; - - /* - * Ensure that the ports for this device are setup correctly. - */ - if (si->pdata->startup) { - ret = si->pdata->startup(si->dev); - if (ret) - return ret; - } - - /* - * Configure PPC for IRDA - we want to drive TXD2 low. - * We also want to drive this pin low during sleep. - */ - PPSR &= ~PPC_TXD2; - PSDR &= ~PPC_TXD2; - PPDR |= PPC_TXD2; - - /* - * Enable HP-SIR modulation, and ensure that the port is disabled. - */ - Ser2UTCR3 = 0; - Ser2HSCR0 = HSCR0_UART; - Ser2UTCR4 = si->utcr4; - Ser2UTCR0 = UTCR0_8BitData; - Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL; - - /* - * Clear status register - */ - Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; - - ret = sa1100_irda_set_speed(si, si->speed = 9600); - if (ret) { - Ser2UTCR3 = 0; - Ser2HSCR0 = 0; - - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); - } - - return ret; -} - -static void sa1100_irda_shutdown(struct sa1100_irda *si) -{ - /* - * Stop all DMA activity. - */ - dmaengine_terminate_all(si->dma_rx.chan); - dmaengine_terminate_all(si->dma_tx.chan); - - /* Disable the port. */ - Ser2UTCR3 = 0; - Ser2HSCR0 = 0; - - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); -} - -static int sa1100_irda_start(struct net_device *dev) -{ - struct sa1100_irda *si = netdev_priv(dev); - int err; - - si->speed = 9600; - - err = sa1100_irda_dma_request(si->dev, &si->dma_rx, "Ser2ICPRc", - &sa1100_irda_fir_rx); - if (err) - goto err_rx_dma; - - err = sa1100_irda_dma_request(si->dev, &si->dma_tx, "Ser2ICPTr", - &sa1100_irda_sir_tx); - if (err) - goto err_tx_dma; - - /* - * Setup the serial port for the specified speed. - */ - err = sa1100_irda_startup(si); - if (err) - goto err_startup; - - /* - * Open a new IrLAP layer instance. - */ - si->irlap = irlap_open(dev, &si->qos, "sa1100"); - err = -ENOMEM; - if (!si->irlap) - goto err_irlap; - - err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev); - if (err) - goto err_irq; - - /* - * Now enable the interrupt and start the queue - */ - si->open = 1; - sa1100_set_power(si, power_level); /* low power mode */ - - netif_start_queue(dev); - return 0; - -err_irq: - irlap_close(si->irlap); -err_irlap: - si->open = 0; - sa1100_irda_shutdown(si); -err_startup: - dma_release_channel(si->dma_tx.chan); -err_tx_dma: - dma_release_channel(si->dma_rx.chan); -err_rx_dma: - return err; -} - -static int sa1100_irda_stop(struct net_device *dev) -{ - struct sa1100_irda *si = netdev_priv(dev); - struct sk_buff *skb; - - netif_stop_queue(dev); - - si->open = 0; - sa1100_irda_shutdown(si); - - /* - * If we have been doing any DMA activity, make sure we - * tidy that up cleanly. - */ - skb = si->dma_rx.skb; - if (skb) { - dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, - DMA_FROM_DEVICE); - dev_kfree_skb(skb); - si->dma_rx.skb = NULL; - } - - skb = si->dma_tx.skb; - if (skb) { - dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, - DMA_TO_DEVICE); - dev_kfree_skb(skb); - si->dma_tx.skb = NULL; - } - - /* Stop IrLAP */ - if (si->irlap) { - irlap_close(si->irlap); - si->irlap = NULL; - } - - /* - * Free resources - */ - dma_release_channel(si->dma_tx.chan); - dma_release_channel(si->dma_rx.chan); - free_irq(dev->irq, dev); - - sa1100_set_power(si, 0); - - return 0; -} - -static int sa1100_irda_init_iobuf(iobuff_t *io, int size) -{ - io->head = kmalloc(size, GFP_KERNEL | GFP_DMA); - if (io->head != NULL) { - io->truesize = size; - io->in_frame = FALSE; - io->state = OUTSIDE_FRAME; - io->data = io->head; - } - return io->head ? 0 : -ENOMEM; -} - -static const struct net_device_ops sa1100_irda_netdev_ops = { - .ndo_open = sa1100_irda_start, - .ndo_stop = sa1100_irda_stop, - .ndo_start_xmit = sa1100_irda_hard_xmit, - .ndo_do_ioctl = sa1100_irda_ioctl, -}; - -static int sa1100_irda_probe(struct platform_device *pdev) -{ - struct net_device *dev; - struct sa1100_irda *si; - unsigned int baudrate_mask; - int err, irq; - - if (!pdev->dev.platform_data) - return -EINVAL; - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return irq < 0 ? irq : -ENXIO; - - err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY; - if (err) - goto err_mem_1; - err = request_mem_region(__PREG(Ser2HSCR0), 0x1c, "IrDA") ? 0 : -EBUSY; - if (err) - goto err_mem_2; - err = request_mem_region(__PREG(Ser2HSCR2), 0x04, "IrDA") ? 0 : -EBUSY; - if (err) - goto err_mem_3; - - dev = alloc_irdadev(sizeof(struct sa1100_irda)); - if (!dev) { - err = -ENOMEM; - goto err_mem_4; - } - - SET_NETDEV_DEV(dev, &pdev->dev); - - si = netdev_priv(dev); - si->dev = &pdev->dev; - si->pdata = pdev->dev.platform_data; - - sg_init_table(&si->dma_rx.sg, 1); - sg_init_table(&si->dma_tx.sg, 1); - - /* - * Initialise the HP-SIR buffers - */ - err = sa1100_irda_init_iobuf(&si->rx_buff, 14384); - if (err) - goto err_mem_5; - err = sa1100_irda_init_iobuf(&si->tx_buff, IRDA_SIR_MAX_FRAME); - if (err) - goto err_mem_5; - - dev->netdev_ops = &sa1100_irda_netdev_ops; - dev->irq = irq; - - irda_init_max_qos_capabilies(&si->qos); - - /* - * We support original IRDA up to 115k2. (we don't currently - * support 4Mbps). Min Turn Time set to 1ms or greater. - */ - baudrate_mask = IR_9600; - - switch (max_rate) { - case 4000000: baudrate_mask |= IR_4000000 << 8; - case 115200: baudrate_mask |= IR_115200; - case 57600: baudrate_mask |= IR_57600; - case 38400: baudrate_mask |= IR_38400; - case 19200: baudrate_mask |= IR_19200; - } - - si->qos.baud_rate.bits &= baudrate_mask; - si->qos.min_turn_time.bits = 7; - - irda_qos_bits_to_value(&si->qos); - - si->utcr4 = UTCR4_HPSIR; - if (tx_lpm) - si->utcr4 |= UTCR4_Z1_6us; - - /* - * Initially enable HP-SIR modulation, and ensure that the port - * is disabled. - */ - Ser2UTCR3 = 0; - Ser2UTCR4 = si->utcr4; - Ser2HSCR0 = HSCR0_UART; - - err = register_netdev(dev); - if (err == 0) - platform_set_drvdata(pdev, dev); - - if (err) { - err_mem_5: - kfree(si->tx_buff.head); - kfree(si->rx_buff.head); - free_netdev(dev); - err_mem_4: - release_mem_region(__PREG(Ser2HSCR2), 0x04); - err_mem_3: - release_mem_region(__PREG(Ser2HSCR0), 0x1c); - err_mem_2: - release_mem_region(__PREG(Ser2UTCR0), 0x24); - } - err_mem_1: - return err; -} - -static int sa1100_irda_remove(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - - if (dev) { - struct sa1100_irda *si = netdev_priv(dev); - unregister_netdev(dev); - kfree(si->tx_buff.head); - kfree(si->rx_buff.head); - free_netdev(dev); - } - - release_mem_region(__PREG(Ser2HSCR2), 0x04); - release_mem_region(__PREG(Ser2HSCR0), 0x1c); - release_mem_region(__PREG(Ser2UTCR0), 0x24); - - return 0; -} - -#ifdef CONFIG_PM -/* - * Suspend the IrDA interface. - */ -static int sa1100_irda_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct sa1100_irda *si; - - if (!dev) - return 0; - - si = netdev_priv(dev); - if (si->open) { - /* - * Stop the transmit queue - */ - netif_device_detach(dev); - disable_irq(dev->irq); - sa1100_irda_shutdown(si); - __sa1100_irda_set_power(si, 0); - } - - return 0; -} - -/* - * Resume the IrDA interface. - */ -static int sa1100_irda_resume(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct sa1100_irda *si; - - if (!dev) - return 0; - - si = netdev_priv(dev); - if (si->open) { - /* - * If we missed a speed change, initialise at the new speed - * directly. It is debatable whether this is actually - * required, but in the interests of continuing from where - * we left off it is desirable. The converse argument is - * that we should re-negotiate at 9600 baud again. - */ - if (si->newspeed) { - si->speed = si->newspeed; - si->newspeed = 0; - } - - sa1100_irda_startup(si); - __sa1100_irda_set_power(si, si->power); - enable_irq(dev->irq); - - /* - * This automatically wakes up the queue - */ - netif_device_attach(dev); - } - - return 0; -} -#else -#define sa1100_irda_suspend NULL -#define sa1100_irda_resume NULL -#endif - -static struct platform_driver sa1100ir_driver = { - .probe = sa1100_irda_probe, - .remove = sa1100_irda_remove, - .suspend = sa1100_irda_suspend, - .resume = sa1100_irda_resume, - .driver = { - .name = "sa11x0-ir", - }, -}; - -static int __init sa1100_irda_init(void) -{ - /* - * Limit power level a sensible range. - */ - if (power_level < 1) - power_level = 1; - if (power_level > 3) - power_level = 3; - - return platform_driver_register(&sa1100ir_driver); -} - -static void __exit sa1100_irda_exit(void) -{ - platform_driver_unregister(&sa1100ir_driver); -} - -module_init(sa1100_irda_init); -module_exit(sa1100_irda_exit); -module_param(power_level, int, 0); -module_param(tx_lpm, int, 0); -module_param(max_rate, int, 0); - -MODULE_AUTHOR("Russell King "); -MODULE_DESCRIPTION("StrongARM SA1100 IrDA driver"); -MODULE_LICENSE("GPL"); -MODULE_PARM_DESC(power_level, "IrDA power level, 1 (low) to 3 (high)"); -MODULE_PARM_DESC(tx_lpm, "Enable transmitter low power (1.6us) mode"); -MODULE_PARM_DESC(max_rate, "Maximum baud rate (4000000, 115200, 57600, 38400, 19200, 9600)"); -MODULE_ALIAS("platform:sa11x0-ir"); diff --git a/drivers/staging/irda/drivers/sh_sir.c b/drivers/staging/irda/drivers/sh_sir.c deleted file mode 100644 index 0d0687cc454a..000000000000 --- a/drivers/staging/irda/drivers/sh_sir.c +++ /dev/null @@ -1,810 +0,0 @@ -/* - * SuperH IrDA Driver - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Kuninori Morimoto - * - * Based on bfin_sir.c - * Copyright 2006-2009 Analog Devices Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "sh_sir" - -#define RX_PHASE (1 << 0) -#define TX_PHASE (1 << 1) -#define TX_COMP_PHASE (1 << 2) /* tx complete */ -#define NONE_PHASE (1 << 31) - -#define IRIF_RINTCLR 0x0016 /* DMA rx interrupt source clear */ -#define IRIF_TINTCLR 0x0018 /* DMA tx interrupt source clear */ -#define IRIF_SIR0 0x0020 /* IrDA-SIR10 control */ -#define IRIF_SIR1 0x0022 /* IrDA-SIR10 baudrate error correction */ -#define IRIF_SIR2 0x0024 /* IrDA-SIR10 baudrate count */ -#define IRIF_SIR3 0x0026 /* IrDA-SIR10 status */ -#define IRIF_SIR_FRM 0x0028 /* Hardware frame processing set */ -#define IRIF_SIR_EOF 0x002A /* EOF value */ -#define IRIF_SIR_FLG 0x002C /* Flag clear */ -#define IRIF_UART_STS2 0x002E /* UART status 2 */ -#define IRIF_UART0 0x0030 /* UART control */ -#define IRIF_UART1 0x0032 /* UART status */ -#define IRIF_UART2 0x0034 /* UART mode */ -#define IRIF_UART3 0x0036 /* UART transmit data */ -#define IRIF_UART4 0x0038 /* UART receive data */ -#define IRIF_UART5 0x003A /* UART interrupt mask */ -#define IRIF_UART6 0x003C /* UART baud rate error correction */ -#define IRIF_UART7 0x003E /* UART baud rate count set */ -#define IRIF_CRC0 0x0040 /* CRC engine control */ -#define IRIF_CRC1 0x0042 /* CRC engine input data */ -#define IRIF_CRC2 0x0044 /* CRC engine calculation */ -#define IRIF_CRC3 0x0046 /* CRC engine output data 1 */ -#define IRIF_CRC4 0x0048 /* CRC engine output data 2 */ - -/* IRIF_SIR0 */ -#define IRTPW (1 << 1) /* transmit pulse width select */ -#define IRERRC (1 << 0) /* Clear receive pulse width error */ - -/* IRIF_SIR3 */ -#define IRERR (1 << 0) /* received pulse width Error */ - -/* IRIF_SIR_FRM */ -#define EOFD (1 << 9) /* EOF detection flag */ -#define FRER (1 << 8) /* Frame Error bit */ -#define FRP (1 << 0) /* Frame processing set */ - -/* IRIF_UART_STS2 */ -#define IRSME (1 << 6) /* Receive Sum Error flag */ -#define IROVE (1 << 5) /* Receive Overrun Error flag */ -#define IRFRE (1 << 4) /* Receive Framing Error flag */ -#define IRPRE (1 << 3) /* Receive Parity Error flag */ - -/* IRIF_UART0_*/ -#define TBEC (1 << 2) /* Transmit Data Clear */ -#define RIE (1 << 1) /* Receive Enable */ -#define TIE (1 << 0) /* Transmit Enable */ - -/* IRIF_UART1 */ -#define URSME (1 << 6) /* Receive Sum Error Flag */ -#define UROVE (1 << 5) /* Receive Overrun Error Flag */ -#define URFRE (1 << 4) /* Receive Framing Error Flag */ -#define URPRE (1 << 3) /* Receive Parity Error Flag */ -#define RBF (1 << 2) /* Receive Buffer Full Flag */ -#define TSBE (1 << 1) /* Transmit Shift Buffer Empty Flag */ -#define TBE (1 << 0) /* Transmit Buffer Empty flag */ -#define TBCOMP (TSBE | TBE) - -/* IRIF_UART5 */ -#define RSEIM (1 << 6) /* Receive Sum Error Flag IRQ Mask */ -#define RBFIM (1 << 2) /* Receive Buffer Full Flag IRQ Mask */ -#define TSBEIM (1 << 1) /* Transmit Shift Buffer Empty Flag IRQ Mask */ -#define TBEIM (1 << 0) /* Transmit Buffer Empty Flag IRQ Mask */ -#define RX_MASK (RSEIM | RBFIM) - -/* IRIF_CRC0 */ -#define CRC_RST (1 << 15) /* CRC Engine Reset */ -#define CRC_CT_MASK 0x0FFF - -/************************************************************************ - - - structure - - -************************************************************************/ -struct sh_sir_self { - void __iomem *membase; - unsigned int irq; - struct clk *clk; - - struct net_device *ndev; - - struct irlap_cb *irlap; - struct qos_info qos; - - iobuff_t tx_buff; - iobuff_t rx_buff; -}; - -/************************************************************************ - - - common function - - -************************************************************************/ -static void sh_sir_write(struct sh_sir_self *self, u32 offset, u16 data) -{ - iowrite16(data, self->membase + offset); -} - -static u16 sh_sir_read(struct sh_sir_self *self, u32 offset) -{ - return ioread16(self->membase + offset); -} - -static void sh_sir_update_bits(struct sh_sir_self *self, u32 offset, - u16 mask, u16 data) -{ - u16 old, new; - - old = sh_sir_read(self, offset); - new = (old & ~mask) | data; - if (old != new) - sh_sir_write(self, offset, new); -} - -/************************************************************************ - - - CRC function - - -************************************************************************/ -static void sh_sir_crc_reset(struct sh_sir_self *self) -{ - sh_sir_write(self, IRIF_CRC0, CRC_RST); -} - -static void sh_sir_crc_add(struct sh_sir_self *self, u8 data) -{ - sh_sir_write(self, IRIF_CRC1, (u16)data); -} - -static u16 sh_sir_crc_cnt(struct sh_sir_self *self) -{ - return CRC_CT_MASK & sh_sir_read(self, IRIF_CRC0); -} - -static u16 sh_sir_crc_out(struct sh_sir_self *self) -{ - return sh_sir_read(self, IRIF_CRC4); -} - -static int sh_sir_crc_init(struct sh_sir_self *self) -{ - struct device *dev = &self->ndev->dev; - int ret = -EIO; - u16 val; - - sh_sir_crc_reset(self); - - sh_sir_crc_add(self, 0xCC); - sh_sir_crc_add(self, 0xF5); - sh_sir_crc_add(self, 0xF1); - sh_sir_crc_add(self, 0xA7); - - val = sh_sir_crc_cnt(self); - if (4 != val) { - dev_err(dev, "CRC count error %x\n", val); - goto crc_init_out; - } - - val = sh_sir_crc_out(self); - if (0x51DF != val) { - dev_err(dev, "CRC result error%x\n", val); - goto crc_init_out; - } - - ret = 0; - -crc_init_out: - - sh_sir_crc_reset(self); - return ret; -} - -/************************************************************************ - - - baud rate functions - - -************************************************************************/ -#define SCLK_BASE 1843200 /* 1.8432MHz */ - -static u32 sh_sir_find_sclk(struct clk *irda_clk) -{ - struct cpufreq_frequency_table *freq_table = irda_clk->freq_table; - struct cpufreq_frequency_table *pos; - struct clk *pclk = clk_get(NULL, "peripheral_clk"); - u32 limit, min = 0xffffffff, tmp; - int index = 0; - - limit = clk_get_rate(pclk); - clk_put(pclk); - - /* IrDA can not set over peripheral_clk */ - cpufreq_for_each_valid_entry_idx(pos, freq_table, index) { - u32 freq = pos->frequency; - - /* IrDA should not over peripheral_clk */ - if (freq > limit) - continue; - - tmp = freq % SCLK_BASE; - if (tmp < min) { - min = tmp; - break; - } - } - - return freq_table[index].frequency; -} - -#define ERR_ROUNDING(a) ((a + 5000) / 10000) -static int sh_sir_set_baudrate(struct sh_sir_self *self, u32 baudrate) -{ - struct clk *clk; - struct device *dev = &self->ndev->dev; - u32 rate; - u16 uabca, uabc; - u16 irbca, irbc; - u32 min, rerr, tmp; - int i; - - /* Baud Rate Error Correction x 10000 */ - u32 rate_err_array[] = { - 0, 625, 1250, 1875, - 2500, 3125, 3750, 4375, - 5000, 5625, 6250, 6875, - 7500, 8125, 8750, 9375, - }; - - /* - * FIXME - * - * it support 9600 only now - */ - switch (baudrate) { - case 9600: - break; - default: - dev_err(dev, "un-supported baudrate %d\n", baudrate); - return -EIO; - } - - clk = clk_get(NULL, "irda_clk"); - if (IS_ERR(clk)) { - dev_err(dev, "can not get irda_clk\n"); - return -EIO; - } - - clk_set_rate(clk, sh_sir_find_sclk(clk)); - rate = clk_get_rate(clk); - clk_put(clk); - - dev_dbg(dev, "selected sclk = %d\n", rate); - - /* - * CALCULATION - * - * 1843200 = system rate / (irbca + (irbc + 1)) - */ - - irbc = rate / SCLK_BASE; - - tmp = rate - (SCLK_BASE * irbc); - tmp *= 10000; - - rerr = tmp / SCLK_BASE; - - min = 0xffffffff; - irbca = 0; - for (i = 0; i < ARRAY_SIZE(rate_err_array); i++) { - tmp = abs(rate_err_array[i] - rerr); - if (min > tmp) { - min = tmp; - irbca = i; - } - } - - tmp = rate / (irbc + ERR_ROUNDING(rate_err_array[irbca])); - if ((SCLK_BASE / 100) < abs(tmp - SCLK_BASE)) - dev_warn(dev, "IrDA freq error margin over %d\n", tmp); - - dev_dbg(dev, "target = %d, result = %d, infrared = %d.%d\n", - SCLK_BASE, tmp, irbc, rate_err_array[irbca]); - - irbca = (irbca & 0xF) << 4; - irbc = (irbc - 1) & 0xF; - - if (!irbc) { - dev_err(dev, "sh_sir can not set 0 in IRIF_SIR2\n"); - return -EIO; - } - - sh_sir_write(self, IRIF_SIR0, IRTPW | IRERRC); - sh_sir_write(self, IRIF_SIR1, irbca); - sh_sir_write(self, IRIF_SIR2, irbc); - - /* - * CALCULATION - * - * BaudRate[bps] = system rate / (uabca + (uabc + 1) x 16) - */ - - uabc = rate / baudrate; - uabc = (uabc / 16) - 1; - uabc = (uabc + 1) * 16; - - tmp = rate - (uabc * baudrate); - tmp *= 10000; - - rerr = tmp / baudrate; - - min = 0xffffffff; - uabca = 0; - for (i = 0; i < ARRAY_SIZE(rate_err_array); i++) { - tmp = abs(rate_err_array[i] - rerr); - if (min > tmp) { - min = tmp; - uabca = i; - } - } - - tmp = rate / (uabc + ERR_ROUNDING(rate_err_array[uabca])); - if ((baudrate / 100) < abs(tmp - baudrate)) - dev_warn(dev, "UART freq error margin over %d\n", tmp); - - dev_dbg(dev, "target = %d, result = %d, uart = %d.%d\n", - baudrate, tmp, - uabc, rate_err_array[uabca]); - - uabca = (uabca & 0xF) << 4; - uabc = (uabc / 16) - 1; - - sh_sir_write(self, IRIF_UART6, uabca); - sh_sir_write(self, IRIF_UART7, uabc); - - return 0; -} - -/************************************************************************ - - - iobuf function - - -************************************************************************/ -static int __sh_sir_init_iobuf(iobuff_t *io, int size) -{ - io->head = kmalloc(size, GFP_KERNEL); - if (!io->head) - return -ENOMEM; - - io->truesize = size; - io->in_frame = FALSE; - io->state = OUTSIDE_FRAME; - io->data = io->head; - - return 0; -} - -static void sh_sir_remove_iobuf(struct sh_sir_self *self) -{ - kfree(self->rx_buff.head); - kfree(self->tx_buff.head); - - self->rx_buff.head = NULL; - self->tx_buff.head = NULL; -} - -static int sh_sir_init_iobuf(struct sh_sir_self *self, int rxsize, int txsize) -{ - int err = -ENOMEM; - - if (self->rx_buff.head || - self->tx_buff.head) { - dev_err(&self->ndev->dev, "iobuff has already existed."); - return err; - } - - err = __sh_sir_init_iobuf(&self->rx_buff, rxsize); - if (err) - goto iobuf_err; - - err = __sh_sir_init_iobuf(&self->tx_buff, txsize); - -iobuf_err: - if (err) - sh_sir_remove_iobuf(self); - - return err; -} - -/************************************************************************ - - - status function - - -************************************************************************/ -static void sh_sir_clear_all_err(struct sh_sir_self *self) -{ - /* Clear error flag for receive pulse width */ - sh_sir_update_bits(self, IRIF_SIR0, IRERRC, IRERRC); - - /* Clear frame / EOF error flag */ - sh_sir_write(self, IRIF_SIR_FLG, 0xffff); - - /* Clear all status error */ - sh_sir_write(self, IRIF_UART_STS2, 0); -} - -static void sh_sir_set_phase(struct sh_sir_self *self, int phase) -{ - u16 uart5 = 0; - u16 uart0 = 0; - - switch (phase) { - case TX_PHASE: - uart5 = TBEIM; - uart0 = TBEC | TIE; - break; - case TX_COMP_PHASE: - uart5 = TSBEIM; - uart0 = TIE; - break; - case RX_PHASE: - uart5 = RX_MASK; - uart0 = RIE; - break; - default: - break; - } - - sh_sir_write(self, IRIF_UART5, uart5); - sh_sir_write(self, IRIF_UART0, uart0); -} - -static int sh_sir_is_which_phase(struct sh_sir_self *self) -{ - u16 val = sh_sir_read(self, IRIF_UART5); - - if (val & TBEIM) - return TX_PHASE; - - if (val & TSBEIM) - return TX_COMP_PHASE; - - if (val & RX_MASK) - return RX_PHASE; - - return NONE_PHASE; -} - -static void sh_sir_tx(struct sh_sir_self *self, int phase) -{ - switch (phase) { - case TX_PHASE: - if (0 >= self->tx_buff.len) { - sh_sir_set_phase(self, TX_COMP_PHASE); - } else { - sh_sir_write(self, IRIF_UART3, self->tx_buff.data[0]); - self->tx_buff.len--; - self->tx_buff.data++; - } - break; - case TX_COMP_PHASE: - sh_sir_set_phase(self, RX_PHASE); - netif_wake_queue(self->ndev); - break; - default: - dev_err(&self->ndev->dev, "should not happen\n"); - break; - } -} - -static int sh_sir_read_data(struct sh_sir_self *self) -{ - u16 val = 0; - int timeout = 1024; - - while (timeout--) { - val = sh_sir_read(self, IRIF_UART1); - - /* data get */ - if (val & RBF) { - if (val & (URSME | UROVE | URFRE | URPRE)) - break; - - return (int)sh_sir_read(self, IRIF_UART4); - } - - udelay(1); - } - - dev_err(&self->ndev->dev, "UART1 %04x : STATUS %04x\n", - val, sh_sir_read(self, IRIF_UART_STS2)); - - /* read data register for clear error */ - sh_sir_read(self, IRIF_UART4); - - return -1; -} - -static void sh_sir_rx(struct sh_sir_self *self) -{ - int timeout = 1024; - int data; - - while (timeout--) { - data = sh_sir_read_data(self); - if (data < 0) - break; - - async_unwrap_char(self->ndev, &self->ndev->stats, - &self->rx_buff, (u8)data); - - if (EOFD & sh_sir_read(self, IRIF_SIR_FRM)) - continue; - - break; - } -} - -static irqreturn_t sh_sir_irq(int irq, void *dev_id) -{ - struct sh_sir_self *self = dev_id; - struct device *dev = &self->ndev->dev; - int phase = sh_sir_is_which_phase(self); - - switch (phase) { - case TX_COMP_PHASE: - case TX_PHASE: - sh_sir_tx(self, phase); - break; - case RX_PHASE: - if (sh_sir_read(self, IRIF_SIR3)) - dev_err(dev, "rcv pulse width error occurred\n"); - - sh_sir_rx(self); - sh_sir_clear_all_err(self); - break; - default: - dev_err(dev, "unknown interrupt\n"); - } - - return IRQ_HANDLED; -} - -/************************************************************************ - - - net_device_ops function - - -************************************************************************/ -static int sh_sir_hard_xmit(struct sk_buff *skb, struct net_device *ndev) -{ - struct sh_sir_self *self = netdev_priv(ndev); - int speed = irda_get_next_speed(skb); - - if ((0 < speed) && - (9600 != speed)) { - dev_err(&ndev->dev, "support 9600 only (%d)\n", speed); - return -EIO; - } - - netif_stop_queue(ndev); - - self->tx_buff.data = self->tx_buff.head; - self->tx_buff.len = 0; - if (skb->len) - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - sh_sir_set_phase(self, TX_PHASE); - dev_kfree_skb(skb); - - return 0; -} - -static int sh_sir_ioctl(struct net_device *ndev, struct ifreq *ifreq, int cmd) -{ - /* - * FIXME - * - * This function is needed for irda framework. - * But nothing to do now - */ - return 0; -} - -static struct net_device_stats *sh_sir_stats(struct net_device *ndev) -{ - struct sh_sir_self *self = netdev_priv(ndev); - - return &self->ndev->stats; -} - -static int sh_sir_open(struct net_device *ndev) -{ - struct sh_sir_self *self = netdev_priv(ndev); - int err; - - clk_enable(self->clk); - err = sh_sir_crc_init(self); - if (err) - goto open_err; - - sh_sir_set_baudrate(self, 9600); - - self->irlap = irlap_open(ndev, &self->qos, DRIVER_NAME); - if (!self->irlap) { - err = -ENODEV; - goto open_err; - } - - /* - * Now enable the interrupt then start the queue - */ - sh_sir_update_bits(self, IRIF_SIR_FRM, FRP, FRP); - sh_sir_read(self, IRIF_UART1); /* flag clear */ - sh_sir_read(self, IRIF_UART4); /* flag clear */ - sh_sir_set_phase(self, RX_PHASE); - - netif_start_queue(ndev); - - dev_info(&self->ndev->dev, "opened\n"); - - return 0; - -open_err: - clk_disable(self->clk); - - return err; -} - -static int sh_sir_stop(struct net_device *ndev) -{ - struct sh_sir_self *self = netdev_priv(ndev); - - /* Stop IrLAP */ - if (self->irlap) { - irlap_close(self->irlap); - self->irlap = NULL; - } - - netif_stop_queue(ndev); - - dev_info(&ndev->dev, "stopped\n"); - - return 0; -} - -static const struct net_device_ops sh_sir_ndo = { - .ndo_open = sh_sir_open, - .ndo_stop = sh_sir_stop, - .ndo_start_xmit = sh_sir_hard_xmit, - .ndo_do_ioctl = sh_sir_ioctl, - .ndo_get_stats = sh_sir_stats, -}; - -/************************************************************************ - - - platform_driver function - - -************************************************************************/ -static int sh_sir_probe(struct platform_device *pdev) -{ - struct net_device *ndev; - struct sh_sir_self *self; - struct resource *res; - char clk_name[8]; - int irq; - int err = -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!res || irq < 0) { - dev_err(&pdev->dev, "Not enough platform resources.\n"); - goto exit; - } - - ndev = alloc_irdadev(sizeof(*self)); - if (!ndev) - goto exit; - - self = netdev_priv(ndev); - self->membase = ioremap_nocache(res->start, resource_size(res)); - if (!self->membase) { - err = -ENXIO; - dev_err(&pdev->dev, "Unable to ioremap.\n"); - goto err_mem_1; - } - - err = sh_sir_init_iobuf(self, IRDA_SKB_MAX_MTU, IRDA_SIR_MAX_FRAME); - if (err) - goto err_mem_2; - - snprintf(clk_name, sizeof(clk_name), "irda%d", pdev->id); - self->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(self->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - err = -ENODEV; - goto err_mem_3; - } - - irda_init_max_qos_capabilies(&self->qos); - - ndev->netdev_ops = &sh_sir_ndo; - ndev->irq = irq; - - self->ndev = ndev; - self->qos.baud_rate.bits &= IR_9600; /* FIXME */ - self->qos.min_turn_time.bits = 1; /* 10 ms or more */ - - irda_qos_bits_to_value(&self->qos); - - err = register_netdev(ndev); - if (err) - goto err_mem_4; - - platform_set_drvdata(pdev, ndev); - err = devm_request_irq(&pdev->dev, irq, sh_sir_irq, 0, "sh_sir", self); - if (err) { - dev_warn(&pdev->dev, "Unable to attach sh_sir interrupt\n"); - goto err_mem_4; - } - - dev_info(&pdev->dev, "SuperH IrDA probed\n"); - - goto exit; - -err_mem_4: - clk_put(self->clk); -err_mem_3: - sh_sir_remove_iobuf(self); -err_mem_2: - iounmap(self->membase); -err_mem_1: - free_netdev(ndev); -exit: - return err; -} - -static int sh_sir_remove(struct platform_device *pdev) -{ - struct net_device *ndev = platform_get_drvdata(pdev); - struct sh_sir_self *self = netdev_priv(ndev); - - if (!self) - return 0; - - unregister_netdev(ndev); - clk_put(self->clk); - sh_sir_remove_iobuf(self); - iounmap(self->membase); - free_netdev(ndev); - - return 0; -} - -static struct platform_driver sh_sir_driver = { - .probe = sh_sir_probe, - .remove = sh_sir_remove, - .driver = { - .name = DRIVER_NAME, - }, -}; - -module_platform_driver(sh_sir_driver); - -MODULE_AUTHOR("Kuninori Morimoto "); -MODULE_DESCRIPTION("SuperH IrDA driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/sir-dev.h b/drivers/staging/irda/drivers/sir-dev.h deleted file mode 100644 index f50b9c1c0639..000000000000 --- a/drivers/staging/irda/drivers/sir-dev.h +++ /dev/null @@ -1,191 +0,0 @@ -/********************************************************************* - * - * sir.h: include file for irda-sir device abstraction layer - * - * Copyright (c) 2002 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#ifndef IRDA_SIR_H -#define IRDA_SIR_H - -#include -#include - -#include -#include // iobuff_t - -struct sir_fsm { - struct semaphore sem; - struct delayed_work work; - unsigned state, substate; - int param; - int result; -}; - -#define SIRDEV_STATE_WAIT_TX_COMPLETE 0x0100 - -/* substates for wait_tx_complete */ -#define SIRDEV_STATE_WAIT_XMIT 0x0101 -#define SIRDEV_STATE_WAIT_UNTIL_SENT 0x0102 -#define SIRDEV_STATE_TX_DONE 0x0103 - -#define SIRDEV_STATE_DONGLE_OPEN 0x0300 - -/* 0x0301-0x03ff reserved for individual dongle substates */ - -#define SIRDEV_STATE_DONGLE_CLOSE 0x0400 - -/* 0x0401-0x04ff reserved for individual dongle substates */ - -#define SIRDEV_STATE_SET_DTR_RTS 0x0500 - -#define SIRDEV_STATE_SET_SPEED 0x0700 -#define SIRDEV_STATE_DONGLE_CHECK 0x0800 -#define SIRDEV_STATE_DONGLE_RESET 0x0900 - -/* 0x0901-0x09ff reserved for individual dongle substates */ - -#define SIRDEV_STATE_DONGLE_SPEED 0x0a00 -/* 0x0a01-0x0aff reserved for individual dongle substates */ - -#define SIRDEV_STATE_PORT_SPEED 0x0b00 -#define SIRDEV_STATE_DONE 0x0c00 -#define SIRDEV_STATE_ERROR 0x0d00 -#define SIRDEV_STATE_COMPLETE 0x0e00 - -#define SIRDEV_STATE_DEAD 0xffff - - -struct sir_dev; - -struct dongle_driver { - - struct module *owner; - - const char *driver_name; - - IRDA_DONGLE type; - - int (*open)(struct sir_dev *dev); - int (*close)(struct sir_dev *dev); - int (*reset)(struct sir_dev *dev); - int (*set_speed)(struct sir_dev *dev, unsigned speed); - - struct list_head dongle_list; -}; - -struct sir_driver { - - struct module *owner; - - const char *driver_name; - - int qos_mtt_bits; - - int (*chars_in_buffer)(struct sir_dev *dev); - void (*wait_until_sent)(struct sir_dev *dev); - int (*set_speed)(struct sir_dev *dev, unsigned speed); - int (*set_dtr_rts)(struct sir_dev *dev, int dtr, int rts); - - int (*do_write)(struct sir_dev *dev, const unsigned char *ptr, size_t len); - - int (*start_dev)(struct sir_dev *dev); - int (*stop_dev)(struct sir_dev *dev); -}; - - -/* exported */ - -int irda_register_dongle(struct dongle_driver *new); -int irda_unregister_dongle(struct dongle_driver *drv); - -struct sir_dev *sirdev_get_instance(const struct sir_driver *drv, - const char *name); -int sirdev_put_instance(struct sir_dev *self); - -int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type); -void sirdev_write_complete(struct sir_dev *dev); -int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count); - -/* low level helpers for SIR device/dongle setup */ -int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len); -int sirdev_raw_read(struct sir_dev *dev, char *buf, int len); -int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts); - -/* not exported */ - -int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type); -int sirdev_put_dongle(struct sir_dev *self); - -void sirdev_enable_rx(struct sir_dev *dev); -int sirdev_schedule_request(struct sir_dev *dev, int state, unsigned param); - -/* inline helpers */ - -static inline int sirdev_schedule_speed(struct sir_dev *dev, unsigned speed) -{ - return sirdev_schedule_request(dev, SIRDEV_STATE_SET_SPEED, speed); -} - -static inline int sirdev_schedule_dongle_open(struct sir_dev *dev, int dongle_id) -{ - return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_OPEN, dongle_id); -} - -static inline int sirdev_schedule_dongle_close(struct sir_dev *dev) -{ - return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_CLOSE, 0); -} - -static inline int sirdev_schedule_dtr_rts(struct sir_dev *dev, int dtr, int rts) -{ - int dtrrts; - - dtrrts = ((dtr) ? 0x02 : 0x00) | ((rts) ? 0x01 : 0x00); - return sirdev_schedule_request(dev, SIRDEV_STATE_SET_DTR_RTS, dtrrts); -} - -#if 0 -static inline int sirdev_schedule_mode(struct sir_dev *dev, int mode) -{ - return sirdev_schedule_request(dev, SIRDEV_STATE_SET_MODE, mode); -} -#endif - - -struct sir_dev { - struct net_device *netdev; - - struct irlap_cb *irlap; - - struct qos_info qos; - - char hwname[32]; - - struct sir_fsm fsm; - atomic_t enable_rx; - int raw_tx; - spinlock_t tx_lock; - - u32 new_speed; - u32 flags; - - unsigned speed; - - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - struct sk_buff *tx_skb; - - const struct dongle_driver * dongle_drv; - const struct sir_driver * drv; - void *priv; - -}; - -#endif /* IRDA_SIR_H */ diff --git a/drivers/staging/irda/drivers/sir_dev.c b/drivers/staging/irda/drivers/sir_dev.c deleted file mode 100644 index 6af26a7d787c..000000000000 --- a/drivers/staging/irda/drivers/sir_dev.c +++ /dev/null @@ -1,987 +0,0 @@ -/********************************************************************* - * - * sir_dev.c: irda sir network device - * - * Copyright (c) 2002 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "sir-dev.h" - - -static struct workqueue_struct *irda_sir_wq; - -/* STATE MACHINE */ - -/* substate handler of the config-fsm to handle the cases where we want - * to wait for transmit completion before changing the port configuration - */ - -static int sirdev_tx_complete_fsm(struct sir_dev *dev) -{ - struct sir_fsm *fsm = &dev->fsm; - unsigned next_state, delay; - unsigned bytes_left; - - do { - next_state = fsm->substate; /* default: stay in current substate */ - delay = 0; - - switch(fsm->substate) { - - case SIRDEV_STATE_WAIT_XMIT: - if (dev->drv->chars_in_buffer) - bytes_left = dev->drv->chars_in_buffer(dev); - else - bytes_left = 0; - if (!bytes_left) { - next_state = SIRDEV_STATE_WAIT_UNTIL_SENT; - break; - } - - if (dev->speed > 115200) - delay = (bytes_left*8*10000) / (dev->speed/100); - else if (dev->speed > 0) - delay = (bytes_left*10*10000) / (dev->speed/100); - else - delay = 0; - /* expected delay (usec) until remaining bytes are sent */ - if (delay < 100) { - udelay(delay); - delay = 0; - break; - } - /* sleep some longer delay (msec) */ - delay = (delay+999) / 1000; - break; - - case SIRDEV_STATE_WAIT_UNTIL_SENT: - /* block until underlaying hardware buffer are empty */ - if (dev->drv->wait_until_sent) - dev->drv->wait_until_sent(dev); - next_state = SIRDEV_STATE_TX_DONE; - break; - - case SIRDEV_STATE_TX_DONE: - return 0; - - default: - net_err_ratelimited("%s - undefined state\n", __func__); - return -EINVAL; - } - fsm->substate = next_state; - } while (delay == 0); - return delay; -} - -/* - * Function sirdev_config_fsm - * - * State machine to handle the configuration of the device (and attached dongle, if any). - * This handler is scheduled for execution in kIrDAd context, so we can sleep. - * however, kIrDAd is shared by all sir_dev devices so we better don't sleep there too - * long. Instead, for longer delays we start a timer to reschedule us later. - * On entry, fsm->sem is always locked and the netdev xmit queue stopped. - * Both must be unlocked/restarted on completion - but only on final exit. - */ - -static void sirdev_config_fsm(struct work_struct *work) -{ - struct sir_dev *dev = container_of(work, struct sir_dev, fsm.work.work); - struct sir_fsm *fsm = &dev->fsm; - int next_state; - int ret = -1; - unsigned delay; - - pr_debug("%s(), <%ld>\n", __func__, jiffies); - - do { - pr_debug("%s - state=0x%04x / substate=0x%04x\n", - __func__, fsm->state, fsm->substate); - - next_state = fsm->state; - delay = 0; - - switch(fsm->state) { - - case SIRDEV_STATE_DONGLE_OPEN: - if (dev->dongle_drv != NULL) { - ret = sirdev_put_dongle(dev); - if (ret) { - fsm->result = -EINVAL; - next_state = SIRDEV_STATE_ERROR; - break; - } - } - - /* Initialize dongle */ - ret = sirdev_get_dongle(dev, fsm->param); - if (ret) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - - /* Dongles are powered through the modem control lines which - * were just set during open. Before resetting, let's wait for - * the power to stabilize. This is what some dongle drivers did - * in open before, while others didn't - should be safe anyway. - */ - - delay = 50; - fsm->substate = SIRDEV_STATE_DONGLE_RESET; - next_state = SIRDEV_STATE_DONGLE_RESET; - - fsm->param = 9600; - - break; - - case SIRDEV_STATE_DONGLE_CLOSE: - /* shouldn't we just treat this as success=? */ - if (dev->dongle_drv == NULL) { - fsm->result = -EINVAL; - next_state = SIRDEV_STATE_ERROR; - break; - } - - ret = sirdev_put_dongle(dev); - if (ret) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - next_state = SIRDEV_STATE_DONE; - break; - - case SIRDEV_STATE_SET_DTR_RTS: - ret = sirdev_set_dtr_rts(dev, - (fsm->param&0x02) ? TRUE : FALSE, - (fsm->param&0x01) ? TRUE : FALSE); - next_state = SIRDEV_STATE_DONE; - break; - - case SIRDEV_STATE_SET_SPEED: - fsm->substate = SIRDEV_STATE_WAIT_XMIT; - next_state = SIRDEV_STATE_DONGLE_CHECK; - break; - - case SIRDEV_STATE_DONGLE_CHECK: - ret = sirdev_tx_complete_fsm(dev); - if (ret < 0) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - if ((delay=ret) != 0) - break; - - if (dev->dongle_drv) { - fsm->substate = SIRDEV_STATE_DONGLE_RESET; - next_state = SIRDEV_STATE_DONGLE_RESET; - } - else { - dev->speed = fsm->param; - next_state = SIRDEV_STATE_PORT_SPEED; - } - break; - - case SIRDEV_STATE_DONGLE_RESET: - if (dev->dongle_drv->reset) { - ret = dev->dongle_drv->reset(dev); - if (ret < 0) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - } - else - ret = 0; - if ((delay=ret) == 0) { - /* set serial port according to dongle default speed */ - if (dev->drv->set_speed) - dev->drv->set_speed(dev, dev->speed); - fsm->substate = SIRDEV_STATE_DONGLE_SPEED; - next_state = SIRDEV_STATE_DONGLE_SPEED; - } - break; - - case SIRDEV_STATE_DONGLE_SPEED: - if (dev->dongle_drv->set_speed) { - ret = dev->dongle_drv->set_speed(dev, fsm->param); - if (ret < 0) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - } - else - ret = 0; - if ((delay=ret) == 0) - next_state = SIRDEV_STATE_PORT_SPEED; - break; - - case SIRDEV_STATE_PORT_SPEED: - /* Finally we are ready to change the serial port speed */ - if (dev->drv->set_speed) - dev->drv->set_speed(dev, dev->speed); - dev->new_speed = 0; - next_state = SIRDEV_STATE_DONE; - break; - - case SIRDEV_STATE_DONE: - /* Signal network layer so it can send more frames */ - netif_wake_queue(dev->netdev); - next_state = SIRDEV_STATE_COMPLETE; - break; - - default: - net_err_ratelimited("%s - undefined state\n", __func__); - fsm->result = -EINVAL; - /* fall thru */ - - case SIRDEV_STATE_ERROR: - net_err_ratelimited("%s - error: %d\n", - __func__, fsm->result); - -#if 0 /* don't enable this before we have netdev->tx_timeout to recover */ - netif_stop_queue(dev->netdev); -#else - netif_wake_queue(dev->netdev); -#endif - /* fall thru */ - - case SIRDEV_STATE_COMPLETE: - /* config change finished, so we are not busy any longer */ - sirdev_enable_rx(dev); - up(&fsm->sem); - return; - } - fsm->state = next_state; - } while(!delay); - - queue_delayed_work(irda_sir_wq, &fsm->work, msecs_to_jiffies(delay)); -} - -/* schedule some device configuration task for execution by kIrDAd - * on behalf of the above state machine. - * can be called from process or interrupt/tasklet context. - */ - -int sirdev_schedule_request(struct sir_dev *dev, int initial_state, unsigned param) -{ - struct sir_fsm *fsm = &dev->fsm; - - pr_debug("%s - state=0x%04x / param=%u\n", __func__, - initial_state, param); - - if (down_trylock(&fsm->sem)) { - if (in_interrupt() || in_atomic() || irqs_disabled()) { - pr_debug("%s(), state machine busy!\n", __func__); - return -EWOULDBLOCK; - } else - down(&fsm->sem); - } - - if (fsm->state == SIRDEV_STATE_DEAD) { - /* race with sirdev_close should never happen */ - net_err_ratelimited("%s(), instance staled!\n", __func__); - up(&fsm->sem); - return -ESTALE; /* or better EPIPE? */ - } - - netif_stop_queue(dev->netdev); - atomic_set(&dev->enable_rx, 0); - - fsm->state = initial_state; - fsm->param = param; - fsm->result = 0; - - INIT_DELAYED_WORK(&fsm->work, sirdev_config_fsm); - queue_delayed_work(irda_sir_wq, &fsm->work, 0); - return 0; -} - - -/***************************************************************************/ - -void sirdev_enable_rx(struct sir_dev *dev) -{ - if (unlikely(atomic_read(&dev->enable_rx))) - return; - - /* flush rx-buffer - should also help in case of problems with echo cancelation */ - dev->rx_buff.data = dev->rx_buff.head; - dev->rx_buff.len = 0; - dev->rx_buff.in_frame = FALSE; - dev->rx_buff.state = OUTSIDE_FRAME; - atomic_set(&dev->enable_rx, 1); -} - -static int sirdev_is_receiving(struct sir_dev *dev) -{ - if (!atomic_read(&dev->enable_rx)) - return 0; - - return dev->rx_buff.state != OUTSIDE_FRAME; -} - -int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type) -{ - int err; - - pr_debug("%s : requesting dongle %d.\n", __func__, type); - - err = sirdev_schedule_dongle_open(dev, type); - if (unlikely(err)) - return err; - down(&dev->fsm.sem); /* block until config change completed */ - err = dev->fsm.result; - up(&dev->fsm.sem); - return err; -} -EXPORT_SYMBOL(sirdev_set_dongle); - -/* used by dongle drivers for dongle programming */ - -int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len) -{ - unsigned long flags; - int ret; - - if (unlikely(len > dev->tx_buff.truesize)) - return -ENOSPC; - - spin_lock_irqsave(&dev->tx_lock, flags); /* serialize with other tx operations */ - while (dev->tx_buff.len > 0) { /* wait until tx idle */ - spin_unlock_irqrestore(&dev->tx_lock, flags); - msleep(10); - spin_lock_irqsave(&dev->tx_lock, flags); - } - - dev->tx_buff.data = dev->tx_buff.head; - memcpy(dev->tx_buff.data, buf, len); - dev->tx_buff.len = len; - - ret = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); - if (ret > 0) { - pr_debug("%s(), raw-tx started\n", __func__); - - dev->tx_buff.data += ret; - dev->tx_buff.len -= ret; - dev->raw_tx = 1; - ret = len; /* all data is going to be sent */ - } - spin_unlock_irqrestore(&dev->tx_lock, flags); - return ret; -} -EXPORT_SYMBOL(sirdev_raw_write); - -/* seems some dongle drivers may need this */ - -int sirdev_raw_read(struct sir_dev *dev, char *buf, int len) -{ - int count; - - if (atomic_read(&dev->enable_rx)) - return -EIO; /* fail if we expect irda-frames */ - - count = (len < dev->rx_buff.len) ? len : dev->rx_buff.len; - - if (count > 0) { - memcpy(buf, dev->rx_buff.data, count); - dev->rx_buff.data += count; - dev->rx_buff.len -= count; - } - - /* remaining stuff gets flushed when re-enabling normal rx */ - - return count; -} -EXPORT_SYMBOL(sirdev_raw_read); - -int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts) -{ - int ret = -ENXIO; - if (dev->drv->set_dtr_rts) - ret = dev->drv->set_dtr_rts(dev, dtr, rts); - return ret; -} -EXPORT_SYMBOL(sirdev_set_dtr_rts); - -/**********************************************************************/ - -/* called from client driver - likely with bh-context - to indicate - * it made some progress with transmission. Hence we send the next - * chunk, if any, or complete the skb otherwise - */ - -void sirdev_write_complete(struct sir_dev *dev) -{ - unsigned long flags; - struct sk_buff *skb; - int actual = 0; - int err; - - spin_lock_irqsave(&dev->tx_lock, flags); - - pr_debug("%s() - dev->tx_buff.len = %d\n", - __func__, dev->tx_buff.len); - - if (likely(dev->tx_buff.len > 0)) { - /* Write data left in transmit buffer */ - actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); - - if (likely(actual>0)) { - dev->tx_buff.data += actual; - dev->tx_buff.len -= actual; - } - else if (unlikely(actual<0)) { - /* could be dropped later when we have tx_timeout to recover */ - net_err_ratelimited("%s: drv->do_write failed (%d)\n", - __func__, actual); - if ((skb=dev->tx_skb) != NULL) { - dev->tx_skb = NULL; - dev_kfree_skb_any(skb); - dev->netdev->stats.tx_errors++; - dev->netdev->stats.tx_dropped++; - } - dev->tx_buff.len = 0; - } - if (dev->tx_buff.len > 0) - goto done; /* more data to send later */ - } - - if (unlikely(dev->raw_tx != 0)) { - /* in raw mode we are just done now after the buffer was sent - * completely. Since this was requested by some dongle driver - * running under the control of the irda-thread we must take - * care here not to re-enable the queue. The queue will be - * restarted when the irda-thread has completed the request. - */ - - pr_debug("%s(), raw-tx done\n", __func__); - dev->raw_tx = 0; - goto done; /* no post-frame handling in raw mode */ - } - - /* we have finished now sending this skb. - * update statistics and free the skb. - * finally we check and trigger a pending speed change, if any. - * if not we switch to rx mode and wake the queue for further - * packets. - * note the scheduled speed request blocks until the lower - * client driver and the corresponding hardware has really - * finished sending all data (xmit fifo drained f.e.) - * before the speed change gets finally done and the queue - * re-activated. - */ - - pr_debug("%s(), finished with frame!\n", __func__); - - if ((skb=dev->tx_skb) != NULL) { - dev->tx_skb = NULL; - dev->netdev->stats.tx_packets++; - dev->netdev->stats.tx_bytes += skb->len; - dev_kfree_skb_any(skb); - } - - if (unlikely(dev->new_speed > 0)) { - pr_debug("%s(), Changing speed!\n", __func__); - err = sirdev_schedule_speed(dev, dev->new_speed); - if (unlikely(err)) { - /* should never happen - * forget the speed change and hope the stack recovers - */ - net_err_ratelimited("%s - schedule speed change failed: %d\n", - __func__, err); - netif_wake_queue(dev->netdev); - } - /* else: success - * speed change in progress now - * on completion dev->new_speed gets cleared, - * rx-reenabled and the queue restarted - */ - } - else { - sirdev_enable_rx(dev); - netif_wake_queue(dev->netdev); - } - -done: - spin_unlock_irqrestore(&dev->tx_lock, flags); -} -EXPORT_SYMBOL(sirdev_write_complete); - -/* called from client driver - likely with bh-context - to give us - * some more received bytes. We put them into the rx-buffer, - * normally unwrapping and building LAP-skb's (unless rx disabled) - */ - -int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count) -{ - if (!dev || !dev->netdev) { - net_warn_ratelimited("%s(), not ready yet!\n", __func__); - return -1; - } - - if (!dev->irlap) { - net_warn_ratelimited("%s - too early: %p / %zd!\n", - __func__, cp, count); - return -1; - } - - if (cp==NULL) { - /* error already at lower level receive - * just update stats and set media busy - */ - irda_device_set_media_busy(dev->netdev, TRUE); - dev->netdev->stats.rx_dropped++; - pr_debug("%s; rx-drop: %zd\n", __func__, count); - return 0; - } - - /* Read the characters into the buffer */ - if (likely(atomic_read(&dev->enable_rx))) { - while (count--) - /* Unwrap and destuff one byte */ - async_unwrap_char(dev->netdev, &dev->netdev->stats, - &dev->rx_buff, *cp++); - } else { - while (count--) { - /* rx not enabled: save the raw bytes and never - * trigger any netif_rx. The received bytes are flushed - * later when we re-enable rx but might be read meanwhile - * by the dongle driver. - */ - dev->rx_buff.data[dev->rx_buff.len++] = *cp++; - - /* What should we do when the buffer is full? */ - if (unlikely(dev->rx_buff.len == dev->rx_buff.truesize)) - dev->rx_buff.len = 0; - } - } - - return 0; -} -EXPORT_SYMBOL(sirdev_receive); - -/**********************************************************************/ - -/* callbacks from network layer */ - -static netdev_tx_t sirdev_hard_xmit(struct sk_buff *skb, - struct net_device *ndev) -{ - struct sir_dev *dev = netdev_priv(ndev); - unsigned long flags; - int actual = 0; - int err; - s32 speed; - - IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); - - netif_stop_queue(ndev); - - pr_debug("%s(), skb->len = %d\n", __func__, skb->len); - - speed = irda_get_next_speed(skb); - if ((speed != dev->speed) && (speed != -1)) { - if (!skb->len) { - err = sirdev_schedule_speed(dev, speed); - if (unlikely(err == -EWOULDBLOCK)) { - /* Failed to initiate the speed change, likely the fsm - * is still busy (pretty unlikely, but...) - * We refuse to accept the skb and return with the queue - * stopped so the network layer will retry after the - * fsm completes and wakes the queue. - */ - return NETDEV_TX_BUSY; - } - else if (unlikely(err)) { - /* other fatal error - forget the speed change and - * hope the stack will recover somehow - */ - netif_start_queue(ndev); - } - /* else: success - * speed change in progress now - * on completion the queue gets restarted - */ - - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } else - dev->new_speed = speed; - } - - /* Init tx buffer*/ - dev->tx_buff.data = dev->tx_buff.head; - - /* Check problems */ - if(spin_is_locked(&dev->tx_lock)) { - pr_debug("%s(), write not completed\n", __func__); - } - - /* serialize with write completion */ - spin_lock_irqsave(&dev->tx_lock, flags); - - /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ - dev->tx_buff.len = async_wrap_skb(skb, dev->tx_buff.data, dev->tx_buff.truesize); - - /* transmission will start now - disable receive. - * if we are just in the middle of an incoming frame, - * treat it as collision. probably it's a good idea to - * reset the rx_buf OUTSIDE_FRAME in this case too? - */ - atomic_set(&dev->enable_rx, 0); - if (unlikely(sirdev_is_receiving(dev))) - dev->netdev->stats.collisions++; - - actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); - - if (likely(actual > 0)) { - dev->tx_skb = skb; - dev->tx_buff.data += actual; - dev->tx_buff.len -= actual; - } - else if (unlikely(actual < 0)) { - /* could be dropped later when we have tx_timeout to recover */ - net_err_ratelimited("%s: drv->do_write failed (%d)\n", - __func__, actual); - dev_kfree_skb_any(skb); - dev->netdev->stats.tx_errors++; - dev->netdev->stats.tx_dropped++; - netif_wake_queue(ndev); - } - spin_unlock_irqrestore(&dev->tx_lock, flags); - - return NETDEV_TX_OK; -} - -/* called from network layer with rtnl hold */ - -static int sirdev_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct sir_dev *dev = netdev_priv(ndev); - int ret = 0; - - IRDA_ASSERT(dev != NULL, return -1;); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, ndev->name, cmd); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else - ret = sirdev_schedule_speed(dev, irq->ifr_baudrate); - /* cannot sleep here for completion - * we are called from network layer with rtnl hold - */ - break; - - case SIOCSDONGLE: /* Set dongle */ - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else - ret = sirdev_schedule_dongle_open(dev, irq->ifr_dongle); - /* cannot sleep here for completion - * we are called from network layer with rtnl hold - */ - break; - - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else - irda_device_set_media_busy(dev->netdev, TRUE); - break; - - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = sirdev_is_receiving(dev); - break; - - case SIOCSDTRRTS: - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else - ret = sirdev_schedule_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts); - /* cannot sleep here for completion - * we are called from network layer with rtnl hold - */ - break; - - case SIOCSMODE: -#if 0 - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else - ret = sirdev_schedule_mode(dev, irq->ifr_mode); - /* cannot sleep here for completion - * we are called from network layer with rtnl hold - */ - break; -#endif - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -/* ----------------------------------------------------------------------------- */ - -#define SIRBUF_ALLOCSIZE 4269 /* worst case size of a wrapped IrLAP frame */ - -static int sirdev_alloc_buffers(struct sir_dev *dev) -{ - dev->tx_buff.truesize = SIRBUF_ALLOCSIZE; - dev->rx_buff.truesize = IRDA_SKB_MAX_MTU; - - /* Bootstrap ZeroCopy Rx */ - dev->rx_buff.skb = __netdev_alloc_skb(dev->netdev, dev->rx_buff.truesize, - GFP_KERNEL); - if (dev->rx_buff.skb == NULL) - return -ENOMEM; - skb_reserve(dev->rx_buff.skb, 1); - dev->rx_buff.head = dev->rx_buff.skb->data; - - dev->tx_buff.head = kmalloc(dev->tx_buff.truesize, GFP_KERNEL); - if (dev->tx_buff.head == NULL) { - kfree_skb(dev->rx_buff.skb); - dev->rx_buff.skb = NULL; - dev->rx_buff.head = NULL; - return -ENOMEM; - } - - dev->tx_buff.data = dev->tx_buff.head; - dev->rx_buff.data = dev->rx_buff.head; - dev->tx_buff.len = 0; - dev->rx_buff.len = 0; - - dev->rx_buff.in_frame = FALSE; - dev->rx_buff.state = OUTSIDE_FRAME; - return 0; -}; - -static void sirdev_free_buffers(struct sir_dev *dev) -{ - kfree_skb(dev->rx_buff.skb); - kfree(dev->tx_buff.head); - dev->rx_buff.head = dev->tx_buff.head = NULL; - dev->rx_buff.skb = NULL; -} - -static int sirdev_open(struct net_device *ndev) -{ - struct sir_dev *dev = netdev_priv(ndev); - const struct sir_driver *drv = dev->drv; - - if (!drv) - return -ENODEV; - - /* increase the reference count of the driver module before doing serious stuff */ - if (!try_module_get(drv->owner)) - return -ESTALE; - - if (sirdev_alloc_buffers(dev)) - goto errout_dec; - - if (!dev->drv->start_dev || dev->drv->start_dev(dev)) - goto errout_free; - - sirdev_enable_rx(dev); - dev->raw_tx = 0; - - netif_start_queue(ndev); - dev->irlap = irlap_open(ndev, &dev->qos, dev->hwname); - if (!dev->irlap) - goto errout_stop; - - netif_wake_queue(ndev); - - pr_debug("%s - done, speed = %d\n", __func__, dev->speed); - - return 0; - -errout_stop: - atomic_set(&dev->enable_rx, 0); - if (dev->drv->stop_dev) - dev->drv->stop_dev(dev); -errout_free: - sirdev_free_buffers(dev); -errout_dec: - module_put(drv->owner); - return -EAGAIN; -} - -static int sirdev_close(struct net_device *ndev) -{ - struct sir_dev *dev = netdev_priv(ndev); - const struct sir_driver *drv; - -/* pr_debug("%s\n", __func__); */ - - netif_stop_queue(ndev); - - down(&dev->fsm.sem); /* block on pending config completion */ - - atomic_set(&dev->enable_rx, 0); - - if (unlikely(!dev->irlap)) - goto out; - irlap_close(dev->irlap); - dev->irlap = NULL; - - drv = dev->drv; - if (unlikely(!drv || !dev->priv)) - goto out; - - if (drv->stop_dev) - drv->stop_dev(dev); - - sirdev_free_buffers(dev); - module_put(drv->owner); - -out: - dev->speed = 0; - up(&dev->fsm.sem); - return 0; -} - -static const struct net_device_ops sirdev_ops = { - .ndo_start_xmit = sirdev_hard_xmit, - .ndo_open = sirdev_open, - .ndo_stop = sirdev_close, - .ndo_do_ioctl = sirdev_ioctl, -}; -/* ----------------------------------------------------------------------------- */ - -struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name) -{ - struct net_device *ndev; - struct sir_dev *dev; - - pr_debug("%s - %s\n", __func__, name); - - /* instead of adding tests to protect against drv->do_write==NULL - * at several places we refuse to create a sir_dev instance for - * drivers which don't implement do_write. - */ - if (!drv || !drv->do_write) - return NULL; - - /* - * Allocate new instance of the device - */ - ndev = alloc_irdadev(sizeof(*dev)); - if (ndev == NULL) { - net_err_ratelimited("%s - Can't allocate memory for IrDA control block!\n", - __func__); - goto out; - } - dev = netdev_priv(ndev); - - irda_init_max_qos_capabilies(&dev->qos); - dev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - dev->qos.min_turn_time.bits = drv->qos_mtt_bits; - irda_qos_bits_to_value(&dev->qos); - - strncpy(dev->hwname, name, sizeof(dev->hwname)-1); - - atomic_set(&dev->enable_rx, 0); - dev->tx_skb = NULL; - - spin_lock_init(&dev->tx_lock); - sema_init(&dev->fsm.sem, 1); - - dev->drv = drv; - dev->netdev = ndev; - - /* Override the network functions we need to use */ - ndev->netdev_ops = &sirdev_ops; - - if (register_netdev(ndev)) { - net_err_ratelimited("%s(), register_netdev() failed!\n", - __func__); - goto out_freenetdev; - } - - return dev; - -out_freenetdev: - free_netdev(ndev); -out: - return NULL; -} -EXPORT_SYMBOL(sirdev_get_instance); - -int sirdev_put_instance(struct sir_dev *dev) -{ - int err = 0; - - pr_debug("%s\n", __func__); - - atomic_set(&dev->enable_rx, 0); - - netif_carrier_off(dev->netdev); - netif_device_detach(dev->netdev); - - if (dev->dongle_drv) - err = sirdev_schedule_dongle_close(dev); - if (err) - net_err_ratelimited("%s - error %d\n", __func__, err); - - sirdev_close(dev->netdev); - - down(&dev->fsm.sem); - dev->fsm.state = SIRDEV_STATE_DEAD; /* mark staled */ - dev->dongle_drv = NULL; - dev->priv = NULL; - up(&dev->fsm.sem); - - /* Remove netdevice */ - unregister_netdev(dev->netdev); - - free_netdev(dev->netdev); - - return 0; -} -EXPORT_SYMBOL(sirdev_put_instance); - -static int __init sir_wq_init(void) -{ - irda_sir_wq = create_singlethread_workqueue("irda_sir_wq"); - if (!irda_sir_wq) - return -ENOMEM; - return 0; -} - -static void __exit sir_wq_exit(void) -{ - destroy_workqueue(irda_sir_wq); -} - -module_init(sir_wq_init); -module_exit(sir_wq_exit); - -MODULE_AUTHOR("Martin Diehl "); -MODULE_DESCRIPTION("IrDA SIR core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/irda/drivers/sir_dongle.c b/drivers/staging/irda/drivers/sir_dongle.c deleted file mode 100644 index 7436f73ff1bb..000000000000 --- a/drivers/staging/irda/drivers/sir_dongle.c +++ /dev/null @@ -1,133 +0,0 @@ -/********************************************************************* - * - * sir_dongle.c: manager for serial dongle protocol drivers - * - * Copyright (c) 2002 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#include -#include -#include -#include - -#include - -#include "sir-dev.h" - -/************************************************************************** - * - * dongle registration and attachment - * - */ - -static LIST_HEAD(dongle_list); /* list of registered dongle drivers */ -static DEFINE_MUTEX(dongle_list_lock); /* protects the list */ - -int irda_register_dongle(struct dongle_driver *new) -{ - struct list_head *entry; - struct dongle_driver *drv; - - pr_debug("%s : registering dongle \"%s\" (%d).\n", - __func__, new->driver_name, new->type); - - mutex_lock(&dongle_list_lock); - list_for_each(entry, &dongle_list) { - drv = list_entry(entry, struct dongle_driver, dongle_list); - if (new->type == drv->type) { - mutex_unlock(&dongle_list_lock); - return -EEXIST; - } - } - list_add(&new->dongle_list, &dongle_list); - mutex_unlock(&dongle_list_lock); - return 0; -} -EXPORT_SYMBOL(irda_register_dongle); - -int irda_unregister_dongle(struct dongle_driver *drv) -{ - mutex_lock(&dongle_list_lock); - list_del(&drv->dongle_list); - mutex_unlock(&dongle_list_lock); - return 0; -} -EXPORT_SYMBOL(irda_unregister_dongle); - -int sirdev_get_dongle(struct sir_dev *dev, IRDA_DONGLE type) -{ - struct list_head *entry; - const struct dongle_driver *drv = NULL; - int err = -EINVAL; - - request_module("irda-dongle-%d", type); - - if (dev->dongle_drv != NULL) - return -EBUSY; - - /* serialize access to the list of registered dongles */ - mutex_lock(&dongle_list_lock); - - list_for_each(entry, &dongle_list) { - drv = list_entry(entry, struct dongle_driver, dongle_list); - if (drv->type == type) - break; - else - drv = NULL; - } - - if (!drv) { - err = -ENODEV; - goto out_unlock; /* no such dongle */ - } - - /* handling of SMP races with dongle module removal - three cases: - * 1) dongle driver was already unregistered - then we haven't found the - * requested dongle above and are already out here - * 2) the module is already marked deleted but the driver is still - * registered - then the try_module_get() below will fail - * 3) the try_module_get() below succeeds before the module is marked - * deleted - then sys_delete_module() fails and prevents the removal - * because the module is in use. - */ - - if (!try_module_get(drv->owner)) { - err = -ESTALE; - goto out_unlock; /* rmmod already pending */ - } - dev->dongle_drv = drv; - - if (!drv->open || (err=drv->open(dev))!=0) - goto out_reject; /* failed to open driver */ - - mutex_unlock(&dongle_list_lock); - return 0; - -out_reject: - dev->dongle_drv = NULL; - module_put(drv->owner); -out_unlock: - mutex_unlock(&dongle_list_lock); - return err; -} - -int sirdev_put_dongle(struct sir_dev *dev) -{ - const struct dongle_driver *drv = dev->dongle_drv; - - if (drv) { - if (drv->close) - drv->close(dev); /* close this dongle instance */ - - dev->dongle_drv = NULL; /* unlink the dongle driver */ - module_put(drv->owner);/* decrement driver's module refcount */ - } - - return 0; -} diff --git a/drivers/staging/irda/drivers/smsc-ircc2.c b/drivers/staging/irda/drivers/smsc-ircc2.c deleted file mode 100644 index 19a55cba6beb..000000000000 --- a/drivers/staging/irda/drivers/smsc-ircc2.c +++ /dev/null @@ -1,3026 +0,0 @@ -/********************************************************************* - * - * Description: Driver for the SMC Infrared Communications Controller - * Author: Daniele Peri (peri@csai.unipa.it) - * Created at: - * Modified at: - * Modified by: - * - * Copyright (c) 2002 Daniele Peri - * All Rights Reserved. - * Copyright (c) 2002 Jean Tourrilhes - * Copyright (c) 2006 Linus Walleij - * - * - * Based on smc-ircc.c: - * - * Copyright (c) 2001 Stefani Seibold - * Copyright (c) 1999-2001 Dag Brattli - * Copyright (c) 1998-1999 Thomas Davis, - * - * and irport.c: - * - * Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, All Rights Reserved. - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#ifdef CONFIG_PCI -#include -#endif - -#include -#include -#include - -#include "smsc-ircc2.h" -#include "smsc-sio.h" - - -MODULE_AUTHOR("Daniele Peri "); -MODULE_DESCRIPTION("SMC IrCC SIR/FIR controller driver"); -MODULE_LICENSE("GPL"); - -static bool smsc_nopnp = true; -module_param_named(nopnp, smsc_nopnp, bool, 0); -MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings, defaults to true"); - -#define DMA_INVAL 255 -static int ircc_dma = DMA_INVAL; -module_param_hw(ircc_dma, int, dma, 0); -MODULE_PARM_DESC(ircc_dma, "DMA channel"); - -#define IRQ_INVAL 255 -static int ircc_irq = IRQ_INVAL; -module_param_hw(ircc_irq, int, irq, 0); -MODULE_PARM_DESC(ircc_irq, "IRQ line"); - -static int ircc_fir; -module_param_hw(ircc_fir, int, ioport, 0); -MODULE_PARM_DESC(ircc_fir, "FIR Base Address"); - -static int ircc_sir; -module_param_hw(ircc_sir, int, ioport, 0); -MODULE_PARM_DESC(ircc_sir, "SIR Base Address"); - -static int ircc_cfg; -module_param_hw(ircc_cfg, int, ioport, 0); -MODULE_PARM_DESC(ircc_cfg, "Configuration register base address"); - -static int ircc_transceiver; -module_param(ircc_transceiver, int, 0); -MODULE_PARM_DESC(ircc_transceiver, "Transceiver type"); - -/* Types */ - -#ifdef CONFIG_PCI -struct smsc_ircc_subsystem_configuration { - unsigned short vendor; /* PCI vendor ID */ - unsigned short device; /* PCI vendor ID */ - unsigned short subvendor; /* PCI subsystem vendor ID */ - unsigned short subdevice; /* PCI subsystem device ID */ - unsigned short sir_io; /* I/O port for SIR */ - unsigned short fir_io; /* I/O port for FIR */ - unsigned char fir_irq; /* FIR IRQ */ - unsigned char fir_dma; /* FIR DMA */ - unsigned short cfg_base; /* I/O port for chip configuration */ - int (*preconfigure)(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); /* Preconfig function */ - const char *name; /* name shown as info */ -}; -#endif - -struct smsc_transceiver { - char *name; - void (*set_for_speed)(int fir_base, u32 speed); - int (*probe)(int fir_base); -}; - -struct smsc_chip { - char *name; - #if 0 - u8 type; - #endif - u16 flags; - u8 devid; - u8 rev; -}; - -struct smsc_chip_address { - unsigned int cfg_base; - unsigned int type; -}; - -/* Private data for each instance */ -struct smsc_ircc_cb { - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - - chipio_t io; /* IrDA controller information */ - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - dma_addr_t tx_buff_dma; - dma_addr_t rx_buff_dma; - - struct qos_info qos; /* QoS capabilities for this device */ - - spinlock_t lock; /* For serializing operations */ - - __u32 new_speed; - __u32 flags; /* Interface flags */ - - int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */ - int tx_len; /* Number of frames in tx_buff */ - - int transceiver; - struct platform_device *pldev; -}; - -/* Constants */ - -#define SMSC_IRCC2_DRIVER_NAME "smsc-ircc2" - -#define SMSC_IRCC2_C_IRDA_FALLBACK_SPEED 9600 -#define SMSC_IRCC2_C_DEFAULT_TRANSCEIVER 1 -#define SMSC_IRCC2_C_NET_TIMEOUT 0 -#define SMSC_IRCC2_C_SIR_STOP 0 - -static const char *driver_name = SMSC_IRCC2_DRIVER_NAME; - -/* Prototypes */ - -static int smsc_ircc_open(unsigned int firbase, unsigned int sirbase, u8 dma, u8 irq); -static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base); -static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq); -static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self); -static void smsc_ircc_init_chip(struct smsc_ircc_cb *self); -static int __exit smsc_ircc_close(struct smsc_ircc_cb *self); -static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self); -static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self); -static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self); -static netdev_tx_t smsc_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev); -static netdev_tx_t smsc_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev); -static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int bofs); -static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self); -static void smsc_ircc_change_speed(struct smsc_ircc_cb *self, u32 speed); -static void smsc_ircc_set_sir_speed(struct smsc_ircc_cb *self, u32 speed); -static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id); -static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev); -static void smsc_ircc_sir_start(struct smsc_ircc_cb *self); -#if SMSC_IRCC2_C_SIR_STOP -static void smsc_ircc_sir_stop(struct smsc_ircc_cb *self); -#endif -static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self); -static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len); -static int smsc_ircc_net_open(struct net_device *dev); -static int smsc_ircc_net_close(struct net_device *dev); -static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -#if SMSC_IRCC2_C_NET_TIMEOUT -static void smsc_ircc_timeout(struct net_device *dev); -#endif -static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self); -static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self); -static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed); -static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self); - -/* Probing */ -static int __init smsc_ircc_look_for_chips(void); -static const struct smsc_chip * __init smsc_ircc_probe(unsigned short cfg_base, u8 reg, const struct smsc_chip *chip, char *type); -static int __init smsc_superio_flat(const struct smsc_chip *chips, unsigned short cfg_base, char *type); -static int __init smsc_superio_paged(const struct smsc_chip *chips, unsigned short cfg_base, char *type); -static int __init smsc_superio_fdc(unsigned short cfg_base); -static int __init smsc_superio_lpc(unsigned short cfg_base); -#ifdef CONFIG_PCI -static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuration *conf); -static int __init preconfigure_through_82801(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); -static void __init preconfigure_ali_port(struct pci_dev *dev, - unsigned short port); -static int __init preconfigure_through_ali(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); -static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg, - unsigned short ircc_fir, - unsigned short ircc_sir, - unsigned char ircc_dma, - unsigned char ircc_irq); -#endif - -/* Transceivers specific functions */ - -static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed); -static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base); -static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed); -static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base); -static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed); -static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base); - -/* Power Management */ - -static int smsc_ircc_suspend(struct platform_device *dev, pm_message_t state); -static int smsc_ircc_resume(struct platform_device *dev); - -static struct platform_driver smsc_ircc_driver = { - .suspend = smsc_ircc_suspend, - .resume = smsc_ircc_resume, - .driver = { - .name = SMSC_IRCC2_DRIVER_NAME, - }, -}; - -/* Transceivers for SMSC-ircc */ - -static struct smsc_transceiver smsc_transceivers[] = -{ - { "Toshiba Satellite 1800 (GP data pin select)", smsc_ircc_set_transceiver_toshiba_sat1800, smsc_ircc_probe_transceiver_toshiba_sat1800 }, - { "Fast pin select", smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select, smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select }, - { "ATC IRMode", smsc_ircc_set_transceiver_smsc_ircc_atc, smsc_ircc_probe_transceiver_smsc_ircc_atc }, - { NULL, NULL } -}; -#define SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS (ARRAY_SIZE(smsc_transceivers) - 1) - -/* SMC SuperIO chipsets definitions */ - -#define KEY55_1 0 /* SuperIO Configuration mode with Key <0x55> */ -#define KEY55_2 1 /* SuperIO Configuration mode with Key <0x55,0x55> */ -#define NoIRDA 2 /* SuperIO Chip has no IRDA Port */ -#define SIR 0 /* SuperIO Chip has only slow IRDA */ -#define FIR 4 /* SuperIO Chip has fast IRDA */ -#define SERx4 8 /* SuperIO Chip supports 115,2 KBaud * 4=460,8 KBaud */ - -static struct smsc_chip __initdata fdc_chips_flat[] = -{ - /* Base address 0x3f0 or 0x370 */ - { "37C44", KEY55_1|NoIRDA, 0x00, 0x00 }, /* This chip cannot be detected */ - { "37C665GT", KEY55_2|NoIRDA, 0x65, 0x01 }, - { "37C665GT", KEY55_2|NoIRDA, 0x66, 0x01 }, - { "37C669", KEY55_2|SIR|SERx4, 0x03, 0x02 }, - { "37C669", KEY55_2|SIR|SERx4, 0x04, 0x02 }, /* ID? */ - { "37C78", KEY55_2|NoIRDA, 0x78, 0x00 }, - { "37N769", KEY55_1|FIR|SERx4, 0x28, 0x00 }, - { "37N869", KEY55_1|FIR|SERx4, 0x29, 0x00 }, - { NULL } -}; - -static struct smsc_chip __initdata fdc_chips_paged[] = -{ - /* Base address 0x3f0 or 0x370 */ - { "37B72X", KEY55_1|SIR|SERx4, 0x4c, 0x00 }, - { "37B77X", KEY55_1|SIR|SERx4, 0x43, 0x00 }, - { "37B78X", KEY55_1|SIR|SERx4, 0x44, 0x00 }, - { "37B80X", KEY55_1|SIR|SERx4, 0x42, 0x00 }, - { "37C67X", KEY55_1|FIR|SERx4, 0x40, 0x00 }, - { "37C93X", KEY55_2|SIR|SERx4, 0x02, 0x01 }, - { "37C93XAPM", KEY55_1|SIR|SERx4, 0x30, 0x01 }, - { "37C93XFR", KEY55_2|FIR|SERx4, 0x03, 0x01 }, - { "37M707", KEY55_1|SIR|SERx4, 0x42, 0x00 }, - { "37M81X", KEY55_1|SIR|SERx4, 0x4d, 0x00 }, - { "37N958FR", KEY55_1|FIR|SERx4, 0x09, 0x04 }, - { "37N971", KEY55_1|FIR|SERx4, 0x0a, 0x00 }, - { "37N972", KEY55_1|FIR|SERx4, 0x0b, 0x00 }, - { NULL } -}; - -static struct smsc_chip __initdata lpc_chips_flat[] = -{ - /* Base address 0x2E or 0x4E */ - { "47N227", KEY55_1|FIR|SERx4, 0x5a, 0x00 }, - { "47N227", KEY55_1|FIR|SERx4, 0x7a, 0x00 }, - { "47N267", KEY55_1|FIR|SERx4, 0x5e, 0x00 }, - { NULL } -}; - -static struct smsc_chip __initdata lpc_chips_paged[] = -{ - /* Base address 0x2E or 0x4E */ - { "47B27X", KEY55_1|SIR|SERx4, 0x51, 0x00 }, - { "47B37X", KEY55_1|SIR|SERx4, 0x52, 0x00 }, - { "47M10X", KEY55_1|SIR|SERx4, 0x59, 0x00 }, - { "47M120", KEY55_1|NoIRDA|SERx4, 0x5c, 0x00 }, - { "47M13X", KEY55_1|SIR|SERx4, 0x59, 0x00 }, - { "47M14X", KEY55_1|SIR|SERx4, 0x5f, 0x00 }, - { "47N252", KEY55_1|FIR|SERx4, 0x0e, 0x00 }, - { "47S42X", KEY55_1|SIR|SERx4, 0x57, 0x00 }, - { NULL } -}; - -#define SMSCSIO_TYPE_FDC 1 -#define SMSCSIO_TYPE_LPC 2 -#define SMSCSIO_TYPE_FLAT 4 -#define SMSCSIO_TYPE_PAGED 8 - -static struct smsc_chip_address __initdata possible_addresses[] = -{ - { 0x3f0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, - { 0x370, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, - { 0xe0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, - { 0x2e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, - { 0x4e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, - { 0, 0 } -}; - -/* Globals */ - -static struct smsc_ircc_cb *dev_self[] = { NULL, NULL }; -static unsigned short dev_count; - -static inline void register_bank(int iobase, int bank) -{ - outb(((inb(iobase + IRCC_MASTER) & 0xf0) | (bank & 0x07)), - iobase + IRCC_MASTER); -} - -/* PNP hotplug support */ -static const struct pnp_device_id smsc_ircc_pnp_table[] = { - { .id = "SMCf010", .driver_data = 0 }, - /* and presumably others */ - { } -}; -MODULE_DEVICE_TABLE(pnp, smsc_ircc_pnp_table); - -static int pnp_driver_registered; - -#ifdef CONFIG_PNP -static int smsc_ircc_pnp_probe(struct pnp_dev *dev, - const struct pnp_device_id *dev_id) -{ - unsigned int firbase, sirbase; - u8 dma, irq; - - if (!(pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && - pnp_dma_valid(dev, 0) && pnp_irq_valid(dev, 0))) - return -EINVAL; - - sirbase = pnp_port_start(dev, 0); - firbase = pnp_port_start(dev, 1); - dma = pnp_dma(dev, 0); - irq = pnp_irq(dev, 0); - - if (smsc_ircc_open(firbase, sirbase, dma, irq)) - return -ENODEV; - - return 0; -} - -static struct pnp_driver smsc_ircc_pnp_driver = { - .name = "smsc-ircc2", - .id_table = smsc_ircc_pnp_table, - .probe = smsc_ircc_pnp_probe, -}; -#else /* CONFIG_PNP */ -static struct pnp_driver smsc_ircc_pnp_driver; -#endif - -/******************************************************************************* - * - * - * SMSC-ircc stuff - * - * - *******************************************************************************/ - -static int __init smsc_ircc_legacy_probe(void) -{ - int ret = 0; - -#ifdef CONFIG_PCI - if (smsc_ircc_preconfigure_subsystems(ircc_cfg, ircc_fir, ircc_sir, ircc_dma, ircc_irq) < 0) { - /* Ignore errors from preconfiguration */ - net_err_ratelimited("%s, Preconfiguration failed !\n", - driver_name); - } -#endif - - if (ircc_fir > 0 && ircc_sir > 0) { - net_info_ratelimited(" Overriding FIR address 0x%04x\n", - ircc_fir); - net_info_ratelimited(" Overriding SIR address 0x%04x\n", - ircc_sir); - - if (smsc_ircc_open(ircc_fir, ircc_sir, ircc_dma, ircc_irq)) - ret = -ENODEV; - } else { - ret = -ENODEV; - - /* try user provided configuration register base address */ - if (ircc_cfg > 0) { - net_info_ratelimited(" Overriding configuration address 0x%04x\n", - ircc_cfg); - if (!smsc_superio_fdc(ircc_cfg)) - ret = 0; - if (!smsc_superio_lpc(ircc_cfg)) - ret = 0; - } - - if (smsc_ircc_look_for_chips() > 0) - ret = 0; - } - return ret; -} - -/* - * Function smsc_ircc_init () - * - * Initialize chip. Just try to find out how many chips we are dealing with - * and where they are - */ -static int __init smsc_ircc_init(void) -{ - int ret; - - pr_debug("%s\n", __func__); - - ret = platform_driver_register(&smsc_ircc_driver); - if (ret) { - net_err_ratelimited("%s, Can't register driver!\n", - driver_name); - return ret; - } - - dev_count = 0; - - if (smsc_nopnp || !pnp_platform_devices || - ircc_cfg || ircc_fir || ircc_sir || - ircc_dma != DMA_INVAL || ircc_irq != IRQ_INVAL) { - ret = smsc_ircc_legacy_probe(); - } else { - if (pnp_register_driver(&smsc_ircc_pnp_driver) == 0) - pnp_driver_registered = 1; - } - - if (ret) { - if (pnp_driver_registered) - pnp_unregister_driver(&smsc_ircc_pnp_driver); - platform_driver_unregister(&smsc_ircc_driver); - } - - return ret; -} - -static netdev_tx_t smsc_ircc_net_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct smsc_ircc_cb *self = netdev_priv(dev); - - if (self->io.speed > 115200) - return smsc_ircc_hard_xmit_fir(skb, dev); - else - return smsc_ircc_hard_xmit_sir(skb, dev); -} - -static const struct net_device_ops smsc_ircc_netdev_ops = { - .ndo_open = smsc_ircc_net_open, - .ndo_stop = smsc_ircc_net_close, - .ndo_do_ioctl = smsc_ircc_net_ioctl, - .ndo_start_xmit = smsc_ircc_net_xmit, -#if SMSC_IRCC2_C_NET_TIMEOUT - .ndo_tx_timeout = smsc_ircc_timeout, -#endif -}; - -/* - * Function smsc_ircc_open (firbase, sirbase, dma, irq) - * - * Try to open driver instance - * - */ -static int smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq) -{ - struct smsc_ircc_cb *self; - struct net_device *dev; - int err; - - pr_debug("%s\n", __func__); - - err = smsc_ircc_present(fir_base, sir_base); - if (err) - goto err_out; - - err = -ENOMEM; - if (dev_count >= ARRAY_SIZE(dev_self)) { - net_warn_ratelimited("%s(), too many devices!\n", __func__); - goto err_out1; - } - - /* - * Allocate new instance of the driver - */ - dev = alloc_irdadev(sizeof(struct smsc_ircc_cb)); - if (!dev) { - net_warn_ratelimited("%s() can't allocate net device\n", - __func__); - goto err_out1; - } - -#if SMSC_IRCC2_C_NET_TIMEOUT - dev->watchdog_timeo = HZ * 2; /* Allow enough time for speed change */ -#endif - dev->netdev_ops = &smsc_ircc_netdev_ops; - - self = netdev_priv(dev); - self->netdev = dev; - - /* Make ifconfig display some details */ - dev->base_addr = self->io.fir_base = fir_base; - dev->irq = self->io.irq = irq; - - /* Need to store self somewhere */ - dev_self[dev_count] = self; - spin_lock_init(&self->lock); - - self->rx_buff.truesize = SMSC_IRCC2_RX_BUFF_TRUESIZE; - self->tx_buff.truesize = SMSC_IRCC2_TX_BUFF_TRUESIZE; - - self->rx_buff.head = - dma_zalloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL); - if (self->rx_buff.head == NULL) - goto err_out2; - - self->tx_buff.head = - dma_zalloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL); - if (self->tx_buff.head == NULL) - goto err_out3; - - self->rx_buff.in_frame = FALSE; - self->rx_buff.state = OUTSIDE_FRAME; - self->tx_buff.data = self->tx_buff.head; - self->rx_buff.data = self->rx_buff.head; - - smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq); - smsc_ircc_setup_qos(self); - smsc_ircc_init_chip(self); - - if (ircc_transceiver > 0 && - ircc_transceiver < SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS) - self->transceiver = ircc_transceiver; - else - smsc_ircc_probe_transceiver(self); - - err = register_netdev(self->netdev); - if (err) { - net_err_ratelimited("%s, Network device registration failed!\n", - driver_name); - goto err_out4; - } - - self->pldev = platform_device_register_simple(SMSC_IRCC2_DRIVER_NAME, - dev_count, NULL, 0); - if (IS_ERR(self->pldev)) { - err = PTR_ERR(self->pldev); - goto err_out5; - } - platform_set_drvdata(self->pldev, self); - - net_info_ratelimited("IrDA: Registered device %s\n", dev->name); - dev_count++; - - return 0; - - err_out5: - unregister_netdev(self->netdev); - - err_out4: - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - err_out3: - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - err_out2: - free_netdev(self->netdev); - dev_self[dev_count] = NULL; - err_out1: - release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); - release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); - err_out: - return err; -} - -/* - * Function smsc_ircc_present(fir_base, sir_base) - * - * Check the smsc-ircc chip presence - * - */ -static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base) -{ - unsigned char low, high, chip, config, dma, irq, version; - - if (!request_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT, - driver_name)) { - net_warn_ratelimited("%s: can't get fir_base of 0x%03x\n", - __func__, fir_base); - goto out1; - } - - if (!request_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT, - driver_name)) { - net_warn_ratelimited("%s: can't get sir_base of 0x%03x\n", - __func__, sir_base); - goto out2; - } - - register_bank(fir_base, 3); - - high = inb(fir_base + IRCC_ID_HIGH); - low = inb(fir_base + IRCC_ID_LOW); - chip = inb(fir_base + IRCC_CHIP_ID); - version = inb(fir_base + IRCC_VERSION); - config = inb(fir_base + IRCC_INTERFACE); - dma = config & IRCC_INTERFACE_DMA_MASK; - irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4; - - if (high != 0x10 || low != 0xb8 || (chip != 0xf1 && chip != 0xf2)) { - net_warn_ratelimited("%s(), addr 0x%04x - no device found!\n", - __func__, fir_base); - goto out3; - } - net_info_ratelimited("SMsC IrDA Controller found\n IrCC version %d.%d, firport 0x%03x, sirport 0x%03x dma=%d, irq=%d\n", - chip & 0x0f, version, - fir_base, sir_base, dma, irq); - - return 0; - - out3: - release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); - out2: - release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); - out1: - return -ENODEV; -} - -/* - * Function smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq) - * - * Setup I/O - * - */ -static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, - unsigned int fir_base, unsigned int sir_base, - u8 dma, u8 irq) -{ - unsigned char config, chip_dma, chip_irq; - - register_bank(fir_base, 3); - config = inb(fir_base + IRCC_INTERFACE); - chip_dma = config & IRCC_INTERFACE_DMA_MASK; - chip_irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4; - - self->io.fir_base = fir_base; - self->io.sir_base = sir_base; - self->io.fir_ext = SMSC_IRCC2_FIR_CHIP_IO_EXTENT; - self->io.sir_ext = SMSC_IRCC2_SIR_CHIP_IO_EXTENT; - self->io.fifo_size = SMSC_IRCC2_FIFO_SIZE; - self->io.speed = SMSC_IRCC2_C_IRDA_FALLBACK_SPEED; - - if (irq != IRQ_INVAL) { - if (irq != chip_irq) - net_info_ratelimited("%s, Overriding IRQ - chip says %d, using %d\n", - driver_name, chip_irq, irq); - self->io.irq = irq; - } else - self->io.irq = chip_irq; - - if (dma != DMA_INVAL) { - if (dma != chip_dma) - net_info_ratelimited("%s, Overriding DMA - chip says %d, using %d\n", - driver_name, chip_dma, dma); - self->io.dma = dma; - } else - self->io.dma = chip_dma; - -} - -/* - * Function smsc_ircc_setup_qos(self) - * - * Setup qos - * - */ -static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self) -{ - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| - IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); - - self->qos.min_turn_time.bits = SMSC_IRCC2_MIN_TURN_TIME; - self->qos.window_size.bits = SMSC_IRCC2_WINDOW_SIZE; - irda_qos_bits_to_value(&self->qos); -} - -/* - * Function smsc_ircc_init_chip(self) - * - * Init chip - * - */ -static void smsc_ircc_init_chip(struct smsc_ircc_cb *self) -{ - int iobase = self->io.fir_base; - - register_bank(iobase, 0); - outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER); - outb(0x00, iobase + IRCC_MASTER); - - register_bank(iobase, 1); - outb(((inb(iobase + IRCC_SCE_CFGA) & 0x87) | IRCC_CFGA_IRDA_SIR_A), - iobase + IRCC_SCE_CFGA); - -#ifdef smsc_669 /* Uses pin 88/89 for Rx/Tx */ - outb(((inb(iobase + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), - iobase + IRCC_SCE_CFGB); -#else - outb(((inb(iobase + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR), - iobase + IRCC_SCE_CFGB); -#endif - (void) inb(iobase + IRCC_FIFO_THRESHOLD); - outb(SMSC_IRCC2_FIFO_THRESHOLD, iobase + IRCC_FIFO_THRESHOLD); - - register_bank(iobase, 4); - outb((inb(iobase + IRCC_CONTROL) & 0x30), iobase + IRCC_CONTROL); - - register_bank(iobase, 0); - outb(0, iobase + IRCC_LCR_A); - - smsc_ircc_set_sir_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED); - - /* Power on device */ - outb(0x00, iobase + IRCC_MASTER); -} - -/* - * Function smsc_ircc_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct smsc_ircc_cb *self; - unsigned long flags; - int ret = 0; - - IRDA_ASSERT(dev != NULL, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self != NULL, return -1;); - - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - ret = -EPERM; - else { - /* Make sure we are the only one touching - * self->io.speed and the hardware - Jean II */ - spin_lock_irqsave(&self->lock, flags); - smsc_ircc_change_speed(self, irq->ifr_baudrate); - spin_unlock_irqrestore(&self->lock, flags); - } - break; - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = smsc_ircc_is_receiving(self); - break; - #if 0 - case SIOCSDTRRTS: - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - smsc_ircc_sir_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts); - break; - #endif - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -#if SMSC_IRCC2_C_NET_TIMEOUT -/* - * Function smsc_ircc_timeout (struct net_device *dev) - * - * The networking timeout management. - * - */ - -static void smsc_ircc_timeout(struct net_device *dev) -{ - struct smsc_ircc_cb *self = netdev_priv(dev); - unsigned long flags; - - net_warn_ratelimited("%s: transmit timed out, changing speed to: %d\n", - dev->name, self->io.speed); - spin_lock_irqsave(&self->lock, flags); - smsc_ircc_sir_start(self); - smsc_ircc_change_speed(self, self->io.speed); - netif_trans_update(dev); /* prevent tx timeout */ - netif_wake_queue(dev); - spin_unlock_irqrestore(&self->lock, flags); -} -#endif - -/* - * Function smsc_ircc_hard_xmit_sir (struct sk_buff *skb, struct net_device *dev) - * - * Transmits the current frame until FIFO is full, then - * waits until the next transmit interrupt, and continues until the - * frame is transmitted. - */ -static netdev_tx_t smsc_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev) -{ - struct smsc_ircc_cb *self; - unsigned long flags; - s32 speed; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); - - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); - - netif_stop_queue(dev); - - /* Make sure test of self->io.speed & speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if (speed != self->io.speed && speed != -1) { - /* Check for empty frame */ - if (!skb->len) { - /* - * We send frames one by one in SIR mode (no - * pipelining), so at this point, if we were sending - * a previous frame, we just received the interrupt - * telling us it is finished (UART_IIR_THRI). - * Therefore, waiting for the transmitter to really - * finish draining the fifo won't take too long. - * And the interrupt handler is not expected to run. - * - Jean II */ - smsc_ircc_sir_wait_hw_transmitter_finish(self); - smsc_ircc_change_speed(self, speed); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - self->new_speed = speed; - } - - /* Init tx buffer */ - self->tx_buff.data = self->tx_buff.head; - - /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - dev->stats.tx_bytes += self->tx_buff.len; - - /* Turn on transmit finished interrupt. Will fire immediately! */ - outb(UART_IER_THRI, self->io.sir_base + UART_IER); - - spin_unlock_irqrestore(&self->lock, flags); - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* - * Function smsc_ircc_set_fir_speed (self, baud) - * - * Change the speed of the device - * - */ -static void smsc_ircc_set_fir_speed(struct smsc_ircc_cb *self, u32 speed) -{ - int fir_base, ir_mode, ctrl, fast; - - IRDA_ASSERT(self != NULL, return;); - fir_base = self->io.fir_base; - - self->io.speed = speed; - - switch (speed) { - default: - case 576000: - ir_mode = IRCC_CFGA_IRDA_HDLC; - ctrl = IRCC_CRC; - fast = 0; - pr_debug("%s(), handling baud of 576000\n", __func__); - break; - case 1152000: - ir_mode = IRCC_CFGA_IRDA_HDLC; - ctrl = IRCC_1152 | IRCC_CRC; - fast = IRCC_LCR_A_FAST | IRCC_LCR_A_GP_DATA; - pr_debug("%s(), handling baud of 1152000\n", - __func__); - break; - case 4000000: - ir_mode = IRCC_CFGA_IRDA_4PPM; - ctrl = IRCC_CRC; - fast = IRCC_LCR_A_FAST; - pr_debug("%s(), handling baud of 4000000\n", - __func__); - break; - } - #if 0 - Now in tranceiver! - /* This causes an interrupt */ - register_bank(fir_base, 0); - outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast, fir_base + IRCC_LCR_A); - #endif - - register_bank(fir_base, 1); - outb(((inb(fir_base + IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | ir_mode), fir_base + IRCC_SCE_CFGA); - - register_bank(fir_base, 4); - outb((inb(fir_base + IRCC_CONTROL) & 0x30) | ctrl, fir_base + IRCC_CONTROL); -} - -/* - * Function smsc_ircc_fir_start(self) - * - * Change the speed of the device - * - */ -static void smsc_ircc_fir_start(struct smsc_ircc_cb *self) -{ - struct net_device *dev; - int fir_base; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(self != NULL, return;); - dev = self->netdev; - IRDA_ASSERT(dev != NULL, return;); - - fir_base = self->io.fir_base; - - /* Reset everything */ - - /* Clear FIFO */ - outb(inb(fir_base + IRCC_LCR_A) | IRCC_LCR_A_FIFO_RESET, fir_base + IRCC_LCR_A); - - /* Enable interrupt */ - /*outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, fir_base + IRCC_IER);*/ - - register_bank(fir_base, 1); - - /* Select the TX/RX interface */ -#ifdef SMSC_669 /* Uses pin 88/89 for Rx/Tx */ - outb(((inb(fir_base + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), - fir_base + IRCC_SCE_CFGB); -#else - outb(((inb(fir_base + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR), - fir_base + IRCC_SCE_CFGB); -#endif - (void) inb(fir_base + IRCC_FIFO_THRESHOLD); - - /* Enable SCE interrupts */ - outb(0, fir_base + IRCC_MASTER); - register_bank(fir_base, 0); - outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, fir_base + IRCC_IER); - outb(IRCC_MASTER_INT_EN, fir_base + IRCC_MASTER); -} - -/* - * Function smsc_ircc_fir_stop(self, baud) - * - * Change the speed of the device - * - */ -static void smsc_ircc_fir_stop(struct smsc_ircc_cb *self) -{ - int fir_base; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(self != NULL, return;); - - fir_base = self->io.fir_base; - register_bank(fir_base, 0); - /*outb(IRCC_MASTER_RESET, fir_base + IRCC_MASTER);*/ - outb(inb(fir_base + IRCC_LCR_B) & IRCC_LCR_B_SIP_ENABLE, fir_base + IRCC_LCR_B); -} - - -/* - * Function smsc_ircc_change_speed(self, baud) - * - * Change the speed of the device - * - * This function *must* be called with spinlock held, because it may - * be called from the irq handler. - Jean II - */ -static void smsc_ircc_change_speed(struct smsc_ircc_cb *self, u32 speed) -{ - struct net_device *dev; - int last_speed_was_sir; - - pr_debug("%s() changing speed to: %d\n", __func__, speed); - - IRDA_ASSERT(self != NULL, return;); - dev = self->netdev; - - last_speed_was_sir = self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED; - - #if 0 - /* Temp Hack */ - speed= 1152000; - self->io.speed = speed; - last_speed_was_sir = 0; - smsc_ircc_fir_start(self); - #endif - - if (self->io.speed == 0) - smsc_ircc_sir_start(self); - - #if 0 - if (!last_speed_was_sir) speed = self->io.speed; - #endif - - if (self->io.speed != speed) - smsc_ircc_set_transceiver_for_speed(self, speed); - - self->io.speed = speed; - - if (speed <= SMSC_IRCC2_MAX_SIR_SPEED) { - if (!last_speed_was_sir) { - smsc_ircc_fir_stop(self); - smsc_ircc_sir_start(self); - } - smsc_ircc_set_sir_speed(self, speed); - } else { - if (last_speed_was_sir) { - #if SMSC_IRCC2_C_SIR_STOP - smsc_ircc_sir_stop(self); - #endif - smsc_ircc_fir_start(self); - } - smsc_ircc_set_fir_speed(self, speed); - - #if 0 - self->tx_buff.len = 10; - self->tx_buff.data = self->tx_buff.head; - - smsc_ircc_dma_xmit(self, 4000); - #endif - /* Be ready for incoming frames */ - smsc_ircc_dma_receive(self); - } - - netif_wake_queue(dev); -} - -/* - * Function smsc_ircc_set_sir_speed (self, speed) - * - * Set speed of IrDA port to specified baudrate - * - */ -static void smsc_ircc_set_sir_speed(struct smsc_ircc_cb *self, __u32 speed) -{ - int iobase; - int fcr; /* FIFO control reg */ - int lcr; /* Line control reg */ - int divisor; - - pr_debug("%s(), Setting speed to: %d\n", __func__, speed); - - IRDA_ASSERT(self != NULL, return;); - iobase = self->io.sir_base; - - /* Update accounting for new speed */ - self->io.speed = speed; - - /* Turn off interrupts */ - outb(0, iobase + UART_IER); - - divisor = SMSC_IRCC2_MAX_SIR_SPEED / speed; - - fcr = UART_FCR_ENABLE_FIFO; - - /* - * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and - * almost 1,7 ms at 19200 bps. At speeds above that we can just forget - * about this timeout since it will always be fast enough. - */ - fcr |= self->io.speed < 38400 ? - UART_FCR_TRIGGER_1 : UART_FCR_TRIGGER_14; - - /* IrDA ports use 8N1 */ - lcr = UART_LCR_WLEN8; - - outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */ - outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */ - outb(divisor >> 8, iobase + UART_DLM); - outb(lcr, iobase + UART_LCR); /* Set 8N1 */ - outb(fcr, iobase + UART_FCR); /* Enable FIFO's */ - - /* Turn on interrups */ - outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); - - pr_debug("%s() speed changed to: %d\n", __func__, speed); -} - - -/* - * Function smsc_ircc_hard_xmit_fir (skb, dev) - * - * Transmit the frame! - * - */ -static netdev_tx_t smsc_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev) -{ - struct smsc_ircc_cb *self; - unsigned long flags; - s32 speed; - int mtt; - - IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); - - netif_stop_queue(dev); - - /* Make sure test of self->io.speed & speed change are atomic */ - spin_lock_irqsave(&self->lock, flags); - - /* Check if we need to change the speed after this frame */ - speed = irda_get_next_speed(skb); - if (speed != self->io.speed && speed != -1) { - /* Check for empty frame */ - if (!skb->len) { - /* Note : you should make sure that speed changes - * are not going to corrupt any outgoing frame. - * Look at nsc-ircc for the gory details - Jean II */ - smsc_ircc_change_speed(self, speed); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - self->new_speed = speed; - } - - skb_copy_from_linear_data(skb, self->tx_buff.head, skb->len); - - self->tx_buff.len = skb->len; - self->tx_buff.data = self->tx_buff.head; - - mtt = irda_get_mtt(skb); - if (mtt) { - int bofs; - - /* - * Compute how many BOFs (STA or PA's) we need to waste the - * min turn time given the speed of the link. - */ - bofs = mtt * (self->io.speed / 1000) / 8000; - if (bofs > 4095) - bofs = 4095; - - smsc_ircc_dma_xmit(self, bofs); - } else { - /* Transmit frame */ - smsc_ircc_dma_xmit(self, 0); - } - - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* - * Function smsc_ircc_dma_xmit (self, bofs) - * - * Transmit data using DMA - * - */ -static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int bofs) -{ - int iobase = self->io.fir_base; - u8 ctrl; - - pr_debug("%s\n", __func__); -#if 1 - /* Disable Rx */ - register_bank(iobase, 0); - outb(0x00, iobase + IRCC_LCR_B); -#endif - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, - iobase + IRCC_SCE_CFGB); - - self->io.direction = IO_XMIT; - - /* Set BOF additional count for generating the min turn time */ - register_bank(iobase, 4); - outb(bofs & 0xff, iobase + IRCC_BOF_COUNT_LO); - ctrl = inb(iobase + IRCC_CONTROL) & 0xf0; - outb(ctrl | ((bofs >> 8) & 0x0f), iobase + IRCC_BOF_COUNT_HI); - - /* Set max Tx frame size */ - outb(self->tx_buff.len >> 8, iobase + IRCC_TX_SIZE_HI); - outb(self->tx_buff.len & 0xff, iobase + IRCC_TX_SIZE_LO); - - /*outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR);*/ - - /* Enable burst mode chip Tx DMA */ - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | - IRCC_CFGB_DMA_BURST, iobase + IRCC_SCE_CFGB); - - /* Setup DMA controller (must be done after enabling chip DMA) */ - irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, - DMA_TX_MODE); - - /* Enable interrupt */ - - register_bank(iobase, 0); - outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER); - outb(IRCC_MASTER_INT_EN, iobase + IRCC_MASTER); - - /* Enable transmit */ - outb(IRCC_LCR_B_SCE_TRANSMIT | IRCC_LCR_B_SIP_ENABLE, iobase + IRCC_LCR_B); -} - -/* - * Function smsc_ircc_dma_xmit_complete (self) - * - * The transfer of a frame in finished. This function will only be called - * by the interrupt handler - * - */ -static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self) -{ - int iobase = self->io.fir_base; - - pr_debug("%s\n", __func__); -#if 0 - /* Disable Tx */ - register_bank(iobase, 0); - outb(0x00, iobase + IRCC_LCR_B); -#endif - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, - iobase + IRCC_SCE_CFGB); - - /* Check for underrun! */ - register_bank(iobase, 0); - if (inb(iobase + IRCC_LSR) & IRCC_LSR_UNDERRUN) { - self->netdev->stats.tx_errors++; - self->netdev->stats.tx_fifo_errors++; - - /* Reset error condition */ - register_bank(iobase, 0); - outb(IRCC_MASTER_ERROR_RESET, iobase + IRCC_MASTER); - outb(0x00, iobase + IRCC_MASTER); - } else { - self->netdev->stats.tx_packets++; - self->netdev->stats.tx_bytes += self->tx_buff.len; - } - - /* Check if it's time to change the speed */ - if (self->new_speed) { - smsc_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - } - - netif_wake_queue(self->netdev); -} - -/* - * Function smsc_ircc_dma_receive(self) - * - * Get ready for receiving a frame. The device will initiate a DMA - * if it starts to receive a frame. - * - */ -static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self) -{ - int iobase = self->io.fir_base; -#if 0 - /* Turn off chip DMA */ - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, - iobase + IRCC_SCE_CFGB); -#endif - - /* Disable Tx */ - register_bank(iobase, 0); - outb(0x00, iobase + IRCC_LCR_B); - - /* Turn off chip DMA */ - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, - iobase + IRCC_SCE_CFGB); - - self->io.direction = IO_RECV; - self->rx_buff.data = self->rx_buff.head; - - /* Set max Rx frame size */ - register_bank(iobase, 4); - outb((2050 >> 8) & 0x0f, iobase + IRCC_RX_SIZE_HI); - outb(2050 & 0xff, iobase + IRCC_RX_SIZE_LO); - - /* Setup DMA controller */ - irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, - DMA_RX_MODE); - - /* Enable burst mode chip Rx DMA */ - register_bank(iobase, 1); - outb(inb(iobase + IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | - IRCC_CFGB_DMA_BURST, iobase + IRCC_SCE_CFGB); - - /* Enable interrupt */ - register_bank(iobase, 0); - outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER); - outb(IRCC_MASTER_INT_EN, iobase + IRCC_MASTER); - - /* Enable receiver */ - register_bank(iobase, 0); - outb(IRCC_LCR_B_SCE_RECEIVE | IRCC_LCR_B_SIP_ENABLE, - iobase + IRCC_LCR_B); - - return 0; -} - -/* - * Function smsc_ircc_dma_receive_complete(self) - * - * Finished with receiving frames - * - */ -static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self) -{ - struct sk_buff *skb; - int len, msgcnt, lsr; - int iobase = self->io.fir_base; - - register_bank(iobase, 0); - - pr_debug("%s\n", __func__); -#if 0 - /* Disable Rx */ - register_bank(iobase, 0); - outb(0x00, iobase + IRCC_LCR_B); -#endif - register_bank(iobase, 0); - outb(inb(iobase + IRCC_LSAR) & ~IRCC_LSAR_ADDRESS_MASK, iobase + IRCC_LSAR); - lsr= inb(iobase + IRCC_LSR); - msgcnt = inb(iobase + IRCC_LCR_B) & 0x08; - - pr_debug("%s: dma count = %d\n", __func__, - get_dma_residue(self->io.dma)); - - len = self->rx_buff.truesize - get_dma_residue(self->io.dma); - - /* Look for errors */ - if (lsr & (IRCC_LSR_FRAME_ERROR | IRCC_LSR_CRC_ERROR | IRCC_LSR_SIZE_ERROR)) { - self->netdev->stats.rx_errors++; - if (lsr & IRCC_LSR_FRAME_ERROR) - self->netdev->stats.rx_frame_errors++; - if (lsr & IRCC_LSR_CRC_ERROR) - self->netdev->stats.rx_crc_errors++; - if (lsr & IRCC_LSR_SIZE_ERROR) - self->netdev->stats.rx_length_errors++; - if (lsr & (IRCC_LSR_UNDERRUN | IRCC_LSR_OVERRUN)) - self->netdev->stats.rx_length_errors++; - return; - } - - /* Remove CRC */ - len -= self->io.speed < 4000000 ? 2 : 4; - - if (len < 2 || len > 2050) { - net_warn_ratelimited("%s(), bogus len=%d\n", __func__, len); - return; - } - pr_debug("%s: msgcnt = %d, len=%d\n", __func__, msgcnt, len); - - skb = dev_alloc_skb(len + 1); - if (!skb) - return; - - /* Make sure IP header gets aligned */ - skb_reserve(skb, 1); - - skb_put_data(skb, self->rx_buff.data, len); - self->netdev->stats.rx_packets++; - self->netdev->stats.rx_bytes += len; - - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); -} - -/* - * Function smsc_ircc_sir_receive (self) - * - * Receive one frame from the infrared port - * - */ -static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self) -{ - int boguscount = 0; - int iobase; - - IRDA_ASSERT(self != NULL, return;); - - iobase = self->io.sir_base; - - /* - * Receive all characters in Rx FIFO, unwrap and unstuff them. - * async_unwrap_char will deliver all found frames - */ - do { - async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, - inb(iobase + UART_RX)); - - /* Make sure we don't stay here to long */ - if (boguscount++ > 32) { - pr_debug("%s(), breaking!\n", __func__); - break; - } - } while (inb(iobase + UART_LSR) & UART_LSR_DR); -} - - -/* - * Function smsc_ircc_interrupt (irq, dev_id, regs) - * - * An interrupt from the chip has arrived. Time to do some work - * - */ -static irqreturn_t smsc_ircc_interrupt(int dummy, void *dev_id) -{ - struct net_device *dev = dev_id; - struct smsc_ircc_cb *self = netdev_priv(dev); - int iobase, iir, lcra, lsr; - irqreturn_t ret = IRQ_NONE; - - /* Serialise the interrupt handler in various CPUs, stop Tx path */ - spin_lock(&self->lock); - - /* Check if we should use the SIR interrupt handler */ - if (self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED) { - ret = smsc_ircc_interrupt_sir(dev); - goto irq_ret_unlock; - } - - iobase = self->io.fir_base; - - register_bank(iobase, 0); - iir = inb(iobase + IRCC_IIR); - if (iir == 0) - goto irq_ret_unlock; - ret = IRQ_HANDLED; - - /* Disable interrupts */ - outb(0, iobase + IRCC_IER); - lcra = inb(iobase + IRCC_LCR_A); - lsr = inb(iobase + IRCC_LSR); - - pr_debug("%s(), iir = 0x%02x\n", __func__, iir); - - if (iir & IRCC_IIR_EOM) { - if (self->io.direction == IO_RECV) - smsc_ircc_dma_receive_complete(self); - else - smsc_ircc_dma_xmit_complete(self); - - smsc_ircc_dma_receive(self); - } - - if (iir & IRCC_IIR_ACTIVE_FRAME) { - /*printk(KERN_WARNING "%s(): Active Frame\n", __func__);*/ - } - - /* Enable interrupts again */ - - register_bank(iobase, 0); - outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER); - - irq_ret_unlock: - spin_unlock(&self->lock); - - return ret; -} - -/* - * Function irport_interrupt_sir (irq, dev_id) - * - * Interrupt handler for SIR modes - */ -static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev) -{ - struct smsc_ircc_cb *self = netdev_priv(dev); - int boguscount = 0; - int iobase; - int iir, lsr; - - /* Already locked coming here in smsc_ircc_interrupt() */ - /*spin_lock(&self->lock);*/ - - iobase = self->io.sir_base; - - iir = inb(iobase + UART_IIR) & UART_IIR_ID; - if (iir == 0) - return IRQ_NONE; - while (iir) { - /* Clear interrupt */ - lsr = inb(iobase + UART_LSR); - - pr_debug("%s(), iir=%02x, lsr=%02x, iobase=%#x\n", - __func__, iir, lsr, iobase); - - switch (iir) { - case UART_IIR_RLSI: - pr_debug("%s(), RLSI\n", __func__); - break; - case UART_IIR_RDI: - /* Receive interrupt */ - smsc_ircc_sir_receive(self); - break; - case UART_IIR_THRI: - if (lsr & UART_LSR_THRE) - /* Transmitter ready for data */ - smsc_ircc_sir_write_wakeup(self); - break; - default: - pr_debug("%s(), unhandled IIR=%#x\n", - __func__, iir); - break; - } - - /* Make sure we don't stay here to long */ - if (boguscount++ > 100) - break; - - iir = inb(iobase + UART_IIR) & UART_IIR_ID; - } - /*spin_unlock(&self->lock);*/ - return IRQ_HANDLED; -} - - -#if 0 /* unused */ -/* - * Function ircc_is_receiving (self) - * - * Return TRUE is we are currently receiving a frame - * - */ -static int ircc_is_receiving(struct smsc_ircc_cb *self) -{ - int status = FALSE; - /* int iobase; */ - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(self != NULL, return FALSE;); - - pr_debug("%s: dma count = %d\n", __func__, - get_dma_residue(self->io.dma)); - - status = (self->rx_buff.state != OUTSIDE_FRAME); - - return status; -} -#endif /* unused */ - -static int smsc_ircc_request_irq(struct smsc_ircc_cb *self) -{ - int error; - - error = request_irq(self->io.irq, smsc_ircc_interrupt, 0, - self->netdev->name, self->netdev); - if (error) - pr_debug("%s(), unable to allocate irq=%d, err=%d\n", - __func__, self->io.irq, error); - - return error; -} - -static void smsc_ircc_start_interrupts(struct smsc_ircc_cb *self) -{ - unsigned long flags; - - spin_lock_irqsave(&self->lock, flags); - - self->io.speed = 0; - smsc_ircc_change_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED); - - spin_unlock_irqrestore(&self->lock, flags); -} - -static void smsc_ircc_stop_interrupts(struct smsc_ircc_cb *self) -{ - int iobase = self->io.fir_base; - unsigned long flags; - - spin_lock_irqsave(&self->lock, flags); - - register_bank(iobase, 0); - outb(0, iobase + IRCC_IER); - outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER); - outb(0x00, iobase + IRCC_MASTER); - - spin_unlock_irqrestore(&self->lock, flags); -} - - -/* - * Function smsc_ircc_net_open (dev) - * - * Start the device - * - */ -static int smsc_ircc_net_open(struct net_device *dev) -{ - struct smsc_ircc_cb *self; - char hwname[16]; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); - - if (self->io.suspended) { - pr_debug("%s(), device is suspended\n", __func__); - return -EAGAIN; - } - - if (request_irq(self->io.irq, smsc_ircc_interrupt, 0, dev->name, - (void *) dev)) { - pr_debug("%s(), unable to allocate irq=%d\n", - __func__, self->io.irq); - return -EAGAIN; - } - - smsc_ircc_start_interrupts(self); - - /* Give self a hardware name */ - /* It would be cool to offer the chip revision here - Jean II */ - sprintf(hwname, "SMSC @ 0x%03x", self->io.fir_base); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - self->irlap = irlap_open(dev, &self->qos, hwname); - - /* - * Always allocate the DMA channel after the IRQ, - * and clean up on failure. - */ - if (request_dma(self->io.dma, dev->name)) { - smsc_ircc_net_close(dev); - - net_warn_ratelimited("%s(), unable to allocate DMA=%d\n", - __func__, self->io.dma); - return -EAGAIN; - } - - netif_start_queue(dev); - - return 0; -} - -/* - * Function smsc_ircc_net_close (dev) - * - * Stop the device - * - */ -static int smsc_ircc_net_close(struct net_device *dev) -{ - struct smsc_ircc_cb *self; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); - - /* Stop device */ - netif_stop_queue(dev); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - - smsc_ircc_stop_interrupts(self); - - /* if we are called from smsc_ircc_resume we don't have IRQ reserved */ - if (!self->io.suspended) - free_irq(self->io.irq, dev); - - disable_dma(self->io.dma); - free_dma(self->io.dma); - - return 0; -} - -static int smsc_ircc_suspend(struct platform_device *dev, pm_message_t state) -{ - struct smsc_ircc_cb *self = platform_get_drvdata(dev); - - if (!self->io.suspended) { - pr_debug("%s, Suspending\n", driver_name); - - rtnl_lock(); - if (netif_running(self->netdev)) { - netif_device_detach(self->netdev); - smsc_ircc_stop_interrupts(self); - free_irq(self->io.irq, self->netdev); - disable_dma(self->io.dma); - } - self->io.suspended = 1; - rtnl_unlock(); - } - - return 0; -} - -static int smsc_ircc_resume(struct platform_device *dev) -{ - struct smsc_ircc_cb *self = platform_get_drvdata(dev); - - if (self->io.suspended) { - pr_debug("%s, Waking up\n", driver_name); - - rtnl_lock(); - smsc_ircc_init_chip(self); - if (netif_running(self->netdev)) { - if (smsc_ircc_request_irq(self)) { - /* - * Don't fail resume process, just kill this - * network interface - */ - unregister_netdevice(self->netdev); - } else { - enable_dma(self->io.dma); - smsc_ircc_start_interrupts(self); - netif_device_attach(self->netdev); - } - } - self->io.suspended = 0; - rtnl_unlock(); - } - return 0; -} - -/* - * Function smsc_ircc_close (self) - * - * Close driver instance - * - */ -static int __exit smsc_ircc_close(struct smsc_ircc_cb *self) -{ - pr_debug("%s\n", __func__); - - IRDA_ASSERT(self != NULL, return -1;); - - platform_device_unregister(self->pldev); - - /* Remove netdevice */ - unregister_netdev(self->netdev); - - smsc_ircc_stop_interrupts(self); - - /* Release the PORTS that this driver is using */ - pr_debug("%s(), releasing 0x%03x\n", __func__, - self->io.fir_base); - - release_region(self->io.fir_base, self->io.fir_ext); - - pr_debug("%s(), releasing 0x%03x\n", __func__, - self->io.sir_base); - - release_region(self->io.sir_base, self->io.sir_ext); - - if (self->tx_buff.head) - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - - if (self->rx_buff.head) - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - - free_netdev(self->netdev); - - return 0; -} - -static void __exit smsc_ircc_cleanup(void) -{ - int i; - - pr_debug("%s\n", __func__); - - for (i = 0; i < 2; i++) { - if (dev_self[i]) - smsc_ircc_close(dev_self[i]); - } - - if (pnp_driver_registered) - pnp_unregister_driver(&smsc_ircc_pnp_driver); - - platform_driver_unregister(&smsc_ircc_driver); -} - -/* - * Start SIR operations - * - * This function *must* be called with spinlock held, because it may - * be called from the irq handler (via smsc_ircc_change_speed()). - Jean II - */ -static void smsc_ircc_sir_start(struct smsc_ircc_cb *self) -{ - struct net_device *dev; - int fir_base, sir_base; - - pr_debug("%s\n", __func__); - - IRDA_ASSERT(self != NULL, return;); - dev = self->netdev; - IRDA_ASSERT(dev != NULL, return;); - - fir_base = self->io.fir_base; - sir_base = self->io.sir_base; - - /* Reset everything */ - outb(IRCC_MASTER_RESET, fir_base + IRCC_MASTER); - - #if SMSC_IRCC2_C_SIR_STOP - /*smsc_ircc_sir_stop(self);*/ - #endif - - register_bank(fir_base, 1); - outb(((inb(fir_base + IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | IRCC_CFGA_IRDA_SIR_A), fir_base + IRCC_SCE_CFGA); - - /* Initialize UART */ - outb(UART_LCR_WLEN8, sir_base + UART_LCR); /* Reset DLAB */ - outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), sir_base + UART_MCR); - - /* Turn on interrups */ - outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, sir_base + UART_IER); - - pr_debug("%s() - exit\n", __func__); - - outb(0x00, fir_base + IRCC_MASTER); -} - -#if SMSC_IRCC2_C_SIR_STOP -void smsc_ircc_sir_stop(struct smsc_ircc_cb *self) -{ - int iobase; - - pr_debug("%s\n", __func__); - iobase = self->io.sir_base; - - /* Reset UART */ - outb(0, iobase + UART_MCR); - - /* Turn off interrupts */ - outb(0, iobase + UART_IER); -} -#endif - -/* - * Function smsc_sir_write_wakeup (self) - * - * Called by the SIR interrupt handler when there's room for more data. - * If we have more packets to send, we send them here. - * - */ -static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self) -{ - int actual = 0; - int iobase; - int fcr; - - IRDA_ASSERT(self != NULL, return;); - - pr_debug("%s\n", __func__); - - iobase = self->io.sir_base; - - /* Finished with frame? */ - if (self->tx_buff.len > 0) { - /* Write data left in transmit buffer */ - actual = smsc_ircc_sir_write(iobase, self->io.fifo_size, - self->tx_buff.data, self->tx_buff.len); - self->tx_buff.data += actual; - self->tx_buff.len -= actual; - } else { - - /*if (self->tx_buff.len ==0) {*/ - - /* - * Now serial buffer is almost free & we can start - * transmission of another packet. But first we must check - * if we need to change the speed of the hardware - */ - if (self->new_speed) { - pr_debug("%s(), Changing speed to %d.\n", - __func__, self->new_speed); - smsc_ircc_sir_wait_hw_transmitter_finish(self); - smsc_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - } else { - /* Tell network layer that we want more frames */ - netif_wake_queue(self->netdev); - } - self->netdev->stats.tx_packets++; - - if (self->io.speed <= 115200) { - /* - * Reset Rx FIFO to make sure that all reflected transmit data - * is discarded. This is needed for half duplex operation - */ - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR; - fcr |= self->io.speed < 38400 ? - UART_FCR_TRIGGER_1 : UART_FCR_TRIGGER_14; - - outb(fcr, iobase + UART_FCR); - - /* Turn on receive interrupts */ - outb(UART_IER_RDI, iobase + UART_IER); - } - } -} - -/* - * Function smsc_ircc_sir_write (iobase, fifo_size, buf, len) - * - * Fill Tx FIFO with transmit data - * - */ -static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len) -{ - int actual = 0; - - /* Tx FIFO should be empty! */ - if (!(inb(iobase + UART_LSR) & UART_LSR_THRE)) { - net_warn_ratelimited("%s(), failed, fifo not empty!\n", - __func__); - return 0; - } - - /* Fill FIFO with current frame */ - while (fifo_size-- > 0 && actual < len) { - /* Transmit next byte */ - outb(buf[actual], iobase + UART_TX); - actual++; - } - return actual; -} - -/* - * Function smsc_ircc_is_receiving (self) - * - * Returns true is we are currently receiving data - * - */ -static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self) -{ - return self->rx_buff.state != OUTSIDE_FRAME; -} - - -/* - * Function smsc_ircc_probe_transceiver(self) - * - * Tries to find the used Transceiver - * - */ -static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self) -{ - unsigned int i; - - IRDA_ASSERT(self != NULL, return;); - - for (i = 0; smsc_transceivers[i].name != NULL; i++) - if (smsc_transceivers[i].probe(self->io.fir_base)) { - net_info_ratelimited(" %s transceiver found\n", - smsc_transceivers[i].name); - self->transceiver= i + 1; - return; - } - - net_info_ratelimited("No transceiver found. Defaulting to %s\n", - smsc_transceivers[SMSC_IRCC2_C_DEFAULT_TRANSCEIVER].name); - - self->transceiver = SMSC_IRCC2_C_DEFAULT_TRANSCEIVER; -} - - -/* - * Function smsc_ircc_set_transceiver_for_speed(self, speed) - * - * Set the transceiver according to the speed - * - */ -static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed) -{ - unsigned int trx; - - trx = self->transceiver; - if (trx > 0) - smsc_transceivers[trx - 1].set_for_speed(self->io.fir_base, speed); -} - -/* - * Function smsc_ircc_wait_hw_transmitter_finish () - * - * Wait for the real end of HW transmission - * - * The UART is a strict FIFO, and we get called only when we have finished - * pushing data to the FIFO, so the maximum amount of time we must wait - * is only for the FIFO to drain out. - * - * We use a simple calibrated loop. We may need to adjust the loop - * delay (udelay) to balance I/O traffic and latency. And we also need to - * adjust the maximum timeout. - * It would probably be better to wait for the proper interrupt, - * but it doesn't seem to be available. - * - * We can't use jiffies or kernel timers because : - * 1) We are called from the interrupt handler, which disable softirqs, - * so jiffies won't be increased - * 2) Jiffies granularity is usually very coarse (10ms), and we don't - * want to wait that long to detect stuck hardware. - * Jean II - */ - -static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self) -{ - int iobase = self->io.sir_base; - int count = SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US; - - /* Calibrated busy loop */ - while (count-- > 0 && !(inb(iobase + UART_LSR) & UART_LSR_TEMT)) - udelay(1); - - if (count < 0) - pr_debug("%s(): stuck transmitter\n", __func__); -} - - -/* PROBING - * - * REVISIT we can be told about the device by PNP, and should use that info - * instead of probing hardware and creating a platform_device ... - */ - -static int __init smsc_ircc_look_for_chips(void) -{ - struct smsc_chip_address *address; - char *type; - unsigned int cfg_base, found; - - found = 0; - address = possible_addresses; - - while (address->cfg_base) { - cfg_base = address->cfg_base; - - /*printk(KERN_WARNING "%s(): probing: 0x%02x for: 0x%02x\n", __func__, cfg_base, address->type);*/ - - if (address->type & SMSCSIO_TYPE_FDC) { - type = "FDC"; - if (address->type & SMSCSIO_TYPE_FLAT) - if (!smsc_superio_flat(fdc_chips_flat, cfg_base, type)) - found++; - - if (address->type & SMSCSIO_TYPE_PAGED) - if (!smsc_superio_paged(fdc_chips_paged, cfg_base, type)) - found++; - } - if (address->type & SMSCSIO_TYPE_LPC) { - type = "LPC"; - if (address->type & SMSCSIO_TYPE_FLAT) - if (!smsc_superio_flat(lpc_chips_flat, cfg_base, type)) - found++; - - if (address->type & SMSCSIO_TYPE_PAGED) - if (!smsc_superio_paged(lpc_chips_paged, cfg_base, type)) - found++; - } - address++; - } - return found; -} - -/* - * Function smsc_superio_flat (chip, base, type) - * - * Try to get configuration of a smc SuperIO chip with flat register model - * - */ -static int __init smsc_superio_flat(const struct smsc_chip *chips, unsigned short cfgbase, char *type) -{ - unsigned short firbase, sirbase; - u8 mode, dma, irq; - int ret = -ENODEV; - - pr_debug("%s\n", __func__); - - if (smsc_ircc_probe(cfgbase, SMSCSIOFLAT_DEVICEID_REG, chips, type) == NULL) - return ret; - - outb(SMSCSIOFLAT_UARTMODE0C_REG, cfgbase); - mode = inb(cfgbase + 1); - - /*printk(KERN_WARNING "%s(): mode: 0x%02x\n", __func__, mode);*/ - - if (!(mode & SMSCSIOFLAT_UART2MODE_VAL_IRDA)) - net_warn_ratelimited("%s(): IrDA not enabled\n", __func__); - - outb(SMSCSIOFLAT_UART2BASEADDR_REG, cfgbase); - sirbase = inb(cfgbase + 1) << 2; - - /* FIR iobase */ - outb(SMSCSIOFLAT_FIRBASEADDR_REG, cfgbase); - firbase = inb(cfgbase + 1) << 3; - - /* DMA */ - outb(SMSCSIOFLAT_FIRDMASELECT_REG, cfgbase); - dma = inb(cfgbase + 1) & SMSCSIOFLAT_FIRDMASELECT_MASK; - - /* IRQ */ - outb(SMSCSIOFLAT_UARTIRQSELECT_REG, cfgbase); - irq = inb(cfgbase + 1) & SMSCSIOFLAT_UART2IRQSELECT_MASK; - - net_info_ratelimited("%s(): fir: 0x%02x, sir: 0x%02x, dma: %02d, irq: %d, mode: 0x%02x\n", - __func__, firbase, sirbase, dma, irq, mode); - - if (firbase && smsc_ircc_open(firbase, sirbase, dma, irq) == 0) - ret = 0; - - /* Exit configuration */ - outb(SMSCSIO_CFGEXITKEY, cfgbase); - - return ret; -} - -/* - * Function smsc_superio_paged (chip, base, type) - * - * Try to get configuration of a smc SuperIO chip with paged register model - * - */ -static int __init smsc_superio_paged(const struct smsc_chip *chips, unsigned short cfg_base, char *type) -{ - unsigned short fir_io, sir_io; - int ret = -ENODEV; - - pr_debug("%s\n", __func__); - - if (smsc_ircc_probe(cfg_base, 0x20, chips, type) == NULL) - return ret; - - /* Select logical device (UART2) */ - outb(0x07, cfg_base); - outb(0x05, cfg_base + 1); - - /* SIR iobase */ - outb(0x60, cfg_base); - sir_io = inb(cfg_base + 1) << 8; - outb(0x61, cfg_base); - sir_io |= inb(cfg_base + 1); - - /* Read FIR base */ - outb(0x62, cfg_base); - fir_io = inb(cfg_base + 1) << 8; - outb(0x63, cfg_base); - fir_io |= inb(cfg_base + 1); - outb(0x2b, cfg_base); /* ??? */ - - if (fir_io && smsc_ircc_open(fir_io, sir_io, ircc_dma, ircc_irq) == 0) - ret = 0; - - /* Exit configuration */ - outb(SMSCSIO_CFGEXITKEY, cfg_base); - - return ret; -} - - -static int __init smsc_access(unsigned short cfg_base, unsigned char reg) -{ - pr_debug("%s\n", __func__); - - outb(reg, cfg_base); - return inb(cfg_base) != reg ? -1 : 0; -} - -static const struct smsc_chip * __init smsc_ircc_probe(unsigned short cfg_base, u8 reg, const struct smsc_chip *chip, char *type) -{ - u8 devid, xdevid, rev; - - pr_debug("%s\n", __func__); - - /* Leave configuration */ - - outb(SMSCSIO_CFGEXITKEY, cfg_base); - - if (inb(cfg_base) == SMSCSIO_CFGEXITKEY) /* not a smc superio chip */ - return NULL; - - outb(reg, cfg_base); - - xdevid = inb(cfg_base + 1); - - /* Enter configuration */ - - outb(SMSCSIO_CFGACCESSKEY, cfg_base); - - #if 0 - if (smsc_access(cfg_base,0x55)) /* send second key and check */ - return NULL; - #endif - - /* probe device ID */ - - if (smsc_access(cfg_base, reg)) - return NULL; - - devid = inb(cfg_base + 1); - - if (devid == 0 || devid == 0xff) /* typical values for unused port */ - return NULL; - - /* probe revision ID */ - - if (smsc_access(cfg_base, reg + 1)) - return NULL; - - rev = inb(cfg_base + 1); - - if (rev >= 128) /* i think this will make no sense */ - return NULL; - - if (devid == xdevid) /* protection against false positives */ - return NULL; - - /* Check for expected device ID; are there others? */ - - while (chip->devid != devid) { - - chip++; - - if (chip->name == NULL) - return NULL; - } - - net_info_ratelimited("found SMC SuperIO Chip (devid=0x%02x rev=%02X base=0x%04x): %s%s\n", - devid, rev, cfg_base, type, chip->name); - - if (chip->rev > rev) { - net_info_ratelimited("Revision higher than expected\n"); - return NULL; - } - - if (chip->flags & NoIRDA) - net_info_ratelimited("chipset does not support IRDA\n"); - - return chip; -} - -static int __init smsc_superio_fdc(unsigned short cfg_base) -{ - int ret = -1; - - if (!request_region(cfg_base, 2, driver_name)) { - net_warn_ratelimited("%s: can't get cfg_base of 0x%03x\n", - __func__, cfg_base); - } else { - if (!smsc_superio_flat(fdc_chips_flat, cfg_base, "FDC") || - !smsc_superio_paged(fdc_chips_paged, cfg_base, "FDC")) - ret = 0; - - release_region(cfg_base, 2); - } - - return ret; -} - -static int __init smsc_superio_lpc(unsigned short cfg_base) -{ - int ret = -1; - - if (!request_region(cfg_base, 2, driver_name)) { - net_warn_ratelimited("%s: can't get cfg_base of 0x%03x\n", - __func__, cfg_base); - } else { - if (!smsc_superio_flat(lpc_chips_flat, cfg_base, "LPC") || - !smsc_superio_paged(lpc_chips_paged, cfg_base, "LPC")) - ret = 0; - - release_region(cfg_base, 2); - } - return ret; -} - -/* - * Look for some specific subsystem setups that need - * pre-configuration not properly done by the BIOS (especially laptops) - * This code is based in part on smcinit.c, tosh1800-smcinit.c - * and tosh2450-smcinit.c. The table lists the device entries - * for ISA bridges with an LPC (Low Pin Count) controller which - * handles the communication with the SMSC device. After the LPC - * controller is initialized through PCI, the SMSC device is initialized - * through a dedicated port in the ISA port-mapped I/O area, this latter - * area is used to configure the SMSC device with default - * SIR and FIR I/O ports, DMA and IRQ. Different vendors have - * used different sets of parameters and different control port - * addresses making a subsystem device table necessary. - */ -#ifdef CONFIG_PCI -static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __initdata = { - /* - * Subsystems needing entries: - * 0x10b9:0x1533 0x103c:0x0850 HP nx9010 family - * 0x10b9:0x1533 0x0e11:0x005a Compaq nc4000 family - * 0x8086:0x24cc 0x0e11:0x002a HP nx9000 family - */ - { - /* Guessed entry */ - .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ - .device = 0x24cc, - .subvendor = 0x103c, - .subdevice = 0x08bc, - .sir_io = 0x02f8, - .fir_io = 0x0130, - .fir_irq = 0x05, - .fir_dma = 0x03, - .cfg_base = 0x004e, - .preconfigure = preconfigure_through_82801, - .name = "HP nx5000 family", - }, - { - .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ - .device = 0x24cc, - .subvendor = 0x103c, - .subdevice = 0x088c, - /* Quite certain these are the same for nc8000 as for nc6000 */ - .sir_io = 0x02f8, - .fir_io = 0x0130, - .fir_irq = 0x05, - .fir_dma = 0x03, - .cfg_base = 0x004e, - .preconfigure = preconfigure_through_82801, - .name = "HP nc8000 family", - }, - { - .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ - .device = 0x24cc, - .subvendor = 0x103c, - .subdevice = 0x0890, - .sir_io = 0x02f8, - .fir_io = 0x0130, - .fir_irq = 0x05, - .fir_dma = 0x03, - .cfg_base = 0x004e, - .preconfigure = preconfigure_through_82801, - .name = "HP nc6000 family", - }, - { - .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */ - .device = 0x24cc, - .subvendor = 0x0e11, - .subdevice = 0x0860, - /* I assume these are the same for x1000 as for the others */ - .sir_io = 0x02e8, - .fir_io = 0x02f8, - .fir_irq = 0x07, - .fir_dma = 0x03, - .cfg_base = 0x002e, - .preconfigure = preconfigure_through_82801, - .name = "Compaq x1000 family", - }, - { - /* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */ - .vendor = PCI_VENDOR_ID_INTEL, - .device = 0x24c0, - .subvendor = 0x1179, - .subdevice = 0xffff, /* 0xffff is "any" */ - .sir_io = 0x03f8, - .fir_io = 0x0130, - .fir_irq = 0x07, - .fir_dma = 0x01, - .cfg_base = 0x002e, - .preconfigure = preconfigure_through_82801, - .name = "Toshiba laptop with Intel 82801DB/DBL LPC bridge", - }, - { - .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801CAM ISA bridge */ - .device = 0x248c, - .subvendor = 0x1179, - .subdevice = 0xffff, /* 0xffff is "any" */ - .sir_io = 0x03f8, - .fir_io = 0x0130, - .fir_irq = 0x03, - .fir_dma = 0x03, - .cfg_base = 0x002e, - .preconfigure = preconfigure_through_82801, - .name = "Toshiba laptop with Intel 82801CAM ISA bridge", - }, - { - /* 82801DBM (ICH4-M) LPC Interface Bridge */ - .vendor = PCI_VENDOR_ID_INTEL, - .device = 0x24cc, - .subvendor = 0x1179, - .subdevice = 0xffff, /* 0xffff is "any" */ - .sir_io = 0x03f8, - .fir_io = 0x0130, - .fir_irq = 0x03, - .fir_dma = 0x03, - .cfg_base = 0x002e, - .preconfigure = preconfigure_through_82801, - .name = "Toshiba laptop with Intel 8281DBM LPC bridge", - }, - { - /* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */ - .vendor = PCI_VENDOR_ID_AL, - .device = 0x1533, - .subvendor = 0x1179, - .subdevice = 0xffff, /* 0xffff is "any" */ - .sir_io = 0x02e8, - .fir_io = 0x02f8, - .fir_irq = 0x07, - .fir_dma = 0x03, - .cfg_base = 0x002e, - .preconfigure = preconfigure_through_ali, - .name = "Toshiba laptop with ALi ISA bridge", - }, - { } // Terminator -}; - - -/* - * This sets up the basic SMSC parameters - * (FIR port, SIR port, FIR DMA, FIR IRQ) - * through the chip configuration port. - */ -static int __init preconfigure_smsc_chip(struct - smsc_ircc_subsystem_configuration - *conf) -{ - unsigned short iobase = conf->cfg_base; - unsigned char tmpbyte; - - outb(LPC47N227_CFGACCESSKEY, iobase); // enter configuration state - outb(SMSCSIOFLAT_DEVICEID_REG, iobase); // set for device ID - tmpbyte = inb(iobase +1); // Read device ID - pr_debug("Detected Chip id: 0x%02x, setting up registers...\n", - tmpbyte); - - /* Disable UART1 and set up SIR I/O port */ - outb(0x24, iobase); // select CR24 - UART1 base addr - outb(0x00, iobase + 1); // disable UART1 - outb(SMSCSIOFLAT_UART2BASEADDR_REG, iobase); // select CR25 - UART2 base addr - outb( (conf->sir_io >> 2), iobase + 1); // bits 2-9 of 0x3f8 - tmpbyte = inb(iobase + 1); - if (tmpbyte != (conf->sir_io >> 2) ) { - net_warn_ratelimited("ERROR: could not configure SIR ioport\n"); - net_warn_ratelimited("Try to supply ircc_cfg argument\n"); - return -ENXIO; - } - - /* Set up FIR IRQ channel for UART2 */ - outb(SMSCSIOFLAT_UARTIRQSELECT_REG, iobase); // select CR28 - UART1,2 IRQ select - tmpbyte = inb(iobase + 1); - tmpbyte &= SMSCSIOFLAT_UART1IRQSELECT_MASK; // Do not touch the UART1 portion - tmpbyte |= (conf->fir_irq & SMSCSIOFLAT_UART2IRQSELECT_MASK); - outb(tmpbyte, iobase + 1); - tmpbyte = inb(iobase + 1) & SMSCSIOFLAT_UART2IRQSELECT_MASK; - if (tmpbyte != conf->fir_irq) { - net_warn_ratelimited("ERROR: could not configure FIR IRQ channel\n"); - return -ENXIO; - } - - /* Set up FIR I/O port */ - outb(SMSCSIOFLAT_FIRBASEADDR_REG, iobase); // CR2B - SCE (FIR) base addr - outb((conf->fir_io >> 3), iobase + 1); - tmpbyte = inb(iobase + 1); - if (tmpbyte != (conf->fir_io >> 3) ) { - net_warn_ratelimited("ERROR: could not configure FIR I/O port\n"); - return -ENXIO; - } - - /* Set up FIR DMA channel */ - outb(SMSCSIOFLAT_FIRDMASELECT_REG, iobase); // CR2C - SCE (FIR) DMA select - outb((conf->fir_dma & LPC47N227_FIRDMASELECT_MASK), iobase + 1); // DMA - tmpbyte = inb(iobase + 1) & LPC47N227_FIRDMASELECT_MASK; - if (tmpbyte != (conf->fir_dma & LPC47N227_FIRDMASELECT_MASK)) { - net_warn_ratelimited("ERROR: could not configure FIR DMA channel\n"); - return -ENXIO; - } - - outb(SMSCSIOFLAT_UARTMODE0C_REG, iobase); // CR0C - UART mode - tmpbyte = inb(iobase + 1); - tmpbyte &= ~SMSCSIOFLAT_UART2MODE_MASK | - SMSCSIOFLAT_UART2MODE_VAL_IRDA; - outb(tmpbyte, iobase + 1); // enable IrDA (HPSIR) mode, high speed - - outb(LPC47N227_APMBOOTDRIVE_REG, iobase); // CR07 - Auto Pwr Mgt/boot drive sel - tmpbyte = inb(iobase + 1); - outb(tmpbyte | LPC47N227_UART2AUTOPWRDOWN_MASK, iobase + 1); // enable UART2 autopower down - - /* This one was not part of tosh1800 */ - outb(0x0a, iobase); // CR0a - ecp fifo / ir mux - tmpbyte = inb(iobase + 1); - outb(tmpbyte | 0x40, iobase + 1); // send active device to ir port - - outb(LPC47N227_UART12POWER_REG, iobase); // CR02 - UART 1,2 power - tmpbyte = inb(iobase + 1); - outb(tmpbyte | LPC47N227_UART2POWERDOWN_MASK, iobase + 1); // UART2 power up mode, UART1 power down - - outb(LPC47N227_FDCPOWERVALIDCONF_REG, iobase); // CR00 - FDC Power/valid config cycle - tmpbyte = inb(iobase + 1); - outb(tmpbyte | LPC47N227_VALID_MASK, iobase + 1); // valid config cycle done - - outb(LPC47N227_CFGEXITKEY, iobase); // Exit configuration - - return 0; -} - -/* 82801CAM generic registers */ -#define VID 0x00 -#define DID 0x02 -#define PIRQ_A_D_ROUT 0x60 -#define SIRQ_CNTL 0x64 -#define PIRQ_E_H_ROUT 0x68 -#define PCI_DMA_C 0x90 -/* LPC-specific registers */ -#define COM_DEC 0xe0 -#define GEN1_DEC 0xe4 -#define LPC_EN 0xe6 -#define GEN2_DEC 0xec -/* - * Sets up the I/O range using the 82801CAM ISA bridge, 82801DBM LPC bridge - * or Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge. - * They all work the same way! - */ -static int __init preconfigure_through_82801(struct pci_dev *dev, - struct - smsc_ircc_subsystem_configuration - *conf) -{ - unsigned short tmpword; - unsigned char tmpbyte; - - net_info_ratelimited("Setting up Intel 82801 controller and SMSC device\n"); - /* - * Select the range for the COMA COM port (SIR) - * Register COM_DEC: - * Bit 7: reserved - * Bit 6-4, COMB decode range - * Bit 3: reserved - * Bit 2-0, COMA decode range - * - * Decode ranges: - * 000 = 0x3f8-0x3ff (COM1) - * 001 = 0x2f8-0x2ff (COM2) - * 010 = 0x220-0x227 - * 011 = 0x228-0x22f - * 100 = 0x238-0x23f - * 101 = 0x2e8-0x2ef (COM4) - * 110 = 0x338-0x33f - * 111 = 0x3e8-0x3ef (COM3) - */ - pci_read_config_byte(dev, COM_DEC, &tmpbyte); - tmpbyte &= 0xf8; /* mask COMA bits */ - switch(conf->sir_io) { - case 0x3f8: - tmpbyte |= 0x00; - break; - case 0x2f8: - tmpbyte |= 0x01; - break; - case 0x220: - tmpbyte |= 0x02; - break; - case 0x228: - tmpbyte |= 0x03; - break; - case 0x238: - tmpbyte |= 0x04; - break; - case 0x2e8: - tmpbyte |= 0x05; - break; - case 0x338: - tmpbyte |= 0x06; - break; - case 0x3e8: - tmpbyte |= 0x07; - break; - default: - tmpbyte |= 0x01; /* COM2 default */ - } - pr_debug("COM_DEC (write): 0x%02x\n", tmpbyte); - pci_write_config_byte(dev, COM_DEC, tmpbyte); - - /* Enable Low Pin Count interface */ - pci_read_config_word(dev, LPC_EN, &tmpword); - /* These seem to be set up at all times, - * just make sure it is properly set. - */ - switch(conf->cfg_base) { - case 0x04e: - tmpword |= 0x2000; - break; - case 0x02e: - tmpword |= 0x1000; - break; - case 0x062: - tmpword |= 0x0800; - break; - case 0x060: - tmpword |= 0x0400; - break; - default: - net_warn_ratelimited("Uncommon I/O base address: 0x%04x\n", - conf->cfg_base); - break; - } - tmpword &= 0xfffd; /* disable LPC COMB */ - tmpword |= 0x0001; /* set bit 0 : enable LPC COMA addr range (GEN2) */ - pr_debug("LPC_EN (write): 0x%04x\n", tmpword); - pci_write_config_word(dev, LPC_EN, tmpword); - - /* - * Configure LPC DMA channel - * PCI_DMA_C bits: - * Bit 15-14: DMA channel 7 select - * Bit 13-12: DMA channel 6 select - * Bit 11-10: DMA channel 5 select - * Bit 9-8: Reserved - * Bit 7-6: DMA channel 3 select - * Bit 5-4: DMA channel 2 select - * Bit 3-2: DMA channel 1 select - * Bit 1-0: DMA channel 0 select - * 00 = Reserved value - * 01 = PC/PCI DMA - * 10 = Reserved value - * 11 = LPC I/F DMA - */ - pci_read_config_word(dev, PCI_DMA_C, &tmpword); - switch(conf->fir_dma) { - case 0x07: - tmpword |= 0xc000; - break; - case 0x06: - tmpword |= 0x3000; - break; - case 0x05: - tmpword |= 0x0c00; - break; - case 0x03: - tmpword |= 0x00c0; - break; - case 0x02: - tmpword |= 0x0030; - break; - case 0x01: - tmpword |= 0x000c; - break; - case 0x00: - tmpword |= 0x0003; - break; - default: - break; /* do not change settings */ - } - pr_debug("PCI_DMA_C (write): 0x%04x\n", tmpword); - pci_write_config_word(dev, PCI_DMA_C, tmpword); - - /* - * GEN2_DEC bits: - * Bit 15-4: Generic I/O range - * Bit 3-1: reserved (read as 0) - * Bit 0: enable GEN2 range on LPC I/F - */ - tmpword = conf->fir_io & 0xfff8; - tmpword |= 0x0001; - pr_debug("GEN2_DEC (write): 0x%04x\n", tmpword); - pci_write_config_word(dev, GEN2_DEC, tmpword); - - /* Pre-configure chip */ - return preconfigure_smsc_chip(conf); -} - -/* - * Pre-configure a certain port on the ALi 1533 bridge. - * This is based on reverse-engineering since ALi does not - * provide any data sheet for the 1533 chip. - */ -static void __init preconfigure_ali_port(struct pci_dev *dev, - unsigned short port) -{ - unsigned char reg; - /* These bits obviously control the different ports */ - unsigned char mask; - unsigned char tmpbyte; - - switch(port) { - case 0x0130: - case 0x0178: - reg = 0xb0; - mask = 0x80; - break; - case 0x03f8: - reg = 0xb4; - mask = 0x80; - break; - case 0x02f8: - reg = 0xb4; - mask = 0x30; - break; - case 0x02e8: - reg = 0xb4; - mask = 0x08; - break; - default: - net_err_ratelimited("Failed to configure unsupported port on ALi 1533 bridge: 0x%04x\n", - port); - return; - } - - pci_read_config_byte(dev, reg, &tmpbyte); - /* Turn on the right bits */ - tmpbyte |= mask; - pci_write_config_byte(dev, reg, tmpbyte); - net_info_ratelimited("Activated ALi 1533 ISA bridge port 0x%04x\n", - port); -} - -static int __init preconfigure_through_ali(struct pci_dev *dev, - struct - smsc_ircc_subsystem_configuration - *conf) -{ - /* Configure the two ports on the ALi 1533 */ - preconfigure_ali_port(dev, conf->sir_io); - preconfigure_ali_port(dev, conf->fir_io); - - /* Pre-configure chip */ - return preconfigure_smsc_chip(conf); -} - -static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg, - unsigned short ircc_fir, - unsigned short ircc_sir, - unsigned char ircc_dma, - unsigned char ircc_irq) -{ - struct pci_dev *dev = NULL; - unsigned short ss_vendor = 0x0000; - unsigned short ss_device = 0x0000; - int ret = 0; - - for_each_pci_dev(dev) { - struct smsc_ircc_subsystem_configuration *conf; - - /* - * Cache the subsystem vendor/device: - * some manufacturers fail to set this for all components, - * so we save it in case there is just 0x0000 0x0000 on the - * device we want to check. - */ - if (dev->subsystem_vendor != 0x0000U) { - ss_vendor = dev->subsystem_vendor; - ss_device = dev->subsystem_device; - } - conf = subsystem_configurations; - for( ; conf->subvendor; conf++) { - if(conf->vendor == dev->vendor && - conf->device == dev->device && - conf->subvendor == ss_vendor && - /* Sometimes these are cached values */ - (conf->subdevice == ss_device || - conf->subdevice == 0xffff)) { - struct smsc_ircc_subsystem_configuration - tmpconf; - - memcpy(&tmpconf, conf, - sizeof(struct smsc_ircc_subsystem_configuration)); - - /* - * Override the default values with anything - * passed in as parameter - */ - if (ircc_cfg != 0) - tmpconf.cfg_base = ircc_cfg; - if (ircc_fir != 0) - tmpconf.fir_io = ircc_fir; - if (ircc_sir != 0) - tmpconf.sir_io = ircc_sir; - if (ircc_dma != DMA_INVAL) - tmpconf.fir_dma = ircc_dma; - if (ircc_irq != IRQ_INVAL) - tmpconf.fir_irq = ircc_irq; - - net_info_ratelimited("Detected unconfigured %s SMSC IrDA chip, pre-configuring device\n", - conf->name); - if (conf->preconfigure) - ret = conf->preconfigure(dev, &tmpconf); - else - ret = -ENODEV; - } - } - } - - return ret; -} -#endif // CONFIG_PCI - -/************************************************ - * - * Transceivers specific functions - * - ************************************************/ - - -/* - * Function smsc_ircc_set_transceiver_smsc_ircc_atc(fir_base, speed) - * - * Program transceiver through smsc-ircc ATC circuitry - * - */ - -static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed) -{ - unsigned long jiffies_now, jiffies_timeout; - u8 val; - - jiffies_now = jiffies; - jiffies_timeout = jiffies + SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES; - - /* ATC */ - register_bank(fir_base, 4); - outb((inb(fir_base + IRCC_ATC) & IRCC_ATC_MASK) | IRCC_ATC_nPROGREADY|IRCC_ATC_ENABLE, - fir_base + IRCC_ATC); - - while ((val = (inb(fir_base + IRCC_ATC) & IRCC_ATC_nPROGREADY)) && - !time_after(jiffies, jiffies_timeout)) - /* empty */; - - if (val) - net_warn_ratelimited("%s(): ATC: 0x%02x\n", - __func__, inb(fir_base + IRCC_ATC)); -} - -/* - * Function smsc_ircc_probe_transceiver_smsc_ircc_atc(fir_base) - * - * Probe transceiver smsc-ircc ATC circuitry - * - */ - -static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base) -{ - return 0; -} - -/* - * Function smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(self, speed) - * - * Set transceiver - * - */ - -static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed) -{ - u8 fast_mode; - - switch (speed) { - default: - case 576000 : - fast_mode = 0; - break; - case 1152000 : - case 4000000 : - fast_mode = IRCC_LCR_A_FAST; - break; - } - register_bank(fir_base, 0); - outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast_mode, fir_base + IRCC_LCR_A); -} - -/* - * Function smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(fir_base) - * - * Probe transceiver - * - */ - -static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base) -{ - return 0; -} - -/* - * Function smsc_ircc_set_transceiver_toshiba_sat1800(fir_base, speed) - * - * Set transceiver - * - */ - -static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed) -{ - u8 fast_mode; - - switch (speed) { - default: - case 576000 : - fast_mode = 0; - break; - case 1152000 : - case 4000000 : - fast_mode = /*IRCC_LCR_A_FAST |*/ IRCC_LCR_A_GP_DATA; - break; - - } - /* This causes an interrupt */ - register_bank(fir_base, 0); - outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast_mode, fir_base + IRCC_LCR_A); -} - -/* - * Function smsc_ircc_probe_transceiver_toshiba_sat1800(fir_base) - * - * Probe transceiver - * - */ - -static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base) -{ - return 0; -} - - -module_init(smsc_ircc_init); -module_exit(smsc_ircc_cleanup); diff --git a/drivers/staging/irda/drivers/smsc-ircc2.h b/drivers/staging/irda/drivers/smsc-ircc2.h deleted file mode 100644 index 4829fa22cb29..000000000000 --- a/drivers/staging/irda/drivers/smsc-ircc2.h +++ /dev/null @@ -1,191 +0,0 @@ -/********************************************************************* - * - * Description: Definitions for the SMC IrCC chipset - * Status: Experimental. - * Author: Daniele Peri (peri@csai.unipa.it) - * - * Copyright (c) 2002 Daniele Peri - * All Rights Reserved. - * - * Based on smc-ircc.h: - * - * Copyright (c) 1999-2000, Dag Brattli - * Copyright (c) 1998-1999, Thomas Davis (tadavis@jps.net> - * All Rights Reserved - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef SMSC_IRCC2_H -#define SMSC_IRCC2_H - -/* DMA modes needed */ -#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ -#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ - -/* Master Control Register */ -#define IRCC_MASTER 0x07 -#define IRCC_MASTER_POWERDOWN 0x80 -#define IRCC_MASTER_RESET 0x40 -#define IRCC_MASTER_INT_EN 0x20 -#define IRCC_MASTER_ERROR_RESET 0x10 - -/* Register block 0 */ - -/* Interrupt Identification */ -#define IRCC_IIR 0x01 -#define IRCC_IIR_ACTIVE_FRAME 0x80 -#define IRCC_IIR_EOM 0x40 -#define IRCC_IIR_RAW_MODE 0x20 -#define IRCC_IIR_FIFO 0x10 - -/* Interrupt Enable */ -#define IRCC_IER 0x02 -#define IRCC_IER_ACTIVE_FRAME 0x80 -#define IRCC_IER_EOM 0x40 -#define IRCC_IER_RAW_MODE 0x20 -#define IRCC_IER_FIFO 0x10 - -/* Line Status Register */ -#define IRCC_LSR 0x03 -#define IRCC_LSR_UNDERRUN 0x80 -#define IRCC_LSR_OVERRUN 0x40 -#define IRCC_LSR_FRAME_ERROR 0x20 -#define IRCC_LSR_SIZE_ERROR 0x10 -#define IRCC_LSR_CRC_ERROR 0x80 -#define IRCC_LSR_FRAME_ABORT 0x40 - -/* Line Status Address Register */ -#define IRCC_LSAR 0x03 -#define IRCC_LSAR_ADDRESS_MASK 0x07 - -/* Line Control Register A */ -#define IRCC_LCR_A 0x04 -#define IRCC_LCR_A_FIFO_RESET 0x80 -#define IRCC_LCR_A_FAST 0x40 -#define IRCC_LCR_A_GP_DATA 0x20 -#define IRCC_LCR_A_RAW_TX 0x10 -#define IRCC_LCR_A_RAW_RX 0x08 -#define IRCC_LCR_A_ABORT 0x04 -#define IRCC_LCR_A_DATA_DONE 0x02 - -/* Line Control Register B */ -#define IRCC_LCR_B 0x05 -#define IRCC_LCR_B_SCE_DISABLED 0x00 -#define IRCC_LCR_B_SCE_TRANSMIT 0x40 -#define IRCC_LCR_B_SCE_RECEIVE 0x80 -#define IRCC_LCR_B_SCE_UNDEFINED 0xc0 -#define IRCC_LCR_B_SIP_ENABLE 0x20 -#define IRCC_LCR_B_BRICK_WALL 0x10 - -/* Bus Status Register */ -#define IRCC_BSR 0x06 -#define IRCC_BSR_NOT_EMPTY 0x80 -#define IRCC_BSR_FIFO_FULL 0x40 -#define IRCC_BSR_TIMEOUT 0x20 - -/* Register block 1 */ - -#define IRCC_FIFO_THRESHOLD 0x02 - -#define IRCC_SCE_CFGA 0x00 -#define IRCC_CFGA_AUX_IR 0x80 -#define IRCC_CFGA_HALF_DUPLEX 0x04 -#define IRCC_CFGA_TX_POLARITY 0x02 -#define IRCC_CFGA_RX_POLARITY 0x01 - -#define IRCC_CFGA_COM 0x00 -#define IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK 0x87 -#define IRCC_CFGA_IRDA_SIR_A 0x08 -#define IRCC_CFGA_ASK_SIR 0x10 -#define IRCC_CFGA_IRDA_SIR_B 0x18 -#define IRCC_CFGA_IRDA_HDLC 0x20 -#define IRCC_CFGA_IRDA_4PPM 0x28 -#define IRCC_CFGA_CONSUMER 0x30 -#define IRCC_CFGA_RAW_IR 0x38 -#define IRCC_CFGA_OTHER 0x40 - -#define IRCC_IR_HDLC 0x04 -#define IRCC_IR_4PPM 0x01 -#define IRCC_IR_CONSUMER 0x02 - -#define IRCC_SCE_CFGB 0x01 -#define IRCC_CFGB_LOOPBACK 0x20 -#define IRCC_CFGB_LPBCK_TX_CRC 0x10 -#define IRCC_CFGB_NOWAIT 0x08 -#define IRCC_CFGB_STRING_MOVE 0x04 -#define IRCC_CFGB_DMA_BURST 0x02 -#define IRCC_CFGB_DMA_ENABLE 0x01 - -#define IRCC_CFGB_MUX_COM 0x00 -#define IRCC_CFGB_MUX_IR 0x40 -#define IRCC_CFGB_MUX_AUX 0x80 -#define IRCC_CFGB_MUX_INACTIVE 0xc0 - -/* Register block 3 - Identification Registers! */ -#define IRCC_ID_HIGH 0x00 /* 0x10 */ -#define IRCC_ID_LOW 0x01 /* 0xB8 */ -#define IRCC_CHIP_ID 0x02 /* 0xF1 */ -#define IRCC_VERSION 0x03 /* 0x01 */ -#define IRCC_INTERFACE 0x04 /* low 4 = DMA, high 4 = IRQ */ -#define IRCC_INTERFACE_DMA_MASK 0x0F /* low 4 = DMA, high 4 = IRQ */ -#define IRCC_INTERFACE_IRQ_MASK 0xF0 /* low 4 = DMA, high 4 = IRQ */ - -/* Register block 4 - IrDA */ -#define IRCC_CONTROL 0x00 -#define IRCC_BOF_COUNT_LO 0x01 /* Low byte */ -#define IRCC_BOF_COUNT_HI 0x00 /* High nibble (bit 0-3) */ -#define IRCC_BRICKWALL_CNT_LO 0x02 /* Low byte */ -#define IRCC_BRICKWALL_CNT_HI 0x03 /* High nibble (bit 4-7) */ -#define IRCC_TX_SIZE_LO 0x04 /* Low byte */ -#define IRCC_TX_SIZE_HI 0x03 /* High nibble (bit 0-3) */ -#define IRCC_RX_SIZE_HI 0x05 /* High nibble (bit 0-3) */ -#define IRCC_RX_SIZE_LO 0x06 /* Low byte */ - -#define IRCC_1152 0x80 -#define IRCC_CRC 0x40 - -/* Register block 5 - IrDA */ -#define IRCC_ATC 0x00 -#define IRCC_ATC_nPROGREADY 0x80 -#define IRCC_ATC_SPEED 0x40 -#define IRCC_ATC_ENABLE 0x20 -#define IRCC_ATC_MASK 0xE0 - - -#define IRCC_IRHALFDUPLEX_TIMEOUT 0x01 - -#define IRCC_SCE_TX_DELAY_TIMER 0x02 - -/* - * Other definitions - */ - -#define SMSC_IRCC2_MAX_SIR_SPEED 115200 -#define SMSC_IRCC2_FIR_CHIP_IO_EXTENT 8 -#define SMSC_IRCC2_SIR_CHIP_IO_EXTENT 8 -#define SMSC_IRCC2_FIFO_SIZE 16 -#define SMSC_IRCC2_FIFO_THRESHOLD 64 -/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ -#define SMSC_IRCC2_RX_BUFF_TRUESIZE 14384 -#define SMSC_IRCC2_TX_BUFF_TRUESIZE 14384 -#define SMSC_IRCC2_MIN_TURN_TIME 0x07 -#define SMSC_IRCC2_WINDOW_SIZE 0x07 -/* Maximum wait for hw transmitter to finish */ -#define SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US 1000 /* 1 ms */ -/* Maximum wait for ATC transceiver programming to finish */ -#define SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES 1 -#endif /* SMSC_IRCC2_H */ diff --git a/drivers/staging/irda/drivers/smsc-sio.h b/drivers/staging/irda/drivers/smsc-sio.h deleted file mode 100644 index 59e20e653ebe..000000000000 --- a/drivers/staging/irda/drivers/smsc-sio.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef SMSC_SIO_H -#define SMSC_SIO_H - -/****************************************** - Keys. They should work with every SMsC SIO - ******************************************/ - -#define SMSCSIO_CFGACCESSKEY 0x55 -#define SMSCSIO_CFGEXITKEY 0xaa - -/***************************** - * Generic SIO Flat (!?) * - *****************************/ - -/* Register 0x0d */ -#define SMSCSIOFLAT_DEVICEID_REG 0x0d - -/* Register 0x0c */ -#define SMSCSIOFLAT_UARTMODE0C_REG 0x0c -#define SMSCSIOFLAT_UART2MODE_MASK 0x38 -#define SMSCSIOFLAT_UART2MODE_VAL_COM 0x00 -#define SMSCSIOFLAT_UART2MODE_VAL_IRDA 0x08 -#define SMSCSIOFLAT_UART2MODE_VAL_ASKIR 0x10 - -/* Register 0x25 */ -#define SMSCSIOFLAT_UART2BASEADDR_REG 0x25 - -/* Register 0x2b */ -#define SMSCSIOFLAT_FIRBASEADDR_REG 0x2b - -/* Register 0x2c */ -#define SMSCSIOFLAT_FIRDMASELECT_REG 0x2c -#define SMSCSIOFLAT_FIRDMASELECT_MASK 0x0f - -/* Register 0x28 */ -#define SMSCSIOFLAT_UARTIRQSELECT_REG 0x28 -#define SMSCSIOFLAT_UART2IRQSELECT_MASK 0x0f -#define SMSCSIOFLAT_UART1IRQSELECT_MASK 0xf0 -#define SMSCSIOFLAT_UARTIRQSELECT_VAL_NONE 0x00 - - -/********************* - * LPC47N227 * - *********************/ - -#define LPC47N227_CFGACCESSKEY 0x55 -#define LPC47N227_CFGEXITKEY 0xaa - -/* Register 0x00 */ -#define LPC47N227_FDCPOWERVALIDCONF_REG 0x00 -#define LPC47N227_FDCPOWER_MASK 0x08 -#define LPC47N227_VALID_MASK 0x80 - -/* Register 0x02 */ -#define LPC47N227_UART12POWER_REG 0x02 -#define LPC47N227_UART1POWERDOWN_MASK 0x08 -#define LPC47N227_UART2POWERDOWN_MASK 0x80 - -/* Register 0x07 */ -#define LPC47N227_APMBOOTDRIVE_REG 0x07 -#define LPC47N227_PARPORT2AUTOPWRDOWN_MASK 0x10 /* auto power down on if set */ -#define LPC47N227_UART2AUTOPWRDOWN_MASK 0x20 /* auto power down on if set */ -#define LPC47N227_UART1AUTOPWRDOWN_MASK 0x40 /* auto power down on if set */ - -/* Register 0x0c */ -#define LPC47N227_UARTMODE0C_REG 0x0c -#define LPC47N227_UART2MODE_MASK 0x38 -#define LPC47N227_UART2MODE_VAL_COM 0x00 -#define LPC47N227_UART2MODE_VAL_IRDA 0x08 -#define LPC47N227_UART2MODE_VAL_ASKIR 0x10 - -/* Register 0x0d */ -#define LPC47N227_DEVICEID_REG 0x0d -#define LPC47N227_DEVICEID_DEFVAL 0x5a - -/* Register 0x0e */ -#define LPC47N227_REVISIONID_REG 0x0e - -/* Register 0x25 */ -#define LPC47N227_UART2BASEADDR_REG 0x25 - -/* Register 0x28 */ -#define LPC47N227_UARTIRQSELECT_REG 0x28 -#define LPC47N227_UART2IRQSELECT_MASK 0x0f -#define LPC47N227_UART1IRQSELECT_MASK 0xf0 -#define LPC47N227_UARTIRQSELECT_VAL_NONE 0x00 - -/* Register 0x2b */ -#define LPC47N227_FIRBASEADDR_REG 0x2b - -/* Register 0x2c */ -#define LPC47N227_FIRDMASELECT_REG 0x2c -#define LPC47N227_FIRDMASELECT_MASK 0x0f -#define LPC47N227_FIRDMASELECT_VAL_DMA1 0x01 /* 47n227 has three dma channels */ -#define LPC47N227_FIRDMASELECT_VAL_DMA2 0x02 -#define LPC47N227_FIRDMASELECT_VAL_DMA3 0x03 -#define LPC47N227_FIRDMASELECT_VAL_NONE 0x0f - - -#endif diff --git a/drivers/staging/irda/drivers/stir4200.c b/drivers/staging/irda/drivers/stir4200.c deleted file mode 100644 index ee2cb70b688d..000000000000 --- a/drivers/staging/irda/drivers/stir4200.c +++ /dev/null @@ -1,1134 +0,0 @@ -/***************************************************************************** -* -* Filename: stir4200.c -* Version: 0.4 -* Description: Irda SigmaTel USB Dongle -* Status: Experimental -* Author: Stephen Hemminger -* -* Based on earlier driver by Paul Stewart -* -* Copyright (C) 2000, Roman Weissgaerber -* Copyright (C) 2001, Dag Brattli -* Copyright (C) 2001, Jean Tourrilhes -* Copyright (C) 2004, Stephen Hemminger -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - -/* - * This dongle does no framing, and requires polling to receive the - * data. The STIr4200 has bulk in and out endpoints just like - * usr-irda devices, but the data it sends and receives is raw; like - * irtty, it needs to call the wrap and unwrap functions to add and - * remove SOF/BOF and escape characters to/from the frame. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Stephen Hemminger "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver for SigmaTel STIr4200"); -MODULE_LICENSE("GPL"); - -static int qos_mtt_bits = 0x07; /* 1 ms or more */ -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); - -static int rx_sensitivity = 1; /* FIR 0..4, SIR 0..6 */ -module_param(rx_sensitivity, int, 0); -MODULE_PARM_DESC(rx_sensitivity, "Set Receiver sensitivity (0-6, 0 is most sensitive)"); - -static int tx_power = 0; /* 0 = highest ... 3 = lowest */ -module_param(tx_power, int, 0); -MODULE_PARM_DESC(tx_power, "Set Transmitter power (0-3, 0 is highest power)"); - -#define STIR_IRDA_HEADER 4 -#define CTRL_TIMEOUT 100 /* milliseconds */ -#define TRANSMIT_TIMEOUT 200 /* milliseconds */ -#define STIR_FIFO_SIZE 4096 -#define FIFO_REGS_SIZE 3 - -enum FirChars { - FIR_CE = 0x7d, - FIR_XBOF = 0x7f, - FIR_EOF = 0x7e, -}; - -enum StirRequests { - REQ_WRITE_REG = 0x00, - REQ_READ_REG = 0x01, - REQ_READ_ROM = 0x02, - REQ_WRITE_SINGLE = 0x03, -}; - -/* Register offsets */ -enum StirRegs { - REG_RSVD=0, - REG_MODE, - REG_PDCLK, - REG_CTRL1, - REG_CTRL2, - REG_FIFOCTL, - REG_FIFOLSB, - REG_FIFOMSB, - REG_DPLL, - REG_IRDIG, - REG_TEST=15, -}; - -enum StirModeMask { - MODE_FIR = 0x80, - MODE_SIR = 0x20, - MODE_ASK = 0x10, - MODE_FASTRX = 0x08, - MODE_FFRSTEN = 0x04, - MODE_NRESET = 0x02, - MODE_2400 = 0x01, -}; - -enum StirPdclkMask { - PDCLK_4000000 = 0x02, - PDCLK_115200 = 0x09, - PDCLK_57600 = 0x13, - PDCLK_38400 = 0x1D, - PDCLK_19200 = 0x3B, - PDCLK_9600 = 0x77, - PDCLK_2400 = 0xDF, -}; - -enum StirCtrl1Mask { - CTRL1_SDMODE = 0x80, - CTRL1_RXSLOW = 0x40, - CTRL1_TXPWD = 0x10, - CTRL1_RXPWD = 0x08, - CTRL1_SRESET = 0x01, -}; - -enum StirCtrl2Mask { - CTRL2_SPWIDTH = 0x08, - CTRL2_REVID = 0x03, -}; - -enum StirFifoCtlMask { - FIFOCTL_DIR = 0x10, - FIFOCTL_CLR = 0x08, - FIFOCTL_EMPTY = 0x04, -}; - -enum StirDiagMask { - IRDIG_RXHIGH = 0x80, - IRDIG_RXLOW = 0x40, -}; - -enum StirTestMask { - TEST_PLLDOWN = 0x80, - TEST_LOOPIR = 0x40, - TEST_LOOPUSB = 0x20, - TEST_TSTENA = 0x10, - TEST_TSTOSC = 0x0F, -}; - -struct stir_cb { - struct usb_device *usbdev; /* init: probe_irda */ - struct net_device *netdev; /* network layer */ - struct irlap_cb *irlap; /* The link layer we are binded to */ - - struct qos_info qos; - unsigned speed; /* Current speed */ - - struct task_struct *thread; /* transmit thread */ - - struct sk_buff *tx_pending; - void *io_buf; /* transmit/receive buffer */ - __u8 *fifo_status; - - iobuff_t rx_buff; /* receive unwrap state machine */ - ktime_t rx_time; - int receiving; - struct urb *rx_urb; -}; - - -/* These are the currently known USB ids */ -static const struct usb_device_id dongles[] = { - /* SigmaTel, Inc, STIr4200 IrDA/USB Bridge */ - { USB_DEVICE(0x066f, 0x4200) }, - { } -}; - -MODULE_DEVICE_TABLE(usb, dongles); - -/* Send control message to set dongle register */ -static int write_reg(struct stir_cb *stir, __u16 reg, __u8 value) -{ - struct usb_device *dev = stir->usbdev; - - pr_debug("%s: write reg %d = 0x%x\n", - stir->netdev->name, reg, value); - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - REQ_WRITE_SINGLE, - USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE, - value, reg, NULL, 0, - CTRL_TIMEOUT); -} - -/* Send control message to read multiple registers */ -static inline int read_reg(struct stir_cb *stir, __u16 reg, - __u8 *data, __u16 count) -{ - struct usb_device *dev = stir->usbdev; - - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - REQ_READ_REG, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, reg, data, count, - CTRL_TIMEOUT); -} - -static inline int isfir(u32 speed) -{ - return speed == 4000000; -} - -/* - * Prepare a FIR IrDA frame for transmission to the USB dongle. The - * FIR transmit frame is documented in the datasheet. It consists of - * a two byte 0x55 0xAA sequence, two little-endian length bytes, a - * sequence of exactly 16 XBOF bytes of 0x7E, two BOF bytes of 0x7E, - * then the data escaped as follows: - * - * 0x7D -> 0x7D 0x5D - * 0x7E -> 0x7D 0x5E - * 0x7F -> 0x7D 0x5F - * - * Then, 4 bytes of little endian (stuffed) FCS follow, then two - * trailing EOF bytes of 0x7E. - */ -static inline __u8 *stuff_fir(__u8 *p, __u8 c) -{ - switch(c) { - case 0x7d: - case 0x7e: - case 0x7f: - *p++ = 0x7d; - c ^= IRDA_TRANS; - /* fall through */ - default: - *p++ = c; - } - return p; -} - -/* Take raw data in skb and put it wrapped into buf */ -static unsigned wrap_fir_skb(const struct sk_buff *skb, __u8 *buf) -{ - __u8 *ptr = buf; - __u32 fcs = ~(crc32_le(~0, skb->data, skb->len)); - __u16 wraplen; - int i; - - /* Header */ - buf[0] = 0x55; - buf[1] = 0xAA; - - ptr = buf + STIR_IRDA_HEADER; - memset(ptr, 0x7f, 16); - ptr += 16; - - /* BOF */ - *ptr++ = 0x7e; - *ptr++ = 0x7e; - - /* Address / Control / Information */ - for (i = 0; i < skb->len; i++) - ptr = stuff_fir(ptr, skb->data[i]); - - /* FCS */ - ptr = stuff_fir(ptr, fcs & 0xff); - ptr = stuff_fir(ptr, (fcs >> 8) & 0xff); - ptr = stuff_fir(ptr, (fcs >> 16) & 0xff); - ptr = stuff_fir(ptr, (fcs >> 24) & 0xff); - - /* EOFs */ - *ptr++ = 0x7e; - *ptr++ = 0x7e; - - /* Total length, minus the header */ - wraplen = (ptr - buf) - STIR_IRDA_HEADER; - buf[2] = wraplen & 0xff; - buf[3] = (wraplen >> 8) & 0xff; - - return wraplen + STIR_IRDA_HEADER; -} - -static unsigned wrap_sir_skb(struct sk_buff *skb, __u8 *buf) -{ - __u16 wraplen; - - wraplen = async_wrap_skb(skb, buf + STIR_IRDA_HEADER, - STIR_FIFO_SIZE - STIR_IRDA_HEADER); - buf[0] = 0x55; - buf[1] = 0xAA; - buf[2] = wraplen & 0xff; - buf[3] = (wraplen >> 8) & 0xff; - - return wraplen + STIR_IRDA_HEADER; -} - -/* - * Frame is fully formed in the rx_buff so check crc - * and pass up to irlap - * setup for next receive - */ -static void fir_eof(struct stir_cb *stir) -{ - iobuff_t *rx_buff = &stir->rx_buff; - int len = rx_buff->len - 4; - struct sk_buff *skb, *nskb; - __u32 fcs; - - if (unlikely(len <= 0)) { - pr_debug("%s: short frame len %d\n", - stir->netdev->name, len); - - ++stir->netdev->stats.rx_errors; - ++stir->netdev->stats.rx_length_errors; - return; - } - - fcs = ~(crc32_le(~0, rx_buff->data, len)); - if (fcs != get_unaligned_le32(rx_buff->data + len)) { - pr_debug("crc error calc 0x%x len %d\n", fcs, len); - stir->netdev->stats.rx_errors++; - stir->netdev->stats.rx_crc_errors++; - return; - } - - /* if frame is short then just copy it */ - if (len < IRDA_RX_COPY_THRESHOLD) { - nskb = dev_alloc_skb(len + 1); - if (unlikely(!nskb)) { - ++stir->netdev->stats.rx_dropped; - return; - } - skb_reserve(nskb, 1); - skb = nskb; - skb_copy_to_linear_data(nskb, rx_buff->data, len); - } else { - nskb = dev_alloc_skb(rx_buff->truesize); - if (unlikely(!nskb)) { - ++stir->netdev->stats.rx_dropped; - return; - } - skb_reserve(nskb, 1); - skb = rx_buff->skb; - rx_buff->skb = nskb; - rx_buff->head = nskb->data; - } - - skb_put(skb, len); - - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - skb->dev = stir->netdev; - - netif_rx(skb); - - stir->netdev->stats.rx_packets++; - stir->netdev->stats.rx_bytes += len; - - rx_buff->data = rx_buff->head; - rx_buff->len = 0; -} - -/* Unwrap FIR stuffed data and bump it to IrLAP */ -static void stir_fir_chars(struct stir_cb *stir, - const __u8 *bytes, int len) -{ - iobuff_t *rx_buff = &stir->rx_buff; - int i; - - for (i = 0; i < len; i++) { - __u8 byte = bytes[i]; - - switch(rx_buff->state) { - case OUTSIDE_FRAME: - /* ignore garbage till start of frame */ - if (unlikely(byte != FIR_EOF)) - continue; - /* Now receiving frame */ - rx_buff->state = BEGIN_FRAME; - - /* Time to initialize receive buffer */ - rx_buff->data = rx_buff->head; - rx_buff->len = 0; - continue; - - case LINK_ESCAPE: - if (byte == FIR_EOF) { - pr_debug("%s: got EOF after escape\n", - stir->netdev->name); - goto frame_error; - } - rx_buff->state = INSIDE_FRAME; - byte ^= IRDA_TRANS; - break; - - case BEGIN_FRAME: - /* ignore multiple BOF/EOF */ - if (byte == FIR_EOF) - continue; - rx_buff->state = INSIDE_FRAME; - rx_buff->in_frame = TRUE; - - /* fall through */ - case INSIDE_FRAME: - switch(byte) { - case FIR_CE: - rx_buff->state = LINK_ESCAPE; - continue; - case FIR_XBOF: - /* 0x7f is not used in this framing */ - pr_debug("%s: got XBOF without escape\n", - stir->netdev->name); - goto frame_error; - case FIR_EOF: - rx_buff->state = OUTSIDE_FRAME; - rx_buff->in_frame = FALSE; - fir_eof(stir); - continue; - } - break; - } - - /* add byte to rx buffer */ - if (unlikely(rx_buff->len >= rx_buff->truesize)) { - pr_debug("%s: fir frame exceeds %d\n", - stir->netdev->name, rx_buff->truesize); - ++stir->netdev->stats.rx_over_errors; - goto error_recovery; - } - - rx_buff->data[rx_buff->len++] = byte; - continue; - - frame_error: - ++stir->netdev->stats.rx_frame_errors; - - error_recovery: - ++stir->netdev->stats.rx_errors; - rx_buff->state = OUTSIDE_FRAME; - rx_buff->in_frame = FALSE; - } -} - -/* Unwrap SIR stuffed data and bump it up to IrLAP */ -static void stir_sir_chars(struct stir_cb *stir, - const __u8 *bytes, int len) -{ - int i; - - for (i = 0; i < len; i++) - async_unwrap_char(stir->netdev, &stir->netdev->stats, - &stir->rx_buff, bytes[i]); -} - -static inline void unwrap_chars(struct stir_cb *stir, - const __u8 *bytes, int length) -{ - if (isfir(stir->speed)) - stir_fir_chars(stir, bytes, length); - else - stir_sir_chars(stir, bytes, length); -} - -/* Mode parameters for each speed */ -static const struct { - unsigned speed; - __u8 pdclk; -} stir_modes[] = { - { 2400, PDCLK_2400 }, - { 9600, PDCLK_9600 }, - { 19200, PDCLK_19200 }, - { 38400, PDCLK_38400 }, - { 57600, PDCLK_57600 }, - { 115200, PDCLK_115200 }, - { 4000000, PDCLK_4000000 }, -}; - - -/* - * Setup chip for speed. - * Called at startup to initialize the chip - * and on speed changes. - * - * Note: Write multiple registers doesn't appear to work - */ -static int change_speed(struct stir_cb *stir, unsigned speed) -{ - int i, err; - __u8 mode; - - for (i = 0; i < ARRAY_SIZE(stir_modes); ++i) { - if (speed == stir_modes[i].speed) - goto found; - } - - dev_warn(&stir->netdev->dev, "invalid speed %d\n", speed); - return -EINVAL; - - found: - pr_debug("speed change from %d to %d\n", stir->speed, speed); - - /* Reset modulator */ - err = write_reg(stir, REG_CTRL1, CTRL1_SRESET); - if (err) - goto out; - - /* Undocumented magic to tweak the DPLL */ - err = write_reg(stir, REG_DPLL, 0x15); - if (err) - goto out; - - /* Set clock */ - err = write_reg(stir, REG_PDCLK, stir_modes[i].pdclk); - if (err) - goto out; - - mode = MODE_NRESET | MODE_FASTRX; - if (isfir(speed)) - mode |= MODE_FIR | MODE_FFRSTEN; - else - mode |= MODE_SIR; - - if (speed == 2400) - mode |= MODE_2400; - - err = write_reg(stir, REG_MODE, mode); - if (err) - goto out; - - /* This resets TEMIC style transceiver if any. */ - err = write_reg(stir, REG_CTRL1, - CTRL1_SDMODE | (tx_power & 3) << 1); - if (err) - goto out; - - err = write_reg(stir, REG_CTRL1, (tx_power & 3) << 1); - if (err) - goto out; - - /* Reset sensitivity */ - err = write_reg(stir, REG_CTRL2, (rx_sensitivity & 7) << 5); - out: - stir->speed = speed; - return err; -} - -/* - * Called from net/core when new frame is available. - */ -static netdev_tx_t stir_hard_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct stir_cb *stir = netdev_priv(netdev); - - netif_stop_queue(netdev); - - /* the IRDA wrapping routines don't deal with non linear skb */ - SKB_LINEAR_ASSERT(skb); - - skb = xchg(&stir->tx_pending, skb); - wake_up_process(stir->thread); - - /* this should never happen unless stop/wakeup problem */ - if (unlikely(skb)) { - WARN_ON(1); - dev_kfree_skb(skb); - } - - return NETDEV_TX_OK; -} - -/* - * Wait for the transmit FIFO to have space for next data - * - * If space < 0 then wait till FIFO completely drains. - * FYI: can take up to 13 seconds at 2400baud. - */ -static int fifo_txwait(struct stir_cb *stir, int space) -{ - int err; - unsigned long count, status; - unsigned long prev_count = 0x1fff; - - /* Read FIFO status and count */ - for (;; prev_count = count) { - err = read_reg(stir, REG_FIFOCTL, stir->fifo_status, - FIFO_REGS_SIZE); - if (unlikely(err != FIFO_REGS_SIZE)) { - dev_warn(&stir->netdev->dev, - "FIFO register read error: %d\n", err); - - return err; - } - - status = stir->fifo_status[0]; - count = (unsigned)(stir->fifo_status[2] & 0x1f) << 8 - | stir->fifo_status[1]; - - pr_debug("fifo status 0x%lx count %lu\n", status, count); - - /* is fifo receiving already, or empty */ - if (!(status & FIFOCTL_DIR) || - (status & FIFOCTL_EMPTY)) - return 0; - - if (signal_pending(current)) - return -EINTR; - - /* shutting down? */ - if (!netif_running(stir->netdev) || - !netif_device_present(stir->netdev)) - return -ESHUTDOWN; - - /* only waiting for some space */ - if (space >= 0 && STIR_FIFO_SIZE - 4 > space + count) - return 0; - - /* queue confused */ - if (prev_count < count) - break; - - /* estimate transfer time for remaining chars */ - msleep((count * 8000) / stir->speed); - } - - err = write_reg(stir, REG_FIFOCTL, FIFOCTL_CLR); - if (err) - return err; - err = write_reg(stir, REG_FIFOCTL, 0); - if (err) - return err; - - return 0; -} - - -/* Wait for turnaround delay before starting transmit. */ -static void turnaround_delay(const struct stir_cb *stir, long us) -{ - long ticks; - - if (us <= 0) - return; - - us -= ktime_us_delta(ktime_get(), stir->rx_time); - - if (us < 10) - return; - - ticks = us / (1000000 / HZ); - if (ticks > 0) - schedule_timeout_interruptible(1 + ticks); - else - udelay(us); -} - -/* - * Start receiver by submitting a request to the receive pipe. - * If nothing is available it will return after rx_interval. - */ -static int receive_start(struct stir_cb *stir) -{ - /* reset state */ - stir->receiving = 1; - - stir->rx_buff.in_frame = FALSE; - stir->rx_buff.state = OUTSIDE_FRAME; - - stir->rx_urb->status = 0; - return usb_submit_urb(stir->rx_urb, GFP_KERNEL); -} - -/* Stop all pending receive Urb's */ -static void receive_stop(struct stir_cb *stir) -{ - stir->receiving = 0; - usb_kill_urb(stir->rx_urb); - - if (stir->rx_buff.in_frame) - stir->netdev->stats.collisions++; -} -/* - * Wrap data in socket buffer and send it. - */ -static void stir_send(struct stir_cb *stir, struct sk_buff *skb) -{ - unsigned wraplen; - int first_frame = 0; - - /* if receiving, need to turnaround */ - if (stir->receiving) { - receive_stop(stir); - turnaround_delay(stir, irda_get_mtt(skb)); - first_frame = 1; - } - - if (isfir(stir->speed)) - wraplen = wrap_fir_skb(skb, stir->io_buf); - else - wraplen = wrap_sir_skb(skb, stir->io_buf); - - /* check for space available in fifo */ - if (!first_frame) - fifo_txwait(stir, wraplen); - - stir->netdev->stats.tx_packets++; - stir->netdev->stats.tx_bytes += skb->len; - netif_trans_update(stir->netdev); - pr_debug("send %d (%d)\n", skb->len, wraplen); - - if (usb_bulk_msg(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1), - stir->io_buf, wraplen, - NULL, TRANSMIT_TIMEOUT)) - stir->netdev->stats.tx_errors++; -} - -/* - * Transmit state machine thread - */ -static int stir_transmit_thread(void *arg) -{ - struct stir_cb *stir = arg; - struct net_device *dev = stir->netdev; - struct sk_buff *skb; - - while (!kthread_should_stop()) { -#ifdef CONFIG_PM - /* if suspending, then power off and wait */ - if (unlikely(freezing(current))) { - if (stir->receiving) - receive_stop(stir); - else - fifo_txwait(stir, -1); - - write_reg(stir, REG_CTRL1, CTRL1_TXPWD|CTRL1_RXPWD); - - try_to_freeze(); - - if (change_speed(stir, stir->speed)) - break; - } -#endif - - /* if something to send? */ - skb = xchg(&stir->tx_pending, NULL); - if (skb) { - unsigned new_speed = irda_get_next_speed(skb); - netif_wake_queue(dev); - - if (skb->len > 0) - stir_send(stir, skb); - dev_kfree_skb(skb); - - if ((new_speed != -1) && (stir->speed != new_speed)) { - if (fifo_txwait(stir, -1) || - change_speed(stir, new_speed)) - break; - } - continue; - } - - /* nothing to send? start receiving */ - if (!stir->receiving && - irda_device_txqueue_empty(dev)) { - /* Wait otherwise chip gets confused. */ - if (fifo_txwait(stir, -1)) - break; - - if (unlikely(receive_start(stir))) { - if (net_ratelimit()) - dev_info(&dev->dev, - "%s: receive usb submit failed\n", - stir->netdev->name); - stir->receiving = 0; - msleep(10); - continue; - } - } - - /* sleep if nothing to send */ - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - - } - return 0; -} - - -/* - * USB bulk receive completion callback. - * Wakes up every ms (usb round trip) with wrapped - * data. - */ -static void stir_rcv_irq(struct urb *urb) -{ - struct stir_cb *stir = urb->context; - int err; - - /* in process of stopping, just drop data */ - if (!netif_running(stir->netdev)) - return; - - /* unlink, shutdown, unplug, other nasties */ - if (urb->status != 0) - return; - - if (urb->actual_length > 0) { - pr_debug("receive %d\n", urb->actual_length); - unwrap_chars(stir, urb->transfer_buffer, - urb->actual_length); - - stir->rx_time = ktime_get(); - } - - /* kernel thread is stopping receiver don't resubmit */ - if (!stir->receiving) - return; - - /* resubmit existing urb */ - err = usb_submit_urb(urb, GFP_ATOMIC); - - /* in case of error, the kernel thread will restart us */ - if (err) { - dev_warn(&stir->netdev->dev, "usb receive submit error: %d\n", - err); - stir->receiving = 0; - wake_up_process(stir->thread); - } -} - -/* - * Function stir_net_open (dev) - * - * Network device is taken up. Usually this is done by "ifconfig irda0 up" - */ -static int stir_net_open(struct net_device *netdev) -{ - struct stir_cb *stir = netdev_priv(netdev); - int err; - char hwname[16]; - - err = usb_clear_halt(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1)); - if (err) - goto err_out1; - err = usb_clear_halt(stir->usbdev, usb_rcvbulkpipe(stir->usbdev, 2)); - if (err) - goto err_out1; - - err = change_speed(stir, 9600); - if (err) - goto err_out1; - - err = -ENOMEM; - - /* Initialize for SIR/FIR to copy data directly into skb. */ - stir->receiving = 0; - stir->rx_buff.truesize = IRDA_SKB_MAX_MTU; - stir->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!stir->rx_buff.skb) - goto err_out1; - - skb_reserve(stir->rx_buff.skb, 1); - stir->rx_buff.head = stir->rx_buff.skb->data; - stir->rx_time = ktime_get(); - - stir->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!stir->rx_urb) - goto err_out2; - - stir->io_buf = kmalloc(STIR_FIFO_SIZE, GFP_KERNEL); - if (!stir->io_buf) - goto err_out3; - - usb_fill_bulk_urb(stir->rx_urb, stir->usbdev, - usb_rcvbulkpipe(stir->usbdev, 2), - stir->io_buf, STIR_FIFO_SIZE, - stir_rcv_irq, stir); - - stir->fifo_status = kmalloc(FIFO_REGS_SIZE, GFP_KERNEL); - if (!stir->fifo_status) - goto err_out4; - - /* - * Now that everything should be initialized properly, - * Open new IrLAP layer instance to take care of us... - * Note : will send immediately a speed change... - */ - sprintf(hwname, "usb#%d", stir->usbdev->devnum); - stir->irlap = irlap_open(netdev, &stir->qos, hwname); - if (!stir->irlap) { - dev_err(&stir->usbdev->dev, "irlap_open failed\n"); - goto err_out5; - } - - /** Start kernel thread for transmit. */ - stir->thread = kthread_run(stir_transmit_thread, stir, - "%s", stir->netdev->name); - if (IS_ERR(stir->thread)) { - err = PTR_ERR(stir->thread); - dev_err(&stir->usbdev->dev, "unable to start kernel thread\n"); - goto err_out6; - } - - netif_start_queue(netdev); - - return 0; - - err_out6: - irlap_close(stir->irlap); - err_out5: - kfree(stir->fifo_status); - err_out4: - kfree(stir->io_buf); - err_out3: - usb_free_urb(stir->rx_urb); - err_out2: - kfree_skb(stir->rx_buff.skb); - err_out1: - return err; -} - -/* - * Function stir_net_close (stir) - * - * Network device is taken down. Usually this is done by - * "ifconfig irda0 down" - */ -static int stir_net_close(struct net_device *netdev) -{ - struct stir_cb *stir = netdev_priv(netdev); - - /* Stop transmit processing */ - netif_stop_queue(netdev); - - /* Kill transmit thread */ - kthread_stop(stir->thread); - kfree(stir->fifo_status); - - /* Mop up receive urb's */ - usb_kill_urb(stir->rx_urb); - - kfree(stir->io_buf); - usb_free_urb(stir->rx_urb); - kfree_skb(stir->rx_buff.skb); - - /* Stop and remove instance of IrLAP */ - if (stir->irlap) - irlap_close(stir->irlap); - - stir->irlap = NULL; - - return 0; -} - -/* - * IOCTLs : Extra out-of-band network commands... - */ -static int stir_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct stir_cb *stir = netdev_priv(netdev); - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the device is still there */ - if (netif_device_present(stir->netdev)) - ret = change_speed(stir, irq->ifr_baudrate); - break; - - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - /* Check if the IrDA stack is still there */ - if (netif_running(stir->netdev)) - irda_device_set_media_busy(stir->netdev, TRUE); - break; - - case SIOCGRECEIVING: - /* Only approximately true */ - irq->ifr_receiving = stir->receiving; - break; - - default: - ret = -EOPNOTSUPP; - } - - return ret; -} - -static const struct net_device_ops stir_netdev_ops = { - .ndo_open = stir_net_open, - .ndo_stop = stir_net_close, - .ndo_start_xmit = stir_hard_xmit, - .ndo_do_ioctl = stir_net_ioctl, -}; - -/* - * This routine is called by the USB subsystem for each new device - * in the system. We need to check if the device is ours, and in - * this case start handling it. - * Note : it might be worth protecting this function by a global - * spinlock... Or not, because maybe USB already deal with that... - */ -static int stir_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct stir_cb *stir = NULL; - struct net_device *net; - int ret = -ENOMEM; - - /* Allocate network device container. */ - net = alloc_irdadev(sizeof(*stir)); - if(!net) - goto err_out1; - - SET_NETDEV_DEV(net, &intf->dev); - stir = netdev_priv(net); - stir->netdev = net; - stir->usbdev = dev; - - ret = usb_reset_configuration(dev); - if (ret != 0) { - dev_err(&intf->dev, "usb reset configuration failed\n"); - goto err_out2; - } - - printk(KERN_INFO "SigmaTel STIr4200 IRDA/USB found at address %d, " - "Vendor: %x, Product: %x\n", - dev->devnum, le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&stir->qos); - - /* That's the Rx capability. */ - stir->qos.baud_rate.bits &= IR_2400 | IR_9600 | IR_19200 | - IR_38400 | IR_57600 | IR_115200 | - (IR_4000000 << 8); - stir->qos.min_turn_time.bits &= qos_mtt_bits; - irda_qos_bits_to_value(&stir->qos); - - /* Override the network functions we need to use */ - net->netdev_ops = &stir_netdev_ops; - - ret = register_netdev(net); - if (ret != 0) - goto err_out2; - - dev_info(&intf->dev, "IrDA: Registered SigmaTel device %s\n", - net->name); - - usb_set_intfdata(intf, stir); - - return 0; - -err_out2: - free_netdev(net); -err_out1: - return ret; -} - -/* - * The current device is removed, the USB layer tell us to shut it down... - */ -static void stir_disconnect(struct usb_interface *intf) -{ - struct stir_cb *stir = usb_get_intfdata(intf); - - if (!stir) - return; - - unregister_netdev(stir->netdev); - free_netdev(stir->netdev); - - usb_set_intfdata(intf, NULL); -} - -#ifdef CONFIG_PM -/* USB suspend, so power off the transmitter/receiver */ -static int stir_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct stir_cb *stir = usb_get_intfdata(intf); - - netif_device_detach(stir->netdev); - return 0; -} - -/* Coming out of suspend, so reset hardware */ -static int stir_resume(struct usb_interface *intf) -{ - struct stir_cb *stir = usb_get_intfdata(intf); - - netif_device_attach(stir->netdev); - - /* receiver restarted when send thread wakes up */ - return 0; -} -#endif - -/* - * USB device callbacks - */ -static struct usb_driver irda_driver = { - .name = "stir4200", - .probe = stir_probe, - .disconnect = stir_disconnect, - .id_table = dongles, -#ifdef CONFIG_PM - .suspend = stir_suspend, - .resume = stir_resume, -#endif -}; - -module_usb_driver(irda_driver); diff --git a/drivers/staging/irda/drivers/tekram-sir.c b/drivers/staging/irda/drivers/tekram-sir.c deleted file mode 100644 index 9dcf0c103b9d..000000000000 --- a/drivers/staging/irda/drivers/tekram-sir.c +++ /dev/null @@ -1,225 +0,0 @@ -/********************************************************************* - * - * Filename: tekram.c - * Version: 1.3 - * Description: Implementation of the Tekram IrMate IR-210B dongle - * Status: Experimental. - * Author: Dag Brattli - * Created at: Wed Oct 21 20:02:35 1998 - * Modified at: Sun Oct 27 22:02:38 2002 - * Modified by: Martin Diehl - * - * Copyright (c) 1998-1999 Dag Brattli, - * Copyright (c) 2002 Martin Diehl, - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int tekram_delay = 150; /* default is 150 ms */ -module_param(tekram_delay, int, 0); -MODULE_PARM_DESC(tekram_delay, "tekram dongle write complete delay"); - -static int tekram_open(struct sir_dev *); -static int tekram_close(struct sir_dev *); -static int tekram_change_speed(struct sir_dev *, unsigned); -static int tekram_reset(struct sir_dev *); - -#define TEKRAM_115200 0x00 -#define TEKRAM_57600 0x01 -#define TEKRAM_38400 0x02 -#define TEKRAM_19200 0x03 -#define TEKRAM_9600 0x04 - -#define TEKRAM_PW 0x10 /* Pulse select bit */ - -static struct dongle_driver tekram = { - .owner = THIS_MODULE, - .driver_name = "Tekram IR-210B", - .type = IRDA_TEKRAM_DONGLE, - .open = tekram_open, - .close = tekram_close, - .reset = tekram_reset, - .set_speed = tekram_change_speed, -}; - -static int __init tekram_sir_init(void) -{ - if (tekram_delay < 1 || tekram_delay > 500) - tekram_delay = 200; - pr_debug("%s - using %d ms delay\n", - tekram.driver_name, tekram_delay); - return irda_register_dongle(&tekram); -} - -static void __exit tekram_sir_cleanup(void) -{ - irda_unregister_dongle(&tekram); -} - -static int tekram_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int tekram_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function tekram_change_speed (dev, state, speed) - * - * Set the speed for the Tekram IRMate 210 type dongle. Warning, this - * function must be called with a process context! - * - * Algorithm - * 1. clear DTR - * 2. set RTS, and wait at least 7 us - * 3. send Control Byte to the IR-210 through TXD to set new baud rate - * wait until the stop bit of Control Byte is sent (for 9600 baud rate, - * it takes about 100 msec) - * - * [oops, why 100 msec? sending 1 byte (10 bits) takes 1.05 msec - * - is this probably to compensate for delays in tty layer?] - * - * 5. clear RTS (return to NORMAL Operation) - * 6. wait at least 50 us, new setting (baud rate, etc) takes effect here - * after - */ - -#define TEKRAM_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) - -static int tekram_change_speed(struct sir_dev *dev, unsigned speed) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - u8 byte; - static int ret = 0; - - switch(state) { - case SIRDEV_STATE_DONGLE_SPEED: - - switch (speed) { - default: - speed = 9600; - ret = -EINVAL; - /* fall thru */ - case 9600: - byte = TEKRAM_PW|TEKRAM_9600; - break; - case 19200: - byte = TEKRAM_PW|TEKRAM_19200; - break; - case 38400: - byte = TEKRAM_PW|TEKRAM_38400; - break; - case 57600: - byte = TEKRAM_PW|TEKRAM_57600; - break; - case 115200: - byte = TEKRAM_115200; - break; - } - - /* Set DTR, Clear RTS */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - - /* Wait at least 7us */ - udelay(14); - - /* Write control byte */ - sirdev_raw_write(dev, &byte, 1); - - dev->speed = speed; - - state = TEKRAM_STATE_WAIT_SPEED; - delay = tekram_delay; - break; - - case TEKRAM_STATE_WAIT_SPEED: - /* Set DTR, Set RTS */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - udelay(50); - break; - - default: - net_err_ratelimited("%s - undefined state %d\n", - __func__, state); - ret = -EINVAL; - break; - } - - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -/* - * Function tekram_reset (driver) - * - * This function resets the tekram dongle. Warning, this function - * must be called with a process context!! - * - * Algorithm: - * 0. Clear RTS and DTR, and wait 50 ms (power off the IR-210 ) - * 1. clear RTS - * 2. set DTR, and wait at least 1 ms - * 3. clear DTR to SPACE state, wait at least 50 us for further - * operation - */ - -static int tekram_reset(struct sir_dev *dev) -{ - /* Clear DTR, Set RTS */ - sirdev_set_dtr_rts(dev, FALSE, TRUE); - - /* Should sleep 1 ms */ - msleep(1); - - /* Set DTR, Set RTS */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Wait at least 50 us */ - udelay(75); - - dev->speed = 9600; - - return 0; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-0"); /* IRDA_TEKRAM_DONGLE */ - -module_init(tekram_sir_init); -module_exit(tekram_sir_cleanup); diff --git a/drivers/staging/irda/drivers/toim3232-sir.c b/drivers/staging/irda/drivers/toim3232-sir.c deleted file mode 100644 index b977d6d33e74..000000000000 --- a/drivers/staging/irda/drivers/toim3232-sir.c +++ /dev/null @@ -1,358 +0,0 @@ -/********************************************************************* - * - * Filename: toim3232-sir.c - * Version: 1.0 - * Description: Implementation of dongles based on the Vishay/Temic - * TOIM3232 SIR Endec chipset. Currently only the - * IRWave IR320ST-2 is tested, although it should work - * with any TOIM3232 or TOIM4232 chipset based RS232 - * dongle with minimal modification. - * Based heavily on the Tekram driver (tekram.c), - * with thanks to Dag Brattli and Martin Diehl. - * Status: Experimental. - * Author: David Basden - * Created at: Thu Feb 09 23:47:32 2006 - * - * Copyright (c) 2006 David Basden. - * Copyright (c) 1998-1999 Dag Brattli, - * Copyright (c) 2002 Martin Diehl, - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -/* - * This driver has currently only been tested on the IRWave IR320ST-2 - * - * PROTOCOL: - * - * The protocol for talking to the TOIM3232 is quite easy, and is - * designed to interface with RS232 with only level convertors. The - * BR/~D line on the chip is brought high to signal 'command mode', - * where a command byte is sent to select the baudrate of the RS232 - * interface and the pulse length of the IRDA output. When BR/~D - * is brought low, the dongle then changes to the selected baudrate, - * and the RS232 interface is used for data until BR/~D is brought - * high again. The initial speed for the TOIMx323 after RESET is - * 9600 baud. The baudrate for command-mode is the last selected - * baud-rate, or 9600 after a RESET. - * - * The dongle I have (below) adds some extra hardware on the front end, - * but this is mostly directed towards pariasitic power from the RS232 - * line rather than changing very much about how to communicate with - * the TOIM3232. - * - * The protocol to talk to the TOIM4232 chipset seems to be almost - * identical to the TOIM3232 (and the 4232 datasheet is more detailed) - * so this code will probably work on that as well, although I haven't - * tested it on that hardware. - * - * Target dongle variations that might be common: - * - * DTR and RTS function: - * The data sheet for the 4232 has a sample implementation that hooks the - * DTR and RTS lines to the RESET and BaudRate/~Data lines of the - * chip (through line-converters). Given both DTR and RTS would have to - * be held low in normal operation, and the TOIMx232 requires +5V to - * signal ground, most dongle designers would almost certainly choose - * an implementation that kept at least one of DTR or RTS high in - * normal operation to provide power to the dongle, but will likely - * vary between designs. - * - * User specified command bits: - * There are two user-controllable output lines from the TOIMx232 that - * can be set low or high by setting the appropriate bits in the - * high-nibble of the command byte (when setting speed and pulse length). - * These might be used to switch on and off added hardware or extra - * dongle features. - * - * - * Target hardware: IRWave IR320ST-2 - * - * The IRWave IR320ST-2 is a simple dongle based on the Vishay/Temic - * TOIM3232 SIR Endec and the Vishay/Temic TFDS4500 SIR IRDA transceiver. - * It uses a hex inverter and some discrete components to buffer and - * line convert the RS232 down to 5V. - * - * The dongle is powered through a voltage regulator, fed by a large - * capacitor. To switch the dongle on, DTR is brought high to charge - * the capacitor and drive the voltage regulator. DTR isn't associated - * with any control lines on the TOIM3232. Parisitic power is also taken - * from the RTS, TD and RD lines when brought high, but through resistors. - * When DTR is low, the circuit might lose power even with RTS high. - * - * RTS is inverted and attached to the BR/~D input pin. When RTS - * is high, BR/~D is low, and the TOIM3232 is in the normal 'data' mode. - * RTS is brought low, BR/~D is high, and the TOIM3232 is in 'command - * mode'. - * - * For some unknown reason, the RESET line isn't actually connected - * to anything. This means to reset the dongle to get it to a known - * state (9600 baud) you must drop DTR and RTS low, wait for the power - * capacitor to discharge, and then bring DTR (and RTS for data mode) - * high again, and wait for the capacitor to charge, the power supply - * to stabilise, and the oscillator clock to stabilise. - * - * Fortunately, if the current baudrate is known, the chipset can - * easily change speed by entering command mode without having to - * reset the dongle first. - * - * Major Components: - * - * - Vishay/Temic TOIM3232 SIR Endec to change RS232 pulse timings - * to IRDA pulse timings - * - 3.6864MHz crystal to drive TOIM3232 clock oscillator - * - DM74lS04M Inverting Hex line buffer for RS232 input buffering - * and level conversion - * - PJ2951AC 150mA voltage regulator - * - Vishay/Temic TFDS4500 SIR IRDA front-end transceiver - * - */ - -#include -#include -#include -#include - -#include - -#include "sir-dev.h" - -static int toim3232delay = 150; /* default is 150 ms */ -module_param(toim3232delay, int, 0); -MODULE_PARM_DESC(toim3232delay, "toim3232 dongle write complete delay"); - -static int toim3232_open(struct sir_dev *); -static int toim3232_close(struct sir_dev *); -static int toim3232_change_speed(struct sir_dev *, unsigned); -static int toim3232_reset(struct sir_dev *); - -#define TOIM3232_115200 0x00 -#define TOIM3232_57600 0x01 -#define TOIM3232_38400 0x02 -#define TOIM3232_19200 0x03 -#define TOIM3232_9600 0x06 -#define TOIM3232_2400 0x0A - -#define TOIM3232_PW 0x10 /* Pulse select bit */ - -static struct dongle_driver toim3232 = { - .owner = THIS_MODULE, - .driver_name = "Vishay TOIM3232", - .type = IRDA_TOIM3232_DONGLE, - .open = toim3232_open, - .close = toim3232_close, - .reset = toim3232_reset, - .set_speed = toim3232_change_speed, -}; - -static int __init toim3232_sir_init(void) -{ - if (toim3232delay < 1 || toim3232delay > 500) - toim3232delay = 200; - pr_debug("%s - using %d ms delay\n", - toim3232.driver_name, toim3232delay); - return irda_register_dongle(&toim3232); -} - -static void __exit toim3232_sir_cleanup(void) -{ - irda_unregister_dongle(&toim3232); -} - -static int toim3232_open(struct sir_dev *dev) -{ - struct qos_info *qos = &dev->qos; - - /* Pull the lines high to start with. - * - * For the IR320ST-2, we need to charge the main supply capacitor to - * switch the device on. We keep DTR high throughout to do this. - * When RTS, TD and RD are high, they will also trickle-charge the - * cap. RTS is high for data transmission, and low for baud rate select. - * -- DGB - */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* The TOI3232 supports many speeds between 1200bps and 115000bps. - * We really only care about those supported by the IRDA spec, but - * 38400 seems to be implemented in many places */ - qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - - /* From the tekram driver. Not sure what a reasonable value is -- DGB */ - qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ - irda_qos_bits_to_value(qos); - - /* irda thread waits 50 msec for power settling */ - - return 0; -} - -static int toim3232_close(struct sir_dev *dev) -{ - /* Power off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - return 0; -} - -/* - * Function toim3232change_speed (dev, state, speed) - * - * Set the speed for the TOIM3232 based dongle. Warning, this - * function must be called with a process context! - * - * Algorithm - * 1. keep DTR high but clear RTS to bring into baud programming mode - * 2. wait at least 7us to enter programming mode - * 3. send control word to set baud rate and timing - * 4. wait at least 1us - * 5. bring RTS high to enter DATA mode (RS232 is passed through to transceiver) - * 6. should take effect immediately (although probably worth waiting) - */ - -#define TOIM3232_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) - -static int toim3232_change_speed(struct sir_dev *dev, unsigned speed) -{ - unsigned state = dev->fsm.substate; - unsigned delay = 0; - u8 byte; - static int ret = 0; - - switch(state) { - case SIRDEV_STATE_DONGLE_SPEED: - - /* Figure out what we are going to send as a control byte */ - switch (speed) { - case 2400: - byte = TOIM3232_PW|TOIM3232_2400; - break; - default: - speed = 9600; - ret = -EINVAL; - /* fall thru */ - case 9600: - byte = TOIM3232_PW|TOIM3232_9600; - break; - case 19200: - byte = TOIM3232_PW|TOIM3232_19200; - break; - case 38400: - byte = TOIM3232_PW|TOIM3232_38400; - break; - case 57600: - byte = TOIM3232_PW|TOIM3232_57600; - break; - case 115200: - byte = TOIM3232_115200; - break; - } - - /* Set DTR, Clear RTS: Go into baud programming mode */ - sirdev_set_dtr_rts(dev, TRUE, FALSE); - - /* Wait at least 7us */ - udelay(14); - - /* Write control byte */ - sirdev_raw_write(dev, &byte, 1); - - dev->speed = speed; - - state = TOIM3232_STATE_WAIT_SPEED; - delay = toim3232delay; - break; - - case TOIM3232_STATE_WAIT_SPEED: - /* Have transmitted control byte * Wait for 'at least 1us' */ - udelay(14); - - /* Set DTR, Set RTS: Go into normal data mode */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Wait (TODO: check this is needed) */ - udelay(50); - break; - - default: - printk(KERN_ERR "%s - undefined state %d\n", __func__, state); - ret = -EINVAL; - break; - } - - dev->fsm.substate = state; - return (delay > 0) ? delay : ret; -} - -/* - * Function toim3232reset (driver) - * - * This function resets the toim3232 dongle. Warning, this function - * must be called with a process context!! - * - * What we should do is: - * 0. Pull RESET high - * 1. Wait for at least 7us - * 2. Pull RESET low - * 3. Wait for at least 7us - * 4. Pull BR/~D high - * 5. Wait for at least 7us - * 6. Send control byte to set baud rate - * 7. Wait at least 1us after stop bit - * 8. Pull BR/~D low - * 9. Should then be in data mode - * - * Because the IR320ST-2 doesn't have the RESET line connected for some reason, - * we'll have to do something else. - * - * The default speed after a RESET is 9600, so lets try just bringing it up in - * data mode after switching it off, waiting for the supply capacitor to - * discharge, and then switch it back on. This isn't actually pulling RESET - * high, but it seems to have the same effect. - * - * This behaviour will probably work on dongles that have the RESET line connected, - * but if not, add a flag for the IR320ST-2, and implment the above-listed proper - * behaviour. - * - * RTS is inverted and then fed to BR/~D, so to put it in programming mode, we - * need to have pull RTS low - */ - -static int toim3232_reset(struct sir_dev *dev) -{ - /* Switch off both DTR and RTS to switch off dongle */ - sirdev_set_dtr_rts(dev, FALSE, FALSE); - - /* Should sleep a while. This might be evil doing it this way.*/ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(50)); - - /* Set DTR, Set RTS (data mode) */ - sirdev_set_dtr_rts(dev, TRUE, TRUE); - - /* Wait at least 10 ms for power to stabilize again */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(10)); - - /* Speed should now be 9600 */ - dev->speed = 9600; - - return 0; -} - -MODULE_AUTHOR("David Basden "); -MODULE_DESCRIPTION("Vishay/Temic TOIM3232 based dongle driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("irda-dongle-12"); /* IRDA_TOIM3232_DONGLE */ - -module_init(toim3232_sir_init); -module_exit(toim3232_sir_cleanup); diff --git a/drivers/staging/irda/drivers/via-ircc.c b/drivers/staging/irda/drivers/via-ircc.c deleted file mode 100644 index ca4442a9d631..000000000000 --- a/drivers/staging/irda/drivers/via-ircc.c +++ /dev/null @@ -1,1593 +0,0 @@ -/******************************************************************** - Filename: via-ircc.c - Version: 1.0 - Description: Driver for the VIA VT8231/VT8233 IrDA chipsets - Author: VIA Technologies,inc - Date : 08/06/2003 - -Copyright (c) 1998-2003 VIA Technologies, Inc. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 2, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTIES OR REPRESENTATIONS; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, see . - -F01 Oct/02/02: Modify code for V0.11(move out back to back transfer) -F02 Oct/28/02: Add SB device ID for 3147 and 3177. - Comment : - jul/09/2002 : only implement two kind of dongle currently. - Oct/02/2002 : work on VT8231 and VT8233 . - Aug/06/2003 : change driver format to pci driver . - -2004-02-16: -- Removed unneeded 'legacy' pci stuff. -- Make sure SIR mode is set (hw_init()) before calling mode-dependent stuff. -- On speed change from core, don't send SIR frame with new speed. - Use current speed and change speeds later. -- Make module-param dongle_id actually work. -- New dongle_id 17 (0x11): TDFS4500. Single-ended SIR only. - Tested with home-grown PCB on EPIA boards. -- Code cleanup. - - ********************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include "via-ircc.h" - -#define VIA_MODULE_NAME "via-ircc" -#define CHIP_IO_EXTENT 0x40 - -static char *driver_name = VIA_MODULE_NAME; - -/* Module parameters */ -static int qos_mtt_bits = 0x07; /* 1 ms or more */ -static int dongle_id = 0; /* default: probe */ - -/* We can't guess the type of connected dongle, user *must* supply it. */ -module_param(dongle_id, int, 0); - -/* Some prototypes */ -static int via_ircc_open(struct pci_dev *pdev, chipio_t *info, - unsigned int id); -static int via_ircc_dma_receive(struct via_ircc_cb *self); -static int via_ircc_dma_receive_complete(struct via_ircc_cb *self, - int iobase); -static netdev_tx_t via_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev); -static netdev_tx_t via_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev); -static void via_hw_init(struct via_ircc_cb *self); -static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 baud); -static irqreturn_t via_ircc_interrupt(int irq, void *dev_id); -static int via_ircc_is_receiving(struct via_ircc_cb *self); -static int via_ircc_read_dongle_id(int iobase); - -static int via_ircc_net_open(struct net_device *dev); -static int via_ircc_net_close(struct net_device *dev); -static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, - int cmd); -static void via_ircc_change_dongle_speed(int iobase, int speed, - int dongle_id); -static int RxTimerHandler(struct via_ircc_cb *self, int iobase); -static void hwreset(struct via_ircc_cb *self); -static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase); -static int upload_rxdata(struct via_ircc_cb *self, int iobase); -static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id); -static void via_remove_one(struct pci_dev *pdev); - -/* FIXME : Should use udelay() instead, even if we are x86 only - Jean II */ -static void iodelay(int udelay) -{ - u8 data; - int i; - - for (i = 0; i < udelay; i++) { - data = inb(0x80); - } -} - -static const struct pci_device_id via_pci_tbl[] = { - { PCI_VENDOR_ID_VIA, 0x8231, PCI_ANY_ID, PCI_ANY_ID,0,0,0 }, - { PCI_VENDOR_ID_VIA, 0x3109, PCI_ANY_ID, PCI_ANY_ID,0,0,1 }, - { PCI_VENDOR_ID_VIA, 0x3074, PCI_ANY_ID, PCI_ANY_ID,0,0,2 }, - { PCI_VENDOR_ID_VIA, 0x3147, PCI_ANY_ID, PCI_ANY_ID,0,0,3 }, - { PCI_VENDOR_ID_VIA, 0x3177, PCI_ANY_ID, PCI_ANY_ID,0,0,4 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci,via_pci_tbl); - - -static struct pci_driver via_driver = { - .name = VIA_MODULE_NAME, - .id_table = via_pci_tbl, - .probe = via_init_one, - .remove = via_remove_one, -}; - - -/* - * Function via_ircc_init () - * - * Initialize chip. Just find out chip type and resource. - */ -static int __init via_ircc_init(void) -{ - int rc; - - rc = pci_register_driver(&via_driver); - if (rc < 0) { - pr_debug("%s(): error rc = %d, returning -ENODEV...\n", - __func__, rc); - return -ENODEV; - } - return 0; -} - -static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id) -{ - int rc; - u8 temp,oldPCI_40,oldPCI_44,bTmp,bTmp1; - u16 Chipset,FirDRQ1,FirDRQ0,FirIRQ,FirIOBase; - chipio_t info; - - pr_debug("%s(): Device ID=(0X%X)\n", __func__, id->device); - - rc = pci_enable_device (pcidev); - if (rc) { - pr_debug("%s(): error rc = %d\n", __func__, rc); - return -ENODEV; - } - - // South Bridge exist - if ( ReadLPCReg(0x20) != 0x3C ) - Chipset=0x3096; - else - Chipset=0x3076; - - if (Chipset==0x3076) { - pr_debug("%s(): Chipset = 3076\n", __func__); - - WriteLPCReg(7,0x0c ); - temp=ReadLPCReg(0x30);//check if BIOS Enable Fir - if((temp&0x01)==1) { // BIOS close or no FIR - WriteLPCReg(0x1d, 0x82 ); - WriteLPCReg(0x23,0x18); - temp=ReadLPCReg(0xF0); - if((temp&0x01)==0) { - temp=(ReadLPCReg(0x74)&0x03); //DMA - FirDRQ0=temp + 4; - temp=(ReadLPCReg(0x74)&0x0C) >> 2; - FirDRQ1=temp + 4; - } else { - temp=(ReadLPCReg(0x74)&0x0C) >> 2; //DMA - FirDRQ0=temp + 4; - FirDRQ1=FirDRQ0; - } - FirIRQ=(ReadLPCReg(0x70)&0x0f); //IRQ - FirIOBase=ReadLPCReg(0x60 ) << 8; //IO Space :high byte - FirIOBase=FirIOBase| ReadLPCReg(0x61) ; //low byte - FirIOBase=FirIOBase ; - info.fir_base=FirIOBase; - info.irq=FirIRQ; - info.dma=FirDRQ1; - info.dma2=FirDRQ0; - pci_read_config_byte(pcidev,0x40,&bTmp); - pci_write_config_byte(pcidev,0x40,((bTmp | 0x08) & 0xfe)); - pci_read_config_byte(pcidev,0x42,&bTmp); - pci_write_config_byte(pcidev,0x42,(bTmp | 0xf0)); - pci_write_config_byte(pcidev,0x5a,0xc0); - WriteLPCReg(0x28, 0x70 ); - rc = via_ircc_open(pcidev, &info, 0x3076); - } else - rc = -ENODEV; //IR not turn on - } else { //Not VT1211 - pr_debug("%s(): Chipset = 3096\n", __func__); - - pci_read_config_byte(pcidev,0x67,&bTmp);//check if BIOS Enable Fir - if((bTmp&0x01)==1) { // BIOS enable FIR - //Enable Double DMA clock - pci_read_config_byte(pcidev,0x42,&oldPCI_40); - pci_write_config_byte(pcidev,0x42,oldPCI_40 | 0x80); - pci_read_config_byte(pcidev,0x40,&oldPCI_40); - pci_write_config_byte(pcidev,0x40,oldPCI_40 & 0xf7); - pci_read_config_byte(pcidev,0x44,&oldPCI_44); - pci_write_config_byte(pcidev,0x44,0x4e); - //---------- read configuration from Function0 of south bridge - if((bTmp&0x02)==0) { - pci_read_config_byte(pcidev,0x44,&bTmp1); //DMA - FirDRQ0 = (bTmp1 & 0x30) >> 4; - pci_read_config_byte(pcidev,0x44,&bTmp1); - FirDRQ1 = (bTmp1 & 0xc0) >> 6; - } else { - pci_read_config_byte(pcidev,0x44,&bTmp1); //DMA - FirDRQ0 = (bTmp1 & 0x30) >> 4 ; - FirDRQ1=0; - } - pci_read_config_byte(pcidev,0x47,&bTmp1); //IRQ - FirIRQ = bTmp1 & 0x0f; - - pci_read_config_byte(pcidev,0x69,&bTmp); - FirIOBase = bTmp << 8;//hight byte - pci_read_config_byte(pcidev,0x68,&bTmp); - FirIOBase = (FirIOBase | bTmp ) & 0xfff0; - //------------------------- - info.fir_base=FirIOBase; - info.irq=FirIRQ; - info.dma=FirDRQ1; - info.dma2=FirDRQ0; - rc = via_ircc_open(pcidev, &info, 0x3096); - } else - rc = -ENODEV; //IR not turn on !!!!! - }//Not VT1211 - - pr_debug("%s(): End - rc = %d\n", __func__, rc); - return rc; -} - -static void __exit via_ircc_cleanup(void) -{ - /* Cleanup all instances of the driver */ - pci_unregister_driver (&via_driver); -} - -static const struct net_device_ops via_ircc_sir_ops = { - .ndo_start_xmit = via_ircc_hard_xmit_sir, - .ndo_open = via_ircc_net_open, - .ndo_stop = via_ircc_net_close, - .ndo_do_ioctl = via_ircc_net_ioctl, -}; -static const struct net_device_ops via_ircc_fir_ops = { - .ndo_start_xmit = via_ircc_hard_xmit_fir, - .ndo_open = via_ircc_net_open, - .ndo_stop = via_ircc_net_close, - .ndo_do_ioctl = via_ircc_net_ioctl, -}; - -/* - * Function via_ircc_open(pdev, iobase, irq) - * - * Open driver instance - * - */ -static int via_ircc_open(struct pci_dev *pdev, chipio_t *info, unsigned int id) -{ - struct net_device *dev; - struct via_ircc_cb *self; - int err; - - /* Allocate new instance of the driver */ - dev = alloc_irdadev(sizeof(struct via_ircc_cb)); - if (dev == NULL) - return -ENOMEM; - - self = netdev_priv(dev); - self->netdev = dev; - spin_lock_init(&self->lock); - - pci_set_drvdata(pdev, self); - - /* Initialize Resource */ - self->io.cfg_base = info->cfg_base; - self->io.fir_base = info->fir_base; - self->io.irq = info->irq; - self->io.fir_ext = CHIP_IO_EXTENT; - self->io.dma = info->dma; - self->io.dma2 = info->dma2; - self->io.fifo_size = 32; - self->chip_id = id; - self->st_fifo.len = 0; - self->RxDataReady = 0; - - /* Reserve the ioports that we need */ - if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) { - pr_debug("%s(), can't get iobase of 0x%03x\n", - __func__, self->io.fir_base); - err = -ENODEV; - goto err_out1; - } - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - /* Check if user has supplied the dongle id or not */ - if (!dongle_id) - dongle_id = via_ircc_read_dongle_id(self->io.fir_base); - self->io.dongle_id = dongle_id; - - /* The only value we must override it the baudrate */ - /* Maximum speeds and capabilities are dongle-dependent. */ - switch( self->io.dongle_id ){ - case 0x0d: - self->qos.baud_rate.bits = - IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 | - IR_576000 | IR_1152000 | (IR_4000000 << 8); - break; - default: - self->qos.baud_rate.bits = - IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200; - break; - } - - /* Following was used for testing: - * - * self->qos.baud_rate.bits = IR_9600; - * - * Is is no good, as it prohibits (error-prone) speed-changes. - */ - - self->qos.min_turn_time.bits = qos_mtt_bits; - irda_qos_bits_to_value(&self->qos); - - /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ - self->rx_buff.truesize = 14384 + 2048; - self->tx_buff.truesize = 14384 + 2048; - - /* Allocate memory if needed */ - self->rx_buff.head = - dma_zalloc_coherent(&pdev->dev, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL); - if (self->rx_buff.head == NULL) { - err = -ENOMEM; - goto err_out2; - } - - self->tx_buff.head = - dma_zalloc_coherent(&pdev->dev, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL); - if (self->tx_buff.head == NULL) { - err = -ENOMEM; - goto err_out3; - } - - self->rx_buff.in_frame = FALSE; - self->rx_buff.state = OUTSIDE_FRAME; - self->tx_buff.data = self->tx_buff.head; - self->rx_buff.data = self->rx_buff.head; - - /* Reset Tx queue info */ - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - - /* Override the network functions we need to use */ - dev->netdev_ops = &via_ircc_sir_ops; - - err = register_netdev(dev); - if (err) - goto err_out4; - - net_info_ratelimited("IrDA: Registered device %s (via-ircc)\n", - dev->name); - - /* Initialise the hardware.. - */ - self->io.speed = 9600; - via_hw_init(self); - return 0; - err_out4: - dma_free_coherent(&pdev->dev, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - err_out3: - dma_free_coherent(&pdev->dev, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - err_out2: - release_region(self->io.fir_base, self->io.fir_ext); - err_out1: - free_netdev(dev); - return err; -} - -/* - * Function via_remove_one(pdev) - * - * Close driver instance - * - */ -static void via_remove_one(struct pci_dev *pdev) -{ - struct via_ircc_cb *self = pci_get_drvdata(pdev); - int iobase; - - iobase = self->io.fir_base; - - ResetChip(iobase, 5); //hardware reset. - /* Remove netdevice */ - unregister_netdev(self->netdev); - - /* Release the PORT that this driver is using */ - pr_debug("%s(), Releasing Region %03x\n", - __func__, self->io.fir_base); - release_region(self->io.fir_base, self->io.fir_ext); - if (self->tx_buff.head) - dma_free_coherent(&pdev->dev, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - if (self->rx_buff.head) - dma_free_coherent(&pdev->dev, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - - free_netdev(self->netdev); - - pci_disable_device(pdev); -} - -/* - * Function via_hw_init(self) - * - * Returns non-negative on success. - * - * Formerly via_ircc_setup - */ -static void via_hw_init(struct via_ircc_cb *self) -{ - int iobase = self->io.fir_base; - - SetMaxRxPacketSize(iobase, 0x0fff); //set to max:4095 - // FIFO Init - EnRXFIFOReadyInt(iobase, OFF); - EnRXFIFOHalfLevelInt(iobase, OFF); - EnTXFIFOHalfLevelInt(iobase, OFF); - EnTXFIFOUnderrunEOMInt(iobase, ON); - EnTXFIFOReadyInt(iobase, OFF); - InvertTX(iobase, OFF); - InvertRX(iobase, OFF); - - if (ReadLPCReg(0x20) == 0x3c) - WriteLPCReg(0xF0, 0); // for VT1211 - /* Int Init */ - EnRXSpecInt(iobase, ON); - - /* The following is basically hwreset */ - /* If this is the case, why not just call hwreset() ? Jean II */ - ResetChip(iobase, 5); - EnableDMA(iobase, OFF); - EnableTX(iobase, OFF); - EnableRX(iobase, OFF); - EnRXDMA(iobase, OFF); - EnTXDMA(iobase, OFF); - RXStart(iobase, OFF); - TXStart(iobase, OFF); - InitCard(iobase); - CommonInit(iobase); - SIRFilter(iobase, ON); - SetSIR(iobase, ON); - CRC16(iobase, ON); - EnTXCRC(iobase, 0); - WriteReg(iobase, I_ST_CT_0, 0x00); - SetBaudRate(iobase, 9600); - SetPulseWidth(iobase, 12); - SetSendPreambleCount(iobase, 0); - - self->io.speed = 9600; - self->st_fifo.len = 0; - - via_ircc_change_dongle_speed(iobase, self->io.speed, - self->io.dongle_id); - - WriteReg(iobase, I_ST_CT_0, 0x80); -} - -/* - * Function via_ircc_read_dongle_id (void) - * - */ -static int via_ircc_read_dongle_id(int iobase) -{ - net_err_ratelimited("via-ircc: dongle probing not supported, please specify dongle_id module parameter\n"); - return 9; /* Default to IBM */ -} - -/* - * Function via_ircc_change_dongle_speed (iobase, speed, dongle_id) - * Change speed of the attach dongle - * only implement two type of dongle currently. - */ -static void via_ircc_change_dongle_speed(int iobase, int speed, - int dongle_id) -{ - u8 mode = 0; - - /* speed is unused, as we use IsSIROn()/IsMIROn() */ - speed = speed; - - pr_debug("%s(): change_dongle_speed to %d for 0x%x, %d\n", - __func__, speed, iobase, dongle_id); - - switch (dongle_id) { - - /* Note: The dongle_id's listed here are derived from - * nsc-ircc.c */ - - case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ - UseOneRX(iobase, ON); // use one RX pin RX1,RX2 - InvertTX(iobase, OFF); - InvertRX(iobase, OFF); - - EnRX2(iobase, ON); //sir to rx2 - EnGPIOtoRX2(iobase, OFF); - - if (IsSIROn(iobase)) { //sir - // Mode select Off - SlowIRRXLowActive(iobase, ON); - udelay(1000); - SlowIRRXLowActive(iobase, OFF); - } else { - if (IsMIROn(iobase)) { //mir - // Mode select On - SlowIRRXLowActive(iobase, OFF); - udelay(20); - } else { // fir - if (IsFIROn(iobase)) { //fir - // Mode select On - SlowIRRXLowActive(iobase, OFF); - udelay(20); - } - } - } - break; - - case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ - UseOneRX(iobase, ON); //use ONE RX....RX1 - InvertTX(iobase, OFF); - InvertRX(iobase, OFF); // invert RX pin - - EnRX2(iobase, ON); - EnGPIOtoRX2(iobase, OFF); - if (IsSIROn(iobase)) { //sir - // Mode select On - SlowIRRXLowActive(iobase, ON); - udelay(20); - // Mode select Off - SlowIRRXLowActive(iobase, OFF); - } - if (IsMIROn(iobase)) { //mir - // Mode select On - SlowIRRXLowActive(iobase, OFF); - udelay(20); - // Mode select Off - SlowIRRXLowActive(iobase, ON); - } else { // fir - if (IsFIROn(iobase)) { //fir - // Mode select On - SlowIRRXLowActive(iobase, OFF); - // TX On - WriteTX(iobase, ON); - udelay(20); - // Mode select OFF - SlowIRRXLowActive(iobase, ON); - udelay(20); - // TX Off - WriteTX(iobase, OFF); - } - } - break; - - case 0x0d: - UseOneRX(iobase, OFF); // use two RX pin RX1,RX2 - InvertTX(iobase, OFF); - InvertRX(iobase, OFF); - SlowIRRXLowActive(iobase, OFF); - if (IsSIROn(iobase)) { //sir - EnGPIOtoRX2(iobase, OFF); - WriteGIO(iobase, OFF); - EnRX2(iobase, OFF); //sir to rx2 - } else { // fir mir - EnGPIOtoRX2(iobase, OFF); - WriteGIO(iobase, OFF); - EnRX2(iobase, OFF); //fir to rx - } - break; - - case 0x11: /* Temic TFDS4500 */ - - pr_debug("%s: Temic TFDS4500: One RX pin, TX normal, RX inverted\n", - __func__); - - UseOneRX(iobase, ON); //use ONE RX....RX1 - InvertTX(iobase, OFF); - InvertRX(iobase, ON); // invert RX pin - - EnRX2(iobase, ON); //sir to rx2 - EnGPIOtoRX2(iobase, OFF); - - if( IsSIROn(iobase) ){ //sir - - // Mode select On - SlowIRRXLowActive(iobase, ON); - udelay(20); - // Mode select Off - SlowIRRXLowActive(iobase, OFF); - - } else{ - pr_debug("%s: Warning: TFDS4500 not running in SIR mode !\n", - __func__); - } - break; - - case 0x0ff: /* Vishay */ - if (IsSIROn(iobase)) - mode = 0; - else if (IsMIROn(iobase)) - mode = 1; - else if (IsFIROn(iobase)) - mode = 2; - else if (IsVFIROn(iobase)) - mode = 5; //VFIR-16 - SI_SetMode(iobase, mode); - break; - - default: - net_err_ratelimited("%s: Error: dongle_id %d unsupported !\n", - __func__, dongle_id); - } -} - -/* - * Function via_ircc_change_speed (self, baud) - * - * Change the speed of the device - * - */ -static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 speed) -{ - struct net_device *dev = self->netdev; - u16 iobase; - u8 value = 0, bTmp; - - iobase = self->io.fir_base; - /* Update accounting for new speed */ - self->io.speed = speed; - pr_debug("%s: change_speed to %d bps.\n", __func__, speed); - - WriteReg(iobase, I_ST_CT_0, 0x0); - - /* Controller mode sellection */ - switch (speed) { - case 2400: - case 9600: - case 19200: - case 38400: - case 57600: - case 115200: - value = (115200/speed)-1; - SetSIR(iobase, ON); - CRC16(iobase, ON); - break; - case 576000: - /* FIXME: this can't be right, as it's the same as 115200, - * and 576000 is MIR, not SIR. */ - value = 0; - SetSIR(iobase, ON); - CRC16(iobase, ON); - break; - case 1152000: - value = 0; - SetMIR(iobase, ON); - /* FIXME: CRC ??? */ - break; - case 4000000: - value = 0; - SetFIR(iobase, ON); - SetPulseWidth(iobase, 0); - SetSendPreambleCount(iobase, 14); - CRC16(iobase, OFF); - EnTXCRC(iobase, ON); - break; - case 16000000: - value = 0; - SetVFIR(iobase, ON); - /* FIXME: CRC ??? */ - break; - default: - value = 0; - break; - } - - /* Set baudrate to 0x19[2..7] */ - bTmp = (ReadReg(iobase, I_CF_H_1) & 0x03); - bTmp |= value << 2; - WriteReg(iobase, I_CF_H_1, bTmp); - - /* Some dongles may need to be informed about speed changes. */ - via_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id); - - /* Set FIFO size to 64 */ - SetFIFO(iobase, 64); - - /* Enable IR */ - WriteReg(iobase, I_ST_CT_0, 0x80); - - // EnTXFIFOHalfLevelInt(iobase,ON); - - /* Enable some interrupts so we can receive frames */ - //EnAllInt(iobase,ON); - - if (IsSIROn(iobase)) { - SIRFilter(iobase, ON); - SIRRecvAny(iobase, ON); - } else { - SIRFilter(iobase, OFF); - SIRRecvAny(iobase, OFF); - } - - if (speed > 115200) { - /* Install FIR xmit handler */ - dev->netdev_ops = &via_ircc_fir_ops; - via_ircc_dma_receive(self); - } else { - /* Install SIR xmit handler */ - dev->netdev_ops = &via_ircc_sir_ops; - } - netif_wake_queue(dev); -} - -/* - * Function via_ircc_hard_xmit (skb, dev) - * - * Transmit the frame! - * - */ -static netdev_tx_t via_ircc_hard_xmit_sir(struct sk_buff *skb, - struct net_device *dev) -{ - struct via_ircc_cb *self; - unsigned long flags; - u16 iobase; - __u32 speed; - - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;); - iobase = self->io.fir_base; - - netif_stop_queue(dev); - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame */ - if (!skb->len) { - via_ircc_change_speed(self, speed); - netif_trans_update(dev); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else - self->new_speed = speed; - } - InitCard(iobase); - CommonInit(iobase); - SIRFilter(iobase, ON); - SetSIR(iobase, ON); - CRC16(iobase, ON); - EnTXCRC(iobase, 0); - WriteReg(iobase, I_ST_CT_0, 0x00); - - spin_lock_irqsave(&self->lock, flags); - self->tx_buff.data = self->tx_buff.head; - self->tx_buff.len = - async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - dev->stats.tx_bytes += self->tx_buff.len; - /* Send this frame with old speed */ - SetBaudRate(iobase, self->io.speed); - SetPulseWidth(iobase, 12); - SetSendPreambleCount(iobase, 0); - WriteReg(iobase, I_ST_CT_0, 0x80); - - EnableTX(iobase, ON); - EnableRX(iobase, OFF); - - ResetChip(iobase, 0); - ResetChip(iobase, 1); - ResetChip(iobase, 2); - ResetChip(iobase, 3); - ResetChip(iobase, 4); - - EnAllInt(iobase, ON); - EnTXDMA(iobase, ON); - EnRXDMA(iobase, OFF); - - irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, - DMA_TX_MODE); - - SetSendByte(iobase, self->tx_buff.len); - RXStart(iobase, OFF); - TXStart(iobase, ON); - - netif_trans_update(dev); - spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -static netdev_tx_t via_ircc_hard_xmit_fir(struct sk_buff *skb, - struct net_device *dev) -{ - struct via_ircc_cb *self; - u16 iobase; - __u32 speed; - unsigned long flags; - - self = netdev_priv(dev); - iobase = self->io.fir_base; - - if (self->st_fifo.len) - return NETDEV_TX_OK; - if (self->chip_id == 0x3076) - iodelay(1500); - else - udelay(1500); - netif_stop_queue(dev); - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - if (!skb->len) { - via_ircc_change_speed(self, speed); - netif_trans_update(dev); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } else - self->new_speed = speed; - } - spin_lock_irqsave(&self->lock, flags); - self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; - self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; - - self->tx_fifo.tail += skb->len; - dev->stats.tx_bytes += skb->len; - skb_copy_from_linear_data(skb, - self->tx_fifo.queue[self->tx_fifo.free].start, skb->len); - self->tx_fifo.len++; - self->tx_fifo.free++; -//F01 if (self->tx_fifo.len == 1) { - via_ircc_dma_xmit(self, iobase); -//F01 } -//F01 if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) netif_wake_queue(self->netdev); - netif_trans_update(dev); - dev_kfree_skb(skb); - spin_unlock_irqrestore(&self->lock, flags); - return NETDEV_TX_OK; - -} - -static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase) -{ - EnTXDMA(iobase, OFF); - self->io.direction = IO_XMIT; - EnPhys(iobase, ON); - EnableTX(iobase, ON); - EnableRX(iobase, OFF); - ResetChip(iobase, 0); - ResetChip(iobase, 1); - ResetChip(iobase, 2); - ResetChip(iobase, 3); - ResetChip(iobase, 4); - EnAllInt(iobase, ON); - EnTXDMA(iobase, ON); - EnRXDMA(iobase, OFF); - irda_setup_dma(self->io.dma, - ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - - self->tx_buff.head) + self->tx_buff_dma, - self->tx_fifo.queue[self->tx_fifo.ptr].len, DMA_TX_MODE); - pr_debug("%s: tx_fifo.ptr=%x,len=%x,tx_fifo.len=%x..\n", - __func__, self->tx_fifo.ptr, - self->tx_fifo.queue[self->tx_fifo.ptr].len, - self->tx_fifo.len); - - SetSendByte(iobase, self->tx_fifo.queue[self->tx_fifo.ptr].len); - RXStart(iobase, OFF); - TXStart(iobase, ON); - return 0; - -} - -/* - * Function via_ircc_dma_xmit_complete (self) - * - * The transfer of a frame in finished. This function will only be called - * by the interrupt handler - * - */ -static int via_ircc_dma_xmit_complete(struct via_ircc_cb *self) -{ - int iobase; - u8 Tx_status; - - iobase = self->io.fir_base; - /* Disable DMA */ -// DisableDmaChannel(self->io.dma); - /* Check for underrun! */ - /* Clear bit, by writing 1 into it */ - Tx_status = GetTXStatus(iobase); - if (Tx_status & 0x08) { - self->netdev->stats.tx_errors++; - self->netdev->stats.tx_fifo_errors++; - hwreset(self); - /* how to clear underrun? */ - } else { - self->netdev->stats.tx_packets++; - ResetChip(iobase, 3); - ResetChip(iobase, 4); - } - /* Check if we need to change the speed */ - if (self->new_speed) { - via_ircc_change_speed(self, self->new_speed); - self->new_speed = 0; - } - - /* Finished with this frame, so prepare for next */ - if (IsFIROn(iobase)) { - if (self->tx_fifo.len) { - self->tx_fifo.len--; - self->tx_fifo.ptr++; - } - } - pr_debug("%s: tx_fifo.len=%x ,tx_fifo.ptr=%x,tx_fifo.free=%x...\n", - __func__, - self->tx_fifo.len, self->tx_fifo.ptr, self->tx_fifo.free); -/* F01_S - // Any frames to be sent back-to-back? - if (self->tx_fifo.len) { - // Not finished yet! - via_ircc_dma_xmit(self, iobase); - ret = FALSE; - } else { -F01_E*/ - // Reset Tx FIFO info - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; -//F01 } - - // Make sure we have room for more frames -//F01 if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) { - // Not busy transmitting anymore - // Tell the network layer, that we can accept more frames - netif_wake_queue(self->netdev); -//F01 } - return TRUE; -} - -/* - * Function via_ircc_dma_receive (self) - * - * Set configuration for receive a frame. - * - */ -static int via_ircc_dma_receive(struct via_ircc_cb *self) -{ - int iobase; - - iobase = self->io.fir_base; - - self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; - self->tx_fifo.tail = self->tx_buff.head; - self->RxDataReady = 0; - self->io.direction = IO_RECV; - self->rx_buff.data = self->rx_buff.head; - self->st_fifo.len = self->st_fifo.pending_bytes = 0; - self->st_fifo.tail = self->st_fifo.head = 0; - - EnPhys(iobase, ON); - EnableTX(iobase, OFF); - EnableRX(iobase, ON); - - ResetChip(iobase, 0); - ResetChip(iobase, 1); - ResetChip(iobase, 2); - ResetChip(iobase, 3); - ResetChip(iobase, 4); - - EnAllInt(iobase, ON); - EnTXDMA(iobase, OFF); - EnRXDMA(iobase, ON); - irda_setup_dma(self->io.dma2, self->rx_buff_dma, - self->rx_buff.truesize, DMA_RX_MODE); - TXStart(iobase, OFF); - RXStart(iobase, ON); - - return 0; -} - -/* - * Function via_ircc_dma_receive_complete (self) - * - * Controller Finished with receiving frames, - * and this routine is call by ISR - * - */ -static int via_ircc_dma_receive_complete(struct via_ircc_cb *self, - int iobase) -{ - struct st_fifo *st_fifo; - struct sk_buff *skb; - int len, i; - u8 status = 0; - - iobase = self->io.fir_base; - st_fifo = &self->st_fifo; - - if (self->io.speed < 4000000) { //Speed below FIR - len = GetRecvByte(iobase, self); - skb = dev_alloc_skb(len + 1); - if (skb == NULL) - return FALSE; - // Make sure IP header gets aligned - skb_reserve(skb, 1); - skb_put(skb, len - 2); - if (self->chip_id == 0x3076) { - for (i = 0; i < len - 2; i++) - skb->data[i] = self->rx_buff.data[i * 2]; - } else { - if (self->chip_id == 0x3096) { - for (i = 0; i < len - 2; i++) - skb->data[i] = - self->rx_buff.data[i]; - } - } - // Move to next frame - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - return TRUE; - } - - else { //FIR mode - len = GetRecvByte(iobase, self); - if (len == 0) - return TRUE; //interrupt only, data maybe move by RxT - if (((len - 4) < 2) || ((len - 4) > 2048)) { - pr_debug("%s(): Trouble:len=%x,CurCount=%x,LastCount=%x\n", - __func__, len, RxCurCount(iobase, self), - self->RxLastCount); - hwreset(self); - return FALSE; - } - pr_debug("%s(): fifo.len=%x,len=%x,CurCount=%x..\n", - __func__, - st_fifo->len, len - 4, RxCurCount(iobase, self)); - - st_fifo->entries[st_fifo->tail].status = status; - st_fifo->entries[st_fifo->tail].len = len; - st_fifo->pending_bytes += len; - st_fifo->tail++; - st_fifo->len++; - if (st_fifo->tail > MAX_RX_WINDOW) - st_fifo->tail = 0; - self->RxDataReady = 0; - - // It maybe have MAX_RX_WINDOW package receive by - // receive_complete before Timer IRQ -/* F01_S - if (st_fifo->len < (MAX_RX_WINDOW+2 )) { - RXStart(iobase,ON); - SetTimer(iobase,4); - } - else { -F01_E */ - EnableRX(iobase, OFF); - EnRXDMA(iobase, OFF); - RXStart(iobase, OFF); -//F01_S - // Put this entry back in fifo - if (st_fifo->head > MAX_RX_WINDOW) - st_fifo->head = 0; - status = st_fifo->entries[st_fifo->head].status; - len = st_fifo->entries[st_fifo->head].len; - st_fifo->head++; - st_fifo->len--; - - skb = dev_alloc_skb(len + 1 - 4); - /* - * if frame size, data ptr, or skb ptr are wrong, then get next - * entry. - */ - if ((skb == NULL) || (skb->data == NULL) || - (self->rx_buff.data == NULL) || (len < 6)) { - self->netdev->stats.rx_dropped++; - kfree_skb(skb); - return TRUE; - } - skb_reserve(skb, 1); - skb_put(skb, len - 4); - - skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4); - pr_debug("%s(): len=%x.rx_buff=%p\n", __func__, - len - 4, self->rx_buff.data); - - // Move to next frame - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - -//F01_E - } //FIR - return TRUE; - -} - -/* - * if frame is received , but no INT ,then use this routine to upload frame. - */ -static int upload_rxdata(struct via_ircc_cb *self, int iobase) -{ - struct sk_buff *skb; - int len; - struct st_fifo *st_fifo; - st_fifo = &self->st_fifo; - - len = GetRecvByte(iobase, self); - - pr_debug("%s(): len=%x\n", __func__, len); - - if ((len - 4) < 2) { - self->netdev->stats.rx_dropped++; - return FALSE; - } - - skb = dev_alloc_skb(len + 1); - if (skb == NULL) { - self->netdev->stats.rx_dropped++; - return FALSE; - } - skb_reserve(skb, 1); - skb_put(skb, len - 4 + 1); - skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4 + 1); - st_fifo->tail++; - st_fifo->len++; - if (st_fifo->tail > MAX_RX_WINDOW) - st_fifo->tail = 0; - // Move to next frame - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - if (st_fifo->len < (MAX_RX_WINDOW + 2)) { - RXStart(iobase, ON); - } else { - EnableRX(iobase, OFF); - EnRXDMA(iobase, OFF); - RXStart(iobase, OFF); - } - return TRUE; -} - -/* - * Implement back to back receive , use this routine to upload data. - */ - -static int RxTimerHandler(struct via_ircc_cb *self, int iobase) -{ - struct st_fifo *st_fifo; - struct sk_buff *skb; - int len; - u8 status; - - st_fifo = &self->st_fifo; - - if (CkRxRecv(iobase, self)) { - // if still receiving ,then return ,don't upload frame - self->RetryCount = 0; - SetTimer(iobase, 20); - self->RxDataReady++; - return FALSE; - } else - self->RetryCount++; - - if ((self->RetryCount >= 1) || - ((st_fifo->pending_bytes + 2048) > self->rx_buff.truesize) || - (st_fifo->len >= (MAX_RX_WINDOW))) { - while (st_fifo->len > 0) { //upload frame - // Put this entry back in fifo - if (st_fifo->head > MAX_RX_WINDOW) - st_fifo->head = 0; - status = st_fifo->entries[st_fifo->head].status; - len = st_fifo->entries[st_fifo->head].len; - st_fifo->head++; - st_fifo->len--; - - skb = dev_alloc_skb(len + 1 - 4); - /* - * if frame size, data ptr, or skb ptr are wrong, - * then get next entry. - */ - if ((skb == NULL) || (skb->data == NULL) || - (self->rx_buff.data == NULL) || (len < 6)) { - self->netdev->stats.rx_dropped++; - continue; - } - skb_reserve(skb, 1); - skb_put(skb, len - 4); - skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4); - - pr_debug("%s(): len=%x.head=%x\n", __func__, - len - 4, st_fifo->head); - - // Move to next frame - self->rx_buff.data += len; - self->netdev->stats.rx_bytes += len; - self->netdev->stats.rx_packets++; - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - } //while - self->RetryCount = 0; - - pr_debug("%s(): End of upload HostStatus=%x,RxStatus=%x\n", - __func__, GetHostStatus(iobase), GetRXStatus(iobase)); - - /* - * if frame is receive complete at this routine ,then upload - * frame. - */ - if ((GetRXStatus(iobase) & 0x10) && - (RxCurCount(iobase, self) != self->RxLastCount)) { - upload_rxdata(self, iobase); - if (irda_device_txqueue_empty(self->netdev)) - via_ircc_dma_receive(self); - } - } // timer detect complete - else - SetTimer(iobase, 4); - return TRUE; - -} - - - -/* - * Function via_ircc_interrupt (irq, dev_id) - * - * An interrupt from the chip has arrived. Time to do some work - * - */ -static irqreturn_t via_ircc_interrupt(int dummy, void *dev_id) -{ - struct net_device *dev = dev_id; - struct via_ircc_cb *self = netdev_priv(dev); - int iobase; - u8 iHostIntType, iRxIntType, iTxIntType; - - iobase = self->io.fir_base; - spin_lock(&self->lock); - iHostIntType = GetHostStatus(iobase); - - pr_debug("%s(): iHostIntType %02x: %s %s %s %02x\n", - __func__, iHostIntType, - (iHostIntType & 0x40) ? "Timer" : "", - (iHostIntType & 0x20) ? "Tx" : "", - (iHostIntType & 0x10) ? "Rx" : "", - (iHostIntType & 0x0e) >> 1); - - if ((iHostIntType & 0x40) != 0) { //Timer Event - self->EventFlag.TimeOut++; - ClearTimerInt(iobase, 1); - if (self->io.direction == IO_XMIT) { - via_ircc_dma_xmit(self, iobase); - } - if (self->io.direction == IO_RECV) { - /* - * frame ready hold too long, must reset. - */ - if (self->RxDataReady > 30) { - hwreset(self); - if (irda_device_txqueue_empty(self->netdev)) { - via_ircc_dma_receive(self); - } - } else { // call this to upload frame. - RxTimerHandler(self, iobase); - } - } //RECV - } //Timer Event - if ((iHostIntType & 0x20) != 0) { //Tx Event - iTxIntType = GetTXStatus(iobase); - - pr_debug("%s(): iTxIntType %02x: %s %s %s %s\n", - __func__, iTxIntType, - (iTxIntType & 0x08) ? "FIFO underr." : "", - (iTxIntType & 0x04) ? "EOM" : "", - (iTxIntType & 0x02) ? "FIFO ready" : "", - (iTxIntType & 0x01) ? "Early EOM" : ""); - - if (iTxIntType & 0x4) { - self->EventFlag.EOMessage++; // read and will auto clean - if (via_ircc_dma_xmit_complete(self)) { - if (irda_device_txqueue_empty - (self->netdev)) { - via_ircc_dma_receive(self); - } - } else { - self->EventFlag.Unknown++; - } - } //EOP - } //Tx Event - //---------------------------------------- - if ((iHostIntType & 0x10) != 0) { //Rx Event - /* Check if DMA has finished */ - iRxIntType = GetRXStatus(iobase); - - pr_debug("%s(): iRxIntType %02x: %s %s %s %s %s %s %s\n", - __func__, iRxIntType, - (iRxIntType & 0x80) ? "PHY err." : "", - (iRxIntType & 0x40) ? "CRC err" : "", - (iRxIntType & 0x20) ? "FIFO overr." : "", - (iRxIntType & 0x10) ? "EOF" : "", - (iRxIntType & 0x08) ? "RxData" : "", - (iRxIntType & 0x02) ? "RxMaxLen" : "", - (iRxIntType & 0x01) ? "SIR bad" : ""); - if (!iRxIntType) - pr_debug("%s(): RxIRQ =0\n", __func__); - - if (iRxIntType & 0x10) { - if (via_ircc_dma_receive_complete(self, iobase)) { -//F01 if(!(IsFIROn(iobase))) via_ircc_dma_receive(self); - via_ircc_dma_receive(self); - } - } // No ERR - else { //ERR - pr_debug("%s(): RxIRQ ERR:iRxIntType=%x,HostIntType=%x,CurCount=%x,RxLastCount=%x_____\n", - __func__, iRxIntType, iHostIntType, - RxCurCount(iobase, self), self->RxLastCount); - - if (iRxIntType & 0x20) { //FIFO OverRun ERR - ResetChip(iobase, 0); - ResetChip(iobase, 1); - } else { //PHY,CRC ERR - - if (iRxIntType != 0x08) - hwreset(self); //F01 - } - via_ircc_dma_receive(self); - } //ERR - - } //Rx Event - spin_unlock(&self->lock); - return IRQ_RETVAL(iHostIntType); -} - -static void hwreset(struct via_ircc_cb *self) -{ - int iobase; - iobase = self->io.fir_base; - - ResetChip(iobase, 5); - EnableDMA(iobase, OFF); - EnableTX(iobase, OFF); - EnableRX(iobase, OFF); - EnRXDMA(iobase, OFF); - EnTXDMA(iobase, OFF); - RXStart(iobase, OFF); - TXStart(iobase, OFF); - InitCard(iobase); - CommonInit(iobase); - SIRFilter(iobase, ON); - SetSIR(iobase, ON); - CRC16(iobase, ON); - EnTXCRC(iobase, 0); - WriteReg(iobase, I_ST_CT_0, 0x00); - SetBaudRate(iobase, 9600); - SetPulseWidth(iobase, 12); - SetSendPreambleCount(iobase, 0); - WriteReg(iobase, I_ST_CT_0, 0x80); - - /* Restore speed. */ - via_ircc_change_speed(self, self->io.speed); - - self->st_fifo.len = 0; -} - -/* - * Function via_ircc_is_receiving (self) - * - * Return TRUE is we are currently receiving a frame - * - */ -static int via_ircc_is_receiving(struct via_ircc_cb *self) -{ - int status = FALSE; - int iobase; - - IRDA_ASSERT(self != NULL, return FALSE;); - - iobase = self->io.fir_base; - if (CkRxRecv(iobase, self)) - status = TRUE; - - pr_debug("%s(): status=%x....\n", __func__, status); - - return status; -} - - -/* - * Function via_ircc_net_open (dev) - * - * Start the device - * - */ -static int via_ircc_net_open(struct net_device *dev) -{ - struct via_ircc_cb *self; - int iobase; - char hwname[32]; - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - dev->stats.rx_packets = 0; - IRDA_ASSERT(self != NULL, return 0;); - iobase = self->io.fir_base; - if (request_irq(self->io.irq, via_ircc_interrupt, 0, dev->name, dev)) { - net_warn_ratelimited("%s, unable to allocate irq=%d\n", - driver_name, self->io.irq); - return -EAGAIN; - } - /* - * Always allocate the DMA channel after the IRQ, and clean up on - * failure. - */ - if (request_dma(self->io.dma, dev->name)) { - net_warn_ratelimited("%s, unable to allocate dma=%d\n", - driver_name, self->io.dma); - free_irq(self->io.irq, dev); - return -EAGAIN; - } - if (self->io.dma2 != self->io.dma) { - if (request_dma(self->io.dma2, dev->name)) { - net_warn_ratelimited("%s, unable to allocate dma2=%d\n", - driver_name, self->io.dma2); - free_irq(self->io.irq, dev); - free_dma(self->io.dma); - return -EAGAIN; - } - } - - - /* turn on interrupts */ - EnAllInt(iobase, ON); - EnInternalLoop(iobase, OFF); - EnExternalLoop(iobase, OFF); - - /* */ - via_ircc_dma_receive(self); - - /* Ready to play! */ - netif_start_queue(dev); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - sprintf(hwname, "VIA @ 0x%x", iobase); - self->irlap = irlap_open(dev, &self->qos, hwname); - - self->RxLastCount = 0; - - return 0; -} - -/* - * Function via_ircc_net_close (dev) - * - * Stop the device - * - */ -static int via_ircc_net_close(struct net_device *dev) -{ - struct via_ircc_cb *self; - int iobase; - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); - - /* Stop device */ - netif_stop_queue(dev); - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - iobase = self->io.fir_base; - EnTXDMA(iobase, OFF); - EnRXDMA(iobase, OFF); - DisableDmaChannel(self->io.dma); - - /* Disable interrupts */ - EnAllInt(iobase, OFF); - free_irq(self->io.irq, dev); - free_dma(self->io.dma); - if (self->io.dma2 != self->io.dma) - free_dma(self->io.dma2); - - return 0; -} - -/* - * Function via_ircc_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, - int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *) rq; - struct via_ircc_cb *self; - unsigned long flags; - int ret = 0; - - IRDA_ASSERT(dev != NULL, return -1;); - self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return -1;); - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, - cmd); - /* Disable interrupts & save flags */ - spin_lock_irqsave(&self->lock, flags); - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - via_ircc_change_speed(self, irq->ifr_baudrate); - break; - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = via_ircc_is_receiving(self); - break; - default: - ret = -EOPNOTSUPP; - } - out: - spin_unlock_irqrestore(&self->lock, flags); - return ret; -} - -MODULE_AUTHOR("VIA Technologies,inc"); -MODULE_DESCRIPTION("VIA IrDA Device Driver"); -MODULE_LICENSE("GPL"); - -module_init(via_ircc_init); -module_exit(via_ircc_cleanup); diff --git a/drivers/staging/irda/drivers/via-ircc.h b/drivers/staging/irda/drivers/via-ircc.h deleted file mode 100644 index ac1525573398..000000000000 --- a/drivers/staging/irda/drivers/via-ircc.h +++ /dev/null @@ -1,846 +0,0 @@ -/********************************************************************* - * - * Filename: via-ircc.h - * Version: 1.0 - * Description: Driver for the VIA VT8231/VT8233 IrDA chipsets - * Author: VIA Technologies, inc - * Date : 08/06/2003 - -Copyright (c) 1998-2003 VIA Technologies, Inc. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 2, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTIES OR REPRESENTATIONS; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, see . - - * Comment: - * jul/08/2002 : Rx buffer length should use Rx ring ptr. - * Oct/28/2002 : Add SB id for 3147 and 3177. - * jul/09/2002 : only implement two kind of dongle currently. - * Oct/02/2002 : work on VT8231 and VT8233 . - * Aug/06/2003 : change driver format to pci driver . - ********************************************************************/ -#ifndef via_IRCC_H -#define via_IRCC_H -#include -#include -#include -#include - -#define MAX_TX_WINDOW 7 -#define MAX_RX_WINDOW 7 - -struct st_fifo_entry { - int status; - int len; -}; - -struct st_fifo { - struct st_fifo_entry entries[MAX_RX_WINDOW + 2]; - int pending_bytes; - int head; - int tail; - int len; -}; - -struct frame_cb { - void *start; /* Start of frame in DMA mem */ - int len; /* Length of frame in DMA mem */ -}; - -struct tx_fifo { - struct frame_cb queue[MAX_TX_WINDOW + 2]; /* Info about frames in queue */ - int ptr; /* Currently being sent */ - int len; /* Length of queue */ - int free; /* Next free slot */ - void *tail; /* Next free start in DMA mem */ -}; - - -struct eventflag // for keeping track of Interrupt Events -{ - //--------tx part - unsigned char TxFIFOUnderRun; - unsigned char EOMessage; - unsigned char TxFIFOReady; - unsigned char EarlyEOM; - //--------rx part - unsigned char PHYErr; - unsigned char CRCErr; - unsigned char RxFIFOOverRun; - unsigned char EOPacket; - unsigned char RxAvail; - unsigned char TooLargePacket; - unsigned char SIRBad; - //--------unknown - unsigned char Unknown; - //---------- - unsigned char TimeOut; - unsigned char RxDMATC; - unsigned char TxDMATC; -}; - -/* Private data for each instance */ -struct via_ircc_cb { - struct st_fifo st_fifo; /* Info about received frames */ - struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ - - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; /* QoS capabilities for this device */ - - chipio_t io; /* IrDA controller information */ - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - dma_addr_t tx_buff_dma; - dma_addr_t rx_buff_dma; - - __u8 ier; /* Interrupt enable register */ - - spinlock_t lock; /* For serializing operations */ - - __u32 flags; /* Interface flags */ - __u32 new_speed; - int index; /* Instance index */ - - struct eventflag EventFlag; - unsigned int chip_id; /* to remember chip id */ - unsigned int RetryCount; - unsigned int RxDataReady; - unsigned int RxLastCount; -}; - - -//---------I=Infrared, H=Host, M=Misc, T=Tx, R=Rx, ST=Status, -// CF=Config, CT=Control, L=Low, H=High, C=Count -#define I_CF_L_0 0x10 -#define I_CF_H_0 0x11 -#define I_SIR_BOF 0x12 -#define I_SIR_EOF 0x13 -#define I_ST_CT_0 0x15 -#define I_ST_L_1 0x16 -#define I_ST_H_1 0x17 -#define I_CF_L_1 0x18 -#define I_CF_H_1 0x19 -#define I_CF_L_2 0x1a -#define I_CF_H_2 0x1b -#define I_CF_3 0x1e -#define H_CT 0x20 -#define H_ST 0x21 -#define M_CT 0x22 -#define TX_CT_1 0x23 -#define TX_CT_2 0x24 -#define TX_ST 0x25 -#define RX_CT 0x26 -#define RX_ST 0x27 -#define RESET 0x28 -#define P_ADDR 0x29 -#define RX_C_L 0x2a -#define RX_C_H 0x2b -#define RX_P_L 0x2c -#define RX_P_H 0x2d -#define TX_C_L 0x2e -#define TX_C_H 0x2f -#define TIMER 0x32 -#define I_CF_4 0x33 -#define I_T_C_L 0x34 -#define I_T_C_H 0x35 -#define VERSION 0x3f -//------------------------------- -#define StartAddr 0x10 // the first register address -#define EndAddr 0x3f // the last register address -#define GetBit(val,bit) val = (unsigned char) ((val>>bit) & 0x1) - // Returns the bit -#define SetBit(val,bit) val= (unsigned char ) (val | (0x1 << bit)) - // Sets bit to 1 -#define ResetBit(val,bit) val= (unsigned char ) (val & ~(0x1 << bit)) - // Sets bit to 0 - -#define OFF 0 -#define ON 1 -#define DMA_TX_MODE 0x08 -#define DMA_RX_MODE 0x04 - -#define DMA1 0 -#define DMA2 0xc0 -#define MASK1 DMA1+0x0a -#define MASK2 DMA2+0x14 - -#define Clk_bit 0x40 -#define Tx_bit 0x01 -#define Rd_Valid 0x08 -#define RxBit 0x08 - -static void DisableDmaChannel(unsigned int channel) -{ - switch (channel) { // 8 Bit DMA channels DMAC1 - case 0: - outb(4, MASK1); //mask channel 0 - break; - case 1: - outb(5, MASK1); //Mask channel 1 - break; - case 2: - outb(6, MASK1); //Mask channel 2 - break; - case 3: - outb(7, MASK1); //Mask channel 3 - break; - case 5: - outb(5, MASK2); //Mask channel 5 - break; - case 6: - outb(6, MASK2); //Mask channel 6 - break; - case 7: - outb(7, MASK2); //Mask channel 7 - break; - default: - break; - } -} - -static unsigned char ReadLPCReg(int iRegNum) -{ - unsigned char iVal; - - outb(0x87, 0x2e); - outb(0x87, 0x2e); - outb(iRegNum, 0x2e); - iVal = inb(0x2f); - outb(0xaa, 0x2e); - - return iVal; -} - -static void WriteLPCReg(int iRegNum, unsigned char iVal) -{ - - outb(0x87, 0x2e); - outb(0x87, 0x2e); - outb(iRegNum, 0x2e); - outb(iVal, 0x2f); - outb(0xAA, 0x2e); -} - -static __u8 ReadReg(unsigned int BaseAddr, int iRegNum) -{ - return (__u8) inb(BaseAddr + iRegNum); -} - -static void WriteReg(unsigned int BaseAddr, int iRegNum, unsigned char iVal) -{ - outb(iVal, BaseAddr + iRegNum); -} - -static int WriteRegBit(unsigned int BaseAddr, unsigned char RegNum, - unsigned char BitPos, unsigned char value) -{ - __u8 Rtemp, Wtemp; - - if (BitPos > 7) { - return -1; - } - if ((RegNum < StartAddr) || (RegNum > EndAddr)) - return -1; - Rtemp = ReadReg(BaseAddr, RegNum); - if (value == 0) - Wtemp = ResetBit(Rtemp, BitPos); - else { - if (value == 1) - Wtemp = SetBit(Rtemp, BitPos); - else - return -1; - } - WriteReg(BaseAddr, RegNum, Wtemp); - return 0; -} - -static __u8 CheckRegBit(unsigned int BaseAddr, unsigned char RegNum, - unsigned char BitPos) -{ - __u8 temp; - - if (BitPos > 7) - return 0xff; - if ((RegNum < StartAddr) || (RegNum > EndAddr)) { -// printf("what is the register %x!\n",RegNum); - } - temp = ReadReg(BaseAddr, RegNum); - return GetBit(temp, BitPos); -} - -static void SetMaxRxPacketSize(__u16 iobase, __u16 size) -{ - __u16 low, high; - if ((size & 0xe000) == 0) { - low = size & 0x00ff; - high = (size & 0x1f00) >> 8; - WriteReg(iobase, I_CF_L_2, low); - WriteReg(iobase, I_CF_H_2, high); - - } - -} - -//for both Rx and Tx - -static void SetFIFO(__u16 iobase, __u16 value) -{ - switch (value) { - case 128: - WriteRegBit(iobase, 0x11, 0, 0); - WriteRegBit(iobase, 0x11, 7, 1); - break; - case 64: - WriteRegBit(iobase, 0x11, 0, 0); - WriteRegBit(iobase, 0x11, 7, 0); - break; - case 32: - WriteRegBit(iobase, 0x11, 0, 1); - WriteRegBit(iobase, 0x11, 7, 0); - break; - default: - WriteRegBit(iobase, 0x11, 0, 0); - WriteRegBit(iobase, 0x11, 7, 0); - } - -} - -#define CRC16(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,7,val) //0 for 32 CRC -/* -#define SetVFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,5,val) -#define SetFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,6,val) -#define SetMIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,5,val) -#define SetSIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,4,val) -*/ -#define SIRFilter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,3,val) -#define Filter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,2,val) -#define InvertTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,1,val) -#define InvertRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,0,val) -//****************************I_CF_H_0 -#define EnableTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,4,val) -#define EnableRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,3,val) -#define EnableDMA(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,2,val) -#define SIRRecvAny(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,1,val) -#define DiableTrans(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,0,val) -//***************************I_SIR_BOF,I_SIR_EOF -#define SetSIRBOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_BOF,val) -#define SetSIREOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_EOF,val) -#define GetSIRBOF(BaseAddr) ReadReg(BaseAddr,I_SIR_BOF) -#define GetSIREOF(BaseAddr) ReadReg(BaseAddr,I_SIR_EOF) -//*******************I_ST_CT_0 -#define EnPhys(BaseAddr,val) WriteRegBit(BaseAddr,I_ST_CT_0,7,val) -#define IsModeError(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,6) //RO -#define IsVFIROn(BaseAddr) CheckRegBit(BaseAddr,0x14,0) //RO for VT1211 only -#define IsFIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,5) //RO -#define IsMIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,4) //RO -#define IsSIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,3) //RO -#define IsEnableTX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,2) //RO -#define IsEnableRX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,1) //RO -#define Is16CRC(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,0) //RO -//***************************I_CF_3 -#define DisableAdjacentPulseWidth(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,5,val) //1 disable -#define DisablePulseWidthAdjust(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,4,val) //1 disable -#define UseOneRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,1,val) //0 use two RX -#define SlowIRRXLowActive(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,0,val) //0 show RX high=1 in SIR -//***************************H_CT -#define EnAllInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,7,val) -#define TXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,6,val) -#define RXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,5,val) -#define ClearRXInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,4,val) // 1 clear -//*****************H_ST -#define IsRXInt(BaseAddr) CheckRegBit(BaseAddr,H_ST,4) -#define GetIntIndentify(BaseAddr) ((ReadReg(BaseAddr,H_ST)&0xf1) >>1) -#define IsHostBusy(BaseAddr) CheckRegBit(BaseAddr,H_ST,0) -#define GetHostStatus(BaseAddr) ReadReg(BaseAddr,H_ST) //RO -//**************************M_CT -#define EnTXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,7,val) -#define EnRXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,6,val) -#define SwapDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,5,val) -#define EnInternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,4,val) -#define EnExternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,3,val) -//**************************TX_CT_1 -#define EnTXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,4,val) //half empty int (1 half) -#define EnTXFIFOUnderrunEOMInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,5,val) -#define EnTXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,6,val) //int when reach it threshold (setting by bit 4) -//**************************TX_CT_2 -#define ForceUnderrun(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,7,val) // force an underrun int -#define EnTXCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,6,val) //1 for FIR,MIR...0 (not SIR) -#define ForceBADCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,5,val) //force an bad CRC -#define SendSIP(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,4,val) //send indication pulse for prevent SIR disturb -#define ClearEnTX(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,3,val) // opposite to EnTX -//*****************TX_ST -#define GetTXStatus(BaseAddr) ReadReg(BaseAddr,TX_ST) //RO -//**************************RX_CT -#define EnRXSpecInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,0,val) -#define EnRXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,1,val) //enable int when reach it threshold (setting by bit 7) -#define EnRXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,7,val) //enable int when (1) half full...or (0) just not full -//*****************RX_ST -#define GetRXStatus(BaseAddr) ReadReg(BaseAddr,RX_ST) //RO -//***********************P_ADDR -#define SetPacketAddr(BaseAddr,addr) WriteReg(BaseAddr,P_ADDR,addr) -//***********************I_CF_4 -#define EnGPIOtoRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,7,val) -#define EnTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,1,val) -#define ClearTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,0,val) -//***********************I_T_C_L -#define WriteGIO(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,7,val) -#define ReadGIO(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,7) -#define ReadRX(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,3) //RO -#define WriteTX(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,0,val) -//***********************I_T_C_H -#define EnRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_H,7,val) -#define ReadRX2(BaseAddr) CheckRegBit(BaseAddr,I_T_C_H,7) -//**********************Version -#define GetFIRVersion(BaseAddr) ReadReg(BaseAddr,VERSION) - - -static void SetTimer(__u16 iobase, __u8 count) -{ - EnTimerInt(iobase, OFF); - WriteReg(iobase, TIMER, count); - EnTimerInt(iobase, ON); -} - - -static void SetSendByte(__u16 iobase, __u32 count) -{ - __u32 low, high; - - if ((count & 0xf000) == 0) { - low = count & 0x00ff; - high = (count & 0x0f00) >> 8; - WriteReg(iobase, TX_C_L, low); - WriteReg(iobase, TX_C_H, high); - } -} - -static void ResetChip(__u16 iobase, __u8 type) -{ - __u8 value; - - value = (type + 2) << 4; - WriteReg(iobase, RESET, type); -} - -static int CkRxRecv(__u16 iobase, struct via_ircc_cb *self) -{ - __u8 low, high; - __u16 wTmp = 0, wTmp1 = 0, wTmp_new = 0; - - low = ReadReg(iobase, RX_C_L); - high = ReadReg(iobase, RX_C_H); - wTmp1 = high; - wTmp = (wTmp1 << 8) | low; - udelay(10); - low = ReadReg(iobase, RX_C_L); - high = ReadReg(iobase, RX_C_H); - wTmp1 = high; - wTmp_new = (wTmp1 << 8) | low; - if (wTmp_new != wTmp) - return 1; - else - return 0; - -} - -static __u16 RxCurCount(__u16 iobase, struct via_ircc_cb * self) -{ - __u8 low, high; - __u16 wTmp = 0, wTmp1 = 0; - - low = ReadReg(iobase, RX_P_L); - high = ReadReg(iobase, RX_P_H); - wTmp1 = high; - wTmp = (wTmp1 << 8) | low; - return wTmp; -} - -/* This Routine can only use in recevie_complete - * for it will update last count. - */ - -static __u16 GetRecvByte(__u16 iobase, struct via_ircc_cb * self) -{ - __u8 low, high; - __u16 wTmp, wTmp1, ret; - - low = ReadReg(iobase, RX_P_L); - high = ReadReg(iobase, RX_P_H); - wTmp1 = high; - wTmp = (wTmp1 << 8) | low; - - - if (wTmp >= self->RxLastCount) - ret = wTmp - self->RxLastCount; - else - ret = (0x8000 - self->RxLastCount) + wTmp; - self->RxLastCount = wTmp; - -/* RX_P is more actually the RX_C - low=ReadReg(iobase,RX_C_L); - high=ReadReg(iobase,RX_C_H); - - if(!(high&0xe000)) { - temp=(high<<8)+low; - return temp; - } - else return 0; -*/ - return ret; -} - -static void Sdelay(__u16 scale) -{ - __u8 bTmp; - int i, j; - - for (j = 0; j < scale; j++) { - for (i = 0; i < 0x20; i++) { - bTmp = inb(0xeb); - outb(bTmp, 0xeb); - } - } -} - -static void Tdelay(__u16 scale) -{ - __u8 bTmp; - int i, j; - - for (j = 0; j < scale; j++) { - for (i = 0; i < 0x50; i++) { - bTmp = inb(0xeb); - outb(bTmp, 0xeb); - } - } -} - - -static void ActClk(__u16 iobase, __u8 value) -{ - __u8 bTmp; - bTmp = ReadReg(iobase, 0x34); - if (value) - WriteReg(iobase, 0x34, bTmp | Clk_bit); - else - WriteReg(iobase, 0x34, bTmp & ~Clk_bit); -} - -static void ClkTx(__u16 iobase, __u8 Clk, __u8 Tx) -{ - __u8 bTmp; - - bTmp = ReadReg(iobase, 0x34); - if (Clk == 0) - bTmp &= ~Clk_bit; - else { - if (Clk == 1) - bTmp |= Clk_bit; - } - WriteReg(iobase, 0x34, bTmp); - Sdelay(1); - if (Tx == 0) - bTmp &= ~Tx_bit; - else { - if (Tx == 1) - bTmp |= Tx_bit; - } - WriteReg(iobase, 0x34, bTmp); -} - -static void Wr_Byte(__u16 iobase, __u8 data) -{ - __u8 bData = data; -// __u8 btmp; - int i; - - ClkTx(iobase, 0, 1); - - Tdelay(2); - ActClk(iobase, 1); - Tdelay(1); - - for (i = 0; i < 8; i++) { //LDN - - if ((bData >> i) & 0x01) { - ClkTx(iobase, 0, 1); //bit data = 1; - } else { - ClkTx(iobase, 0, 0); //bit data = 1; - } - Tdelay(2); - Sdelay(1); - ActClk(iobase, 1); //clk hi - Tdelay(1); - } -} - -static __u8 Rd_Indx(__u16 iobase, __u8 addr, __u8 index) -{ - __u8 data = 0, bTmp, data_bit; - int i; - - bTmp = addr | (index << 1) | 0; - ClkTx(iobase, 0, 0); - Tdelay(2); - ActClk(iobase, 1); - udelay(1); - Wr_Byte(iobase, bTmp); - Sdelay(1); - ClkTx(iobase, 0, 0); - Tdelay(2); - for (i = 0; i < 10; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - Tdelay(1); - ClkTx(iobase, 0, 1); - Tdelay(1); - bTmp = ReadReg(iobase, 0x34); - if (!(bTmp & Rd_Valid)) - break; - } - if (!(bTmp & Rd_Valid)) { - for (i = 0; i < 8; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - bTmp = ReadReg(iobase, 0x34); - data_bit = 1 << i; - if (bTmp & RxBit) - data |= data_bit; - else - data &= ~data_bit; - Tdelay(2); - } - } else { - for (i = 0; i < 2; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - Tdelay(2); - } - bTmp = ReadReg(iobase, 0x34); - } - for (i = 0; i < 1; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - Tdelay(2); - } - ClkTx(iobase, 0, 0); - Tdelay(1); - for (i = 0; i < 3; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - Tdelay(2); - } - return data; -} - -static void Wr_Indx(__u16 iobase, __u8 addr, __u8 index, __u8 data) -{ - int i; - __u8 bTmp; - - ClkTx(iobase, 0, 0); - udelay(2); - ActClk(iobase, 1); - udelay(1); - bTmp = addr | (index << 1) | 1; - Wr_Byte(iobase, bTmp); - Wr_Byte(iobase, data); - for (i = 0; i < 2; i++) { - ClkTx(iobase, 0, 0); - Tdelay(2); - ActClk(iobase, 1); - Tdelay(1); - } - ActClk(iobase, 0); -} - -static void ResetDongle(__u16 iobase) -{ - int i; - ClkTx(iobase, 0, 0); - Tdelay(1); - for (i = 0; i < 30; i++) { - ActClk(iobase, 1); - Tdelay(1); - ActClk(iobase, 0); - Tdelay(1); - } - ActClk(iobase, 0); -} - -static void SetSITmode(__u16 iobase) -{ - - __u8 bTmp; - - bTmp = ReadLPCReg(0x28); - WriteLPCReg(0x28, bTmp | 0x10); //select ITMOFF - bTmp = ReadReg(iobase, 0x35); - WriteReg(iobase, 0x35, bTmp | 0x40); // Driver ITMOFF - WriteReg(iobase, 0x28, bTmp | 0x80); // enable All interrupt -} - -static void SI_SetMode(__u16 iobase, int mode) -{ - //__u32 dTmp; - __u8 bTmp; - - WriteLPCReg(0x28, 0x70); // S/W Reset - SetSITmode(iobase); - ResetDongle(iobase); - udelay(10); - Wr_Indx(iobase, 0x40, 0x0, 0x17); //RX ,APEN enable,Normal power - Wr_Indx(iobase, 0x40, 0x1, mode); //Set Mode - Wr_Indx(iobase, 0x40, 0x2, 0xff); //Set power to FIR VFIR > 1m - bTmp = Rd_Indx(iobase, 0x40, 1); -} - -static void InitCard(__u16 iobase) -{ - ResetChip(iobase, 5); - WriteReg(iobase, I_ST_CT_0, 0x00); // open CHIP on - SetSIRBOF(iobase, 0xc0); // hardware default value - SetSIREOF(iobase, 0xc1); -} - -static void CommonInit(__u16 iobase) -{ -// EnTXCRC(iobase,0); - SwapDMA(iobase, OFF); - SetMaxRxPacketSize(iobase, 0x0fff); //set to max:4095 - EnRXFIFOReadyInt(iobase, OFF); - EnRXFIFOHalfLevelInt(iobase, OFF); - EnTXFIFOHalfLevelInt(iobase, OFF); - EnTXFIFOUnderrunEOMInt(iobase, ON); -// EnTXFIFOReadyInt(iobase,ON); - InvertTX(iobase, OFF); - InvertRX(iobase, OFF); -// WriteLPCReg(0xF0,0); //(if VT1211 then do this) - if (IsSIROn(iobase)) { - SIRFilter(iobase, ON); - SIRRecvAny(iobase, ON); - } else { - SIRFilter(iobase, OFF); - SIRRecvAny(iobase, OFF); - } - EnRXSpecInt(iobase, ON); - WriteReg(iobase, I_ST_CT_0, 0x80); - EnableDMA(iobase, ON); -} - -static void SetBaudRate(__u16 iobase, __u32 rate) -{ - __u8 value = 11, temp; - - if (IsSIROn(iobase)) { - switch (rate) { - case (__u32) (2400L): - value = 47; - break; - case (__u32) (9600L): - value = 11; - break; - case (__u32) (19200L): - value = 5; - break; - case (__u32) (38400L): - value = 2; - break; - case (__u32) (57600L): - value = 1; - break; - case (__u32) (115200L): - value = 0; - break; - default: - break; - } - } else if (IsMIROn(iobase)) { - value = 0; // will automatically be fixed in 1.152M - } else if (IsFIROn(iobase)) { - value = 0; // will automatically be fixed in 4M - } - temp = (ReadReg(iobase, I_CF_H_1) & 0x03); - temp |= value << 2; - WriteReg(iobase, I_CF_H_1, temp); -} - -static void SetPulseWidth(__u16 iobase, __u8 width) -{ - __u8 temp, temp1, temp2; - - temp = (ReadReg(iobase, I_CF_L_1) & 0x1f); - temp1 = (ReadReg(iobase, I_CF_H_1) & 0xfc); - temp2 = (width & 0x07) << 5; - temp |= temp2; - temp2 = (width & 0x18) >> 3; - temp1 |= temp2; - WriteReg(iobase, I_CF_L_1, temp); - WriteReg(iobase, I_CF_H_1, temp1); -} - -static void SetSendPreambleCount(__u16 iobase, __u8 count) -{ - __u8 temp; - - temp = ReadReg(iobase, I_CF_L_1) & 0xe0; - temp |= count; - WriteReg(iobase, I_CF_L_1, temp); - -} - -static void SetVFIR(__u16 BaseAddr, __u8 val) -{ - __u8 tmp; - - tmp = ReadReg(BaseAddr, I_CF_L_0); - WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); - WriteRegBit(BaseAddr, I_CF_H_0, 5, val); -} - -static void SetFIR(__u16 BaseAddr, __u8 val) -{ - __u8 tmp; - - WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); - tmp = ReadReg(BaseAddr, I_CF_L_0); - WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); - WriteRegBit(BaseAddr, I_CF_L_0, 6, val); -} - -static void SetMIR(__u16 BaseAddr, __u8 val) -{ - __u8 tmp; - - WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); - tmp = ReadReg(BaseAddr, I_CF_L_0); - WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); - WriteRegBit(BaseAddr, I_CF_L_0, 5, val); -} - -static void SetSIR(__u16 BaseAddr, __u8 val) -{ - __u8 tmp; - - WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); - tmp = ReadReg(BaseAddr, I_CF_L_0); - WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); - WriteRegBit(BaseAddr, I_CF_L_0, 4, val); -} - -#endif /* via_IRCC_H */ diff --git a/drivers/staging/irda/drivers/vlsi_ir.c b/drivers/staging/irda/drivers/vlsi_ir.c deleted file mode 100644 index 3dff3c55ddf5..000000000000 --- a/drivers/staging/irda/drivers/vlsi_ir.c +++ /dev/null @@ -1,1872 +0,0 @@ -/********************************************************************* - * - * vlsi_ir.c: VLSI82C147 PCI IrDA controller driver for Linux - * - * Copyright (c) 2001-2003 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include - -#define DRIVER_NAME "vlsi_ir" -#define DRIVER_VERSION "v0.5" -#define DRIVER_DESCRIPTION "IrDA SIR/MIR/FIR driver for VLSI 82C147" -#define DRIVER_AUTHOR "Martin Diehl " - -MODULE_DESCRIPTION(DRIVER_DESCRIPTION); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_LICENSE("GPL"); - -/********************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "vlsi_ir.h" - -/********************************************************/ - -static /* const */ char drivername[] = DRIVER_NAME; - -static const struct pci_device_id vlsi_irda_table[] = { - { - .class = PCI_CLASS_WIRELESS_IRDA << 8, - .class_mask = PCI_CLASS_SUBCLASS_MASK << 8, - .vendor = PCI_VENDOR_ID_VLSI, - .device = PCI_DEVICE_ID_VLSI_82C147, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, - { /* all zeroes */ } -}; - -MODULE_DEVICE_TABLE(pci, vlsi_irda_table); - -/********************************************************/ - -/* clksrc: which clock source to be used - * 0: auto - try PLL, fallback to 40MHz XCLK - * 1: on-chip 48MHz PLL - * 2: external 48MHz XCLK - * 3: external 40MHz XCLK (HP OB-800) - */ - -static int clksrc = 0; /* default is 0(auto) */ -module_param(clksrc, int, 0); -MODULE_PARM_DESC(clksrc, "clock input source selection"); - -/* ringsize: size of the tx and rx descriptor rings - * independent for tx and rx - * specify as ringsize=tx[,rx] - * allowed values: 4, 8, 16, 32, 64 - * Due to the IrDA 1.x max. allowed window size=7, - * there should be no gain when using rings larger than 8 - */ - -static int ringsize[] = {8,8}; /* default is tx=8 / rx=8 */ -module_param_array(ringsize, int, NULL, 0); -MODULE_PARM_DESC(ringsize, "TX, RX ring descriptor size"); - -/* sirpulse: tuning of the SIR pulse width within IrPHY 1.3 limits - * 0: very short, 1.5us (exception: 6us at 2.4 kbaud) - * 1: nominal 3/16 bittime width - * note: IrDA compliant peer devices should be happy regardless - * which one is used. Primary goal is to save some power - * on the sender's side - at 9.6kbaud for example the short - * pulse width saves more than 90% of the transmitted IR power. - */ - -static int sirpulse = 1; /* default is 3/16 bittime */ -module_param(sirpulse, int, 0); -MODULE_PARM_DESC(sirpulse, "SIR pulse width tuning"); - -/* qos_mtt_bits: encoded min-turn-time value we require the peer device - * to use before transmitting to us. "Type 1" (per-station) - * bitfield according to IrLAP definition (section 6.6.8) - * Don't know which transceiver is used by my OB800 - the - * pretty common HP HDLS-1100 requires 1 msec - so lets use this. - */ - -static int qos_mtt_bits = 0x07; /* default is 1 ms or more */ -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "IrLAP bitfield representing min-turn-time"); - -/********************************************************/ - -static void vlsi_reg_debug(unsigned iobase, const char *s) -{ - int i; - - printk(KERN_DEBUG "%s: ", s); - for (i = 0; i < 0x20; i++) - printk("%02x", (unsigned)inb((iobase+i))); - printk("\n"); -} - -static void vlsi_ring_debug(struct vlsi_ring *r) -{ - struct ring_descr *rd; - unsigned i; - - printk(KERN_DEBUG "%s - ring %p / size %u / mask 0x%04x / len %u / dir %d / hw %p\n", - __func__, r, r->size, r->mask, r->len, r->dir, r->rd[0].hw); - printk(KERN_DEBUG "%s - head = %d / tail = %d\n", __func__, - atomic_read(&r->head) & r->mask, atomic_read(&r->tail) & r->mask); - for (i = 0; i < r->size; i++) { - rd = &r->rd[i]; - printk(KERN_DEBUG "%s - ring descr %u: ", __func__, i); - printk("skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw); - printk(KERN_DEBUG "%s - hw: status=%02x count=%u addr=0x%08x\n", - __func__, (unsigned) rd_get_status(rd), - (unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd)); - } -} - -/********************************************************/ - -/* needed regardless of CONFIG_PROC_FS */ -static struct proc_dir_entry *vlsi_proc_root = NULL; - -#ifdef CONFIG_PROC_FS - -static void vlsi_proc_pdev(struct seq_file *seq, struct pci_dev *pdev) -{ - unsigned iobase = pci_resource_start(pdev, 0); - unsigned i; - - seq_printf(seq, "\n%s (vid/did: [%04x:%04x])\n", - pci_name(pdev), (int)pdev->vendor, (int)pdev->device); - seq_printf(seq, "pci-power-state: %u\n", (unsigned) pdev->current_state); - seq_printf(seq, "resources: irq=%u / io=0x%04x / dma_mask=0x%016Lx\n", - pdev->irq, (unsigned)pci_resource_start(pdev, 0), (unsigned long long)pdev->dma_mask); - seq_puts(seq, "hw registers: "); - for (i = 0; i < 0x20; i++) - seq_printf(seq, "%02x", (unsigned)inb((iobase+i))); - seq_putc(seq, '\n'); -} - -static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - u8 byte; - u16 word; - s32 sec, usec; - unsigned iobase = ndev->base_addr; - - seq_printf(seq, "\n%s link state: %s / %s / %s / %s\n", ndev->name, - netif_device_present(ndev) ? "attached" : "detached", - netif_running(ndev) ? "running" : "not running", - netif_carrier_ok(ndev) ? "carrier ok" : "no carrier", - netif_queue_stopped(ndev) ? "queue stopped" : "queue running"); - - if (!netif_running(ndev)) - return; - - seq_puts(seq, "\nhw-state:\n"); - pci_read_config_byte(idev->pdev, VLSI_PCI_IRMISC, &byte); - seq_printf(seq, "IRMISC:%s%s%s uart%s", - (byte&IRMISC_IRRAIL) ? " irrail" : "", - (byte&IRMISC_IRPD) ? " irpd" : "", - (byte&IRMISC_UARTTST) ? " uarttest" : "", - (byte&IRMISC_UARTEN) ? "@" : " disabled\n"); - if (byte&IRMISC_UARTEN) { - seq_printf(seq, "0x%s\n", - (byte&2) ? ((byte&1) ? "3e8" : "2e8") - : ((byte&1) ? "3f8" : "2f8")); - } - pci_read_config_byte(idev->pdev, VLSI_PCI_CLKCTL, &byte); - seq_printf(seq, "CLKCTL: PLL %s%s%s / clock %s / wakeup %s\n", - (byte&CLKCTL_PD_INV) ? "powered" : "down", - (byte&CLKCTL_LOCK) ? " locked" : "", - (byte&CLKCTL_EXTCLK) ? ((byte&CLKCTL_XCKSEL)?" / 40 MHz XCLK":" / 48 MHz XCLK") : "", - (byte&CLKCTL_CLKSTP) ? "stopped" : "running", - (byte&CLKCTL_WAKE) ? "enabled" : "disabled"); - pci_read_config_byte(idev->pdev, VLSI_PCI_MSTRPAGE, &byte); - seq_printf(seq, "MSTRPAGE: 0x%02x\n", (unsigned)byte); - - byte = inb(iobase+VLSI_PIO_IRINTR); - seq_printf(seq, "IRINTR:%s%s%s%s%s%s%s%s\n", - (byte&IRINTR_ACTEN) ? " ACTEN" : "", - (byte&IRINTR_RPKTEN) ? " RPKTEN" : "", - (byte&IRINTR_TPKTEN) ? " TPKTEN" : "", - (byte&IRINTR_OE_EN) ? " OE_EN" : "", - (byte&IRINTR_ACTIVITY) ? " ACTIVITY" : "", - (byte&IRINTR_RPKTINT) ? " RPKTINT" : "", - (byte&IRINTR_TPKTINT) ? " TPKTINT" : "", - (byte&IRINTR_OE_INT) ? " OE_INT" : ""); - word = inw(iobase+VLSI_PIO_RINGPTR); - seq_printf(seq, "RINGPTR: rx=%u / tx=%u\n", RINGPTR_GET_RX(word), RINGPTR_GET_TX(word)); - word = inw(iobase+VLSI_PIO_RINGBASE); - seq_printf(seq, "RINGBASE: busmap=0x%08x\n", - ((unsigned)word << 10)|(MSTRPAGE_VALUE<<24)); - word = inw(iobase+VLSI_PIO_RINGSIZE); - seq_printf(seq, "RINGSIZE: rx=%u / tx=%u\n", RINGSIZE_TO_RXSIZE(word), - RINGSIZE_TO_TXSIZE(word)); - - word = inw(iobase+VLSI_PIO_IRCFG); - seq_printf(seq, "IRCFG:%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - (word&IRCFG_LOOP) ? " LOOP" : "", - (word&IRCFG_ENTX) ? " ENTX" : "", - (word&IRCFG_ENRX) ? " ENRX" : "", - (word&IRCFG_MSTR) ? " MSTR" : "", - (word&IRCFG_RXANY) ? " RXANY" : "", - (word&IRCFG_CRC16) ? " CRC16" : "", - (word&IRCFG_FIR) ? " FIR" : "", - (word&IRCFG_MIR) ? " MIR" : "", - (word&IRCFG_SIR) ? " SIR" : "", - (word&IRCFG_SIRFILT) ? " SIRFILT" : "", - (word&IRCFG_SIRTEST) ? " SIRTEST" : "", - (word&IRCFG_TXPOL) ? " TXPOL" : "", - (word&IRCFG_RXPOL) ? " RXPOL" : ""); - word = inw(iobase+VLSI_PIO_IRENABLE); - seq_printf(seq, "IRENABLE:%s%s%s%s%s%s%s%s\n", - (word&IRENABLE_PHYANDCLOCK) ? " PHYANDCLOCK" : "", - (word&IRENABLE_CFGER) ? " CFGERR" : "", - (word&IRENABLE_FIR_ON) ? " FIR_ON" : "", - (word&IRENABLE_MIR_ON) ? " MIR_ON" : "", - (word&IRENABLE_SIR_ON) ? " SIR_ON" : "", - (word&IRENABLE_ENTXST) ? " ENTXST" : "", - (word&IRENABLE_ENRXST) ? " ENRXST" : "", - (word&IRENABLE_CRC16_ON) ? " CRC16_ON" : ""); - word = inw(iobase+VLSI_PIO_PHYCTL); - seq_printf(seq, "PHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n", - (unsigned)PHYCTL_TO_BAUD(word), - (unsigned)PHYCTL_TO_PLSWID(word), - (unsigned)PHYCTL_TO_PREAMB(word)); - word = inw(iobase+VLSI_PIO_NPHYCTL); - seq_printf(seq, "NPHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n", - (unsigned)PHYCTL_TO_BAUD(word), - (unsigned)PHYCTL_TO_PLSWID(word), - (unsigned)PHYCTL_TO_PREAMB(word)); - word = inw(iobase+VLSI_PIO_MAXPKT); - seq_printf(seq, "MAXPKT: max. rx packet size = %u\n", word); - word = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; - seq_printf(seq, "RCVBCNT: rx-fifo filling level = %u\n", word); - - seq_puts(seq, "\nsw-state:\n"); - seq_printf(seq, "IrPHY setup: %d baud - %s encoding\n", idev->baud, - (idev->mode==IFF_SIR)?"SIR":((idev->mode==IFF_MIR)?"MIR":"FIR")); - sec = div_s64_rem(ktime_us_delta(ktime_get(), idev->last_rx), - USEC_PER_SEC, &usec); - seq_printf(seq, "last rx: %ul.%06u sec\n", sec, usec); - - seq_printf(seq, "RX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu", - ndev->stats.rx_packets, ndev->stats.rx_bytes, ndev->stats.rx_errors, - ndev->stats.rx_dropped); - seq_printf(seq, " / overrun=%lu / length=%lu / frame=%lu / crc=%lu\n", - ndev->stats.rx_over_errors, ndev->stats.rx_length_errors, - ndev->stats.rx_frame_errors, ndev->stats.rx_crc_errors); - seq_printf(seq, "TX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu / fifo=%lu\n", - ndev->stats.tx_packets, ndev->stats.tx_bytes, ndev->stats.tx_errors, - ndev->stats.tx_dropped, ndev->stats.tx_fifo_errors); - -} - -static void vlsi_proc_ring(struct seq_file *seq, struct vlsi_ring *r) -{ - struct ring_descr *rd; - unsigned i, j; - int h, t; - - seq_printf(seq, "size %u / mask 0x%04x / len %u / dir %d / hw %p\n", - r->size, r->mask, r->len, r->dir, r->rd[0].hw); - h = atomic_read(&r->head) & r->mask; - t = atomic_read(&r->tail) & r->mask; - seq_printf(seq, "head = %d / tail = %d ", h, t); - if (h == t) - seq_puts(seq, "(empty)\n"); - else { - if (((t+1)&r->mask) == h) - seq_puts(seq, "(full)\n"); - else - seq_printf(seq, "(level = %d)\n", ((unsigned)(t-h) & r->mask)); - rd = &r->rd[h]; - j = (unsigned) rd_get_count(rd); - seq_printf(seq, "current: rd = %d / status = %02x / len = %u\n", - h, (unsigned)rd_get_status(rd), j); - if (j > 0) { - seq_printf(seq, " data: %*ph\n", - min_t(unsigned, j, 20), rd->buf); - } - } - for (i = 0; i < r->size; i++) { - rd = &r->rd[i]; - seq_printf(seq, "> ring descr %u: ", i); - seq_printf(seq, "skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw); - seq_printf(seq, " hw: status=%02x count=%u busaddr=0x%08x\n", - (unsigned) rd_get_status(rd), - (unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd)); - } -} - -static int vlsi_seq_show(struct seq_file *seq, void *v) -{ - struct net_device *ndev = seq->private; - vlsi_irda_dev_t *idev = netdev_priv(ndev); - unsigned long flags; - - seq_printf(seq, "\n%s %s\n\n", DRIVER_NAME, DRIVER_VERSION); - seq_printf(seq, "clksrc: %s\n", - (clksrc>=2) ? ((clksrc==3)?"40MHz XCLK":"48MHz XCLK") - : ((clksrc==1)?"48MHz PLL":"autodetect")); - seq_printf(seq, "ringsize: tx=%d / rx=%d\n", - ringsize[0], ringsize[1]); - seq_printf(seq, "sirpulse: %s\n", (sirpulse)?"3/16 bittime":"short"); - seq_printf(seq, "qos_mtt_bits: 0x%02x\n", (unsigned)qos_mtt_bits); - - spin_lock_irqsave(&idev->lock, flags); - if (idev->pdev != NULL) { - vlsi_proc_pdev(seq, idev->pdev); - - if (idev->pdev->current_state == 0) - vlsi_proc_ndev(seq, ndev); - else - seq_printf(seq, "\nPCI controller down - resume_ok = %d\n", - idev->resume_ok); - if (netif_running(ndev) && idev->rx_ring && idev->tx_ring) { - seq_puts(seq, "\n--------- RX ring -----------\n\n"); - vlsi_proc_ring(seq, idev->rx_ring); - seq_puts(seq, "\n--------- TX ring -----------\n\n"); - vlsi_proc_ring(seq, idev->tx_ring); - } - } - seq_putc(seq, '\n'); - spin_unlock_irqrestore(&idev->lock, flags); - - return 0; -} - -static int vlsi_seq_open(struct inode *inode, struct file *file) -{ - return single_open(file, vlsi_seq_show, PDE_DATA(inode)); -} - -static const struct file_operations vlsi_proc_fops = { - .owner = THIS_MODULE, - .open = vlsi_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -#define VLSI_PROC_FOPS (&vlsi_proc_fops) - -#else /* CONFIG_PROC_FS */ -#define VLSI_PROC_FOPS NULL -#endif - -/********************************************************/ - -static struct vlsi_ring *vlsi_alloc_ring(struct pci_dev *pdev, struct ring_descr_hw *hwmap, - unsigned size, unsigned len, int dir) -{ - struct vlsi_ring *r; - struct ring_descr *rd; - unsigned i, j; - dma_addr_t busaddr; - - if (!size || ((size-1)&size)!=0) /* must be >0 and power of 2 */ - return NULL; - - r = kmalloc(sizeof(*r) + size * sizeof(struct ring_descr), GFP_KERNEL); - if (!r) - return NULL; - memset(r, 0, sizeof(*r)); - - r->pdev = pdev; - r->dir = dir; - r->len = len; - r->rd = (struct ring_descr *)(r+1); - r->mask = size - 1; - r->size = size; - atomic_set(&r->head, 0); - atomic_set(&r->tail, 0); - - for (i = 0; i < size; i++) { - rd = r->rd + i; - memset(rd, 0, sizeof(*rd)); - rd->hw = hwmap + i; - rd->buf = kmalloc(len, GFP_KERNEL|GFP_DMA); - if (rd->buf) - busaddr = pci_map_single(pdev, rd->buf, len, dir); - if (rd->buf == NULL || pci_dma_mapping_error(pdev, busaddr)) { - if (rd->buf) { - net_err_ratelimited("%s: failed to create PCI-MAP for %p\n", - __func__, rd->buf); - kfree(rd->buf); - rd->buf = NULL; - } - for (j = 0; j < i; j++) { - rd = r->rd + j; - busaddr = rd_get_addr(rd); - rd_set_addr_status(rd, 0, 0); - pci_unmap_single(pdev, busaddr, len, dir); - kfree(rd->buf); - rd->buf = NULL; - } - kfree(r); - return NULL; - } - rd_set_addr_status(rd, busaddr, 0); - /* initially, the dma buffer is owned by the CPU */ - rd->skb = NULL; - } - return r; -} - -static int vlsi_free_ring(struct vlsi_ring *r) -{ - struct ring_descr *rd; - unsigned i; - dma_addr_t busaddr; - - for (i = 0; i < r->size; i++) { - rd = r->rd + i; - if (rd->skb) - dev_kfree_skb_any(rd->skb); - busaddr = rd_get_addr(rd); - rd_set_addr_status(rd, 0, 0); - if (busaddr) - pci_unmap_single(r->pdev, busaddr, r->len, r->dir); - kfree(rd->buf); - } - kfree(r); - return 0; -} - -static int vlsi_create_hwif(vlsi_irda_dev_t *idev) -{ - char *ringarea; - struct ring_descr_hw *hwmap; - - idev->virtaddr = NULL; - idev->busaddr = 0; - - ringarea = pci_zalloc_consistent(idev->pdev, HW_RING_AREA_SIZE, - &idev->busaddr); - if (!ringarea) - goto out; - - hwmap = (struct ring_descr_hw *)ringarea; - idev->rx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[1], - XFER_BUF_SIZE, PCI_DMA_FROMDEVICE); - if (idev->rx_ring == NULL) - goto out_unmap; - - hwmap += MAX_RING_DESCR; - idev->tx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[0], - XFER_BUF_SIZE, PCI_DMA_TODEVICE); - if (idev->tx_ring == NULL) - goto out_free_rx; - - idev->virtaddr = ringarea; - return 0; - -out_free_rx: - vlsi_free_ring(idev->rx_ring); -out_unmap: - idev->rx_ring = idev->tx_ring = NULL; - pci_free_consistent(idev->pdev, HW_RING_AREA_SIZE, ringarea, idev->busaddr); - idev->busaddr = 0; -out: - return -ENOMEM; -} - -static int vlsi_destroy_hwif(vlsi_irda_dev_t *idev) -{ - vlsi_free_ring(idev->rx_ring); - vlsi_free_ring(idev->tx_ring); - idev->rx_ring = idev->tx_ring = NULL; - - if (idev->busaddr) - pci_free_consistent(idev->pdev,HW_RING_AREA_SIZE,idev->virtaddr,idev->busaddr); - - idev->virtaddr = NULL; - idev->busaddr = 0; - - return 0; -} - -/********************************************************/ - -static int vlsi_process_rx(struct vlsi_ring *r, struct ring_descr *rd) -{ - u16 status; - int crclen, len = 0; - struct sk_buff *skb; - int ret = 0; - struct net_device *ndev = pci_get_drvdata(r->pdev); - vlsi_irda_dev_t *idev = netdev_priv(ndev); - - pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); - /* dma buffer now owned by the CPU */ - status = rd_get_status(rd); - if (status & RD_RX_ERROR) { - if (status & RD_RX_OVER) - ret |= VLSI_RX_OVER; - if (status & RD_RX_LENGTH) - ret |= VLSI_RX_LENGTH; - if (status & RD_RX_PHYERR) - ret |= VLSI_RX_FRAME; - if (status & RD_RX_CRCERR) - ret |= VLSI_RX_CRC; - goto done; - } - - len = rd_get_count(rd); - crclen = (idev->mode==IFF_FIR) ? sizeof(u32) : sizeof(u16); - len -= crclen; /* remove trailing CRC */ - if (len <= 0) { - pr_debug("%s: strange frame (len=%d)\n", __func__, len); - ret |= VLSI_RX_DROP; - goto done; - } - - if (idev->mode == IFF_SIR) { /* hw checks CRC in MIR, FIR mode */ - - /* rd->buf is a streaming PCI_DMA_FROMDEVICE map. Doing the - * endian-adjustment there just in place will dirty a cache line - * which belongs to the map and thus we must be sure it will - * get flushed before giving the buffer back to hardware. - * vlsi_fill_rx() will do this anyway - but here we rely on. - */ - le16_to_cpus(rd->buf+len); - if (irda_calc_crc16(INIT_FCS,rd->buf,len+crclen) != GOOD_FCS) { - pr_debug("%s: crc error\n", __func__); - ret |= VLSI_RX_CRC; - goto done; - } - } - - if (!rd->skb) { - net_warn_ratelimited("%s: rx packet lost\n", __func__); - ret |= VLSI_RX_DROP; - goto done; - } - - skb = rd->skb; - rd->skb = NULL; - skb->dev = ndev; - skb_put_data(skb, rd->buf, len); - skb_reset_mac_header(skb); - if (in_interrupt()) - netif_rx(skb); - else - netif_rx_ni(skb); - -done: - rd_set_status(rd, 0); - rd_set_count(rd, 0); - /* buffer still owned by CPU */ - - return (ret) ? -ret : len; -} - -static void vlsi_fill_rx(struct vlsi_ring *r) -{ - struct ring_descr *rd; - - for (rd = ring_last(r); rd != NULL; rd = ring_put(r)) { - if (rd_is_active(rd)) { - net_warn_ratelimited("%s: driver bug: rx descr race with hw\n", - __func__); - vlsi_ring_debug(r); - break; - } - if (!rd->skb) { - rd->skb = dev_alloc_skb(IRLAP_SKB_ALLOCSIZE); - if (rd->skb) { - skb_reserve(rd->skb,1); - rd->skb->protocol = htons(ETH_P_IRDA); - } - else - break; /* probably not worth logging? */ - } - /* give dma buffer back to busmaster */ - pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir); - rd_activate(rd); - } -} - -static void vlsi_rx_interrupt(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - struct vlsi_ring *r = idev->rx_ring; - struct ring_descr *rd; - int ret; - - for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { - - if (rd_is_active(rd)) - break; - - ret = vlsi_process_rx(r, rd); - - if (ret < 0) { - ret = -ret; - ndev->stats.rx_errors++; - if (ret & VLSI_RX_DROP) - ndev->stats.rx_dropped++; - if (ret & VLSI_RX_OVER) - ndev->stats.rx_over_errors++; - if (ret & VLSI_RX_LENGTH) - ndev->stats.rx_length_errors++; - if (ret & VLSI_RX_FRAME) - ndev->stats.rx_frame_errors++; - if (ret & VLSI_RX_CRC) - ndev->stats.rx_crc_errors++; - } - else if (ret > 0) { - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += ret; - } - } - - idev->last_rx = ktime_get(); /* remember "now" for later mtt delay */ - - vlsi_fill_rx(r); - - if (ring_first(r) == NULL) { - /* we are in big trouble, if this should ever happen */ - net_err_ratelimited("%s: rx ring exhausted!\n", __func__); - vlsi_ring_debug(r); - } - else - outw(0, ndev->base_addr+VLSI_PIO_PROMPT); -} - -/* caller must have stopped the controller from busmastering */ - -static void vlsi_unarm_rx(vlsi_irda_dev_t *idev) -{ - struct net_device *ndev = pci_get_drvdata(idev->pdev); - struct vlsi_ring *r = idev->rx_ring; - struct ring_descr *rd; - int ret; - - for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { - - ret = 0; - if (rd_is_active(rd)) { - rd_set_status(rd, 0); - if (rd_get_count(rd)) { - pr_debug("%s - dropping rx packet\n", __func__); - ret = -VLSI_RX_DROP; - } - rd_set_count(rd, 0); - pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); - if (rd->skb) { - dev_kfree_skb_any(rd->skb); - rd->skb = NULL; - } - } - else - ret = vlsi_process_rx(r, rd); - - if (ret < 0) { - ret = -ret; - ndev->stats.rx_errors++; - if (ret & VLSI_RX_DROP) - ndev->stats.rx_dropped++; - if (ret & VLSI_RX_OVER) - ndev->stats.rx_over_errors++; - if (ret & VLSI_RX_LENGTH) - ndev->stats.rx_length_errors++; - if (ret & VLSI_RX_FRAME) - ndev->stats.rx_frame_errors++; - if (ret & VLSI_RX_CRC) - ndev->stats.rx_crc_errors++; - } - else if (ret > 0) { - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += ret; - } - } -} - -/********************************************************/ - -static int vlsi_process_tx(struct vlsi_ring *r, struct ring_descr *rd) -{ - u16 status; - int len; - int ret; - - pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); - /* dma buffer now owned by the CPU */ - status = rd_get_status(rd); - if (status & RD_TX_UNDRN) - ret = VLSI_TX_FIFO; - else - ret = 0; - rd_set_status(rd, 0); - - if (rd->skb) { - len = rd->skb->len; - dev_kfree_skb_any(rd->skb); - rd->skb = NULL; - } - else /* tx-skb already freed? - should never happen */ - len = rd_get_count(rd); /* incorrect for SIR! (due to wrapping) */ - - rd_set_count(rd, 0); - /* dma buffer still owned by the CPU */ - - return (ret) ? -ret : len; -} - -static int vlsi_set_baud(vlsi_irda_dev_t *idev, unsigned iobase) -{ - u16 nphyctl; - u16 config; - unsigned mode; - int ret; - int baudrate; - int fifocnt; - - baudrate = idev->new_baud; - pr_debug("%s: %d -> %d\n", __func__, idev->baud, idev->new_baud); - if (baudrate == 4000000) { - mode = IFF_FIR; - config = IRCFG_FIR; - nphyctl = PHYCTL_FIR; - } - else if (baudrate == 1152000) { - mode = IFF_MIR; - config = IRCFG_MIR | IRCFG_CRC16; - nphyctl = PHYCTL_MIR(clksrc==3); - } - else { - mode = IFF_SIR; - config = IRCFG_SIR | IRCFG_SIRFILT | IRCFG_RXANY; - switch(baudrate) { - default: - net_warn_ratelimited("%s: undefined baudrate %d - fallback to 9600!\n", - __func__, baudrate); - baudrate = 9600; - /* fallthru */ - case 2400: - case 9600: - case 19200: - case 38400: - case 57600: - case 115200: - nphyctl = PHYCTL_SIR(baudrate,sirpulse,clksrc==3); - break; - } - } - config |= IRCFG_MSTR | IRCFG_ENRX; - - fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; - if (fifocnt != 0) { - pr_debug("%s: rx fifo not empty(%d)\n", __func__, fifocnt); - } - - outw(0, iobase+VLSI_PIO_IRENABLE); - outw(config, iobase+VLSI_PIO_IRCFG); - outw(nphyctl, iobase+VLSI_PIO_NPHYCTL); - wmb(); - outw(IRENABLE_PHYANDCLOCK, iobase+VLSI_PIO_IRENABLE); - mb(); - - udelay(1); /* chip applies IRCFG on next rising edge of its 8MHz clock */ - - /* read back settings for validation */ - - config = inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_MASK; - - if (mode == IFF_FIR) - config ^= IRENABLE_FIR_ON; - else if (mode == IFF_MIR) - config ^= (IRENABLE_MIR_ON|IRENABLE_CRC16_ON); - else - config ^= IRENABLE_SIR_ON; - - if (config != (IRENABLE_PHYANDCLOCK|IRENABLE_ENRXST)) { - net_warn_ratelimited("%s: failed to set %s mode!\n", - __func__, - mode == IFF_SIR ? "SIR" : - mode == IFF_MIR ? "MIR" : "FIR"); - ret = -1; - } - else { - if (inw(iobase+VLSI_PIO_PHYCTL) != nphyctl) { - net_warn_ratelimited("%s: failed to apply baudrate %d\n", - __func__, baudrate); - ret = -1; - } - else { - idev->mode = mode; - idev->baud = baudrate; - idev->new_baud = 0; - ret = 0; - } - } - - if (ret) - vlsi_reg_debug(iobase,__func__); - - return ret; -} - -static netdev_tx_t vlsi_hard_start_xmit(struct sk_buff *skb, - struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - struct vlsi_ring *r = idev->tx_ring; - struct ring_descr *rd; - unsigned long flags; - unsigned iobase = ndev->base_addr; - u8 status; - u16 config; - int mtt, diff; - int len, speed; - char *msg = NULL; - - speed = irda_get_next_speed(skb); - spin_lock_irqsave(&idev->lock, flags); - if (speed != -1 && speed != idev->baud) { - netif_stop_queue(ndev); - idev->new_baud = speed; - status = RD_TX_CLRENTX; /* stop tx-ring after this frame */ - } - else - status = 0; - - if (skb->len == 0) { - /* handle zero packets - should be speed change */ - if (status == 0) { - msg = "bogus zero-length packet"; - goto drop_unlock; - } - - /* due to the completely asynch tx operation we might have - * IrLAP racing with the hardware here, f.e. if the controller - * is just sending the last packet with current speed while - * the LAP is already switching the speed using synchronous - * len=0 packet. Immediate execution would lead to hw lockup - * requiring a powercycle to reset. Good candidate to trigger - * this is the final UA:RSP packet after receiving a DISC:CMD - * when getting the LAP down. - * Note that we are not protected by the queue_stop approach - * because the final UA:RSP arrives _without_ request to apply - * new-speed-after-this-packet - hence the driver doesn't know - * this was the last packet and doesn't stop the queue. So the - * forced switch to default speed from LAP gets through as fast - * as only some 10 usec later while the UA:RSP is still processed - * by the hardware and we would get screwed. - */ - - if (ring_first(idev->tx_ring) == NULL) { - /* no race - tx-ring already empty */ - vlsi_set_baud(idev, iobase); - netif_wake_queue(ndev); - } - else - ; - /* keep the speed change pending like it would - * for any len>0 packet. tx completion interrupt - * will apply it when the tx ring becomes empty. - */ - spin_unlock_irqrestore(&idev->lock, flags); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } - - /* sanity checks - simply drop the packet */ - - rd = ring_last(r); - if (!rd) { - msg = "ring full, but queue wasn't stopped"; - goto drop_unlock; - } - - if (rd_is_active(rd)) { - msg = "entry still owned by hw"; - goto drop_unlock; - } - - if (!rd->buf) { - msg = "tx ring entry without pci buffer"; - goto drop_unlock; - } - - if (rd->skb) { - msg = "ring entry with old skb still attached"; - goto drop_unlock; - } - - /* no need for serialization or interrupt disable during mtt */ - spin_unlock_irqrestore(&idev->lock, flags); - - if ((mtt = irda_get_mtt(skb)) > 0) { - diff = ktime_us_delta(ktime_get(), idev->last_rx); - if (mtt > diff) - udelay(mtt - diff); - /* must not sleep here - called under netif_tx_lock! */ - } - - /* tx buffer already owned by CPU due to pci_dma_sync_single_for_cpu() - * after subsequent tx-completion - */ - - if (idev->mode == IFF_SIR) { - status |= RD_TX_DISCRC; /* no hw-crc creation */ - len = async_wrap_skb(skb, rd->buf, r->len); - - /* Some rare worst case situation in SIR mode might lead to - * potential buffer overflow. The wrapper detects this, returns - * with a shortened frame (without FCS/EOF) but doesn't provide - * any error indication about the invalid packet which we are - * going to transmit. - * Therefore we log if the buffer got filled to the point, where the - * wrapper would abort, i.e. when there are less than 5 bytes left to - * allow appending the FCS/EOF. - */ - - if (len >= r->len-5) - net_warn_ratelimited("%s: possible buffer overflow with SIR wrapping!\n", - __func__); - } - else { - /* hw deals with MIR/FIR mode wrapping */ - status |= RD_TX_PULSE; /* send 2 us highspeed indication pulse */ - len = skb->len; - if (len > r->len) { - msg = "frame exceeds tx buffer length"; - goto drop; - } - else - skb_copy_from_linear_data(skb, rd->buf, len); - } - - rd->skb = skb; /* remember skb for tx-complete stats */ - - rd_set_count(rd, len); - rd_set_status(rd, status); /* not yet active! */ - - /* give dma buffer back to busmaster-hw (flush caches to make - * CPU-driven changes visible from the pci bus). - */ - - pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir); - -/* Switching to TX mode here races with the controller - * which may stop TX at any time when fetching an inactive descriptor - * or one with CLR_ENTX set. So we switch on TX only, if TX was not running - * _after_ the new descriptor was activated on the ring. This ensures - * we will either find TX already stopped or we can be sure, there - * will be a TX-complete interrupt even if the chip stopped doing - * TX just after we found it still running. The ISR will then find - * the non-empty ring and restart TX processing. The enclosing - * spinlock provides the correct serialization to prevent race with isr. - */ - - spin_lock_irqsave(&idev->lock,flags); - - rd_activate(rd); - - if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) { - int fifocnt; - - fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; - if (fifocnt != 0) { - pr_debug("%s: rx fifo not empty(%d)\n", - __func__, fifocnt); - } - - config = inw(iobase+VLSI_PIO_IRCFG); - mb(); - outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG); - wmb(); - outw(0, iobase+VLSI_PIO_PROMPT); - } - - if (ring_put(r) == NULL) { - netif_stop_queue(ndev); - pr_debug("%s: tx ring full - queue stopped\n", __func__); - } - spin_unlock_irqrestore(&idev->lock, flags); - - return NETDEV_TX_OK; - -drop_unlock: - spin_unlock_irqrestore(&idev->lock, flags); -drop: - net_warn_ratelimited("%s: dropping packet - %s\n", __func__, msg); - dev_kfree_skb_any(skb); - ndev->stats.tx_errors++; - ndev->stats.tx_dropped++; - /* Don't even think about returning NET_XMIT_DROP (=1) here! - * In fact any retval!=0 causes the packet scheduler to requeue the - * packet for later retry of transmission - which isn't exactly - * what we want after we've just called dev_kfree_skb_any ;-) - */ - return NETDEV_TX_OK; -} - -static void vlsi_tx_interrupt(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - struct vlsi_ring *r = idev->tx_ring; - struct ring_descr *rd; - unsigned iobase; - int ret; - u16 config; - - for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { - - if (rd_is_active(rd)) - break; - - ret = vlsi_process_tx(r, rd); - - if (ret < 0) { - ret = -ret; - ndev->stats.tx_errors++; - if (ret & VLSI_TX_DROP) - ndev->stats.tx_dropped++; - if (ret & VLSI_TX_FIFO) - ndev->stats.tx_fifo_errors++; - } - else if (ret > 0){ - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += ret; - } - } - - iobase = ndev->base_addr; - - if (idev->new_baud && rd == NULL) /* tx ring empty and speed change pending */ - vlsi_set_baud(idev, iobase); - - config = inw(iobase+VLSI_PIO_IRCFG); - if (rd == NULL) /* tx ring empty: re-enable rx */ - outw((config & ~IRCFG_ENTX) | IRCFG_ENRX, iobase+VLSI_PIO_IRCFG); - - else if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) { - int fifocnt; - - fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; - if (fifocnt != 0) { - pr_debug("%s: rx fifo not empty(%d)\n", - __func__, fifocnt); - } - outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG); - } - - outw(0, iobase+VLSI_PIO_PROMPT); - - if (netif_queue_stopped(ndev) && !idev->new_baud) { - netif_wake_queue(ndev); - pr_debug("%s: queue awoken\n", __func__); - } -} - -/* caller must have stopped the controller from busmastering */ - -static void vlsi_unarm_tx(vlsi_irda_dev_t *idev) -{ - struct net_device *ndev = pci_get_drvdata(idev->pdev); - struct vlsi_ring *r = idev->tx_ring; - struct ring_descr *rd; - int ret; - - for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { - - ret = 0; - if (rd_is_active(rd)) { - rd_set_status(rd, 0); - rd_set_count(rd, 0); - pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); - if (rd->skb) { - dev_kfree_skb_any(rd->skb); - rd->skb = NULL; - } - pr_debug("%s - dropping tx packet\n", __func__); - ret = -VLSI_TX_DROP; - } - else - ret = vlsi_process_tx(r, rd); - - if (ret < 0) { - ret = -ret; - ndev->stats.tx_errors++; - if (ret & VLSI_TX_DROP) - ndev->stats.tx_dropped++; - if (ret & VLSI_TX_FIFO) - ndev->stats.tx_fifo_errors++; - } - else if (ret > 0){ - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += ret; - } - } - -} - -/********************************************************/ - -static int vlsi_start_clock(struct pci_dev *pdev) -{ - u8 clkctl, lock; - int i, count; - - if (clksrc < 2) { /* auto or PLL: try PLL */ - clkctl = CLKCTL_PD_INV | CLKCTL_CLKSTP; - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); - - /* procedure to detect PLL lock synchronisation: - * after 0.5 msec initial delay we expect to find 3 PLL lock - * indications within 10 msec for successful PLL detection. - */ - udelay(500); - count = 0; - for (i = 500; i <= 10000; i += 50) { /* max 10 msec */ - pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &lock); - if (lock&CLKCTL_LOCK) { - if (++count >= 3) - break; - } - udelay(50); - } - if (count < 3) { - if (clksrc == 1) { /* explicitly asked for PLL hence bail out */ - net_err_ratelimited("%s: no PLL or failed to lock!\n", - __func__); - clkctl = CLKCTL_CLKSTP; - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); - return -1; - } - else /* was: clksrc=0(auto) */ - clksrc = 3; /* fallback to 40MHz XCLK (OB800) */ - - pr_debug("%s: PLL not locked, fallback to clksrc=%d\n", - __func__, clksrc); - } - else - clksrc = 1; /* got successful PLL lock */ - } - - if (clksrc != 1) { - /* we get here if either no PLL detected in auto-mode or - an external clock source was explicitly specified */ - - clkctl = CLKCTL_EXTCLK | CLKCTL_CLKSTP; - if (clksrc == 3) - clkctl |= CLKCTL_XCKSEL; - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); - - /* no way to test for working XCLK */ - } - else - pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl); - - /* ok, now going to connect the chip with the clock source */ - - clkctl &= ~CLKCTL_CLKSTP; - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); - - return 0; -} - -static void vlsi_stop_clock(struct pci_dev *pdev) -{ - u8 clkctl; - - /* disconnect chip from clock source */ - pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl); - clkctl |= CLKCTL_CLKSTP; - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); - - /* disable all clock sources */ - clkctl &= ~(CLKCTL_EXTCLK | CLKCTL_PD_INV); - pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); -} - -/********************************************************/ - -/* writing all-zero to the VLSI PCI IO register area seems to prevent - * some occasional situations where the hardware fails (symptoms are - * what appears as stalled tx/rx state machines, i.e. everything ok for - * receive or transmit but hw makes no progress or is unable to access - * the bus memory locations). - * Best place to call this is immediately after/before the internal clock - * gets started/stopped. - */ - -static inline void vlsi_clear_regs(unsigned iobase) -{ - unsigned i; - const unsigned chip_io_extent = 32; - - for (i = 0; i < chip_io_extent; i += sizeof(u16)) - outw(0, iobase + i); -} - -static int vlsi_init_chip(struct pci_dev *pdev) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - vlsi_irda_dev_t *idev = netdev_priv(ndev); - unsigned iobase; - u16 ptr; - - /* start the clock and clean the registers */ - - if (vlsi_start_clock(pdev)) { - net_err_ratelimited("%s: no valid clock source\n", __func__); - return -1; - } - iobase = ndev->base_addr; - vlsi_clear_regs(iobase); - - outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); /* w/c pending IRQ, disable all INT */ - - outw(0, iobase+VLSI_PIO_IRENABLE); /* disable IrPHY-interface */ - - /* disable everything, particularly IRCFG_MSTR - (also resetting the RING_PTR) */ - - outw(0, iobase+VLSI_PIO_IRCFG); - wmb(); - - outw(MAX_PACKET_LENGTH, iobase+VLSI_PIO_MAXPKT); /* max possible value=0x0fff */ - - outw(BUS_TO_RINGBASE(idev->busaddr), iobase+VLSI_PIO_RINGBASE); - - outw(TX_RX_TO_RINGSIZE(idev->tx_ring->size, idev->rx_ring->size), - iobase+VLSI_PIO_RINGSIZE); - - ptr = inw(iobase+VLSI_PIO_RINGPTR); - atomic_set(&idev->rx_ring->head, RINGPTR_GET_RX(ptr)); - atomic_set(&idev->rx_ring->tail, RINGPTR_GET_RX(ptr)); - atomic_set(&idev->tx_ring->head, RINGPTR_GET_TX(ptr)); - atomic_set(&idev->tx_ring->tail, RINGPTR_GET_TX(ptr)); - - vlsi_set_baud(idev, iobase); /* idev->new_baud used as provided by caller */ - - outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); /* just in case - w/c pending IRQ's */ - wmb(); - - /* DO NOT BLINDLY ENABLE IRINTR_ACTEN! - * basically every received pulse fires an ACTIVITY-INT - * leading to >>1000 INT's per second instead of few 10 - */ - - outb(IRINTR_RPKTEN|IRINTR_TPKTEN, iobase+VLSI_PIO_IRINTR); - - return 0; -} - -static int vlsi_start_hw(vlsi_irda_dev_t *idev) -{ - struct pci_dev *pdev = idev->pdev; - struct net_device *ndev = pci_get_drvdata(pdev); - unsigned iobase = ndev->base_addr; - u8 byte; - - /* we don't use the legacy UART, disable its address decoding */ - - pci_read_config_byte(pdev, VLSI_PCI_IRMISC, &byte); - byte &= ~(IRMISC_UARTEN | IRMISC_UARTTST); - pci_write_config_byte(pdev, VLSI_PCI_IRMISC, byte); - - /* enable PCI busmaster access to our 16MB page */ - - pci_write_config_byte(pdev, VLSI_PCI_MSTRPAGE, MSTRPAGE_VALUE); - pci_set_master(pdev); - - if (vlsi_init_chip(pdev) < 0) { - pci_disable_device(pdev); - return -1; - } - - vlsi_fill_rx(idev->rx_ring); - - idev->last_rx = ktime_get(); /* first mtt may start from now on */ - - outw(0, iobase+VLSI_PIO_PROMPT); /* kick hw state machine */ - - return 0; -} - -static int vlsi_stop_hw(vlsi_irda_dev_t *idev) -{ - struct pci_dev *pdev = idev->pdev; - struct net_device *ndev = pci_get_drvdata(pdev); - unsigned iobase = ndev->base_addr; - unsigned long flags; - - spin_lock_irqsave(&idev->lock,flags); - outw(0, iobase+VLSI_PIO_IRENABLE); - outw(0, iobase+VLSI_PIO_IRCFG); /* disable everything */ - - /* disable and w/c irqs */ - outb(0, iobase+VLSI_PIO_IRINTR); - wmb(); - outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); - spin_unlock_irqrestore(&idev->lock,flags); - - vlsi_unarm_tx(idev); - vlsi_unarm_rx(idev); - - vlsi_clear_regs(iobase); - vlsi_stop_clock(pdev); - - pci_disable_device(pdev); - - return 0; -} - -/**************************************************************/ - -static void vlsi_tx_timeout(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - - - vlsi_reg_debug(ndev->base_addr, __func__); - vlsi_ring_debug(idev->tx_ring); - - if (netif_running(ndev)) - netif_stop_queue(ndev); - - vlsi_stop_hw(idev); - - /* now simply restart the whole thing */ - - if (!idev->new_baud) - idev->new_baud = idev->baud; /* keep current baudrate */ - - if (vlsi_start_hw(idev)) - net_err_ratelimited("%s: failed to restart hw - %s(%s) unusable!\n", - __func__, pci_name(idev->pdev), ndev->name); - else - netif_start_queue(ndev); -} - -static int vlsi_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - struct if_irda_req *irq = (struct if_irda_req *) rq; - unsigned long flags; - u16 fifocnt; - int ret = 0; - - switch (cmd) { - case SIOCSBANDWIDTH: - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - spin_lock_irqsave(&idev->lock, flags); - idev->new_baud = irq->ifr_baudrate; - /* when called from userland there might be a minor race window here - * if the stack tries to change speed concurrently - which would be - * pretty strange anyway with the userland having full control... - */ - vlsi_set_baud(idev, ndev->base_addr); - spin_unlock_irqrestore(&idev->lock, flags); - break; - case SIOCSMEDIABUSY: - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - irda_device_set_media_busy(ndev, TRUE); - break; - case SIOCGRECEIVING: - /* the best we can do: check whether there are any bytes in rx fifo. - * The trustable window (in case some data arrives just afterwards) - * may be as short as 1usec or so at 4Mbps. - */ - fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; - irq->ifr_receiving = (fifocnt!=0) ? 1 : 0; - break; - default: - net_warn_ratelimited("%s: notsupp - cmd=%04x\n", - __func__, cmd); - ret = -EOPNOTSUPP; - } - - return ret; -} - -/********************************************************/ - -static irqreturn_t vlsi_interrupt(int irq, void *dev_instance) -{ - struct net_device *ndev = dev_instance; - vlsi_irda_dev_t *idev = netdev_priv(ndev); - unsigned iobase; - u8 irintr; - int boguscount = 5; - unsigned long flags; - int handled = 0; - - iobase = ndev->base_addr; - spin_lock_irqsave(&idev->lock,flags); - do { - irintr = inb(iobase+VLSI_PIO_IRINTR); - mb(); - outb(irintr, iobase+VLSI_PIO_IRINTR); /* acknowledge asap */ - - if (!(irintr&=IRINTR_INT_MASK)) /* not our INT - probably shared */ - break; - - handled = 1; - - if (unlikely(!(irintr & ~IRINTR_ACTIVITY))) - break; /* nothing todo if only activity */ - - if (irintr&IRINTR_RPKTINT) - vlsi_rx_interrupt(ndev); - - if (irintr&IRINTR_TPKTINT) - vlsi_tx_interrupt(ndev); - - } while (--boguscount > 0); - spin_unlock_irqrestore(&idev->lock,flags); - - if (boguscount <= 0) - net_info_ratelimited("%s: too much work in interrupt!\n", - __func__); - return IRQ_RETVAL(handled); -} - -/********************************************************/ - -static int vlsi_open(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - int err = -EAGAIN; - char hwname[32]; - - if (pci_request_regions(idev->pdev, drivername)) { - net_warn_ratelimited("%s: io resource busy\n", __func__); - goto errout; - } - ndev->base_addr = pci_resource_start(idev->pdev,0); - ndev->irq = idev->pdev->irq; - - /* under some rare occasions the chip apparently comes up with - * IRQ's pending. We better w/c pending IRQ and disable them all - */ - - outb(IRINTR_INT_MASK, ndev->base_addr+VLSI_PIO_IRINTR); - - if (request_irq(ndev->irq, vlsi_interrupt, IRQF_SHARED, - drivername, ndev)) { - net_warn_ratelimited("%s: couldn't get IRQ: %d\n", - __func__, ndev->irq); - goto errout_io; - } - - if ((err = vlsi_create_hwif(idev)) != 0) - goto errout_irq; - - sprintf(hwname, "VLSI-FIR @ 0x%04x", (unsigned)ndev->base_addr); - idev->irlap = irlap_open(ndev,&idev->qos,hwname); - if (!idev->irlap) - goto errout_free_ring; - - idev->last_rx = ktime_get(); /* first mtt may start from now on */ - - idev->new_baud = 9600; /* start with IrPHY using 9600(SIR) mode */ - - if ((err = vlsi_start_hw(idev)) != 0) - goto errout_close_irlap; - - netif_start_queue(ndev); - - net_info_ratelimited("%s: device %s operational\n", - __func__, ndev->name); - - return 0; - -errout_close_irlap: - irlap_close(idev->irlap); -errout_free_ring: - vlsi_destroy_hwif(idev); -errout_irq: - free_irq(ndev->irq,ndev); -errout_io: - pci_release_regions(idev->pdev); -errout: - return err; -} - -static int vlsi_close(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - - netif_stop_queue(ndev); - - if (idev->irlap) - irlap_close(idev->irlap); - idev->irlap = NULL; - - vlsi_stop_hw(idev); - - vlsi_destroy_hwif(idev); - - free_irq(ndev->irq,ndev); - - pci_release_regions(idev->pdev); - - net_info_ratelimited("%s: device %s stopped\n", __func__, ndev->name); - - return 0; -} - -static const struct net_device_ops vlsi_netdev_ops = { - .ndo_open = vlsi_open, - .ndo_stop = vlsi_close, - .ndo_start_xmit = vlsi_hard_start_xmit, - .ndo_do_ioctl = vlsi_ioctl, - .ndo_tx_timeout = vlsi_tx_timeout, -}; - -static int vlsi_irda_init(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - struct pci_dev *pdev = idev->pdev; - - ndev->irq = pdev->irq; - ndev->base_addr = pci_resource_start(pdev,0); - - /* PCI busmastering - * see include file for details why we need these 2 masks, in this order! - */ - - if (pci_set_dma_mask(pdev,DMA_MASK_USED_BY_HW) || - pci_set_dma_mask(pdev,DMA_MASK_MSTRPAGE)) { - net_err_ratelimited("%s: aborting due to PCI BM-DMA address limitations\n", - __func__); - return -1; - } - - irda_init_max_qos_capabilies(&idev->qos); - - /* the VLSI82C147 does not support 576000! */ - - idev->qos.baud_rate.bits = IR_2400 | IR_9600 - | IR_19200 | IR_38400 | IR_57600 | IR_115200 - | IR_1152000 | (IR_4000000 << 8); - - idev->qos.min_turn_time.bits = qos_mtt_bits; - - irda_qos_bits_to_value(&idev->qos); - - /* currently no public media definitions for IrDA */ - - ndev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; - ndev->if_port = IF_PORT_UNKNOWN; - - ndev->netdev_ops = &vlsi_netdev_ops; - ndev->watchdog_timeo = 500*HZ/1000; /* max. allowed turn time for IrLAP */ - - SET_NETDEV_DEV(ndev, &pdev->dev); - - return 0; -} - -/**************************************************************/ - -static int -vlsi_irda_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct net_device *ndev; - vlsi_irda_dev_t *idev; - - if (pci_enable_device(pdev)) - goto out; - else - pdev->current_state = 0; /* hw must be running now */ - - net_info_ratelimited("%s: IrDA PCI controller %s detected\n", - drivername, pci_name(pdev)); - - if ( !pci_resource_start(pdev,0) || - !(pci_resource_flags(pdev,0) & IORESOURCE_IO) ) { - net_err_ratelimited("%s: bar 0 invalid", __func__); - goto out_disable; - } - - ndev = alloc_irdadev(sizeof(*idev)); - if (ndev==NULL) { - net_err_ratelimited("%s: Unable to allocate device memory.\n", - __func__); - goto out_disable; - } - - idev = netdev_priv(ndev); - - spin_lock_init(&idev->lock); - mutex_init(&idev->mtx); - mutex_lock(&idev->mtx); - idev->pdev = pdev; - - if (vlsi_irda_init(ndev) < 0) - goto out_freedev; - - if (register_netdev(ndev) < 0) { - net_err_ratelimited("%s: register_netdev failed\n", __func__); - goto out_freedev; - } - - if (vlsi_proc_root != NULL) { - struct proc_dir_entry *ent; - - ent = proc_create_data(ndev->name, S_IFREG|S_IRUGO, - vlsi_proc_root, VLSI_PROC_FOPS, ndev); - if (!ent) { - net_warn_ratelimited("%s: failed to create proc entry\n", - __func__); - } else { - proc_set_size(ent, 0); - } - idev->proc_entry = ent; - } - net_info_ratelimited("%s: registered device %s\n", - drivername, ndev->name); - - pci_set_drvdata(pdev, ndev); - mutex_unlock(&idev->mtx); - - return 0; - -out_freedev: - mutex_unlock(&idev->mtx); - free_netdev(ndev); -out_disable: - pci_disable_device(pdev); -out: - return -ENODEV; -} - -static void vlsi_irda_remove(struct pci_dev *pdev) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - vlsi_irda_dev_t *idev; - - if (!ndev) { - net_err_ratelimited("%s: lost netdevice?\n", drivername); - return; - } - - unregister_netdev(ndev); - - idev = netdev_priv(ndev); - mutex_lock(&idev->mtx); - if (idev->proc_entry) { - remove_proc_entry(ndev->name, vlsi_proc_root); - idev->proc_entry = NULL; - } - mutex_unlock(&idev->mtx); - - free_netdev(ndev); - - net_info_ratelimited("%s: %s removed\n", drivername, pci_name(pdev)); -} - -#ifdef CONFIG_PM - -/* The Controller doesn't provide PCI PM capabilities as defined by PCI specs. - * Some of the Linux PCI-PM code however depends on this, for example in - * pci_set_power_state(). So we have to take care to perform the required - * operations on our own (particularly reflecting the pdev->current_state) - * otherwise we might get cheated by pci-pm. - */ - - -static int vlsi_irda_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - vlsi_irda_dev_t *idev; - - if (!ndev) { - net_err_ratelimited("%s - %s: no netdevice\n", - __func__, pci_name(pdev)); - return 0; - } - idev = netdev_priv(ndev); - mutex_lock(&idev->mtx); - if (pdev->current_state != 0) { /* already suspended */ - if (state.event > pdev->current_state) { /* simply go deeper */ - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - pdev->current_state = state.event; - } - else - net_err_ratelimited("%s - %s: invalid suspend request %u -> %u\n", - __func__, pci_name(pdev), - pdev->current_state, state.event); - mutex_unlock(&idev->mtx); - return 0; - } - - if (netif_running(ndev)) { - netif_device_detach(ndev); - vlsi_stop_hw(idev); - pci_save_state(pdev); - if (!idev->new_baud) - /* remember speed settings to restore on resume */ - idev->new_baud = idev->baud; - } - - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - pdev->current_state = state.event; - idev->resume_ok = 1; - mutex_unlock(&idev->mtx); - return 0; -} - -static int vlsi_irda_resume(struct pci_dev *pdev) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - vlsi_irda_dev_t *idev; - - if (!ndev) { - net_err_ratelimited("%s - %s: no netdevice\n", - __func__, pci_name(pdev)); - return 0; - } - idev = netdev_priv(ndev); - mutex_lock(&idev->mtx); - if (pdev->current_state == 0) { - mutex_unlock(&idev->mtx); - net_warn_ratelimited("%s - %s: already resumed\n", - __func__, pci_name(pdev)); - return 0; - } - - pci_set_power_state(pdev, PCI_D0); - pdev->current_state = PM_EVENT_ON; - - if (!idev->resume_ok) { - /* should be obsolete now - but used to happen due to: - * - pci layer initially setting pdev->current_state = 4 (unknown) - * - pci layer did not walk the save_state-tree (might be APM problem) - * so we could not refuse to suspend from undefined state - * - vlsi_irda_suspend detected invalid state and refused to save - * configuration for resume - but was too late to stop suspending - * - vlsi_irda_resume got screwed when trying to resume from garbage - * - * now we explicitly set pdev->current_state = 0 after enabling the - * device and independently resume_ok should catch any garbage config. - */ - net_warn_ratelimited("%s - hm, nothing to resume?\n", __func__); - mutex_unlock(&idev->mtx); - return 0; - } - - if (netif_running(ndev)) { - pci_restore_state(pdev); - vlsi_start_hw(idev); - netif_device_attach(ndev); - } - idev->resume_ok = 0; - mutex_unlock(&idev->mtx); - return 0; -} - -#endif /* CONFIG_PM */ - -/*********************************************************/ - -static struct pci_driver vlsi_irda_driver = { - .name = drivername, - .id_table = vlsi_irda_table, - .probe = vlsi_irda_probe, - .remove = vlsi_irda_remove, -#ifdef CONFIG_PM - .suspend = vlsi_irda_suspend, - .resume = vlsi_irda_resume, -#endif -}; - -#define PROC_DIR ("driver/" DRIVER_NAME) - -static int __init vlsi_mod_init(void) -{ - int i, ret; - - if (clksrc < 0 || clksrc > 3) { - net_err_ratelimited("%s: invalid clksrc=%d\n", - drivername, clksrc); - return -1; - } - - for (i = 0; i < 2; i++) { - switch(ringsize[i]) { - case 4: - case 8: - case 16: - case 32: - case 64: - break; - default: - net_warn_ratelimited("%s: invalid %s ringsize %d, using default=8\n", - drivername, - i ? "rx" : "tx", - ringsize[i]); - ringsize[i] = 8; - break; - } - } - - sirpulse = !!sirpulse; - - /* proc_mkdir returns NULL if !CONFIG_PROC_FS. - * Failure to create the procfs entry is handled like running - * without procfs - it's not required for the driver to work. - */ - vlsi_proc_root = proc_mkdir(PROC_DIR, NULL); - - ret = pci_register_driver(&vlsi_irda_driver); - - if (ret && vlsi_proc_root) - remove_proc_entry(PROC_DIR, NULL); - return ret; - -} - -static void __exit vlsi_mod_exit(void) -{ - pci_unregister_driver(&vlsi_irda_driver); - if (vlsi_proc_root) - remove_proc_entry(PROC_DIR, NULL); -} - -module_init(vlsi_mod_init); -module_exit(vlsi_mod_exit); diff --git a/drivers/staging/irda/drivers/vlsi_ir.h b/drivers/staging/irda/drivers/vlsi_ir.h deleted file mode 100644 index f9db2ce4c5c6..000000000000 --- a/drivers/staging/irda/drivers/vlsi_ir.h +++ /dev/null @@ -1,757 +0,0 @@ - -/********************************************************************* - * - * vlsi_ir.h: VLSI82C147 PCI IrDA controller driver for Linux - * - * Version: 0.5 - * - * Copyright (c) 2001-2003 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRDA_VLSI_FIR_H -#define IRDA_VLSI_FIR_H - -/* ================================================================ - * compatibility stuff - */ - -/* definitions not present in pci_ids.h */ - -#ifndef PCI_CLASS_WIRELESS_IRDA -#define PCI_CLASS_WIRELESS_IRDA 0x0d00 -#endif - -#ifndef PCI_CLASS_SUBCLASS_MASK -#define PCI_CLASS_SUBCLASS_MASK 0xffff -#endif - -/* ================================================================ */ - -/* non-standard PCI registers */ - -enum vlsi_pci_regs { - VLSI_PCI_CLKCTL = 0x40, /* chip clock input control */ - VLSI_PCI_MSTRPAGE = 0x41, /* addr [31:24] for all busmaster cycles */ - VLSI_PCI_IRMISC = 0x42 /* mainly legacy UART related */ -}; - -/* ------------------------------------------ */ - -/* VLSI_PCI_CLKCTL: Clock Control Register (u8, rw) */ - -/* Three possible clock sources: either on-chip 48MHz PLL or - * external clock applied to EXTCLK pin. External clock may - * be either 48MHz or 40MHz, which is indicated by XCKSEL. - * CLKSTP controls whether the selected clock source gets - * connected to the IrDA block. - * - * On my HP OB-800 the BIOS sets external 40MHz clock as source - * when IrDA enabled and I've never detected any PLL lock success. - * Apparently the 14.3...MHz OSC input required for the PLL to work - * is not connected and the 40MHz EXTCLK is provided externally. - * At least this is what makes the driver working for me. - */ - -enum vlsi_pci_clkctl { - - /* PLL control */ - - CLKCTL_PD_INV = 0x04, /* PD#: inverted power down signal, - * i.e. PLL is powered, if PD_INV set */ - CLKCTL_LOCK = 0x40, /* (ro) set, if PLL is locked */ - - /* clock source selection */ - - CLKCTL_EXTCLK = 0x20, /* set to select external clock input, not PLL */ - CLKCTL_XCKSEL = 0x10, /* set to indicate EXTCLK is 40MHz, not 48MHz */ - - /* IrDA block control */ - - CLKCTL_CLKSTP = 0x80, /* set to disconnect from selected clock source */ - CLKCTL_WAKE = 0x08 /* set to enable wakeup feature: whenever IR activity - * is detected, PD_INV gets set(?) and CLKSTP cleared */ -}; - -/* ------------------------------------------ */ - -/* VLSI_PCI_MSTRPAGE: Master Page Register (u8, rw) and busmastering stuff */ - -#define DMA_MASK_USED_BY_HW 0xffffffff -#define DMA_MASK_MSTRPAGE 0x00ffffff -#define MSTRPAGE_VALUE (DMA_MASK_MSTRPAGE >> 24) - - /* PCI busmastering is somewhat special for this guy - in short: - * - * We select to operate using fixed MSTRPAGE=0, use ISA DMA - * address restrictions to make the PCI BM api aware of this, - * but ensure the hardware is dealing with real 32bit access. - * - * In detail: - * The chip executes normal 32bit busmaster cycles, i.e. - * drives all 32 address lines. These addresses however are - * composed of [0:23] taken from various busaddr-pointers - * and [24:31] taken from the MSTRPAGE register in the VLSI82C147 - * config space. Therefore _all_ busmastering must be - * targeted to/from one single 16MB (busaddr-) superpage! - * The point is to make sure all the allocations for memory - * locations with busmaster access (ring descriptors, buffers) - * are indeed bus-mappable to the same 16MB range (for x86 this - * means they must reside in the same 16MB physical memory address - * range). The only constraint we have which supports "several objects - * mappable to common 16MB range" paradigma, is the old ISA DMA - * restriction to the first 16MB of physical address range. - * Hence the approach here is to enable PCI busmaster support using - * the correct 32bit dma-mask used by the chip. Afterwards the device's - * dma-mask gets restricted to 24bit, which must be honoured somehow by - * all allocations for memory areas to be exposed to the chip ... - * - * Note: - * Don't be surprised to get "Setting latency timer..." messages every - * time when PCI busmastering is enabled for the chip. - * The chip has its PCI latency timer RO fixed at 0 - which is not a - * problem here, because it is never requesting _burst_ transactions. - */ - -/* ------------------------------------------ */ - -/* VLSI_PCIIRMISC: IR Miscellaneous Register (u8, rw) */ - -/* legacy UART emulation - not used by this driver - would require: - * (see below for some register-value definitions) - * - * - IRMISC_UARTEN must be set to enable UART address decoding - * - IRMISC_UARTSEL configured - * - IRCFG_MASTER must be cleared - * - IRCFG_SIR must be set - * - IRENABLE_PHYANDCLOCK must be asserted 0->1 (and hence IRENABLE_SIR_ON) - */ - -enum vlsi_pci_irmisc { - - /* IR transceiver control */ - - IRMISC_IRRAIL = 0x40, /* (ro?) IR rail power indication (and control?) - * 0=3.3V / 1=5V. Probably set during power-on? - * unclear - not touched by driver */ - IRMISC_IRPD = 0x08, /* transceiver power down, if set */ - - /* legacy UART control */ - - IRMISC_UARTTST = 0x80, /* UART test mode - "always write 0" */ - IRMISC_UARTEN = 0x04, /* enable UART address decoding */ - - /* bits [1:0] IRMISC_UARTSEL to select legacy UART address */ - - IRMISC_UARTSEL_3f8 = 0x00, - IRMISC_UARTSEL_2f8 = 0x01, - IRMISC_UARTSEL_3e8 = 0x02, - IRMISC_UARTSEL_2e8 = 0x03 -}; - -/* ================================================================ */ - -/* registers mapped to 32 byte PCI IO space */ - -/* note: better access all registers at the indicated u8/u16 size - * although some of them contain only 1 byte of information. - * some of them (particaluarly PROMPT and IRCFG) ignore - * access when using the wrong addressing mode! - */ - -enum vlsi_pio_regs { - VLSI_PIO_IRINTR = 0x00, /* interrupt enable/request (u8, rw) */ - VLSI_PIO_RINGPTR = 0x02, /* rx/tx ring pointer (u16, ro) */ - VLSI_PIO_RINGBASE = 0x04, /* [23:10] of ring address (u16, rw) */ - VLSI_PIO_RINGSIZE = 0x06, /* rx/tx ring size (u16, rw) */ - VLSI_PIO_PROMPT = 0x08, /* triggers ring processing (u16, wo) */ - /* 0x0a-0x0f: reserved / duplicated UART regs */ - VLSI_PIO_IRCFG = 0x10, /* configuration select (u16, rw) */ - VLSI_PIO_SIRFLAG = 0x12, /* BOF/EOF for filtered SIR (u16, ro) */ - VLSI_PIO_IRENABLE = 0x14, /* enable and status register (u16, rw/ro) */ - VLSI_PIO_PHYCTL = 0x16, /* physical layer current status (u16, ro) */ - VLSI_PIO_NPHYCTL = 0x18, /* next physical layer select (u16, rw) */ - VLSI_PIO_MAXPKT = 0x1a, /* [11:0] max len for packet receive (u16, rw) */ - VLSI_PIO_RCVBCNT = 0x1c /* current receive-FIFO byte count (u16, ro) */ - /* 0x1e-0x1f: reserved / duplicated UART regs */ -}; - -/* ------------------------------------------ */ - -/* VLSI_PIO_IRINTR: Interrupt Register (u8, rw) */ - -/* enable-bits: - * 1 = enable / 0 = disable - * interrupt condition bits: - * set according to corresponding interrupt source - * (regardless of the state of the enable bits) - * enable bit status indicates whether interrupt gets raised - * write-to-clear - * note: RPKTINT and TPKTINT behave different in legacy UART mode (which we don't use :-) - */ - -enum vlsi_pio_irintr { - IRINTR_ACTEN = 0x80, /* activity interrupt enable */ - IRINTR_ACTIVITY = 0x40, /* activity monitor (traffic detected) */ - IRINTR_RPKTEN = 0x20, /* receive packet interrupt enable*/ - IRINTR_RPKTINT = 0x10, /* rx-packet transferred from fifo to memory finished */ - IRINTR_TPKTEN = 0x08, /* transmit packet interrupt enable */ - IRINTR_TPKTINT = 0x04, /* last bit of tx-packet+crc shifted to ir-pulser */ - IRINTR_OE_EN = 0x02, /* UART rx fifo overrun error interrupt enable */ - IRINTR_OE_INT = 0x01 /* UART rx fifo overrun error (read LSR to clear) */ -}; - -/* we use this mask to check whether the (shared PCI) interrupt is ours */ - -#define IRINTR_INT_MASK (IRINTR_ACTIVITY|IRINTR_RPKTINT|IRINTR_TPKTINT) - -/* ------------------------------------------ */ - -/* VLSI_PIO_RINGPTR: Ring Pointer Read-Back Register (u16, ro) */ - -/* _both_ ring pointers are indices relative to the _entire_ rx,tx-ring! - * i.e. the referenced descriptor is located - * at RINGBASE + PTR * sizeof(descr) for rx and tx - * therefore, the tx-pointer has offset MAX_RING_DESCR - */ - -#define MAX_RING_DESCR 64 /* tx, rx rings may contain up to 64 descr each */ - -#define RINGPTR_RX_MASK (MAX_RING_DESCR-1) -#define RINGPTR_TX_MASK ((MAX_RING_DESCR-1)<<8) - -#define RINGPTR_GET_RX(p) ((p)&RINGPTR_RX_MASK) -#define RINGPTR_GET_TX(p) (((p)&RINGPTR_TX_MASK)>>8) - -/* ------------------------------------------ */ - -/* VLSI_PIO_RINGBASE: Ring Pointer Base Address Register (u16, ro) */ - -/* Contains [23:10] part of the ring base (bus-) address - * which must be 1k-alinged. [31:24] is taken from - * VLSI_PCI_MSTRPAGE above. - * The controller initiates non-burst PCI BM cycles to - * fetch and update the descriptors in the ring. - * Once fetched, the descriptor remains cached onchip - * until it gets closed and updated due to the ring - * processing state machine. - * The entire ring area is split in rx and tx areas with each - * area consisting of 64 descriptors of 8 bytes each. - * The rx(tx) ring is located at ringbase+0 (ringbase+64*8). - */ - -#define BUS_TO_RINGBASE(p) (((p)>>10)&0x3fff) - -/* ------------------------------------------ */ - -/* VLSI_PIO_RINGSIZE: Ring Size Register (u16, rw) */ - -/* bit mask to indicate the ring size to be used for rx and tx. - * possible values encoded bits - * 4 0000 - * 8 0001 - * 16 0011 - * 32 0111 - * 64 1111 - * located at [15:12] for tx and [11:8] for rx ([7:0] unused) - * - * note: probably a good idea to have IRCFG_MSTR cleared when writing - * this so the state machines are stopped and the RINGPTR is reset! - */ - -#define SIZE_TO_BITS(num) ((((num)-1)>>2)&0x0f) -#define TX_RX_TO_RINGSIZE(tx,rx) ((SIZE_TO_BITS(tx)<<12)|(SIZE_TO_BITS(rx)<<8)) -#define RINGSIZE_TO_RXSIZE(rs) ((((rs)&0x0f00)>>6)+4) -#define RINGSIZE_TO_TXSIZE(rs) ((((rs)&0xf000)>>10)+4) - - -/* ------------------------------------------ */ - -/* VLSI_PIO_PROMPT: Ring Prompting Register (u16, write-to-start) */ - -/* writing any value kicks the ring processing state machines - * for both tx, rx rings as follows: - * - active rings (currently owning an active descriptor) - * ignore the prompt and continue - * - idle rings fetch the next descr from the ring and start - * their processing - */ - -/* ------------------------------------------ */ - -/* VLSI_PIO_IRCFG: IR Config Register (u16, rw) */ - -/* notes: - * - not more than one SIR/MIR/FIR bit must be set at any time - * - SIR, MIR, FIR and CRC16 select the configuration which will - * be applied on next 0->1 transition of IRENABLE_PHYANDCLOCK (see below). - * - besides allowing the PCI interface to execute busmaster cycles - * and therefore the ring SM to operate, the MSTR bit has side-effects: - * when MSTR is cleared, the RINGPTR's get reset and the legacy UART mode - * (in contrast to busmaster access mode) gets enabled. - * - clearing ENRX or setting ENTX while data is received may stall the - * receive fifo until ENRX reenabled _and_ another packet arrives - * - SIRFILT means the chip performs the required unwrapping of hardware - * headers (XBOF's, BOF/EOF) and un-escaping in the _receive_ direction. - * Only the resulting IrLAP payload is copied to the receive buffers - - * but with the 16bit FCS still encluded. Question remains, whether it - * was already checked or we should do it before passing the packet to IrLAP? - */ - -enum vlsi_pio_ircfg { - IRCFG_LOOP = 0x4000, /* enable loopback test mode */ - IRCFG_ENTX = 0x1000, /* transmit enable */ - IRCFG_ENRX = 0x0800, /* receive enable */ - IRCFG_MSTR = 0x0400, /* master enable */ - IRCFG_RXANY = 0x0200, /* receive any packet */ - IRCFG_CRC16 = 0x0080, /* 16bit (not 32bit) CRC select for MIR/FIR */ - IRCFG_FIR = 0x0040, /* FIR 4PPM encoding mode enable */ - IRCFG_MIR = 0x0020, /* MIR HDLC encoding mode enable */ - IRCFG_SIR = 0x0010, /* SIR encoding mode enable */ - IRCFG_SIRFILT = 0x0008, /* enable SIR decode filter (receiver unwrapping) */ - IRCFG_SIRTEST = 0x0004, /* allow SIR decode filter when not in SIR mode */ - IRCFG_TXPOL = 0x0002, /* invert tx polarity when set */ - IRCFG_RXPOL = 0x0001 /* invert rx polarity when set */ -}; - -/* ------------------------------------------ */ - -/* VLSI_PIO_SIRFLAG: SIR Flag Register (u16, ro) */ - -/* register contains hardcoded BOF=0xc0 at [7:0] and EOF=0xc1 at [15:8] - * which is used for unwrapping received frames in SIR decode-filter mode - */ - -/* ------------------------------------------ */ - -/* VLSI_PIO_IRENABLE: IR Enable Register (u16, rw/ro) */ - -/* notes: - * - IREN acts as gate for latching the configured IR mode information - * from IRCFG and IRPHYCTL when IREN=reset and applying them when - * IREN gets set afterwards. - * - ENTXST reflects IRCFG_ENTX - * - ENRXST = IRCFG_ENRX && (!IRCFG_ENTX || IRCFG_LOOP) - */ - -enum vlsi_pio_irenable { - IRENABLE_PHYANDCLOCK = 0x8000, /* enable IR phy and gate the mode config (rw) */ - IRENABLE_CFGER = 0x4000, /* mode configuration error (ro) */ - IRENABLE_FIR_ON = 0x2000, /* FIR on status (ro) */ - IRENABLE_MIR_ON = 0x1000, /* MIR on status (ro) */ - IRENABLE_SIR_ON = 0x0800, /* SIR on status (ro) */ - IRENABLE_ENTXST = 0x0400, /* transmit enable status (ro) */ - IRENABLE_ENRXST = 0x0200, /* Receive enable status (ro) */ - IRENABLE_CRC16_ON = 0x0100 /* 16bit (not 32bit) CRC enabled status (ro) */ -}; - -#define IRENABLE_MASK 0xff00 /* Read mask */ - -/* ------------------------------------------ */ - -/* VLSI_PIO_PHYCTL: IR Physical Layer Current Control Register (u16, ro) */ - -/* read-back of the currently applied physical layer status. - * applied from VLSI_PIO_NPHYCTL at rising edge of IRENABLE_PHYANDCLOCK - * contents identical to VLSI_PIO_NPHYCTL (see below) - */ - -/* ------------------------------------------ */ - -/* VLSI_PIO_NPHYCTL: IR Physical Layer Next Control Register (u16, rw) */ - -/* latched during IRENABLE_PHYANDCLOCK=0 and applied at 0-1 transition - * - * consists of BAUD[15:10], PLSWID[9:5] and PREAMB[4:0] bits defined as follows: - * - * SIR-mode: BAUD = (115.2kHz / baudrate) - 1 - * PLSWID = (pulsetime * freq / (BAUD+1)) - 1 - * where pulsetime is the requested IrPHY pulse width - * and freq is 8(16)MHz for 40(48)MHz primary input clock - * PREAMB: don't care for SIR - * - * The nominal SIR pulse width is 3/16 bit time so we have PLSWID=12 - * fixed for all SIR speeds at 40MHz input clock (PLSWID=24 at 48MHz). - * IrPHY also allows shorter pulses down to the nominal pulse duration - * at 115.2kbaud (minus some tolerance) which is 1.41 usec. - * Using the expression PLSWID = 12/(BAUD+1)-1 (multiplied by two for 48MHz) - * we get the minimum acceptable PLSWID values according to the VLSI - * specification, which provides 1.5 usec pulse width for all speeds (except - * for 2.4kbaud getting 6usec). This is fine with IrPHY v1.3 specs and - * reduces the transceiver power which drains the battery. At 9.6kbaud for - * example this amounts to more than 90% battery power saving! - * - * MIR-mode: BAUD = 0 - * PLSWID = 9(10) for 40(48) MHz input clock - * to get nominal MIR pulse width - * PREAMB = 1 - * - * FIR-mode: BAUD = 0 - * PLSWID: don't care - * PREAMB = 15 - */ - -#define PHYCTL_BAUD_SHIFT 10 -#define PHYCTL_BAUD_MASK 0xfc00 -#define PHYCTL_PLSWID_SHIFT 5 -#define PHYCTL_PLSWID_MASK 0x03e0 -#define PHYCTL_PREAMB_SHIFT 0 -#define PHYCTL_PREAMB_MASK 0x001f - -#define PHYCTL_TO_BAUD(bwp) (((bwp)&PHYCTL_BAUD_MASK)>>PHYCTL_BAUD_SHIFT) -#define PHYCTL_TO_PLSWID(bwp) (((bwp)&PHYCTL_PLSWID_MASK)>>PHYCTL_PLSWID_SHIFT) -#define PHYCTL_TO_PREAMB(bwp) (((bwp)&PHYCTL_PREAMB_MASK)>>PHYCTL_PREAMB_SHIFT) - -#define BWP_TO_PHYCTL(b,w,p) ((((b)<0) ? (tmp-1) : 0; -} - -#define PHYCTL_SIR(br,ws,cs) BWP_TO_PHYCTL(BAUD_BITS(br),calc_width_bits((br),(ws),(cs)),0) -#define PHYCTL_MIR(cs) BWP_TO_PHYCTL(0,((cs)?9:10),1) -#define PHYCTL_FIR BWP_TO_PHYCTL(0,0,15) - -/* quite ugly, I know. But implementing these calculations here avoids - * having magic numbers in the code and allows some playing with pulsewidths - * without risk to violate the standards. - * FWIW, here is the table for reference: - * - * baudrate BAUD min-PLSWID nom-PLSWID PREAMB - * 2400 47 0(0) 12(24) 0 - * 9600 11 0(0) 12(24) 0 - * 19200 5 1(2) 12(24) 0 - * 38400 2 3(6) 12(24) 0 - * 57600 1 5(10) 12(24) 0 - * 115200 0 11(22) 12(24) 0 - * MIR 0 - 9(10) 1 - * FIR 0 - 0 15 - * - * note: x(y) means x-value for 40MHz / y-value for 48MHz primary input clock - */ - -/* ------------------------------------------ */ - - -/* VLSI_PIO_MAXPKT: Maximum Packet Length register (u16, rw) */ - -/* maximum acceptable length for received packets */ - -/* hw imposed limitation - register uses only [11:0] */ -#define MAX_PACKET_LENGTH 0x0fff - -/* IrLAP I-field (apparently not defined elsewhere) */ -#define IRDA_MTU 2048 - -/* complete packet consists of A(1)+C(1)+I(<=IRDA_MTU) */ -#define IRLAP_SKB_ALLOCSIZE (1+1+IRDA_MTU) - -/* the buffers we use to exchange frames with the hardware need to be - * larger than IRLAP_SKB_ALLOCSIZE because we may have up to 4 bytes FCS - * appended and, in SIR mode, a lot of frame wrapping bytes. The worst - * case appears to be a SIR packet with I-size==IRDA_MTU and all bytes - * requiring to be escaped to provide transparency. Furthermore, the peer - * might ask for quite a number of additional XBOFs: - * up to 115+48 XBOFS 163 - * regular BOF 1 - * A-field 1 - * C-field 1 - * I-field, IRDA_MTU, all escaped 4096 - * FCS (16 bit at SIR, escaped) 4 - * EOF 1 - * AFAICS nothing in IrLAP guarantees A/C field not to need escaping - * (f.e. 0xc0/0xc1 - i.e. BOF/EOF - are legal values there) so in the - * worst case we have 4269 bytes total frame size. - * However, the VLSI uses 12 bits only for all buffer length values, - * which limits the maximum useable buffer size <= 4095. - * Note this is not a limitation in the receive case because we use - * the SIR filtering mode where the hw unwraps the frame and only the - * bare packet+fcs is stored into the buffer - in contrast to the SIR - * tx case where we have to pass frame-wrapped packets to the hw. - * If this would ever become an issue in real life, the only workaround - * I see would be using the legacy UART emulation in SIR mode. - */ - -#define XFER_BUF_SIZE MAX_PACKET_LENGTH - -/* ------------------------------------------ */ - -/* VLSI_PIO_RCVBCNT: Receive Byte Count Register (u16, ro) */ - -/* receive packet counter gets incremented on every non-filtered - * byte which was put in the receive fifo and reset for each - * new packet. Used to decide whether we are just in the middle - * of receiving - */ - -/* better apply the [11:0] mask when reading, as some docs say the - * reserved [15:12] would return 1 when reading - which is wrong AFAICS - */ -#define RCVBCNT_MASK 0x0fff - -/******************************************************************/ - -/* descriptors for rx/tx ring - * - * accessed by hardware - don't change! - * - * the descriptor is owned by hardware, when the ACTIVE status bit - * is set and nothing (besides reading status to test the bit) - * shall be done. The bit gets cleared by hw, when the descriptor - * gets closed. Premature reaping of descriptors owned be the chip - * can be achieved by disabling IRCFG_MSTR - * - * Attention: Writing addr overwrites status! - * - * ### FIXME: depends on endianess (but there ain't no non-i586 ob800 ;-) - */ - -struct ring_descr_hw { - volatile __le16 rd_count; /* tx/rx count [11:0] */ - __le16 reserved; - union { - __le32 addr; /* [23:0] of the buffer's busaddress */ - struct { - u8 addr_res[3]; - volatile u8 status; /* descriptor status */ - } __packed rd_s; - } __packed rd_u; -} __packed; - -#define rd_addr rd_u.addr -#define rd_status rd_u.rd_s.status - -/* ring descriptor status bits */ - -#define RD_ACTIVE 0x80 /* descriptor owned by hw (both TX,RX) */ - -/* TX ring descriptor status */ - -#define RD_TX_DISCRC 0x40 /* do not send CRC (for SIR) */ -#define RD_TX_BADCRC 0x20 /* force a bad CRC */ -#define RD_TX_PULSE 0x10 /* send indication pulse after this frame (MIR/FIR) */ -#define RD_TX_FRCEUND 0x08 /* force underrun */ -#define RD_TX_CLRENTX 0x04 /* clear ENTX after this frame */ -#define RD_TX_UNDRN 0x01 /* TX fifo underrun (probably PCI problem) */ - -/* RX ring descriptor status */ - -#define RD_RX_PHYERR 0x40 /* physical encoding error */ -#define RD_RX_CRCERR 0x20 /* CRC error (MIR/FIR) */ -#define RD_RX_LENGTH 0x10 /* frame exceeds buffer length */ -#define RD_RX_OVER 0x08 /* RX fifo overrun (probably PCI problem) */ -#define RD_RX_SIRBAD 0x04 /* EOF missing: BOF follows BOF (SIR, filtered) */ - -#define RD_RX_ERROR 0x7c /* any error in received frame */ - -/* the memory required to hold the 2 descriptor rings */ -#define HW_RING_AREA_SIZE (2 * MAX_RING_DESCR * sizeof(struct ring_descr_hw)) - -/******************************************************************/ - -/* sw-ring descriptors consists of a bus-mapped transfer buffer with - * associated skb and a pointer to the hw entry descriptor - */ - -struct ring_descr { - struct ring_descr_hw *hw; - struct sk_buff *skb; - void *buf; -}; - -/* wrappers for operations on hw-exposed ring descriptors - * access to the hw-part of the descriptors must use these. - */ - -static inline int rd_is_active(struct ring_descr *rd) -{ - return (rd->hw->rd_status & RD_ACTIVE) != 0; -} - -static inline void rd_activate(struct ring_descr *rd) -{ - rd->hw->rd_status |= RD_ACTIVE; -} - -static inline void rd_set_status(struct ring_descr *rd, u8 s) -{ - rd->hw->rd_status = s; /* may pass ownership to the hardware */ -} - -static inline void rd_set_addr_status(struct ring_descr *rd, dma_addr_t a, u8 s) -{ - /* order is important for two reasons: - * - overlayed: writing addr overwrites status - * - we want to write status last so we have valid address in - * case status has RD_ACTIVE set - */ - - if ((a & ~DMA_MASK_MSTRPAGE)>>24 != MSTRPAGE_VALUE) { - net_err_ratelimited("%s: pci busaddr inconsistency!\n", - __func__); - dump_stack(); - return; - } - - a &= DMA_MASK_MSTRPAGE; /* clear highbyte to make sure we won't write - * to status - just in case MSTRPAGE_VALUE!=0 - */ - rd->hw->rd_addr = cpu_to_le32(a); - wmb(); - rd_set_status(rd, s); /* may pass ownership to the hardware */ -} - -static inline void rd_set_count(struct ring_descr *rd, u16 c) -{ - rd->hw->rd_count = cpu_to_le16(c); -} - -static inline u8 rd_get_status(struct ring_descr *rd) -{ - return rd->hw->rd_status; -} - -static inline dma_addr_t rd_get_addr(struct ring_descr *rd) -{ - dma_addr_t a; - - a = le32_to_cpu(rd->hw->rd_addr); - return (a & DMA_MASK_MSTRPAGE) | (MSTRPAGE_VALUE << 24); -} - -static inline u16 rd_get_count(struct ring_descr *rd) -{ - return le16_to_cpu(rd->hw->rd_count); -} - -/******************************************************************/ - -/* sw descriptor rings for rx, tx: - * - * operations follow producer-consumer paradigm, with the hw - * in the middle doing the processing. - * ring size must be power of two. - * - * producer advances r->tail after inserting for processing - * consumer advances r->head after removing processed rd - * ring is empty if head==tail / full if (tail+1)==head - */ - -struct vlsi_ring { - struct pci_dev *pdev; - int dir; - unsigned len; - unsigned size; - unsigned mask; - atomic_t head, tail; - struct ring_descr *rd; -}; - -/* ring processing helpers */ - -static inline struct ring_descr *ring_last(struct vlsi_ring *r) -{ - int t; - - t = atomic_read(&r->tail) & r->mask; - return (((t+1) & r->mask) == (atomic_read(&r->head) & r->mask)) ? NULL : &r->rd[t]; -} - -static inline struct ring_descr *ring_put(struct vlsi_ring *r) -{ - atomic_inc(&r->tail); - return ring_last(r); -} - -static inline struct ring_descr *ring_first(struct vlsi_ring *r) -{ - int h; - - h = atomic_read(&r->head) & r->mask; - return (h == (atomic_read(&r->tail) & r->mask)) ? NULL : &r->rd[h]; -} - -static inline struct ring_descr *ring_get(struct vlsi_ring *r) -{ - atomic_inc(&r->head); - return ring_first(r); -} - -/******************************************************************/ - -/* our private compound VLSI-PCI-IRDA device information */ - -typedef struct vlsi_irda_dev { - struct pci_dev *pdev; - - struct irlap_cb *irlap; - - struct qos_info qos; - - unsigned mode; - int baud, new_baud; - - dma_addr_t busaddr; - void *virtaddr; - struct vlsi_ring *tx_ring, *rx_ring; - - ktime_t last_rx; - - spinlock_t lock; - struct mutex mtx; - - u8 resume_ok; - struct proc_dir_entry *proc_entry; - -} vlsi_irda_dev_t; - -/********************************************************/ - -/* the remapped error flags we use for returning from frame - * post-processing in vlsi_process_tx/rx() after it was completed - * by the hardware. These functions either return the >=0 number - * of transferred bytes in case of success or the negative (-) - * of the or'ed error flags. - */ - -#define VLSI_TX_DROP 0x0001 -#define VLSI_TX_FIFO 0x0002 - -#define VLSI_RX_DROP 0x0100 -#define VLSI_RX_OVER 0x0200 -#define VLSI_RX_LENGTH 0x0400 -#define VLSI_RX_FRAME 0x0800 -#define VLSI_RX_CRC 0x1000 - -/********************************************************/ - -#endif /* IRDA_VLSI_FIR_H */ - diff --git a/drivers/staging/irda/drivers/w83977af.h b/drivers/staging/irda/drivers/w83977af.h deleted file mode 100644 index 04476c2e9121..000000000000 --- a/drivers/staging/irda/drivers/w83977af.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef W83977AF_H -#define W83977AF_H - -#define W977_EFIO_BASE 0x370 -#define W977_EFIO2_BASE 0x3f0 -#define W977_DEVICE_IR 0x06 - - -/* - * Enter extended function mode - */ -static inline void w977_efm_enter(unsigned int efio) -{ - outb(0x87, efio); - outb(0x87, efio); -} - -/* - * Select a device to configure - */ - -static inline void w977_select_device(__u8 devnum, unsigned int efio) -{ - outb(0x07, efio); - outb(devnum, efio+1); -} - -/* - * Write a byte to a register - */ -static inline void w977_write_reg(__u8 reg, __u8 value, unsigned int efio) -{ - outb(reg, efio); - outb(value, efio+1); -} - -/* - * read a byte from a register - */ -static inline __u8 w977_read_reg(__u8 reg, unsigned int efio) -{ - outb(reg, efio); - return inb(efio+1); -} - -/* - * Exit extended function mode - */ -static inline void w977_efm_exit(unsigned int efio) -{ - outb(0xAA, efio); -} -#endif diff --git a/drivers/staging/irda/drivers/w83977af_ir.c b/drivers/staging/irda/drivers/w83977af_ir.c deleted file mode 100644 index 282b6c9ae05b..000000000000 --- a/drivers/staging/irda/drivers/w83977af_ir.c +++ /dev/null @@ -1,1285 +0,0 @@ -/********************************************************************* - * - * Filename: w83977af_ir.c - * Version: 1.0 - * Description: FIR driver for the Winbond W83977AF Super I/O chip - * Status: Experimental. - * Author: Paul VanderSpek - * Created at: Wed Nov 4 11:46:16 1998 - * Modified at: Fri Jan 28 12:10:59 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli - * Copyright (c) 1998-1999 Rebel.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Paul VanderSpek nor Rebel.com admit liability nor provide - * warranty for any of this software. This material is provided "AS-IS" - * and at no charge. - * - * If you find bugs in this file, its very likely that the same bug - * will also be in pc87108.c since the implementations are quite - * similar. - * - * Notice that all functions that needs to access the chip in _any_ - * way, must save BSR register on entry, and restore it on exit. - * It is _very_ important to follow this policy! - * - * __u8 bank; - * - * bank = inb( iobase+BSR); - * - * do_your_stuff_here(); - * - * outb( bank, iobase+BSR); - * - ********************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include "w83977af.h" -#include "w83977af_ir.h" - -#define CONFIG_USE_W977_PNP /* Currently needed */ -#define PIO_MAX_SPEED 115200 - -static char *driver_name = "w83977af_ir"; -static int qos_mtt_bits = 0x07; /* 1 ms or more */ - -#define CHIP_IO_EXTENT 8 - -static unsigned int io[] = { 0x180, ~0, ~0, ~0 }; -#ifdef CONFIG_ARCH_NETWINDER /* Adjust to NetWinder differences */ -static unsigned int irq[] = { 6, 0, 0, 0 }; -#else -static unsigned int irq[] = { 11, 0, 0, 0 }; -#endif -static unsigned int dma[] = { 1, 0, 0, 0 }; -static unsigned int efbase[] = { W977_EFIO_BASE, W977_EFIO2_BASE }; -static unsigned int efio = W977_EFIO_BASE; - -static struct w83977af_ir *dev_self[] = { NULL, NULL, NULL, NULL}; - -/* Some prototypes */ -static int w83977af_open(int i, unsigned int iobase, unsigned int irq, - unsigned int dma); -static int w83977af_close(struct w83977af_ir *self); -static int w83977af_probe(int iobase, int irq, int dma); -static int w83977af_dma_receive(struct w83977af_ir *self); -static int w83977af_dma_receive_complete(struct w83977af_ir *self); -static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, - struct net_device *dev); -static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size); -static void w83977af_dma_write(struct w83977af_ir *self, int iobase); -static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed); -static int w83977af_is_receiving(struct w83977af_ir *self); - -static int w83977af_net_open(struct net_device *dev); -static int w83977af_net_close(struct net_device *dev); -static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); - -/* - * Function w83977af_init () - * - * Initialize chip. Just try to find out how many chips we are dealing with - * and where they are - */ -static int __init w83977af_init(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dev_self) && io[i] < 2000; i++) { - if (w83977af_open(i, io[i], irq[i], dma[i]) == 0) - return 0; - } - return -ENODEV; -} - -/* - * Function w83977af_cleanup () - * - * Close all configured chips - * - */ -static void __exit w83977af_cleanup(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dev_self); i++) { - if (dev_self[i]) - w83977af_close(dev_self[i]); - } -} - -static const struct net_device_ops w83977_netdev_ops = { - .ndo_open = w83977af_net_open, - .ndo_stop = w83977af_net_close, - .ndo_start_xmit = w83977af_hard_xmit, - .ndo_do_ioctl = w83977af_net_ioctl, -}; - -/* - * Function w83977af_open (iobase, irq) - * - * Open driver instance - * - */ -static int w83977af_open(int i, unsigned int iobase, unsigned int irq, - unsigned int dma) -{ - struct net_device *dev; - struct w83977af_ir *self; - int err; - - /* Lock the port that we need */ - if (!request_region(iobase, CHIP_IO_EXTENT, driver_name)) { - pr_debug("%s: can't get iobase of 0x%03x\n", - __func__, iobase); - return -ENODEV; - } - - if (w83977af_probe(iobase, irq, dma) == -1) { - err = -1; - goto err_out; - } - /* - * Allocate new instance of the driver - */ - dev = alloc_irdadev(sizeof(struct w83977af_ir)); - if (!dev) { - pr_err("IrDA: Can't allocate memory for IrDA control block!\n"); - err = -ENOMEM; - goto err_out; - } - - self = netdev_priv(dev); - spin_lock_init(&self->lock); - - /* Initialize IO */ - self->io.fir_base = iobase; - self->io.irq = irq; - self->io.fir_ext = CHIP_IO_EXTENT; - self->io.dma = dma; - self->io.fifo_size = 32; - - /* Initialize QoS for this device */ - irda_init_max_qos_capabilies(&self->qos); - - /* The only value we must override it the baudrate */ - - /* FIXME: The HP HDLS-1100 does not support 1152000! */ - self->qos.baud_rate.bits = IR_9600 | IR_19200 | IR_38400 | IR_57600 | - IR_115200 | IR_576000 | IR_1152000 | (IR_4000000 << 8); - - /* The HP HDLS-1100 needs 1 ms according to the specs */ - self->qos.min_turn_time.bits = qos_mtt_bits; - irda_qos_bits_to_value(&self->qos); - - /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ - self->rx_buff.truesize = 14384; - self->tx_buff.truesize = 4000; - - /* Allocate memory if needed */ - self->rx_buff.head = - dma_zalloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL); - if (!self->rx_buff.head) { - err = -ENOMEM; - goto err_out1; - } - - self->tx_buff.head = - dma_zalloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL); - if (!self->tx_buff.head) { - err = -ENOMEM; - goto err_out2; - } - - self->rx_buff.in_frame = FALSE; - self->rx_buff.state = OUTSIDE_FRAME; - self->tx_buff.data = self->tx_buff.head; - self->rx_buff.data = self->rx_buff.head; - self->netdev = dev; - - dev->netdev_ops = &w83977_netdev_ops; - - err = register_netdev(dev); - if (err) { - net_err_ratelimited("%s:, register_netdevice() failed!\n", - __func__); - goto err_out3; - } - net_info_ratelimited("IrDA: Registered device %s\n", dev->name); - - /* Need to store self somewhere */ - dev_self[i] = self; - - return 0; -err_out3: - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); -err_out2: - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); -err_out1: - free_netdev(dev); -err_out: - release_region(iobase, CHIP_IO_EXTENT); - return err; -} - -/* - * Function w83977af_close (self) - * - * Close driver instance - * - */ -static int w83977af_close(struct w83977af_ir *self) -{ - int iobase; - - iobase = self->io.fir_base; - -#ifdef CONFIG_USE_W977_PNP - /* enter PnP configuration mode */ - w977_efm_enter(efio); - - w977_select_device(W977_DEVICE_IR, efio); - - /* Deactivate device */ - w977_write_reg(0x30, 0x00, efio); - - w977_efm_exit(efio); -#endif /* CONFIG_USE_W977_PNP */ - - /* Remove netdevice */ - unregister_netdev(self->netdev); - - /* Release the PORT that this driver is using */ - pr_debug("%s: Releasing Region %03x\n", __func__, self->io.fir_base); - release_region(self->io.fir_base, self->io.fir_ext); - - if (self->tx_buff.head) - dma_free_coherent(NULL, self->tx_buff.truesize, - self->tx_buff.head, self->tx_buff_dma); - - if (self->rx_buff.head) - dma_free_coherent(NULL, self->rx_buff.truesize, - self->rx_buff.head, self->rx_buff_dma); - - free_netdev(self->netdev); - - return 0; -} - -static int w83977af_probe(int iobase, int irq, int dma) -{ - int version; - int i; - - for (i = 0; i < 2; i++) { -#ifdef CONFIG_USE_W977_PNP - /* Enter PnP configuration mode */ - w977_efm_enter(efbase[i]); - - w977_select_device(W977_DEVICE_IR, efbase[i]); - - /* Configure PnP port, IRQ, and DMA channel */ - w977_write_reg(0x60, (iobase >> 8) & 0xff, efbase[i]); - w977_write_reg(0x61, (iobase) & 0xff, efbase[i]); - - w977_write_reg(0x70, irq, efbase[i]); -#ifdef CONFIG_ARCH_NETWINDER - /* Netwinder uses 1 higher than Linux */ - w977_write_reg(0x74, dma + 1, efbase[i]); -#else - w977_write_reg(0x74, dma, efbase[i]); -#endif /* CONFIG_ARCH_NETWINDER */ - w977_write_reg(0x75, 0x04, efbase[i]);/* Disable Tx DMA */ - - /* Set append hardware CRC, enable IR bank selection */ - w977_write_reg(0xf0, APEDCRC | ENBNKSEL, efbase[i]); - - /* Activate device */ - w977_write_reg(0x30, 0x01, efbase[i]); - - w977_efm_exit(efbase[i]); -#endif /* CONFIG_USE_W977_PNP */ - /* Disable Advanced mode */ - switch_bank(iobase, SET2); - outb(iobase + 2, 0x00); - - /* Turn on UART (global) interrupts */ - switch_bank(iobase, SET0); - outb(HCR_EN_IRQ, iobase + HCR); - - /* Switch to advanced mode */ - switch_bank(iobase, SET2); - outb(inb(iobase + ADCR1) | ADCR1_ADV_SL, iobase + ADCR1); - - /* Set default IR-mode */ - switch_bank(iobase, SET0); - outb(HCR_SIR, iobase + HCR); - - /* Read the Advanced IR ID */ - switch_bank(iobase, SET3); - version = inb(iobase + AUID); - - /* Should be 0x1? */ - if (0x10 == (version & 0xf0)) { - efio = efbase[i]; - - /* Set FIFO size to 32 */ - switch_bank(iobase, SET2); - outb(ADCR2_RXFS32 | ADCR2_TXFS32, iobase + ADCR2); - - /* Set FIFO threshold to TX17, RX16 */ - switch_bank(iobase, SET0); - outb(UFR_RXTL | UFR_TXTL | UFR_TXF_RST | UFR_RXF_RST | - UFR_EN_FIFO, iobase + UFR); - - /* Receiver frame length */ - switch_bank(iobase, SET4); - outb(2048 & 0xff, iobase + 6); - outb((2048 >> 8) & 0x1f, iobase + 7); - - /* - * Init HP HSDL-1100 transceiver. - * - * Set IRX_MSL since we have 2 * receive paths IRRX, - * and IRRXH. Clear IRSL0D since we want IRSL0 * to - * be a input pin used for IRRXH - * - * IRRX pin 37 connected to receiver - * IRTX pin 38 connected to transmitter - * FIRRX pin 39 connected to receiver (IRSL0) - * CIRRX pin 40 connected to pin 37 - */ - switch_bank(iobase, SET7); - outb(0x40, iobase + 7); - - net_info_ratelimited("W83977AF (IR) driver loaded. Version: 0x%02x\n", - version); - - return 0; - } else { - /* Try next extented function register address */ - pr_debug("%s: Wrong chip version\n", __func__); - } - } - return -1; -} - -static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) -{ - int ir_mode = HCR_SIR; - int iobase; - __u8 set; - - iobase = self->io.fir_base; - - /* Update accounting for new speed */ - self->io.speed = speed; - - /* Save current bank */ - set = inb(iobase + SSR); - - /* Disable interrupts */ - switch_bank(iobase, SET0); - outb(0, iobase + ICR); - - /* Select Set 2 */ - switch_bank(iobase, SET2); - outb(0x00, iobase + ABHL); - - switch (speed) { - case 9600: outb(0x0c, iobase + ABLL); break; - case 19200: outb(0x06, iobase + ABLL); break; - case 38400: outb(0x03, iobase + ABLL); break; - case 57600: outb(0x02, iobase + ABLL); break; - case 115200: outb(0x01, iobase + ABLL); break; - case 576000: - ir_mode = HCR_MIR_576; - pr_debug("%s: handling baud of 576000\n", __func__); - break; - case 1152000: - ir_mode = HCR_MIR_1152; - pr_debug("%s: handling baud of 1152000\n", __func__); - break; - case 4000000: - ir_mode = HCR_FIR; - pr_debug("%s: handling baud of 4000000\n", __func__); - break; - default: - ir_mode = HCR_FIR; - pr_debug("%s: unknown baud rate of %d\n", __func__, speed); - break; - } - - /* Set speed mode */ - switch_bank(iobase, SET0); - outb(ir_mode, iobase + HCR); - - /* set FIFO size to 32 */ - switch_bank(iobase, SET2); - outb(ADCR2_RXFS32 | ADCR2_TXFS32, iobase + ADCR2); - - /* set FIFO threshold to TX17, RX16 */ - switch_bank(iobase, SET0); - outb(0x00, iobase + UFR); /* Reset */ - outb(UFR_EN_FIFO, iobase + UFR); /* First we must enable FIFO */ - outb(0xa7, iobase + UFR); - - netif_wake_queue(self->netdev); - - /* Enable some interrupts so we can receive frames */ - switch_bank(iobase, SET0); - if (speed > PIO_MAX_SPEED) { - outb(ICR_EFSFI, iobase + ICR); - w83977af_dma_receive(self); - } else { - outb(ICR_ERBRI, iobase + ICR); - } - - /* Restore SSR */ - outb(set, iobase + SSR); -} - -/* - * Function w83977af_hard_xmit (skb, dev) - * - * Sets up a DMA transfer to send the current frame. - * - */ -static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct w83977af_ir *self; - __s32 speed; - int iobase; - __u8 set; - int mtt; - - self = netdev_priv(dev); - - iobase = self->io.fir_base; - - pr_debug("%s: %ld, skb->len=%d\n", __func__, jiffies, (int)skb->len); - - /* Lock transmit buffer */ - netif_stop_queue(dev); - - /* Check if we need to change the speed */ - speed = irda_get_next_speed(skb); - if ((speed != self->io.speed) && (speed != -1)) { - /* Check for empty frame */ - if (!skb->len) { - w83977af_change_speed(self, speed); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - self->new_speed = speed; - } - - /* Save current set */ - set = inb(iobase + SSR); - - /* Decide if we should use PIO or DMA transfer */ - if (self->io.speed > PIO_MAX_SPEED) { - self->tx_buff.data = self->tx_buff.head; - skb_copy_from_linear_data(skb, self->tx_buff.data, skb->len); - self->tx_buff.len = skb->len; - - mtt = irda_get_mtt(skb); - pr_debug("%s: %ld, mtt=%d\n", __func__, jiffies, mtt); - if (mtt > 1000) - mdelay(mtt / 1000); - else if (mtt) - udelay(mtt); - - /* Enable DMA interrupt */ - switch_bank(iobase, SET0); - outb(ICR_EDMAI, iobase + ICR); - w83977af_dma_write(self, iobase); - } else { - self->tx_buff.data = self->tx_buff.head; - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, - self->tx_buff.truesize); - - /* Add interrupt on tx low level (will fire immediately) */ - switch_bank(iobase, SET0); - outb(ICR_ETXTHI, iobase + ICR); - } - dev_kfree_skb(skb); - - /* Restore set register */ - outb(set, iobase + SSR); - - return NETDEV_TX_OK; -} - -/* - * Function w83977af_dma_write (self, iobase) - * - * Send frame using DMA - * - */ -static void w83977af_dma_write(struct w83977af_ir *self, int iobase) -{ - __u8 set; - - pr_debug("%s: len=%d\n", __func__, self->tx_buff.len); - - /* Save current set */ - set = inb(iobase + SSR); - - /* Disable DMA */ - switch_bank(iobase, SET0); - outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); - - /* Choose transmit DMA channel */ - switch_bank(iobase, SET2); - outb(ADCR1_D_CHSW | /*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase + ADCR1); - irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, - DMA_MODE_WRITE); - self->io.direction = IO_XMIT; - - /* Enable DMA */ - switch_bank(iobase, SET0); - outb(inb(iobase + HCR) | HCR_EN_DMA | HCR_TX_WT, iobase + HCR); - - /* Restore set register */ - outb(set, iobase + SSR); -} - -/* - * Function w83977af_pio_write (iobase, buf, len, fifo_size) - * - * - * - */ -static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size) -{ - int actual = 0; - __u8 set; - - /* Save current bank */ - set = inb(iobase + SSR); - - switch_bank(iobase, SET0); - if (!(inb_p(iobase + USR) & USR_TSRE)) { - pr_debug("%s: warning, FIFO not empty yet!\n", __func__); - - fifo_size -= 17; - pr_debug("%s: %d bytes left in tx fifo\n", __func__, fifo_size); - } - - /* Fill FIFO with current frame */ - while ((fifo_size-- > 0) && (actual < len)) { - /* Transmit next byte */ - outb(buf[actual++], iobase + TBR); - } - - pr_debug("%s: fifo_size %d ; %d sent of %d\n", - __func__, fifo_size, actual, len); - - /* Restore bank */ - outb(set, iobase + SSR); - - return actual; -} - -/* - * Function w83977af_dma_xmit_complete (self) - * - * The transfer of a frame in finished. So do the necessary things - * - * - */ -static void w83977af_dma_xmit_complete(struct w83977af_ir *self) -{ - int iobase; - __u8 set; - - pr_debug("%s: %ld\n", __func__, jiffies); - - IRDA_ASSERT(self, return;); - - iobase = self->io.fir_base; - - /* Save current set */ - set = inb(iobase + SSR); - - /* Disable DMA */ - switch_bank(iobase, SET0); - outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); - - /* Check for underrun! */ - if (inb(iobase + AUDR) & AUDR_UNDR) { - pr_debug("%s: Transmit underrun!\n", __func__); - - self->netdev->stats.tx_errors++; - self->netdev->stats.tx_fifo_errors++; - - /* Clear bit, by writing 1 to it */ - outb(AUDR_UNDR, iobase + AUDR); - } else { - self->netdev->stats.tx_packets++; - } - - if (self->new_speed) { - w83977af_change_speed(self, self->new_speed); - self->new_speed = 0; - } - - /* Unlock tx_buff and request another frame */ - /* Tell the network layer, that we want more frames */ - netif_wake_queue(self->netdev); - - /* Restore set */ - outb(set, iobase + SSR); -} - -/* - * Function w83977af_dma_receive (self) - * - * Get ready for receiving a frame. The device will initiate a DMA - * if it starts to receive a frame. - * - */ -static int w83977af_dma_receive(struct w83977af_ir *self) -{ - int iobase; - __u8 set; -#ifdef CONFIG_ARCH_NETWINDER - unsigned long flags; - __u8 hcr; -#endif - IRDA_ASSERT(self, return -1;); - - pr_debug("%s\n", __func__); - - iobase = self->io.fir_base; - - /* Save current set */ - set = inb(iobase + SSR); - - /* Disable DMA */ - switch_bank(iobase, SET0); - outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); - - /* Choose DMA Rx, DMA Fairness, and Advanced mode */ - switch_bank(iobase, SET2); - outb((inb(iobase + ADCR1) & ~ADCR1_D_CHSW)/*|ADCR1_DMA_F*/ | ADCR1_ADV_SL, - iobase + ADCR1); - - self->io.direction = IO_RECV; - self->rx_buff.data = self->rx_buff.head; - -#ifdef CONFIG_ARCH_NETWINDER - spin_lock_irqsave(&self->lock, flags); - - disable_dma(self->io.dma); - clear_dma_ff(self->io.dma); - set_dma_mode(self->io.dma, DMA_MODE_READ); - set_dma_addr(self->io.dma, self->rx_buff_dma); - set_dma_count(self->io.dma, self->rx_buff.truesize); -#else - irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, - DMA_MODE_READ); -#endif - /* - * Reset Rx FIFO. This will also flush the ST_FIFO, it's very - * important that we don't reset the Tx FIFO since it might not - * be finished transmitting yet - */ - switch_bank(iobase, SET0); - outb(UFR_RXTL | UFR_TXTL | UFR_RXF_RST | UFR_EN_FIFO, iobase + UFR); - self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0; - - /* Enable DMA */ - switch_bank(iobase, SET0); -#ifdef CONFIG_ARCH_NETWINDER - hcr = inb(iobase + HCR); - outb(hcr | HCR_EN_DMA, iobase + HCR); - enable_dma(self->io.dma); - spin_unlock_irqrestore(&self->lock, flags); -#else - outb(inb(iobase + HCR) | HCR_EN_DMA, iobase + HCR); -#endif - /* Restore set */ - outb(set, iobase + SSR); - - return 0; -} - -/* - * Function w83977af_receive_complete (self) - * - * Finished with receiving a frame - * - */ -static int w83977af_dma_receive_complete(struct w83977af_ir *self) -{ - struct sk_buff *skb; - struct st_fifo *st_fifo; - int len; - int iobase; - __u8 set; - __u8 status; - - pr_debug("%s\n", __func__); - - st_fifo = &self->st_fifo; - - iobase = self->io.fir_base; - - /* Save current set */ - set = inb(iobase + SSR); - - iobase = self->io.fir_base; - - /* Read status FIFO */ - switch_bank(iobase, SET5); - while ((status = inb(iobase + FS_FO)) & FS_FO_FSFDR) { - st_fifo->entries[st_fifo->tail].status = status; - - st_fifo->entries[st_fifo->tail].len = inb(iobase + RFLFL); - st_fifo->entries[st_fifo->tail].len |= inb(iobase + RFLFH) << 8; - - st_fifo->tail++; - st_fifo->len++; - } - - while (st_fifo->len) { - /* Get first entry */ - status = st_fifo->entries[st_fifo->head].status; - len = st_fifo->entries[st_fifo->head].len; - st_fifo->head++; - st_fifo->len--; - - /* Check for errors */ - if (status & FS_FO_ERR_MSK) { - if (status & FS_FO_LST_FR) { - /* Add number of lost frames to stats */ - self->netdev->stats.rx_errors += len; - } else { - /* Skip frame */ - self->netdev->stats.rx_errors++; - - self->rx_buff.data += len; - - if (status & FS_FO_MX_LEX) - self->netdev->stats.rx_length_errors++; - - if (status & FS_FO_PHY_ERR) - self->netdev->stats.rx_frame_errors++; - - if (status & FS_FO_CRC_ERR) - self->netdev->stats.rx_crc_errors++; - } - /* The errors below can be reported in both cases */ - if (status & FS_FO_RX_OV) - self->netdev->stats.rx_fifo_errors++; - - if (status & FS_FO_FSF_OV) - self->netdev->stats.rx_fifo_errors++; - - } else { - /* Check if we have transferred all data to memory */ - switch_bank(iobase, SET0); - if (inb(iobase + USR) & USR_RDR) - udelay(80); /* Should be enough!? */ - - skb = dev_alloc_skb(len + 1); - if (!skb) { - pr_info("%s: memory squeeze, dropping frame\n", - __func__); - /* Restore set register */ - outb(set, iobase + SSR); - - return FALSE; - } - - /* Align to 20 bytes */ - skb_reserve(skb, 1); - - /* Copy frame without CRC */ - if (self->io.speed < 4000000) { - skb_put(skb, len - 2); - skb_copy_to_linear_data(skb, - self->rx_buff.data, - len - 2); - } else { - skb_put(skb, len - 4); - skb_copy_to_linear_data(skb, - self->rx_buff.data, - len - 4); - } - - /* Move to next frame */ - self->rx_buff.data += len; - self->netdev->stats.rx_packets++; - - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb->protocol = htons(ETH_P_IRDA); - netif_rx(skb); - } - } - /* Restore set register */ - outb(set, iobase + SSR); - - return TRUE; -} - -/* - * Function pc87108_pio_receive (self) - * - * Receive all data in receiver FIFO - * - */ -static void w83977af_pio_receive(struct w83977af_ir *self) -{ - __u8 byte = 0x00; - int iobase; - - IRDA_ASSERT(self, return;); - - iobase = self->io.fir_base; - - /* Receive all characters in Rx FIFO */ - do { - byte = inb(iobase + RBR); - async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, - byte); - } while (inb(iobase + USR) & USR_RDR); /* Data available */ -} - -/* - * Function w83977af_sir_interrupt (self, eir) - * - * Handle SIR interrupt - * - */ -static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) -{ - int actual; - __u8 new_icr = 0; - __u8 set; - int iobase; - - pr_debug("%s: isr=%#x\n", __func__, isr); - - iobase = self->io.fir_base; - /* Transmit FIFO low on data */ - if (isr & ISR_TXTH_I) { - /* Write data left in transmit buffer */ - actual = w83977af_pio_write(self->io.fir_base, - self->tx_buff.data, - self->tx_buff.len, - self->io.fifo_size); - - self->tx_buff.data += actual; - self->tx_buff.len -= actual; - - self->io.direction = IO_XMIT; - - /* Check if finished */ - if (self->tx_buff.len > 0) { - new_icr |= ICR_ETXTHI; - } else { - set = inb(iobase + SSR); - switch_bank(iobase, SET0); - outb(AUDR_SFEND, iobase + AUDR); - outb(set, iobase + SSR); - - self->netdev->stats.tx_packets++; - - /* Feed me more packets */ - netif_wake_queue(self->netdev); - new_icr |= ICR_ETBREI; - } - } - /* Check if transmission has completed */ - if (isr & ISR_TXEMP_I) { - /* Check if we need to change the speed? */ - if (self->new_speed) { - pr_debug("%s: Changing speed!\n", __func__); - w83977af_change_speed(self, self->new_speed); - self->new_speed = 0; - } - - /* Turn around and get ready to receive some data */ - self->io.direction = IO_RECV; - new_icr |= ICR_ERBRI; - } - - /* Rx FIFO threshold or timeout */ - if (isr & ISR_RXTH_I) { - w83977af_pio_receive(self); - - /* Keep receiving */ - new_icr |= ICR_ERBRI; - } - return new_icr; -} - -/* - * Function pc87108_fir_interrupt (self, eir) - * - * Handle MIR/FIR interrupt - * - */ -static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) -{ - __u8 new_icr = 0; - __u8 set; - int iobase; - - iobase = self->io.fir_base; - set = inb(iobase + SSR); - - /* End of frame detected in FIFO */ - if (isr & (ISR_FEND_I | ISR_FSF_I)) { - if (w83977af_dma_receive_complete(self)) { - /* Wait for next status FIFO interrupt */ - new_icr |= ICR_EFSFI; - } else { - /* DMA not finished yet */ - - /* Set timer value, resolution 1 ms */ - switch_bank(iobase, SET4); - outb(0x01, iobase + TMRL); /* 1 ms */ - outb(0x00, iobase + TMRH); - - /* Start timer */ - outb(IR_MSL_EN_TMR, iobase + IR_MSL); - - new_icr |= ICR_ETMRI; - } - } - /* Timer finished */ - if (isr & ISR_TMR_I) { - /* Disable timer */ - switch_bank(iobase, SET4); - outb(0, iobase + IR_MSL); - - /* Clear timer event */ - /* switch_bank(iobase, SET0); */ -/* outb(ASCR_CTE, iobase+ASCR); */ - - /* Check if this is a TX timer interrupt */ - if (self->io.direction == IO_XMIT) { - w83977af_dma_write(self, iobase); - - new_icr |= ICR_EDMAI; - } else { - /* Check if DMA has now finished */ - w83977af_dma_receive_complete(self); - - new_icr |= ICR_EFSFI; - } - } - /* Finished with DMA */ - if (isr & ISR_DMA_I) { - w83977af_dma_xmit_complete(self); - - /* Check if there are more frames to be transmitted */ - /* if (irda_device_txqueue_empty(self)) { */ - - /* Prepare for receive - * - * ** Netwinder Tx DMA likes that we do this anyway ** - */ - w83977af_dma_receive(self); - new_icr = ICR_EFSFI; - /* } */ - } - - /* Restore set */ - outb(set, iobase + SSR); - - return new_icr; -} - -/* - * Function w83977af_interrupt (irq, dev_id, regs) - * - * An interrupt from the chip has arrived. Time to do some work - * - */ -static irqreturn_t w83977af_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct w83977af_ir *self; - __u8 set, icr, isr; - int iobase; - - self = netdev_priv(dev); - - iobase = self->io.fir_base; - - /* Save current bank */ - set = inb(iobase + SSR); - switch_bank(iobase, SET0); - - icr = inb(iobase + ICR); - isr = inb(iobase + ISR) & icr; /* Mask out the interesting ones */ - - outb(0, iobase + ICR); /* Disable interrupts */ - - if (isr) { - /* Dispatch interrupt handler for the current speed */ - if (self->io.speed > PIO_MAX_SPEED) - icr = w83977af_fir_interrupt(self, isr); - else - icr = w83977af_sir_interrupt(self, isr); - } - - outb(icr, iobase + ICR); /* Restore (new) interrupts */ - outb(set, iobase + SSR); /* Restore bank register */ - return IRQ_RETVAL(isr); -} - -/* - * Function w83977af_is_receiving (self) - * - * Return TRUE is we are currently receiving a frame - * - */ -static int w83977af_is_receiving(struct w83977af_ir *self) -{ - int status = FALSE; - int iobase; - __u8 set; - - IRDA_ASSERT(self, return FALSE;); - - if (self->io.speed > 115200) { - iobase = self->io.fir_base; - - /* Check if rx FIFO is not empty */ - set = inb(iobase + SSR); - switch_bank(iobase, SET2); - if ((inb(iobase + RXFDTH) & 0x3f) != 0) { - /* We are receiving something */ - status = TRUE; - } - outb(set, iobase + SSR); - } else { - status = (self->rx_buff.state != OUTSIDE_FRAME); - } - - return status; -} - -/* - * Function w83977af_net_open (dev) - * - * Start the device - * - */ -static int w83977af_net_open(struct net_device *dev) -{ - struct w83977af_ir *self; - int iobase; - char hwname[32]; - __u8 set; - - IRDA_ASSERT(dev, return -1;); - self = netdev_priv(dev); - - IRDA_ASSERT(self, return 0;); - - iobase = self->io.fir_base; - - if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, - (void *)dev)) { - return -EAGAIN; - } - /* - * Always allocate the DMA channel after the IRQ, - * and clean up on failure. - */ - if (request_dma(self->io.dma, dev->name)) { - free_irq(self->io.irq, dev); - return -EAGAIN; - } - - /* Save current set */ - set = inb(iobase + SSR); - - /* Enable some interrupts so we can receive frames again */ - switch_bank(iobase, SET0); - if (self->io.speed > 115200) { - outb(ICR_EFSFI, iobase + ICR); - w83977af_dma_receive(self); - } else { - outb(ICR_ERBRI, iobase + ICR); - } - - /* Restore bank register */ - outb(set, iobase + SSR); - - /* Ready to play! */ - netif_start_queue(dev); - - /* Give self a hardware name */ - sprintf(hwname, "w83977af @ 0x%03x", self->io.fir_base); - - /* - * Open new IrLAP layer instance, now that everything should be - * initialized properly - */ - self->irlap = irlap_open(dev, &self->qos, hwname); - - return 0; -} - -/* - * Function w83977af_net_close (dev) - * - * Stop the device - * - */ -static int w83977af_net_close(struct net_device *dev) -{ - struct w83977af_ir *self; - int iobase; - __u8 set; - - IRDA_ASSERT(dev, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self, return 0;); - - iobase = self->io.fir_base; - - /* Stop device */ - netif_stop_queue(dev); - - /* Stop and remove instance of IrLAP */ - if (self->irlap) - irlap_close(self->irlap); - self->irlap = NULL; - - disable_dma(self->io.dma); - - /* Save current set */ - set = inb(iobase + SSR); - - /* Disable interrupts */ - switch_bank(iobase, SET0); - outb(0, iobase + ICR); - - free_irq(self->io.irq, dev); - free_dma(self->io.dma); - - /* Restore bank register */ - outb(set, iobase + SSR); - - return 0; -} - -/* - * Function w83977af_net_ioctl (dev, rq, cmd) - * - * Process IOCTL commands for this device - * - */ -static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct if_irda_req *irq = (struct if_irda_req *)rq; - struct w83977af_ir *self; - unsigned long flags; - int ret = 0; - - IRDA_ASSERT(dev, return -1;); - - self = netdev_priv(dev); - - IRDA_ASSERT(self, return -1;); - - pr_debug("%s: %s, (cmd=0x%X)\n", __func__, dev->name, cmd); - - spin_lock_irqsave(&self->lock, flags); - - switch (cmd) { - case SIOCSBANDWIDTH: /* Set bandwidth */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - w83977af_change_speed(self, irq->ifr_baudrate); - break; - case SIOCSMEDIABUSY: /* Set media busy */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - goto out; - } - irda_device_set_media_busy(self->netdev, TRUE); - break; - case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = w83977af_is_receiving(self); - break; - default: - ret = -EOPNOTSUPP; - } -out: - spin_unlock_irqrestore(&self->lock, flags); - return ret; -} - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("Winbond W83977AF IrDA Device Driver"); -MODULE_LICENSE("GPL"); - -module_param(qos_mtt_bits, int, 0); -MODULE_PARM_DESC(qos_mtt_bits, "Mimimum Turn Time"); -module_param_hw_array(io, int, ioport, NULL, 0); -MODULE_PARM_DESC(io, "Base I/O addresses"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "IRQ lines"); - -/* - * Function init_module (void) - * - * - * - */ -module_init(w83977af_init); - -/* - * Function cleanup_module (void) - * - * - * - */ -module_exit(w83977af_cleanup); diff --git a/drivers/staging/irda/drivers/w83977af_ir.h b/drivers/staging/irda/drivers/w83977af_ir.h deleted file mode 100644 index fefe9b11e200..000000000000 --- a/drivers/staging/irda/drivers/w83977af_ir.h +++ /dev/null @@ -1,198 +0,0 @@ -/********************************************************************* - * - * Filename: w83977af_ir.h - * Version: - * Description: - * Status: Experimental. - * Author: Paul VanderSpek - * Created at: Thu Nov 19 13:55:34 1998 - * Modified at: Tue Jan 11 13:08:19 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef W83977AF_IR_H -#define W83977AF_IR_H - -#include -#include - -/* Flags for configuration register CRF0 */ -#define ENBNKSEL 0x01 -#define APEDCRC 0x02 -#define TXW4C 0x04 -#define RXW4C 0x08 - -/* Bank 0 */ -#define RBR 0x00 /* Receiver buffer register */ -#define TBR 0x00 /* Transmitter buffer register */ - -#define ICR 0x01 /* Interrupt configuration register */ -#define ICR_ERBRI 0x01 /* Receiver buffer register interrupt */ -#define ICR_ETBREI 0x02 /* Transeiver empty interrupt */ -#define ICR_EUSRI 0x04//* IR status interrupt */ -#define ICR_EHSRI 0x04 -#define ICR_ETXURI 0x04 /* Tx underrun */ -#define ICR_EDMAI 0x10 /* DMA interrupt */ -#define ICR_ETXTHI 0x20 /* Transmitter threshold interrupt */ -#define ICR_EFSFI 0x40 /* Frame status FIFO interrupt */ -#define ICR_ETMRI 0x80 /* Timer interrupt */ - -#define UFR 0x02 /* FIFO control register */ -#define UFR_EN_FIFO 0x01 /* Enable FIFO's */ -#define UFR_RXF_RST 0x02 /* Reset Rx FIFO */ -#define UFR_TXF_RST 0x04 /* Reset Tx FIFO */ -#define UFR_RXTL 0x80 /* Rx FIFO threshold (set to 16) */ -#define UFR_TXTL 0x20 /* Tx FIFO threshold (set to 17) */ - -#define ISR 0x02 /* Interrupt status register */ -#define ISR_RXTH_I 0x01 /* Receive threshold interrupt */ -#define ISR_TXEMP_I 0x02 /* Transmitter empty interrupt */ -#define ISR_FEND_I 0x04 -#define ISR_DMA_I 0x10 -#define ISR_TXTH_I 0x20 /* Transmitter threshold interrupt */ -#define ISR_FSF_I 0x40 -#define ISR_TMR_I 0x80 /* Timer interrupt */ - -#define UCR 0x03 /* Uart control register */ -#define UCR_DLS8 0x03 /* 8N1 */ - -#define SSR 0x03 /* Sets select register */ -#define SET0 UCR_DLS8 /* Make sure we keep 8N1 */ -#define SET1 (0x80|UCR_DLS8) /* Make sure we keep 8N1 */ -#define SET2 0xE0 -#define SET3 0xE4 -#define SET4 0xE8 -#define SET5 0xEC -#define SET6 0xF0 -#define SET7 0xF4 - -#define HCR 0x04 -#define HCR_MODE_MASK ~(0xD0) -#define HCR_SIR 0x60 -#define HCR_MIR_576 0x20 -#define HCR_MIR_1152 0x80 -#define HCR_FIR 0xA0 -#define HCR_EN_DMA 0x04 -#define HCR_EN_IRQ 0x08 -#define HCR_TX_WT 0x08 - -#define USR 0x05 /* IR status register */ -#define USR_RDR 0x01 /* Receive data ready */ -#define USR_TSRE 0x40 /* Transmitter empty? */ - -#define AUDR 0x07 -#define AUDR_SFEND 0x08 /* Set a frame end */ -#define AUDR_RXBSY 0x20 /* Rx busy */ -#define AUDR_UNDR 0x40 /* Transeiver underrun */ - -/* Set 2 */ -#define ABLL 0x00 /* Advanced baud rate divisor latch (low byte) */ -#define ABHL 0x01 /* Advanced baud rate divisor latch (high byte) */ - -#define ADCR1 0x02 -#define ADCR1_ADV_SL 0x01 -#define ADCR1_D_CHSW 0x08 /* the specs are wrong. its bit 3, not 4 */ -#define ADCR1_DMA_F 0x02 - -#define ADCR2 0x04 -#define ADCR2_TXFS32 0x01 -#define ADCR2_RXFS32 0x04 - -#define RXFDTH 0x07 - -/* Set 3 */ -#define AUID 0x00 - -/* Set 4 */ -#define TMRL 0x00 /* Timer value register (low byte) */ -#define TMRH 0x01 /* Timer value register (high byte) */ - -#define IR_MSL 0x02 /* Infrared mode select */ -#define IR_MSL_EN_TMR 0x01 /* Enable timer */ - -#define TFRLL 0x04 /* Transmitter frame length (low byte) */ -#define TFRLH 0x05 /* Transmitter frame length (high byte) */ -#define RFRLL 0x06 /* Receiver frame length (low byte) */ -#define RFRLH 0x07 /* Receiver frame length (high byte) */ - -/* Set 5 */ - -#define FS_FO 0x05 /* Frame status FIFO */ -#define FS_FO_FSFDR 0x80 /* Frame status FIFO data ready */ -#define FS_FO_LST_FR 0x40 /* Frame lost */ -#define FS_FO_MX_LEX 0x10 /* Max frame len exceeded */ -#define FS_FO_PHY_ERR 0x08 /* Physical layer error */ -#define FS_FO_CRC_ERR 0x04 -#define FS_FO_RX_OV 0x02 /* Receive overrun */ -#define FS_FO_FSF_OV 0x01 /* Frame status FIFO overrun */ -#define FS_FO_ERR_MSK 0x5f /* Error mask */ - -#define RFLFL 0x06 -#define RFLFH 0x07 - -/* Set 6 */ -#define IR_CFG2 0x00 -#define IR_CFG2_DIS_CRC 0x02 - -/* Set 7 */ -#define IRM_CR 0x07 /* Infrared module control register */ -#define IRM_CR_IRX_MSL 0x40 -#define IRM_CR_AF_MNT 0x80 /* Automatic format */ - -/* For storing entries in the status FIFO */ -struct st_fifo_entry { - int status; - int len; -}; - -struct st_fifo { - struct st_fifo_entry entries[10]; - int head; - int tail; - int len; -}; - -/* Private data for each instance */ -struct w83977af_ir { - struct st_fifo st_fifo; - - int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */ - int tx_len; /* Number of frames in tx_buff */ - - struct net_device *netdev; /* Yes! we are some kind of netdevice */ - - struct irlap_cb *irlap; /* The link layer we are binded to */ - struct qos_info qos; /* QoS capabilities for this device */ - - chipio_t io; /* IrDA controller information */ - iobuff_t tx_buff; /* Transmit buffer */ - iobuff_t rx_buff; /* Receive buffer */ - dma_addr_t tx_buff_dma; - dma_addr_t rx_buff_dma; - - /* Note : currently locking is *very* incomplete, but this - * will get you started. Check in nsc-ircc.c for a proper - * locking strategy. - Jean II */ - spinlock_t lock; /* For serializing operations */ - - __u32 new_speed; -}; - -static inline void switch_bank( int iobase, int set) -{ - outb(set, iobase+SSR); -} - -#endif diff --git a/drivers/staging/irda/include/net/irda/af_irda.h b/drivers/staging/irda/include/net/irda/af_irda.h deleted file mode 100644 index 0df574931522..000000000000 --- a/drivers/staging/irda/include/net/irda/af_irda.h +++ /dev/null @@ -1,87 +0,0 @@ -/********************************************************************* - * - * Filename: af_irda.h - * Version: 1.0 - * Description: IrDA sockets declarations - * Status: Stable - * Author: Dag Brattli - * Created at: Tue Dec 9 21:13:12 1997 - * Modified at: Fri Jan 28 13:16:32 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef AF_IRDA_H -#define AF_IRDA_H - -#include -#include -#include /* struct iriap_cb */ -#include /* struct ias_value */ -#include /* struct lsap_cb */ -#include /* struct tsap_cb */ -#include /* struct discovery_t */ -#include - -/* IrDA Socket */ -struct irda_sock { - /* struct sock has to be the first member of irda_sock */ - struct sock sk; - __u32 saddr; /* my local address */ - __u32 daddr; /* peer address */ - - struct lsap_cb *lsap; /* LSAP used by Ultra */ - __u8 pid; /* Protocol IP (PID) used by Ultra */ - - struct tsap_cb *tsap; /* TSAP used by this connection */ - __u8 dtsap_sel; /* remote TSAP address */ - __u8 stsap_sel; /* local TSAP address */ - - __u32 max_sdu_size_rx; - __u32 max_sdu_size_tx; - __u32 max_data_size; - __u8 max_header_size; - struct qos_info qos_tx; - - __u16_host_order mask; /* Hint bits mask */ - __u16_host_order hints; /* Hint bits */ - - void *ckey; /* IrLMP client handle */ - void *skey; /* IrLMP service handle */ - - struct ias_object *ias_obj; /* Our service name + lsap in IAS */ - struct iriap_cb *iriap; /* Used to query remote IAS */ - struct ias_value *ias_result; /* Result of remote IAS query */ - - hashbin_t *cachelog; /* Result of discovery query */ - __u32 cachedaddr; /* Result of selective discovery query */ - - int nslots; /* Number of slots to use for discovery */ - - int errno; /* status of the IAS query */ - - wait_queue_head_t query_wait; /* Wait for the answer to a query */ - struct timer_list watchdog; /* Timeout for discovery */ - - LOCAL_FLOW tx_flow; - LOCAL_FLOW rx_flow; -}; - -static inline struct irda_sock *irda_sk(struct sock *sk) -{ - return (struct irda_sock *)sk; -} - -#endif /* AF_IRDA_H */ diff --git a/drivers/staging/irda/include/net/irda/crc.h b/drivers/staging/irda/include/net/irda/crc.h deleted file mode 100644 index f202296df9bb..000000000000 --- a/drivers/staging/irda/include/net/irda/crc.h +++ /dev/null @@ -1,29 +0,0 @@ -/********************************************************************* - * - * Filename: crc.h - * Version: - * Description: CRC routines - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Sun May 2 20:25:23 1999 - * Modified by: Dag Brattli - * - ********************************************************************/ - -#ifndef IRDA_CRC_H -#define IRDA_CRC_H - -#include -#include - -#define INIT_FCS 0xffff /* Initial FCS value */ -#define GOOD_FCS 0xf0b8 /* Good final FCS value */ - -/* Recompute the FCS with one more character appended. */ -#define irda_fcs(fcs, c) crc_ccitt_byte(fcs, c) - -/* Recompute the FCS with len bytes appended. */ -#define irda_calc_crc16(fcs, buf, len) crc_ccitt(fcs, buf, len) - -#endif diff --git a/drivers/staging/irda/include/net/irda/discovery.h b/drivers/staging/irda/include/net/irda/discovery.h deleted file mode 100644 index 63ae32530567..000000000000 --- a/drivers/staging/irda/include/net/irda/discovery.h +++ /dev/null @@ -1,95 +0,0 @@ -/********************************************************************* - * - * Filename: discovery.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Apr 6 16:53:53 1999 - * Modified at: Tue Oct 5 10:05:10 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef DISCOVERY_H -#define DISCOVERY_H - -#include - -#include -#include /* irda_queue_t */ -#include /* LAP_REASON */ - -#define DISCOVERY_EXPIRE_TIMEOUT (2*sysctl_discovery_timeout*HZ) -#define DISCOVERY_DEFAULT_SLOTS 0 - -/* - * This type is used by the protocols that transmit 16 bits words in - * little endian format. A little endian machine stores MSB of word in - * byte[1] and LSB in byte[0]. A big endian machine stores MSB in byte[0] - * and LSB in byte[1]. - * - * This structure is used in the code for things that are endian neutral - * but that fit in a word so that we can manipulate them efficiently. - * By endian neutral, I mean things that are really an array of bytes, - * and always used as such, for example the hint bits. Jean II - */ -typedef union { - __u16 word; - __u8 byte[2]; -} __u16_host_order; - -/* Types of discovery */ -typedef enum { - DISCOVERY_LOG, /* What's in our discovery log */ - DISCOVERY_ACTIVE, /* Doing our own discovery on the medium */ - DISCOVERY_PASSIVE, /* Peer doing discovery on the medium */ - EXPIRY_TIMEOUT, /* Entry expired due to timeout */ -} DISCOVERY_MODE; - -#define NICKNAME_MAX_LEN 21 - -/* Basic discovery information about a peer */ -typedef struct irda_device_info discinfo_t; /* linux/irda.h */ - -/* - * The DISCOVERY structure is used for both discovery requests and responses - */ -typedef struct discovery_t { - irda_queue_t q; /* Must be first! */ - - discinfo_t data; /* Basic discovery information */ - int name_len; /* Length of nickname */ - - LAP_REASON condition; /* More info about the discovery */ - int gen_addr_bit; /* Need to generate a new device - * address? */ - int nslots; /* Number of slots to use when - * discovering */ - unsigned long timestamp; /* Last time discovered */ - unsigned long firststamp; /* First time discovered */ -} discovery_t; - -void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *discovery); -void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log); -void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force); -struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, - __u16 mask, int old_entries); - -#endif diff --git a/drivers/staging/irda/include/net/irda/ircomm_core.h b/drivers/staging/irda/include/net/irda/ircomm_core.h deleted file mode 100644 index 2a580ce9edad..000000000000 --- a/drivers/staging/irda/include/net/irda/ircomm_core.h +++ /dev/null @@ -1,106 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_core.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Wed Jun 9 08:58:43 1999 - * Modified at: Mon Dec 13 11:52:29 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRCOMM_CORE_H -#define IRCOMM_CORE_H - -#include -#include -#include - -#define IRCOMM_MAGIC 0x98347298 -#define IRCOMM_HEADER_SIZE 1 - -struct ircomm_cb; /* Forward decl. */ - -/* - * A small call-table, so we don't have to check the service-type whenever - * we want to do something - */ -typedef struct { - int (*data_request)(struct ircomm_cb *, struct sk_buff *, int clen); - int (*connect_request)(struct ircomm_cb *, struct sk_buff *, - struct ircomm_info *); - int (*connect_response)(struct ircomm_cb *, struct sk_buff *); - int (*disconnect_request)(struct ircomm_cb *, struct sk_buff *, - struct ircomm_info *); -} call_t; - -struct ircomm_cb { - irda_queue_t queue; - magic_t magic; - - notify_t notify; - call_t issue; - - int state; - int line; /* Which TTY line we are using */ - - struct tsap_cb *tsap; - struct lsap_cb *lsap; - - __u8 dlsap_sel; /* Destination LSAP/TSAP selector */ - __u8 slsap_sel; /* Source LSAP/TSAP selector */ - - __u32 saddr; /* Source device address (link we are using) */ - __u32 daddr; /* Destination device address */ - - int max_header_size; /* Header space we must reserve for each frame */ - int max_data_size; /* The amount of data we can fill in each frame */ - - LOCAL_FLOW flow_status; /* Used by ircomm_lmp */ - int pkt_count; /* Number of frames we have sent to IrLAP */ - - __u8 service_type; -}; - -extern hashbin_t *ircomm; - -struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line); -int ircomm_close(struct ircomm_cb *self); - -int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb); -void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb); -void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb); -int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb); -int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel, - __u32 saddr, __u32 daddr, struct sk_buff *skb, - __u8 service_type); -void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb, - struct ircomm_info *info); -void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb, - struct ircomm_info *info); -int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata); -int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata); -void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb, - struct ircomm_info *info); -void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow); - -#define ircomm_is_connected(self) (self->state == IRCOMM_CONN) - -#endif diff --git a/drivers/staging/irda/include/net/irda/ircomm_event.h b/drivers/staging/irda/include/net/irda/ircomm_event.h deleted file mode 100644 index 5bbc32998d57..000000000000 --- a/drivers/staging/irda/include/net/irda/ircomm_event.h +++ /dev/null @@ -1,83 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_event.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Jun 6 23:51:13 1999 - * Modified at: Thu Jun 10 08:36:25 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRCOMM_EVENT_H -#define IRCOMM_EVENT_H - -#include - -typedef enum { - IRCOMM_IDLE, - IRCOMM_WAITI, - IRCOMM_WAITR, - IRCOMM_CONN, -} IRCOMM_STATE; - -/* IrCOMM Events */ -typedef enum { - IRCOMM_CONNECT_REQUEST, - IRCOMM_CONNECT_RESPONSE, - IRCOMM_TTP_CONNECT_INDICATION, - IRCOMM_LMP_CONNECT_INDICATION, - IRCOMM_TTP_CONNECT_CONFIRM, - IRCOMM_LMP_CONNECT_CONFIRM, - - IRCOMM_LMP_DISCONNECT_INDICATION, - IRCOMM_TTP_DISCONNECT_INDICATION, - IRCOMM_DISCONNECT_REQUEST, - - IRCOMM_TTP_DATA_INDICATION, - IRCOMM_LMP_DATA_INDICATION, - IRCOMM_DATA_REQUEST, - IRCOMM_CONTROL_REQUEST, - IRCOMM_CONTROL_INDICATION, -} IRCOMM_EVENT; - -/* - * Used for passing information through the state-machine - */ -struct ircomm_info { - __u32 saddr; /* Source device address */ - __u32 daddr; /* Destination device address */ - __u8 dlsap_sel; - LM_REASON reason; /* Reason for disconnect */ - __u32 max_data_size; - __u32 max_header_size; - - struct qos_info *qos; -}; - -extern const char *const ircomm_state[]; - -struct ircomm_cb; /* Forward decl. */ - -int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info); -void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state); - -#endif diff --git a/drivers/staging/irda/include/net/irda/ircomm_lmp.h b/drivers/staging/irda/include/net/irda/ircomm_lmp.h deleted file mode 100644 index 5042a5021a04..000000000000 --- a/drivers/staging/irda/include/net/irda/ircomm_lmp.h +++ /dev/null @@ -1,36 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_lmp.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Wed Jun 9 10:06:07 1999 - * Modified at: Fri Aug 13 07:32:32 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRCOMM_LMP_H -#define IRCOMM_LMP_H - -#include - -int ircomm_open_lsap(struct ircomm_cb *self); - -#endif diff --git a/drivers/staging/irda/include/net/irda/ircomm_param.h b/drivers/staging/irda/include/net/irda/ircomm_param.h deleted file mode 100644 index 1f67432321c4..000000000000 --- a/drivers/staging/irda/include/net/irda/ircomm_param.h +++ /dev/null @@ -1,147 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_param.h - * Version: 1.0 - * Description: Parameter handling for the IrCOMM protocol - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Jun 7 08:47:28 1999 - * Modified at: Wed Aug 25 13:46:33 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRCOMM_PARAMS_H -#define IRCOMM_PARAMS_H - -#include - -/* Parameters common to all service types */ -#define IRCOMM_SERVICE_TYPE 0x00 -#define IRCOMM_PORT_TYPE 0x01 /* Only used in LM-IAS */ -#define IRCOMM_PORT_NAME 0x02 /* Only used in LM-IAS */ - -/* Parameters for both 3 wire and 9 wire */ -#define IRCOMM_DATA_RATE 0x10 -#define IRCOMM_DATA_FORMAT 0x11 -#define IRCOMM_FLOW_CONTROL 0x12 -#define IRCOMM_XON_XOFF 0x13 -#define IRCOMM_ENQ_ACK 0x14 -#define IRCOMM_LINE_STATUS 0x15 -#define IRCOMM_BREAK 0x16 - -/* Parameters for 9 wire */ -#define IRCOMM_DTE 0x20 -#define IRCOMM_DCE 0x21 -#define IRCOMM_POLL 0x22 - -/* Service type (details) */ -#define IRCOMM_3_WIRE_RAW 0x01 -#define IRCOMM_3_WIRE 0x02 -#define IRCOMM_9_WIRE 0x04 -#define IRCOMM_CENTRONICS 0x08 - -/* Port type (details) */ -#define IRCOMM_SERIAL 0x00 -#define IRCOMM_PARALLEL 0x01 - -/* Data format (details) */ -#define IRCOMM_WSIZE_5 0x00 -#define IRCOMM_WSIZE_6 0x01 -#define IRCOMM_WSIZE_7 0x02 -#define IRCOMM_WSIZE_8 0x03 - -#define IRCOMM_1_STOP_BIT 0x00 -#define IRCOMM_2_STOP_BIT 0x04 /* 1.5 if char len 5 */ - -#define IRCOMM_PARITY_DISABLE 0x00 -#define IRCOMM_PARITY_ENABLE 0x08 - -#define IRCOMM_PARITY_ODD 0x00 -#define IRCOMM_PARITY_EVEN 0x10 -#define IRCOMM_PARITY_MARK 0x20 -#define IRCOMM_PARITY_SPACE 0x30 - -/* Flow control */ -#define IRCOMM_XON_XOFF_IN 0x01 -#define IRCOMM_XON_XOFF_OUT 0x02 -#define IRCOMM_RTS_CTS_IN 0x04 -#define IRCOMM_RTS_CTS_OUT 0x08 -#define IRCOMM_DSR_DTR_IN 0x10 -#define IRCOMM_DSR_DTR_OUT 0x20 -#define IRCOMM_ENQ_ACK_IN 0x40 -#define IRCOMM_ENQ_ACK_OUT 0x80 - -/* Line status */ -#define IRCOMM_OVERRUN_ERROR 0x02 -#define IRCOMM_PARITY_ERROR 0x04 -#define IRCOMM_FRAMING_ERROR 0x08 - -/* DTE (Data terminal equipment) line settings */ -#define IRCOMM_DELTA_DTR 0x01 -#define IRCOMM_DELTA_RTS 0x02 -#define IRCOMM_DTR 0x04 -#define IRCOMM_RTS 0x08 - -/* DCE (Data communications equipment) line settings */ -#define IRCOMM_DELTA_CTS 0x01 /* Clear to send has changed */ -#define IRCOMM_DELTA_DSR 0x02 /* Data set ready has changed */ -#define IRCOMM_DELTA_RI 0x04 /* Ring indicator has changed */ -#define IRCOMM_DELTA_CD 0x08 /* Carrier detect has changed */ -#define IRCOMM_CTS 0x10 /* Clear to send is high */ -#define IRCOMM_DSR 0x20 /* Data set ready is high */ -#define IRCOMM_RI 0x40 /* Ring indicator is high */ -#define IRCOMM_CD 0x80 /* Carrier detect is high */ -#define IRCOMM_DCE_DELTA_ANY 0x0f - -/* - * Parameter state - */ -struct ircomm_params { - /* General control params */ - __u8 service_type; - __u8 port_type; - char port_name[32]; - - /* Control params for 3- and 9-wire service type */ - __u32 data_rate; /* Data rate in bps */ - __u8 data_format; - __u8 flow_control; - char xonxoff[2]; - char enqack[2]; - __u8 line_status; - __u8 _break; - - __u8 null_modem; - - /* Control params for 9-wire service type */ - __u8 dte; - __u8 dce; - __u8 poll; - - /* Control params for Centronics service type */ -}; - -struct ircomm_tty_cb; /* Forward decl. */ - -int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush); - -extern pi_param_info_t ircomm_param_info; - -#endif /* IRCOMM_PARAMS_H */ - diff --git a/drivers/staging/irda/include/net/irda/ircomm_ttp.h b/drivers/staging/irda/include/net/irda/ircomm_ttp.h deleted file mode 100644 index c5627288bca3..000000000000 --- a/drivers/staging/irda/include/net/irda/ircomm_ttp.h +++ /dev/null @@ -1,37 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_ttp.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Wed Jun 9 10:06:07 1999 - * Modified at: Fri Aug 13 07:32:22 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRCOMM_TTP_H -#define IRCOMM_TTP_H - -#include - -int ircomm_open_tsap(struct ircomm_cb *self); - -#endif - diff --git a/drivers/staging/irda/include/net/irda/ircomm_tty.h b/drivers/staging/irda/include/net/irda/ircomm_tty.h deleted file mode 100644 index 8d4f588974bc..000000000000 --- a/drivers/staging/irda/include/net/irda/ircomm_tty.h +++ /dev/null @@ -1,121 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_tty.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Jun 6 23:24:22 1999 - * Modified at: Fri Jan 28 13:16:57 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRCOMM_TTY_H -#define IRCOMM_TTY_H - -#include -#include -#include -#include /* struct tty_struct */ - -#include -#include -#include - -#define IRCOMM_TTY_PORTS 32 -#define IRCOMM_TTY_MAGIC 0x3432 -#define IRCOMM_TTY_MAJOR 161 -#define IRCOMM_TTY_MINOR 0 - -/* This is used as an initial value to max_header_size before the proper - * value is filled in (5 for ttp, 4 for lmp). This allow us to detect - * the state of the underlying connection. - Jean II */ -#define IRCOMM_TTY_HDR_UNINITIALISED 16 -/* Same for payload size. See qos.c for the smallest max data size */ -#define IRCOMM_TTY_DATA_UNINITIALISED (64 - IRCOMM_TTY_HDR_UNINITIALISED) - -/* - * IrCOMM TTY driver state - */ -struct ircomm_tty_cb { - irda_queue_t queue; /* Must be first */ - struct tty_port port; - magic_t magic; - - int state; /* Connect state */ - - struct ircomm_cb *ircomm; /* IrCOMM layer instance */ - - struct sk_buff *tx_skb; /* Transmit buffer */ - struct sk_buff *ctrl_skb; /* Control data buffer */ - - /* Parameters */ - struct ircomm_params settings; - - __u8 service_type; /* The service that we support */ - int client; /* True if we are a client */ - LOCAL_FLOW flow; /* IrTTP flow status */ - - int line; - - __u8 dlsap_sel; - __u8 slsap_sel; - - __u32 saddr; - __u32 daddr; - - __u32 max_data_size; /* Max data we can transmit in one packet */ - __u32 max_header_size; /* The amount of header space we must reserve */ - __u32 tx_data_size; /* Max data size of current tx_skb */ - - struct iriap_cb *iriap; /* Instance used for querying remote IAS */ - struct ias_object* obj; - void *skey; - void *ckey; - - struct timer_list watchdog_timer; - struct work_struct tqueue; - - /* Protect concurent access to : - * o self->ctrl_skb - * o self->tx_skb - * Maybe other things may gain to be protected as well... - * Jean II */ - spinlock_t spinlock; -}; - -void ircomm_tty_start(struct tty_struct *tty); -void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self); - -int ircomm_tty_tiocmget(struct tty_struct *tty); -int ircomm_tty_tiocmset(struct tty_struct *tty, unsigned int set, - unsigned int clear); -int ircomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg); -void ircomm_tty_set_termios(struct tty_struct *tty, - struct ktermios *old_termios); - -#endif - - - - - - - diff --git a/drivers/staging/irda/include/net/irda/ircomm_tty_attach.h b/drivers/staging/irda/include/net/irda/ircomm_tty_attach.h deleted file mode 100644 index 20dcbdf258cf..000000000000 --- a/drivers/staging/irda/include/net/irda/ircomm_tty_attach.h +++ /dev/null @@ -1,92 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_tty_attach.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Wed Jun 9 15:55:18 1999 - * Modified at: Fri Dec 10 21:04:55 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRCOMM_TTY_ATTACH_H -#define IRCOMM_TTY_ATTACH_H - -#include - -typedef enum { - IRCOMM_TTY_IDLE, - IRCOMM_TTY_SEARCH, - IRCOMM_TTY_QUERY_PARAMETERS, - IRCOMM_TTY_QUERY_LSAP_SEL, - IRCOMM_TTY_SETUP, - IRCOMM_TTY_READY, -} IRCOMM_TTY_STATE; - -/* IrCOMM TTY Events */ -typedef enum { - IRCOMM_TTY_ATTACH_CABLE, - IRCOMM_TTY_DETACH_CABLE, - IRCOMM_TTY_DATA_REQUEST, - IRCOMM_TTY_DATA_INDICATION, - IRCOMM_TTY_DISCOVERY_REQUEST, - IRCOMM_TTY_DISCOVERY_INDICATION, - IRCOMM_TTY_CONNECT_CONFIRM, - IRCOMM_TTY_CONNECT_INDICATION, - IRCOMM_TTY_DISCONNECT_REQUEST, - IRCOMM_TTY_DISCONNECT_INDICATION, - IRCOMM_TTY_WD_TIMER_EXPIRED, - IRCOMM_TTY_GOT_PARAMETERS, - IRCOMM_TTY_GOT_LSAPSEL, -} IRCOMM_TTY_EVENT; - -/* Used for passing information through the state-machine */ -struct ircomm_tty_info { - __u32 saddr; /* Source device address */ - __u32 daddr; /* Destination device address */ - __u8 dlsap_sel; -}; - -extern const char *const ircomm_state[]; -extern const char *const ircomm_tty_state[]; - -int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, - struct sk_buff *skb, struct ircomm_tty_info *info); - - -int ircomm_tty_attach_cable(struct ircomm_tty_cb *self); -void ircomm_tty_detach_cable(struct ircomm_tty_cb *self); -void ircomm_tty_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb); -void ircomm_tty_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb); -void ircomm_tty_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb); -int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self); -void ircomm_tty_link_established(struct ircomm_tty_cb *self); - -#endif /* IRCOMM_TTY_ATTACH_H */ diff --git a/drivers/staging/irda/include/net/irda/irda.h b/drivers/staging/irda/include/net/irda/irda.h deleted file mode 100644 index 92c8fb575213..000000000000 --- a/drivers/staging/irda/include/net/irda/irda.h +++ /dev/null @@ -1,115 +0,0 @@ -/********************************************************************* - * - * Filename: irda.h - * Version: 1.0 - * Description: IrDA common include file for kernel internal use - * Status: Stable - * Author: Dag Brattli - * Created at: Tue Dec 9 21:13:12 1997 - * Modified at: Fri Jan 28 13:16:32 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef NET_IRDA_H -#define NET_IRDA_H - -#include /* struct sk_buff */ -#include -#include /* sa_family_t in */ -#include - -typedef __u32 magic_t; - -#ifndef TRUE -#define TRUE 1 -#endif - -#ifndef FALSE -#define FALSE 0 -#endif - -/* Hack to do small backoff when setting media busy in IrLAP */ -#ifndef SMALL -#define SMALL 5 -#endif - -#ifndef IRDA_MIN /* Lets not mix this MIN with other header files */ -#define IRDA_MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef IRDA_ALIGN -# define IRDA_ALIGN __attribute__((aligned)) -#endif - -#ifdef CONFIG_IRDA_DEBUG -#define IRDA_ASSERT(expr, func) \ -do { if(!(expr)) { \ - printk( "Assertion failed! %s:%s:%d %s\n", \ - __FILE__,__func__,__LINE__,(#expr) ); \ - func } } while (0) -#define IRDA_ASSERT_LABEL(label) label -#else -#define IRDA_ASSERT(expr, func) do { (void)(expr); } while (0) -#define IRDA_ASSERT_LABEL(label) -#endif /* CONFIG_IRDA_DEBUG */ - -/* - * Magic numbers used by Linux-IrDA. Random numbers which must be unique to - * give the best protection - */ - -#define IRTTY_MAGIC 0x2357 -#define LAP_MAGIC 0x1357 -#define LMP_MAGIC 0x4321 -#define LMP_LSAP_MAGIC 0x69333 -#define LMP_LAP_MAGIC 0x3432 -#define IRDA_DEVICE_MAGIC 0x63454 -#define IAS_MAGIC 0x007 -#define TTP_MAGIC 0x241169 -#define TTP_TSAP_MAGIC 0x4345 -#define IROBEX_MAGIC 0x341324 -#define HB_MAGIC 0x64534 -#define IRLAN_MAGIC 0x754 -#define IAS_OBJECT_MAGIC 0x34234 -#define IAS_ATTRIB_MAGIC 0x45232 -#define IRDA_TASK_MAGIC 0x38423 - -#define IAS_DEVICE_ID 0x0000 /* Defined by IrDA, IrLMP section 4.1 (page 68) */ -#define IAS_PNP_ID 0xd342 -#define IAS_OBEX_ID 0x34323 -#define IAS_IRLAN_ID 0x34234 -#define IAS_IRCOMM_ID 0x2343 -#define IAS_IRLPT_ID 0x9876 - -struct net_device; -struct packet_type; - -void irda_proc_register(void); -void irda_proc_unregister(void); - -int irda_sysctl_register(void); -void irda_sysctl_unregister(void); - -int irsock_init(void); -void irsock_cleanup(void); - -int irda_nl_register(void); -void irda_nl_unregister(void); - -int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *ptype, struct net_device *orig_dev); - -#endif /* NET_IRDA_H */ diff --git a/drivers/staging/irda/include/net/irda/irda_device.h b/drivers/staging/irda/include/net/irda/irda_device.h deleted file mode 100644 index 664bf8178412..000000000000 --- a/drivers/staging/irda/include/net/irda/irda_device.h +++ /dev/null @@ -1,285 +0,0 @@ -/********************************************************************* - * - * Filename: irda_device.h - * Version: 0.9 - * Description: Contains various declarations used by the drivers - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Apr 14 12:41:42 1998 - * Modified at: Mon Mar 20 09:08:57 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 1998 Thomas Davis, , - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -/* - * This header contains all the IrDA definitions a driver really - * needs, and therefore the driver should not need to include - * any other IrDA headers - Jean II - */ - -#ifndef IRDA_DEVICE_H -#define IRDA_DEVICE_H - -#include -#include -#include -#include /* struct sk_buff */ -#include -#include - -#include -#include -#include /* struct qos_info */ -#include /* irda_queue_t */ - -/* A few forward declarations (to make compiler happy) */ -struct irlap_cb; - -/* Some non-standard interface flags (should not conflict with any in if.h) */ -#define IFF_SIR 0x0001 /* Supports SIR speeds */ -#define IFF_MIR 0x0002 /* Supports MIR speeds */ -#define IFF_FIR 0x0004 /* Supports FIR speeds */ -#define IFF_VFIR 0x0008 /* Supports VFIR speeds */ -#define IFF_PIO 0x0010 /* Supports PIO transfer of data */ -#define IFF_DMA 0x0020 /* Supports DMA transfer of data */ -#define IFF_SHM 0x0040 /* Supports shared memory data transfers */ -#define IFF_DONGLE 0x0080 /* Interface has a dongle attached */ -#define IFF_AIR 0x0100 /* Supports Advanced IR (AIR) standards */ - -#define IO_XMIT 0x01 -#define IO_RECV 0x02 - -typedef enum { - IRDA_IRLAP, /* IrDA mode, and deliver to IrLAP */ - IRDA_RAW, /* IrDA mode */ - SHARP_ASK, - TV_REMOTE, /* Also known as Consumer Electronics IR */ -} INFRARED_MODE; - -typedef enum { - IRDA_TASK_INIT, /* All tasks are initialized with this state */ - IRDA_TASK_DONE, /* Signals that the task is finished */ - IRDA_TASK_WAIT, - IRDA_TASK_WAIT1, - IRDA_TASK_WAIT2, - IRDA_TASK_WAIT3, - IRDA_TASK_CHILD_INIT, /* Initializing child task */ - IRDA_TASK_CHILD_WAIT, /* Waiting for child task to finish */ - IRDA_TASK_CHILD_DONE /* Child task is finished */ -} IRDA_TASK_STATE; - -struct irda_task; -typedef int (*IRDA_TASK_CALLBACK) (struct irda_task *task); - -struct irda_task { - irda_queue_t q; - magic_t magic; - - IRDA_TASK_STATE state; - IRDA_TASK_CALLBACK function; - IRDA_TASK_CALLBACK finished; - - struct irda_task *parent; - struct timer_list timer; - - void *instance; /* Instance being called */ - void *param; /* Parameter to be used by instance */ -}; - -/* Dongle info */ -struct dongle_reg; -typedef struct { - struct dongle_reg *issue; /* Registration info */ - struct net_device *dev; /* Device we are attached to */ - struct irda_task *speed_task; /* Task handling speed change */ - struct irda_task *reset_task; /* Task handling reset */ - __u32 speed; /* Current speed */ - - /* Callbacks to the IrDA device driver */ - int (*set_mode)(struct net_device *, int mode); - int (*read)(struct net_device *dev, __u8 *buf, int len); - int (*write)(struct net_device *dev, __u8 *buf, int len); - int (*set_dtr_rts)(struct net_device *dev, int dtr, int rts); -} dongle_t; - -/* Dongle registration info */ -struct dongle_reg { - irda_queue_t q; /* Must be first */ - IRDA_DONGLE type; - - void (*open)(dongle_t *dongle, struct qos_info *qos); - void (*close)(dongle_t *dongle); - int (*reset)(struct irda_task *task); - int (*change_speed)(struct irda_task *task); - struct module *owner; -}; - -/* - * Per-packet information we need to hide inside sk_buff - * (must not exceed 48 bytes, check with struct sk_buff) - * The default_qdisc_pad field is a temporary hack. - */ -struct irda_skb_cb { - unsigned int default_qdisc_pad; - magic_t magic; /* Be sure that we can trust the information */ - __u32 next_speed; /* The Speed to be set *after* this frame */ - __u16 mtt; /* Minimum turn around time */ - __u16 xbofs; /* Number of xbofs required, used by SIR mode */ - __u16 next_xbofs; /* Number of xbofs required *after* this frame */ - void *context; /* May be used by drivers */ - void (*destructor)(struct sk_buff *skb); /* Used for flow control */ - __u16 xbofs_delay; /* Number of xbofs used for generating the mtt */ - __u8 line; /* Used by IrCOMM in IrLPT mode */ -}; - -/* Chip specific info */ -typedef struct { - int cfg_base; /* Config register IO base */ - int sir_base; /* SIR IO base */ - int fir_base; /* FIR IO base */ - int mem_base; /* Shared memory base */ - int sir_ext; /* Length of SIR iobase */ - int fir_ext; /* Length of FIR iobase */ - int irq, irq2; /* Interrupts used */ - int dma, dma2; /* DMA channel(s) used */ - int fifo_size; /* FIFO size */ - int irqflags; /* interrupt flags (ie, IRQF_SHARED) */ - int direction; /* Link direction, used by some FIR drivers */ - int enabled; /* Powered on? */ - int suspended; /* Suspended by APM */ - __u32 speed; /* Currently used speed */ - __u32 new_speed; /* Speed we must change to when Tx is finished */ - int dongle_id; /* Dongle or transceiver currently used */ -} chipio_t; - -/* IO buffer specific info (inspired by struct sk_buff) */ -typedef struct { - int state; /* Receiving state (transmit state not used) */ - int in_frame; /* True if receiving frame */ - - __u8 *head; /* start of buffer */ - __u8 *data; /* start of data in buffer */ - - int len; /* current length of data */ - int truesize; /* total allocated size of buffer */ - __u16 fcs; - - struct sk_buff *skb; /* ZeroCopy Rx in async_unwrap_char() */ -} iobuff_t; - -/* Maximum SIR frame (skb) that we expect to receive *unwrapped*. - * Max LAP MTU (I field) is 2048 bytes max (IrLAP 1.1, chapt 6.6.5, p40). - * Max LAP header is 2 bytes (for now). - * Max CRC is 2 bytes at SIR, 4 bytes at FIR. - * Need 1 byte for skb_reserve() to align IP header for IrLAN. - * Add a few extra bytes just to be safe (buffer is power of two anyway) - * Jean II */ -#define IRDA_SKB_MAX_MTU 2064 -/* Maximum SIR frame that we expect to send, wrapped (i.e. with XBOFS - * and escaped characters on top of above). */ -#define IRDA_SIR_MAX_FRAME 4269 - -/* The SIR unwrapper async_unwrap_char() will use a Rx-copy-break mechanism - * when using the optional ZeroCopy Rx, where only small frames are memcpy - * to a smaller skb to save memory. This is the threshold under which copy - * will happen (and over which it won't happen). - * Some FIR drivers may use this #define as well... - * This is the same value as various Ethernet drivers. - Jean II */ -#define IRDA_RX_COPY_THRESHOLD 256 - -/* Function prototypes */ -int irda_device_init(void); -void irda_device_cleanup(void); - -/* IrLAP entry points used by the drivers. - * We declare them here to avoid the driver pulling a whole bunch stack - * headers they don't really need - Jean II */ -struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, - const char *hw_name); -void irlap_close(struct irlap_cb *self); - -/* Interface to be uses by IrLAP */ -void irda_device_set_media_busy(struct net_device *dev, int status); -int irda_device_is_media_busy(struct net_device *dev); -int irda_device_is_receiving(struct net_device *dev); - -/* Interface for internal use */ -static inline int irda_device_txqueue_empty(const struct net_device *dev) -{ - return qdisc_all_tx_empty(dev); -} -int irda_device_set_raw_mode(struct net_device* self, int status); -struct net_device *alloc_irdadev(int sizeof_priv); - -void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode); - -/* - * Function irda_get_mtt (skb) - * - * Utility function for getting the minimum turnaround time out of - * the skb, where it has been hidden in the cb field. - */ -static inline __u16 irda_get_mtt(const struct sk_buff *skb) -{ - const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb; - return (cb->magic == LAP_MAGIC) ? cb->mtt : 10000; -} - -/* - * Function irda_get_next_speed (skb) - * - * Extract the speed that should be set *after* this frame from the skb - * - * Note : return -1 for user space frames - */ -static inline __u32 irda_get_next_speed(const struct sk_buff *skb) -{ - const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb; - return (cb->magic == LAP_MAGIC) ? cb->next_speed : -1; -} - -/* - * Function irda_get_next_xbofs (skb) - * - * Extract the xbofs that should be set for this frame from the skb - * - * Note : default to 10 for user space frames - */ -static inline __u16 irda_get_xbofs(const struct sk_buff *skb) -{ - const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb; - return (cb->magic == LAP_MAGIC) ? cb->xbofs : 10; -} - -/* - * Function irda_get_next_xbofs (skb) - * - * Extract the xbofs that should be set *after* this frame from the skb - * - * Note : return -1 for user space frames - */ -static inline __u16 irda_get_next_xbofs(const struct sk_buff *skb) -{ - const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb; - return (cb->magic == LAP_MAGIC) ? cb->next_xbofs : -1; -} -#endif /* IRDA_DEVICE_H */ - - diff --git a/drivers/staging/irda/include/net/irda/iriap.h b/drivers/staging/irda/include/net/irda/iriap.h deleted file mode 100644 index fcc896491a95..000000000000 --- a/drivers/staging/irda/include/net/irda/iriap.h +++ /dev/null @@ -1,108 +0,0 @@ -/********************************************************************* - * - * Filename: iriap.h - * Version: 0.5 - * Description: Information Access Protocol (IAP) - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Aug 21 00:02:07 1997 - * Modified at: Sat Dec 25 16:42:09 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997-1999 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRIAP_H -#define IRIAP_H - -#include -#include - -#include -#include -#include /* irda_queue_t */ -#include /* struct timer_list */ - -#define IAP_LST 0x80 -#define IAP_ACK 0x40 - -#define IAS_SERVER 0 -#define IAS_CLIENT 1 - -/* IrIAP Op-codes */ -#define GET_INFO_BASE 0x01 -#define GET_OBJECTS 0x02 -#define GET_VALUE 0x03 -#define GET_VALUE_BY_CLASS 0x04 -#define GET_OBJECT_INFO 0x05 -#define GET_ATTRIB_NAMES 0x06 - -#define IAS_SUCCESS 0 -#define IAS_CLASS_UNKNOWN 1 -#define IAS_ATTRIB_UNKNOWN 2 -#define IAS_DISCONNECT 10 - -typedef void (*CONFIRM_CALLBACK)(int result, __u16 obj_id, - struct ias_value *value, void *priv); - -struct iriap_cb { - irda_queue_t q; /* Must be first */ - magic_t magic; /* Magic cookie */ - - int mode; /* Client or server */ - - __u32 saddr; - __u32 daddr; - __u8 operation; - - struct sk_buff *request_skb; - struct lsap_cb *lsap; - __u8 slsap_sel; - - /* Client states */ - IRIAP_STATE client_state; - IRIAP_STATE call_state; - - /* Server states */ - IRIAP_STATE server_state; - IRIAP_STATE r_connect_state; - - CONFIRM_CALLBACK confirm; - void *priv; /* Used to identify client */ - - __u8 max_header_size; - __u32 max_data_size; - - struct timer_list watchdog_timer; -}; - -int iriap_init(void); -void iriap_cleanup(void); - -struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv, - CONFIRM_CALLBACK callback); -void iriap_close(struct iriap_cb *self); - -int iriap_getvaluebyclass_request(struct iriap_cb *self, - __u32 saddr, __u32 daddr, - char *name, char *attr); -void iriap_connect_request(struct iriap_cb *self); -void iriap_send_ack( struct iriap_cb *self); -void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb); - -void iriap_register_server(void); - -#endif - - diff --git a/drivers/staging/irda/include/net/irda/iriap_event.h b/drivers/staging/irda/include/net/irda/iriap_event.h deleted file mode 100644 index 89747f06d9eb..000000000000 --- a/drivers/staging/irda/include/net/irda/iriap_event.h +++ /dev/null @@ -1,85 +0,0 @@ -/********************************************************************* - * - * Filename: iriap_event.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Sun Oct 31 22:02:54 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRIAP_FSM_H -#define IRIAP_FSM_H - -/* Forward because of circular include dependecies */ -struct iriap_cb; - -/* IrIAP states */ -typedef enum { - /* Client */ - S_DISCONNECT, - S_CONNECTING, - S_CALL, - - /* S-Call */ - S_MAKE_CALL, - S_CALLING, - S_OUTSTANDING, - S_REPLYING, - S_WAIT_FOR_CALL, - S_WAIT_ACTIVE, - - /* Server */ - R_DISCONNECT, - R_CALL, - - /* R-Connect */ - R_WAITING, - R_WAIT_ACTIVE, - R_RECEIVING, - R_EXECUTE, - R_RETURNING, -} IRIAP_STATE; - -typedef enum { - IAP_CALL_REQUEST, - IAP_CALL_REQUEST_GVBC, - IAP_CALL_RESPONSE, - IAP_RECV_F_LST, - IAP_LM_DISCONNECT_INDICATION, - IAP_LM_CONNECT_INDICATION, - IAP_LM_CONNECT_CONFIRM, -} IRIAP_EVENT; - -void iriap_next_client_state (struct iriap_cb *self, IRIAP_STATE state); -void iriap_next_call_state (struct iriap_cb *self, IRIAP_STATE state); -void iriap_next_server_state (struct iriap_cb *self, IRIAP_STATE state); -void iriap_next_r_connect_state(struct iriap_cb *self, IRIAP_STATE state); - - -void iriap_do_client_event(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -void iriap_do_call_event (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); - -void iriap_do_server_event (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -void iriap_do_r_connect_event(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); - -#endif /* IRIAP_FSM_H */ - diff --git a/drivers/staging/irda/include/net/irda/irias_object.h b/drivers/staging/irda/include/net/irda/irias_object.h deleted file mode 100644 index 83f78081799c..000000000000 --- a/drivers/staging/irda/include/net/irda/irias_object.h +++ /dev/null @@ -1,108 +0,0 @@ -/********************************************************************* - * - * Filename: irias_object.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Oct 1 22:49:50 1998 - * Modified at: Wed Dec 15 11:20:57 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef LM_IAS_OBJECT_H -#define LM_IAS_OBJECT_H - -#include -#include - -/* LM-IAS Attribute types */ -#define IAS_MISSING 0 -#define IAS_INTEGER 1 -#define IAS_OCT_SEQ 2 -#define IAS_STRING 3 - -/* Object ownership of attributes (user or kernel) */ -#define IAS_KERNEL_ATTR 0 -#define IAS_USER_ATTR 1 - -/* - * LM-IAS Object - */ -struct ias_object { - irda_queue_t q; /* Must be first! */ - magic_t magic; - - char *name; - int id; - hashbin_t *attribs; -}; - -/* - * Values used by LM-IAS attributes - */ -struct ias_value { - __u8 type; /* Value description */ - __u8 owner; /* Managed from user/kernel space */ - int charset; /* Only used by string type */ - int len; - - /* Value */ - union { - int integer; - char *string; - __u8 *oct_seq; - } t; -}; - -/* - * Attributes used by LM-IAS objects - */ -struct ias_attrib { - irda_queue_t q; /* Must be first! */ - int magic; - - char *name; /* Attribute name */ - struct ias_value *value; /* Attribute value */ -}; - -struct ias_object *irias_new_object(char *name, int id); -void irias_insert_object(struct ias_object *obj); -int irias_delete_object(struct ias_object *obj); -int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib, - int cleanobject); -void __irias_delete_object(struct ias_object *obj); - -void irias_add_integer_attrib(struct ias_object *obj, char *name, int value, - int user); -void irias_add_string_attrib(struct ias_object *obj, char *name, char *value, - int user); -void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets, - int len, int user); -int irias_object_change_attribute(char *obj_name, char *attrib_name, - struct ias_value *new_value); -struct ias_object *irias_find_object(char *name); -struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name); - -struct ias_value *irias_new_string_value(char *string); -struct ias_value *irias_new_integer_value(int integer); -struct ias_value *irias_new_octseq_value(__u8 *octseq , int len); -struct ias_value *irias_new_missing_value(void); -void irias_delete_value(struct ias_value *value); - -extern struct ias_value irias_missing; -extern hashbin_t *irias_objects; - -#endif diff --git a/drivers/staging/irda/include/net/irda/irlan_client.h b/drivers/staging/irda/include/net/irda/irlan_client.h deleted file mode 100644 index fa8455eda280..000000000000 --- a/drivers/staging/irda/include/net/irda/irlan_client.h +++ /dev/null @@ -1,42 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_client.h - * Version: 0.3 - * Description: IrDA LAN access layer - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Thu Apr 22 14:13:34 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998 Dag Brattli , All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRLAN_CLIENT_H -#define IRLAN_CLIENT_H - -#include -#include -#include -#include - -#include -#include - -void irlan_client_discovery_indication(discinfo_t *, DISCOVERY_MODE, void *); -void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr); - -void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb); -void irlan_client_get_value_confirm(int result, __u16 obj_id, - struct ias_value *value, void *priv); -#endif diff --git a/drivers/staging/irda/include/net/irda/irlan_common.h b/drivers/staging/irda/include/net/irda/irlan_common.h deleted file mode 100644 index 550c2d6ec7ff..000000000000 --- a/drivers/staging/irda/include/net/irda/irlan_common.h +++ /dev/null @@ -1,230 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_common.h - * Version: 0.8 - * Description: IrDA LAN access layer - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Sun Oct 31 19:41:24 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRLAN_H -#define IRLAN_H - -#include /* for HZ */ - -#include -#include -#include -#include -#include - -#include - -#define IRLAN_MTU 1518 -#define IRLAN_TIMEOUT 10*HZ /* 10 seconds */ - -/* Command packet types */ -#define CMD_GET_PROVIDER_INFO 0 -#define CMD_GET_MEDIA_CHAR 1 -#define CMD_OPEN_DATA_CHANNEL 2 -#define CMD_CLOSE_DATA_CHAN 3 -#define CMD_RECONNECT_DATA_CHAN 4 -#define CMD_FILTER_OPERATION 5 - -/* Some responses */ -#define RSP_SUCCESS 0 -#define RSP_INSUFFICIENT_RESOURCES 1 -#define RSP_INVALID_COMMAND_FORMAT 2 -#define RSP_COMMAND_NOT_SUPPORTED 3 -#define RSP_PARAM_NOT_SUPPORTED 4 -#define RSP_VALUE_NOT_SUPPORTED 5 -#define RSP_NOT_OPEN 6 -#define RSP_AUTHENTICATION_REQUIRED 7 -#define RSP_INVALID_PASSWORD 8 -#define RSP_PROTOCOL_ERROR 9 -#define RSP_ASYNCHRONOUS_ERROR 255 - -/* Media types */ -#define MEDIA_802_3 1 -#define MEDIA_802_5 2 - -/* Filter parameters */ -#define DATA_CHAN 1 -#define FILTER_TYPE 2 -#define FILTER_MODE 3 - -/* Filter types */ -#define IRLAN_DIRECTED 0x01 -#define IRLAN_FUNCTIONAL 0x02 -#define IRLAN_GROUP 0x04 -#define IRLAN_MAC_FRAME 0x08 -#define IRLAN_MULTICAST 0x10 -#define IRLAN_BROADCAST 0x20 -#define IRLAN_IPX_SOCKET 0x40 - -/* Filter modes */ -#define ALL 1 -#define FILTER 2 -#define NONE 3 - -/* Filter operations */ -#define GET 1 -#define CLEAR 2 -#define ADD 3 -#define REMOVE 4 -#define DYNAMIC 5 - -/* Access types */ -#define ACCESS_DIRECT 1 -#define ACCESS_PEER 2 -#define ACCESS_HOSTED 3 - -#define IRLAN_BYTE 0 -#define IRLAN_SHORT 1 -#define IRLAN_ARRAY 2 - -/* IrLAN sits on top if IrTTP */ -#define IRLAN_MAX_HEADER (TTP_HEADER+LMP_HEADER) -/* 1 byte for the command code and 1 byte for the parameter count */ -#define IRLAN_CMD_HEADER 2 - -#define IRLAN_STRING_PARAMETER_LEN(name, value) (1 + strlen((name)) + 2 \ - + strlen ((value))) -#define IRLAN_BYTE_PARAMETER_LEN(name) (1 + strlen((name)) + 2 + 1) -#define IRLAN_SHORT_PARAMETER_LEN(name) (1 + strlen((name)) + 2 + 2) - -/* - * IrLAN client - */ -struct irlan_client_cb { - int state; - - int open_retries; - - struct tsap_cb *tsap_ctrl; - __u32 max_sdu_size; - __u8 max_header_size; - - int access_type; /* Access type of provider */ - __u8 reconnect_key[255]; - __u8 key_len; - - __u16 recv_arb_val; - __u16 max_frame; - int filter_type; - - int unicast_open; - int broadcast_open; - - int tx_busy; - struct sk_buff_head txq; /* Transmit control queue */ - - struct iriap_cb *iriap; - - struct timer_list kick_timer; -}; - -/* - * IrLAN provider - */ -struct irlan_provider_cb { - int state; - - struct tsap_cb *tsap_ctrl; - __u32 max_sdu_size; - __u8 max_header_size; - - /* - * Store some values here which are used by the provider to parse - * the filter operations - */ - int data_chan; - int filter_type; - int filter_mode; - int filter_operation; - int filter_entry; - int access_type; /* Access type */ - __u16 send_arb_val; - - __u8 mac_address[ETH_ALEN]; /* Generated MAC address for peer device */ -}; - -/* - * IrLAN control block - */ -struct irlan_cb { - int magic; - struct list_head dev_list; - struct net_device *dev; /* Ethernet device structure*/ - - __u32 saddr; /* Source device address */ - __u32 daddr; /* Destination device address */ - int disconnect_reason; /* Why we got disconnected */ - - int media; /* Media type */ - __u8 version[2]; /* IrLAN version */ - - struct tsap_cb *tsap_data; /* Data TSAP */ - - int use_udata; /* Use Unit Data transfers */ - - __u8 stsap_sel_data; /* Source data TSAP selector */ - __u8 dtsap_sel_data; /* Destination data TSAP selector */ - __u8 dtsap_sel_ctrl; /* Destination ctrl TSAP selector */ - - struct irlan_client_cb client; /* Client specific fields */ - struct irlan_provider_cb provider; /* Provider specific fields */ - - __u32 max_sdu_size; - __u8 max_header_size; - - wait_queue_head_t open_wait; - struct timer_list watchdog_timer; -}; - -void irlan_close(struct irlan_cb *self); -void irlan_close_tsaps(struct irlan_cb *self); - -int irlan_register_netdev(struct irlan_cb *self); -void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel); -void irlan_start_watchdog_timer(struct irlan_cb *self, int timeout); - -void irlan_open_data_tsap(struct irlan_cb *self); - -int irlan_run_ctrl_tx_queue(struct irlan_cb *self); - -struct irlan_cb *irlan_get_any(void); -void irlan_get_provider_info(struct irlan_cb *self); -void irlan_get_media_char(struct irlan_cb *self); -void irlan_open_data_channel(struct irlan_cb *self); -void irlan_close_data_channel(struct irlan_cb *self); -void irlan_set_multicast_filter(struct irlan_cb *self, int status); -void irlan_set_broadcast_filter(struct irlan_cb *self, int status); - -int irlan_insert_byte_param(struct sk_buff *skb, char *param, __u8 value); -int irlan_insert_short_param(struct sk_buff *skb, char *param, __u16 value); -int irlan_insert_string_param(struct sk_buff *skb, char *param, char *value); -int irlan_insert_array_param(struct sk_buff *skb, char *name, __u8 *value, - __u16 value_len); - -int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len); - -#endif - - diff --git a/drivers/staging/irda/include/net/irda/irlan_eth.h b/drivers/staging/irda/include/net/irda/irlan_eth.h deleted file mode 100644 index de5c81691f33..000000000000 --- a/drivers/staging/irda/include/net/irda/irlan_eth.h +++ /dev/null @@ -1,32 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_eth.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Oct 15 08:36:58 1998 - * Modified at: Fri May 14 23:29:00 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRLAN_ETH_H -#define IRLAN_ETH_H - -struct net_device *alloc_irlandev(const char *name); -int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb); - -void irlan_eth_flow_indication( void *instance, void *sap, LOCAL_FLOW flow); -#endif diff --git a/drivers/staging/irda/include/net/irda/irlan_event.h b/drivers/staging/irda/include/net/irda/irlan_event.h deleted file mode 100644 index 018b5a77e610..000000000000 --- a/drivers/staging/irda/include/net/irda/irlan_event.h +++ /dev/null @@ -1,81 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_event.h - * Version: - * Description: LAN access - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Tue Feb 2 09:45:17 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997 Dag Brattli , All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRLAN_EVENT_H -#define IRLAN_EVENT_H - -#include -#include - -#include - -typedef enum { - IRLAN_IDLE, - IRLAN_QUERY, - IRLAN_CONN, - IRLAN_INFO, - IRLAN_MEDIA, - IRLAN_OPEN, - IRLAN_WAIT, - IRLAN_ARB, - IRLAN_DATA, - IRLAN_CLOSE, - IRLAN_SYNC -} IRLAN_STATE; - -typedef enum { - IRLAN_DISCOVERY_INDICATION, - IRLAN_IAS_PROVIDER_AVAIL, - IRLAN_IAS_PROVIDER_NOT_AVAIL, - IRLAN_LAP_DISCONNECT, - IRLAN_LMP_DISCONNECT, - IRLAN_CONNECT_COMPLETE, - IRLAN_DATA_INDICATION, - IRLAN_DATA_CONNECT_INDICATION, - IRLAN_RETRY_CONNECT, - - IRLAN_CONNECT_INDICATION, - IRLAN_GET_INFO_CMD, - IRLAN_GET_MEDIA_CMD, - IRLAN_OPEN_DATA_CMD, - IRLAN_FILTER_CONFIG_CMD, - - IRLAN_CHECK_CON_ARB, - IRLAN_PROVIDER_SIGNAL, - - IRLAN_WATCHDOG_TIMEOUT, -} IRLAN_EVENT; - -extern const char * const irlan_state[]; - -void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); - -void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); - -void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state); -void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state); - -#endif diff --git a/drivers/staging/irda/include/net/irda/irlan_filter.h b/drivers/staging/irda/include/net/irda/irlan_filter.h deleted file mode 100644 index a5a2539485bd..000000000000 --- a/drivers/staging/irda/include/net/irda/irlan_filter.h +++ /dev/null @@ -1,35 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_filter.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Fri Jan 29 15:24:08 1999 - * Modified at: Sun Feb 7 23:35:31 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRLAN_FILTER_H -#define IRLAN_FILTER_H - -void irlan_check_command_param(struct irlan_cb *self, char *param, - char *value); -void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb); -#ifdef CONFIG_PROC_FS -void irlan_print_filter(struct seq_file *seq, int filter_type); -#endif - -#endif /* IRLAN_FILTER_H */ diff --git a/drivers/staging/irda/include/net/irda/irlan_provider.h b/drivers/staging/irda/include/net/irda/irlan_provider.h deleted file mode 100644 index 92f3b0e1029b..000000000000 --- a/drivers/staging/irda/include/net/irda/irlan_provider.h +++ /dev/null @@ -1,52 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_provider.h - * Version: 0.1 - * Description: IrDA LAN access layer - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Sun May 9 12:26:11 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRLAN_SERVER_H -#define IRLAN_SERVER_H - -#include -#include -#include -#include - -#include - -void irlan_provider_ctrl_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb); - - -void irlan_provider_connect_response(struct irlan_cb *, struct tsap_cb *); - -int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb); -int irlan_provider_parse_command(struct irlan_cb *self, int cmd, - struct sk_buff *skb); - -void irlan_provider_send_reply(struct irlan_cb *self, int command, - int ret_code); -int irlan_provider_open_ctrl_tsap(struct irlan_cb *self); - -#endif - - diff --git a/drivers/staging/irda/include/net/irda/irlap.h b/drivers/staging/irda/include/net/irda/irlap.h deleted file mode 100644 index 6f23e820618c..000000000000 --- a/drivers/staging/irda/include/net/irda/irlap.h +++ /dev/null @@ -1,311 +0,0 @@ -/********************************************************************* - * - * Filename: irlap.h - * Version: 0.8 - * Description: An IrDA LAP driver for Linux - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Fri Dec 10 13:21:17 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRLAP_H -#define IRLAP_H - -#include -#include -#include -#include - -#include /* irda_queue_t */ -#include /* struct qos_info */ -#include /* discovery_t */ -#include /* IRLAP_STATE, ... */ -#include /* struct notify_t */ - -#define CONFIG_IRDA_DYNAMIC_WINDOW 1 - -#define LAP_RELIABLE 1 -#define LAP_UNRELIABLE 0 - -#define LAP_ADDR_HEADER 1 /* IrLAP Address Header */ -#define LAP_CTRL_HEADER 1 /* IrLAP Control Header */ - -/* May be different when we get VFIR */ -#define LAP_MAX_HEADER (LAP_ADDR_HEADER + LAP_CTRL_HEADER) - -/* Each IrDA device gets a random 32 bits IRLAP device address */ -#define LAP_ALEN 4 - -#define BROADCAST 0xffffffff /* Broadcast device address */ -#define CBROADCAST 0xfe /* Connection broadcast address */ -#define XID_FORMAT 0x01 /* Discovery XID format */ - -/* Nobody seems to use this constant. */ -#define LAP_WINDOW_SIZE 8 -/* We keep the LAP queue very small to minimise the amount of buffering. - * this improve latency and reduce resource consumption. - * This work only because we have synchronous refilling of IrLAP through - * the flow control mechanism (via scheduler and IrTTP). - * 2 buffers is the minimum we can work with, one that we send while polling - * IrTTP, and another to know that we should not send the pf bit. - * Jean II */ -#define LAP_HIGH_THRESHOLD 2 -/* Some rare non TTP clients don't implement flow control, and - * so don't comply with the above limit (and neither with this one). - * For IAP and management, it doesn't matter, because they never transmit much. - *.For IrLPT, this should be fixed. - * - Jean II */ -#define LAP_MAX_QUEUE 10 -/* Please note that all IrDA management frames (LMP/TTP conn req/disc and - * IAS queries) fall in the second category and are sent to LAP even if TTP - * is stopped. This means that those frames will wait only a maximum of - * two (2) data frames before beeing sent on the "wire", which speed up - * new socket setup when the link is saturated. - * Same story for two sockets competing for the medium : if one saturates - * the LAP, when the other want to transmit it only has to wait for - * maximum three (3) packets (2 + one scheduling), which improve performance - * of delay sensitive applications. - * Jean II */ - -#define NR_EXPECTED 1 -#define NR_UNEXPECTED 0 -#define NR_INVALID -1 - -#define NS_EXPECTED 1 -#define NS_UNEXPECTED 0 -#define NS_INVALID -1 - -/* - * Meta information passed within the IrLAP state machine - */ -struct irlap_info { - __u8 caddr; /* Connection address */ - __u8 control; /* Frame type */ - __u8 cmd; - - __u32 saddr; - __u32 daddr; - - int pf; /* Poll/final bit set */ - - __u8 nr; /* Sequence number of next frame expected */ - __u8 ns; /* Sequence number of frame sent */ - - int S; /* Number of slots */ - int slot; /* Random chosen slot */ - int s; /* Current slot */ - - discovery_t *discovery; /* Discovery information */ -}; - -/* Main structure of IrLAP */ -struct irlap_cb { - irda_queue_t q; /* Must be first */ - magic_t magic; - - /* Device we are attached to */ - struct net_device *netdev; - char hw_name[2*IFNAMSIZ + 1]; - - /* Connection state */ - volatile IRLAP_STATE state; /* Current state */ - - /* Timers used by IrLAP */ - struct timer_list query_timer; - struct timer_list slot_timer; - struct timer_list discovery_timer; - struct timer_list final_timer; - struct timer_list poll_timer; - struct timer_list wd_timer; - struct timer_list backoff_timer; - - /* Media busy stuff */ - struct timer_list media_busy_timer; - int media_busy; - - /* Timeouts which will be different with different turn time */ - int slot_timeout; - int poll_timeout; - int final_timeout; - int wd_timeout; - - struct sk_buff_head txq; /* Frames to be transmitted */ - struct sk_buff_head txq_ultra; - - __u8 caddr; /* Connection address */ - __u32 saddr; /* Source device address */ - __u32 daddr; /* Destination device address */ - - int retry_count; /* Times tried to establish connection */ - int add_wait; /* True if we are waiting for frame */ - - __u8 connect_pending; - __u8 disconnect_pending; - - /* To send a faster RR if tx queue empty */ -#ifdef CONFIG_IRDA_FAST_RR - int fast_RR_timeout; - int fast_RR; -#endif /* CONFIG_IRDA_FAST_RR */ - - int N1; /* N1 * F-timer = Negitiated link disconnect warning threshold */ - int N2; /* N2 * F-timer = Negitiated link disconnect time */ - int N3; /* Connection retry count */ - - int local_busy; - int remote_busy; - int xmitflag; - - __u8 vs; /* Next frame to be sent */ - __u8 vr; /* Next frame to be received */ - __u8 va; /* Last frame acked */ - int window; /* Nr of I-frames allowed to send */ - int window_size; /* Current negotiated window size */ - -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - __u32 line_capacity; /* Number of bytes allowed to send */ - __u32 bytes_left; /* Number of bytes still allowed to transmit */ -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - - struct sk_buff_head wx_list; - - __u8 ack_required; - - /* XID parameters */ - __u8 S; /* Number of slots */ - __u8 slot; /* Random chosen slot */ - __u8 s; /* Current slot */ - int frame_sent; /* Have we sent reply? */ - - hashbin_t *discovery_log; - discovery_t *discovery_cmd; - - __u32 speed; /* Link speed */ - - struct qos_info qos_tx; /* QoS requested by peer */ - struct qos_info qos_rx; /* QoS requested by self */ - struct qos_info *qos_dev; /* QoS supported by device */ - - notify_t notify; /* Callbacks to IrLMP */ - - int mtt_required; /* Minimum turnaround time required */ - int xbofs_delay; /* Nr of XBOF's used to MTT */ - int bofs_count; /* Negotiated extra BOFs */ - int next_bofs; /* Negotiated extra BOFs after next frame */ - - int mode; /* IrLAP mode (primary, secondary or monitor) */ -}; - -/* - * Function prototypes - */ -int irlap_init(void); -void irlap_cleanup(void); - -struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, - const char *hw_name); -void irlap_close(struct irlap_cb *self); - -void irlap_connect_request(struct irlap_cb *self, __u32 daddr, - struct qos_info *qos, int sniff); -void irlap_connect_response(struct irlap_cb *self, struct sk_buff *skb); -void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb); -void irlap_connect_confirm(struct irlap_cb *, struct sk_buff *skb); - -void irlap_data_indication(struct irlap_cb *, struct sk_buff *, int unreliable); -void irlap_data_request(struct irlap_cb *, struct sk_buff *, int unreliable); - -#ifdef CONFIG_IRDA_ULTRA -void irlap_unitdata_request(struct irlap_cb *, struct sk_buff *); -void irlap_unitdata_indication(struct irlap_cb *, struct sk_buff *); -#endif /* CONFIG_IRDA_ULTRA */ - -void irlap_disconnect_request(struct irlap_cb *); -void irlap_disconnect_indication(struct irlap_cb *, LAP_REASON reason); - -void irlap_status_indication(struct irlap_cb *, int quality_of_link); - -void irlap_test_request(__u8 *info, int len); - -void irlap_discovery_request(struct irlap_cb *, discovery_t *discovery); -void irlap_discovery_confirm(struct irlap_cb *, hashbin_t *discovery_log); -void irlap_discovery_indication(struct irlap_cb *, discovery_t *discovery); - -void irlap_reset_indication(struct irlap_cb *self); -void irlap_reset_confirm(void); - -void irlap_update_nr_received(struct irlap_cb *, int nr); -int irlap_validate_nr_received(struct irlap_cb *, int nr); -int irlap_validate_ns_received(struct irlap_cb *, int ns); - -int irlap_generate_rand_time_slot(int S, int s); -void irlap_initiate_connection_state(struct irlap_cb *); -void irlap_flush_all_queues(struct irlap_cb *); -void irlap_wait_min_turn_around(struct irlap_cb *, struct qos_info *); - -void irlap_apply_default_connection_parameters(struct irlap_cb *self); -void irlap_apply_connection_parameters(struct irlap_cb *self, int now); - -#define IRLAP_GET_HEADER_SIZE(self) (LAP_MAX_HEADER) -#define IRLAP_GET_TX_QUEUE_LEN(self) skb_queue_len(&self->txq) - -/* Return TRUE if the node is in primary mode (i.e. master) - * - Jean II */ -static inline int irlap_is_primary(struct irlap_cb *self) -{ - int ret; - switch(self->state) { - case LAP_XMIT_P: - case LAP_NRM_P: - ret = 1; - break; - case LAP_XMIT_S: - case LAP_NRM_S: - ret = 0; - break; - default: - ret = -1; - } - return ret; -} - -/* Clear a pending IrLAP disconnect. - Jean II */ -static inline void irlap_clear_disconnect(struct irlap_cb *self) -{ - self->disconnect_pending = FALSE; -} - -/* - * Function irlap_next_state (self, state) - * - * Switches state and provides debug information - * - */ -static inline void irlap_next_state(struct irlap_cb *self, IRLAP_STATE state) -{ - /* - if (!self || self->magic != LAP_MAGIC) - return; - - pr_debug("next LAP state = %s\n", irlap_state[state]); - */ - self->state = state; -} - -#endif diff --git a/drivers/staging/irda/include/net/irda/irlap_event.h b/drivers/staging/irda/include/net/irda/irlap_event.h deleted file mode 100644 index e4325fee1267..000000000000 --- a/drivers/staging/irda/include/net/irda/irlap_event.h +++ /dev/null @@ -1,129 +0,0 @@ -/********************************************************************* - * - * - * Filename: irlap_event.h - * Version: 0.1 - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Aug 16 00:59:29 1997 - * Modified at: Tue Dec 21 11:20:30 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRLAP_EVENT_H -#define IRLAP_EVENT_H - -#include - -/* A few forward declarations (to make compiler happy) */ -struct irlap_cb; -struct irlap_info; - -/* IrLAP States */ -typedef enum { - LAP_NDM, /* Normal disconnected mode */ - LAP_QUERY, - LAP_REPLY, - LAP_CONN, /* Connect indication */ - LAP_SETUP, /* Setting up connection */ - LAP_OFFLINE, /* A really boring state */ - LAP_XMIT_P, - LAP_PCLOSE, - LAP_NRM_P, /* Normal response mode as primary */ - LAP_RESET_WAIT, - LAP_RESET, - LAP_NRM_S, /* Normal response mode as secondary */ - LAP_XMIT_S, - LAP_SCLOSE, - LAP_RESET_CHECK, -} IRLAP_STATE; - -/* IrLAP Events */ -typedef enum { - /* Services events */ - DISCOVERY_REQUEST, - CONNECT_REQUEST, - CONNECT_RESPONSE, - DISCONNECT_REQUEST, - DATA_REQUEST, - RESET_REQUEST, - RESET_RESPONSE, - - /* Send events */ - SEND_I_CMD, - SEND_UI_FRAME, - - /* Receive events */ - RECV_DISCOVERY_XID_CMD, - RECV_DISCOVERY_XID_RSP, - RECV_SNRM_CMD, - RECV_TEST_CMD, - RECV_TEST_RSP, - RECV_UA_RSP, - RECV_DM_RSP, - RECV_RD_RSP, - RECV_I_CMD, - RECV_I_RSP, - RECV_UI_FRAME, - RECV_FRMR_RSP, - RECV_RR_CMD, - RECV_RR_RSP, - RECV_RNR_CMD, - RECV_RNR_RSP, - RECV_REJ_CMD, - RECV_REJ_RSP, - RECV_SREJ_CMD, - RECV_SREJ_RSP, - RECV_DISC_CMD, - - /* Timer events */ - SLOT_TIMER_EXPIRED, - QUERY_TIMER_EXPIRED, - FINAL_TIMER_EXPIRED, - POLL_TIMER_EXPIRED, - DISCOVERY_TIMER_EXPIRED, - WD_TIMER_EXPIRED, - BACKOFF_TIMER_EXPIRED, - MEDIA_BUSY_TIMER_EXPIRED, -} IRLAP_EVENT; - -/* - * Disconnect reason code - */ -typedef enum { /* FIXME check the two first reason codes */ - LAP_DISC_INDICATION=1, /* Received a disconnect request from peer */ - LAP_NO_RESPONSE, /* To many retransmits without response */ - LAP_RESET_INDICATION, /* To many retransmits, or invalid nr/ns */ - LAP_FOUND_NONE, /* No devices were discovered */ - LAP_MEDIA_BUSY, - LAP_PRIMARY_CONFLICT, -} LAP_REASON; - -extern const char *const irlap_state[]; - -void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -void irlap_print_event(IRLAP_EVENT event); - -int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb); - -#endif diff --git a/drivers/staging/irda/include/net/irda/irlap_frame.h b/drivers/staging/irda/include/net/irda/irlap_frame.h deleted file mode 100644 index cbc12a926e5f..000000000000 --- a/drivers/staging/irda/include/net/irda/irlap_frame.h +++ /dev/null @@ -1,167 +0,0 @@ -/********************************************************************* - * - * Filename: irlap_frame.h - * Version: 0.9 - * Description: IrLAP frame declarations - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Aug 19 10:27:26 1997 - * Modified at: Sat Dec 25 21:07:26 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997-1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRLAP_FRAME_H -#define IRLAP_FRAME_H - -#include - -#include - -/* A few forward declarations (to make compiler happy) */ -struct irlap_cb; -struct discovery_t; - -/* Frame types and templates */ -#define INVALID 0xff - -/* Unnumbered (U) commands */ -#define SNRM_CMD 0x83 /* Set Normal Response Mode */ -#define DISC_CMD 0x43 /* Disconnect */ -#define XID_CMD 0x2f /* Exchange Station Identification */ -#define TEST_CMD 0xe3 /* Test */ - -/* Unnumbered responses */ -#define RNRM_RSP 0x83 /* Request Normal Response Mode */ -#define UA_RSP 0x63 /* Unnumbered Acknowledgement */ -#define FRMR_RSP 0x87 /* Frame Reject */ -#define DM_RSP 0x0f /* Disconnect Mode */ -#define RD_RSP 0x43 /* Request Disconnection */ -#define XID_RSP 0xaf /* Exchange Station Identification */ -#define TEST_RSP 0xe3 /* Test frame */ - -/* Supervisory (S) */ -#define RR 0x01 /* Receive Ready */ -#define REJ 0x09 /* Reject */ -#define RNR 0x05 /* Receive Not Ready */ -#define SREJ 0x0d /* Selective Reject */ - -/* Information (I) */ -#define I_FRAME 0x00 /* Information Format */ -#define UI_FRAME 0x03 /* Unnumbered Information */ - -#define CMD_FRAME 0x01 -#define RSP_FRAME 0x00 - -#define PF_BIT 0x10 /* Poll/final bit */ - -/* Some IrLAP field lengths */ -/* - * Only baud rate triplet is 4 bytes (PV can be 2 bytes). - * All others params (7) are 3 bytes, so that's 7*3 + 1*4 bytes. - */ -#define IRLAP_NEGOCIATION_PARAMS_LEN 25 -#define IRLAP_DISCOVERY_INFO_LEN 32 - -struct disc_frame { - __u8 caddr; /* Connection address */ - __u8 control; -} __packed; - -struct xid_frame { - __u8 caddr; /* Connection address */ - __u8 control; - __u8 ident; /* Should always be XID_FORMAT */ - __le32 saddr; /* Source device address */ - __le32 daddr; /* Destination device address */ - __u8 flags; /* Discovery flags */ - __u8 slotnr; - __u8 version; -} __packed; - -struct test_frame { - __u8 caddr; /* Connection address */ - __u8 control; - __le32 saddr; /* Source device address */ - __le32 daddr; /* Destination device address */ -} __packed; - -struct ua_frame { - __u8 caddr; - __u8 control; - __le32 saddr; /* Source device address */ - __le32 daddr; /* Dest device address */ -} __packed; - -struct dm_frame { - __u8 caddr; /* Connection address */ - __u8 control; -} __packed; - -struct rd_frame { - __u8 caddr; /* Connection address */ - __u8 control; -} __packed; - -struct rr_frame { - __u8 caddr; /* Connection address */ - __u8 control; -} __packed; - -struct i_frame { - __u8 caddr; - __u8 control; -} __packed; - -struct snrm_frame { - __u8 caddr; - __u8 control; - __le32 saddr; - __le32 daddr; - __u8 ncaddr; -} __packed; - -void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb); -void irlap_send_discovery_xid_frame(struct irlap_cb *, int S, __u8 s, - __u8 command, - struct discovery_t *discovery); -void irlap_send_snrm_frame(struct irlap_cb *, struct qos_info *); -void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr, - struct sk_buff *cmd); -void irlap_send_ua_response_frame(struct irlap_cb *, struct qos_info *); -void irlap_send_dm_frame(struct irlap_cb *self); -void irlap_send_rd_frame(struct irlap_cb *self); -void irlap_send_disc_frame(struct irlap_cb *self); -void irlap_send_rr_frame(struct irlap_cb *self, int command); - -void irlap_send_data_primary(struct irlap_cb *, struct sk_buff *); -void irlap_send_data_primary_poll(struct irlap_cb *, struct sk_buff *); -void irlap_send_data_secondary(struct irlap_cb *, struct sk_buff *); -void irlap_send_data_secondary_final(struct irlap_cb *, struct sk_buff *); -void irlap_resend_rejected_frames(struct irlap_cb *, int command); -void irlap_resend_rejected_frame(struct irlap_cb *self, int command); - -void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb, - __u8 caddr, int command); - -int irlap_insert_qos_negotiation_params(struct irlap_cb *self, - struct sk_buff *skb); - -#endif diff --git a/drivers/staging/irda/include/net/irda/irlmp.h b/drivers/staging/irda/include/net/irda/irlmp.h deleted file mode 100644 index f132924cc9da..000000000000 --- a/drivers/staging/irda/include/net/irda/irlmp.h +++ /dev/null @@ -1,295 +0,0 @@ -/********************************************************************* - * - * Filename: irlmp.h - * Version: 0.9 - * Description: IrDA Link Management Protocol (LMP) layer - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 17 20:54:32 1997 - * Modified at: Fri Dec 10 13:23:01 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRLMP_H -#define IRLMP_H - -#include /* for HZ */ - -#include - -#include -#include -#include /* LAP_MAX_HEADER, ... */ -#include -#include -#include - -/* LSAP-SEL's */ -#define LSAP_MASK 0x7f -#define LSAP_IAS 0x00 -#define LSAP_ANY 0xff -#define LSAP_MAX 0x6f /* 0x70-0x7f are reserved */ -#define LSAP_CONNLESS 0x70 /* Connectionless LSAP, mostly used for Ultra */ - -#define DEV_ADDR_ANY 0xffffffff - -#define LMP_HEADER 2 /* Dest LSAP + Source LSAP */ -#define LMP_CONTROL_HEADER 4 /* LMP_HEADER + opcode + parameter */ -#define LMP_PID_HEADER 1 /* Used by Ultra */ -#define LMP_MAX_HEADER (LMP_CONTROL_HEADER+LAP_MAX_HEADER) - -#define LM_MAX_CONNECTIONS 10 - -#define LM_IDLE_TIMEOUT 2*HZ /* 2 seconds for now */ - -typedef enum { - S_PNP = 0, - S_PDA, - S_COMPUTER, - S_PRINTER, - S_MODEM, - S_FAX, - S_LAN, - S_TELEPHONY, - S_COMM, - S_OBEX, - S_ANY, - S_END, -} SERVICE; - -/* For selective discovery */ -typedef void (*DISCOVERY_CALLBACK1) (discinfo_t *, DISCOVERY_MODE, void *); -/* For expiry (the same) */ -typedef void (*DISCOVERY_CALLBACK2) (discinfo_t *, DISCOVERY_MODE, void *); - -typedef struct { - irda_queue_t queue; /* Must be first */ - - __u16_host_order hints; /* Hint bits */ -} irlmp_service_t; - -typedef struct { - irda_queue_t queue; /* Must be first */ - - __u16_host_order hint_mask; - - DISCOVERY_CALLBACK1 disco_callback; /* Selective discovery */ - DISCOVERY_CALLBACK2 expir_callback; /* Selective expiration */ - void *priv; /* Used to identify client */ -} irlmp_client_t; - -/* - * Information about each logical LSAP connection - */ -struct lsap_cb { - irda_queue_t queue; /* Must be first */ - magic_t magic; - - unsigned long connected; /* set_bit used on this */ - int persistent; - - __u8 slsap_sel; /* Source (this) LSAP address */ - __u8 dlsap_sel; /* Destination LSAP address (if connected) */ -#ifdef CONFIG_IRDA_ULTRA - __u8 pid; /* Used by connectionless LSAP */ -#endif /* CONFIG_IRDA_ULTRA */ - struct sk_buff *conn_skb; /* Store skb here while connecting */ - - struct timer_list watchdog_timer; - - LSAP_STATE lsap_state; /* Connection state */ - notify_t notify; /* Indication/Confirm entry points */ - struct qos_info qos; /* QoS for this connection */ - - struct lap_cb *lap; /* Pointer to LAP connection structure */ -}; - -/* - * Used for caching the last slsap->dlsap->handle mapping - * - * We don't need to keep/match the remote address in the cache because - * we are associated with a specific LAP (which implies it). - * Jean II - */ -typedef struct { - int valid; - - __u8 slsap_sel; - __u8 dlsap_sel; - struct lsap_cb *lsap; -} CACHE_ENTRY; - -/* - * Information about each registered IrLAP layer - */ -struct lap_cb { - irda_queue_t queue; /* Must be first */ - magic_t magic; - - int reason; /* LAP disconnect reason */ - - IRLMP_STATE lap_state; - - struct irlap_cb *irlap; /* Instance of IrLAP layer */ - hashbin_t *lsaps; /* LSAP associated with this link */ - struct lsap_cb *flow_next; /* Next lsap to be polled for Tx */ - - __u8 caddr; /* Connection address */ - __u32 saddr; /* Source device address */ - __u32 daddr; /* Destination device address */ - - struct qos_info *qos; /* LAP QoS for this session */ - struct timer_list idle_timer; - -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - /* The lsap cache was moved from struct irlmp_cb to here because - * it must be associated with the specific LAP. Also, this - * improves performance. - Jean II */ - CACHE_ENTRY cache; /* Caching last slsap->dlsap->handle mapping */ -#endif -}; - -/* - * Main structure for IrLMP - */ -struct irlmp_cb { - magic_t magic; - - __u8 conflict_flag; - - discovery_t discovery_cmd; /* Discovery command to use by IrLAP */ - discovery_t discovery_rsp; /* Discovery response to use by IrLAP */ - - /* Last lsap picked automatically by irlmp_find_free_slsap() */ - int last_lsap_sel; - - struct timer_list discovery_timer; - - hashbin_t *links; /* IrLAP connection table */ - hashbin_t *unconnected_lsaps; - hashbin_t *clients; - hashbin_t *services; - - hashbin_t *cachelog; /* Current discovery log */ - - int running; - - __u16_host_order hints; /* Hint bits */ -}; - -/* Prototype declarations */ -int irlmp_init(void); -void irlmp_cleanup(void); -struct lsap_cb *irlmp_open_lsap(__u8 slsap, notify_t *notify, __u8 pid); -void irlmp_close_lsap( struct lsap_cb *self); - -__u16 irlmp_service_to_hint(int service); -void *irlmp_register_service(__u16 hints); -int irlmp_unregister_service(void *handle); -void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb, - DISCOVERY_CALLBACK2 expir_clb, void *priv); -int irlmp_unregister_client(void *handle); -int irlmp_update_client(void *handle, __u16 hint_mask, - DISCOVERY_CALLBACK1 disco_clb, - DISCOVERY_CALLBACK2 expir_clb, void *priv); - -void irlmp_register_link(struct irlap_cb *, __u32 saddr, notify_t *); -void irlmp_unregister_link(__u32 saddr); - -int irlmp_connect_request(struct lsap_cb *, __u8 dlsap_sel, - __u32 saddr, __u32 daddr, - struct qos_info *, struct sk_buff *); -void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb); -int irlmp_connect_response(struct lsap_cb *, struct sk_buff *); -void irlmp_connect_confirm(struct lsap_cb *, struct sk_buff *); -struct lsap_cb *irlmp_dup(struct lsap_cb *self, void *instance); - -void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason, - struct sk_buff *userdata); -int irlmp_disconnect_request(struct lsap_cb *, struct sk_buff *userdata); - -void irlmp_discovery_confirm(hashbin_t *discovery_log, DISCOVERY_MODE mode); -void irlmp_discovery_request(int nslots); -discinfo_t *irlmp_get_discoveries(int *pn, __u16 mask, int nslots); -void irlmp_do_expiry(void); -void irlmp_do_discovery(int nslots); -discovery_t *irlmp_get_discovery_response(void); -void irlmp_discovery_expiry(discinfo_t *expiry, int number); - -int irlmp_data_request(struct lsap_cb *, struct sk_buff *); -void irlmp_data_indication(struct lsap_cb *, struct sk_buff *); - -int irlmp_udata_request(struct lsap_cb *, struct sk_buff *); -void irlmp_udata_indication(struct lsap_cb *, struct sk_buff *); - -#ifdef CONFIG_IRDA_ULTRA -int irlmp_connless_data_request(struct lsap_cb *, struct sk_buff *, __u8); -void irlmp_connless_data_indication(struct lsap_cb *, struct sk_buff *); -#endif /* CONFIG_IRDA_ULTRA */ - -void irlmp_status_indication(struct lap_cb *, LINK_STATUS link, LOCK_STATUS lock); -void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow); - -LM_REASON irlmp_convert_lap_reason(LAP_REASON); - -static inline __u32 irlmp_get_saddr(const struct lsap_cb *self) -{ - return (self && self->lap) ? self->lap->saddr : 0; -} - -static inline __u32 irlmp_get_daddr(const struct lsap_cb *self) -{ - return (self && self->lap) ? self->lap->daddr : 0; -} - -const char *irlmp_reason_str(LM_REASON reason); - -extern int sysctl_discovery_timeout; -extern int sysctl_discovery_slots; -extern int sysctl_discovery; -extern int sysctl_lap_keepalive_time; /* in ms, default is LM_IDLE_TIMEOUT */ -extern struct irlmp_cb *irlmp; - -/* Check if LAP queue is full. - * Used by IrTTP for low control, see comments in irlap.h - Jean II */ -static inline int irlmp_lap_tx_queue_full(struct lsap_cb *self) -{ - if (self == NULL) - return 0; - if (self->lap == NULL) - return 0; - if (self->lap->irlap == NULL) - return 0; - - return IRLAP_GET_TX_QUEUE_LEN(self->lap->irlap) >= LAP_HIGH_THRESHOLD; -} - -/* After doing a irlmp_dup(), this get one of the two socket back into - * a state where it's waiting incoming connections. - * Note : this can be used *only* if the socket is not yet connected - * (i.e. NO irlmp_connect_response() done on this socket). - * - Jean II */ -static inline void irlmp_listen(struct lsap_cb *self) -{ - self->dlsap_sel = LSAP_ANY; - self->lap = NULL; - self->lsap_state = LSAP_DISCONNECTED; - /* Started when we received the LM_CONNECT_INDICATION */ - del_timer(&self->watchdog_timer); -} - -#endif diff --git a/drivers/staging/irda/include/net/irda/irlmp_event.h b/drivers/staging/irda/include/net/irda/irlmp_event.h deleted file mode 100644 index a1a082fe384e..000000000000 --- a/drivers/staging/irda/include/net/irda/irlmp_event.h +++ /dev/null @@ -1,98 +0,0 @@ -/********************************************************************* - * - * Filename: irlmp_event.h - * Version: 0.1 - * Description: IrDA-LMP event handling - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Thu Jul 8 12:18:54 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRLMP_EVENT_H -#define IRLMP_EVENT_H - -/* A few forward declarations (to make compiler happy) */ -struct irlmp_cb; -struct lsap_cb; -struct lap_cb; -struct discovery_t; - -/* LAP states */ -typedef enum { - /* IrLAP connection control states */ - LAP_STANDBY, /* No LAP connection */ - LAP_U_CONNECT, /* Starting LAP connection */ - LAP_ACTIVE, /* LAP connection is active */ -} IRLMP_STATE; - -/* LSAP connection control states */ -typedef enum { - LSAP_DISCONNECTED, /* No LSAP connection */ - LSAP_CONNECT, /* Connect indication from peer */ - LSAP_CONNECT_PEND, /* Connect request from service user */ - LSAP_DATA_TRANSFER_READY, /* LSAP connection established */ - LSAP_SETUP, /* Trying to set up LSAP connection */ - LSAP_SETUP_PEND, /* Request to start LAP connection */ -} LSAP_STATE; - -typedef enum { - /* LSAP events */ - LM_CONNECT_REQUEST, - LM_CONNECT_CONFIRM, - LM_CONNECT_RESPONSE, - LM_CONNECT_INDICATION, - - LM_DISCONNECT_INDICATION, - LM_DISCONNECT_REQUEST, - - LM_DATA_REQUEST, - LM_UDATA_REQUEST, - LM_DATA_INDICATION, - LM_UDATA_INDICATION, - - LM_WATCHDOG_TIMEOUT, - - /* IrLAP events */ - LM_LAP_CONNECT_REQUEST, - LM_LAP_CONNECT_INDICATION, - LM_LAP_CONNECT_CONFIRM, - LM_LAP_DISCONNECT_INDICATION, - LM_LAP_DISCONNECT_REQUEST, - LM_LAP_DISCOVERY_REQUEST, - LM_LAP_DISCOVERY_CONFIRM, - LM_LAP_IDLE_TIMEOUT, -} IRLMP_EVENT; - -extern const char *const irlmp_state[]; -extern const char *const irlsap_state[]; - -void irlmp_watchdog_timer_expired(struct timer_list *t); -void irlmp_discovery_timer_expired(struct timer_list *t); -void irlmp_idle_timer_expired(struct timer_list *t); - -void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb); -int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb); - -#endif /* IRLMP_EVENT_H */ - - - - diff --git a/drivers/staging/irda/include/net/irda/irlmp_frame.h b/drivers/staging/irda/include/net/irda/irlmp_frame.h deleted file mode 100644 index 1906eb71422e..000000000000 --- a/drivers/staging/irda/include/net/irda/irlmp_frame.h +++ /dev/null @@ -1,62 +0,0 @@ -/********************************************************************* - * - * Filename: irlmp_frame.h - * Version: 0.9 - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Aug 19 02:09:59 1997 - * Modified at: Fri Dec 10 13:21:53 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRMLP_FRAME_H -#define IRMLP_FRAME_H - -#include - -#include - -/* IrLMP frame opcodes */ -#define CONNECT_CMD 0x01 -#define CONNECT_CNF 0x81 -#define DISCONNECT 0x02 -#define ACCESSMODE_CMD 0x03 -#define ACCESSMODE_CNF 0x83 - -#define CONTROL_BIT 0x80 - -void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, - int expedited, struct sk_buff *skb); -void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, - __u8 opcode, struct sk_buff *skb); -void irlmp_link_data_indication(struct lap_cb *, struct sk_buff *, - int unreliable); -#ifdef CONFIG_IRDA_ULTRA -void irlmp_link_unitdata_indication(struct lap_cb *, struct sk_buff *); -#endif /* CONFIG_IRDA_ULTRA */ - -void irlmp_link_connect_indication(struct lap_cb *, __u32 saddr, __u32 daddr, - struct qos_info *qos, struct sk_buff *skb); -void irlmp_link_connect_request(__u32 daddr); -void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos, - struct sk_buff *skb); -void irlmp_link_disconnect_indication(struct lap_cb *, struct irlap_cb *, - LAP_REASON reason, struct sk_buff *); -void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log); -void irlmp_link_discovery_indication(struct lap_cb *, discovery_t *discovery); - -#endif diff --git a/drivers/staging/irda/include/net/irda/irmod.h b/drivers/staging/irda/include/net/irda/irmod.h deleted file mode 100644 index 86f0dbb8ee5d..000000000000 --- a/drivers/staging/irda/include/net/irda/irmod.h +++ /dev/null @@ -1,109 +0,0 @@ -/********************************************************************* - * - * Filename: irmod.h - * Version: 0.3 - * Description: IrDA module and utilities functions - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Dec 15 13:58:52 1997 - * Modified at: Fri Jan 28 13:15:24 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charg. - * - ********************************************************************/ - -#ifndef IRMOD_H -#define IRMOD_H - -/* Misc status information */ -typedef enum { - STATUS_OK, - STATUS_ABORTED, - STATUS_NO_ACTIVITY, - STATUS_NOISY, - STATUS_REMOTE, -} LINK_STATUS; - -typedef enum { - LOCK_NO_CHANGE, - LOCK_LOCKED, - LOCK_UNLOCKED, -} LOCK_STATUS; - -typedef enum { FLOW_STOP, FLOW_START } LOCAL_FLOW; - -/* - * IrLMP disconnect reasons. The order is very important, since they - * correspond to disconnect reasons sent in IrLMP disconnect frames, so - * please do not touch :-) - */ -typedef enum { - LM_USER_REQUEST = 1, /* User request */ - LM_LAP_DISCONNECT, /* Unexpected IrLAP disconnect */ - LM_CONNECT_FAILURE, /* Failed to establish IrLAP connection */ - LM_LAP_RESET, /* IrLAP reset */ - LM_INIT_DISCONNECT, /* Link Management initiated disconnect */ - LM_LSAP_NOTCONN, /* Data delivered on unconnected LSAP */ - LM_NON_RESP_CLIENT, /* Non responsive LM-MUX client */ - LM_NO_AVAIL_CLIENT, /* No available LM-MUX client */ - LM_CONN_HALF_OPEN, /* Connection is half open */ - LM_BAD_SOURCE_ADDR, /* Illegal source address (i.e 0x00) */ -} LM_REASON; -#define LM_UNKNOWN 0xff /* Unspecified disconnect reason */ - -/* A few forward declarations (to make compiler happy) */ -struct qos_info; /* in */ - -/* - * Notify structure used between transport and link management layers - */ -typedef struct { - int (*data_indication)(void *priv, void *sap, struct sk_buff *skb); - int (*udata_indication)(void *priv, void *sap, struct sk_buff *skb); - void (*connect_confirm)(void *instance, void *sap, - struct qos_info *qos, __u32 max_sdu_size, - __u8 max_header_size, struct sk_buff *skb); - void (*connect_indication)(void *instance, void *sap, - struct qos_info *qos, __u32 max_sdu_size, - __u8 max_header_size, struct sk_buff *skb); - void (*disconnect_indication)(void *instance, void *sap, - LM_REASON reason, struct sk_buff *); - void (*flow_indication)(void *instance, void *sap, LOCAL_FLOW flow); - void (*status_indication)(void *instance, - LINK_STATUS link, LOCK_STATUS lock); - void *instance; /* Layer instance pointer */ - char name[16]; /* Name of layer */ -} notify_t; - -#define NOTIFY_MAX_NAME 16 - -/* Zero the notify structure */ -void irda_notify_init(notify_t *notify); - -/* Locking wrapper - Note the inverted logic on irda_lock(). - * Those function basically return false if the lock is already in the - * position you want to set it. - Jean II */ -#define irda_lock(lock) (! test_and_set_bit(0, (void *) (lock))) -#define irda_unlock(lock) (test_and_clear_bit(0, (void *) (lock))) - -#endif /* IRMOD_H */ - - - - - - - - - diff --git a/drivers/staging/irda/include/net/irda/irqueue.h b/drivers/staging/irda/include/net/irda/irqueue.h deleted file mode 100644 index 37f512bd6733..000000000000 --- a/drivers/staging/irda/include/net/irda/irqueue.h +++ /dev/null @@ -1,96 +0,0 @@ -/********************************************************************* - * - * Filename: irqueue.h - * Version: 0.3 - * Description: General queue implementation - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Jun 9 13:26:50 1998 - * Modified at: Thu Oct 7 13:25:16 1999 - * Modified by: Dag Brattli - * - * Copyright (C) 1998-1999, Aage Kvalnes - * Copyright (c) 1998, Dag Brattli - * All Rights Reserved. - * - * This code is taken from the Vortex Operating System written by Aage - * Kvalnes and has been ported to Linux and Linux/IR by Dag Brattli - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include - -#ifndef IRDA_QUEUE_H -#define IRDA_QUEUE_H - -#define NAME_SIZE 32 - -/* - * Hash types (some flags can be xored) - * See comments in irqueue.c for which one to use... - */ -#define HB_NOLOCK 0 /* No concurent access prevention */ -#define HB_LOCK 1 /* Prevent concurent write with global lock */ - -/* - * Hash defines - */ -#define HASHBIN_SIZE 8 -#define HASHBIN_MASK 0x7 - -#ifndef IRDA_ALIGN -#define IRDA_ALIGN __attribute__((aligned)) -#endif - -#define Q_NULL { NULL, NULL, "", 0 } - -typedef void (*FREE_FUNC)(void *arg); - -struct irda_queue { - struct irda_queue *q_next; - struct irda_queue *q_prev; - - char q_name[NAME_SIZE]; - long q_hash; /* Must be able to cast a (void *) */ -}; -typedef struct irda_queue irda_queue_t; - -typedef struct hashbin_t { - __u32 magic; - int hb_type; - int hb_size; - spinlock_t hb_spinlock; /* HB_LOCK - Can be used by the user */ - - irda_queue_t* hb_queue[HASHBIN_SIZE] IRDA_ALIGN; - - irda_queue_t* hb_current; -} hashbin_t; - -hashbin_t *hashbin_new(int type); -int hashbin_delete(hashbin_t* hashbin, FREE_FUNC func); -int hashbin_clear(hashbin_t* hashbin, FREE_FUNC free_func); -void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, - const char* name); -void* hashbin_remove(hashbin_t* hashbin, long hashv, const char* name); -void* hashbin_remove_first(hashbin_t *hashbin); -void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry); -void* hashbin_find(hashbin_t* hashbin, long hashv, const char* name); -void* hashbin_lock_find(hashbin_t* hashbin, long hashv, const char* name); -void* hashbin_find_next(hashbin_t* hashbin, long hashv, const char* name, - void ** pnext); -irda_queue_t *hashbin_get_first(hashbin_t *hashbin); -irda_queue_t *hashbin_get_next(hashbin_t *hashbin); - -#define HASHBIN_GET_SIZE(hashbin) hashbin->hb_size - -#endif diff --git a/drivers/staging/irda/include/net/irda/irttp.h b/drivers/staging/irda/include/net/irda/irttp.h deleted file mode 100644 index 98682d4bae8f..000000000000 --- a/drivers/staging/irda/include/net/irda/irttp.h +++ /dev/null @@ -1,210 +0,0 @@ -/********************************************************************* - * - * Filename: irttp.h - * Version: 1.0 - * Description: Tiny Transport Protocol (TTP) definitions - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:31 1997 - * Modified at: Sun Dec 12 13:09:07 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef IRTTP_H -#define IRTTP_H - -#include -#include -#include - -#include -#include /* struct lsap_cb */ -#include /* struct qos_info */ -#include - -#define TTP_MAX_CONNECTIONS LM_MAX_CONNECTIONS -#define TTP_HEADER 1 -#define TTP_MAX_HEADER (TTP_HEADER + LMP_MAX_HEADER) -#define TTP_SAR_HEADER 5 -#define TTP_PARAMETERS 0x80 -#define TTP_MORE 0x80 - -/* Transmission queue sizes */ -/* Worst case scenario, two window of data - Jean II */ -#define TTP_TX_MAX_QUEUE 14 -/* We need to keep at least 5 frames to make sure that we can refill - * appropriately the LAP layer. LAP keeps only two buffers, and we need - * to have 7 to make a full window - Jean II */ -#define TTP_TX_LOW_THRESHOLD 5 -/* Most clients are synchronous with respect to flow control, so we can - * keep a low number of Tx buffers in TTP - Jean II */ -#define TTP_TX_HIGH_THRESHOLD 7 - -/* Receive queue sizes */ -/* Minimum of credit that the peer should hold. - * If the peer has less credits than 9 frames, we will explicitly send - * him some credits (through irttp_give_credit() and a specific frame). - * Note that when we give credits it's likely that it won't be sent in - * this LAP window, but in the next one. So, we make sure that the peer - * has something to send while waiting for credits (one LAP window == 7 - * + 1 frames while he process the credits). - Jean II */ -#define TTP_RX_MIN_CREDIT 8 -/* This is the default maximum number of credits held by the peer, so the - * default maximum number of frames he can send us before needing flow - * control answer from us (this may be negociated differently at TSAP setup). - * We want to minimise the number of times we have to explicitly send some - * credit to the peer, hoping we can piggyback it on the return data. In - * particular, it doesn't make sense for us to send credit more than once - * per LAP window. - * Moreover, giving credits has some latency, so we need strictly more than - * a LAP window, otherwise we may already have credits in our Tx queue. - * But on the other hand, we don't want to keep too many Rx buffer here - * before starting to flow control the other end, so make it exactly one - * LAP window + 1 + MIN_CREDITS. - Jean II */ -#define TTP_RX_DEFAULT_CREDIT 16 -/* Maximum number of credits we can allow the peer to have, and therefore - * maximum Rx queue size. - * Note that we try to deliver packets to the higher layer every time we - * receive something, so in normal mode the Rx queue will never contains - * more than one or two packets. - Jean II */ -#define TTP_RX_MAX_CREDIT 21 - -/* What clients should use when calling ttp_open_tsap() */ -#define DEFAULT_INITIAL_CREDIT TTP_RX_DEFAULT_CREDIT - -/* Some priorities for disconnect requests */ -#define P_NORMAL 0 -#define P_HIGH 1 - -#define TTP_SAR_DISABLE 0 -#define TTP_SAR_UNBOUND 0xffffffff - -/* Parameters */ -#define TTP_MAX_SDU_SIZE 0x01 - -/* - * This structure contains all data associated with one instance of a TTP - * connection. - */ -struct tsap_cb { - irda_queue_t q; /* Must be first */ - magic_t magic; /* Just in case */ - - __u8 stsap_sel; /* Source TSAP */ - __u8 dtsap_sel; /* Destination TSAP */ - - struct lsap_cb *lsap; /* Corresponding LSAP to this TSAP */ - - __u8 connected; /* TSAP connected */ - - __u8 initial_credit; /* Initial credit to give peer */ - - int avail_credit; /* Available credit to return to peer */ - int remote_credit; /* Credit held by peer TTP entity */ - int send_credit; /* Credit held by local TTP entity */ - - struct sk_buff_head tx_queue; /* Frames to be transmitted */ - struct sk_buff_head rx_queue; /* Received frames */ - struct sk_buff_head rx_fragments; - int tx_queue_lock; - int rx_queue_lock; - spinlock_t lock; - - notify_t notify; /* Callbacks to client layer */ - - struct net_device_stats stats; - struct timer_list todo_timer; - - __u32 max_seg_size; /* Max data that fit into an IrLAP frame */ - __u8 max_header_size; - - int rx_sdu_busy; /* RxSdu.busy */ - __u32 rx_sdu_size; /* Current size of a partially received frame */ - __u32 rx_max_sdu_size; /* Max receive user data size */ - - int tx_sdu_busy; /* TxSdu.busy */ - __u32 tx_max_sdu_size; /* Max transmit user data size */ - - int close_pend; /* Close, but disconnect_pend */ - unsigned long disconnect_pend; /* Disconnect, but still data to send */ - struct sk_buff *disconnect_skb; -}; - -struct irttp_cb { - magic_t magic; - hashbin_t *tsaps; -}; - -int irttp_init(void); -void irttp_cleanup(void); - -struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify); -int irttp_close_tsap(struct tsap_cb *self); - -int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb); -int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb); - -int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel, - __u32 saddr, __u32 daddr, - struct qos_info *qos, __u32 max_sdu_size, - struct sk_buff *userdata); -int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size, - struct sk_buff *userdata); -int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *skb, - int priority); -void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow); -struct tsap_cb *irttp_dup(struct tsap_cb *self, void *instance); - -static inline __u32 irttp_get_saddr(struct tsap_cb *self) -{ - return irlmp_get_saddr(self->lsap); -} - -static inline __u32 irttp_get_daddr(struct tsap_cb *self) -{ - return irlmp_get_daddr(self->lsap); -} - -static inline __u32 irttp_get_max_seg_size(struct tsap_cb *self) -{ - return self->max_seg_size; -} - -/* After doing a irttp_dup(), this get one of the two socket back into - * a state where it's waiting incoming connections. - * Note : this can be used *only* if the socket is not yet connected - * (i.e. NO irttp_connect_response() done on this socket). - * - Jean II */ -static inline void irttp_listen(struct tsap_cb *self) -{ - irlmp_listen(self->lsap); - self->dtsap_sel = LSAP_ANY; -} - -/* Return TRUE if the node is in primary mode (i.e. master) - * - Jean II */ -static inline int irttp_is_primary(struct tsap_cb *self) -{ - if ((self == NULL) || - (self->lsap == NULL) || - (self->lsap->lap == NULL) || - (self->lsap->lap->irlap == NULL)) - return -2; - return irlap_is_primary(self->lsap->lap->irlap); -} - -#endif /* IRTTP_H */ diff --git a/drivers/staging/irda/include/net/irda/parameters.h b/drivers/staging/irda/include/net/irda/parameters.h deleted file mode 100644 index 2d9cd0007cba..000000000000 --- a/drivers/staging/irda/include/net/irda/parameters.h +++ /dev/null @@ -1,100 +0,0 @@ -/********************************************************************* - * - * Filename: parameters.h - * Version: 1.0 - * Description: A more general way to handle (pi,pl,pv) parameters - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Jun 7 08:47:28 1999 - * Modified at: Sun Jan 30 14:05:14 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - * Michel Dänzer , 10/2001 - * - simplify irda_pv_t to avoid endianness issues - * - ********************************************************************/ - -#ifndef IRDA_PARAMS_H -#define IRDA_PARAMS_H - -/* - * The currently supported types. Beware not to change the sequence since - * it a good reason why the sized integers has a value equal to their size - */ -typedef enum { - PV_INTEGER, /* Integer of any (pl) length */ - PV_INT_8_BITS, /* Integer of 8 bits in length */ - PV_INT_16_BITS, /* Integer of 16 bits in length */ - PV_STRING, /* \0 terminated string */ - PV_INT_32_BITS, /* Integer of 32 bits in length */ - PV_OCT_SEQ, /* Octet sequence */ - PV_NO_VALUE /* Does not contain any value (pl=0) */ -} PV_TYPE; - -/* Bit 7 of type field */ -#define PV_BIG_ENDIAN 0x80 -#define PV_LITTLE_ENDIAN 0x00 -#define PV_MASK 0x7f /* To mask away endian bit */ - -#define PV_PUT 0 -#define PV_GET 1 - -typedef union { - char *c; - __u32 i; - __u32 *ip; -} irda_pv_t; - -typedef struct { - __u8 pi; - __u8 pl; - irda_pv_t pv; -} irda_param_t; - -typedef int (*PI_HANDLER)(void *self, irda_param_t *param, int get); -typedef int (*PV_HANDLER)(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); - -typedef struct { - const PI_HANDLER func; /* Handler for this parameter identifier */ - PV_TYPE type; /* Data type for this parameter */ -} pi_minor_info_t; - -typedef struct { - const pi_minor_info_t *pi_minor_call_table; - int len; -} pi_major_info_t; - -typedef struct { - const pi_major_info_t *tables; - int len; - __u8 pi_mask; - int pi_major_offset; -} pi_param_info_t; - -int irda_param_pack(__u8 *buf, char *fmt, ...); - -int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len, - pi_param_info_t *info); -int irda_param_extract_all(void *self, __u8 *buf, int len, - pi_param_info_t *info); - -#define irda_param_insert_byte(buf,pi,pv) irda_param_pack(buf,"bbb",pi,1,pv) - -#endif /* IRDA_PARAMS_H */ - diff --git a/drivers/staging/irda/include/net/irda/qos.h b/drivers/staging/irda/include/net/irda/qos.h deleted file mode 100644 index a0315b50ac27..000000000000 --- a/drivers/staging/irda/include/net/irda/qos.h +++ /dev/null @@ -1,101 +0,0 @@ -/********************************************************************* - * - * Filename: qos.h - * Version: 1.0 - * Description: Quality of Service definitions - * Status: Experimental. - * Author: Dag Brattli - * Created at: Fri Sep 19 23:21:09 1997 - * Modified at: Thu Dec 2 13:51:54 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#ifndef IRDA_QOS_H -#define IRDA_QOS_H - -#include - -#include - -#define PI_BAUD_RATE 0x01 -#define PI_MAX_TURN_TIME 0x82 -#define PI_DATA_SIZE 0x83 -#define PI_WINDOW_SIZE 0x84 -#define PI_ADD_BOFS 0x85 -#define PI_MIN_TURN_TIME 0x86 -#define PI_LINK_DISC 0x08 - -#define IR_115200_MAX 0x3f - -/* Baud rates (first byte) */ -#define IR_2400 0x01 -#define IR_9600 0x02 -#define IR_19200 0x04 -#define IR_38400 0x08 -#define IR_57600 0x10 -#define IR_115200 0x20 -#define IR_576000 0x40 -#define IR_1152000 0x80 - -/* Baud rates (second byte) */ -#define IR_4000000 0x01 -#define IR_16000000 0x02 - -/* Quality of Service information */ -struct qos_value { - __u32 value; - __u16 bits; /* LSB is first byte, MSB is second byte */ -}; - -struct qos_info { - magic_t magic; - - struct qos_value baud_rate; /* IR_11520O | ... */ - struct qos_value max_turn_time; - struct qos_value data_size; - struct qos_value window_size; - struct qos_value additional_bofs; - struct qos_value min_turn_time; - struct qos_value link_disc_time; - - struct qos_value power; -}; - -extern int sysctl_max_baud_rate; -extern int sysctl_max_inactive_time; - -void irda_init_max_qos_capabilies(struct qos_info *qos); -void irda_qos_compute_intersection(struct qos_info *, struct qos_info *); - -__u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time); - -void irda_qos_bits_to_value(struct qos_info *qos); - -/* So simple, how could we not inline those two ? - * Note : one byte is 10 bits if you include start and stop bits - * Jean II */ -#define irlap_min_turn_time_in_bytes(speed, min_turn_time) ( \ - speed * min_turn_time / 10000000 \ -) -#define irlap_xbofs_in_usec(speed, xbofs) ( \ - xbofs * 10000000 / speed \ -) - -#endif - diff --git a/drivers/staging/irda/include/net/irda/timer.h b/drivers/staging/irda/include/net/irda/timer.h deleted file mode 100644 index 6dab15f5dae1..000000000000 --- a/drivers/staging/irda/include/net/irda/timer.h +++ /dev/null @@ -1,102 +0,0 @@ -/********************************************************************* - * - * Filename: timer.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Aug 16 00:59:29 1997 - * Modified at: Thu Oct 7 12:25:24 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1998-1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef TIMER_H -#define TIMER_H - -#include -#include - -#include /* for HZ */ - -#include - -/* A few forward declarations (to make compiler happy) */ -struct irlmp_cb; -struct irlap_cb; -struct lsap_cb; -struct lap_cb; - -/* - * Timeout definitions, some defined in IrLAP 6.13.5 - p. 92 - */ -#define POLL_TIMEOUT (450*HZ/1000) /* Must never exceed 500 ms */ -#define FINAL_TIMEOUT (500*HZ/1000) /* Must never exceed 500 ms */ - -/* - * Normally twice of p-timer. Note 3, IrLAP 6.3.11.2 - p. 60 suggests - * at least twice duration of the P-timer. - */ -#define WD_TIMEOUT (POLL_TIMEOUT*2) - -#define MEDIABUSY_TIMEOUT (500*HZ/1000) /* 500 msec */ -#define SMALLBUSY_TIMEOUT (100*HZ/1000) /* 100 msec - IrLAP 6.13.4 */ - -/* - * Slot timer must never exceed 85 ms, and must always be at least 25 ms, - * suggested to 75-85 msec by IrDA lite. This doesn't work with a lot of - * devices, and other stackes uses a lot more, so it's best we do it as well - * (Note : this is the default value and sysctl overrides it - Jean II) - */ -#define SLOT_TIMEOUT (90*HZ/1000) - -/* - * The latest discovery frame (XID) is longer due to the extra discovery - * information (hints, device name...). This is its extra length. - * We use that when setting the query timeout. Jean II - */ -#define XIDEXTRA_TIMEOUT (34*HZ/1000) /* 34 msec */ - -#define WATCHDOG_TIMEOUT (20*HZ) /* 20 sec */ - -static inline void irda_start_timer(struct timer_list *ptimer, int timeout, - void (*callback)(struct timer_list *)) -{ - ptimer->function = callback; - - /* Set new value for timer (update or add timer). - * We use mod_timer() because it's more efficient and also - * safer with respect to race conditions - Jean II */ - mod_timer(ptimer, jiffies + timeout); -} - - -void irlap_start_slot_timer(struct irlap_cb *self, int timeout); -void irlap_start_query_timer(struct irlap_cb *self, int S, int s); -void irlap_start_final_timer(struct irlap_cb *self, int timeout); -void irlap_start_wd_timer(struct irlap_cb *self, int timeout); -void irlap_start_backoff_timer(struct irlap_cb *self, int timeout); - -void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout); -void irlap_stop_mbusy_timer(struct irlap_cb *); - -void irlmp_start_watchdog_timer(struct lsap_cb *, int timeout); -void irlmp_start_discovery_timer(struct irlmp_cb *, int timeout); -void irlmp_start_idle_timer(struct lap_cb *, int timeout); -void irlmp_stop_idle_timer(struct lap_cb *self); - -#endif - diff --git a/drivers/staging/irda/include/net/irda/wrapper.h b/drivers/staging/irda/include/net/irda/wrapper.h deleted file mode 100644 index eef53ebe3d76..000000000000 --- a/drivers/staging/irda/include/net/irda/wrapper.h +++ /dev/null @@ -1,58 +0,0 @@ -/********************************************************************* - * - * Filename: wrapper.h - * Version: 1.2 - * Description: IrDA SIR async wrapper layer - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Tue Jan 11 12:37:29 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef WRAPPER_H -#define WRAPPER_H - -#include -#include -#include - -#include /* iobuff_t */ - -#define BOF 0xc0 /* Beginning of frame */ -#define XBOF 0xff -#define EOF 0xc1 /* End of frame */ -#define CE 0x7d /* Control escape */ - -#define STA BOF /* Start flag */ -#define STO EOF /* End flag */ - -#define IRDA_TRANS 0x20 /* Asynchronous transparency modifier */ - -/* States for receiving a frame in async mode */ -enum { - OUTSIDE_FRAME, - BEGIN_FRAME, - LINK_ESCAPE, - INSIDE_FRAME -}; - -/* Proto definitions */ -int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize); -void async_unwrap_char(struct net_device *dev, struct net_device_stats *stats, - iobuff_t *buf, __u8 byte); - -#endif diff --git a/drivers/staging/irda/net/Kconfig b/drivers/staging/irda/net/Kconfig deleted file mode 100644 index 6abeae6c666a..000000000000 --- a/drivers/staging/irda/net/Kconfig +++ /dev/null @@ -1,96 +0,0 @@ -# -# IrDA protocol configuration -# - -menuconfig IRDA - depends on NET && !S390 - tristate "IrDA (infrared) subsystem support" - select CRC_CCITT - ---help--- - Say Y here if you want to build support for the IrDA (TM) protocols. - The Infrared Data Associations (tm) specifies standards for wireless - infrared communication and is supported by most laptops and PDA's. - - To use Linux support for the IrDA (tm) protocols, you will also need - some user-space utilities like irattach. For more information, see - the file . You also want to - read the IR-HOWTO, available at - . - - If you want to exchange bits of data (vCal, vCard) with a PDA, you - will need to install some OBEX application, such as OpenObex : - - - To compile this support as a module, choose M here: the module will - be called irda. - -comment "IrDA protocols" - depends on IRDA - -source "drivers/staging/irda/net/irlan/Kconfig" - -source "drivers/staging/irda/net/irnet/Kconfig" - -source "drivers/staging/irda/net/ircomm/Kconfig" - -config IRDA_ULTRA - bool "Ultra (connectionless) protocol" - depends on IRDA - help - Say Y here to support the connectionless Ultra IRDA protocol. - Ultra allows to exchange data over IrDA with really simple devices - (watch, beacon) without the overhead of the IrDA protocol (no handshaking, - no management frames, simple fixed header). - Ultra is available as a special socket : socket(AF_IRDA, SOCK_DGRAM, 1); - -comment "IrDA options" - depends on IRDA - -config IRDA_CACHE_LAST_LSAP - bool "Cache last LSAP" - depends on IRDA - help - Say Y here if you want IrLMP to cache the last LSAP used. This - makes sense since most frames will be sent/received on the same - connection. Enabling this option will save a hash-lookup per frame. - - If unsure, say Y. - -config IRDA_FAST_RR - bool "Fast RRs (low latency)" - depends on IRDA - ---help--- - Say Y here is you want IrLAP to send fast RR (Receive Ready) frames - when acting as a primary station. - Disabling this option will make latency over IrDA very bad. Enabling - this option will make the IrDA stack send more packet than strictly - necessary, thus reduce your battery life (but not that much). - - Fast RR will make IrLAP send out a RR frame immediately when - receiving a frame if its own transmit queue is currently empty. This - will give a lot of speed improvement when receiving much data since - the secondary station will not have to wait the max. turn around - time (usually 500ms) before it is allowed to transmit the next time. - If the transmit queue of the secondary is also empty, the primary will - start backing-off before sending another RR frame, waiting longer - each time until the back-off reaches the max. turn around time. - This back-off increase in controlled via - /proc/sys/net/irda/fast_poll_increase - - If unsure, say Y. - -config IRDA_DEBUG - bool "Debug information" - depends on IRDA - help - Say Y here if you want the IrDA subsystem to write debug information - to your syslog. You can change the debug level in - /proc/sys/net/irda/debug . - When this option is enabled, the IrDA also perform many extra internal - verifications which will usually prevent the kernel to crash in case of - bugs. - - If unsure, say Y (since it makes it easier to find the bugs). - -source "drivers/staging/irda/drivers/Kconfig" - diff --git a/drivers/staging/irda/net/Makefile b/drivers/staging/irda/net/Makefile deleted file mode 100644 index bd1a635b88cf..000000000000 --- a/drivers/staging/irda/net/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# -# Makefile for the Linux IrDA protocol layer. -# - -subdir-ccflags-y += -I$(srctree)/drivers/staging/irda/include - -obj-$(CONFIG_IRDA) += irda.o -obj-$(CONFIG_IRLAN) += irlan/ -obj-$(CONFIG_IRNET) += irnet/ -obj-$(CONFIG_IRCOMM) += ircomm/ - -irda-y := iriap.o iriap_event.o irlmp.o irlmp_event.o irlmp_frame.o \ - irlap.o irlap_event.o irlap_frame.o timer.o qos.o irqueue.o \ - irttp.o irda_device.o irias_object.o wrapper.o af_irda.o \ - discovery.o parameters.o irnetlink.o irmod.o -irda-$(CONFIG_PROC_FS) += irproc.o -irda-$(CONFIG_SYSCTL) += irsysctl.o diff --git a/drivers/staging/irda/net/af_irda.c b/drivers/staging/irda/net/af_irda.c deleted file mode 100644 index 2f1e9ab3d6d0..000000000000 --- a/drivers/staging/irda/net/af_irda.c +++ /dev/null @@ -1,2694 +0,0 @@ -/********************************************************************* - * - * Filename: af_irda.c - * Version: 0.9 - * Description: IrDA sockets implementation - * Status: Stable - * Author: Dag Brattli - * Created at: Sun May 31 10:12:43 1998 - * Modified at: Sat Dec 25 21:10:23 1999 - * Modified by: Dag Brattli - * Sources: af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc. - * - * Copyright (c) 1999 Dag Brattli - * Copyright (c) 1999-2003 Jean Tourrilhes - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - * Linux-IrDA now supports four different types of IrDA sockets: - * - * o SOCK_STREAM: TinyTP connections with SAR disabled. The - * max SDU size is 0 for conn. of this type - * o SOCK_SEQPACKET: TinyTP connections with SAR enabled. TTP may - * fragment the messages, but will preserve - * the message boundaries - * o SOCK_DGRAM: IRDAPROTO_UNITDATA: TinyTP connections with Unitdata - * (unreliable) transfers - * IRDAPROTO_ULTRA: Connectionless and unreliable data - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* TIOCOUTQ, TIOCINQ */ -#include - -#include -#include - -#include - -static int irda_create(struct net *net, struct socket *sock, int protocol, int kern); - -static const struct proto_ops irda_stream_ops; -static const struct proto_ops irda_seqpacket_ops; -static const struct proto_ops irda_dgram_ops; - -#ifdef CONFIG_IRDA_ULTRA -static const struct proto_ops irda_ultra_ops; -#define ULTRA_MAX_DATA 382 -#endif /* CONFIG_IRDA_ULTRA */ - -#define IRDA_MAX_HEADER (TTP_MAX_HEADER) - -/* - * Function irda_data_indication (instance, sap, skb) - * - * Received some data from TinyTP. Just queue it on the receive queue - * - */ -static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb) -{ - struct irda_sock *self; - struct sock *sk; - int err; - - self = instance; - sk = instance; - - err = sock_queue_rcv_skb(sk, skb); - if (err) { - pr_debug("%s(), error: no more mem!\n", __func__); - self->rx_flow = FLOW_STOP; - - /* When we return error, TTP will need to requeue the skb */ - return err; - } - - return 0; -} - -/* - * Function irda_disconnect_indication (instance, sap, reason, skb) - * - * Connection has been closed. Check reason to find out why - * - */ -static void irda_disconnect_indication(void *instance, void *sap, - LM_REASON reason, struct sk_buff *skb) -{ - struct irda_sock *self; - struct sock *sk; - - self = instance; - - pr_debug("%s(%p)\n", __func__, self); - - /* Don't care about it, but let's not leak it */ - if(skb) - dev_kfree_skb(skb); - - sk = instance; - if (sk == NULL) { - pr_debug("%s(%p) : BUG : sk is NULL\n", - __func__, self); - return; - } - - /* Prevent race conditions with irda_release() and irda_shutdown() */ - bh_lock_sock(sk); - if (!sock_flag(sk, SOCK_DEAD) && sk->sk_state != TCP_CLOSE) { - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - - sk->sk_state_change(sk); - - /* Close our TSAP. - * If we leave it open, IrLMP put it back into the list of - * unconnected LSAPs. The problem is that any incoming request - * can then be matched to this socket (and it will be, because - * it is at the head of the list). This would prevent any - * listening socket waiting on the same TSAP to get those - * requests. Some apps forget to close sockets, or hang to it - * a bit too long, so we may stay in this dead state long - * enough to be noticed... - * Note : all socket function do check sk->sk_state, so we are - * safe... - * Jean II - */ - if (self->tsap) { - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } - } - bh_unlock_sock(sk); - - /* Note : once we are there, there is not much you want to do - * with the socket anymore, apart from closing it. - * For example, bind() and connect() won't reset sk->sk_err, - * sk->sk_shutdown and sk->sk_flags to valid values... - * Jean II - */ -} - -/* - * Function irda_connect_confirm (instance, sap, qos, max_sdu_size, skb) - * - * Connections has been confirmed by the remote device - * - */ -static void irda_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, __u8 max_header_size, - struct sk_buff *skb) -{ - struct irda_sock *self; - struct sock *sk; - - self = instance; - - pr_debug("%s(%p)\n", __func__, self); - - sk = instance; - if (sk == NULL) { - dev_kfree_skb(skb); - return; - } - - dev_kfree_skb(skb); - // Should be ??? skb_queue_tail(&sk->sk_receive_queue, skb); - - /* How much header space do we need to reserve */ - self->max_header_size = max_header_size; - - /* IrTTP max SDU size in transmit direction */ - self->max_sdu_size_tx = max_sdu_size; - - /* Find out what the largest chunk of data that we can transmit is */ - switch (sk->sk_type) { - case SOCK_STREAM: - if (max_sdu_size != 0) { - net_err_ratelimited("%s: max_sdu_size must be 0\n", - __func__); - return; - } - self->max_data_size = irttp_get_max_seg_size(self->tsap); - break; - case SOCK_SEQPACKET: - if (max_sdu_size == 0) { - net_err_ratelimited("%s: max_sdu_size cannot be 0\n", - __func__); - return; - } - self->max_data_size = max_sdu_size; - break; - default: - self->max_data_size = irttp_get_max_seg_size(self->tsap); - } - - pr_debug("%s(), max_data_size=%d\n", __func__, - self->max_data_size); - - memcpy(&self->qos_tx, qos, sizeof(struct qos_info)); - - /* We are now connected! */ - sk->sk_state = TCP_ESTABLISHED; - sk->sk_state_change(sk); -} - -/* - * Function irda_connect_indication(instance, sap, qos, max_sdu_size, userdata) - * - * Incoming connection - * - */ -static void irda_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_sdu_size, - __u8 max_header_size, struct sk_buff *skb) -{ - struct irda_sock *self; - struct sock *sk; - - self = instance; - - pr_debug("%s(%p)\n", __func__, self); - - sk = instance; - if (sk == NULL) { - dev_kfree_skb(skb); - return; - } - - /* How much header space do we need to reserve */ - self->max_header_size = max_header_size; - - /* IrTTP max SDU size in transmit direction */ - self->max_sdu_size_tx = max_sdu_size; - - /* Find out what the largest chunk of data that we can transmit is */ - switch (sk->sk_type) { - case SOCK_STREAM: - if (max_sdu_size != 0) { - net_err_ratelimited("%s: max_sdu_size must be 0\n", - __func__); - kfree_skb(skb); - return; - } - self->max_data_size = irttp_get_max_seg_size(self->tsap); - break; - case SOCK_SEQPACKET: - if (max_sdu_size == 0) { - net_err_ratelimited("%s: max_sdu_size cannot be 0\n", - __func__); - kfree_skb(skb); - return; - } - self->max_data_size = max_sdu_size; - break; - default: - self->max_data_size = irttp_get_max_seg_size(self->tsap); - } - - pr_debug("%s(), max_data_size=%d\n", __func__, - self->max_data_size); - - memcpy(&self->qos_tx, qos, sizeof(struct qos_info)); - - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_state_change(sk); -} - -/* - * Function irda_connect_response (handle) - * - * Accept incoming connection - * - */ -static void irda_connect_response(struct irda_sock *self) -{ - struct sk_buff *skb; - - skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, GFP_KERNEL); - if (skb == NULL) { - pr_debug("%s() Unable to allocate sk_buff!\n", - __func__); - return; - } - - /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(skb, IRDA_MAX_HEADER); - - irttp_connect_response(self->tsap, self->max_sdu_size_rx, skb); -} - -/* - * Function irda_flow_indication (instance, sap, flow) - * - * Used by TinyTP to tell us if it can accept more data or not - * - */ -static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) -{ - struct irda_sock *self; - struct sock *sk; - - self = instance; - sk = instance; - BUG_ON(sk == NULL); - - switch (flow) { - case FLOW_STOP: - pr_debug("%s(), IrTTP wants us to slow down\n", - __func__); - self->tx_flow = flow; - break; - case FLOW_START: - self->tx_flow = flow; - pr_debug("%s(), IrTTP wants us to start again\n", - __func__); - wake_up_interruptible(sk_sleep(sk)); - break; - default: - pr_debug("%s(), Unknown flow command!\n", __func__); - /* Unknown flow command, better stop */ - self->tx_flow = flow; - break; - } -} - -/* - * Function irda_getvalue_confirm (obj_id, value, priv) - * - * Got answer from remote LM-IAS, just pass object to requester... - * - * Note : duplicate from above, but we need our own version that - * doesn't touch the dtsap_sel and save the full value structure... - */ -static void irda_getvalue_confirm(int result, __u16 obj_id, - struct ias_value *value, void *priv) -{ - struct irda_sock *self; - - self = priv; - if (!self) { - net_warn_ratelimited("%s: lost myself!\n", __func__); - return; - } - - pr_debug("%s(%p)\n", __func__, self); - - /* We probably don't need to make any more queries */ - iriap_close(self->iriap); - self->iriap = NULL; - - /* Check if request succeeded */ - if (result != IAS_SUCCESS) { - pr_debug("%s(), IAS query failed! (%d)\n", __func__, - result); - - self->errno = result; /* We really need it later */ - - /* Wake up any processes waiting for result */ - wake_up_interruptible(&self->query_wait); - - return; - } - - /* Pass the object to the caller (so the caller must delete it) */ - self->ias_result = value; - self->errno = 0; - - /* Wake up any processes waiting for result */ - wake_up_interruptible(&self->query_wait); -} - -/* - * Function irda_selective_discovery_indication (discovery) - * - * Got a selective discovery indication from IrLMP. - * - * IrLMP is telling us that this node is new and matching our hint bit - * filter. Wake up any process waiting for answer... - */ -static void irda_selective_discovery_indication(discinfo_t *discovery, - DISCOVERY_MODE mode, - void *priv) -{ - struct irda_sock *self; - - self = priv; - if (!self) { - net_warn_ratelimited("%s: lost myself!\n", __func__); - return; - } - - /* Pass parameter to the caller */ - self->cachedaddr = discovery->daddr; - - /* Wake up process if its waiting for device to be discovered */ - wake_up_interruptible(&self->query_wait); -} - -/* - * Function irda_discovery_timeout (priv) - * - * Timeout in the selective discovery process - * - * We were waiting for a node to be discovered, but nothing has come up - * so far. Wake up the user and tell him that we failed... - */ -static void irda_discovery_timeout(struct timer_list *t) -{ - struct irda_sock *self; - - self = from_timer(self, t, watchdog); - BUG_ON(self == NULL); - - /* Nothing for the caller */ - self->cachelog = NULL; - self->cachedaddr = 0; - self->errno = -ETIME; - - /* Wake up process if its still waiting... */ - wake_up_interruptible(&self->query_wait); -} - -/* - * Function irda_open_tsap (self) - * - * Open local Transport Service Access Point (TSAP) - * - */ -static int irda_open_tsap(struct irda_sock *self, __u8 tsap_sel, char *name) -{ - notify_t notify; - - if (self->tsap) { - pr_debug("%s: busy!\n", __func__); - return -EBUSY; - } - - /* Initialize callbacks to be used by the IrDA stack */ - irda_notify_init(¬ify); - notify.connect_confirm = irda_connect_confirm; - notify.connect_indication = irda_connect_indication; - notify.disconnect_indication = irda_disconnect_indication; - notify.data_indication = irda_data_indication; - notify.udata_indication = irda_data_indication; - notify.flow_indication = irda_flow_indication; - notify.instance = self; - strncpy(notify.name, name, NOTIFY_MAX_NAME); - - self->tsap = irttp_open_tsap(tsap_sel, DEFAULT_INITIAL_CREDIT, - ¬ify); - if (self->tsap == NULL) { - pr_debug("%s(), Unable to allocate TSAP!\n", - __func__); - return -ENOMEM; - } - /* Remember which TSAP selector we actually got */ - self->stsap_sel = self->tsap->stsap_sel; - - return 0; -} - -/* - * Function irda_open_lsap (self) - * - * Open local Link Service Access Point (LSAP). Used for opening Ultra - * sockets - */ -#ifdef CONFIG_IRDA_ULTRA -static int irda_open_lsap(struct irda_sock *self, int pid) -{ - notify_t notify; - - if (self->lsap) { - net_warn_ratelimited("%s(), busy!\n", __func__); - return -EBUSY; - } - - /* Initialize callbacks to be used by the IrDA stack */ - irda_notify_init(¬ify); - notify.udata_indication = irda_data_indication; - notify.instance = self; - strncpy(notify.name, "Ultra", NOTIFY_MAX_NAME); - - self->lsap = irlmp_open_lsap(LSAP_CONNLESS, ¬ify, pid); - if (self->lsap == NULL) { - pr_debug("%s(), Unable to allocate LSAP!\n", __func__); - return -ENOMEM; - } - - return 0; -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irda_find_lsap_sel (self, name) - * - * Try to lookup LSAP selector in remote LM-IAS - * - * Basically, we start a IAP query, and then go to sleep. When the query - * return, irda_getvalue_confirm will wake us up, and we can examine the - * result of the query... - * Note that in some case, the query fail even before we go to sleep, - * creating some races... - */ -static int irda_find_lsap_sel(struct irda_sock *self, char *name) -{ - pr_debug("%s(%p, %s)\n", __func__, self, name); - - if (self->iriap) { - net_warn_ratelimited("%s(): busy with a previous query\n", - __func__); - return -EBUSY; - } - - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - irda_getvalue_confirm); - if(self->iriap == NULL) - return -ENOMEM; - - /* Treat unexpected wakeup as disconnect */ - self->errno = -EHOSTUNREACH; - - /* Query remote LM-IAS */ - iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr, - name, "IrDA:TinyTP:LsapSel"); - - /* Wait for answer, if not yet finished (or failed) */ - if (wait_event_interruptible(self->query_wait, (self->iriap==NULL))) - /* Treat signals as disconnect */ - return -EHOSTUNREACH; - - /* Check what happened */ - if (self->errno) - { - /* Requested object/attribute doesn't exist */ - if((self->errno == IAS_CLASS_UNKNOWN) || - (self->errno == IAS_ATTRIB_UNKNOWN)) - return -EADDRNOTAVAIL; - else - return -EHOSTUNREACH; - } - - /* Get the remote TSAP selector */ - switch (self->ias_result->type) { - case IAS_INTEGER: - pr_debug("%s() int=%d\n", - __func__, self->ias_result->t.integer); - - if (self->ias_result->t.integer != -1) - self->dtsap_sel = self->ias_result->t.integer; - else - self->dtsap_sel = 0; - break; - default: - self->dtsap_sel = 0; - pr_debug("%s(), bad type!\n", __func__); - break; - } - if (self->ias_result) - irias_delete_value(self->ias_result); - - if (self->dtsap_sel) - return 0; - - return -EADDRNOTAVAIL; -} - -/* - * Function irda_discover_daddr_and_lsap_sel (self, name) - * - * This try to find a device with the requested service. - * - * It basically look into the discovery log. For each address in the list, - * it queries the LM-IAS of the device to find if this device offer - * the requested service. - * If there is more than one node supporting the service, we complain - * to the user (it should move devices around). - * The, we set both the destination address and the lsap selector to point - * on the service on the unique device we have found. - * - * Note : this function fails if there is more than one device in range, - * because IrLMP doesn't disconnect the LAP when the last LSAP is closed. - * Moreover, we would need to wait the LAP disconnection... - */ -static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name) -{ - discinfo_t *discoveries; /* Copy of the discovery log */ - int number; /* Number of nodes in the log */ - int i; - int err = -ENETUNREACH; - __u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */ - __u8 dtsap_sel = 0x0; /* TSAP associated with it */ - - pr_debug("%s(), name=%s\n", __func__, name); - - /* Ask lmp for the current discovery log - * Note : we have to use irlmp_get_discoveries(), as opposed - * to play with the cachelog directly, because while we are - * making our ias query, le log might change... */ - discoveries = irlmp_get_discoveries(&number, self->mask.word, - self->nslots); - /* Check if the we got some results */ - if (discoveries == NULL) - return -ENETUNREACH; /* No nodes discovered */ - - /* - * Now, check all discovered devices (if any), and connect - * client only about the services that the client is - * interested in... - */ - for(i = 0; i < number; i++) { - /* Try the address in the log */ - self->daddr = discoveries[i].daddr; - self->saddr = 0x0; - pr_debug("%s(), trying daddr = %08x\n", - __func__, self->daddr); - - /* Query remote LM-IAS for this service */ - err = irda_find_lsap_sel(self, name); - switch (err) { - case 0: - /* We found the requested service */ - if(daddr != DEV_ADDR_ANY) { - pr_debug("%s(), discovered service ''%s'' in two different devices !!!\n", - __func__, name); - self->daddr = DEV_ADDR_ANY; - kfree(discoveries); - return -ENOTUNIQ; - } - /* First time we found that one, save it ! */ - daddr = self->daddr; - dtsap_sel = self->dtsap_sel; - break; - case -EADDRNOTAVAIL: - /* Requested service simply doesn't exist on this node */ - break; - default: - /* Something bad did happen :-( */ - pr_debug("%s(), unexpected IAS query failure\n", - __func__); - self->daddr = DEV_ADDR_ANY; - kfree(discoveries); - return -EHOSTUNREACH; - } - } - /* Cleanup our copy of the discovery log */ - kfree(discoveries); - - /* Check out what we found */ - if(daddr == DEV_ADDR_ANY) { - pr_debug("%s(), cannot discover service ''%s'' in any device !!!\n", - __func__, name); - self->daddr = DEV_ADDR_ANY; - return -EADDRNOTAVAIL; - } - - /* Revert back to discovered device & service */ - self->daddr = daddr; - self->saddr = 0x0; - self->dtsap_sel = dtsap_sel; - - pr_debug("%s(), discovered requested service ''%s'' at address %08x\n", - __func__, name, self->daddr); - - return 0; -} - -/* - * Function irda_getname (sock, uaddr, uaddr_len, peer) - * - * Return the our own, or peers socket address (sockaddr_irda) - * - */ -static int irda_getname(struct socket *sock, struct sockaddr *uaddr, - int *uaddr_len, int peer) -{ - struct sockaddr_irda saddr; - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - - memset(&saddr, 0, sizeof(saddr)); - if (peer) { - if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; - - saddr.sir_family = AF_IRDA; - saddr.sir_lsap_sel = self->dtsap_sel; - saddr.sir_addr = self->daddr; - } else { - saddr.sir_family = AF_IRDA; - saddr.sir_lsap_sel = self->stsap_sel; - saddr.sir_addr = self->saddr; - } - - pr_debug("%s(), tsap_sel = %#x\n", __func__, saddr.sir_lsap_sel); - pr_debug("%s(), addr = %08x\n", __func__, saddr.sir_addr); - - /* uaddr_len come to us uninitialised */ - *uaddr_len = sizeof (struct sockaddr_irda); - memcpy(uaddr, &saddr, *uaddr_len); - - return 0; -} - -/* - * Function irda_listen (sock, backlog) - * - * Just move to the listen state - * - */ -static int irda_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - int err = -EOPNOTSUPP; - - lock_sock(sk); - - if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) && - (sk->sk_type != SOCK_DGRAM)) - goto out; - - if (sk->sk_state != TCP_LISTEN) { - sk->sk_max_ack_backlog = backlog; - sk->sk_state = TCP_LISTEN; - - err = 0; - } -out: - release_sock(sk); - - return err; -} - -/* - * Function irda_bind (sock, uaddr, addr_len) - * - * Used by servers to register their well known TSAP - * - */ -static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) -{ - struct sock *sk = sock->sk; - struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr; - struct irda_sock *self = irda_sk(sk); - int err; - - pr_debug("%s(%p)\n", __func__, self); - - if (addr_len != sizeof(struct sockaddr_irda)) - return -EINVAL; - - lock_sock(sk); -#ifdef CONFIG_IRDA_ULTRA - /* Special care for Ultra sockets */ - if ((sk->sk_type == SOCK_DGRAM) && - (sk->sk_protocol == IRDAPROTO_ULTRA)) { - self->pid = addr->sir_lsap_sel; - err = -EOPNOTSUPP; - if (self->pid & 0x80) { - pr_debug("%s(), extension in PID not supp!\n", - __func__); - goto out; - } - err = irda_open_lsap(self, self->pid); - if (err < 0) - goto out; - - /* Pretend we are connected */ - sock->state = SS_CONNECTED; - sk->sk_state = TCP_ESTABLISHED; - err = 0; - - goto out; - } -#endif /* CONFIG_IRDA_ULTRA */ - - self->ias_obj = irias_new_object(addr->sir_name, jiffies); - err = -ENOMEM; - if (self->ias_obj == NULL) - goto out; - - err = irda_open_tsap(self, addr->sir_lsap_sel, addr->sir_name); - if (err < 0) { - irias_delete_object(self->ias_obj); - self->ias_obj = NULL; - goto out; - } - - /* Register with LM-IAS */ - irias_add_integer_attrib(self->ias_obj, "IrDA:TinyTP:LsapSel", - self->stsap_sel, IAS_KERNEL_ATTR); - irias_insert_object(self->ias_obj); - - err = 0; -out: - release_sock(sk); - return err; -} - -/* - * Function irda_accept (sock, newsock, flags) - * - * Wait for incoming connection - * - */ -static int irda_accept(struct socket *sock, struct socket *newsock, int flags, - bool kern) -{ - struct sock *sk = sock->sk; - struct irda_sock *new, *self = irda_sk(sk); - struct sock *newsk; - struct sk_buff *skb = NULL; - int err; - - err = irda_create(sock_net(sk), newsock, sk->sk_protocol, kern); - if (err) - return err; - - err = -EINVAL; - - lock_sock(sk); - if (sock->state != SS_UNCONNECTED) - goto out; - - err = -EOPNOTSUPP; - if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) && - (sk->sk_type != SOCK_DGRAM)) - goto out; - - err = -EINVAL; - if (sk->sk_state != TCP_LISTEN) - goto out; - - /* - * The read queue this time is holding sockets ready to use - * hooked into the SABM we saved - */ - - /* - * We can perform the accept only if there is incoming data - * on the listening socket. - * So, we will block the caller until we receive any data. - * If the caller was waiting on select() or poll() before - * calling us, the data is waiting for us ;-) - * Jean II - */ - while (1) { - skb = skb_dequeue(&sk->sk_receive_queue); - if (skb) - break; - - /* Non blocking operation */ - err = -EWOULDBLOCK; - if (flags & O_NONBLOCK) - goto out; - - err = wait_event_interruptible(*(sk_sleep(sk)), - skb_peek(&sk->sk_receive_queue)); - if (err) - goto out; - } - - newsk = newsock->sk; - err = -EIO; - if (newsk == NULL) - goto out; - - newsk->sk_state = TCP_ESTABLISHED; - - new = irda_sk(newsk); - - /* Now attach up the new socket */ - new->tsap = irttp_dup(self->tsap, new); - err = -EPERM; /* value does not seem to make sense. -arnd */ - if (!new->tsap) { - pr_debug("%s(), dup failed!\n", __func__); - goto out; - } - - new->stsap_sel = new->tsap->stsap_sel; - new->dtsap_sel = new->tsap->dtsap_sel; - new->saddr = irttp_get_saddr(new->tsap); - new->daddr = irttp_get_daddr(new->tsap); - - new->max_sdu_size_tx = self->max_sdu_size_tx; - new->max_sdu_size_rx = self->max_sdu_size_rx; - new->max_data_size = self->max_data_size; - new->max_header_size = self->max_header_size; - - memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info)); - - /* Clean up the original one to keep it in listen state */ - irttp_listen(self->tsap); - - sk->sk_ack_backlog--; - - newsock->state = SS_CONNECTED; - - irda_connect_response(new); - err = 0; -out: - kfree_skb(skb); - release_sock(sk); - return err; -} - -/* - * Function irda_connect (sock, uaddr, addr_len, flags) - * - * Connect to a IrDA device - * - * The main difference with a "standard" connect is that with IrDA we need - * to resolve the service name into a TSAP selector (in TCP, port number - * doesn't have to be resolved). - * Because of this service name resolution, we can offer "auto-connect", - * where we connect to a service without specifying a destination address. - * - * 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 EBUSY : already processing a connect - * o EHOSTUNREACH : bad addr->sir_addr argument - * o EADDRNOTAVAIL : bad addr->sir_name argument - * o ENOTUNIQ : more than one node has addr->sir_name (auto-connect) - * o ENETUNREACH : no node found on the network (auto-connect) - */ -static int irda_connect(struct socket *sock, struct sockaddr *uaddr, - int addr_len, int flags) -{ - struct sock *sk = sock->sk; - struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr; - struct irda_sock *self = irda_sk(sk); - int err; - - pr_debug("%s(%p)\n", __func__, self); - - lock_sock(sk); - /* Don't allow connect for Ultra sockets */ - err = -ESOCKTNOSUPPORT; - if ((sk->sk_type == SOCK_DGRAM) && (sk->sk_protocol == IRDAPROTO_ULTRA)) - goto out; - - if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { - sock->state = SS_CONNECTED; - err = 0; - goto out; /* Connect completed during a ERESTARTSYS event */ - } - - if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) { - sock->state = SS_UNCONNECTED; - err = -ECONNREFUSED; - goto out; - } - - err = -EISCONN; /* No reconnect on a seqpacket socket */ - if (sk->sk_state == TCP_ESTABLISHED) - goto out; - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - err = -EINVAL; - if (addr_len != sizeof(struct sockaddr_irda)) - goto out; - - /* Check if user supplied any destination device address */ - if ((!addr->sir_addr) || (addr->sir_addr == DEV_ADDR_ANY)) { - /* Try to find one suitable */ - err = irda_discover_daddr_and_lsap_sel(self, addr->sir_name); - if (err) { - pr_debug("%s(), auto-connect failed!\n", __func__); - goto out; - } - } else { - /* Use the one provided by the user */ - self->daddr = addr->sir_addr; - pr_debug("%s(), daddr = %08x\n", __func__, self->daddr); - - /* If we don't have a valid service name, we assume the - * user want to connect on a specific LSAP. Prevent - * the use of invalid LSAPs (IrLMP 1.1 p10). Jean II */ - if((addr->sir_name[0] != '\0') || - (addr->sir_lsap_sel >= 0x70)) { - /* Query remote LM-IAS using service name */ - err = irda_find_lsap_sel(self, addr->sir_name); - if (err) { - pr_debug("%s(), connect failed!\n", __func__); - goto out; - } - } else { - /* Directly connect to the remote LSAP - * specified by the sir_lsap field. - * Please use with caution, in IrDA LSAPs are - * dynamic and there is no "well-known" LSAP. */ - self->dtsap_sel = addr->sir_lsap_sel; - } - } - - /* Check if we have opened a local TSAP */ - if (!self->tsap) { - err = irda_open_tsap(self, LSAP_ANY, addr->sir_name); - if (err) - goto out; - } - - /* Move to connecting socket, start sending Connect Requests */ - sock->state = SS_CONNECTING; - sk->sk_state = TCP_SYN_SENT; - - /* Connect to remote device */ - err = irttp_connect_request(self->tsap, self->dtsap_sel, - self->saddr, self->daddr, NULL, - self->max_sdu_size_rx, NULL); - if (err) { - pr_debug("%s(), connect failed!\n", __func__); - goto out; - } - - /* Now the loop */ - err = -EINPROGRESS; - if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) - goto out; - - err = -ERESTARTSYS; - if (wait_event_interruptible(*(sk_sleep(sk)), - (sk->sk_state != TCP_SYN_SENT))) - goto out; - - if (sk->sk_state != TCP_ESTABLISHED) { - sock->state = SS_UNCONNECTED; - err = sock_error(sk); - if (!err) - err = -ECONNRESET; - goto out; - } - - sock->state = SS_CONNECTED; - - /* At this point, IrLMP has assigned our source address */ - self->saddr = irttp_get_saddr(self->tsap); - err = 0; -out: - release_sock(sk); - return err; -} - -static struct proto irda_proto = { - .name = "IRDA", - .owner = THIS_MODULE, - .obj_size = sizeof(struct irda_sock), -}; - -/* - * Function irda_create (sock, protocol) - * - * Create IrDA socket - * - */ -static int irda_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - struct irda_sock *self; - - if (protocol < 0 || protocol > SK_PROTOCOL_MAX) - return -EINVAL; - - if (net != &init_net) - return -EAFNOSUPPORT; - - /* Check for valid socket type */ - switch (sock->type) { - case SOCK_STREAM: /* For TTP connections with SAR disabled */ - case SOCK_SEQPACKET: /* For TTP connections with SAR enabled */ - case SOCK_DGRAM: /* For TTP Unitdata or LMP Ultra transfers */ - break; - default: - return -ESOCKTNOSUPPORT; - } - - /* Allocate networking socket */ - sk = sk_alloc(net, PF_IRDA, GFP_KERNEL, &irda_proto, kern); - if (sk == NULL) - return -ENOMEM; - - self = irda_sk(sk); - pr_debug("%s() : self is %p\n", __func__, self); - - init_waitqueue_head(&self->query_wait); - - switch (sock->type) { - case SOCK_STREAM: - sock->ops = &irda_stream_ops; - self->max_sdu_size_rx = TTP_SAR_DISABLE; - break; - case SOCK_SEQPACKET: - sock->ops = &irda_seqpacket_ops; - self->max_sdu_size_rx = TTP_SAR_UNBOUND; - break; - case SOCK_DGRAM: - switch (protocol) { -#ifdef CONFIG_IRDA_ULTRA - case IRDAPROTO_ULTRA: - sock->ops = &irda_ultra_ops; - /* Initialise now, because we may send on unbound - * sockets. Jean II */ - self->max_data_size = ULTRA_MAX_DATA - LMP_PID_HEADER; - self->max_header_size = IRDA_MAX_HEADER + LMP_PID_HEADER; - break; -#endif /* CONFIG_IRDA_ULTRA */ - case IRDAPROTO_UNITDATA: - sock->ops = &irda_dgram_ops; - /* We let Unitdata conn. be like seqpack conn. */ - self->max_sdu_size_rx = TTP_SAR_UNBOUND; - break; - default: - sk_free(sk); - return -ESOCKTNOSUPPORT; - } - break; - default: - sk_free(sk); - return -ESOCKTNOSUPPORT; - } - - /* Initialise networking socket struct */ - sock_init_data(sock, sk); /* Note : set sk->sk_refcnt to 1 */ - sk->sk_family = PF_IRDA; - sk->sk_protocol = protocol; - - /* Register as a client with IrLMP */ - self->ckey = irlmp_register_client(0, NULL, NULL, NULL); - self->mask.word = 0xffff; - self->rx_flow = self->tx_flow = FLOW_START; - self->nslots = DISCOVERY_DEFAULT_SLOTS; - self->daddr = DEV_ADDR_ANY; /* Until we get connected */ - self->saddr = 0x0; /* so IrLMP assign us any link */ - return 0; -} - -/* - * Function irda_destroy_socket (self) - * - * Destroy socket - * - */ -static void irda_destroy_socket(struct irda_sock *self) -{ - pr_debug("%s(%p)\n", __func__, self); - - /* Unregister with IrLMP */ - irlmp_unregister_client(self->ckey); - irlmp_unregister_service(self->skey); - - /* Unregister with LM-IAS */ - if (self->ias_obj) { - irias_delete_object(self->ias_obj); - self->ias_obj = NULL; - } - - if (self->iriap) { - iriap_close(self->iriap); - self->iriap = NULL; - } - - if (self->tsap) { - irttp_disconnect_request(self->tsap, NULL, P_NORMAL); - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } -#ifdef CONFIG_IRDA_ULTRA - if (self->lsap) { - irlmp_close_lsap(self->lsap); - self->lsap = NULL; - } -#endif /* CONFIG_IRDA_ULTRA */ -} - -/* - * Function irda_release (sock) - */ -static int irda_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - if (sk == NULL) - return 0; - - lock_sock(sk); - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - - /* Destroy IrDA socket */ - irda_destroy_socket(irda_sk(sk)); - - sock_orphan(sk); - sock->sk = NULL; - release_sock(sk); - - /* Purge queues (see sock_init_data()) */ - skb_queue_purge(&sk->sk_receive_queue); - - /* Destroy networking socket if we are the last reference on it, - * i.e. if(sk->sk_refcnt == 0) -> sk_free(sk) */ - sock_put(sk); - - /* Notes on socket locking and deallocation... - Jean II - * In theory we should put pairs of sock_hold() / sock_put() to - * prevent the socket to be destroyed whenever there is an - * outstanding request or outstanding incoming packet or event. - * - * 1) This may include IAS request, both in connect and getsockopt. - * Unfortunately, the situation is a bit more messy than it looks, - * because we close iriap and kfree(self) above. - * - * 2) This may include selective discovery in getsockopt. - * Same stuff as above, irlmp registration and self are gone. - * - * Probably 1 and 2 may not matter, because it's all triggered - * by a process and the socket layer already prevent the - * socket to go away while a process is holding it, through - * sockfd_put() and fput()... - * - * 3) This may include deferred TSAP closure. In particular, - * we may receive a late irda_disconnect_indication() - * Fortunately, (tsap_cb *)->close_pend should protect us - * from that. - * - * I did some testing on SMP, and it looks solid. And the socket - * memory leak is now gone... - Jean II - */ - - return 0; -} - -/* - * Function irda_sendmsg (sock, msg, len) - * - * Send message down to TinyTP. This function is used for both STREAM and - * SEQPACK services. This is possible since it forces the client to - * fragment the message if necessary - */ -static int irda_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct irda_sock *self; - struct sk_buff *skb; - int err = -EPIPE; - - pr_debug("%s(), len=%zd\n", __func__, len); - - /* Note : socket.c set MSG_EOR on SEQPACKET sockets */ - if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR | MSG_CMSG_COMPAT | - MSG_NOSIGNAL)) { - return -EINVAL; - } - - lock_sock(sk); - - if (sk->sk_shutdown & SEND_SHUTDOWN) - goto out_err; - - if (sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - - self = irda_sk(sk); - - /* Check if IrTTP is wants us to slow down */ - - if (wait_event_interruptible(*(sk_sleep(sk)), - (self->tx_flow != FLOW_STOP || sk->sk_state != TCP_ESTABLISHED))) { - err = -ERESTARTSYS; - goto out; - } - - /* Check if we are still connected */ - if (sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - - /* Check that we don't send out too big frames */ - if (len > self->max_data_size) { - pr_debug("%s(), Chopping frame from %zd to %d bytes!\n", - __func__, len, self->max_data_size); - len = self->max_data_size; - } - - skb = sock_alloc_send_skb(sk, len + self->max_header_size + 16, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - goto out_err; - - skb_reserve(skb, self->max_header_size + 16); - skb_reset_transport_header(skb); - skb_put(skb, len); - err = memcpy_from_msg(skb_transport_header(skb), msg, len); - if (err) { - kfree_skb(skb); - goto out_err; - } - - /* - * Just send the message to TinyTP, and let it deal with possible - * errors. No need to duplicate all that here - */ - err = irttp_data_request(self->tsap, skb); - if (err) { - pr_debug("%s(), err=%d\n", __func__, err); - goto out_err; - } - - release_sock(sk); - /* Tell client how much data we actually sent */ - return len; - -out_err: - err = sk_stream_error(sk, msg->msg_flags, err); -out: - release_sock(sk); - return err; - -} - -/* - * Function irda_recvmsg_dgram (sock, msg, size, flags) - * - * Try to receive message and copy it to user. The frame is discarded - * after being read, regardless of how much the user actually read - */ -static int irda_recvmsg_dgram(struct socket *sock, struct msghdr *msg, - size_t size, int flags) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - struct sk_buff *skb; - size_t copied; - int err; - - skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, - flags & MSG_DONTWAIT, &err); - if (!skb) - return err; - - skb_reset_transport_header(skb); - copied = skb->len; - - if (copied > size) { - pr_debug("%s(), Received truncated frame (%zd < %zd)!\n", - __func__, copied, size); - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - skb_copy_datagram_msg(skb, 0, msg, copied); - - skb_free_datagram(sk, skb); - - /* - * Check if we have previously stopped IrTTP and we know - * have more free space in our rx_queue. If so tell IrTTP - * to start delivering frames again before our rx_queue gets - * empty - */ - if (self->rx_flow == FLOW_STOP) { - if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) { - pr_debug("%s(), Starting IrTTP\n", __func__); - self->rx_flow = FLOW_START; - irttp_flow_request(self->tsap, FLOW_START); - } - } - - return copied; -} - -/* - * Function irda_recvmsg_stream (sock, msg, size, flags) - */ -static int irda_recvmsg_stream(struct socket *sock, struct msghdr *msg, - size_t size, int flags) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - int noblock = flags & MSG_DONTWAIT; - size_t copied = 0; - int target, err; - long timeo; - - if ((err = sock_error(sk)) < 0) - return err; - - if (sock->flags & __SO_ACCEPTCON) - return -EINVAL; - - err =-EOPNOTSUPP; - if (flags & MSG_OOB) - return -EOPNOTSUPP; - - err = 0; - target = sock_rcvlowat(sk, flags & MSG_WAITALL, size); - timeo = sock_rcvtimeo(sk, noblock); - - do { - int chunk; - struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue); - - if (skb == NULL) { - DEFINE_WAIT(wait); - err = 0; - - if (copied >= target) - break; - - prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - - /* - * POSIX 1003.1g mandates this order. - */ - err = sock_error(sk); - if (err) - ; - else if (sk->sk_shutdown & RCV_SHUTDOWN) - ; - else if (noblock) - err = -EAGAIN; - else if (signal_pending(current)) - err = sock_intr_errno(timeo); - else if (sk->sk_state != TCP_ESTABLISHED) - err = -ENOTCONN; - else if (skb_peek(&sk->sk_receive_queue) == NULL) - /* Wait process until data arrives */ - schedule(); - - finish_wait(sk_sleep(sk), &wait); - - if (err) - return err; - if (sk->sk_shutdown & RCV_SHUTDOWN) - break; - - continue; - } - - 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) { - pr_debug("%s(), back on q!\n", - __func__); - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - - kfree_skb(skb); - } else { - pr_debug("%s() questionable!?\n", __func__); - - /* put message back and return */ - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - } while (size); - - /* - * Check if we have previously stopped IrTTP and we know - * have more free space in our rx_queue. If so tell IrTTP - * to start delivering frames again before our rx_queue gets - * empty - */ - if (self->rx_flow == FLOW_STOP) { - if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) { - pr_debug("%s(), Starting IrTTP\n", __func__); - self->rx_flow = FLOW_START; - irttp_flow_request(self->tsap, FLOW_START); - } - } - - return copied; -} - -/* - * Function irda_sendmsg_dgram (sock, msg, len) - * - * Send message down to TinyTP for the unreliable sequenced - * packet service... - * - */ -static int irda_sendmsg_dgram(struct socket *sock, struct msghdr *msg, - size_t len) -{ - struct sock *sk = sock->sk; - struct irda_sock *self; - struct sk_buff *skb; - int err; - - pr_debug("%s(), len=%zd\n", __func__, len); - - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_shutdown & SEND_SHUTDOWN) { - send_sig(SIGPIPE, current, 0); - err = -EPIPE; - goto out; - } - - err = -ENOTCONN; - if (sk->sk_state != TCP_ESTABLISHED) - goto out; - - self = irda_sk(sk); - - /* - * Check that we don't send out too big frames. This is an unreliable - * service, so we have no fragmentation and no coalescence - */ - if (len > self->max_data_size) { - pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n", - __func__, len, self->max_data_size); - len = self->max_data_size; - } - - skb = sock_alloc_send_skb(sk, len + self->max_header_size, - msg->msg_flags & MSG_DONTWAIT, &err); - err = -ENOBUFS; - if (!skb) - goto out; - - skb_reserve(skb, self->max_header_size); - skb_reset_transport_header(skb); - - pr_debug("%s(), appending user data\n", __func__); - skb_put(skb, len); - err = memcpy_from_msg(skb_transport_header(skb), msg, len); - if (err) { - kfree_skb(skb); - goto out; - } - - /* - * Just send the message to TinyTP, and let it deal with possible - * errors. No need to duplicate all that here - */ - err = irttp_udata_request(self->tsap, skb); - if (err) { - pr_debug("%s(), err=%d\n", __func__, err); - goto out; - } - - release_sock(sk); - return len; - -out: - release_sock(sk); - return err; -} - -/* - * Function irda_sendmsg_ultra (sock, msg, len) - * - * Send message down to IrLMP for the unreliable Ultra - * packet service... - */ -#ifdef CONFIG_IRDA_ULTRA -static int irda_sendmsg_ultra(struct socket *sock, struct msghdr *msg, - size_t len) -{ - struct sock *sk = sock->sk; - struct irda_sock *self; - __u8 pid = 0; - int bound = 0; - struct sk_buff *skb; - int err; - - pr_debug("%s(), len=%zd\n", __func__, len); - - err = -EINVAL; - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) - return -EINVAL; - - lock_sock(sk); - - err = -EPIPE; - if (sk->sk_shutdown & SEND_SHUTDOWN) { - send_sig(SIGPIPE, current, 0); - goto out; - } - - self = irda_sk(sk); - - /* Check if an address was specified with sendto. Jean II */ - if (msg->msg_name) { - DECLARE_SOCKADDR(struct sockaddr_irda *, addr, msg->msg_name); - err = -EINVAL; - /* Check address, extract pid. Jean II */ - if (msg->msg_namelen < sizeof(*addr)) - goto out; - if (addr->sir_family != AF_IRDA) - goto out; - - pid = addr->sir_lsap_sel; - if (pid & 0x80) { - pr_debug("%s(), extension in PID not supp!\n", - __func__); - err = -EOPNOTSUPP; - goto out; - } - } else { - /* Check that the socket is properly bound to an Ultra - * port. Jean II */ - if ((self->lsap == NULL) || - (sk->sk_state != TCP_ESTABLISHED)) { - pr_debug("%s(), socket not bound to Ultra PID.\n", - __func__); - err = -ENOTCONN; - goto out; - } - /* Use PID from socket */ - bound = 1; - } - - /* - * Check that we don't send out too big frames. This is an unreliable - * service, so we have no fragmentation and no coalescence - */ - if (len > self->max_data_size) { - pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n", - __func__, len, self->max_data_size); - len = self->max_data_size; - } - - skb = sock_alloc_send_skb(sk, len + self->max_header_size, - msg->msg_flags & MSG_DONTWAIT, &err); - err = -ENOBUFS; - if (!skb) - goto out; - - skb_reserve(skb, self->max_header_size); - skb_reset_transport_header(skb); - - pr_debug("%s(), appending user data\n", __func__); - skb_put(skb, len); - err = memcpy_from_msg(skb_transport_header(skb), msg, len); - if (err) { - kfree_skb(skb); - goto out; - } - - err = irlmp_connless_data_request((bound ? self->lsap : NULL), - skb, pid); - if (err) - pr_debug("%s(), err=%d\n", __func__, err); -out: - release_sock(sk); - return err ? : len; -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irda_shutdown (sk, how) - */ -static int irda_shutdown(struct socket *sock, int how) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - - pr_debug("%s(%p)\n", __func__, self); - - lock_sock(sk); - - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - - if (self->iriap) { - iriap_close(self->iriap); - self->iriap = NULL; - } - - if (self->tsap) { - irttp_disconnect_request(self->tsap, NULL, P_NORMAL); - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } - - /* A few cleanup so the socket look as good as new... */ - self->rx_flow = self->tx_flow = FLOW_START; /* needed ??? */ - self->daddr = DEV_ADDR_ANY; /* Until we get re-connected */ - self->saddr = 0x0; /* so IrLMP assign us any link */ - - release_sock(sk); - - return 0; -} - -/* - * Function irda_poll (file, sock, wait) - */ -static __poll_t irda_poll(struct file * file, struct socket *sock, - poll_table *wait) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - __poll_t mask; - - poll_wait(file, sk_sleep(sk), wait); - mask = 0; - - /* Exceptional events? */ - if (sk->sk_err) - mask |= EPOLLERR; - if (sk->sk_shutdown & RCV_SHUTDOWN) { - pr_debug("%s(), POLLHUP\n", __func__); - mask |= EPOLLHUP; - } - - /* Readable? */ - if (!skb_queue_empty(&sk->sk_receive_queue)) { - pr_debug("Socket is readable\n"); - mask |= EPOLLIN | EPOLLRDNORM; - } - - /* Connection-based need to check for termination and startup */ - switch (sk->sk_type) { - case SOCK_STREAM: - if (sk->sk_state == TCP_CLOSE) { - pr_debug("%s(), POLLHUP\n", __func__); - mask |= EPOLLHUP; - } - - if (sk->sk_state == TCP_ESTABLISHED) { - if ((self->tx_flow == FLOW_START) && - sock_writeable(sk)) - { - mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; - } - } - break; - case SOCK_SEQPACKET: - if ((self->tx_flow == FLOW_START) && - sock_writeable(sk)) - { - mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; - } - break; - case SOCK_DGRAM: - if (sock_writeable(sk)) - mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; - break; - default: - break; - } - - return mask; -} - -/* - * Function irda_ioctl (sock, cmd, arg) - */ -static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - int err; - - pr_debug("%s(), cmd=%#x\n", __func__, cmd); - - err = -EINVAL; - switch (cmd) { - case TIOCOUTQ: { - long amount; - - amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - if (amount < 0) - amount = 0; - err = put_user(amount, (unsigned int __user *)arg); - 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; - err = put_user(amount, (unsigned int __user *)arg); - break; - } - - case SIOCGSTAMP: - if (sk != NULL) - err = sock_get_timestamp(sk, (struct timeval __user *)arg); - break; - - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - break; - default: - pr_debug("%s(), doing device ioctl!\n", __func__); - err = -ENOIOCTLCMD; - } - - return err; -} - -#ifdef CONFIG_COMPAT -/* - * Function irda_ioctl (sock, cmd, arg) - */ -static int irda_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - /* - * All IRDA's ioctl are standard ones. - */ - return -ENOIOCTLCMD; -} -#endif - -/* - * Function irda_setsockopt (sock, level, optname, optval, optlen) - * - * Set some options for the socket - * - */ -static int irda_setsockopt(struct socket *sock, int level, int optname, - char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - struct irda_ias_set *ias_opt; - struct ias_object *ias_obj; - struct ias_attrib * ias_attr; /* Attribute in IAS object */ - int opt, free_ias = 0, err = 0; - - pr_debug("%s(%p)\n", __func__, self); - - if (level != SOL_IRLMP) - return -ENOPROTOOPT; - - lock_sock(sk); - - switch (optname) { - case IRLMP_IAS_SET: - /* The user want to add an attribute to an existing IAS object - * (in the IAS database) or to create a new object with this - * attribute. - * We first query IAS to know if the object exist, and then - * create the right attribute... - */ - - if (optlen != sizeof(struct irda_ias_set)) { - err = -EINVAL; - goto out; - } - - /* Copy query to the driver. */ - ias_opt = memdup_user(optval, optlen); - if (IS_ERR(ias_opt)) { - err = PTR_ERR(ias_opt); - goto out; - } - - /* Find the object we target. - * If the user gives us an empty string, we use the object - * associated with this socket. This will workaround - * duplicated class name - Jean II */ - if(ias_opt->irda_class_name[0] == '\0') { - if(self->ias_obj == NULL) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - ias_obj = self->ias_obj; - } else - ias_obj = irias_find_object(ias_opt->irda_class_name); - - /* Only ROOT can mess with the global IAS database. - * Users can only add attributes to the object associated - * with the socket they own - Jean II */ - if((!capable(CAP_NET_ADMIN)) && - ((ias_obj == NULL) || (ias_obj != self->ias_obj))) { - kfree(ias_opt); - err = -EPERM; - goto out; - } - - /* If the object doesn't exist, create it */ - if(ias_obj == (struct ias_object *) NULL) { - /* Create a new object */ - ias_obj = irias_new_object(ias_opt->irda_class_name, - jiffies); - if (ias_obj == NULL) { - kfree(ias_opt); - err = -ENOMEM; - goto out; - } - free_ias = 1; - } - - /* Do we have the attribute already ? */ - if(irias_find_attrib(ias_obj, ias_opt->irda_attrib_name)) { - kfree(ias_opt); - if (free_ias) { - kfree(ias_obj->name); - kfree(ias_obj); - } - err = -EINVAL; - goto out; - } - - /* Look at the type */ - switch(ias_opt->irda_attrib_type) { - case IAS_INTEGER: - /* Add an integer attribute */ - irias_add_integer_attrib( - ias_obj, - ias_opt->irda_attrib_name, - ias_opt->attribute.irda_attrib_int, - IAS_USER_ATTR); - break; - case IAS_OCT_SEQ: - /* Check length */ - if(ias_opt->attribute.irda_attrib_octet_seq.len > - IAS_MAX_OCTET_STRING) { - kfree(ias_opt); - if (free_ias) { - kfree(ias_obj->name); - kfree(ias_obj); - } - - err = -EINVAL; - goto out; - } - /* Add an octet sequence attribute */ - irias_add_octseq_attrib( - ias_obj, - ias_opt->irda_attrib_name, - ias_opt->attribute.irda_attrib_octet_seq.octet_seq, - ias_opt->attribute.irda_attrib_octet_seq.len, - IAS_USER_ATTR); - break; - case IAS_STRING: - /* Should check charset & co */ - /* Check length */ - /* The length is encoded in a __u8, and - * IAS_MAX_STRING == 256, so there is no way - * userspace can pass us a string too large. - * Jean II */ - /* NULL terminate the string (avoid troubles) */ - ias_opt->attribute.irda_attrib_string.string[ias_opt->attribute.irda_attrib_string.len] = '\0'; - /* Add a string attribute */ - irias_add_string_attrib( - ias_obj, - ias_opt->irda_attrib_name, - ias_opt->attribute.irda_attrib_string.string, - IAS_USER_ATTR); - break; - default : - kfree(ias_opt); - if (free_ias) { - kfree(ias_obj->name); - kfree(ias_obj); - } - err = -EINVAL; - goto out; - } - irias_insert_object(ias_obj); - kfree(ias_opt); - break; - case IRLMP_IAS_DEL: - /* The user want to delete an object from our local IAS - * database. We just need to query the IAS, check is the - * object is not owned by the kernel and delete it. - */ - - if (optlen != sizeof(struct irda_ias_set)) { - err = -EINVAL; - goto out; - } - - /* Copy query to the driver. */ - ias_opt = memdup_user(optval, optlen); - if (IS_ERR(ias_opt)) { - err = PTR_ERR(ias_opt); - goto out; - } - - /* Find the object we target. - * If the user gives us an empty string, we use the object - * associated with this socket. This will workaround - * duplicated class name - Jean II */ - if(ias_opt->irda_class_name[0] == '\0') - ias_obj = self->ias_obj; - else - ias_obj = irias_find_object(ias_opt->irda_class_name); - if(ias_obj == (struct ias_object *) NULL) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - - /* Only ROOT can mess with the global IAS database. - * Users can only del attributes from the object associated - * with the socket they own - Jean II */ - if((!capable(CAP_NET_ADMIN)) && - ((ias_obj == NULL) || (ias_obj != self->ias_obj))) { - kfree(ias_opt); - err = -EPERM; - goto out; - } - - /* Find the attribute (in the object) we target */ - ias_attr = irias_find_attrib(ias_obj, - ias_opt->irda_attrib_name); - if(ias_attr == (struct ias_attrib *) NULL) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - - /* Check is the user space own the object */ - if(ias_attr->value->owner != IAS_USER_ATTR) { - pr_debug("%s(), attempting to delete a kernel attribute\n", - __func__); - kfree(ias_opt); - err = -EPERM; - goto out; - } - - /* Remove the attribute (and maybe the object) */ - irias_delete_attrib(ias_obj, ias_attr, 1); - kfree(ias_opt); - break; - case IRLMP_MAX_SDU_SIZE: - if (optlen < sizeof(int)) { - err = -EINVAL; - goto out; - } - - if (get_user(opt, (int __user *)optval)) { - err = -EFAULT; - goto out; - } - - /* Only possible for a seqpacket service (TTP with SAR) */ - if (sk->sk_type != SOCK_SEQPACKET) { - pr_debug("%s(), setting max_sdu_size = %d\n", - __func__, opt); - self->max_sdu_size_rx = opt; - } else { - net_warn_ratelimited("%s: not allowed to set MAXSDUSIZE for this socket type!\n", - __func__); - err = -ENOPROTOOPT; - goto out; - } - break; - case IRLMP_HINTS_SET: - if (optlen < sizeof(int)) { - err = -EINVAL; - goto out; - } - - /* The input is really a (__u8 hints[2]), easier as an int */ - if (get_user(opt, (int __user *)optval)) { - err = -EFAULT; - goto out; - } - - /* Unregister any old registration */ - irlmp_unregister_service(self->skey); - - self->skey = irlmp_register_service((__u16) opt); - break; - case IRLMP_HINT_MASK_SET: - /* As opposed to the previous case which set the hint bits - * that we advertise, this one set the filter we use when - * making a discovery (nodes which don't match any hint - * bit in the mask are not reported). - */ - if (optlen < sizeof(int)) { - err = -EINVAL; - goto out; - } - - /* The input is really a (__u8 hints[2]), easier as an int */ - if (get_user(opt, (int __user *)optval)) { - err = -EFAULT; - goto out; - } - - /* Set the new hint mask */ - self->mask.word = (__u16) opt; - /* Mask out extension bits */ - self->mask.word &= 0x7f7f; - /* Check if no bits */ - if(!self->mask.word) - self->mask.word = 0xFFFF; - - break; - default: - err = -ENOPROTOOPT; - break; - } - -out: - release_sock(sk); - - return err; -} - -/* - * Function irda_extract_ias_value(ias_opt, ias_value) - * - * Translate internal IAS value structure to the user space representation - * - * The external representation of IAS values, as we exchange them with - * user space program is quite different from the internal representation, - * as stored in the IAS database (because we need a flat structure for - * crossing kernel boundary). - * This function transform the former in the latter. We also check - * that the value type is valid. - */ -static int irda_extract_ias_value(struct irda_ias_set *ias_opt, - struct ias_value *ias_value) -{ - /* Look at the type */ - switch (ias_value->type) { - case IAS_INTEGER: - /* Copy the integer */ - ias_opt->attribute.irda_attrib_int = ias_value->t.integer; - break; - case IAS_OCT_SEQ: - /* Set length */ - ias_opt->attribute.irda_attrib_octet_seq.len = ias_value->len; - /* Copy over */ - memcpy(ias_opt->attribute.irda_attrib_octet_seq.octet_seq, - ias_value->t.oct_seq, ias_value->len); - break; - case IAS_STRING: - /* Set length */ - ias_opt->attribute.irda_attrib_string.len = ias_value->len; - ias_opt->attribute.irda_attrib_string.charset = ias_value->charset; - /* Copy over */ - memcpy(ias_opt->attribute.irda_attrib_string.string, - ias_value->t.string, ias_value->len); - /* NULL terminate the string (avoid troubles) */ - ias_opt->attribute.irda_attrib_string.string[ias_value->len] = '\0'; - break; - case IAS_MISSING: - default : - return -EINVAL; - } - - /* Copy type over */ - ias_opt->irda_attrib_type = ias_value->type; - - return 0; -} - -/* - * Function irda_getsockopt (sock, level, optname, optval, optlen) - */ -static int irda_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - struct irda_device_list list = { 0 }; - struct irda_device_info *discoveries; - struct irda_ias_set * ias_opt; /* IAS get/query params */ - struct ias_object * ias_obj; /* Object in IAS */ - struct ias_attrib * ias_attr; /* Attribute in IAS object */ - int daddr = DEV_ADDR_ANY; /* Dest address for IAS queries */ - int val = 0; - int len = 0; - int err = 0; - int offset, total; - - pr_debug("%s(%p)\n", __func__, self); - - if (level != SOL_IRLMP) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - if(len < 0) - return -EINVAL; - - lock_sock(sk); - - switch (optname) { - case IRLMP_ENUMDEVICES: - - /* Offset to first device entry */ - offset = sizeof(struct irda_device_list) - - sizeof(struct irda_device_info); - - if (len < offset) { - err = -EINVAL; - goto out; - } - - /* Ask lmp for the current discovery log */ - discoveries = irlmp_get_discoveries(&list.len, self->mask.word, - self->nslots); - /* Check if the we got some results */ - if (discoveries == NULL) { - err = -EAGAIN; - goto out; /* Didn't find any devices */ - } - - /* Write total list length back to client */ - if (copy_to_user(optval, &list, offset)) - err = -EFAULT; - - /* Copy the list itself - watch for overflow */ - if (list.len > 2048) { - err = -EINVAL; - goto bed; - } - total = offset + (list.len * sizeof(struct irda_device_info)); - if (total > len) - total = len; - if (copy_to_user(optval+offset, discoveries, total - offset)) - err = -EFAULT; - - /* Write total number of bytes used back to client */ - if (put_user(total, optlen)) - err = -EFAULT; -bed: - /* Free up our buffer */ - kfree(discoveries); - break; - case IRLMP_MAX_SDU_SIZE: - val = self->max_data_size; - len = sizeof(int); - if (put_user(len, optlen)) { - err = -EFAULT; - goto out; - } - - if (copy_to_user(optval, &val, len)) { - err = -EFAULT; - goto out; - } - - break; - case IRLMP_IAS_GET: - /* The user want an object from our local IAS database. - * We just need to query the IAS and return the value - * that we found */ - - /* Check that the user has allocated the right space for us */ - if (len != sizeof(struct irda_ias_set)) { - err = -EINVAL; - goto out; - } - - /* Copy query to the driver. */ - ias_opt = memdup_user(optval, len); - if (IS_ERR(ias_opt)) { - err = PTR_ERR(ias_opt); - goto out; - } - - /* Find the object we target. - * If the user gives us an empty string, we use the object - * associated with this socket. This will workaround - * duplicated class name - Jean II */ - if(ias_opt->irda_class_name[0] == '\0') - ias_obj = self->ias_obj; - else - ias_obj = irias_find_object(ias_opt->irda_class_name); - if(ias_obj == (struct ias_object *) NULL) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - - /* Find the attribute (in the object) we target */ - ias_attr = irias_find_attrib(ias_obj, - ias_opt->irda_attrib_name); - if(ias_attr == (struct ias_attrib *) NULL) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - - /* Translate from internal to user structure */ - err = irda_extract_ias_value(ias_opt, ias_attr->value); - if(err) { - kfree(ias_opt); - goto out; - } - - /* Copy reply to the user */ - if (copy_to_user(optval, ias_opt, - sizeof(struct irda_ias_set))) { - kfree(ias_opt); - err = -EFAULT; - goto out; - } - /* Note : don't need to put optlen, we checked it */ - kfree(ias_opt); - break; - case IRLMP_IAS_QUERY: - /* The user want an object from a remote IAS database. - * We need to use IAP to query the remote database and - * then wait for the answer to come back. */ - - /* Check that the user has allocated the right space for us */ - if (len != sizeof(struct irda_ias_set)) { - err = -EINVAL; - goto out; - } - - /* Copy query to the driver. */ - ias_opt = memdup_user(optval, len); - if (IS_ERR(ias_opt)) { - err = PTR_ERR(ias_opt); - goto out; - } - - /* At this point, there are two cases... - * 1) the socket is connected - that's the easy case, we - * just query the device we are connected to... - * 2) the socket is not connected - the user doesn't want - * to connect and/or may not have a valid service name - * (so can't create a fake connection). In this case, - * we assume that the user pass us a valid destination - * address in the requesting structure... - */ - if(self->daddr != DEV_ADDR_ANY) { - /* We are connected - reuse known daddr */ - daddr = self->daddr; - } else { - /* We are not connected, we must specify a valid - * destination address */ - daddr = ias_opt->daddr; - if((!daddr) || (daddr == DEV_ADDR_ANY)) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - } - - /* Check that we can proceed with IAP */ - if (self->iriap) { - net_warn_ratelimited("%s: busy with a previous query\n", - __func__); - kfree(ias_opt); - err = -EBUSY; - goto out; - } - - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - irda_getvalue_confirm); - - if (self->iriap == NULL) { - kfree(ias_opt); - err = -ENOMEM; - goto out; - } - - /* Treat unexpected wakeup as disconnect */ - self->errno = -EHOSTUNREACH; - - /* Query remote LM-IAS */ - iriap_getvaluebyclass_request(self->iriap, - self->saddr, daddr, - ias_opt->irda_class_name, - ias_opt->irda_attrib_name); - - /* Wait for answer, if not yet finished (or failed) */ - if (wait_event_interruptible(self->query_wait, - (self->iriap == NULL))) { - /* pending request uses copy of ias_opt-content - * we can free it regardless! */ - kfree(ias_opt); - /* Treat signals as disconnect */ - err = -EHOSTUNREACH; - goto out; - } - - /* Check what happened */ - if (self->errno) - { - kfree(ias_opt); - /* Requested object/attribute doesn't exist */ - if((self->errno == IAS_CLASS_UNKNOWN) || - (self->errno == IAS_ATTRIB_UNKNOWN)) - err = -EADDRNOTAVAIL; - else - err = -EHOSTUNREACH; - - goto out; - } - - /* Translate from internal to user structure */ - err = irda_extract_ias_value(ias_opt, self->ias_result); - if (self->ias_result) - irias_delete_value(self->ias_result); - if (err) { - kfree(ias_opt); - goto out; - } - - /* Copy reply to the user */ - if (copy_to_user(optval, ias_opt, - sizeof(struct irda_ias_set))) { - kfree(ias_opt); - err = -EFAULT; - goto out; - } - /* Note : don't need to put optlen, we checked it */ - kfree(ias_opt); - break; - case IRLMP_WAITDEVICE: - /* This function is just another way of seeing life ;-) - * IRLMP_ENUMDEVICES assumes that you have a static network, - * and that you just want to pick one of the devices present. - * On the other hand, in here we assume that no device is - * present and that at some point in the future a device will - * come into range. When this device arrive, we just wake - * up the caller, so that he has time to connect to it before - * the device goes away... - * Note : once the node has been discovered for more than a - * few second, it won't trigger this function, unless it - * goes away and come back changes its hint bits (so we - * might call it IRLMP_WAITNEWDEVICE). - */ - - /* Check that the user is passing us an int */ - if (len != sizeof(int)) { - err = -EINVAL; - goto out; - } - /* Get timeout in ms (max time we block the caller) */ - if (get_user(val, (int __user *)optval)) { - err = -EFAULT; - goto out; - } - - /* Tell IrLMP we want to be notified */ - irlmp_update_client(self->ckey, self->mask.word, - irda_selective_discovery_indication, - NULL, (void *) self); - - /* Do some discovery (and also return cached results) */ - irlmp_discovery_request(self->nslots); - - /* Wait until a node is discovered */ - if (!self->cachedaddr) { - pr_debug("%s(), nothing discovered yet, going to sleep...\n", - __func__); - - /* Set watchdog timer to expire in ms. */ - self->errno = 0; - timer_setup(&self->watchdog, irda_discovery_timeout, 0); - mod_timer(&self->watchdog, - jiffies + msecs_to_jiffies(val)); - - /* Wait for IR-LMP to call us back */ - err = __wait_event_interruptible(self->query_wait, - (self->cachedaddr != 0 || self->errno == -ETIME)); - - /* If watchdog is still activated, kill it! */ - del_timer(&(self->watchdog)); - - pr_debug("%s(), ...waking up !\n", __func__); - - if (err != 0) - goto out; - } - else - pr_debug("%s(), found immediately !\n", - __func__); - - /* Tell IrLMP that we have been notified */ - irlmp_update_client(self->ckey, self->mask.word, - NULL, NULL, NULL); - - /* Check if the we got some results */ - if (!self->cachedaddr) { - err = -EAGAIN; /* Didn't find any devices */ - goto out; - } - daddr = self->cachedaddr; - /* Cleanup */ - self->cachedaddr = 0; - - /* We return the daddr of the device that trigger the - * wakeup. As irlmp pass us only the new devices, we - * are sure that it's not an old device. - * If the user want more details, he should query - * the whole discovery log and pick one device... - */ - if (put_user(daddr, (int __user *)optval)) { - err = -EFAULT; - goto out; - } - - break; - default: - err = -ENOPROTOOPT; - } - -out: - - release_sock(sk); - - return err; -} - -static const struct net_proto_family irda_family_ops = { - .family = PF_IRDA, - .create = irda_create, - .owner = THIS_MODULE, -}; - -static const struct proto_ops irda_stream_ops = { - .family = PF_IRDA, - .owner = THIS_MODULE, - .release = irda_release, - .bind = irda_bind, - .connect = irda_connect, - .socketpair = sock_no_socketpair, - .accept = irda_accept, - .getname = irda_getname, - .poll = irda_poll, - .ioctl = irda_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = irda_compat_ioctl, -#endif - .listen = irda_listen, - .shutdown = irda_shutdown, - .setsockopt = irda_setsockopt, - .getsockopt = irda_getsockopt, - .sendmsg = irda_sendmsg, - .recvmsg = irda_recvmsg_stream, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -}; - -static const struct proto_ops irda_seqpacket_ops = { - .family = PF_IRDA, - .owner = THIS_MODULE, - .release = irda_release, - .bind = irda_bind, - .connect = irda_connect, - .socketpair = sock_no_socketpair, - .accept = irda_accept, - .getname = irda_getname, - .poll = datagram_poll, - .ioctl = irda_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = irda_compat_ioctl, -#endif - .listen = irda_listen, - .shutdown = irda_shutdown, - .setsockopt = irda_setsockopt, - .getsockopt = irda_getsockopt, - .sendmsg = irda_sendmsg, - .recvmsg = irda_recvmsg_dgram, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -}; - -static const struct proto_ops irda_dgram_ops = { - .family = PF_IRDA, - .owner = THIS_MODULE, - .release = irda_release, - .bind = irda_bind, - .connect = irda_connect, - .socketpair = sock_no_socketpair, - .accept = irda_accept, - .getname = irda_getname, - .poll = datagram_poll, - .ioctl = irda_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = irda_compat_ioctl, -#endif - .listen = irda_listen, - .shutdown = irda_shutdown, - .setsockopt = irda_setsockopt, - .getsockopt = irda_getsockopt, - .sendmsg = irda_sendmsg_dgram, - .recvmsg = irda_recvmsg_dgram, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -}; - -#ifdef CONFIG_IRDA_ULTRA -static const struct proto_ops irda_ultra_ops = { - .family = PF_IRDA, - .owner = THIS_MODULE, - .release = irda_release, - .bind = irda_bind, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = irda_getname, - .poll = datagram_poll, - .ioctl = irda_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = irda_compat_ioctl, -#endif - .listen = sock_no_listen, - .shutdown = irda_shutdown, - .setsockopt = irda_setsockopt, - .getsockopt = irda_getsockopt, - .sendmsg = irda_sendmsg_ultra, - .recvmsg = irda_recvmsg_dgram, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -}; -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irsock_init (pro) - * - * Initialize IrDA protocol - * - */ -int __init irsock_init(void) -{ - int rc = proto_register(&irda_proto, 0); - - if (rc == 0) - rc = sock_register(&irda_family_ops); - - return rc; -} - -/* - * Function irsock_cleanup (void) - * - * Remove IrDA protocol - * - */ -void irsock_cleanup(void) -{ - sock_unregister(PF_IRDA); - proto_unregister(&irda_proto); -} diff --git a/drivers/staging/irda/net/discovery.c b/drivers/staging/irda/net/discovery.c deleted file mode 100644 index 1e54954a4081..000000000000 --- a/drivers/staging/irda/net/discovery.c +++ /dev/null @@ -1,417 +0,0 @@ -/********************************************************************* - * - * Filename: discovery.c - * Version: 0.1 - * Description: Routines for handling discoveries at the IrLMP layer - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Apr 6 15:33:50 1999 - * Modified at: Sat Oct 9 17:11:31 1999 - * Modified by: Dag Brattli - * Modified at: Fri May 28 3:11 CST 1999 - * Modified by: Horst von Brand - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -/* - * Function irlmp_add_discovery (cachelog, discovery) - * - * Add a new discovery to the cachelog, and remove any old discoveries - * from the same device - * - * Note : we try to preserve the time this device was *first* discovered - * (as opposed to the time of last discovery used for cleanup). This is - * used by clients waiting for discovery events to tell if the device - * discovered is "new" or just the same old one. They can't rely there - * on a binary flag (new/old), because not all discovery events are - * propagated to them, and they might not always listen, so they would - * miss some new devices popping up... - * Jean II - */ -void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new) -{ - discovery_t *discovery, *node; - unsigned long flags; - - /* Set time of first discovery if node is new (see below) */ - new->firststamp = new->timestamp; - - spin_lock_irqsave(&cachelog->hb_spinlock, flags); - - /* - * Remove all discoveries of devices that has previously been - * discovered on the same link with the same name (info), or the - * same daddr. We do this since some devices (mostly PDAs) change - * their device address between every discovery. - */ - discovery = (discovery_t *) hashbin_get_first(cachelog); - while (discovery != NULL ) { - node = discovery; - - /* Be sure to stay one item ahead */ - discovery = (discovery_t *) hashbin_get_next(cachelog); - - if ((node->data.saddr == new->data.saddr) && - ((node->data.daddr == new->data.daddr) || - (strcmp(node->data.info, new->data.info) == 0))) - { - /* This discovery is a previous discovery - * from the same device, so just remove it - */ - hashbin_remove_this(cachelog, (irda_queue_t *) node); - /* Check if hints bits are unchanged */ - if (get_unaligned((__u16 *)node->data.hints) == get_unaligned((__u16 *)new->data.hints)) - /* Set time of first discovery for this node */ - new->firststamp = node->firststamp; - kfree(node); - } - } - - /* Insert the new and updated version */ - hashbin_insert(cachelog, (irda_queue_t *) new, new->data.daddr, NULL); - - spin_unlock_irqrestore(&cachelog->hb_spinlock, flags); -} - -/* - * Function irlmp_add_discovery_log (cachelog, log) - * - * Merge a disovery log into the cachelog. - * - */ -void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log) -{ - discovery_t *discovery; - - /* - * If log is missing this means that IrLAP was unable to perform the - * discovery, so restart discovery again with just the half timeout - * of the normal one. - */ - /* Well... It means that there was nobody out there - Jean II */ - if (log == NULL) { - /* irlmp_start_discovery_timer(irlmp, 150); */ - return; - } - - /* - * Locking : we are the only owner of this discovery log, so - * no need to lock it. - * We just need to lock the global log in irlmp_add_discovery(). - */ - discovery = (discovery_t *) hashbin_remove_first(log); - while (discovery != NULL) { - irlmp_add_discovery(cachelog, discovery); - - discovery = (discovery_t *) hashbin_remove_first(log); - } - - /* Delete the now empty log */ - hashbin_delete(log, (FREE_FUNC) kfree); -} - -/* - * Function irlmp_expire_discoveries (log, saddr, force) - * - * Go through all discoveries and expire all that has stayed too long - * - * Note : this assume that IrLAP won't change its saddr, which - * currently is a valid assumption... - */ -void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force) -{ - discovery_t * discovery; - discovery_t * curr; - unsigned long flags; - discinfo_t * buffer = NULL; - int n; /* Size of the full log */ - int i = 0; /* How many we expired */ - - IRDA_ASSERT(log != NULL, return;); - spin_lock_irqsave(&log->hb_spinlock, flags); - - discovery = (discovery_t *) hashbin_get_first(log); - while (discovery != NULL) { - /* Be sure to be one item ahead */ - curr = discovery; - discovery = (discovery_t *) hashbin_get_next(log); - - /* Test if it's time to expire this discovery */ - if ((curr->data.saddr == saddr) && - (force || - ((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT))) - { - /* Create buffer as needed. - * As this function get called a lot and most time - * we don't have anything to put in the log (we are - * quite picky), we can save a lot of overhead - * by not calling kmalloc. Jean II */ - if(buffer == NULL) { - /* Create the client specific buffer */ - n = HASHBIN_GET_SIZE(log); - buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC); - if (!buffer) { - spin_unlock_irqrestore(&log->hb_spinlock, flags); - return; - } - - } - - /* Copy discovery information */ - memcpy(&(buffer[i]), &(curr->data), - sizeof(discinfo_t)); - i++; - - /* Remove it from the log */ - curr = hashbin_remove_this(log, (irda_queue_t *) curr); - kfree(curr); - } - } - - /* Drop the spinlock before calling the higher layers, as - * we can't guarantee they won't call us back and create a - * deadlock. We will work on our own private data, so we - * don't care to be interrupted. - Jean II */ - spin_unlock_irqrestore(&log->hb_spinlock, flags); - - if(buffer == NULL) - return; - - /* Tell IrLMP and registered clients about it */ - irlmp_discovery_expiry(buffer, i); - - /* Free up our buffer */ - kfree(buffer); -} - -#if 0 -/* - * Function irlmp_dump_discoveries (log) - * - * Print out all discoveries in log - * - */ -void irlmp_dump_discoveries(hashbin_t *log) -{ - discovery_t *discovery; - - IRDA_ASSERT(log != NULL, return;); - - discovery = (discovery_t *) hashbin_get_first(log); - while (discovery != NULL) { - pr_debug("Discovery:\n"); - pr_debug(" daddr=%08x\n", discovery->data.daddr); - pr_debug(" saddr=%08x\n", discovery->data.saddr); - pr_debug(" nickname=%s\n", discovery->data.info); - - discovery = (discovery_t *) hashbin_get_next(log); - } -} -#endif - -/* - * Function irlmp_copy_discoveries (log, pn, mask) - * - * Copy all discoveries in a buffer - * - * This function implement a safe way for lmp clients to access the - * discovery log. The basic problem is that we don't want the log - * to change (add/remove) while the client is reading it. If the - * lmp client manipulate directly the hashbin, he is sure to get - * into troubles... - * The idea is that we copy all the current discovery log in a buffer - * which is specific to the client and pass this copy to him. As we - * do this operation with the spinlock grabbed, we are safe... - * Note : we don't want those clients to grab the spinlock, because - * we have no control on how long they will hold it... - * Note : we choose to copy the log in "struct irda_device_info" to - * save space... - * Note : the client must kfree himself() the log... - * Jean II - */ -struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, - __u16 mask, int old_entries) -{ - discovery_t * discovery; - unsigned long flags; - discinfo_t * buffer = NULL; - int j_timeout = (sysctl_discovery_timeout * HZ); - int n; /* Size of the full log */ - int i = 0; /* How many we picked */ - - IRDA_ASSERT(pn != NULL, return NULL;); - IRDA_ASSERT(log != NULL, return NULL;); - - /* Save spin lock */ - spin_lock_irqsave(&log->hb_spinlock, flags); - - discovery = (discovery_t *) hashbin_get_first(log); - while (discovery != NULL) { - /* Mask out the ones we don't want : - * We want to match the discovery mask, and to get only - * the most recent one (unless we want old ones) */ - if ((get_unaligned((__u16 *)discovery->data.hints) & mask) && - ((old_entries) || - ((jiffies - discovery->firststamp) < j_timeout))) { - /* Create buffer as needed. - * As this function get called a lot and most time - * we don't have anything to put in the log (we are - * quite picky), we can save a lot of overhead - * by not calling kmalloc. Jean II */ - if(buffer == NULL) { - /* Create the client specific buffer */ - n = HASHBIN_GET_SIZE(log); - buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC); - if (!buffer) { - spin_unlock_irqrestore(&log->hb_spinlock, flags); - return NULL; - } - - } - - /* Copy discovery information */ - memcpy(&(buffer[i]), &(discovery->data), - sizeof(discinfo_t)); - i++; - } - discovery = (discovery_t *) hashbin_get_next(log); - } - - spin_unlock_irqrestore(&log->hb_spinlock, flags); - - /* Get the actual number of device in the buffer and return */ - *pn = i; - return buffer; -} - -#ifdef CONFIG_PROC_FS -static inline discovery_t *discovery_seq_idx(loff_t pos) - -{ - discovery_t *discovery; - - for (discovery = (discovery_t *) hashbin_get_first(irlmp->cachelog); - discovery != NULL; - discovery = (discovery_t *) hashbin_get_next(irlmp->cachelog)) { - if (pos-- == 0) - break; - } - - return discovery; -} - -static void *discovery_seq_start(struct seq_file *seq, loff_t *pos) -{ - spin_lock_irq(&irlmp->cachelog->hb_spinlock); - return *pos ? discovery_seq_idx(*pos - 1) : SEQ_START_TOKEN; -} - -static void *discovery_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - return (v == SEQ_START_TOKEN) - ? (void *) hashbin_get_first(irlmp->cachelog) - : (void *) hashbin_get_next(irlmp->cachelog); -} - -static void discovery_seq_stop(struct seq_file *seq, void *v) -{ - spin_unlock_irq(&irlmp->cachelog->hb_spinlock); -} - -static int discovery_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) - seq_puts(seq, "IrLMP: Discovery log:\n\n"); - else { - const discovery_t *discovery = v; - - seq_printf(seq, "nickname: %s, hint: 0x%02x%02x", - discovery->data.info, - discovery->data.hints[0], - discovery->data.hints[1]); -#if 0 - if ( discovery->data.hints[0] & HINT_PNP) - seq_puts(seq, "PnP Compatible "); - if ( discovery->data.hints[0] & HINT_PDA) - seq_puts(seq, "PDA/Palmtop "); - if ( discovery->data.hints[0] & HINT_COMPUTER) - seq_puts(seq, "Computer "); - if ( discovery->data.hints[0] & HINT_PRINTER) - seq_puts(seq, "Printer "); - if ( discovery->data.hints[0] & HINT_MODEM) - seq_puts(seq, "Modem "); - if ( discovery->data.hints[0] & HINT_FAX) - seq_puts(seq, "Fax "); - if ( discovery->data.hints[0] & HINT_LAN) - seq_puts(seq, "LAN Access "); - - if ( discovery->data.hints[1] & HINT_TELEPHONY) - seq_puts(seq, "Telephony "); - if ( discovery->data.hints[1] & HINT_FILE_SERVER) - seq_puts(seq, "File Server "); - if ( discovery->data.hints[1] & HINT_COMM) - seq_puts(seq, "IrCOMM "); - if ( discovery->data.hints[1] & HINT_OBEX) - seq_puts(seq, "IrOBEX "); -#endif - seq_printf(seq,", saddr: 0x%08x, daddr: 0x%08x\n\n", - discovery->data.saddr, - discovery->data.daddr); - - seq_putc(seq, '\n'); - } - return 0; -} - -static const struct seq_operations discovery_seq_ops = { - .start = discovery_seq_start, - .next = discovery_seq_next, - .stop = discovery_seq_stop, - .show = discovery_seq_show, -}; - -static int discovery_seq_open(struct inode *inode, struct file *file) -{ - IRDA_ASSERT(irlmp != NULL, return -EINVAL;); - - return seq_open(file, &discovery_seq_ops); -} - -const struct file_operations discovery_seq_fops = { - .owner = THIS_MODULE, - .open = discovery_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; -#endif diff --git a/drivers/staging/irda/net/ircomm/Kconfig b/drivers/staging/irda/net/ircomm/Kconfig deleted file mode 100644 index 19492c1707b7..000000000000 --- a/drivers/staging/irda/net/ircomm/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -config IRCOMM - tristate "IrCOMM protocol" - depends on IRDA && TTY - help - Say Y here if you want to build support for the IrCOMM protocol. - To compile it as modules, choose M here: the modules will be - called ircomm and ircomm_tty. - IrCOMM implements serial port emulation, and makes it possible to - use all existing applications that understands TTY's with an - infrared link. Thus you should be able to use application like PPP, - minicom and others. - diff --git a/drivers/staging/irda/net/ircomm/Makefile b/drivers/staging/irda/net/ircomm/Makefile deleted file mode 100644 index ab23b5ba7e33..000000000000 --- a/drivers/staging/irda/net/ircomm/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for the Linux IrDA IrCOMM protocol layer. -# - -obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o - -ircomm-y := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o -ircomm-tty-y := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o diff --git a/drivers/staging/irda/net/ircomm/ircomm_core.c b/drivers/staging/irda/net/ircomm/ircomm_core.c deleted file mode 100644 index 3af219545f6d..000000000000 --- a/drivers/staging/irda/net/ircomm/ircomm_core.c +++ /dev/null @@ -1,563 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_core.c - * Version: 1.0 - * Description: IrCOMM service interface - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Jun 6 20:37:34 1999 - * Modified at: Tue Dec 21 13:26:41 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -static int __ircomm_close(struct ircomm_cb *self); -static void ircomm_control_indication(struct ircomm_cb *self, - struct sk_buff *skb, int clen); - -#ifdef CONFIG_PROC_FS -extern struct proc_dir_entry *proc_irda; -static int ircomm_seq_open(struct inode *, struct file *); - -static const struct file_operations ircomm_proc_fops = { - .owner = THIS_MODULE, - .open = ircomm_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; -#endif /* CONFIG_PROC_FS */ - -hashbin_t *ircomm = NULL; - -static int __init ircomm_init(void) -{ - ircomm = hashbin_new(HB_LOCK); - if (ircomm == NULL) { - net_err_ratelimited("%s(), can't allocate hashbin!\n", - __func__); - return -ENOMEM; - } - -#ifdef CONFIG_PROC_FS - { struct proc_dir_entry *ent; - ent = proc_create("ircomm", 0, proc_irda, &ircomm_proc_fops); - if (!ent) { - printk(KERN_ERR "ircomm_init: can't create /proc entry!\n"); - return -ENODEV; - } - } -#endif /* CONFIG_PROC_FS */ - - net_info_ratelimited("IrCOMM protocol (Dag Brattli)\n"); - - return 0; -} - -static void __exit ircomm_cleanup(void) -{ - hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close); - -#ifdef CONFIG_PROC_FS - remove_proc_entry("ircomm", proc_irda); -#endif /* CONFIG_PROC_FS */ -} - -/* - * Function ircomm_open (client_notify) - * - * Start a new IrCOMM instance - * - */ -struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line) -{ - struct ircomm_cb *self = NULL; - int ret; - - pr_debug("%s(), service_type=0x%02x\n", __func__ , - service_type); - - IRDA_ASSERT(ircomm != NULL, return NULL;); - - self = kzalloc(sizeof(struct ircomm_cb), GFP_KERNEL); - if (self == NULL) - return NULL; - - self->notify = *notify; - self->magic = IRCOMM_MAGIC; - - /* Check if we should use IrLMP or IrTTP */ - if (service_type & IRCOMM_3_WIRE_RAW) { - self->flow_status = FLOW_START; - ret = ircomm_open_lsap(self); - } else - ret = ircomm_open_tsap(self); - - if (ret < 0) { - kfree(self); - return NULL; - } - - self->service_type = service_type; - self->line = line; - - hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL); - - ircomm_next_state(self, IRCOMM_IDLE); - - return self; -} - -EXPORT_SYMBOL(ircomm_open); - -/* - * Function ircomm_close_instance (self) - * - * Remove IrCOMM instance - * - */ -static int __ircomm_close(struct ircomm_cb *self) -{ - /* Disconnect link if any */ - ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL); - - /* Remove TSAP */ - if (self->tsap) { - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } - - /* Remove LSAP */ - if (self->lsap) { - irlmp_close_lsap(self->lsap); - self->lsap = NULL; - } - self->magic = 0; - - kfree(self); - - return 0; -} - -/* - * Function ircomm_close (self) - * - * Closes and removes the specified IrCOMM instance - * - */ -int ircomm_close(struct ircomm_cb *self) -{ - struct ircomm_cb *entry; - - IRDA_ASSERT(self != NULL, return -EIO;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;); - - entry = hashbin_remove(ircomm, self->line, NULL); - - IRDA_ASSERT(entry == self, return -1;); - - return __ircomm_close(self); -} - -EXPORT_SYMBOL(ircomm_close); - -/* - * Function ircomm_connect_request (self, service_type) - * - * Impl. of this function is differ from one of the reference. This - * function does discovery as well as sending connect request - * - */ -int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel, - __u32 saddr, __u32 daddr, struct sk_buff *skb, - __u8 service_type) -{ - struct ircomm_info info; - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); - - self->service_type= service_type; - - info.dlsap_sel = dlsap_sel; - info.saddr = saddr; - info.daddr = daddr; - - ret = ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, skb, &info); - - return ret; -} - -EXPORT_SYMBOL(ircomm_connect_request); - -/* - * Function ircomm_connect_indication (self, qos, skb) - * - * Notify user layer about the incoming connection - * - */ -void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb, - struct ircomm_info *info) -{ - /* - * If there are any data hiding in the control channel, we must - * deliver it first. The side effect is that the control channel - * will be removed from the skb - */ - if (self->notify.connect_indication) - self->notify.connect_indication(self->notify.instance, self, - info->qos, info->max_data_size, - info->max_header_size, skb); - else { - pr_debug("%s(), missing handler\n", __func__); - } -} - -/* - * Function ircomm_connect_response (self, userdata, max_sdu_size) - * - * User accepts connection - * - */ -int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); - - ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL); - - return ret; -} - -EXPORT_SYMBOL(ircomm_connect_response); - -/* - * Function connect_confirm (self, skb) - * - * Notify user layer that the link is now connected - * - */ -void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb, - struct ircomm_info *info) -{ - if (self->notify.connect_confirm ) - self->notify.connect_confirm(self->notify.instance, - self, info->qos, - info->max_data_size, - info->max_header_size, skb); - else { - pr_debug("%s(), missing handler\n", __func__); - } -} - -/* - * Function ircomm_data_request (self, userdata) - * - * Send IrCOMM data to peer device - * - */ -int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -EFAULT;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;); - IRDA_ASSERT(skb != NULL, return -EFAULT;); - - ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL); - - return ret; -} - -EXPORT_SYMBOL(ircomm_data_request); - -/* - * Function ircomm_data_indication (self, skb) - * - * Data arrived, so deliver it to user - * - */ -void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(skb->len > 0, return;); - - if (self->notify.data_indication) - self->notify.data_indication(self->notify.instance, self, skb); - else { - pr_debug("%s(), missing handler\n", __func__); - } -} - -/* - * Function ircomm_process_data (self, skb) - * - * Data arrived which may contain control channel data - * - */ -void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb) -{ - int clen; - - IRDA_ASSERT(skb->len > 0, return;); - - clen = skb->data[0]; - - /* - * Input validation check: a stir4200/mcp2150 combinations sometimes - * results in frames with clen > remaining packet size. These are - * illegal; if we throw away just this frame then it seems to carry on - * fine - */ - if (unlikely(skb->len < (clen + 1))) { - pr_debug("%s() throwing away illegal frame\n", - __func__); - return; - } - - /* - * If there are any data hiding in the control channel, we must - * deliver it first. The side effect is that the control channel - * will be removed from the skb - */ - if (clen > 0) - ircomm_control_indication(self, skb, clen); - - /* Remove control channel from data channel */ - skb_pull(skb, clen+1); - - if (skb->len) - ircomm_data_indication(self, skb); - else { - pr_debug("%s(), data was control info only!\n", - __func__); - } -} - -/* - * Function ircomm_control_request (self, params) - * - * Send control data to peer device - * - */ -int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -EFAULT;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;); - IRDA_ASSERT(skb != NULL, return -EFAULT;); - - ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL); - - return ret; -} - -EXPORT_SYMBOL(ircomm_control_request); - -/* - * Function ircomm_control_indication (self, skb) - * - * Data has arrived on the control channel - * - */ -static void ircomm_control_indication(struct ircomm_cb *self, - struct sk_buff *skb, int clen) -{ - /* Use udata for delivering data on the control channel */ - if (self->notify.udata_indication) { - struct sk_buff *ctrl_skb; - - /* We don't own the skb, so clone it */ - ctrl_skb = skb_clone(skb, GFP_ATOMIC); - if (!ctrl_skb) - return; - - /* Remove data channel from control channel */ - skb_trim(ctrl_skb, clen+1); - - self->notify.udata_indication(self->notify.instance, self, - ctrl_skb); - - /* Drop reference count - - * see ircomm_tty_control_indication(). */ - dev_kfree_skb(ctrl_skb); - } else { - pr_debug("%s(), missing handler\n", __func__); - } -} - -/* - * Function ircomm_disconnect_request (self, userdata, priority) - * - * User layer wants to disconnect the IrCOMM connection - * - */ -int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata) -{ - struct ircomm_info info; - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); - - ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata, - &info); - return ret; -} - -EXPORT_SYMBOL(ircomm_disconnect_request); - -/* - * Function disconnect_indication (self, skb) - * - * Tell user that the link has been disconnected - * - */ -void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb, - struct ircomm_info *info) -{ - IRDA_ASSERT(info != NULL, return;); - - if (self->notify.disconnect_indication) { - self->notify.disconnect_indication(self->notify.instance, self, - info->reason, skb); - } else { - pr_debug("%s(), missing handler\n", __func__); - } -} - -/* - * Function ircomm_flow_request (self, flow) - * - * - * - */ -void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - - if (self->service_type == IRCOMM_3_WIRE_RAW) - return; - - irttp_flow_request(self->tsap, flow); -} - -EXPORT_SYMBOL(ircomm_flow_request); - -#ifdef CONFIG_PROC_FS -static void *ircomm_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct ircomm_cb *self; - loff_t off = 0; - - spin_lock_irq(&ircomm->hb_spinlock); - - for (self = (struct ircomm_cb *) hashbin_get_first(ircomm); - self != NULL; - self = (struct ircomm_cb *) hashbin_get_next(ircomm)) { - if (off++ == *pos) - break; - - } - return self; -} - -static void *ircomm_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - - return (void *) hashbin_get_next(ircomm); -} - -static void ircomm_seq_stop(struct seq_file *seq, void *v) -{ - spin_unlock_irq(&ircomm->hb_spinlock); -} - -static int ircomm_seq_show(struct seq_file *seq, void *v) -{ - const struct ircomm_cb *self = v; - - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EINVAL; ); - - if(self->line < 0x10) - seq_printf(seq, "ircomm%d", self->line); - else - seq_printf(seq, "irlpt%d", self->line - 0x10); - - seq_printf(seq, - " state: %s, slsap_sel: %#02x, dlsap_sel: %#02x, mode:", - ircomm_state[ self->state], - self->slsap_sel, self->dlsap_sel); - - if(self->service_type & IRCOMM_3_WIRE_RAW) - seq_printf(seq, " 3-wire-raw"); - if(self->service_type & IRCOMM_3_WIRE) - seq_printf(seq, " 3-wire"); - if(self->service_type & IRCOMM_9_WIRE) - seq_printf(seq, " 9-wire"); - if(self->service_type & IRCOMM_CENTRONICS) - seq_printf(seq, " Centronics"); - seq_putc(seq, '\n'); - - return 0; -} - -static const struct seq_operations ircomm_seq_ops = { - .start = ircomm_seq_start, - .next = ircomm_seq_next, - .stop = ircomm_seq_stop, - .show = ircomm_seq_show, -}; - -static int ircomm_seq_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &ircomm_seq_ops); -} -#endif /* CONFIG_PROC_FS */ - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("IrCOMM protocol"); -MODULE_LICENSE("GPL"); - -module_init(ircomm_init); -module_exit(ircomm_cleanup); diff --git a/drivers/staging/irda/net/ircomm/ircomm_event.c b/drivers/staging/irda/net/ircomm/ircomm_event.c deleted file mode 100644 index b0730ac9f388..000000000000 --- a/drivers/staging/irda/net/ircomm/ircomm_event.c +++ /dev/null @@ -1,246 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_event.c - * Version: 1.0 - * Description: IrCOMM layer state machine - * Status: Stable - * Author: Dag Brattli - * Created at: Sun Jun 6 20:33:11 1999 - * Modified at: Sun Dec 12 13:44:32 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info); -static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info); -static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info); -static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info); - -const char *const ircomm_state[] = { - "IRCOMM_IDLE", - "IRCOMM_WAITI", - "IRCOMM_WAITR", - "IRCOMM_CONN", -}; - -static const char *const ircomm_event[] __maybe_unused = { - "IRCOMM_CONNECT_REQUEST", - "IRCOMM_CONNECT_RESPONSE", - "IRCOMM_TTP_CONNECT_INDICATION", - "IRCOMM_LMP_CONNECT_INDICATION", - "IRCOMM_TTP_CONNECT_CONFIRM", - "IRCOMM_LMP_CONNECT_CONFIRM", - - "IRCOMM_LMP_DISCONNECT_INDICATION", - "IRCOMM_TTP_DISCONNECT_INDICATION", - "IRCOMM_DISCONNECT_REQUEST", - - "IRCOMM_TTP_DATA_INDICATION", - "IRCOMM_LMP_DATA_INDICATION", - "IRCOMM_DATA_REQUEST", - "IRCOMM_CONTROL_REQUEST", - "IRCOMM_CONTROL_INDICATION", -}; - -static int (*state[])(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) = -{ - ircomm_state_idle, - ircomm_state_waiti, - ircomm_state_waitr, - ircomm_state_conn, -}; - -/* - * Function ircomm_state_idle (self, event, skb) - * - * IrCOMM is currently idle - * - */ -static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) -{ - int ret = 0; - - switch (event) { - case IRCOMM_CONNECT_REQUEST: - ircomm_next_state(self, IRCOMM_WAITI); - ret = self->issue.connect_request(self, skb, info); - break; - case IRCOMM_TTP_CONNECT_INDICATION: - case IRCOMM_LMP_CONNECT_INDICATION: - ircomm_next_state(self, IRCOMM_WAITR); - ircomm_connect_indication(self, skb, info); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_state_waiti (self, event, skb) - * - * The IrCOMM user has requested an IrCOMM connection to the remote - * device and is awaiting confirmation - */ -static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) -{ - int ret = 0; - - switch (event) { - case IRCOMM_TTP_CONNECT_CONFIRM: - case IRCOMM_LMP_CONNECT_CONFIRM: - ircomm_next_state(self, IRCOMM_CONN); - ircomm_connect_confirm(self, skb, info); - break; - case IRCOMM_TTP_DISCONNECT_INDICATION: - case IRCOMM_LMP_DISCONNECT_INDICATION: - ircomm_next_state(self, IRCOMM_IDLE); - ircomm_disconnect_indication(self, skb, info); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_state_waitr (self, event, skb) - * - * IrCOMM has received an incoming connection request and is awaiting - * response from the user - */ -static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) -{ - int ret = 0; - - switch (event) { - case IRCOMM_CONNECT_RESPONSE: - ircomm_next_state(self, IRCOMM_CONN); - ret = self->issue.connect_response(self, skb); - break; - case IRCOMM_DISCONNECT_REQUEST: - ircomm_next_state(self, IRCOMM_IDLE); - ret = self->issue.disconnect_request(self, skb, info); - break; - case IRCOMM_TTP_DISCONNECT_INDICATION: - case IRCOMM_LMP_DISCONNECT_INDICATION: - ircomm_next_state(self, IRCOMM_IDLE); - ircomm_disconnect_indication(self, skb, info); - break; - default: - pr_debug("%s(), unknown event = %s\n", __func__ , - ircomm_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_state_conn (self, event, skb) - * - * IrCOMM is connected to the peer IrCOMM device - * - */ -static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) -{ - int ret = 0; - - switch (event) { - case IRCOMM_DATA_REQUEST: - ret = self->issue.data_request(self, skb, 0); - break; - case IRCOMM_TTP_DATA_INDICATION: - ircomm_process_data(self, skb); - break; - case IRCOMM_LMP_DATA_INDICATION: - ircomm_data_indication(self, skb); - break; - case IRCOMM_CONTROL_REQUEST: - /* Just send a separate frame for now */ - ret = self->issue.data_request(self, skb, skb->len); - break; - case IRCOMM_TTP_DISCONNECT_INDICATION: - case IRCOMM_LMP_DISCONNECT_INDICATION: - ircomm_next_state(self, IRCOMM_IDLE); - ircomm_disconnect_indication(self, skb, info); - break; - case IRCOMM_DISCONNECT_REQUEST: - ircomm_next_state(self, IRCOMM_IDLE); - ret = self->issue.disconnect_request(self, skb, info); - break; - default: - pr_debug("%s(), unknown event = %s\n", __func__ , - ircomm_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_do_event (self, event, skb) - * - * Process event - * - */ -int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) -{ - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_state[self->state], ircomm_event[event]); - - return (*state[self->state])(self, event, skb, info); -} - -/* - * Function ircomm_next_state (self, state) - * - * Switch state - * - */ -void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state) -{ - self->state = state; - - pr_debug("%s: next state=%s, service type=%d\n", __func__ , - ircomm_state[self->state], self->service_type); -} diff --git a/drivers/staging/irda/net/ircomm/ircomm_lmp.c b/drivers/staging/irda/net/ircomm/ircomm_lmp.c deleted file mode 100644 index e4cc847bb933..000000000000 --- a/drivers/staging/irda/net/ircomm/ircomm_lmp.c +++ /dev/null @@ -1,350 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_lmp.c - * Version: 1.0 - * Description: Interface between IrCOMM and IrLMP - * Status: Stable - * Author: Dag Brattli - * Created at: Sun Jun 6 20:48:27 1999 - * Modified at: Sun Dec 12 13:44:17 1999 - * Modified by: Dag Brattli - * Sources: Previous IrLPT work by Thomas Davis - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include /* struct irda_skb_cb */ - -#include -#include - - -/* - * Function ircomm_lmp_connect_request (self, userdata) - * - * - * - */ -static int ircomm_lmp_connect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info) -{ - int ret = 0; - - /* Don't forget to refcount it - should be NULL anyway */ - if(userdata) - skb_get(userdata); - - ret = irlmp_connect_request(self->lsap, info->dlsap_sel, - info->saddr, info->daddr, NULL, userdata); - return ret; -} - -/* - * Function ircomm_lmp_connect_response (self, skb) - * - * - * - */ -static int ircomm_lmp_connect_response(struct ircomm_cb *self, - struct sk_buff *userdata) -{ - struct sk_buff *tx_skb; - - /* Any userdata supplied? */ - if (userdata == NULL) { - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX and LAP header */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - } else { - /* - * Check that the client has reserved enough space for - * headers - */ - IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER, - return -1;); - - /* Don't forget to refcount it - should be NULL anyway */ - skb_get(userdata); - tx_skb = userdata; - } - - return irlmp_connect_response(self->lsap, tx_skb); -} - -static int ircomm_lmp_disconnect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info) -{ - struct sk_buff *tx_skb; - int ret; - - if (!userdata) { - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX and LAP header */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - userdata = tx_skb; - } else { - /* Don't forget to refcount it - should be NULL anyway */ - skb_get(userdata); - } - - ret = irlmp_disconnect_request(self->lsap, userdata); - - return ret; -} - -/* - * Function ircomm_lmp_flow_control (skb) - * - * This function is called when a data frame we have sent to IrLAP has - * been deallocated. We do this to make sure we don't flood IrLAP with - * frames, since we are not using the IrTTP flow control mechanism - */ -static void ircomm_lmp_flow_control(struct sk_buff *skb) -{ - struct irda_skb_cb *cb; - struct ircomm_cb *self; - int line; - - IRDA_ASSERT(skb != NULL, return;); - - cb = (struct irda_skb_cb *) skb->cb; - - line = cb->line; - - self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL); - if (!self) { - pr_debug("%s(), didn't find myself\n", __func__); - return; - } - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - - self->pkt_count--; - - if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) { - pr_debug("%s(), asking TTY to start again!\n", __func__); - self->flow_status = FLOW_START; - if (self->notify.flow_indication) - self->notify.flow_indication(self->notify.instance, - self, FLOW_START); - } -} - -/* - * Function ircomm_lmp_data_request (self, userdata) - * - * Send data frame to peer device - * - */ -static int ircomm_lmp_data_request(struct ircomm_cb *self, - struct sk_buff *skb, - int not_used) -{ - struct irda_skb_cb *cb; - int ret; - - IRDA_ASSERT(skb != NULL, return -1;); - - cb = (struct irda_skb_cb *) skb->cb; - - cb->line = self->line; - - pr_debug("%s(), sending frame\n", __func__); - - /* Don't forget to refcount it - see ircomm_tty_do_softint() */ - skb_get(skb); - - skb_orphan(skb); - skb->destructor = ircomm_lmp_flow_control; - - if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) { - pr_debug("%s(), asking TTY to slow down!\n", __func__); - self->flow_status = FLOW_STOP; - if (self->notify.flow_indication) - self->notify.flow_indication(self->notify.instance, - self, FLOW_STOP); - } - ret = irlmp_data_request(self->lsap, skb); - if (ret) { - net_err_ratelimited("%s(), failed\n", __func__); - /* irlmp_data_request already free the packet */ - } - - return ret; -} - -/* - * Function ircomm_lmp_data_indication (instance, sap, skb) - * - * Incoming data which we must deliver to the state machine, to check - * we are still connected. - */ -static int ircomm_lmp_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL); - - /* Drop reference count - see ircomm_tty_data_indication(). */ - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size, - * max_header_size, skb) - * - * Connection has been confirmed by peer device - * - */ -static void ircomm_lmp_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_seg_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(qos != NULL, return;); - - info.max_data_size = max_seg_size; - info.max_header_size = max_header_size; - info.qos = qos; - - ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info); - - /* Drop reference count - see ircomm_tty_connect_confirm(). */ - dev_kfree_skb(skb); -} - -/* - * Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size, - * max_header_size, skb) - * - * Peer device wants to make a connection with us - * - */ -static void ircomm_lmp_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_seg_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *)instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(qos != NULL, return;); - - info.max_data_size = max_seg_size; - info.max_header_size = max_header_size; - info.qos = qos; - - ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info); - - /* Drop reference count - see ircomm_tty_connect_indication(). */ - dev_kfree_skb(skb); -} - -/* - * Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb) - * - * Peer device has closed the connection, or the link went down for some - * other reason - */ -static void ircomm_lmp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - - info.reason = reason; - - ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info); - - /* Drop reference count - see ircomm_tty_disconnect_indication(). */ - if(skb) - dev_kfree_skb(skb); -} -/* - * Function ircomm_open_lsap (self) - * - * Open LSAP. This function will only be used when using "raw" services - * - */ -int ircomm_open_lsap(struct ircomm_cb *self) -{ - notify_t notify; - - /* Register callbacks */ - irda_notify_init(¬ify); - notify.data_indication = ircomm_lmp_data_indication; - notify.connect_confirm = ircomm_lmp_connect_confirm; - notify.connect_indication = ircomm_lmp_connect_indication; - notify.disconnect_indication = ircomm_lmp_disconnect_indication; - notify.instance = self; - strlcpy(notify.name, "IrCOMM", sizeof(notify.name)); - - self->lsap = irlmp_open_lsap(LSAP_ANY, ¬ify, 0); - if (!self->lsap) { - pr_debug("%sfailed to allocate tsap\n", __func__); - return -1; - } - self->slsap_sel = self->lsap->slsap_sel; - - /* - * Initialize the call-table for issuing commands - */ - self->issue.data_request = ircomm_lmp_data_request; - self->issue.connect_request = ircomm_lmp_connect_request; - self->issue.connect_response = ircomm_lmp_connect_response; - self->issue.disconnect_request = ircomm_lmp_disconnect_request; - - return 0; -} diff --git a/drivers/staging/irda/net/ircomm/ircomm_param.c b/drivers/staging/irda/net/ircomm/ircomm_param.c deleted file mode 100644 index 5728e76ca6d5..000000000000 --- a/drivers/staging/irda/net/ircomm/ircomm_param.c +++ /dev/null @@ -1,501 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_param.c - * Version: 1.0 - * Description: Parameter handling for the IrCOMM protocol - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Jun 7 10:25:11 1999 - * Modified at: Sun Jan 30 14:32:03 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include - -static int ircomm_param_service_type(void *instance, irda_param_t *param, - int get); -static int ircomm_param_port_type(void *instance, irda_param_t *param, - int get); -static int ircomm_param_port_name(void *instance, irda_param_t *param, - int get); -static int ircomm_param_service_type(void *instance, irda_param_t *param, - int get); -static int ircomm_param_data_rate(void *instance, irda_param_t *param, - int get); -static int ircomm_param_data_format(void *instance, irda_param_t *param, - int get); -static int ircomm_param_flow_control(void *instance, irda_param_t *param, - int get); -static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get); -static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get); -static int ircomm_param_line_status(void *instance, irda_param_t *param, - int get); -static int ircomm_param_dte(void *instance, irda_param_t *param, int get); -static int ircomm_param_dce(void *instance, irda_param_t *param, int get); -static int ircomm_param_poll(void *instance, irda_param_t *param, int get); - -static const pi_minor_info_t pi_minor_call_table_common[] = { - { ircomm_param_service_type, PV_INT_8_BITS }, - { ircomm_param_port_type, PV_INT_8_BITS }, - { ircomm_param_port_name, PV_STRING } -}; -static const pi_minor_info_t pi_minor_call_table_non_raw[] = { - { ircomm_param_data_rate, PV_INT_32_BITS | PV_BIG_ENDIAN }, - { ircomm_param_data_format, PV_INT_8_BITS }, - { ircomm_param_flow_control, PV_INT_8_BITS }, - { ircomm_param_xon_xoff, PV_INT_16_BITS }, - { ircomm_param_enq_ack, PV_INT_16_BITS }, - { ircomm_param_line_status, PV_INT_8_BITS } -}; -static const pi_minor_info_t pi_minor_call_table_9_wire[] = { - { ircomm_param_dte, PV_INT_8_BITS }, - { ircomm_param_dce, PV_INT_8_BITS }, - { ircomm_param_poll, PV_NO_VALUE }, -}; - -static const pi_major_info_t pi_major_call_table[] = { - { pi_minor_call_table_common, 3 }, - { pi_minor_call_table_non_raw, 6 }, - { pi_minor_call_table_9_wire, 3 } -/* { pi_minor_call_table_centronics } */ -}; - -pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 }; - -/* - * Function ircomm_param_request (self, pi, flush) - * - * Queue a parameter for the control channel - * - */ -int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) -{ - unsigned long flags; - struct sk_buff *skb; - int count; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - /* Make sure we don't send parameters for raw mode */ - if (self->service_type == IRCOMM_3_WIRE_RAW) - return 0; - - spin_lock_irqsave(&self->spinlock, flags); - - skb = self->ctrl_skb; - if (!skb) { - skb = alloc_skb(256, GFP_ATOMIC); - if (!skb) { - spin_unlock_irqrestore(&self->spinlock, flags); - return -ENOMEM; - } - - skb_reserve(skb, self->max_header_size); - self->ctrl_skb = skb; - } - /* - * Inserting is a little bit tricky since we don't know how much - * room we will need. But this should hopefully work OK - */ - count = irda_param_insert(self, pi, skb_tail_pointer(skb), - skb_tailroom(skb), &ircomm_param_info); - if (count < 0) { - net_warn_ratelimited("%s(), no room for parameter!\n", - __func__); - spin_unlock_irqrestore(&self->spinlock, flags); - return -1; - } - skb_put(skb, count); - pr_debug("%s(), skb->len=%d\n", __func__, skb->len); - - spin_unlock_irqrestore(&self->spinlock, flags); - - if (flush) { - /* ircomm_tty_do_softint will take care of the rest */ - schedule_work(&self->tqueue); - } - - return count; -} - -/* - * Function ircomm_param_service_type (self, buf, len) - * - * Handle service type, this function will both be called after the LM-IAS - * query and then the remote device sends its initial parameters - * - */ -static int ircomm_param_service_type(void *instance, irda_param_t *param, - int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - __u8 service_type = (__u8) param->pv.i; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) { - param->pv.i = self->settings.service_type; - return 0; - } - - /* Find all common service types */ - service_type &= self->service_type; - if (!service_type) { - pr_debug("%s(), No common service type to use!\n", __func__); - return -1; - } - pr_debug("%s(), services in common=%02x\n", __func__ , - service_type); - - /* - * Now choose a preferred service type of those available - */ - if (service_type & IRCOMM_CENTRONICS) - self->settings.service_type = IRCOMM_CENTRONICS; - else if (service_type & IRCOMM_9_WIRE) - self->settings.service_type = IRCOMM_9_WIRE; - else if (service_type & IRCOMM_3_WIRE) - self->settings.service_type = IRCOMM_3_WIRE; - else if (service_type & IRCOMM_3_WIRE_RAW) - self->settings.service_type = IRCOMM_3_WIRE_RAW; - - pr_debug("%s(), resulting service type=0x%02x\n", __func__ , - self->settings.service_type); - - /* - * Now the line is ready for some communication. Check if we are a - * server, and send over some initial parameters. - * Client do it in ircomm_tty_state_setup(). - * Note : we may get called from ircomm_tty_getvalue_confirm(), - * therefore before we even have open any socket. And self->client - * is initialised to TRUE only later. So, we check if the link is - * really initialised. - Jean II - */ - if ((self->max_header_size != IRCOMM_TTY_HDR_UNINITIALISED) && - (!self->client) && - (self->settings.service_type != IRCOMM_3_WIRE_RAW)) - { - /* Init connection */ - ircomm_tty_send_initial_parameters(self); - ircomm_tty_link_established(self); - } - - return 0; -} - -/* - * Function ircomm_param_port_type (self, param) - * - * The port type parameter tells if the devices are serial or parallel. - * Since we only advertise serial service, this parameter should only - * be equal to IRCOMM_SERIAL. - */ -static int ircomm_param_port_type(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) - param->pv.i = IRCOMM_SERIAL; - else { - self->settings.port_type = (__u8) param->pv.i; - - pr_debug("%s(), port type=%d\n", __func__ , - self->settings.port_type); - } - return 0; -} - -/* - * Function ircomm_param_port_name (self, param) - * - * Exchange port name - * - */ -static int ircomm_param_port_name(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) { - pr_debug("%s(), not imp!\n", __func__); - } else { - pr_debug("%s(), port-name=%s\n", __func__ , param->pv.c); - strncpy(self->settings.port_name, param->pv.c, 32); - } - - return 0; -} - -/* - * Function ircomm_param_data_rate (self, param) - * - * Exchange data rate to be used in this settings - * - */ -static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) - param->pv.i = self->settings.data_rate; - else - self->settings.data_rate = param->pv.i; - - pr_debug("%s(), data rate = %d\n", __func__ , param->pv.i); - - return 0; -} - -/* - * Function ircomm_param_data_format (self, param) - * - * Exchange data format to be used in this settings - * - */ -static int ircomm_param_data_format(void *instance, irda_param_t *param, - int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) - param->pv.i = self->settings.data_format; - else - self->settings.data_format = (__u8) param->pv.i; - - return 0; -} - -/* - * Function ircomm_param_flow_control (self, param) - * - * Exchange flow control settings to be used in this settings - * - */ -static int ircomm_param_flow_control(void *instance, irda_param_t *param, - int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) - param->pv.i = self->settings.flow_control; - else - self->settings.flow_control = (__u8) param->pv.i; - - pr_debug("%s(), flow control = 0x%02x\n", __func__ , (__u8)param->pv.i); - - return 0; -} - -/* - * Function ircomm_param_xon_xoff (self, param) - * - * Exchange XON/XOFF characters - * - */ -static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) { - param->pv.i = self->settings.xonxoff[0]; - param->pv.i |= self->settings.xonxoff[1] << 8; - } else { - self->settings.xonxoff[0] = (__u16) param->pv.i & 0xff; - self->settings.xonxoff[1] = (__u16) param->pv.i >> 8; - } - - pr_debug("%s(), XON/XOFF = 0x%02x,0x%02x\n", __func__ , - param->pv.i & 0xff, param->pv.i >> 8); - - return 0; -} - -/* - * Function ircomm_param_enq_ack (self, param) - * - * Exchange ENQ/ACK characters - * - */ -static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) { - param->pv.i = self->settings.enqack[0]; - param->pv.i |= self->settings.enqack[1] << 8; - } else { - self->settings.enqack[0] = (__u16) param->pv.i & 0xff; - self->settings.enqack[1] = (__u16) param->pv.i >> 8; - } - - pr_debug("%s(), ENQ/ACK = 0x%02x,0x%02x\n", __func__ , - param->pv.i & 0xff, param->pv.i >> 8); - - return 0; -} - -/* - * Function ircomm_param_line_status (self, param) - * - * - * - */ -static int ircomm_param_line_status(void *instance, irda_param_t *param, - int get) -{ - pr_debug("%s(), not impl.\n", __func__); - - return 0; -} - -/* - * Function ircomm_param_dte (instance, param) - * - * If we get here, there must be some sort of null-modem connection, and - * we are probably working in server mode as well. - */ -static int ircomm_param_dte(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - __u8 dte; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) - param->pv.i = self->settings.dte; - else { - dte = (__u8) param->pv.i; - - self->settings.dce = 0; - - if (dte & IRCOMM_DELTA_DTR) - self->settings.dce |= (IRCOMM_DELTA_DSR| - IRCOMM_DELTA_RI | - IRCOMM_DELTA_CD); - if (dte & IRCOMM_DTR) - self->settings.dce |= (IRCOMM_DSR| - IRCOMM_RI | - IRCOMM_CD); - - if (dte & IRCOMM_DELTA_RTS) - self->settings.dce |= IRCOMM_DELTA_CTS; - if (dte & IRCOMM_RTS) - self->settings.dce |= IRCOMM_CTS; - - /* Take appropriate actions */ - ircomm_tty_check_modem_status(self); - - /* Null modem cable emulator */ - self->settings.null_modem = TRUE; - } - - return 0; -} - -/* - * Function ircomm_param_dce (instance, param) - * - * - * - */ -static int ircomm_param_dce(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - __u8 dce; - - pr_debug("%s(), dce = 0x%02x\n", __func__ , (__u8)param->pv.i); - - dce = (__u8) param->pv.i; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - self->settings.dce = dce; - - /* Check if any of the settings have changed */ - if (dce & 0x0f) { - if (dce & IRCOMM_DELTA_CTS) { - pr_debug("%s(), CTS\n", __func__); - } - } - - ircomm_tty_check_modem_status(self); - - return 0; -} - -/* - * Function ircomm_param_poll (instance, param) - * - * Called when the peer device is polling for the line settings - * - */ -static int ircomm_param_poll(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - /* Poll parameters are always of length 0 (just a signal) */ - if (!get) { - /* Respond with DTE line settings */ - ircomm_param_request(self, IRCOMM_DTE, TRUE); - } - return 0; -} - - - - - diff --git a/drivers/staging/irda/net/ircomm/ircomm_ttp.c b/drivers/staging/irda/net/ircomm/ircomm_ttp.c deleted file mode 100644 index 4b81e0934770..000000000000 --- a/drivers/staging/irda/net/ircomm/ircomm_ttp.c +++ /dev/null @@ -1,350 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_ttp.c - * Version: 1.0 - * Description: Interface between IrCOMM and IrTTP - * Status: Stable - * Author: Dag Brattli - * Created at: Sun Jun 6 20:48:27 1999 - * Modified at: Mon Dec 13 11:35:13 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include - -#include -#include -#include -#include - -#include -#include - -static int ircomm_ttp_data_indication(void *instance, void *sap, - struct sk_buff *skb); -static void ircomm_ttp_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb); -static void ircomm_ttp_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb); -static void ircomm_ttp_flow_indication(void *instance, void *sap, - LOCAL_FLOW cmd); -static void ircomm_ttp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb); -static int ircomm_ttp_data_request(struct ircomm_cb *self, - struct sk_buff *skb, - int clen); -static int ircomm_ttp_connect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info); -static int ircomm_ttp_connect_response(struct ircomm_cb *self, - struct sk_buff *userdata); -static int ircomm_ttp_disconnect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info); - -/* - * Function ircomm_open_tsap (self) - * - * - * - */ -int ircomm_open_tsap(struct ircomm_cb *self) -{ - notify_t notify; - - /* Register callbacks */ - irda_notify_init(¬ify); - notify.data_indication = ircomm_ttp_data_indication; - notify.connect_confirm = ircomm_ttp_connect_confirm; - notify.connect_indication = ircomm_ttp_connect_indication; - notify.flow_indication = ircomm_ttp_flow_indication; - notify.disconnect_indication = ircomm_ttp_disconnect_indication; - notify.instance = self; - strlcpy(notify.name, "IrCOMM", sizeof(notify.name)); - - self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, - ¬ify); - if (!self->tsap) { - pr_debug("%sfailed to allocate tsap\n", __func__); - return -1; - } - self->slsap_sel = self->tsap->stsap_sel; - - /* - * Initialize the call-table for issuing commands - */ - self->issue.data_request = ircomm_ttp_data_request; - self->issue.connect_request = ircomm_ttp_connect_request; - self->issue.connect_response = ircomm_ttp_connect_response; - self->issue.disconnect_request = ircomm_ttp_disconnect_request; - - return 0; -} - -/* - * Function ircomm_ttp_connect_request (self, userdata) - * - * - * - */ -static int ircomm_ttp_connect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info) -{ - int ret = 0; - - /* Don't forget to refcount it - should be NULL anyway */ - if(userdata) - skb_get(userdata); - - ret = irttp_connect_request(self->tsap, info->dlsap_sel, - info->saddr, info->daddr, NULL, - TTP_SAR_DISABLE, userdata); - - return ret; -} - -/* - * Function ircomm_ttp_connect_response (self, skb) - * - * - * - */ -static int ircomm_ttp_connect_response(struct ircomm_cb *self, - struct sk_buff *userdata) -{ - int ret; - - /* Don't forget to refcount it - should be NULL anyway */ - if(userdata) - skb_get(userdata); - - ret = irttp_connect_response(self->tsap, TTP_SAR_DISABLE, userdata); - - return ret; -} - -/* - * Function ircomm_ttp_data_request (self, userdata) - * - * Send IrCOMM data to IrTTP layer. Currently we do not try to combine - * control data with pure data, so they will be sent as separate frames. - * Should not be a big problem though, since control frames are rare. But - * some of them are sent after connection establishment, so this can - * increase the latency a bit. - */ -static int ircomm_ttp_data_request(struct ircomm_cb *self, - struct sk_buff *skb, - int clen) -{ - int ret; - - IRDA_ASSERT(skb != NULL, return -1;); - - pr_debug("%s(), clen=%d\n", __func__ , clen); - - /* - * Insert clen field, currently we either send data only, or control - * only frames, to make things easier and avoid queueing - */ - IRDA_ASSERT(skb_headroom(skb) >= IRCOMM_HEADER_SIZE, return -1;); - - /* Don't forget to refcount it - see ircomm_tty_do_softint() */ - skb_get(skb); - - skb_push(skb, IRCOMM_HEADER_SIZE); - - skb->data[0] = clen; - - ret = irttp_data_request(self->tsap, skb); - if (ret) { - net_err_ratelimited("%s(), failed\n", __func__); - /* irttp_data_request already free the packet */ - } - - return ret; -} - -/* - * Function ircomm_ttp_data_indication (instance, sap, skb) - * - * Incoming data - * - */ -static int ircomm_ttp_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - ircomm_do_event(self, IRCOMM_TTP_DATA_INDICATION, skb, NULL); - - /* Drop reference count - see ircomm_tty_data_indication(). */ - dev_kfree_skb(skb); - - return 0; -} - -static void ircomm_ttp_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(qos != NULL, goto out;); - - if (max_sdu_size != TTP_SAR_DISABLE) { - net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n", - __func__); - goto out; - } - - info.max_data_size = irttp_get_max_seg_size(self->tsap) - - IRCOMM_HEADER_SIZE; - info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE; - info.qos = qos; - - ircomm_do_event(self, IRCOMM_TTP_CONNECT_CONFIRM, skb, &info); - -out: - /* Drop reference count - see ircomm_tty_connect_confirm(). */ - dev_kfree_skb(skb); -} - -/* - * Function ircomm_ttp_connect_indication (instance, sap, qos, max_sdu_size, - * max_header_size, skb) - * - * - * - */ -static void ircomm_ttp_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *)instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(qos != NULL, goto out;); - - if (max_sdu_size != TTP_SAR_DISABLE) { - net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n", - __func__); - goto out; - } - - info.max_data_size = irttp_get_max_seg_size(self->tsap) - - IRCOMM_HEADER_SIZE; - info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE; - info.qos = qos; - - ircomm_do_event(self, IRCOMM_TTP_CONNECT_INDICATION, skb, &info); - -out: - /* Drop reference count - see ircomm_tty_connect_indication(). */ - dev_kfree_skb(skb); -} - -/* - * Function ircomm_ttp_disconnect_request (self, userdata, info) - * - * - * - */ -static int ircomm_ttp_disconnect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info) -{ - int ret; - - /* Don't forget to refcount it - should be NULL anyway */ - if(userdata) - skb_get(userdata); - - ret = irttp_disconnect_request(self->tsap, userdata, P_NORMAL); - - return ret; -} - -/* - * Function ircomm_ttp_disconnect_indication (instance, sap, reason, skb) - * - * - * - */ -static void ircomm_ttp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - - info.reason = reason; - - ircomm_do_event(self, IRCOMM_TTP_DISCONNECT_INDICATION, skb, &info); - - /* Drop reference count - see ircomm_tty_disconnect_indication(). */ - if(skb) - dev_kfree_skb(skb); -} - -/* - * Function ircomm_ttp_flow_indication (instance, sap, cmd) - * - * Layer below is telling us to start or stop the flow of data - * - */ -static void ircomm_ttp_flow_indication(void *instance, void *sap, - LOCAL_FLOW cmd) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - - if (self->notify.flow_indication) - self->notify.flow_indication(self->notify.instance, self, cmd); -} - - diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty.c b/drivers/staging/irda/net/ircomm/ircomm_tty.c deleted file mode 100644 index 473abfaffe7b..000000000000 --- a/drivers/staging/irda/net/ircomm/ircomm_tty.c +++ /dev/null @@ -1,1329 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_tty.c - * Version: 1.0 - * Description: IrCOMM serial TTY driver - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Jun 6 21:00:56 1999 - * Modified at: Wed Feb 23 00:09:02 2000 - * Modified by: Dag Brattli - * Sources: serial.c and previous IrCOMM work by Takahide Higuchi - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for MODULE_ALIAS_CHARDEV_MAJOR */ - -#include - -#include -#include - -#include -#include -#include -#include - -static int ircomm_tty_install(struct tty_driver *driver, - struct tty_struct *tty); -static int ircomm_tty_open(struct tty_struct *tty, struct file *filp); -static void ircomm_tty_close(struct tty_struct * tty, struct file *filp); -static int ircomm_tty_write(struct tty_struct * tty, - const unsigned char *buf, int count); -static int ircomm_tty_write_room(struct tty_struct *tty); -static void ircomm_tty_throttle(struct tty_struct *tty); -static void ircomm_tty_unthrottle(struct tty_struct *tty); -static int ircomm_tty_chars_in_buffer(struct tty_struct *tty); -static void ircomm_tty_flush_buffer(struct tty_struct *tty); -static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch); -static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout); -static void ircomm_tty_hangup(struct tty_struct *tty); -static void ircomm_tty_do_softint(struct work_struct *work); -static void ircomm_tty_shutdown(struct ircomm_tty_cb *self); -static void ircomm_tty_stop(struct tty_struct *tty); - -static int ircomm_tty_data_indication(void *instance, void *sap, - struct sk_buff *skb); -static int ircomm_tty_control_indication(void *instance, void *sap, - struct sk_buff *skb); -static void ircomm_tty_flow_indication(void *instance, void *sap, - LOCAL_FLOW cmd); -#ifdef CONFIG_PROC_FS -static const struct file_operations ircomm_tty_proc_fops; -#endif /* CONFIG_PROC_FS */ -static struct tty_driver *driver; - -static hashbin_t *ircomm_tty = NULL; - -static const struct tty_operations ops = { - .install = ircomm_tty_install, - .open = ircomm_tty_open, - .close = ircomm_tty_close, - .write = ircomm_tty_write, - .write_room = ircomm_tty_write_room, - .chars_in_buffer = ircomm_tty_chars_in_buffer, - .flush_buffer = ircomm_tty_flush_buffer, - .ioctl = ircomm_tty_ioctl, /* ircomm_tty_ioctl.c */ - .tiocmget = ircomm_tty_tiocmget, /* ircomm_tty_ioctl.c */ - .tiocmset = ircomm_tty_tiocmset, /* ircomm_tty_ioctl.c */ - .throttle = ircomm_tty_throttle, - .unthrottle = ircomm_tty_unthrottle, - .send_xchar = ircomm_tty_send_xchar, - .set_termios = ircomm_tty_set_termios, - .stop = ircomm_tty_stop, - .start = ircomm_tty_start, - .hangup = ircomm_tty_hangup, - .wait_until_sent = ircomm_tty_wait_until_sent, -#ifdef CONFIG_PROC_FS - .proc_fops = &ircomm_tty_proc_fops, -#endif /* CONFIG_PROC_FS */ -}; - -static void ircomm_port_raise_dtr_rts(struct tty_port *port, int raise) -{ - struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb, - port); - /* - * Here, we use to lock those two guys, but as ircomm_param_request() - * does it itself, I don't see the point (and I see the deadlock). - * Jean II - */ - if (raise) - self->settings.dte |= IRCOMM_RTS | IRCOMM_DTR; - else - self->settings.dte &= ~(IRCOMM_RTS | IRCOMM_DTR); - - ircomm_param_request(self, IRCOMM_DTE, TRUE); -} - -static int ircomm_port_carrier_raised(struct tty_port *port) -{ - struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb, - port); - return self->settings.dce & IRCOMM_CD; -} - -static const struct tty_port_operations ircomm_port_ops = { - .dtr_rts = ircomm_port_raise_dtr_rts, - .carrier_raised = ircomm_port_carrier_raised, -}; - -/* - * Function ircomm_tty_init() - * - * Init IrCOMM TTY layer/driver - * - */ -static int __init ircomm_tty_init(void) -{ - driver = alloc_tty_driver(IRCOMM_TTY_PORTS); - if (!driver) - return -ENOMEM; - ircomm_tty = hashbin_new(HB_LOCK); - if (ircomm_tty == NULL) { - net_err_ratelimited("%s(), can't allocate hashbin!\n", - __func__); - put_tty_driver(driver); - return -ENOMEM; - } - - driver->driver_name = "ircomm"; - driver->name = "ircomm"; - driver->major = IRCOMM_TTY_MAJOR; - driver->minor_start = IRCOMM_TTY_MINOR; - driver->type = TTY_DRIVER_TYPE_SERIAL; - driver->subtype = SERIAL_TYPE_NORMAL; - driver->init_termios = tty_std_termios; - driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(driver, &ops); - if (tty_register_driver(driver)) { - net_err_ratelimited("%s(): Couldn't register serial driver\n", - __func__); - put_tty_driver(driver); - return -1; - } - return 0; -} - -static void __exit __ircomm_tty_cleanup(struct ircomm_tty_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - ircomm_tty_shutdown(self); - - self->magic = 0; - tty_port_destroy(&self->port); - kfree(self); -} - -/* - * Function ircomm_tty_cleanup () - * - * Remove IrCOMM TTY layer/driver - * - */ -static void __exit ircomm_tty_cleanup(void) -{ - int ret; - - ret = tty_unregister_driver(driver); - if (ret) { - net_err_ratelimited("%s(), failed to unregister driver\n", - __func__); - return; - } - - hashbin_delete(ircomm_tty, (FREE_FUNC) __ircomm_tty_cleanup); - put_tty_driver(driver); -} - -/* - * Function ircomm_startup (self) - * - * - * - */ -static int ircomm_tty_startup(struct ircomm_tty_cb *self) -{ - notify_t notify; - int ret = -ENODEV; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - /* Check if already open */ - if (tty_port_initialized(&self->port)) { - pr_debug("%s(), already open so break out!\n", __func__); - return 0; - } - tty_port_set_initialized(&self->port, 1); - - /* Register with IrCOMM */ - irda_notify_init(¬ify); - /* These callbacks we must handle ourselves */ - notify.data_indication = ircomm_tty_data_indication; - notify.udata_indication = ircomm_tty_control_indication; - notify.flow_indication = ircomm_tty_flow_indication; - - /* Use the ircomm_tty interface for these ones */ - notify.disconnect_indication = ircomm_tty_disconnect_indication; - notify.connect_confirm = ircomm_tty_connect_confirm; - notify.connect_indication = ircomm_tty_connect_indication; - strlcpy(notify.name, "ircomm_tty", sizeof(notify.name)); - notify.instance = self; - - if (!self->ircomm) { - self->ircomm = ircomm_open(¬ify, self->service_type, - self->line); - } - if (!self->ircomm) - goto err; - - self->slsap_sel = self->ircomm->slsap_sel; - - /* Connect IrCOMM link with remote device */ - ret = ircomm_tty_attach_cable(self); - if (ret < 0) { - net_err_ratelimited("%s(), error attaching cable!\n", __func__); - goto err; - } - - return 0; -err: - tty_port_set_initialized(&self->port, 0); - return ret; -} - -/* - * Function ircomm_block_til_ready (self, filp) - * - * - * - */ -static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, - struct tty_struct *tty, struct file *filp) -{ - struct tty_port *port = &self->port; - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - unsigned long flags; - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if (tty_io_error(tty)) { - tty_port_set_active(port, 1); - return 0; - } - - if (filp->f_flags & O_NONBLOCK) { - /* nonblock mode is set */ - if (C_BAUD(tty)) - tty_port_raise_dtr_rts(port); - tty_port_set_active(port, 1); - pr_debug("%s(), O_NONBLOCK requested!\n", __func__); - return 0; - } - - if (C_CLOCAL(tty)) { - pr_debug("%s(), doing CLOCAL!\n", __func__); - do_clocal = 1; - } - - /* Wait for carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, port->count is dropped by one, so that - * mgsl_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - - retval = 0; - add_wait_queue(&port->open_wait, &wait); - - pr_debug("%s(%d):block_til_ready before block on %s open_count=%d\n", - __FILE__, __LINE__, tty->driver->name, port->count); - - spin_lock_irqsave(&port->lock, flags); - port->count--; - port->blocked_open++; - spin_unlock_irqrestore(&port->lock, flags); - - while (1) { - if (C_BAUD(tty) && tty_port_initialized(port)) - tty_port_raise_dtr_rts(port); - - set_current_state(TASK_INTERRUPTIBLE); - - if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { - retval = (port->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS; - break; - } - - /* - * Check if link is ready now. Even if CLOCAL is - * specified, we cannot return before the IrCOMM link is - * ready - */ - if ((do_clocal || tty_port_carrier_raised(port)) && - self->state == IRCOMM_TTY_READY) - { - break; - } - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - pr_debug("%s(%d):block_til_ready blocking on %s open_count=%d\n", - __FILE__, __LINE__, tty->driver->name, port->count); - - schedule(); - } - - __set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); - - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->count++; - port->blocked_open--; - spin_unlock_irqrestore(&port->lock, flags); - - pr_debug("%s(%d):block_til_ready after blocking on %s open_count=%d\n", - __FILE__, __LINE__, tty->driver->name, port->count); - - if (!retval) - tty_port_set_active(port, 1); - - return retval; -} - - -static int ircomm_tty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct ircomm_tty_cb *self; - unsigned int line = tty->index; - - /* Check if instance already exists */ - self = hashbin_lock_find(ircomm_tty, line, NULL); - if (!self) { - /* No, so make new instance */ - self = kzalloc(sizeof(struct ircomm_tty_cb), GFP_KERNEL); - if (self == NULL) - return -ENOMEM; - - tty_port_init(&self->port); - self->port.ops = &ircomm_port_ops; - self->magic = IRCOMM_TTY_MAGIC; - self->flow = FLOW_STOP; - - self->line = line; - INIT_WORK(&self->tqueue, ircomm_tty_do_softint); - self->max_header_size = IRCOMM_TTY_HDR_UNINITIALISED; - self->max_data_size = IRCOMM_TTY_DATA_UNINITIALISED; - - /* Init some important stuff */ - timer_setup(&self->watchdog_timer, NULL, 0); - spin_lock_init(&self->spinlock); - - /* - * Force TTY into raw mode by default which is usually what - * we want for IrCOMM and IrLPT. This way applications will - * not have to twiddle with printcap etc. - * - * Note this is completely usafe and doesn't work properly - */ - tty->termios.c_iflag = 0; - tty->termios.c_oflag = 0; - - /* Insert into hash */ - hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL); - } - - tty->driver_data = self; - - return tty_port_install(&self->port, driver, tty); -} - -/* - * Function ircomm_tty_open (tty, filp) - * - * This routine is called when a particular tty device is opened. This - * routine is mandatory; if this routine is not filled in, the attempted - * open will fail with ENODEV. - */ -static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) -{ - struct ircomm_tty_cb *self = tty->driver_data; - unsigned long flags; - int ret; - - /* ++ is not atomic, so this should be protected - Jean II */ - spin_lock_irqsave(&self->port.lock, flags); - self->port.count++; - spin_unlock_irqrestore(&self->port.lock, flags); - tty_port_tty_set(&self->port, tty); - - pr_debug("%s(), %s%d, count = %d\n", __func__ , tty->driver->name, - self->line, self->port.count); - - /* Not really used by us, but lets do it anyway */ - self->port.low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; - - /* Check if this is a "normal" ircomm device, or an irlpt device */ - if (self->line < 0x10) { - self->service_type = IRCOMM_3_WIRE | IRCOMM_9_WIRE; - self->settings.service_type = IRCOMM_9_WIRE; /* 9 wire as default */ - /* Jan Kiszka -> add DSR/RI -> Conform to IrCOMM spec */ - self->settings.dce = IRCOMM_CTS | IRCOMM_CD | IRCOMM_DSR | IRCOMM_RI; /* Default line settings */ - pr_debug("%s(), IrCOMM device\n", __func__); - } else { - pr_debug("%s(), IrLPT device\n", __func__); - self->service_type = IRCOMM_3_WIRE_RAW; - self->settings.service_type = IRCOMM_3_WIRE_RAW; /* Default */ - } - - ret = ircomm_tty_startup(self); - if (ret) - return ret; - - ret = ircomm_tty_block_til_ready(self, tty, filp); - if (ret) { - pr_debug("%s(), returning after block_til_ready with %d\n", - __func__, ret); - - return ret; - } - return 0; -} - -/* - * Function ircomm_tty_close (tty, filp) - * - * This routine is called when a particular tty device is closed. - * - */ -static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - struct tty_port *port = &self->port; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - if (tty_port_close_start(port, tty, filp) == 0) - return; - - ircomm_tty_shutdown(self); - - tty_driver_flush_buffer(tty); - - tty_port_close_end(port, tty); - tty_port_tty_set(port, NULL); -} - -/* - * Function ircomm_tty_flush_buffer (tty) - * - * - * - */ -static void ircomm_tty_flush_buffer(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* - * Let do_softint() do this to avoid race condition with - * do_softint() ;-) - */ - schedule_work(&self->tqueue); -} - -/* - * Function ircomm_tty_do_softint (work) - * - * We use this routine to give the write wakeup to the user at at a - * safe time (as fast as possible after write have completed). This - * can be compared to the Tx interrupt. - */ -static void ircomm_tty_do_softint(struct work_struct *work) -{ - struct ircomm_tty_cb *self = - container_of(work, struct ircomm_tty_cb, tqueue); - struct tty_struct *tty; - unsigned long flags; - struct sk_buff *skb, *ctrl_skb; - - if (!self || self->magic != IRCOMM_TTY_MAGIC) - return; - - tty = tty_port_tty_get(&self->port); - if (!tty) - return; - - /* Unlink control buffer */ - spin_lock_irqsave(&self->spinlock, flags); - - ctrl_skb = self->ctrl_skb; - self->ctrl_skb = NULL; - - spin_unlock_irqrestore(&self->spinlock, flags); - - /* Flush control buffer if any */ - if(ctrl_skb) { - if(self->flow == FLOW_START) - ircomm_control_request(self->ircomm, ctrl_skb); - /* Drop reference count - see ircomm_ttp_data_request(). */ - dev_kfree_skb(ctrl_skb); - } - - if (tty->hw_stopped) - goto put; - - /* Unlink transmit buffer */ - spin_lock_irqsave(&self->spinlock, flags); - - skb = self->tx_skb; - self->tx_skb = NULL; - - spin_unlock_irqrestore(&self->spinlock, flags); - - /* Flush transmit buffer if any */ - if (skb) { - ircomm_tty_do_event(self, IRCOMM_TTY_DATA_REQUEST, skb, NULL); - /* Drop reference count - see ircomm_ttp_data_request(). */ - dev_kfree_skb(skb); - } - - /* Check if user (still) wants to be waken up */ - tty_wakeup(tty); -put: - tty_kref_put(tty); -} - -/* - * Function ircomm_tty_write (tty, buf, count) - * - * This routine is called by the kernel to write a series of characters - * to the tty device. The characters may come from user space or kernel - * space. This routine will return the number of characters actually - * accepted for writing. This routine is mandatory. - */ -static int ircomm_tty_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned long flags; - struct sk_buff *skb; - int tailroom = 0; - int len = 0; - int size; - - pr_debug("%s(), count=%d, hw_stopped=%d\n", __func__ , count, - tty->hw_stopped); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - /* We may receive packets from the TTY even before we have finished - * our setup. Not cool. - * The problem is that we don't know the final header and data size - * to create the proper skb, so any skb we would create would have - * bogus header and data size, so need care. - * We use a bogus header size to safely detect this condition. - * Another problem is that hw_stopped was set to 0 way before it - * should be, so we would drop this skb. It should now be fixed. - * One option is to not accept data until we are properly setup. - * But, I suspect that when it happens, the ppp line discipline - * just "drops" the data, which might screw up connect scripts. - * The second option is to create a "safe skb", with large header - * and small size (see ircomm_tty_open() for values). - * We just need to make sure that when the real values get filled, - * we don't mess up the original "safe skb" (see tx_data_size). - * Jean II */ - if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) { - pr_debug("%s() : not initialised\n", __func__); -#ifdef IRCOMM_NO_TX_BEFORE_INIT - /* We didn't consume anything, TTY will retry */ - return 0; -#endif - } - - if (count < 1) - return 0; - - /* Protect our manipulation of self->tx_skb and related */ - spin_lock_irqsave(&self->spinlock, flags); - - /* Fetch current transmit buffer */ - skb = self->tx_skb; - - /* - * Send out all the data we get, possibly as multiple fragmented - * frames, but this will only happen if the data is larger than the - * max data size. The normal case however is just the opposite, and - * this function may be called multiple times, and will then actually - * defragment the data and send it out as one packet as soon as - * possible, but at a safer point in time - */ - while (count) { - size = count; - - /* Adjust data size to the max data size */ - if (size > self->max_data_size) - size = self->max_data_size; - - /* - * Do we already have a buffer ready for transmit, or do - * we need to allocate a new frame - */ - if (skb) { - /* - * Any room for more data at the end of the current - * transmit buffer? Cannot use skb_tailroom, since - * dev_alloc_skb gives us a larger skb than we - * requested - * Note : use tx_data_size, because max_data_size - * may have changed and we don't want to overwrite - * the skb. - Jean II - */ - if ((tailroom = (self->tx_data_size - skb->len)) > 0) { - /* Adjust data to tailroom */ - if (size > tailroom) - size = tailroom; - } else { - /* - * Current transmit frame is full, so break - * out, so we can send it as soon as possible - */ - break; - } - } else { - /* Prepare a full sized frame */ - skb = alloc_skb(self->max_data_size+ - self->max_header_size, - GFP_ATOMIC); - if (!skb) { - spin_unlock_irqrestore(&self->spinlock, flags); - return -ENOBUFS; - } - skb_reserve(skb, self->max_header_size); - self->tx_skb = skb; - /* Remember skb size because max_data_size may - * change later on - Jean II */ - self->tx_data_size = self->max_data_size; - } - - /* Copy data */ - skb_put_data(skb, buf + len, size); - - count -= size; - len += size; - } - - spin_unlock_irqrestore(&self->spinlock, flags); - - /* - * Schedule a new thread which will transmit the frame as soon - * as possible, but at a safe point in time. We do this so the - * "user" can give us data multiple times, as PPP does (because of - * its 256 byte tx buffer). We will then defragment and send out - * all this data as one single packet. - */ - schedule_work(&self->tqueue); - - return len; -} - -/* - * Function ircomm_tty_write_room (tty) - * - * This routine returns the numbers of characters the tty driver will - * accept for queuing to be written. This number is subject to change as - * output buffers get emptied, or if the output flow control is acted. - */ -static int ircomm_tty_write_room(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned long flags; - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - -#ifdef IRCOMM_NO_TX_BEFORE_INIT - /* max_header_size tells us if the channel is initialised or not. */ - if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) - /* Don't bother us yet */ - return 0; -#endif - - /* Check if we are allowed to transmit any data. - * hw_stopped is the regular flow control. - * Jean II */ - if (tty->hw_stopped) - ret = 0; - else { - spin_lock_irqsave(&self->spinlock, flags); - if (self->tx_skb) - ret = self->tx_data_size - self->tx_skb->len; - else - ret = self->max_data_size; - spin_unlock_irqrestore(&self->spinlock, flags); - } - pr_debug("%s(), ret=%d\n", __func__ , ret); - - return ret; -} - -/* - * Function ircomm_tty_wait_until_sent (tty, timeout) - * - * This routine waits until the device has written out all of the - * characters in its transmitter FIFO. - */ -static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned long orig_jiffies, poll_time; - unsigned long flags; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - orig_jiffies = jiffies; - - /* Set poll time to 200 ms */ - poll_time = msecs_to_jiffies(200); - if (timeout) - poll_time = min_t(unsigned long, timeout, poll_time); - - spin_lock_irqsave(&self->spinlock, flags); - while (self->tx_skb && self->tx_skb->len) { - spin_unlock_irqrestore(&self->spinlock, flags); - schedule_timeout_interruptible(poll_time); - spin_lock_irqsave(&self->spinlock, flags); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - spin_unlock_irqrestore(&self->spinlock, flags); - __set_current_state(TASK_RUNNING); -} - -/* - * Function ircomm_tty_throttle (tty) - * - * This routine notifies the tty driver that input buffers for the line - * discipline are close to full, and it should somehow signal that no - * more characters should be sent to the tty. - */ -static void ircomm_tty_throttle(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* Software flow control? */ - if (I_IXOFF(tty)) - ircomm_tty_send_xchar(tty, STOP_CHAR(tty)); - - /* Hardware flow control? */ - if (C_CRTSCTS(tty)) { - self->settings.dte &= ~IRCOMM_RTS; - self->settings.dte |= IRCOMM_DELTA_RTS; - - ircomm_param_request(self, IRCOMM_DTE, TRUE); - } - - ircomm_flow_request(self->ircomm, FLOW_STOP); -} - -/* - * Function ircomm_tty_unthrottle (tty) - * - * This routine notifies the tty drivers that it should signals that - * characters can now be sent to the tty without fear of overrunning the - * input buffers of the line disciplines. - */ -static void ircomm_tty_unthrottle(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* Using software flow control? */ - if (I_IXOFF(tty)) - ircomm_tty_send_xchar(tty, START_CHAR(tty)); - - /* Using hardware flow control? */ - if (C_CRTSCTS(tty)) { - self->settings.dte |= (IRCOMM_RTS|IRCOMM_DELTA_RTS); - - ircomm_param_request(self, IRCOMM_DTE, TRUE); - pr_debug("%s(), FLOW_START\n", __func__); - } - ircomm_flow_request(self->ircomm, FLOW_START); -} - -/* - * Function ircomm_tty_chars_in_buffer (tty) - * - * Indicates if there are any data in the buffer - * - */ -static int ircomm_tty_chars_in_buffer(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned long flags; - int len = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - spin_lock_irqsave(&self->spinlock, flags); - - if (self->tx_skb) - len = self->tx_skb->len; - - spin_unlock_irqrestore(&self->spinlock, flags); - - return len; -} - -static void ircomm_tty_shutdown(struct ircomm_tty_cb *self) -{ - unsigned long flags; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - if (!tty_port_initialized(&self->port)) - return; - tty_port_set_initialized(&self->port, 0); - - ircomm_tty_detach_cable(self); - - spin_lock_irqsave(&self->spinlock, flags); - - del_timer(&self->watchdog_timer); - - /* Free parameter buffer */ - if (self->ctrl_skb) { - dev_kfree_skb(self->ctrl_skb); - self->ctrl_skb = NULL; - } - - /* Free transmit buffer */ - if (self->tx_skb) { - dev_kfree_skb(self->tx_skb); - self->tx_skb = NULL; - } - - if (self->ircomm) { - ircomm_close(self->ircomm); - self->ircomm = NULL; - } - - spin_unlock_irqrestore(&self->spinlock, flags); -} - -/* - * Function ircomm_tty_hangup (tty) - * - * This routine notifies the tty driver that it should hangup the tty - * device. - * - */ -static void ircomm_tty_hangup(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - struct tty_port *port = &self->port; - unsigned long flags; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* ircomm_tty_flush_buffer(tty); */ - ircomm_tty_shutdown(self); - - spin_lock_irqsave(&port->lock, flags); - if (port->tty) { - set_bit(TTY_IO_ERROR, &port->tty->flags); - tty_kref_put(port->tty); - } - port->tty = NULL; - port->count = 0; - spin_unlock_irqrestore(&port->lock, flags); - tty_port_set_active(port, 0); - - wake_up_interruptible(&port->open_wait); -} - -/* - * Function ircomm_tty_send_xchar (tty, ch) - * - * This routine is used to send a high-priority XON/XOFF character to - * the device. - */ -static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch) -{ - pr_debug("%s(), not impl\n", __func__); -} - -/* - * Function ircomm_tty_start (tty) - * - * This routine notifies the tty driver that it resume sending - * characters to the tty device. - */ -void ircomm_tty_start(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - ircomm_flow_request(self->ircomm, FLOW_START); -} - -/* - * Function ircomm_tty_stop (tty) - * - * This routine notifies the tty driver that it should stop outputting - * characters to the tty device. - */ -static void ircomm_tty_stop(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - ircomm_flow_request(self->ircomm, FLOW_STOP); -} - -/* - * Function ircomm_check_modem_status (self) - * - * Check for any changes in the DCE's line settings. This function should - * be called whenever the dce parameter settings changes, to update the - * flow control settings and other things - */ -void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) -{ - struct tty_struct *tty; - int status; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - tty = tty_port_tty_get(&self->port); - - status = self->settings.dce; - - if (status & IRCOMM_DCE_DELTA_ANY) { - /*wake_up_interruptible(&self->delta_msr_wait);*/ - } - if (tty_port_check_carrier(&self->port) && (status & IRCOMM_DELTA_CD)) { - pr_debug("%s(), ircomm%d CD now %s...\n", __func__ , self->line, - (status & IRCOMM_CD) ? "on" : "off"); - - if (status & IRCOMM_CD) { - wake_up_interruptible(&self->port.open_wait); - } else { - pr_debug("%s(), Doing serial hangup..\n", __func__); - if (tty) - tty_hangup(tty); - - /* Hangup will remote the tty, so better break out */ - goto put; - } - } - if (tty && tty_port_cts_enabled(&self->port)) { - if (tty->hw_stopped) { - if (status & IRCOMM_CTS) { - pr_debug("%s(), CTS tx start...\n", __func__); - tty->hw_stopped = 0; - - /* Wake up processes blocked on open */ - wake_up_interruptible(&self->port.open_wait); - - schedule_work(&self->tqueue); - goto put; - } - } else { - if (!(status & IRCOMM_CTS)) { - pr_debug("%s(), CTS tx stop...\n", __func__); - tty->hw_stopped = 1; - } - } - } -put: - tty_kref_put(tty); -} - -/* - * Function ircomm_tty_data_indication (instance, sap, skb) - * - * Handle incoming data, and deliver it to the line discipline - * - */ -static int ircomm_tty_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - struct tty_struct *tty; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - tty = tty_port_tty_get(&self->port); - if (!tty) { - pr_debug("%s(), no tty!\n", __func__); - return 0; - } - - /* - * If we receive data when hardware is stopped then something is wrong. - * We try to poll the peers line settings to check if we are up todate. - * Devices like WinCE can do this, and since they don't send any - * params, we can just as well declare the hardware for running. - */ - if (tty->hw_stopped && (self->flow == FLOW_START)) { - pr_debug("%s(), polling for line settings!\n", __func__); - ircomm_param_request(self, IRCOMM_POLL, TRUE); - - /* We can just as well declare the hardware for running */ - ircomm_tty_send_initial_parameters(self); - ircomm_tty_link_established(self); - } - tty_kref_put(tty); - - /* - * Use flip buffer functions since the code may be called from interrupt - * context - */ - tty_insert_flip_string(&self->port, skb->data, skb->len); - tty_flip_buffer_push(&self->port); - - /* No need to kfree_skb - see ircomm_ttp_data_indication() */ - - return 0; -} - -/* - * Function ircomm_tty_control_indication (instance, sap, skb) - * - * Parse all incoming parameters (easy!) - * - */ -static int ircomm_tty_control_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - int clen; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - clen = skb->data[0]; - - irda_param_extract_all(self, skb->data+1, IRDA_MIN(skb->len-1, clen), - &ircomm_param_info); - - /* No need to kfree_skb - see ircomm_control_indication() */ - - return 0; -} - -/* - * Function ircomm_tty_flow_indication (instance, sap, cmd) - * - * This function is called by IrTTP when it wants us to slow down the - * transmission of data. We just mark the hardware as stopped, and wait - * for IrTTP to notify us that things are OK again. - */ -static void ircomm_tty_flow_indication(void *instance, void *sap, - LOCAL_FLOW cmd) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - struct tty_struct *tty; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - tty = tty_port_tty_get(&self->port); - - switch (cmd) { - case FLOW_START: - pr_debug("%s(), hw start!\n", __func__); - if (tty) - tty->hw_stopped = 0; - - /* ircomm_tty_do_softint will take care of the rest */ - schedule_work(&self->tqueue); - break; - default: /* If we get here, something is very wrong, better stop */ - case FLOW_STOP: - pr_debug("%s(), hw stopped!\n", __func__); - if (tty) - tty->hw_stopped = 1; - break; - } - - tty_kref_put(tty); - self->flow = cmd; -} - -#ifdef CONFIG_PROC_FS -static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m) -{ - struct tty_struct *tty; - char sep; - - seq_printf(m, "State: %s\n", ircomm_tty_state[self->state]); - - seq_puts(m, "Service type: "); - if (self->service_type & IRCOMM_9_WIRE) - seq_puts(m, "9_WIRE"); - else if (self->service_type & IRCOMM_3_WIRE) - seq_puts(m, "3_WIRE"); - else if (self->service_type & IRCOMM_3_WIRE_RAW) - seq_puts(m, "3_WIRE_RAW"); - else - seq_puts(m, "No common service type!\n"); - seq_putc(m, '\n'); - - seq_printf(m, "Port name: %s\n", self->settings.port_name); - - seq_printf(m, "DTE status:"); - sep = ' '; - if (self->settings.dte & IRCOMM_RTS) { - seq_printf(m, "%cRTS", sep); - sep = '|'; - } - if (self->settings.dte & IRCOMM_DTR) { - seq_printf(m, "%cDTR", sep); - sep = '|'; - } - seq_putc(m, '\n'); - - seq_puts(m, "DCE status:"); - sep = ' '; - if (self->settings.dce & IRCOMM_CTS) { - seq_printf(m, "%cCTS", sep); - sep = '|'; - } - if (self->settings.dce & IRCOMM_DSR) { - seq_printf(m, "%cDSR", sep); - sep = '|'; - } - if (self->settings.dce & IRCOMM_CD) { - seq_printf(m, "%cCD", sep); - sep = '|'; - } - if (self->settings.dce & IRCOMM_RI) { - seq_printf(m, "%cRI", sep); - sep = '|'; - } - seq_putc(m, '\n'); - - seq_puts(m, "Configuration: "); - if (!self->settings.null_modem) - seq_puts(m, "DTE <-> DCE\n"); - else - seq_puts(m, "DTE <-> DTE (null modem emulation)\n"); - - seq_printf(m, "Data rate: %d\n", self->settings.data_rate); - - seq_puts(m, "Flow control:"); - sep = ' '; - if (self->settings.flow_control & IRCOMM_XON_XOFF_IN) { - seq_printf(m, "%cXON_XOFF_IN", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_XON_XOFF_OUT) { - seq_printf(m, "%cXON_XOFF_OUT", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_RTS_CTS_IN) { - seq_printf(m, "%cRTS_CTS_IN", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_RTS_CTS_OUT) { - seq_printf(m, "%cRTS_CTS_OUT", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_DSR_DTR_IN) { - seq_printf(m, "%cDSR_DTR_IN", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_DSR_DTR_OUT) { - seq_printf(m, "%cDSR_DTR_OUT", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_ENQ_ACK_IN) { - seq_printf(m, "%cENQ_ACK_IN", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_ENQ_ACK_OUT) { - seq_printf(m, "%cENQ_ACK_OUT", sep); - sep = '|'; - } - seq_putc(m, '\n'); - - seq_puts(m, "Flags:"); - sep = ' '; - if (tty_port_cts_enabled(&self->port)) { - seq_printf(m, "%cASYNC_CTS_FLOW", sep); - sep = '|'; - } - if (tty_port_check_carrier(&self->port)) { - seq_printf(m, "%cASYNC_CHECK_CD", sep); - sep = '|'; - } - if (tty_port_initialized(&self->port)) { - seq_printf(m, "%cASYNC_INITIALIZED", sep); - sep = '|'; - } - if (self->port.flags & ASYNC_LOW_LATENCY) { - seq_printf(m, "%cASYNC_LOW_LATENCY", sep); - sep = '|'; - } - if (tty_port_active(&self->port)) { - seq_printf(m, "%cASYNC_NORMAL_ACTIVE", sep); - sep = '|'; - } - seq_putc(m, '\n'); - - seq_printf(m, "Role: %s\n", self->client ? "client" : "server"); - seq_printf(m, "Open count: %d\n", self->port.count); - seq_printf(m, "Max data size: %d\n", self->max_data_size); - seq_printf(m, "Max header size: %d\n", self->max_header_size); - - tty = tty_port_tty_get(&self->port); - if (tty) { - seq_printf(m, "Hardware: %s\n", - tty->hw_stopped ? "Stopped" : "Running"); - tty_kref_put(tty); - } -} - -static int ircomm_tty_proc_show(struct seq_file *m, void *v) -{ - struct ircomm_tty_cb *self; - unsigned long flags; - - spin_lock_irqsave(&ircomm_tty->hb_spinlock, flags); - - self = (struct ircomm_tty_cb *) hashbin_get_first(ircomm_tty); - while (self != NULL) { - if (self->magic != IRCOMM_TTY_MAGIC) - break; - - ircomm_tty_line_info(self, m); - self = (struct ircomm_tty_cb *) hashbin_get_next(ircomm_tty); - } - spin_unlock_irqrestore(&ircomm_tty->hb_spinlock, flags); - return 0; -} - -static int ircomm_tty_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, ircomm_tty_proc_show, NULL); -} - -static const struct file_operations ircomm_tty_proc_fops = { - .owner = THIS_MODULE, - .open = ircomm_tty_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif /* CONFIG_PROC_FS */ - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("IrCOMM serial TTY driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(IRCOMM_TTY_MAJOR); - -module_init(ircomm_tty_init); -module_exit(ircomm_tty_cleanup); diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty_attach.c b/drivers/staging/irda/net/ircomm/ircomm_tty_attach.c deleted file mode 100644 index e2d5ce8ba0db..000000000000 --- a/drivers/staging/irda/net/ircomm/ircomm_tty_attach.c +++ /dev/null @@ -1,987 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_tty_attach.c - * Version: - * Description: Code for attaching the serial driver to IrCOMM - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Jun 5 17:42:00 1999 - * Modified at: Tue Jan 4 14:20:49 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -static void ircomm_tty_ias_register(struct ircomm_tty_cb *self); -static void ircomm_tty_discovery_indication(discinfo_t *discovery, - DISCOVERY_MODE mode, - void *priv); -static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, - struct ias_value *value, void *priv); -static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, - int timeout); -static void ircomm_tty_watchdog_timer_expired(struct timer_list *timer); - -static int ircomm_tty_state_idle(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); -static int ircomm_tty_state_search(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); -static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); -static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); -static int ircomm_tty_state_setup(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); -static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); - -const char *const ircomm_tty_state[] = { - "IRCOMM_TTY_IDLE", - "IRCOMM_TTY_SEARCH", - "IRCOMM_TTY_QUERY_PARAMETERS", - "IRCOMM_TTY_QUERY_LSAP_SEL", - "IRCOMM_TTY_SETUP", - "IRCOMM_TTY_READY", - "*** ERROR *** ", -}; - -static const char *const ircomm_tty_event[] __maybe_unused = { - "IRCOMM_TTY_ATTACH_CABLE", - "IRCOMM_TTY_DETACH_CABLE", - "IRCOMM_TTY_DATA_REQUEST", - "IRCOMM_TTY_DATA_INDICATION", - "IRCOMM_TTY_DISCOVERY_REQUEST", - "IRCOMM_TTY_DISCOVERY_INDICATION", - "IRCOMM_TTY_CONNECT_CONFIRM", - "IRCOMM_TTY_CONNECT_INDICATION", - "IRCOMM_TTY_DISCONNECT_REQUEST", - "IRCOMM_TTY_DISCONNECT_INDICATION", - "IRCOMM_TTY_WD_TIMER_EXPIRED", - "IRCOMM_TTY_GOT_PARAMETERS", - "IRCOMM_TTY_GOT_LSAPSEL", - "*** ERROR ****", -}; - -static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, - struct sk_buff *skb, struct ircomm_tty_info *info) = -{ - ircomm_tty_state_idle, - ircomm_tty_state_search, - ircomm_tty_state_query_parameters, - ircomm_tty_state_query_lsap_sel, - ircomm_tty_state_setup, - ircomm_tty_state_ready, -}; - -/* - * Function ircomm_tty_attach_cable (driver) - * - * Try to attach cable (IrCOMM link). This function will only return - * when the link has been connected, or if an error condition occurs. - * If success, the return value is the resulting service type. - */ -int ircomm_tty_attach_cable(struct ircomm_tty_cb *self) -{ - struct tty_struct *tty; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - /* Check if somebody has already connected to us */ - if (ircomm_is_connected(self->ircomm)) { - pr_debug("%s(), already connected!\n", __func__); - return 0; - } - - /* Make sure nobody tries to write before the link is up */ - tty = tty_port_tty_get(&self->port); - if (tty) { - tty->hw_stopped = 1; - tty_kref_put(tty); - } - - ircomm_tty_ias_register(self); - - ircomm_tty_do_event(self, IRCOMM_TTY_ATTACH_CABLE, NULL, NULL); - - return 0; -} - -/* - * Function ircomm_detach_cable (driver) - * - * Detach cable, or cable has been detached by peer - * - */ -void ircomm_tty_detach_cable(struct ircomm_tty_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - del_timer(&self->watchdog_timer); - - /* Remove discovery handler */ - if (self->ckey) { - irlmp_unregister_client(self->ckey); - self->ckey = NULL; - } - /* Remove IrCOMM hint bits */ - if (self->skey) { - irlmp_unregister_service(self->skey); - self->skey = NULL; - } - - if (self->iriap) { - iriap_close(self->iriap); - self->iriap = NULL; - } - - /* Remove LM-IAS object */ - if (self->obj) { - irias_delete_object(self->obj); - self->obj = NULL; - } - - ircomm_tty_do_event(self, IRCOMM_TTY_DETACH_CABLE, NULL, NULL); - - /* Reset some values */ - self->daddr = self->saddr = 0; - self->dlsap_sel = self->slsap_sel = 0; - - memset(&self->settings, 0, sizeof(struct ircomm_params)); -} - -/* - * Function ircomm_tty_ias_register (self) - * - * Register with LM-IAS depending on which service type we are - * - */ -static void ircomm_tty_ias_register(struct ircomm_tty_cb *self) -{ - __u8 oct_seq[6]; - __u16 hints; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* Compute hint bits based on service */ - hints = irlmp_service_to_hint(S_COMM); - if (self->service_type & IRCOMM_3_WIRE_RAW) - hints |= irlmp_service_to_hint(S_PRINTER); - - /* Advertise IrCOMM hint bit in discovery */ - if (!self->skey) - self->skey = irlmp_register_service(hints); - /* Set up a discovery handler */ - if (!self->ckey) - self->ckey = irlmp_register_client(hints, - ircomm_tty_discovery_indication, - NULL, (void *) self); - - /* If already done, no need to do it again */ - if (self->obj) - return; - - if (self->service_type & IRCOMM_3_WIRE_RAW) { - /* Register IrLPT with LM-IAS */ - self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID); - irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel", - self->slsap_sel, IAS_KERNEL_ATTR); - } else { - /* Register IrCOMM with LM-IAS */ - self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID); - irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel", - self->slsap_sel, IAS_KERNEL_ATTR); - - /* Code the parameters into the buffer */ - irda_param_pack(oct_seq, "bbbbbb", - IRCOMM_SERVICE_TYPE, 1, self->service_type, - IRCOMM_PORT_TYPE, 1, IRCOMM_SERIAL); - - /* Register parameters with LM-IAS */ - irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6, - IAS_KERNEL_ATTR); - } - irias_insert_object(self->obj); -} - -/* - * Function ircomm_tty_ias_unregister (self) - * - * Remove our IAS object and client hook while connected. - * - */ -static void ircomm_tty_ias_unregister(struct ircomm_tty_cb *self) -{ - /* Remove LM-IAS object now so it is not reused. - * IrCOMM deals very poorly with multiple incoming connections. - * It should looks a lot more like IrNET, and "dup" a server TSAP - * to the application TSAP (based on various rules). - * This is a cheap workaround allowing multiple clients to - * connect to us. It will not always work. - * Each IrCOMM socket has an IAS entry. Incoming connection will - * pick the first one found. So, when we are fully connected, - * we remove our IAS entries so that the next IAS entry is used. - * We do that for *both* client and server, because a server - * can also create client instances. - * Jean II */ - if (self->obj) { - irias_delete_object(self->obj); - self->obj = NULL; - } - -#if 0 - /* Remove discovery handler. - * While we are connected, we no longer need to receive - * discovery events. This would be the case if there is - * multiple IrLAP interfaces. Jean II */ - if (self->ckey) { - irlmp_unregister_client(self->ckey); - self->ckey = NULL; - } -#endif -} - -/* - * Function ircomm_send_initial_parameters (self) - * - * Send initial parameters to the remote IrCOMM device. These parameters - * must be sent before any data. - */ -int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (self->service_type & IRCOMM_3_WIRE_RAW) - return 0; - - /* - * Set default values, but only if the application for some reason - * haven't set them already - */ - pr_debug("%s(), data-rate = %d\n", __func__ , - self->settings.data_rate); - if (!self->settings.data_rate) - self->settings.data_rate = 9600; - pr_debug("%s(), data-format = %d\n", __func__ , - self->settings.data_format); - if (!self->settings.data_format) - self->settings.data_format = IRCOMM_WSIZE_8; /* 8N1 */ - - pr_debug("%s(), flow-control = %d\n", __func__ , - self->settings.flow_control); - /*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/ - - /* Do not set delta values for the initial parameters */ - self->settings.dte = IRCOMM_DTR | IRCOMM_RTS; - - /* Only send service type parameter when we are the client */ - if (self->client) - ircomm_param_request(self, IRCOMM_SERVICE_TYPE, FALSE); - ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE); - ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE); - - /* For a 3 wire service, we just flush the last parameter and return */ - if (self->settings.service_type == IRCOMM_3_WIRE) { - ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE); - return 0; - } - - /* Only 9-wire service types continue here */ - ircomm_param_request(self, IRCOMM_FLOW_CONTROL, FALSE); -#if 0 - ircomm_param_request(self, IRCOMM_XON_XOFF, FALSE); - ircomm_param_request(self, IRCOMM_ENQ_ACK, FALSE); -#endif - /* Notify peer that we are ready to receive data */ - ircomm_param_request(self, IRCOMM_DTE, TRUE); - - return 0; -} - -/* - * Function ircomm_tty_discovery_indication (discovery) - * - * Remote device is discovered, try query the remote IAS to see which - * device it is, and which services it has. - * - */ -static void ircomm_tty_discovery_indication(discinfo_t *discovery, - DISCOVERY_MODE mode, - void *priv) -{ - struct ircomm_tty_cb *self; - struct ircomm_tty_info info; - - /* Important note : - * We need to drop all passive discoveries. - * The LSAP management of IrComm is deficient and doesn't deal - * with the case of two instance connecting to each other - * simultaneously (it will deadlock in LMP). - * The proper fix would be to use the same technique as in IrNET, - * to have one server socket and separate instances for the - * connecting/connected socket. - * The workaround is to drop passive discovery, which drastically - * reduce the probability of this happening. - * Jean II */ - if(mode == DISCOVERY_PASSIVE) - return; - - info.daddr = discovery->daddr; - info.saddr = discovery->saddr; - - self = priv; - ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION, - NULL, &info); -} - -/* - * Function ircomm_tty_disconnect_indication (instance, sap, reason, skb) - * - * Link disconnected - * - */ -void ircomm_tty_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - struct tty_struct *tty; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - tty = tty_port_tty_get(&self->port); - if (!tty) - return; - - /* This will stop control data transfers */ - self->flow = FLOW_STOP; - - /* Stop data transfers */ - tty->hw_stopped = 1; - - ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL, - NULL); - tty_kref_put(tty); -} - -/* - * Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv) - * - * Got result from the IAS query we make - * - */ -static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, - struct ias_value *value, - void *priv) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* We probably don't need to make any more queries */ - iriap_close(self->iriap); - self->iriap = NULL; - - /* Check if request succeeded */ - if (result != IAS_SUCCESS) { - pr_debug("%s(), got NULL value!\n", __func__); - return; - } - - switch (value->type) { - case IAS_OCT_SEQ: - pr_debug("%s(), got octet sequence\n", __func__); - - irda_param_extract_all(self, value->t.oct_seq, value->len, - &ircomm_param_info); - - ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL, - NULL); - break; - case IAS_INTEGER: - /* Got LSAP selector */ - pr_debug("%s(), got lsapsel = %d\n", __func__ , - value->t.integer); - - if (value->t.integer == -1) { - pr_debug("%s(), invalid value!\n", __func__); - } else - self->dlsap_sel = value->t.integer; - - ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL); - break; - case IAS_MISSING: - pr_debug("%s(), got IAS_MISSING\n", __func__); - break; - default: - pr_debug("%s(), got unknown type!\n", __func__); - break; - } - irias_delete_value(value); -} - -/* - * Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb) - * - * Connection confirmed - * - */ -void ircomm_tty_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_data_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - self->client = TRUE; - self->max_data_size = max_data_size; - self->max_header_size = max_header_size; - self->flow = FLOW_START; - - ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL); - - /* No need to kfree_skb - see ircomm_ttp_connect_confirm() */ -} - -/* - * Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size, - * skb) - * - * we are discovered and being requested to connect by remote device ! - * - */ -void ircomm_tty_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_data_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - int clen; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - self->client = FALSE; - self->max_data_size = max_data_size; - self->max_header_size = max_header_size; - self->flow = FLOW_START; - - clen = skb->data[0]; - if (clen) - irda_param_extract_all(self, skb->data+1, - IRDA_MIN(skb->len, clen), - &ircomm_param_info); - - ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL); - - /* No need to kfree_skb - see ircomm_ttp_connect_indication() */ -} - -/* - * Function ircomm_tty_link_established (self) - * - * Called when the IrCOMM link is established - * - */ -void ircomm_tty_link_established(struct ircomm_tty_cb *self) -{ - struct tty_struct *tty; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - tty = tty_port_tty_get(&self->port); - if (!tty) - return; - - del_timer(&self->watchdog_timer); - - /* - * IrCOMM link is now up, and if we are not using hardware - * flow-control, then declare the hardware as running. Otherwise we - * will have to wait for the peer device (DCE) to raise the CTS - * line. - */ - if (tty_port_cts_enabled(&self->port) && - ((self->settings.dce & IRCOMM_CTS) == 0)) { - pr_debug("%s(), waiting for CTS ...\n", __func__); - goto put; - } else { - pr_debug("%s(), starting hardware!\n", __func__); - - tty->hw_stopped = 0; - - /* Wake up processes blocked on open */ - wake_up_interruptible(&self->port.open_wait); - } - - schedule_work(&self->tqueue); -put: - tty_kref_put(tty); -} - -/* - * Function ircomm_tty_start_watchdog_timer (self, timeout) - * - * Start the watchdog timer. This timer is used to make sure that any - * connection attempt is successful, and if not, we will retry after - * the timeout - */ -static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, - int timeout) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - irda_start_timer(&self->watchdog_timer, timeout, - ircomm_tty_watchdog_timer_expired); -} - -/* - * Function ircomm_tty_watchdog_timer_expired (data) - * - * Called when the connect procedure have taken to much time. - * - */ -static void ircomm_tty_watchdog_timer_expired(struct timer_list *t) -{ - struct ircomm_tty_cb *self = from_timer(self, t, watchdog_timer); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL); -} - - -/* - * Function ircomm_tty_do_event (self, event, skb) - * - * Process event - * - */ -int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, - struct sk_buff *skb, struct ircomm_tty_info *info) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - - return (*state[self->state])(self, event, skb, info); -} - -/* - * Function ircomm_tty_next_state (self, state) - * - * Switch state - * - */ -static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state) -{ - /* - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - pr_debug("%s: next state=%s, service type=%d\n", __func__ , - ircomm_tty_state[self->state], self->service_type); - */ - self->state = state; -} - -/* - * Function ircomm_tty_state_idle (self, event, skb, info) - * - * Just hanging around - * - */ -static int ircomm_tty_state_idle(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - switch (event) { - case IRCOMM_TTY_ATTACH_CABLE: - /* Try to discover any remote devices */ - ircomm_tty_start_watchdog_timer(self, 3*HZ); - ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); - - irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); - break; - case IRCOMM_TTY_DISCOVERY_INDICATION: - self->daddr = info->daddr; - self->saddr = info->saddr; - - if (self->iriap) { - net_warn_ratelimited("%s(), busy with a previous query\n", - __func__); - return -EBUSY; - } - - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - ircomm_tty_getvalue_confirm); - - iriap_getvaluebyclass_request(self->iriap, - self->saddr, self->daddr, - "IrDA:IrCOMM", "Parameters"); - - ircomm_tty_start_watchdog_timer(self, 3*HZ); - ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS); - break; - case IRCOMM_TTY_CONNECT_INDICATION: - del_timer(&self->watchdog_timer); - - /* Accept connection */ - ircomm_connect_response(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_WD_TIMER_EXPIRED: - /* Just stay idle */ - break; - case IRCOMM_TTY_DETACH_CABLE: - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_tty_state_search (self, event, skb, info) - * - * Trying to discover an IrCOMM device - * - */ -static int ircomm_tty_state_search(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - - switch (event) { - case IRCOMM_TTY_DISCOVERY_INDICATION: - self->daddr = info->daddr; - self->saddr = info->saddr; - - if (self->iriap) { - net_warn_ratelimited("%s(), busy with a previous query\n", - __func__); - return -EBUSY; - } - - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - ircomm_tty_getvalue_confirm); - - if (self->service_type == IRCOMM_3_WIRE_RAW) { - iriap_getvaluebyclass_request(self->iriap, self->saddr, - self->daddr, "IrLPT", - "IrDA:IrLMP:LsapSel"); - ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL); - } else { - iriap_getvaluebyclass_request(self->iriap, self->saddr, - self->daddr, - "IrDA:IrCOMM", - "Parameters"); - - ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS); - } - ircomm_tty_start_watchdog_timer(self, 3*HZ); - break; - case IRCOMM_TTY_CONNECT_INDICATION: - del_timer(&self->watchdog_timer); - ircomm_tty_ias_unregister(self); - - /* Accept connection */ - ircomm_connect_response(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_WD_TIMER_EXPIRED: -#if 1 - /* Give up */ -#else - /* Try to discover any remote devices */ - ircomm_tty_start_watchdog_timer(self, 3*HZ); - irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); -#endif - break; - case IRCOMM_TTY_DETACH_CABLE: - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_tty_state_query (self, event, skb, info) - * - * Querying the remote LM-IAS for IrCOMM parameters - * - */ -static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - - switch (event) { - case IRCOMM_TTY_GOT_PARAMETERS: - if (self->iriap) { - net_warn_ratelimited("%s(), busy with a previous query\n", - __func__); - return -EBUSY; - } - - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - ircomm_tty_getvalue_confirm); - - iriap_getvaluebyclass_request(self->iriap, self->saddr, - self->daddr, "IrDA:IrCOMM", - "IrDA:TinyTP:LsapSel"); - - ircomm_tty_start_watchdog_timer(self, 3*HZ); - ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL); - break; - case IRCOMM_TTY_WD_TIMER_EXPIRED: - /* Go back to search mode */ - ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); - ircomm_tty_start_watchdog_timer(self, 3*HZ); - break; - case IRCOMM_TTY_CONNECT_INDICATION: - del_timer(&self->watchdog_timer); - ircomm_tty_ias_unregister(self); - - /* Accept connection */ - ircomm_connect_response(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_DETACH_CABLE: - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_tty_state_query_lsap_sel (self, event, skb, info) - * - * Query remote LM-IAS for the LSAP selector which we can connect to - * - */ -static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - - switch (event) { - case IRCOMM_TTY_GOT_LSAPSEL: - /* Connect to remote device */ - ret = ircomm_connect_request(self->ircomm, self->dlsap_sel, - self->saddr, self->daddr, - NULL, self->service_type); - ircomm_tty_start_watchdog_timer(self, 3*HZ); - ircomm_tty_next_state(self, IRCOMM_TTY_SETUP); - break; - case IRCOMM_TTY_WD_TIMER_EXPIRED: - /* Go back to search mode */ - ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); - ircomm_tty_start_watchdog_timer(self, 3*HZ); - break; - case IRCOMM_TTY_CONNECT_INDICATION: - del_timer(&self->watchdog_timer); - ircomm_tty_ias_unregister(self); - - /* Accept connection */ - ircomm_connect_response(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_DETACH_CABLE: - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_tty_state_setup (self, event, skb, info) - * - * Trying to connect - * - */ -static int ircomm_tty_state_setup(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - - switch (event) { - case IRCOMM_TTY_CONNECT_CONFIRM: - del_timer(&self->watchdog_timer); - ircomm_tty_ias_unregister(self); - - /* - * Send initial parameters. This will also send out queued - * parameters waiting for the connection to come up - */ - ircomm_tty_send_initial_parameters(self); - ircomm_tty_link_established(self); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_CONNECT_INDICATION: - del_timer(&self->watchdog_timer); - ircomm_tty_ias_unregister(self); - - /* Accept connection */ - ircomm_connect_response(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_WD_TIMER_EXPIRED: - /* Go back to search mode */ - ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); - ircomm_tty_start_watchdog_timer(self, 3*HZ); - break; - case IRCOMM_TTY_DETACH_CABLE: - /* ircomm_disconnect_request(self->ircomm, NULL); */ - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_tty_state_ready (self, event, skb, info) - * - * IrCOMM is now connected - * - */ -static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - switch (event) { - case IRCOMM_TTY_DATA_REQUEST: - ret = ircomm_data_request(self->ircomm, skb); - break; - case IRCOMM_TTY_DETACH_CABLE: - ircomm_disconnect_request(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - case IRCOMM_TTY_DISCONNECT_INDICATION: - ircomm_tty_ias_register(self); - ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); - ircomm_tty_start_watchdog_timer(self, 3*HZ); - - if (tty_port_check_carrier(&self->port)) { - /* Drop carrier */ - self->settings.dce = IRCOMM_DELTA_CD; - ircomm_tty_check_modem_status(self); - } else { - pr_debug("%s(), hanging up!\n", __func__); - tty_port_tty_hangup(&self->port, false); - } - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c b/drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c deleted file mode 100644 index 171c3dee760e..000000000000 --- a/drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c +++ /dev/null @@ -1,291 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_tty_ioctl.c - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Jun 10 14:39:09 1999 - * Modified at: Wed Jan 5 14:45:43 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include - -#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - -/* - * Function ircomm_tty_change_speed (driver) - * - * Change speed of the driver. If the remote device is a DCE, then this - * should make it change the speed of its serial port - */ -static void ircomm_tty_change_speed(struct ircomm_tty_cb *self, - struct tty_struct *tty) -{ - unsigned int cflag, cval; - int baud; - - if (!self->ircomm) - return; - - cflag = tty->termios.c_cflag; - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: cval = IRCOMM_WSIZE_5; break; - case CS6: cval = IRCOMM_WSIZE_6; break; - case CS7: cval = IRCOMM_WSIZE_7; break; - case CS8: cval = IRCOMM_WSIZE_8; break; - default: cval = IRCOMM_WSIZE_5; break; - } - if (cflag & CSTOPB) - cval |= IRCOMM_2_STOP_BIT; - - if (cflag & PARENB) - cval |= IRCOMM_PARITY_ENABLE; - if (!(cflag & PARODD)) - cval |= IRCOMM_PARITY_EVEN; - - /* Determine divisor based on baud rate */ - baud = tty_get_baud_rate(tty); - if (!baud) - baud = 9600; /* B0 transition handled in rs_set_termios */ - - self->settings.data_rate = baud; - ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE); - - /* CTS flow control flag and modem status interrupts */ - tty_port_set_cts_flow(&self->port, cflag & CRTSCTS); - if (cflag & CRTSCTS) { - self->settings.flow_control |= IRCOMM_RTS_CTS_IN; - /* This got me. Bummer. Jean II */ - if (self->service_type == IRCOMM_3_WIRE_RAW) - net_warn_ratelimited("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", - __func__); - } else { - self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN; - } - tty_port_set_check_carrier(&self->port, ~cflag & CLOCAL); - - self->settings.data_format = cval; - - ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE); - ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE); -} - -/* - * Function ircomm_tty_set_termios (tty, old_termios) - * - * This routine allows the tty driver to be notified when device's - * termios settings have changed. Note that a well-designed tty driver - * should be prepared to accept the case where old == NULL, and try to - * do something rational. - */ -void ircomm_tty_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned int cflag = tty->termios.c_cflag; - - if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(tty->termios.c_iflag) == - RELEVANT_IFLAG(old_termios->c_iflag))) - { - return; - } - - ircomm_tty_change_speed(self, tty); - - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { - self->settings.dte &= ~(IRCOMM_DTR|IRCOMM_RTS); - ircomm_param_request(self, IRCOMM_DTE, TRUE); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { - self->settings.dte |= IRCOMM_DTR; - if (!C_CRTSCTS(tty) || !tty_throttled(tty)) - self->settings.dte |= IRCOMM_RTS; - ircomm_param_request(self, IRCOMM_DTE, TRUE); - } - - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) - { - tty->hw_stopped = 0; - ircomm_tty_start(tty); - } -} - -/* - * Function ircomm_tty_tiocmget (tty) - * - * - * - */ -int ircomm_tty_tiocmget(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned int result; - - if (tty_io_error(tty)) - return -EIO; - - result = ((self->settings.dte & IRCOMM_RTS) ? TIOCM_RTS : 0) - | ((self->settings.dte & IRCOMM_DTR) ? TIOCM_DTR : 0) - | ((self->settings.dce & IRCOMM_CD) ? TIOCM_CAR : 0) - | ((self->settings.dce & IRCOMM_RI) ? TIOCM_RNG : 0) - | ((self->settings.dce & IRCOMM_DSR) ? TIOCM_DSR : 0) - | ((self->settings.dce & IRCOMM_CTS) ? TIOCM_CTS : 0); - return result; -} - -/* - * Function ircomm_tty_tiocmset (tty, set, clear) - * - * - * - */ -int ircomm_tty_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - if (tty_io_error(tty)) - return -EIO; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (set & TIOCM_RTS) - self->settings.dte |= IRCOMM_RTS; - if (set & TIOCM_DTR) - self->settings.dte |= IRCOMM_DTR; - - if (clear & TIOCM_RTS) - self->settings.dte &= ~IRCOMM_RTS; - if (clear & TIOCM_DTR) - self->settings.dte &= ~IRCOMM_DTR; - - if ((set|clear) & TIOCM_RTS) - self->settings.dte |= IRCOMM_DELTA_RTS; - if ((set|clear) & TIOCM_DTR) - self->settings.dte |= IRCOMM_DELTA_DTR; - - ircomm_param_request(self, IRCOMM_DTE, TRUE); - - return 0; -} - -/* - * Function get_serial_info (driver, retinfo) - * - * - * - */ -static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self, - struct serial_struct __user *retinfo) -{ - struct serial_struct info; - - memset(&info, 0, sizeof(info)); - info.line = self->line; - info.flags = self->port.flags; - info.baud_base = self->settings.data_rate; - info.close_delay = self->port.close_delay; - info.closing_wait = self->port.closing_wait; - - /* For compatibility */ - info.type = PORT_16550A; - - if (copy_to_user(retinfo, &info, sizeof(*retinfo))) - return -EFAULT; - - return 0; -} - -/* - * Function set_serial_info (driver, new_info) - * - * - * - */ -static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self, - struct serial_struct __user *new_info) -{ - return 0; -} - -/* - * Function ircomm_tty_ioctl (tty, cmd, arg) - * - * - * - */ -int ircomm_tty_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - int ret = 0; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && - (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { - if (tty_io_error(tty)) - return -EIO; - } - - switch (cmd) { - case TIOCGSERIAL: - ret = ircomm_tty_get_serial_info(self, (struct serial_struct __user *) arg); - break; - case TIOCSSERIAL: - ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg); - break; - case TIOCMIWAIT: - pr_debug("(), TIOCMIWAIT, not impl!\n"); - break; - - case TIOCGICOUNT: - pr_debug("%s(), TIOCGICOUNT not impl!\n", __func__); - return 0; - default: - ret = -ENOIOCTLCMD; /* ioctls which we must ignore */ - } - return ret; -} - - - diff --git a/drivers/staging/irda/net/irda_device.c b/drivers/staging/irda/net/irda_device.c deleted file mode 100644 index 682b4eea15e0..000000000000 --- a/drivers/staging/irda/net/irda_device.c +++ /dev/null @@ -1,316 +0,0 @@ -/********************************************************************* - * - * Filename: irda_device.c - * Version: 0.9 - * Description: Utility functions used by the device drivers - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Oct 9 09:22:27 1999 - * Modified at: Sun Jan 23 17:41:24 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -static void __irda_task_delete(struct irda_task *task); - -static hashbin_t *dongles; -static hashbin_t *tasks; - -static void irda_task_timer_expired(struct timer_list *timer); - -int __init irda_device_init(void) -{ - dongles = hashbin_new(HB_NOLOCK); - if (!dongles) { - net_warn_ratelimited("IrDA: Can't allocate dongles hashbin!\n"); - return -ENOMEM; - } - spin_lock_init(&dongles->hb_spinlock); - - tasks = hashbin_new(HB_LOCK); - if (!tasks) { - net_warn_ratelimited("IrDA: Can't allocate tasks hashbin!\n"); - hashbin_delete(dongles, NULL); - return -ENOMEM; - } - - /* We no longer initialise the driver ourselves here, we let - * the system do it for us... - Jean II - */ - - return 0; -} - -static void leftover_dongle(void *arg) -{ - struct dongle_reg *reg = arg; - - net_warn_ratelimited("IrDA: Dongle type %x not unregistered\n", - reg->type); -} - -void irda_device_cleanup(void) -{ - hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete); - - hashbin_delete(dongles, leftover_dongle); -} - -/* - * Function irda_device_set_media_busy (self, status) - * - * Called when we have detected that another station is transmitting - * in contention mode. - */ -void irda_device_set_media_busy(struct net_device *dev, int status) -{ - struct irlap_cb *self; - - pr_debug("%s(%s)\n", __func__, status ? "TRUE" : "FALSE"); - - self = (struct irlap_cb *)dev->atalk_ptr; - - /* Some drivers may enable the receive interrupt before calling - * irlap_open(), or they may disable the receive interrupt - * after calling irlap_close(). - * The IrDA stack is protected from this in irlap_driver_rcv(). - * However, the driver calls directly the wrapper, that calls - * us directly. Make sure we protect ourselves. - * Jean II - */ - if (!self || self->magic != LAP_MAGIC) - return; - - if (status) { - self->media_busy = TRUE; - if (status == SMALL) - irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT); - else - irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT); - pr_debug("Media busy!\n"); - } else { - self->media_busy = FALSE; - irlap_stop_mbusy_timer(self); - } -} -EXPORT_SYMBOL(irda_device_set_media_busy); - -/* - * Function irda_device_is_receiving (dev) - * - * Check if the device driver is currently receiving data - * - */ -int irda_device_is_receiving(struct net_device *dev) -{ - struct if_irda_req req; - int ret; - - if (!dev->netdev_ops->ndo_do_ioctl) { - net_err_ratelimited("%s: do_ioctl not impl. by device driver\n", - __func__); - return -1; - } - - ret = (dev->netdev_ops->ndo_do_ioctl)(dev, (struct ifreq *) &req, - SIOCGRECEIVING); - if (ret < 0) - return ret; - - return req.ifr_receiving; -} - -static void __irda_task_delete(struct irda_task *task) -{ - del_timer(&task->timer); - - kfree(task); -} - -static void irda_task_delete(struct irda_task *task) -{ - /* Unregister task */ - hashbin_remove(tasks, (long)task, NULL); - - __irda_task_delete(task); -} - -/* - * Function irda_task_kick (task) - * - * Tries to execute a task possible multiple times until the task is either - * finished, or askes for a timeout. When a task is finished, we do post - * processing, and notify the parent task, that is waiting for this task - * to complete. - */ -static int irda_task_kick(struct irda_task *task) -{ - int finished = TRUE; - int count = 0; - int timeout; - - IRDA_ASSERT(task != NULL, return -1;); - IRDA_ASSERT(task->magic == IRDA_TASK_MAGIC, return -1;); - - /* Execute task until it's finished, or askes for a timeout */ - do { - timeout = task->function(task); - if (count++ > 100) { - net_err_ratelimited("%s: error in task handler!\n", - __func__); - irda_task_delete(task); - return TRUE; - } - } while ((timeout == 0) && (task->state != IRDA_TASK_DONE)); - - if (timeout < 0) { - net_err_ratelimited("%s: Error executing task!\n", __func__); - irda_task_delete(task); - return TRUE; - } - - /* Check if we are finished */ - if (task->state == IRDA_TASK_DONE) { - del_timer(&task->timer); - - /* Do post processing */ - if (task->finished) - task->finished(task); - - /* Notify parent */ - if (task->parent) { - /* Check if parent is waiting for us to complete */ - if (task->parent->state == IRDA_TASK_CHILD_WAIT) { - task->parent->state = IRDA_TASK_CHILD_DONE; - - /* Stop timer now that we are here */ - del_timer(&task->parent->timer); - - /* Kick parent task */ - irda_task_kick(task->parent); - } - } - irda_task_delete(task); - } else if (timeout > 0) { - irda_start_timer(&task->timer, timeout, - irda_task_timer_expired); - finished = FALSE; - } else { - pr_debug("%s(), not finished, and no timeout!\n", - __func__); - finished = FALSE; - } - - return finished; -} - -/* - * Function irda_task_timer_expired (data) - * - * Task time has expired. We now try to execute task (again), and restart - * the timer if the task has not finished yet - */ -static void irda_task_timer_expired(struct timer_list *t) -{ - struct irda_task *task = from_timer(task, t, timer); - - irda_task_kick(task); -} - -/* - * Function irda_device_setup (dev) - * - * This function should be used by low level device drivers in a similar way - * as ether_setup() is used by normal network device drivers - */ -static void irda_device_setup(struct net_device *dev) -{ - dev->hard_header_len = 0; - dev->addr_len = LAP_ALEN; - - dev->type = ARPHRD_IRDA; - dev->tx_queue_len = 8; /* Window size + 1 s-frame */ - - memset(dev->broadcast, 0xff, LAP_ALEN); - - dev->mtu = 2048; - dev->flags = IFF_NOARP; -} - -/* - * Funciton alloc_irdadev - * Allocates and sets up an IRDA device in a manner similar to - * alloc_etherdev. - */ -struct net_device *alloc_irdadev(int sizeof_priv) -{ - return alloc_netdev(sizeof_priv, "irda%d", NET_NAME_UNKNOWN, - irda_device_setup); -} -EXPORT_SYMBOL(alloc_irdadev); - -#ifdef CONFIG_ISA_DMA_API -/* - * Function setup_dma (idev, buffer, count, mode) - * - * Setup the DMA channel. Commonly used by LPC FIR drivers - * - */ -void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode) -{ - unsigned long flags; - - flags = claim_dma_lock(); - - disable_dma(channel); - clear_dma_ff(channel); - set_dma_mode(channel, mode); - set_dma_addr(channel, buffer); - set_dma_count(channel, count); - enable_dma(channel); - - release_dma_lock(flags); -} -EXPORT_SYMBOL(irda_setup_dma); -#endif diff --git a/drivers/staging/irda/net/iriap.c b/drivers/staging/irda/net/iriap.c deleted file mode 100644 index d64192e9db8b..000000000000 --- a/drivers/staging/irda/net/iriap.c +++ /dev/null @@ -1,1085 +0,0 @@ -/********************************************************************* - * - * Filename: iriap.c - * Version: 0.8 - * Description: Information Access Protocol (IAP) - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Aug 21 00:02:07 1997 - * Modified at: Sat Dec 25 16:42:42 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -/* FIXME: This one should go in irlmp.c */ -static const char *const ias_charset_types[] __maybe_unused = { - "CS_ASCII", - "CS_ISO_8859_1", - "CS_ISO_8859_2", - "CS_ISO_8859_3", - "CS_ISO_8859_4", - "CS_ISO_8859_5", - "CS_ISO_8859_6", - "CS_ISO_8859_7", - "CS_ISO_8859_8", - "CS_ISO_8859_9", - "CS_UNICODE" -}; - -static hashbin_t *iriap = NULL; -static void *service_handle; - -static void __iriap_close(struct iriap_cb *self); -static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode); -static void iriap_disconnect_indication(void *instance, void *sap, - LM_REASON reason, struct sk_buff *skb); -static void iriap_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb); -static void iriap_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, __u8 max_header_size, - struct sk_buff *skb); -static int iriap_data_indication(void *instance, void *sap, - struct sk_buff *skb); - -static void iriap_watchdog_timer_expired(struct timer_list *t); - -static inline void iriap_start_watchdog_timer(struct iriap_cb *self, - int timeout) -{ - irda_start_timer(&self->watchdog_timer, timeout, - iriap_watchdog_timer_expired); -} - -static struct lock_class_key irias_objects_key; - -/* - * Function iriap_init (void) - * - * Initializes the IrIAP layer, called by the module initialization code - * in irmod.c - */ -int __init iriap_init(void) -{ - struct ias_object *obj; - struct iriap_cb *server; - __u8 oct_seq[6]; - __u16 hints; - - /* Allocate master array */ - iriap = hashbin_new(HB_LOCK); - if (!iriap) - return -ENOMEM; - - /* Object repository - defined in irias_object.c */ - irias_objects = hashbin_new(HB_LOCK); - if (!irias_objects) { - net_warn_ratelimited("%s: Can't allocate irias_objects hashbin!\n", - __func__); - hashbin_delete(iriap, NULL); - return -ENOMEM; - } - - lockdep_set_class_and_name(&irias_objects->hb_spinlock, &irias_objects_key, - "irias_objects"); - - /* - * Register some default services for IrLMP - */ - hints = irlmp_service_to_hint(S_COMPUTER); - service_handle = irlmp_register_service(hints); - - /* Register the Device object with LM-IAS */ - obj = irias_new_object("Device", IAS_DEVICE_ID); - irias_add_string_attrib(obj, "DeviceName", "Linux", IAS_KERNEL_ATTR); - - oct_seq[0] = 0x01; /* Version 1 */ - oct_seq[1] = 0x00; /* IAS support bits */ - oct_seq[2] = 0x00; /* LM-MUX support bits */ -#ifdef CONFIG_IRDA_ULTRA - oct_seq[2] |= 0x04; /* Connectionless Data support */ -#endif - irias_add_octseq_attrib(obj, "IrLMPSupport", oct_seq, 3, - IAS_KERNEL_ATTR); - irias_insert_object(obj); - - /* - * Register server support with IrLMP so we can accept incoming - * connections - */ - server = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL); - if (!server) { - pr_debug("%s(), unable to open server\n", __func__); - return -1; - } - iriap_register_lsap(server, LSAP_IAS, IAS_SERVER); - - return 0; -} - -/* - * Function iriap_cleanup (void) - * - * Initializes the IrIAP layer, called by the module cleanup code in - * irmod.c - */ -void iriap_cleanup(void) -{ - irlmp_unregister_service(service_handle); - - hashbin_delete(iriap, (FREE_FUNC) __iriap_close); - hashbin_delete(irias_objects, (FREE_FUNC) __irias_delete_object); -} - -/* - * Function iriap_open (void) - * - * Opens an instance of the IrIAP layer, and registers with IrLMP - */ -struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv, - CONFIRM_CALLBACK callback) -{ - struct iriap_cb *self; - - self = kzalloc(sizeof(*self), GFP_ATOMIC); - if (!self) - return NULL; - - /* - * Initialize instance - */ - - self->magic = IAS_MAGIC; - self->mode = mode; - if (mode == IAS_CLIENT) { - if (iriap_register_lsap(self, slsap_sel, mode)) { - kfree(self); - return NULL; - } - } - - self->confirm = callback; - self->priv = priv; - - /* iriap_getvaluebyclass_request() will construct packets before - * we connect, so this must have a sane value... Jean II */ - self->max_header_size = LMP_MAX_HEADER; - - timer_setup(&self->watchdog_timer, NULL, 0); - - hashbin_insert(iriap, (irda_queue_t *) self, (long) self, NULL); - - /* Initialize state machines */ - iriap_next_client_state(self, S_DISCONNECT); - iriap_next_call_state(self, S_MAKE_CALL); - iriap_next_server_state(self, R_DISCONNECT); - iriap_next_r_connect_state(self, R_WAITING); - - return self; -} -EXPORT_SYMBOL(iriap_open); - -/* - * Function __iriap_close (self) - * - * Removes (deallocates) the IrIAP instance - * - */ -static void __iriap_close(struct iriap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - del_timer(&self->watchdog_timer); - - if (self->request_skb) - dev_kfree_skb(self->request_skb); - - self->magic = 0; - - kfree(self); -} - -/* - * Function iriap_close (void) - * - * Closes IrIAP and deregisters with IrLMP - */ -void iriap_close(struct iriap_cb *self) -{ - struct iriap_cb *entry; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - if (self->lsap) { - irlmp_close_lsap(self->lsap); - self->lsap = NULL; - } - - entry = (struct iriap_cb *) hashbin_remove(iriap, (long) self, NULL); - IRDA_ASSERT(entry == self, return;); - - __iriap_close(self); -} -EXPORT_SYMBOL(iriap_close); - -static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode) -{ - notify_t notify; - - irda_notify_init(¬ify); - notify.connect_confirm = iriap_connect_confirm; - notify.connect_indication = iriap_connect_indication; - notify.disconnect_indication = iriap_disconnect_indication; - notify.data_indication = iriap_data_indication; - notify.instance = self; - if (mode == IAS_CLIENT) - strcpy(notify.name, "IrIAS cli"); - else - strcpy(notify.name, "IrIAS srv"); - - self->lsap = irlmp_open_lsap(slsap_sel, ¬ify, 0); - if (self->lsap == NULL) { - net_err_ratelimited("%s: Unable to allocated LSAP!\n", - __func__); - return -1; - } - self->slsap_sel = self->lsap->slsap_sel; - - return 0; -} - -/* - * Function iriap_disconnect_indication (handle, reason) - * - * Got disconnect, so clean up everything associated with this connection - * - */ -static void iriap_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb) -{ - struct iriap_cb *self; - - pr_debug("%s(), reason=%s [%d]\n", __func__, - irlmp_reason_str(reason), reason); - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - IRDA_ASSERT(iriap != NULL, return;); - - del_timer(&self->watchdog_timer); - - /* Not needed */ - if (skb) - dev_kfree_skb(skb); - - if (self->mode == IAS_CLIENT) { - pr_debug("%s(), disconnect as client\n", __func__); - - - iriap_do_client_event(self, IAP_LM_DISCONNECT_INDICATION, - NULL); - /* - * Inform service user that the request failed by sending - * it a NULL value. Warning, the client might close us, so - * remember no to use self anymore after calling confirm - */ - if (self->confirm) - self->confirm(IAS_DISCONNECT, 0, NULL, self->priv); - } else { - pr_debug("%s(), disconnect as server\n", __func__); - iriap_do_server_event(self, IAP_LM_DISCONNECT_INDICATION, - NULL); - iriap_close(self); - } -} - -/* - * Function iriap_disconnect_request (handle) - */ -static void iriap_disconnect_request(struct iriap_cb *self) -{ - struct sk_buff *tx_skb; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (tx_skb == NULL) { - pr_debug("%s(), Could not allocate an sk_buff of length %d\n", - __func__, LMP_MAX_HEADER); - return; - } - - /* - * Reserve space for MUX control and LAP header - */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - - irlmp_disconnect_request(self->lsap, tx_skb); -} - -/* - * Function iriap_getvaluebyclass (addr, name, attr) - * - * Retrieve all values from attribute in all objects with given class - * name - */ -int iriap_getvaluebyclass_request(struct iriap_cb *self, - __u32 saddr, __u32 daddr, - char *name, char *attr) -{ - struct sk_buff *tx_skb; - int name_len, attr_len, skb_len; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return -1;); - - /* Client must supply the destination device address */ - if (!daddr) - return -1; - - self->daddr = daddr; - self->saddr = saddr; - - /* - * Save operation, so we know what the later indication is about - */ - self->operation = GET_VALUE_BY_CLASS; - - /* Give ourselves 10 secs to finish this operation */ - iriap_start_watchdog_timer(self, 10*HZ); - - name_len = strlen(name); /* Up to IAS_MAX_CLASSNAME = 60 */ - attr_len = strlen(attr); /* Up to IAS_MAX_ATTRIBNAME = 60 */ - - skb_len = self->max_header_size+2+name_len+1+attr_len+4; - tx_skb = alloc_skb(skb_len, GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX and LAP header */ - skb_reserve(tx_skb, self->max_header_size); - skb_put(tx_skb, 3+name_len+attr_len); - frame = tx_skb->data; - - /* Build frame */ - frame[0] = IAP_LST | GET_VALUE_BY_CLASS; - frame[1] = name_len; /* Insert length of name */ - memcpy(frame+2, name, name_len); /* Insert name */ - frame[2+name_len] = attr_len; /* Insert length of attr */ - memcpy(frame+3+name_len, attr, attr_len); /* Insert attr */ - - iriap_do_client_event(self, IAP_CALL_REQUEST_GVBC, tx_skb); - - /* Drop reference count - see state_s_disconnect(). */ - dev_kfree_skb(tx_skb); - - return 0; -} -EXPORT_SYMBOL(iriap_getvaluebyclass_request); - -/* - * Function iriap_getvaluebyclass_confirm (self, skb) - * - * Got result from GetValueByClass command. Parse it and return result - * to service user. - * - */ -static void iriap_getvaluebyclass_confirm(struct iriap_cb *self, - struct sk_buff *skb) -{ - struct ias_value *value; - int charset; - __u32 value_len; - __u32 tmp_cpu32; - __u16 obj_id; - __u16 len; - __u8 type; - __u8 *fp; - int n; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Initialize variables */ - fp = skb->data; - n = 2; - - /* Get length, MSB first */ - len = get_unaligned_be16(fp + n); - n += 2; - - pr_debug("%s(), len=%d\n", __func__, len); - - /* Get object ID, MSB first */ - obj_id = get_unaligned_be16(fp + n); - n += 2; - - type = fp[n++]; - pr_debug("%s(), Value type = %d\n", __func__, type); - - switch (type) { - case IAS_INTEGER: - memcpy(&tmp_cpu32, fp+n, 4); n += 4; - be32_to_cpus(&tmp_cpu32); - value = irias_new_integer_value(tmp_cpu32); - - /* Legal values restricted to 0x01-0x6f, page 15 irttp */ - pr_debug("%s(), lsap=%d\n", __func__, value->t.integer); - break; - case IAS_STRING: - charset = fp[n++]; - - switch (charset) { - case CS_ASCII: - break; -/* case CS_ISO_8859_1: */ -/* case CS_ISO_8859_2: */ -/* case CS_ISO_8859_3: */ -/* case CS_ISO_8859_4: */ -/* case CS_ISO_8859_5: */ -/* case CS_ISO_8859_6: */ -/* case CS_ISO_8859_7: */ -/* case CS_ISO_8859_8: */ -/* case CS_ISO_8859_9: */ -/* case CS_UNICODE: */ - default: - pr_debug("%s(), charset [%d] %s, not supported\n", - __func__, charset, - charset < ARRAY_SIZE(ias_charset_types) ? - ias_charset_types[charset] : - "(unknown)"); - - /* Aborting, close connection! */ - iriap_disconnect_request(self); - return; - /* break; */ - } - value_len = fp[n++]; - pr_debug("%s(), strlen=%d\n", __func__, value_len); - - /* Make sure the string is null-terminated */ - if (n + value_len < skb->len) - fp[n + value_len] = 0x00; - pr_debug("Got string %s\n", fp+n); - - /* Will truncate to IAS_MAX_STRING bytes */ - value = irias_new_string_value(fp+n); - break; - case IAS_OCT_SEQ: - value_len = get_unaligned_be16(fp + n); - n += 2; - - /* Will truncate to IAS_MAX_OCTET_STRING bytes */ - value = irias_new_octseq_value(fp+n, value_len); - break; - default: - value = irias_new_missing_value(); - break; - } - - /* Finished, close connection! */ - iriap_disconnect_request(self); - - /* Warning, the client might close us, so remember no to use self - * anymore after calling confirm - */ - if (self->confirm) - self->confirm(IAS_SUCCESS, obj_id, value, self->priv); - else { - pr_debug("%s(), missing handler!\n", __func__); - irias_delete_value(value); - } -} - -/* - * Function iriap_getvaluebyclass_response () - * - * Send answer back to remote LM-IAS - * - */ -static void iriap_getvaluebyclass_response(struct iriap_cb *self, - __u16 obj_id, - __u8 ret_code, - struct ias_value *value) -{ - struct sk_buff *tx_skb; - int n; - __be32 tmp_be32; - __be16 tmp_be16; - __u8 *fp; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - IRDA_ASSERT(value != NULL, return;); - IRDA_ASSERT(value->len <= 1024, return;); - - /* Initialize variables */ - n = 0; - - /* - * We must adjust the size of the response after the length of the - * value. We add 32 bytes because of the 6 bytes for the frame and - * max 5 bytes for the value coding. - */ - tx_skb = alloc_skb(value->len + self->max_header_size + 32, - GFP_ATOMIC); - if (!tx_skb) - return; - - /* Reserve space for MUX and LAP header */ - skb_reserve(tx_skb, self->max_header_size); - skb_put(tx_skb, 6); - - fp = tx_skb->data; - - /* Build frame */ - fp[n++] = GET_VALUE_BY_CLASS | IAP_LST; - fp[n++] = ret_code; - - /* Insert list length (MSB first) */ - tmp_be16 = htons(0x0001); - memcpy(fp+n, &tmp_be16, 2); n += 2; - - /* Insert object identifier ( MSB first) */ - tmp_be16 = cpu_to_be16(obj_id); - memcpy(fp+n, &tmp_be16, 2); n += 2; - - switch (value->type) { - case IAS_STRING: - skb_put(tx_skb, 3 + value->len); - fp[n++] = value->type; - fp[n++] = 0; /* ASCII */ - fp[n++] = (__u8) value->len; - memcpy(fp+n, value->t.string, value->len); n+=value->len; - break; - case IAS_INTEGER: - skb_put(tx_skb, 5); - fp[n++] = value->type; - - tmp_be32 = cpu_to_be32(value->t.integer); - memcpy(fp+n, &tmp_be32, 4); n += 4; - break; - case IAS_OCT_SEQ: - skb_put(tx_skb, 3 + value->len); - fp[n++] = value->type; - - tmp_be16 = cpu_to_be16(value->len); - memcpy(fp+n, &tmp_be16, 2); n += 2; - memcpy(fp+n, value->t.oct_seq, value->len); n+=value->len; - break; - case IAS_MISSING: - pr_debug("%s: sending IAS_MISSING\n", __func__); - skb_put(tx_skb, 1); - fp[n++] = value->type; - break; - default: - pr_debug("%s(), type not implemented!\n", __func__); - break; - } - iriap_do_r_connect_event(self, IAP_CALL_RESPONSE, tx_skb); - - /* Drop reference count - see state_r_execute(). */ - dev_kfree_skb(tx_skb); -} - -/* - * Function iriap_getvaluebyclass_indication (self, skb) - * - * getvaluebyclass is requested from peer LM-IAS - * - */ -static void iriap_getvaluebyclass_indication(struct iriap_cb *self, - struct sk_buff *skb) -{ - struct ias_object *obj; - struct ias_attrib *attrib; - int name_len; - int attr_len; - char name[IAS_MAX_CLASSNAME + 1]; /* 60 bytes */ - char attr[IAS_MAX_ATTRIBNAME + 1]; /* 60 bytes */ - __u8 *fp; - int n; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - fp = skb->data; - n = 1; - - name_len = fp[n++]; - - IRDA_ASSERT(name_len < IAS_MAX_CLASSNAME + 1, return;); - - memcpy(name, fp+n, name_len); n+=name_len; - name[name_len] = '\0'; - - attr_len = fp[n++]; - - IRDA_ASSERT(attr_len < IAS_MAX_ATTRIBNAME + 1, return;); - - memcpy(attr, fp+n, attr_len); n+=attr_len; - attr[attr_len] = '\0'; - - pr_debug("LM-IAS: Looking up %s: %s\n", name, attr); - obj = irias_find_object(name); - - if (obj == NULL) { - pr_debug("LM-IAS: Object %s not found\n", name); - iriap_getvaluebyclass_response(self, 0x1235, IAS_CLASS_UNKNOWN, - &irias_missing); - return; - } - pr_debug("LM-IAS: found %s, id=%d\n", obj->name, obj->id); - - attrib = irias_find_attrib(obj, attr); - if (attrib == NULL) { - pr_debug("LM-IAS: Attribute %s not found\n", attr); - iriap_getvaluebyclass_response(self, obj->id, - IAS_ATTRIB_UNKNOWN, - &irias_missing); - return; - } - - /* We have a match; send the value. */ - iriap_getvaluebyclass_response(self, obj->id, IAS_SUCCESS, - attrib->value); -} - -/* - * Function iriap_send_ack (void) - * - * Currently not used - * - */ -void iriap_send_ack(struct iriap_cb *self) -{ - struct sk_buff *tx_skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - tx_skb = alloc_skb(LMP_MAX_HEADER + 1, GFP_ATOMIC); - if (!tx_skb) - return; - - /* Reserve space for MUX and LAP header */ - skb_reserve(tx_skb, self->max_header_size); - skb_put(tx_skb, 1); - frame = tx_skb->data; - - /* Build frame */ - frame[0] = IAP_LST | IAP_ACK | self->operation; - - irlmp_data_request(self->lsap, tx_skb); -} - -void iriap_connect_request(struct iriap_cb *self) -{ - int ret; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - ret = irlmp_connect_request(self->lsap, LSAP_IAS, - self->saddr, self->daddr, - NULL, NULL); - if (ret < 0) { - pr_debug("%s(), connect failed!\n", __func__); - self->confirm(IAS_DISCONNECT, 0, NULL, self->priv); - } -} - -/* - * Function iriap_connect_confirm (handle, skb) - * - * LSAP connection confirmed! - * - */ -static void iriap_connect_confirm(void *instance, void *sap, - struct qos_info *qos, __u32 max_seg_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct iriap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - self->max_data_size = max_seg_size; - self->max_header_size = max_header_size; - - del_timer(&self->watchdog_timer); - - iriap_do_client_event(self, IAP_LM_CONNECT_CONFIRM, skb); - - /* Drop reference count - see state_s_make_call(). */ - dev_kfree_skb(skb); -} - -/* - * Function iriap_connect_indication ( handle, skb) - * - * Remote LM-IAS is requesting connection - * - */ -static void iriap_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_seg_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct iriap_cb *self, *new; - - self = instance; - - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(self != NULL, goto out;); - IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;); - - /* Start new server */ - new = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL); - if (!new) { - pr_debug("%s(), open failed\n", __func__); - goto out; - } - - /* Now attach up the new "socket" */ - new->lsap = irlmp_dup(self->lsap, new); - if (!new->lsap) { - pr_debug("%s(), dup failed!\n", __func__); - goto out; - } - - new->max_data_size = max_seg_size; - new->max_header_size = max_header_size; - - /* Clean up the original one to keep it in listen state */ - irlmp_listen(self->lsap); - - iriap_do_server_event(new, IAP_LM_CONNECT_INDICATION, skb); - -out: - /* Drop reference count - see state_r_disconnect(). */ - dev_kfree_skb(skb); -} - -/* - * Function iriap_data_indication (handle, skb) - * - * Receives data from connection identified by handle from IrLMP - * - */ -static int iriap_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct iriap_cb *self; - __u8 *frame; - __u8 opcode; - - self = instance; - - IRDA_ASSERT(skb != NULL, return 0;); - IRDA_ASSERT(self != NULL, goto out;); - IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;); - - frame = skb->data; - - if (self->mode == IAS_SERVER) { - /* Call server */ - pr_debug("%s(), Calling server!\n", __func__); - iriap_do_r_connect_event(self, IAP_RECV_F_LST, skb); - goto out; - } - opcode = frame[0]; - if (~opcode & IAP_LST) { - net_warn_ratelimited("%s:, IrIAS multiframe commands or results is not implemented yet!\n", - __func__); - goto out; - } - - /* Check for ack frames since they don't contain any data */ - if (opcode & IAP_ACK) { - pr_debug("%s() Got ack frame!\n", __func__); - goto out; - } - - opcode &= ~IAP_LST; /* Mask away LST bit */ - - switch (opcode) { - case GET_INFO_BASE: - pr_debug("IrLMP GetInfoBaseDetails not implemented!\n"); - break; - case GET_VALUE_BY_CLASS: - iriap_do_call_event(self, IAP_RECV_F_LST, NULL); - - switch (frame[1]) { - case IAS_SUCCESS: - iriap_getvaluebyclass_confirm(self, skb); - break; - case IAS_CLASS_UNKNOWN: - pr_debug("%s(), No such class!\n", __func__); - /* Finished, close connection! */ - iriap_disconnect_request(self); - - /* - * Warning, the client might close us, so remember - * no to use self anymore after calling confirm - */ - if (self->confirm) - self->confirm(IAS_CLASS_UNKNOWN, 0, NULL, - self->priv); - break; - case IAS_ATTRIB_UNKNOWN: - pr_debug("%s(), No such attribute!\n", __func__); - /* Finished, close connection! */ - iriap_disconnect_request(self); - - /* - * Warning, the client might close us, so remember - * no to use self anymore after calling confirm - */ - if (self->confirm) - self->confirm(IAS_ATTRIB_UNKNOWN, 0, NULL, - self->priv); - break; - } - break; - default: - pr_debug("%s(), Unknown op-code: %02x\n", __func__, - opcode); - break; - } - -out: - /* Cleanup - sub-calls will have done skb_get() as needed. */ - dev_kfree_skb(skb); - return 0; -} - -/* - * Function iriap_call_indication (self, skb) - * - * Received call to server from peer LM-IAS - * - */ -void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb) -{ - __u8 *fp; - __u8 opcode; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - fp = skb->data; - - opcode = fp[0]; - if (~opcode & 0x80) { - net_warn_ratelimited("%s: IrIAS multiframe commands or results is not implemented yet!\n", - __func__); - return; - } - opcode &= 0x7f; /* Mask away LST bit */ - - switch (opcode) { - case GET_INFO_BASE: - net_warn_ratelimited("%s: GetInfoBaseDetails not implemented yet!\n", - __func__); - break; - case GET_VALUE_BY_CLASS: - iriap_getvaluebyclass_indication(self, skb); - break; - } - /* skb will be cleaned up in iriap_data_indication */ -} - -/* - * Function iriap_watchdog_timer_expired (data) - * - * Query has taken too long time, so abort - * - */ -static void iriap_watchdog_timer_expired(struct timer_list *t) -{ - struct iriap_cb *self = from_timer(self, t, watchdog_timer); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - /* iriap_close(self); */ -} - -#ifdef CONFIG_PROC_FS - -static const char *const ias_value_types[] = { - "IAS_MISSING", - "IAS_INTEGER", - "IAS_OCT_SEQ", - "IAS_STRING" -}; - -static inline struct ias_object *irias_seq_idx(loff_t pos) -{ - struct ias_object *obj; - - for (obj = (struct ias_object *) hashbin_get_first(irias_objects); - obj; obj = (struct ias_object *) hashbin_get_next(irias_objects)) { - if (pos-- == 0) - break; - } - - return obj; -} - -static void *irias_seq_start(struct seq_file *seq, loff_t *pos) -{ - spin_lock_irq(&irias_objects->hb_spinlock); - - return *pos ? irias_seq_idx(*pos - 1) : SEQ_START_TOKEN; -} - -static void *irias_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - - return (v == SEQ_START_TOKEN) - ? (void *) hashbin_get_first(irias_objects) - : (void *) hashbin_get_next(irias_objects); -} - -static void irias_seq_stop(struct seq_file *seq, void *v) -{ - spin_unlock_irq(&irias_objects->hb_spinlock); -} - -static int irias_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) - seq_puts(seq, "LM-IAS Objects:\n"); - else { - struct ias_object *obj = v; - struct ias_attrib *attrib; - - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -EINVAL;); - - seq_printf(seq, "name: %s, id=%d\n", - obj->name, obj->id); - - /* Careful for priority inversions here ! - * All other uses of attrib spinlock are independent of - * the object spinlock, so we are safe. Jean II */ - spin_lock(&obj->attribs->hb_spinlock); - - /* List all attributes for this object */ - for (attrib = (struct ias_attrib *) hashbin_get_first(obj->attribs); - attrib != NULL; - attrib = (struct ias_attrib *) hashbin_get_next(obj->attribs)) { - - IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, - goto outloop; ); - - seq_printf(seq, " - Attribute name: \"%s\", ", - attrib->name); - seq_printf(seq, "value[%s]: ", - ias_value_types[attrib->value->type]); - - switch (attrib->value->type) { - case IAS_INTEGER: - seq_printf(seq, "%d\n", - attrib->value->t.integer); - break; - case IAS_STRING: - seq_printf(seq, "\"%s\"\n", - attrib->value->t.string); - break; - case IAS_OCT_SEQ: - seq_printf(seq, "octet sequence (%d bytes)\n", - attrib->value->len); - break; - case IAS_MISSING: - seq_puts(seq, "missing\n"); - break; - default: - seq_printf(seq, "type %d?\n", - attrib->value->type); - } - seq_putc(seq, '\n'); - - } - IRDA_ASSERT_LABEL(outloop:) - spin_unlock(&obj->attribs->hb_spinlock); - } - - return 0; -} - -static const struct seq_operations irias_seq_ops = { - .start = irias_seq_start, - .next = irias_seq_next, - .stop = irias_seq_stop, - .show = irias_seq_show, -}; - -static int irias_seq_open(struct inode *inode, struct file *file) -{ - IRDA_ASSERT( irias_objects != NULL, return -EINVAL;); - - return seq_open(file, &irias_seq_ops); -} - -const struct file_operations irias_seq_fops = { - .owner = THIS_MODULE, - .open = irias_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -#endif /* PROC_FS */ diff --git a/drivers/staging/irda/net/iriap_event.c b/drivers/staging/irda/net/iriap_event.c deleted file mode 100644 index e6098b2e048a..000000000000 --- a/drivers/staging/irda/net/iriap_event.c +++ /dev/null @@ -1,496 +0,0 @@ -/********************************************************************* - * - * Filename: iriap_event.c - * Version: 0.1 - * Description: IAP Finite State Machine - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Aug 21 00:02:07 1997 - * Modified at: Wed Mar 1 11:28:34 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include - -#include -#include -#include -#include - -static void state_s_disconnect (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_connecting (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_call (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); - -static void state_s_make_call (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_calling (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_outstanding (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_replying (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_wait_active (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); - -static void state_r_disconnect (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_call (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_waiting (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_wait_active (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_receiving (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_execute (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_returning (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); - -static void (*iriap_state[])(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) = { - /* Client FSM */ - state_s_disconnect, - state_s_connecting, - state_s_call, - - /* S-Call FSM */ - state_s_make_call, - state_s_calling, - state_s_outstanding, - state_s_replying, - state_s_wait_for_call, - state_s_wait_active, - - /* Server FSM */ - state_r_disconnect, - state_r_call, - - /* R-Connect FSM */ - state_r_waiting, - state_r_wait_active, - state_r_receiving, - state_r_execute, - state_r_returning, -}; - -void iriap_next_client_state(struct iriap_cb *self, IRIAP_STATE state) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - self->client_state = state; -} - -void iriap_next_call_state(struct iriap_cb *self, IRIAP_STATE state) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - self->call_state = state; -} - -void iriap_next_server_state(struct iriap_cb *self, IRIAP_STATE state) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - self->server_state = state; -} - -void iriap_next_r_connect_state(struct iriap_cb *self, IRIAP_STATE state) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - self->r_connect_state = state; -} - -void iriap_do_client_event(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - (*iriap_state[ self->client_state]) (self, event, skb); -} - -void iriap_do_call_event(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - (*iriap_state[ self->call_state]) (self, event, skb); -} - -void iriap_do_server_event(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - (*iriap_state[ self->server_state]) (self, event, skb); -} - -void iriap_do_r_connect_event(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - (*iriap_state[ self->r_connect_state]) (self, event, skb); -} - - -/* - * Function state_s_disconnect (event, skb) - * - * S-Disconnect, The device has no LSAP connection to a particular - * remote device. - */ -static void state_s_disconnect(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - switch (event) { - case IAP_CALL_REQUEST_GVBC: - iriap_next_client_state(self, S_CONNECTING); - IRDA_ASSERT(self->request_skb == NULL, return;); - /* Don't forget to refcount it - - * see iriap_getvaluebyclass_request(). */ - skb_get(skb); - self->request_skb = skb; - iriap_connect_request(self); - break; - case IAP_LM_DISCONNECT_INDICATION: - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__, event); - break; - } -} - -/* - * Function state_s_connecting (self, event, skb) - * - * S-Connecting - * - */ -static void state_s_connecting(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - switch (event) { - case IAP_LM_CONNECT_CONFIRM: - /* - * Jump to S-Call FSM - */ - iriap_do_call_event(self, IAP_CALL_REQUEST, skb); - /* iriap_call_request(self, 0,0,0); */ - iriap_next_client_state(self, S_CALL); - break; - case IAP_LM_DISCONNECT_INDICATION: - /* Abort calls */ - iriap_next_call_state(self, S_MAKE_CALL); - iriap_next_client_state(self, S_DISCONNECT); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__, event); - break; - } -} - -/* - * Function state_s_call (self, event, skb) - * - * S-Call, The device can process calls to a specific remote - * device. Whenever the LSAP connection is disconnected, this state - * catches that event and clears up - */ -static void state_s_call(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - - switch (event) { - case IAP_LM_DISCONNECT_INDICATION: - /* Abort calls */ - iriap_next_call_state(self, S_MAKE_CALL); - iriap_next_client_state(self, S_DISCONNECT); - break; - default: - pr_debug("state_s_call: Unknown event %d\n", event); - break; - } -} - -/* - * Function state_s_make_call (event, skb) - * - * S-Make-Call - * - */ -static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - - IRDA_ASSERT(self != NULL, return;); - - switch (event) { - case IAP_CALL_REQUEST: - /* Already refcounted - see state_s_disconnect() */ - tx_skb = self->request_skb; - self->request_skb = NULL; - - irlmp_data_request(self->lsap, tx_skb); - iriap_next_call_state(self, S_OUTSTANDING); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__, event); - break; - } -} - -/* - * Function state_s_calling (event, skb) - * - * S-Calling - * - */ -static void state_s_calling(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - -/* - * Function state_s_outstanding (event, skb) - * - * S-Outstanding, The device is waiting for a response to a command - * - */ -static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - - switch (event) { - case IAP_RECV_F_LST: - /*iriap_send_ack(self);*/ - /*LM_Idle_request(idle); */ - - iriap_next_call_state(self, S_WAIT_FOR_CALL); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__, event); - break; - } -} - -/* - * Function state_s_replying (event, skb) - * - * S-Replying, The device is collecting a multiple part response - */ -static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - -/* - * Function state_s_wait_for_call (event, skb) - * - * S-Wait-for-Call - * - */ -static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - - -/* - * Function state_s_wait_active (event, skb) - * - * S-Wait-Active - * - */ -static void state_s_wait_active(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - -/************************************************************************** - * - * Server FSM - * - **************************************************************************/ - -/* - * Function state_r_disconnect (self, event, skb) - * - * LM-IAS server is disconnected (not processing any requests!) - * - */ -static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - - switch (event) { - case IAP_LM_CONNECT_INDICATION: - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (tx_skb == NULL) - return; - - /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - - irlmp_connect_response(self->lsap, tx_skb); - /*LM_Idle_request(idle); */ - - iriap_next_server_state(self, R_CALL); - - /* - * Jump to R-Connect FSM, we skip R-Waiting since we do not - * care about LM_Idle_request()! - */ - iriap_next_r_connect_state(self, R_RECEIVING); - break; - default: - pr_debug("%s(), unknown event %d\n", __func__, event); - break; - } -} - -/* - * Function state_r_call (self, event, skb) - */ -static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - switch (event) { - case IAP_LM_DISCONNECT_INDICATION: - /* Abort call */ - iriap_next_server_state(self, R_DISCONNECT); - iriap_next_r_connect_state(self, R_WAITING); - break; - default: - pr_debug("%s(), unknown event!\n", __func__); - break; - } -} - -/* - * R-Connect FSM - */ - -/* - * Function state_r_waiting (self, event, skb) - */ -static void state_r_waiting(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - -static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - -/* - * Function state_r_receiving (self, event, skb) - * - * We are receiving a command - * - */ -static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - switch (event) { - case IAP_RECV_F_LST: - iriap_next_r_connect_state(self, R_EXECUTE); - - iriap_call_indication(self, skb); - break; - default: - pr_debug("%s(), unknown event!\n", __func__); - break; - } -} - -/* - * Function state_r_execute (self, event, skb) - * - * The server is processing the request - * - */ -static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - switch (event) { - case IAP_CALL_RESPONSE: - /* - * Since we don't implement the Waiting state, we return - * to state Receiving instead, DB. - */ - iriap_next_r_connect_state(self, R_RECEIVING); - - /* Don't forget to refcount it - see - * iriap_getvaluebyclass_response(). */ - skb_get(skb); - - irlmp_data_request(self->lsap, skb); - break; - default: - pr_debug("%s(), unknown event!\n", __func__); - break; - } -} - -static void state_r_returning(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), event=%d\n", __func__, event); - - switch (event) { - case IAP_RECV_F_LST: - break; - default: - break; - } -} diff --git a/drivers/staging/irda/net/irias_object.c b/drivers/staging/irda/net/irias_object.c deleted file mode 100644 index 53b86d0e1630..000000000000 --- a/drivers/staging/irda/net/irias_object.c +++ /dev/null @@ -1,555 +0,0 @@ -/********************************************************************* - * - * Filename: irias_object.c - * Version: 0.3 - * Description: IAS object database and functions - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Oct 1 22:50:04 1998 - * Modified at: Wed Dec 15 11:23:16 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include - -#include -#include - -hashbin_t *irias_objects; - -/* - * Used when a missing value needs to be returned - */ -struct ias_value irias_missing = { IAS_MISSING, 0, 0, 0, {0}}; - - -/* - * Function ias_new_object (name, id) - * - * Create a new IAS object - * - */ -struct ias_object *irias_new_object( char *name, int id) -{ - struct ias_object *obj; - - obj = kzalloc(sizeof(struct ias_object), GFP_ATOMIC); - if (obj == NULL) { - net_warn_ratelimited("%s(), Unable to allocate object!\n", - __func__); - return NULL; - } - - obj->magic = IAS_OBJECT_MAGIC; - obj->name = kstrndup(name, IAS_MAX_CLASSNAME, GFP_ATOMIC); - if (!obj->name) { - net_warn_ratelimited("%s(), Unable to allocate name!\n", - __func__); - kfree(obj); - return NULL; - } - obj->id = id; - - /* Locking notes : the attrib spinlock has lower precendence - * than the objects spinlock. Never grap the objects spinlock - * while holding any attrib spinlock (risk of deadlock). Jean II */ - obj->attribs = hashbin_new(HB_LOCK); - - if (obj->attribs == NULL) { - net_warn_ratelimited("%s(), Unable to allocate attribs!\n", - __func__); - kfree(obj->name); - kfree(obj); - return NULL; - } - - return obj; -} -EXPORT_SYMBOL(irias_new_object); - -/* - * Function irias_delete_attrib (attrib) - * - * Delete given attribute and deallocate all its memory - * - */ -static void __irias_delete_attrib(struct ias_attrib *attrib) -{ - IRDA_ASSERT(attrib != NULL, return;); - IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;); - - kfree(attrib->name); - - irias_delete_value(attrib->value); - attrib->magic = ~IAS_ATTRIB_MAGIC; - - kfree(attrib); -} - -void __irias_delete_object(struct ias_object *obj) -{ - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - - kfree(obj->name); - - hashbin_delete(obj->attribs, (FREE_FUNC) __irias_delete_attrib); - - obj->magic = ~IAS_OBJECT_MAGIC; - - kfree(obj); -} - -/* - * Function irias_delete_object (obj) - * - * Remove object from hashbin and deallocate all attributes associated with - * with this object and the object itself - * - */ -int irias_delete_object(struct ias_object *obj) -{ - struct ias_object *node; - - IRDA_ASSERT(obj != NULL, return -1;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;); - - /* Remove from list */ - node = hashbin_remove_this(irias_objects, (irda_queue_t *) obj); - if (!node) - pr_debug("%s(), object already removed!\n", - __func__); - - /* Destroy */ - __irias_delete_object(obj); - - return 0; -} -EXPORT_SYMBOL(irias_delete_object); - -/* - * Function irias_delete_attrib (obj) - * - * Remove attribute from hashbin and, if it was the last attribute of - * the object, remove the object as well. - * - */ -int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib, - int cleanobject) -{ - struct ias_attrib *node; - - IRDA_ASSERT(obj != NULL, return -1;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;); - IRDA_ASSERT(attrib != NULL, return -1;); - - /* Remove attribute from object */ - node = hashbin_remove_this(obj->attribs, (irda_queue_t *) attrib); - if (!node) - return 0; /* Already removed or non-existent */ - - /* Deallocate attribute */ - __irias_delete_attrib(node); - - /* Check if object has still some attributes, destroy it if none. - * At first glance, this look dangerous, as the kernel reference - * various IAS objects. However, we only use this function on - * user attributes, not kernel attributes, so there is no risk - * of deleting a kernel object this way. Jean II */ - node = (struct ias_attrib *) hashbin_get_first(obj->attribs); - if (cleanobject && !node) - irias_delete_object(obj); - - return 0; -} - -/* - * Function irias_insert_object (obj) - * - * Insert an object into the LM-IAS database - * - */ -void irias_insert_object(struct ias_object *obj) -{ - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - - hashbin_insert(irias_objects, (irda_queue_t *) obj, 0, obj->name); -} -EXPORT_SYMBOL(irias_insert_object); - -/* - * Function irias_find_object (name) - * - * Find object with given name - * - */ -struct ias_object *irias_find_object(char *name) -{ - IRDA_ASSERT(name != NULL, return NULL;); - - /* Unsafe (locking), object might change */ - return hashbin_lock_find(irias_objects, 0, name); -} -EXPORT_SYMBOL(irias_find_object); - -/* - * Function irias_find_attrib (obj, name) - * - * Find named attribute in object - * - */ -struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name) -{ - struct ias_attrib *attrib; - - IRDA_ASSERT(obj != NULL, return NULL;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return NULL;); - IRDA_ASSERT(name != NULL, return NULL;); - - attrib = hashbin_lock_find(obj->attribs, 0, name); - if (attrib == NULL) - return NULL; - - /* Unsafe (locking), attrib might change */ - return attrib; -} - -/* - * Function irias_add_attribute (obj, attrib) - * - * Add attribute to object - * - */ -static void irias_add_attrib(struct ias_object *obj, struct ias_attrib *attrib, - int owner) -{ - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - - IRDA_ASSERT(attrib != NULL, return;); - IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;); - - /* Set if attrib is owned by kernel or user space */ - attrib->value->owner = owner; - - hashbin_insert(obj->attribs, (irda_queue_t *) attrib, 0, attrib->name); -} - -/* - * Function irias_object_change_attribute (obj_name, attrib_name, new_value) - * - * Change the value of an objects attribute. - * - */ -int irias_object_change_attribute(char *obj_name, char *attrib_name, - struct ias_value *new_value) -{ - struct ias_object *obj; - struct ias_attrib *attrib; - unsigned long flags; - - /* Find object */ - obj = hashbin_lock_find(irias_objects, 0, obj_name); - if (obj == NULL) { - net_warn_ratelimited("%s: Unable to find object: %s\n", - __func__, obj_name); - return -1; - } - - /* Slightly unsafe (obj might get removed under us) */ - spin_lock_irqsave(&obj->attribs->hb_spinlock, flags); - - /* Find attribute */ - attrib = hashbin_find(obj->attribs, 0, attrib_name); - if (attrib == NULL) { - net_warn_ratelimited("%s: Unable to find attribute: %s\n", - __func__, attrib_name); - spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags); - return -1; - } - - if ( attrib->value->type != new_value->type) { - pr_debug("%s(), changing value type not allowed!\n", - __func__); - spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags); - return -1; - } - - /* Delete old value */ - irias_delete_value(attrib->value); - - /* Insert new value */ - attrib->value = new_value; - - /* Success */ - spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags); - return 0; -} -EXPORT_SYMBOL(irias_object_change_attribute); - -/* - * Function irias_object_add_integer_attrib (obj, name, value) - * - * Add an integer attribute to an LM-IAS object - * - */ -void irias_add_integer_attrib(struct ias_object *obj, char *name, int value, - int owner) -{ - struct ias_attrib *attrib; - - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - IRDA_ASSERT(name != NULL, return;); - - attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC); - if (attrib == NULL) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - return; - } - - attrib->magic = IAS_ATTRIB_MAGIC; - attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); - - /* Insert value */ - attrib->value = irias_new_integer_value(value); - if (!attrib->name || !attrib->value) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - if (attrib->value) - irias_delete_value(attrib->value); - kfree(attrib->name); - kfree(attrib); - return; - } - - irias_add_attrib(obj, attrib, owner); -} -EXPORT_SYMBOL(irias_add_integer_attrib); - - /* - * Function irias_add_octseq_attrib (obj, name, octet_seq, len) - * - * Add a octet sequence attribute to an LM-IAS object - * - */ - -void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets, - int len, int owner) -{ - struct ias_attrib *attrib; - - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - - IRDA_ASSERT(name != NULL, return;); - IRDA_ASSERT(octets != NULL, return;); - - attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC); - if (attrib == NULL) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - return; - } - - attrib->magic = IAS_ATTRIB_MAGIC; - attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); - - attrib->value = irias_new_octseq_value( octets, len); - if (!attrib->name || !attrib->value) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - if (attrib->value) - irias_delete_value(attrib->value); - kfree(attrib->name); - kfree(attrib); - return; - } - - irias_add_attrib(obj, attrib, owner); -} -EXPORT_SYMBOL(irias_add_octseq_attrib); - -/* - * Function irias_object_add_string_attrib (obj, string) - * - * Add a string attribute to an LM-IAS object - * - */ -void irias_add_string_attrib(struct ias_object *obj, char *name, char *value, - int owner) -{ - struct ias_attrib *attrib; - - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - - IRDA_ASSERT(name != NULL, return;); - IRDA_ASSERT(value != NULL, return;); - - attrib = kzalloc(sizeof( struct ias_attrib), GFP_ATOMIC); - if (attrib == NULL) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - return; - } - - attrib->magic = IAS_ATTRIB_MAGIC; - attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); - - attrib->value = irias_new_string_value(value); - if (!attrib->name || !attrib->value) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - if (attrib->value) - irias_delete_value(attrib->value); - kfree(attrib->name); - kfree(attrib); - return; - } - - irias_add_attrib(obj, attrib, owner); -} -EXPORT_SYMBOL(irias_add_string_attrib); - -/* - * Function irias_new_integer_value (integer) - * - * Create new IAS integer value - * - */ -struct ias_value *irias_new_integer_value(int integer) -{ - struct ias_value *value; - - value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) - return NULL; - - value->type = IAS_INTEGER; - value->len = 4; - value->t.integer = integer; - - return value; -} -EXPORT_SYMBOL(irias_new_integer_value); - -/* - * Function irias_new_string_value (string) - * - * Create new IAS string value - * - * Per IrLMP 1.1, 4.3.3.2, strings are up to 256 chars - Jean II - */ -struct ias_value *irias_new_string_value(char *string) -{ - struct ias_value *value; - - value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) - return NULL; - - value->type = IAS_STRING; - value->charset = CS_ASCII; - value->t.string = kstrndup(string, IAS_MAX_STRING, GFP_ATOMIC); - if (!value->t.string) { - net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__); - kfree(value); - return NULL; - } - - value->len = strlen(value->t.string); - - return value; -} - -/* - * Function irias_new_octseq_value (octets, len) - * - * Create new IAS octet-sequence value - * - * Per IrLMP 1.1, 4.3.3.2, octet-sequence are up to 1024 bytes - Jean II - */ -struct ias_value *irias_new_octseq_value(__u8 *octseq , int len) -{ - struct ias_value *value; - - value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) - return NULL; - - value->type = IAS_OCT_SEQ; - /* Check length */ - if(len > IAS_MAX_OCTET_STRING) - len = IAS_MAX_OCTET_STRING; - value->len = len; - - value->t.oct_seq = kmemdup(octseq, len, GFP_ATOMIC); - if (value->t.oct_seq == NULL){ - net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__); - kfree(value); - return NULL; - } - return value; -} - -struct ias_value *irias_new_missing_value(void) -{ - struct ias_value *value; - - value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) - return NULL; - - value->type = IAS_MISSING; - - return value; -} - -/* - * Function irias_delete_value (value) - * - * Delete IAS value - * - */ -void irias_delete_value(struct ias_value *value) -{ - IRDA_ASSERT(value != NULL, return;); - - switch (value->type) { - case IAS_INTEGER: /* Fallthrough */ - case IAS_MISSING: - /* No need to deallocate */ - break; - case IAS_STRING: - /* Deallocate string */ - kfree(value->t.string); - break; - case IAS_OCT_SEQ: - /* Deallocate byte stream */ - kfree(value->t.oct_seq); - break; - default: - pr_debug("%s(), Unknown value type!\n", __func__); - break; - } - kfree(value); -} -EXPORT_SYMBOL(irias_delete_value); diff --git a/drivers/staging/irda/net/irlan/Kconfig b/drivers/staging/irda/net/irlan/Kconfig deleted file mode 100644 index 951abc2e3a7f..000000000000 --- a/drivers/staging/irda/net/irlan/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -config IRLAN - tristate "IrLAN protocol" - depends on IRDA - help - Say Y here if you want to build support for the IrLAN protocol. - To compile it as a module, choose M here: the module will be called - irlan. IrLAN emulates an Ethernet and makes it possible to put up - a wireless LAN using infrared beams. - - The IrLAN protocol can be used to talk with infrared access points - like the HP NetbeamIR, or the ESI JetEye NET. You can also connect - to another Linux machine running the IrLAN protocol for ad-hoc - networking! - diff --git a/drivers/staging/irda/net/irlan/Makefile b/drivers/staging/irda/net/irlan/Makefile deleted file mode 100644 index 94eefbc8e6b9..000000000000 --- a/drivers/staging/irda/net/irlan/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux IrDA IrLAN protocol layer. -# - -obj-$(CONFIG_IRLAN) += irlan.o - -irlan-y := irlan_common.o irlan_eth.o irlan_event.o irlan_client.o irlan_provider.o irlan_filter.o irlan_provider_event.o irlan_client_event.o diff --git a/drivers/staging/irda/net/irlan/irlan_client.c b/drivers/staging/irda/net/irlan/irlan_client.c deleted file mode 100644 index 0b65e80849ae..000000000000 --- a/drivers/staging/irda/net/irlan/irlan_client.c +++ /dev/null @@ -1,559 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_client.c - * Version: 0.9 - * Description: IrDA LAN Access Protocol (IrLAN) Client - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Tue Dec 14 15:47:02 1999 - * Modified by: Dag Brattli - * Sources: skeleton.c by Donald Becker - * slip.c by Laurence Culhane, - * Fred N. van Kempen, - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#undef CONFIG_IRLAN_GRATUITOUS_ARP - -static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *); -static int irlan_client_ctrl_data_indication(void *instance, void *sap, - struct sk_buff *skb); -static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *); -static void irlan_check_response_param(struct irlan_cb *self, char *param, - char *value, int val_len); -static void irlan_client_open_ctrl_tsap(struct irlan_cb *self); - -static void irlan_client_kick_timer_expired(struct timer_list *t) -{ - struct irlan_cb *self = from_timer(self, t, client.kick_timer); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* - * If we are in peer mode, the client may not have got the discovery - * indication it needs to make progress. If the client is still in - * IDLE state, we must kick it to, but only if the provider is not IDLE - */ - if ((self->provider.access_type == ACCESS_PEER) && - (self->client.state == IRLAN_IDLE) && - (self->provider.state != IRLAN_IDLE)) { - irlan_client_wakeup(self, self->saddr, self->daddr); - } -} - -static void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout) -{ - irda_start_timer(&self->client.kick_timer, timeout, - irlan_client_kick_timer_expired); -} - -/* - * Function irlan_client_wakeup (self, saddr, daddr) - * - * Wake up client - * - */ -void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* - * Check if we are already awake, or if we are a provider in direct - * mode (in that case we must leave the client idle - */ - if ((self->client.state != IRLAN_IDLE) || - (self->provider.access_type == ACCESS_DIRECT)) - { - pr_debug("%s(), already awake!\n", __func__); - return; - } - - /* Addresses may have changed! */ - self->saddr = saddr; - self->daddr = daddr; - - if (self->disconnect_reason == LM_USER_REQUEST) { - pr_debug("%s(), still stopped by user\n", __func__); - return; - } - - /* Open TSAPs */ - irlan_client_open_ctrl_tsap(self); - irlan_open_data_tsap(self); - - irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL); - - /* Start kick timer */ - irlan_client_start_kick_timer(self, 2*HZ); -} - -/* - * Function irlan_discovery_indication (daddr) - * - * Remote device with IrLAN server support discovered - * - */ -void irlan_client_discovery_indication(discinfo_t *discovery, - DISCOVERY_MODE mode, - void *priv) -{ - struct irlan_cb *self; - __u32 saddr, daddr; - - IRDA_ASSERT(discovery != NULL, return;); - - /* - * I didn't check it, but I bet that IrLAN suffer from the same - * deficiency as IrComm and doesn't handle two instances - * simultaneously connecting to each other. - * Same workaround, drop passive discoveries. - * Jean II */ - if(mode == DISCOVERY_PASSIVE) - return; - - saddr = discovery->saddr; - daddr = discovery->daddr; - - /* Find instance */ - rcu_read_lock(); - self = irlan_get_any(); - if (self) { - IRDA_ASSERT(self->magic == IRLAN_MAGIC, goto out;); - - pr_debug("%s(), Found instance (%08x)!\n", __func__ , - daddr); - - irlan_client_wakeup(self, saddr, daddr); - } -IRDA_ASSERT_LABEL(out:) - rcu_read_unlock(); -} - -/* - * Function irlan_client_data_indication (handle, skb) - * - * This function gets the data that is received on the control channel - * - */ -static int irlan_client_ctrl_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct irlan_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb); - - /* Ready for a new command */ - pr_debug("%s(), clearing tx_busy\n", __func__); - self->client.tx_busy = FALSE; - - /* Check if we have some queued commands waiting to be sent */ - irlan_run_ctrl_tx_queue(self); - - return 0; -} - -static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *userdata) -{ - struct irlan_cb *self; - struct tsap_cb *tsap; - struct sk_buff *skb; - - pr_debug("%s(), reason=%d\n", __func__ , reason); - - self = instance; - tsap = sap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - IRDA_ASSERT(tsap != NULL, return;); - IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;); - - IRDA_ASSERT(tsap == self->client.tsap_ctrl, return;); - - /* Remove frames queued on the control channel */ - while ((skb = skb_dequeue(&self->client.txq)) != NULL) { - dev_kfree_skb(skb); - } - self->client.tx_busy = FALSE; - - irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); -} - -/* - * Function irlan_client_open_tsaps (self) - * - * Initialize callbacks and open IrTTP TSAPs - * - */ -static void irlan_client_open_ctrl_tsap(struct irlan_cb *self) -{ - struct tsap_cb *tsap; - notify_t notify; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Check if already open */ - if (self->client.tsap_ctrl) - return; - - irda_notify_init(¬ify); - - /* Set up callbacks */ - notify.data_indication = irlan_client_ctrl_data_indication; - notify.connect_confirm = irlan_client_ctrl_connect_confirm; - notify.disconnect_indication = irlan_client_ctrl_disconnect_indication; - notify.instance = self; - strlcpy(notify.name, "IrLAN ctrl (c)", sizeof(notify.name)); - - tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, ¬ify); - if (!tsap) { - pr_debug("%s(), Got no tsap!\n", __func__); - return; - } - self->client.tsap_ctrl = tsap; -} - -/* - * Function irlan_client_connect_confirm (handle, skb) - * - * Connection to peer IrLAN laye confirmed - * - */ -static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct irlan_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - self->client.max_sdu_size = max_sdu_size; - self->client.max_header_size = max_header_size; - - /* TODO: we could set the MTU depending on the max_sdu_size */ - - irlan_do_client_event(self, IRLAN_CONNECT_COMPLETE, NULL); -} - -/* - * Function print_ret_code (code) - * - * Print return code of request to peer IrLAN layer. - * - */ -static void print_ret_code(__u8 code) -{ - switch(code) { - case 0: - printk(KERN_INFO "Success\n"); - break; - case 1: - net_warn_ratelimited("IrLAN: Insufficient resources\n"); - break; - case 2: - net_warn_ratelimited("IrLAN: Invalid command format\n"); - break; - case 3: - net_warn_ratelimited("IrLAN: Command not supported\n"); - break; - case 4: - net_warn_ratelimited("IrLAN: Parameter not supported\n"); - break; - case 5: - net_warn_ratelimited("IrLAN: Value not supported\n"); - break; - case 6: - net_warn_ratelimited("IrLAN: Not open\n"); - break; - case 7: - net_warn_ratelimited("IrLAN: Authentication required\n"); - break; - case 8: - net_warn_ratelimited("IrLAN: Invalid password\n"); - break; - case 9: - net_warn_ratelimited("IrLAN: Protocol error\n"); - break; - case 255: - net_warn_ratelimited("IrLAN: Asynchronous status\n"); - break; - } -} - -/* - * Function irlan_client_parse_response (self, skb) - * - * Extract all parameters from received buffer, then feed them to - * check_params for parsing - */ -void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb) -{ - __u8 *frame; - __u8 *ptr; - int count; - int ret; - __u16 val_len; - int i; - char *name; - char *value; - - IRDA_ASSERT(skb != NULL, return;); - - pr_debug("%s() skb->len=%d\n", __func__ , (int)skb->len); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - if (!skb) { - net_err_ratelimited("%s(), Got NULL skb!\n", __func__); - return; - } - frame = skb->data; - - /* - * Check return code and print it if not success - */ - if (frame[0]) { - print_ret_code(frame[0]); - return; - } - - name = kmalloc(255, GFP_ATOMIC); - if (!name) - return; - value = kmalloc(1016, GFP_ATOMIC); - if (!value) { - kfree(name); - return; - } - - /* How many parameters? */ - count = frame[1]; - - pr_debug("%s(), got %d parameters\n", __func__ , count); - - ptr = frame+2; - - /* For all parameters */ - for (i=0; imagic == IRLAN_MAGIC, return;); - - /* Media type */ - if (strcmp(param, "MEDIA") == 0) { - if (strcmp(value, "802.3") == 0) - self->media = MEDIA_802_3; - else - self->media = MEDIA_802_5; - return; - } - if (strcmp(param, "FILTER_TYPE") == 0) { - if (strcmp(value, "DIRECTED") == 0) - self->client.filter_type |= IRLAN_DIRECTED; - else if (strcmp(value, "FUNCTIONAL") == 0) - self->client.filter_type |= IRLAN_FUNCTIONAL; - else if (strcmp(value, "GROUP") == 0) - self->client.filter_type |= IRLAN_GROUP; - else if (strcmp(value, "MAC_FRAME") == 0) - self->client.filter_type |= IRLAN_MAC_FRAME; - else if (strcmp(value, "MULTICAST") == 0) - self->client.filter_type |= IRLAN_MULTICAST; - else if (strcmp(value, "BROADCAST") == 0) - self->client.filter_type |= IRLAN_BROADCAST; - else if (strcmp(value, "IPX_SOCKET") == 0) - self->client.filter_type |= IRLAN_IPX_SOCKET; - - } - if (strcmp(param, "ACCESS_TYPE") == 0) { - if (strcmp(value, "DIRECT") == 0) - self->client.access_type = ACCESS_DIRECT; - else if (strcmp(value, "PEER") == 0) - self->client.access_type = ACCESS_PEER; - else if (strcmp(value, "HOSTED") == 0) - self->client.access_type = ACCESS_HOSTED; - else { - pr_debug("%s(), unknown access type!\n", __func__); - } - } - /* IRLAN version */ - if (strcmp(param, "IRLAN_VER") == 0) { - pr_debug("IrLAN version %d.%d\n", (__u8)value[0], - (__u8)value[1]); - - self->version[0] = value[0]; - self->version[1] = value[1]; - return; - } - /* Which remote TSAP to use for data channel */ - if (strcmp(param, "DATA_CHAN") == 0) { - self->dtsap_sel_data = value[0]; - pr_debug("Data TSAP = %02x\n", self->dtsap_sel_data); - return; - } - if (strcmp(param, "CON_ARB") == 0) { - memcpy(&tmp_cpu, value, 2); /* Align value */ - le16_to_cpus(&tmp_cpu); /* Convert to host order */ - self->client.recv_arb_val = tmp_cpu; - pr_debug("%s(), receive arb val=%d\n", __func__ , - self->client.recv_arb_val); - } - if (strcmp(param, "MAX_FRAME") == 0) { - memcpy(&tmp_cpu, value, 2); /* Align value */ - le16_to_cpus(&tmp_cpu); /* Convert to host order */ - self->client.max_frame = tmp_cpu; - pr_debug("%s(), max frame=%d\n", __func__ , - self->client.max_frame); - } - - /* RECONNECT_KEY, in case the link goes down! */ - if (strcmp(param, "RECONNECT_KEY") == 0) { - pr_debug("Got reconnect key: "); - /* for (i = 0; i < val_len; i++) */ -/* printk("%02x", value[i]); */ - memcpy(self->client.reconnect_key, value, val_len); - self->client.key_len = val_len; - pr_debug("\n"); - } - /* FILTER_ENTRY, have we got an ethernet address? */ - if (strcmp(param, "FILTER_ENTRY") == 0) { - bytes = value; - pr_debug("Ethernet address = %pM\n", bytes); - for (i = 0; i < 6; i++) - self->dev->dev_addr[i] = bytes[i]; - } -} - -/* - * Function irlan_client_get_value_confirm (obj_id, value) - * - * Got results from remote LM-IAS - * - */ -void irlan_client_get_value_confirm(int result, __u16 obj_id, - struct ias_value *value, void *priv) -{ - struct irlan_cb *self; - - IRDA_ASSERT(priv != NULL, return;); - - self = priv; - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* We probably don't need to make any more queries */ - iriap_close(self->client.iriap); - self->client.iriap = NULL; - - /* Check if request succeeded */ - if (result != IAS_SUCCESS) { - pr_debug("%s(), got NULL value!\n", __func__); - irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, - NULL); - return; - } - - switch (value->type) { - case IAS_INTEGER: - self->dtsap_sel_ctrl = value->t.integer; - - if (value->t.integer != -1) { - irlan_do_client_event(self, IRLAN_IAS_PROVIDER_AVAIL, - NULL); - return; - } - irias_delete_value(value); - break; - default: - pr_debug("%s(), unknown type!\n", __func__); - break; - } - irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, NULL); -} diff --git a/drivers/staging/irda/net/irlan/irlan_client_event.c b/drivers/staging/irda/net/irlan/irlan_client_event.c deleted file mode 100644 index cc93fabbbb19..000000000000 --- a/drivers/staging/irda/net/irlan/irlan_client_event.c +++ /dev/null @@ -1,511 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_client_event.c - * Version: 0.9 - * Description: IrLAN client state machine - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Sun Dec 26 21:52:24 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static int irlan_client_state_idle (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_conn (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_info (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_open (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_wait (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_arb (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_data (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_sync (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); - -static int (*state[])(struct irlan_cb *, IRLAN_EVENT event, struct sk_buff *) = -{ - irlan_client_state_idle, - irlan_client_state_query, - irlan_client_state_conn, - irlan_client_state_info, - irlan_client_state_media, - irlan_client_state_open, - irlan_client_state_wait, - irlan_client_state_arb, - irlan_client_state_data, - irlan_client_state_close, - irlan_client_state_sync -}; - -void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - (*state[ self->client.state]) (self, event, skb); -} - -/* - * Function irlan_client_state_idle (event, skb, info) - * - * IDLE, We are waiting for an indication that there is a provider - * available. - */ -static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - switch (event) { - case IRLAN_DISCOVERY_INDICATION: - if (self->client.iriap) { - net_warn_ratelimited("%s(), busy with a previous query\n", - __func__); - return -EBUSY; - } - - self->client.iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - irlan_client_get_value_confirm); - /* Get some values from peer IAS */ - irlan_next_client_state(self, IRLAN_QUERY); - iriap_getvaluebyclass_request(self->client.iriap, - self->saddr, self->daddr, - "IrLAN", "IrDA:TinyTP:LsapSel"); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_query (event, skb, info) - * - * QUERY, We have queryed the remote IAS and is ready to connect - * to provider, just waiting for the confirm. - * - */ -static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - switch(event) { - case IRLAN_IAS_PROVIDER_AVAIL: - IRDA_ASSERT(self->dtsap_sel_ctrl != 0, return -1;); - - self->client.open_retries = 0; - - irttp_connect_request(self->client.tsap_ctrl, - self->dtsap_sel_ctrl, - self->saddr, self->daddr, NULL, - IRLAN_MTU, NULL); - irlan_next_client_state(self, IRLAN_CONN); - break; - case IRLAN_IAS_PROVIDER_NOT_AVAIL: - pr_debug("%s(), IAS_PROVIDER_NOT_AVAIL\n", __func__); - irlan_next_client_state(self, IRLAN_IDLE); - - /* Give the client a kick! */ - if ((self->provider.access_type == ACCESS_PEER) && - (self->provider.state != IRLAN_IDLE)) - irlan_client_wakeup(self, self->saddr, self->daddr); - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_conn (event, skb, info) - * - * CONN, We have connected to a provider but has not issued any - * commands yet. - * - */ -static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch (event) { - case IRLAN_CONNECT_COMPLETE: - /* Send getinfo cmd */ - irlan_get_provider_info(self); - irlan_next_client_state(self, IRLAN_INFO); - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_info (self, event, skb, info) - * - * INFO, We have issued a GetInfo command and is awaiting a reply. - */ -static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch (event) { - case IRLAN_DATA_INDICATION: - IRDA_ASSERT(skb != NULL, return -1;); - - irlan_client_parse_response(self, skb); - - irlan_next_client_state(self, IRLAN_MEDIA); - - irlan_get_media_char(self); - break; - - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_media (self, event, skb, info) - * - * MEDIA, The irlan_client has issued a GetMedia command and is awaiting a - * reply. - * - */ -static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_DATA_INDICATION: - irlan_client_parse_response(self, skb); - irlan_open_data_channel(self); - irlan_next_client_state(self, IRLAN_OPEN); - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_open (self, event, skb, info) - * - * OPEN, The irlan_client has issued a OpenData command and is awaiting a - * reply - * - */ -static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - struct qos_info qos; - - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_DATA_INDICATION: - irlan_client_parse_response(self, skb); - - /* - * Check if we have got the remote TSAP for data - * communications - */ - IRDA_ASSERT(self->dtsap_sel_data != 0, return -1;); - - /* Check which access type we are dealing with */ - switch (self->client.access_type) { - case ACCESS_PEER: - if (self->provider.state == IRLAN_OPEN) { - - irlan_next_client_state(self, IRLAN_ARB); - irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, - NULL); - } else { - - irlan_next_client_state(self, IRLAN_WAIT); - } - break; - case ACCESS_DIRECT: - case ACCESS_HOSTED: - qos.link_disc_time.bits = 0x01; /* 3 secs */ - - irttp_connect_request(self->tsap_data, - self->dtsap_sel_data, - self->saddr, self->daddr, &qos, - IRLAN_MTU, NULL); - - irlan_next_client_state(self, IRLAN_DATA); - break; - default: - pr_debug("%s(), unknown access type!\n", __func__); - break; - } - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_wait (self, event, skb, info) - * - * WAIT, The irlan_client is waiting for the local provider to enter the - * provider OPEN state. - * - */ -static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_PROVIDER_SIGNAL: - irlan_next_client_state(self, IRLAN_ARB); - irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, NULL); - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - struct qos_info qos; - - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_CHECK_CON_ARB: - if (self->client.recv_arb_val == self->provider.send_arb_val) { - irlan_next_client_state(self, IRLAN_CLOSE); - irlan_close_data_channel(self); - } else if (self->client.recv_arb_val < - self->provider.send_arb_val) - { - qos.link_disc_time.bits = 0x01; /* 3 secs */ - - irlan_next_client_state(self, IRLAN_DATA); - irttp_connect_request(self->tsap_data, - self->dtsap_sel_data, - self->saddr, self->daddr, &qos, - IRLAN_MTU, NULL); - } else if (self->client.recv_arb_val > - self->provider.send_arb_val) - { - pr_debug("%s(), lost the battle :-(\n", __func__); - } - break; - case IRLAN_DATA_CONNECT_INDICATION: - irlan_next_client_state(self, IRLAN_DATA); - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_data (self, event, skb, info) - * - * DATA, The data channel is connected, allowing data transfers between - * the local and remote machines. - * - */ -static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - switch(event) { - case IRLAN_DATA_INDICATION: - irlan_client_parse_response(self, skb); - break; - case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_close (self, event, skb, info) - * - * - * - */ -static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_sync (self, event, skb, info) - * - * - * - */ -static int irlan_client_state_sync(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - if (skb) - dev_kfree_skb(skb); - - return 0; -} - - - - - - - - - - - - - diff --git a/drivers/staging/irda/net/irlan/irlan_common.c b/drivers/staging/irda/net/irlan/irlan_common.c deleted file mode 100644 index fdcd7147007d..000000000000 --- a/drivers/staging/irda/net/irlan/irlan_common.c +++ /dev/null @@ -1,1176 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_common.c - * Version: 0.9 - * Description: IrDA LAN Access Protocol Implementation - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Sun Dec 26 21:53:10 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#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 - - -/* extern char sysctl_devname[]; */ - -/* - * Master structure - */ -static LIST_HEAD(irlans); - -static void *ckey; -static void *skey; - -/* Module parameters */ -static bool eth; /* Use "eth" or "irlan" name for devices */ -static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */ - -#ifdef CONFIG_PROC_FS -static const char *const irlan_access[] = { - "UNKNOWN", - "DIRECT", - "PEER", - "HOSTED" -}; - -static const char *const irlan_media[] = { - "UNKNOWN", - "802.3", - "802.5" -}; - -extern struct proc_dir_entry *proc_irda; - -static int irlan_seq_open(struct inode *inode, struct file *file); - -static const struct file_operations irlan_fops = { - .owner = THIS_MODULE, - .open = irlan_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -extern struct proc_dir_entry *proc_irda; -#endif /* CONFIG_PROC_FS */ - -static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr); -static void __irlan_close(struct irlan_cb *self); -static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, - __u8 value_byte, __u16 value_short, - __u8 *value_array, __u16 value_len); -static void irlan_open_unicast_addr(struct irlan_cb *self); -static void irlan_get_unicast_addr(struct irlan_cb *self); -void irlan_close_tsaps(struct irlan_cb *self); - -/* - * Function irlan_init (void) - * - * Initialize IrLAN layer - * - */ -static int __init irlan_init(void) -{ - struct irlan_cb *new; - __u16 hints; - -#ifdef CONFIG_PROC_FS - { struct proc_dir_entry *proc; - proc = proc_create("irlan", 0, proc_irda, &irlan_fops); - if (!proc) { - printk(KERN_ERR "irlan_init: can't create /proc entry!\n"); - return -ENODEV; - } - } -#endif /* CONFIG_PROC_FS */ - - hints = irlmp_service_to_hint(S_LAN); - - /* Register with IrLMP as a client */ - ckey = irlmp_register_client(hints, &irlan_client_discovery_indication, - NULL, NULL); - if (!ckey) - goto err_ckey; - - /* Register with IrLMP as a service */ - skey = irlmp_register_service(hints); - if (!skey) - goto err_skey; - - /* Start the master IrLAN instance (the only one for now) */ - new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY); - if (!new) - goto err_open; - - /* The master will only open its (listen) control TSAP */ - irlan_provider_open_ctrl_tsap(new); - - /* Do some fast discovery! */ - irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); - - return 0; - -err_open: - irlmp_unregister_service(skey); -err_skey: - irlmp_unregister_client(ckey); -err_ckey: -#ifdef CONFIG_PROC_FS - remove_proc_entry("irlan", proc_irda); -#endif /* CONFIG_PROC_FS */ - - return -ENOMEM; -} - -static void __exit irlan_cleanup(void) -{ - struct irlan_cb *self, *next; - - irlmp_unregister_client(ckey); - irlmp_unregister_service(skey); - -#ifdef CONFIG_PROC_FS - remove_proc_entry("irlan", proc_irda); -#endif /* CONFIG_PROC_FS */ - - /* Cleanup any leftover network devices */ - rtnl_lock(); - list_for_each_entry_safe(self, next, &irlans, dev_list) { - __irlan_close(self); - } - rtnl_unlock(); -} - -/* - * Function irlan_open (void) - * - * Open new instance of a client/provider, we should only register the - * network device if this instance is ment for a particular client/provider - */ -static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr) -{ - struct net_device *dev; - struct irlan_cb *self; - - /* Create network device with irlan */ - dev = alloc_irlandev(eth ? "eth%d" : "irlan%d"); - if (!dev) - return NULL; - - self = netdev_priv(dev); - self->dev = dev; - - /* - * Initialize local device structure - */ - self->magic = IRLAN_MAGIC; - self->saddr = saddr; - self->daddr = daddr; - - /* Provider access can only be PEER, DIRECT, or HOSTED */ - self->provider.access_type = access; - if (access == ACCESS_DIRECT) { - /* - * Since we are emulating an IrLAN sever we will have to - * give ourself an ethernet address! - */ - dev->dev_addr[0] = 0x40; - dev->dev_addr[1] = 0x00; - dev->dev_addr[2] = 0x00; - dev->dev_addr[3] = 0x00; - get_random_bytes(dev->dev_addr+4, 1); - get_random_bytes(dev->dev_addr+5, 1); - } - - self->media = MEDIA_802_3; - self->disconnect_reason = LM_USER_REQUEST; - timer_setup(&self->watchdog_timer, NULL, 0); - timer_setup(&self->client.kick_timer, NULL, 0); - init_waitqueue_head(&self->open_wait); - - skb_queue_head_init(&self->client.txq); - - irlan_next_client_state(self, IRLAN_IDLE); - irlan_next_provider_state(self, IRLAN_IDLE); - - if (register_netdev(dev)) { - pr_debug("%s(), register_netdev() failed!\n", - __func__); - self = NULL; - free_netdev(dev); - } else { - rtnl_lock(); - list_add_rcu(&self->dev_list, &irlans); - rtnl_unlock(); - } - - return self; -} -/* - * Function __irlan_close (self) - * - * This function closes and deallocates the IrLAN client instances. Be - * aware that other functions which calls client_close() must - * remove self from irlans list first. - */ -static void __irlan_close(struct irlan_cb *self) -{ - ASSERT_RTNL(); - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - del_timer_sync(&self->watchdog_timer); - del_timer_sync(&self->client.kick_timer); - - /* Close all open connections and remove TSAPs */ - irlan_close_tsaps(self); - - if (self->client.iriap) - iriap_close(self->client.iriap); - - /* Remove frames queued on the control channel */ - skb_queue_purge(&self->client.txq); - - /* Unregister and free self via destructor */ - unregister_netdevice(self->dev); -} - -/* Find any instance of irlan, used for client discovery wakeup */ -struct irlan_cb *irlan_get_any(void) -{ - struct irlan_cb *self; - - list_for_each_entry_rcu(self, &irlans, dev_list) { - return self; - } - return NULL; -} - -/* - * Function irlan_connect_indication (instance, sap, qos, max_sdu_size, skb) - * - * Here we receive the connect indication for the data channel - * - */ -static void irlan_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct irlan_cb *self; - struct tsap_cb *tsap; - - self = instance; - tsap = sap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - IRDA_ASSERT(tsap == self->tsap_data,return;); - - self->max_sdu_size = max_sdu_size; - self->max_header_size = max_header_size; - - pr_debug("%s: We are now connected!\n", __func__); - - del_timer(&self->watchdog_timer); - - /* If you want to pass the skb to *both* state machines, you will - * need to skb_clone() it, so that you don't free it twice. - * As the state machines don't need it, git rid of it here... - * Jean II */ - if (skb) - dev_kfree_skb(skb); - - irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL); - irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL); - - if (self->provider.access_type == ACCESS_PEER) { - /* - * Data channel is open, so we are now allowed to - * configure the remote filter - */ - irlan_get_unicast_addr(self); - irlan_open_unicast_addr(self); - } - /* Ready to transfer Ethernet frames (at last) */ - netif_start_queue(self->dev); /* Clear reason */ -} - -static void irlan_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct irlan_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - self->max_sdu_size = max_sdu_size; - self->max_header_size = max_header_size; - - /* TODO: we could set the MTU depending on the max_sdu_size */ - - pr_debug("%s: We are now connected!\n", __func__); - del_timer(&self->watchdog_timer); - - /* - * Data channel is open, so we are now allowed to configure the remote - * filter - */ - irlan_get_unicast_addr(self); - irlan_open_unicast_addr(self); - - /* Open broadcast and multicast filter by default */ - irlan_set_broadcast_filter(self, TRUE); - irlan_set_multicast_filter(self, TRUE); - - /* Ready to transfer Ethernet frames */ - netif_start_queue(self->dev); - self->disconnect_reason = 0; /* Clear reason */ - wake_up_interruptible(&self->open_wait); -} - -/* - * Function irlan_client_disconnect_indication (handle) - * - * Callback function for the IrTTP layer. Indicates a disconnection of - * the specified connection (handle) - */ -static void irlan_disconnect_indication(void *instance, - void *sap, LM_REASON reason, - struct sk_buff *userdata) -{ - struct irlan_cb *self; - struct tsap_cb *tsap; - - pr_debug("%s(), reason=%d\n", __func__ , reason); - - self = instance; - tsap = sap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - IRDA_ASSERT(tsap != NULL, return;); - IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;); - - IRDA_ASSERT(tsap == self->tsap_data, return;); - - pr_debug("IrLAN, data channel disconnected by peer!\n"); - - /* Save reason so we know if we should try to reconnect or not */ - self->disconnect_reason = reason; - - switch (reason) { - case LM_USER_REQUEST: /* User request */ - pr_debug("%s(), User requested\n", __func__); - break; - case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */ - pr_debug("%s(), Unexpected IrLAP disconnect\n", __func__); - break; - case LM_CONNECT_FAILURE: /* Failed to establish IrLAP connection */ - pr_debug("%s(), IrLAP connect failed\n", __func__); - break; - case LM_LAP_RESET: /* IrLAP reset */ - pr_debug("%s(), IrLAP reset\n", __func__); - break; - case LM_INIT_DISCONNECT: - pr_debug("%s(), IrLMP connect failed\n", __func__); - break; - default: - net_err_ratelimited("%s(), Unknown disconnect reason\n", - __func__); - break; - } - - /* If you want to pass the skb to *both* state machines, you will - * need to skb_clone() it, so that you don't free it twice. - * As the state machines don't need it, git rid of it here... - * Jean II */ - if (userdata) - dev_kfree_skb(userdata); - - irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); - irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); - - wake_up_interruptible(&self->open_wait); -} - -void irlan_open_data_tsap(struct irlan_cb *self) -{ - struct tsap_cb *tsap; - notify_t notify; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Check if already open */ - if (self->tsap_data) - return; - - irda_notify_init(¬ify); - - notify.data_indication = irlan_eth_receive; - notify.udata_indication = irlan_eth_receive; - notify.connect_indication = irlan_connect_indication; - notify.connect_confirm = irlan_connect_confirm; - notify.flow_indication = irlan_eth_flow_indication; - notify.disconnect_indication = irlan_disconnect_indication; - notify.instance = self; - strlcpy(notify.name, "IrLAN data", sizeof(notify.name)); - - tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, ¬ify); - if (!tsap) { - pr_debug("%s(), Got no tsap!\n", __func__); - return; - } - self->tsap_data = tsap; - - /* - * This is the data TSAP selector which we will pass to the client - * when the client ask for it. - */ - self->stsap_sel_data = self->tsap_data->stsap_sel; -} - -void irlan_close_tsaps(struct irlan_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Disconnect and close all open TSAP connections */ - if (self->tsap_data) { - irttp_disconnect_request(self->tsap_data, NULL, P_NORMAL); - irttp_close_tsap(self->tsap_data); - self->tsap_data = NULL; - } - if (self->client.tsap_ctrl) { - irttp_disconnect_request(self->client.tsap_ctrl, NULL, - P_NORMAL); - irttp_close_tsap(self->client.tsap_ctrl); - self->client.tsap_ctrl = NULL; - } - if (self->provider.tsap_ctrl) { - irttp_disconnect_request(self->provider.tsap_ctrl, NULL, - P_NORMAL); - irttp_close_tsap(self->provider.tsap_ctrl); - self->provider.tsap_ctrl = NULL; - } - self->disconnect_reason = LM_USER_REQUEST; -} - -/* - * Function irlan_ias_register (self, tsap_sel) - * - * Register with LM-IAS - * - */ -void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel) -{ - struct ias_object *obj; - struct ias_value *new_value; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* - * Check if object has already been registered by a previous provider. - * If that is the case, we just change the value of the attribute - */ - if (!irias_find_object("IrLAN")) { - obj = irias_new_object("IrLAN", IAS_IRLAN_ID); - irias_add_integer_attrib(obj, "IrDA:TinyTP:LsapSel", tsap_sel, - IAS_KERNEL_ATTR); - irias_insert_object(obj); - } else { - new_value = irias_new_integer_value(tsap_sel); - irias_object_change_attribute("IrLAN", "IrDA:TinyTP:LsapSel", - new_value); - } - - /* Register PnP object only if not registered before */ - if (!irias_find_object("PnP")) { - obj = irias_new_object("PnP", IAS_PNP_ID); -#if 0 - irias_add_string_attrib(obj, "Name", sysctl_devname, - IAS_KERNEL_ATTR); -#else - irias_add_string_attrib(obj, "Name", "Linux", IAS_KERNEL_ATTR); -#endif - irias_add_string_attrib(obj, "DeviceID", "HWP19F0", - IAS_KERNEL_ATTR); - irias_add_integer_attrib(obj, "CompCnt", 1, IAS_KERNEL_ATTR); - if (self->provider.access_type == ACCESS_PEER) - irias_add_string_attrib(obj, "Comp#01", "PNP8389", - IAS_KERNEL_ATTR); - else - irias_add_string_attrib(obj, "Comp#01", "PNP8294", - IAS_KERNEL_ATTR); - - irias_add_string_attrib(obj, "Manufacturer", - "Linux-IrDA Project", IAS_KERNEL_ATTR); - irias_insert_object(obj); - } -} - -/* - * Function irlan_run_ctrl_tx_queue (self) - * - * Try to send the next command in the control transmit queue - * - */ -int irlan_run_ctrl_tx_queue(struct irlan_cb *self) -{ - struct sk_buff *skb; - - if (irda_lock(&self->client.tx_busy) == FALSE) - return -EBUSY; - - skb = skb_dequeue(&self->client.txq); - if (!skb) { - self->client.tx_busy = FALSE; - return 0; - } - - /* Check that it's really possible to send commands */ - if ((self->client.tsap_ctrl == NULL) || - (self->client.state == IRLAN_IDLE)) - { - self->client.tx_busy = FALSE; - dev_kfree_skb(skb); - return -1; - } - pr_debug("%s(), sending ...\n", __func__); - - return irttp_data_request(self->client.tsap_ctrl, skb); -} - -/* - * Function irlan_ctrl_data_request (self, skb) - * - * This function makes sure that commands on the control channel is being - * sent in a command/response fashion - */ -static void irlan_ctrl_data_request(struct irlan_cb *self, struct sk_buff *skb) -{ - /* Queue command */ - skb_queue_tail(&self->client.txq, skb); - - /* Try to send command */ - irlan_run_ctrl_tx_queue(self); -} - -/* - * Function irlan_get_provider_info (self) - * - * Send Get Provider Information command to peer IrLAN layer - * - */ -void irlan_get_provider_info(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER, - GFP_ATOMIC); - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - frame[0] = CMD_GET_PROVIDER_INFO; - frame[1] = 0x00; /* Zero parameters */ - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_open_data_channel (self) - * - * Send an Open Data Command to provider - * - */ -void irlan_open_data_channel(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3") + - IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "DIRECT"), - GFP_ATOMIC); - if (!skb) - return; - - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - /* Build frame */ - frame[0] = CMD_OPEN_DATA_CHANNEL; - frame[1] = 0x02; /* Two parameters */ - - irlan_insert_string_param(skb, "MEDIA", "802.3"); - irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT"); - /* irlan_insert_string_param(skb, "MODE", "UNRELIABLE"); */ - -/* self->use_udata = TRUE; */ - - irlan_ctrl_data_request(self, skb); -} - -void irlan_close_data_channel(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Check if the TSAP is still there */ - if (self->client.tsap_ctrl == NULL) - return; - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN"), - GFP_ATOMIC); - if (!skb) - return; - - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - /* Build frame */ - frame[0] = CMD_CLOSE_DATA_CHAN; - frame[1] = 0x01; /* One parameter */ - - irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_open_unicast_addr (self) - * - * Make IrLAN provider accept ethernet frames addressed to the unicast - * address. - * - */ -static void irlan_open_unicast_addr(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + - IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"), - GFP_ATOMIC); - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - frame[0] = CMD_FILTER_OPERATION; - frame[1] = 0x03; /* Three parameters */ - irlan_insert_byte_param(skb, "DATA_CHAN" , self->dtsap_sel_data); - irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); - irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_set_broadcast_filter (self, status) - * - * Make IrLAN provider accept ethernet frames addressed to the broadcast - * address. Be careful with the use of this one, since there may be a lot - * of broadcast traffic out there. We can still function without this - * one but then _we_ have to initiate all communication with other - * hosts, since ARP request for this host will not be answered. - */ -void irlan_set_broadcast_filter(struct irlan_cb *self, int status) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") + - /* We may waste one byte here...*/ - IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"), - GFP_ATOMIC); - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - frame[0] = CMD_FILTER_OPERATION; - frame[1] = 0x03; /* Three parameters */ - irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); - irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST"); - if (status) - irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); - else - irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_set_multicast_filter (self, status) - * - * Make IrLAN provider accept ethernet frames addressed to the multicast - * address. - * - */ -void irlan_set_multicast_filter(struct irlan_cb *self, int status) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") + - /* We may waste one byte here...*/ - IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "NONE"), - GFP_ATOMIC); - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - frame[0] = CMD_FILTER_OPERATION; - frame[1] = 0x03; /* Three parameters */ - irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); - irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST"); - if (status) - irlan_insert_string_param(skb, "FILTER_MODE", "ALL"); - else - irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_get_unicast_addr (self) - * - * Retrieves the unicast address from the IrLAN provider. This address - * will be inserted into the devices structure, so the ethernet layer - * can construct its packets. - * - */ -static void irlan_get_unicast_addr(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + - IRLAN_STRING_PARAMETER_LEN("FILTER_OPERATION", - "DYNAMIC"), - GFP_ATOMIC); - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - frame[0] = CMD_FILTER_OPERATION; - frame[1] = 0x03; /* Three parameters */ - irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); - irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); - irlan_insert_string_param(skb, "FILTER_OPERATION", "DYNAMIC"); - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_get_media_char (self) - * - * - * - */ -void irlan_get_media_char(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3"), - GFP_ATOMIC); - - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - /* Build frame */ - frame[0] = CMD_GET_MEDIA_CHAR; - frame[1] = 0x01; /* One parameter */ - - irlan_insert_string_param(skb, "MEDIA", "802.3"); - irlan_ctrl_data_request(self, skb); -} - -/* - * Function insert_byte_param (skb, param, value) - * - * Insert byte parameter into frame - * - */ -int irlan_insert_byte_param(struct sk_buff *skb, char *param, __u8 value) -{ - return __irlan_insert_param(skb, param, IRLAN_BYTE, value, 0, NULL, 0); -} - -int irlan_insert_short_param(struct sk_buff *skb, char *param, __u16 value) -{ - return __irlan_insert_param(skb, param, IRLAN_SHORT, 0, value, NULL, 0); -} - -/* - * Function insert_string (skb, param, value) - * - * Insert string parameter into frame - * - */ -int irlan_insert_string_param(struct sk_buff *skb, char *param, char *string) -{ - int string_len = strlen(string); - - return __irlan_insert_param(skb, param, IRLAN_ARRAY, 0, 0, string, - string_len); -} - -/* - * Function insert_array_param(skb, param, value, len_value) - * - * Insert array parameter into frame - * - */ -int irlan_insert_array_param(struct sk_buff *skb, char *name, __u8 *array, - __u16 array_len) -{ - return __irlan_insert_param(skb, name, IRLAN_ARRAY, 0, 0, array, - array_len); -} - -/* - * Function insert_param (skb, param, value, byte) - * - * Insert parameter at end of buffer, structure of a parameter is: - * - * ----------------------------------------------------------------------- - * | Name Length[1] | Param Name[1..255] | Val Length[2] | Value[0..1016]| - * ----------------------------------------------------------------------- - */ -static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, - __u8 value_byte, __u16 value_short, - __u8 *value_array, __u16 value_len) -{ - __u8 *frame; - __u8 param_len; - __le16 tmp_le; /* Temporary value in little endian format */ - int n=0; - - if (skb == NULL) { - pr_debug("%s(), Got NULL skb\n", __func__); - return 0; - } - - param_len = strlen(param); - switch (type) { - case IRLAN_BYTE: - value_len = 1; - break; - case IRLAN_SHORT: - value_len = 2; - break; - case IRLAN_ARRAY: - IRDA_ASSERT(value_array != NULL, return 0;); - IRDA_ASSERT(value_len > 0, return 0;); - break; - default: - pr_debug("%s(), Unknown parameter type!\n", __func__); - return 0; - } - - /* Insert at end of sk-buffer */ - frame = skb_tail_pointer(skb); - - /* Make space for data */ - if (skb_tailroom(skb) < (param_len+value_len+3)) { - pr_debug("%s(), No more space at end of skb\n", __func__); - return 0; - } - skb_put(skb, param_len+value_len+3); - - /* Insert parameter length */ - frame[n++] = param_len; - - /* Insert parameter */ - memcpy(frame+n, param, param_len); n += param_len; - - /* Insert value length (2 byte little endian format, LSB first) */ - tmp_le = cpu_to_le16(value_len); - memcpy(frame+n, &tmp_le, 2); n += 2; /* To avoid alignment problems */ - - /* Insert value */ - switch (type) { - case IRLAN_BYTE: - frame[n++] = value_byte; - break; - case IRLAN_SHORT: - tmp_le = cpu_to_le16(value_short); - memcpy(frame+n, &tmp_le, 2); n += 2; - break; - case IRLAN_ARRAY: - memcpy(frame+n, value_array, value_len); n+=value_len; - break; - default: - break; - } - IRDA_ASSERT(n == (param_len+value_len+3), return 0;); - - return param_len+value_len+3; -} - -/* - * Function irlan_extract_param (buf, name, value, len) - * - * Extracts a single parameter name/value pair from buffer and updates - * the buffer pointer to point to the next name/value pair. - */ -int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len) -{ - __u8 name_len; - __u16 val_len; - int n=0; - - /* get length of parameter name (1 byte) */ - name_len = buf[n++]; - - if (name_len > 254) { - pr_debug("%s(), name_len > 254\n", __func__); - return -RSP_INVALID_COMMAND_FORMAT; - } - - /* get parameter name */ - memcpy(name, buf+n, name_len); - name[name_len] = '\0'; - n+=name_len; - - /* - * Get length of parameter value (2 bytes in little endian - * format) - */ - memcpy(&val_len, buf+n, 2); /* To avoid alignment problems */ - le16_to_cpus(&val_len); n+=2; - - if (val_len >= 1016) { - pr_debug("%s(), parameter length to long\n", __func__); - return -RSP_INVALID_COMMAND_FORMAT; - } - *len = val_len; - - /* get parameter value */ - memcpy(value, buf+n, val_len); - value[val_len] = '\0'; - n+=val_len; - - pr_debug("Parameter: %s ", name); - pr_debug("Value: %s\n", value); - - return n; -} - -#ifdef CONFIG_PROC_FS - -/* - * Start of reading /proc entries. - * Return entry at pos, - * or start_token to indicate print header line - * or NULL if end of file - */ -static void *irlan_seq_start(struct seq_file *seq, loff_t *pos) -{ - rcu_read_lock(); - return seq_list_start_head(&irlans, *pos); -} - -/* Return entry after v, and increment pos */ -static void *irlan_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_list_next(v, &irlans, pos); -} - -/* End of reading /proc file */ -static void irlan_seq_stop(struct seq_file *seq, void *v) -{ - rcu_read_unlock(); -} - - -/* - * Show one entry in /proc file. - */ -static int irlan_seq_show(struct seq_file *seq, void *v) -{ - if (v == &irlans) - seq_puts(seq, "IrLAN instances:\n"); - else { - struct irlan_cb *self = list_entry(v, struct irlan_cb, dev_list); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - seq_printf(seq,"ifname: %s,\n", - self->dev->name); - seq_printf(seq,"client state: %s, ", - irlan_state[ self->client.state]); - seq_printf(seq,"provider state: %s,\n", - irlan_state[ self->provider.state]); - seq_printf(seq,"saddr: %#08x, ", - self->saddr); - seq_printf(seq,"daddr: %#08x\n", - self->daddr); - seq_printf(seq,"version: %d.%d,\n", - self->version[1], self->version[0]); - seq_printf(seq,"access type: %s\n", - irlan_access[self->client.access_type]); - seq_printf(seq,"media: %s\n", - irlan_media[self->media]); - - seq_printf(seq,"local filter:\n"); - seq_printf(seq,"remote filter: "); - irlan_print_filter(seq, self->client.filter_type); - seq_printf(seq,"tx busy: %s\n", - netif_queue_stopped(self->dev) ? "TRUE" : "FALSE"); - - seq_putc(seq,'\n'); - } - return 0; -} - -static const struct seq_operations irlan_seq_ops = { - .start = irlan_seq_start, - .next = irlan_seq_next, - .stop = irlan_seq_stop, - .show = irlan_seq_show, -}; - -static int irlan_seq_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &irlan_seq_ops); -} -#endif - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("The Linux IrDA LAN protocol"); -MODULE_LICENSE("GPL"); - -module_param(eth, bool, 0); -MODULE_PARM_DESC(eth, "Name devices ethX (0) or irlanX (1)"); -module_param(access, int, 0); -MODULE_PARM_DESC(access, "Access type DIRECT=1, PEER=2, HOSTED=3"); - -module_init(irlan_init); -module_exit(irlan_cleanup); - diff --git a/drivers/staging/irda/net/irlan/irlan_eth.c b/drivers/staging/irda/net/irlan/irlan_eth.c deleted file mode 100644 index 3be852808a9d..000000000000 --- a/drivers/staging/irda/net/irlan/irlan_eth.c +++ /dev/null @@ -1,340 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_eth.c - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Oct 15 08:37:58 1998 - * Modified at: Tue Mar 21 09:06:41 2000 - * Modified by: Dag Brattli - * Sources: skeleton.c by Donald Becker - * slip.c by Laurence Culhane, - * Fred N. van Kempen, - * - * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static int irlan_eth_open(struct net_device *dev); -static int irlan_eth_close(struct net_device *dev); -static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb, - struct net_device *dev); -static void irlan_eth_set_multicast_list(struct net_device *dev); - -static const struct net_device_ops irlan_eth_netdev_ops = { - .ndo_open = irlan_eth_open, - .ndo_stop = irlan_eth_close, - .ndo_start_xmit = irlan_eth_xmit, - .ndo_set_rx_mode = irlan_eth_set_multicast_list, - .ndo_validate_addr = eth_validate_addr, -}; - -/* - * Function irlan_eth_setup (dev) - * - * The network device initialization function. - * - */ -static void irlan_eth_setup(struct net_device *dev) -{ - ether_setup(dev); - - dev->netdev_ops = &irlan_eth_netdev_ops; - dev->needs_free_netdev = true; - dev->min_mtu = 0; - dev->max_mtu = ETH_MAX_MTU; - - /* - * Lets do all queueing in IrTTP instead of this device driver. - * Queueing here as well can introduce some strange latency - * problems, which we will avoid by setting the queue size to 0. - */ - /* - * The bugs in IrTTP and IrLAN that created this latency issue - * have now been fixed, and we can propagate flow control properly - * to the network layer. However, this requires a minimal queue of - * packets for the device. - * Without flow control, the Tx Queue is 14 (ttp) + 0 (dev) = 14 - * With flow control, the Tx Queue is 7 (ttp) + 4 (dev) = 11 - * See irlan_eth_flow_indication()... - * Note : this number was randomly selected and would need to - * be adjusted. - * Jean II */ - dev->tx_queue_len = 4; -} - -/* - * Function alloc_irlandev - * - * Allocate network device and control block - * - */ -struct net_device *alloc_irlandev(const char *name) -{ - return alloc_netdev(sizeof(struct irlan_cb), name, NET_NAME_UNKNOWN, - irlan_eth_setup); -} - -/* - * Function irlan_eth_open (dev) - * - * Network device has been opened by user - * - */ -static int irlan_eth_open(struct net_device *dev) -{ - struct irlan_cb *self = netdev_priv(dev); - - /* Ready to play! */ - netif_stop_queue(dev); /* Wait until data link is ready */ - - /* We are now open, so time to do some work */ - self->disconnect_reason = 0; - irlan_client_wakeup(self, self->saddr, self->daddr); - - /* Make sure we have a hardware address before we return, - so DHCP clients gets happy */ - return wait_event_interruptible(self->open_wait, - !self->tsap_data->connected); -} - -/* - * Function irlan_eth_close (dev) - * - * Stop the ether network device, his function will usually be called by - * ifconfig down. We should now disconnect the link, We start the - * close timer, so that the instance will be removed if we are unable - * to discover the remote device after the disconnect. - */ -static int irlan_eth_close(struct net_device *dev) -{ - struct irlan_cb *self = netdev_priv(dev); - - /* Stop device */ - netif_stop_queue(dev); - - irlan_close_data_channel(self); - irlan_close_tsaps(self); - - irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); - irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); - - /* Remove frames queued on the control channel */ - skb_queue_purge(&self->client.txq); - - self->client.tx_busy = 0; - - return 0; -} - -/* - * Function irlan_eth_tx (skb) - * - * Transmits ethernet frames over IrDA link. - * - */ -static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct irlan_cb *self = netdev_priv(dev); - int ret; - unsigned int len; - - /* skb headroom large enough to contain all IrDA-headers? */ - if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) { - struct sk_buff *new_skb = - skb_realloc_headroom(skb, self->max_header_size); - - /* We have to free the original skb anyway */ - dev_kfree_skb(skb); - - /* Did the realloc succeed? */ - if (new_skb == NULL) - return NETDEV_TX_OK; - - /* Use the new skb instead */ - skb = new_skb; - } - - netif_trans_update(dev); - - len = skb->len; - /* Now queue the packet in the transport layer */ - if (self->use_udata) - ret = irttp_udata_request(self->tsap_data, skb); - else - ret = irttp_data_request(self->tsap_data, skb); - - if (ret < 0) { - /* - * IrTTPs tx queue is full, so we just have to - * drop the frame! You might think that we should - * just return -1 and don't deallocate the frame, - * but that is dangerous since it's possible that - * we have replaced the original skb with a new - * one with larger headroom, and that would really - * confuse do_dev_queue_xmit() in dev.c! I have - * tried :-) DB - */ - /* irttp_data_request already free the packet */ - dev->stats.tx_dropped++; - } else { - dev->stats.tx_packets++; - dev->stats.tx_bytes += len; - } - - return NETDEV_TX_OK; -} - -/* - * Function irlan_eth_receive (handle, skb) - * - * This function gets the data that is received on the data channel - * - */ -int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb) -{ - struct irlan_cb *self = instance; - struct net_device *dev = self->dev; - - if (skb == NULL) { - dev->stats.rx_dropped++; - return 0; - } - if (skb->len < ETH_HLEN) { - pr_debug("%s() : IrLAN frame too short (%d)\n", - __func__, skb->len); - dev->stats.rx_dropped++; - dev_kfree_skb(skb); - return 0; - } - - /* - * Adopt this frame! Important to set all these fields since they - * might have been previously set by the low level IrDA network - * device driver - */ - skb->protocol = eth_type_trans(skb, dev); /* Remove eth header */ - - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - - netif_rx(skb); /* Eat it! */ - - return 0; -} - -/* - * Function irlan_eth_flow (status) - * - * Do flow control between IP/Ethernet and IrLAN/IrTTP. This is done by - * controlling the queue stop/start. - * - * The IrDA link layer has the advantage to have flow control, and - * IrTTP now properly handles that. Flow controlling the higher layers - * prevent us to drop Tx packets in here (up to 15% for a TCP socket, - * more for UDP socket). - * Also, this allow us to reduce the overall transmit queue, which means - * less latency in case of mixed traffic. - * Jean II - */ -void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) -{ - struct irlan_cb *self; - struct net_device *dev; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - dev = self->dev; - - IRDA_ASSERT(dev != NULL, return;); - - pr_debug("%s() : flow %s ; running %d\n", __func__, - flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START", - netif_running(dev)); - - switch (flow) { - case FLOW_STOP: - /* IrTTP is full, stop higher layers */ - netif_stop_queue(dev); - break; - case FLOW_START: - default: - /* Tell upper layers that its time to transmit frames again */ - /* Schedule network layer */ - netif_wake_queue(dev); - break; - } -} - -/* - * Function set_multicast_list (dev) - * - * Configure the filtering of the device - * - */ -#define HW_MAX_ADDRS 4 /* Must query to get it! */ -static void irlan_eth_set_multicast_list(struct net_device *dev) -{ - struct irlan_cb *self = netdev_priv(dev); - - /* Check if data channel has been connected yet */ - if (self->client.state != IRLAN_DATA) { - pr_debug("%s(), delaying!\n", __func__); - return; - } - - if (dev->flags & IFF_PROMISC) { - /* Enable promiscuous mode */ - net_warn_ratelimited("Promiscuous mode not implemented by IrLAN!\n"); - } else if ((dev->flags & IFF_ALLMULTI) || - netdev_mc_count(dev) > HW_MAX_ADDRS) { - /* Disable promiscuous mode, use normal mode. */ - pr_debug("%s(), Setting multicast filter\n", __func__); - /* hardware_set_filter(NULL); */ - - irlan_set_multicast_filter(self, TRUE); - } else if (!netdev_mc_empty(dev)) { - pr_debug("%s(), Setting multicast filter\n", __func__); - /* Walk the address list, and load the filter */ - /* hardware_set_filter(dev->mc_list); */ - - irlan_set_multicast_filter(self, TRUE); - } else { - pr_debug("%s(), Clearing multicast filter\n", __func__); - irlan_set_multicast_filter(self, FALSE); - } - - if (dev->flags & IFF_BROADCAST) - irlan_set_broadcast_filter(self, TRUE); - else - irlan_set_broadcast_filter(self, FALSE); -} diff --git a/drivers/staging/irda/net/irlan/irlan_event.c b/drivers/staging/irda/net/irlan/irlan_event.c deleted file mode 100644 index 9a1cc11c16f6..000000000000 --- a/drivers/staging/irda/net/irlan/irlan_event.c +++ /dev/null @@ -1,60 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_event.c - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Oct 20 09:10:16 1998 - * Modified at: Sat Oct 30 12:59:01 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include - -const char * const irlan_state[] = { - "IRLAN_IDLE", - "IRLAN_QUERY", - "IRLAN_CONN", - "IRLAN_INFO", - "IRLAN_MEDIA", - "IRLAN_OPEN", - "IRLAN_WAIT", - "IRLAN_ARB", - "IRLAN_DATA", - "IRLAN_CLOSE", - "IRLAN_SYNC", -}; - -void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state) -{ - pr_debug("%s(), %s\n", __func__ , irlan_state[state]); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - self->client.state = state; -} - -void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state) -{ - pr_debug("%s(), %s\n", __func__ , irlan_state[state]); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - self->provider.state = state; -} - diff --git a/drivers/staging/irda/net/irlan/irlan_filter.c b/drivers/staging/irda/net/irlan/irlan_filter.c deleted file mode 100644 index e755e90b2f26..000000000000 --- a/drivers/staging/irda/net/irlan/irlan_filter.c +++ /dev/null @@ -1,240 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_filter.c - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Fri Jan 29 11:16:38 1999 - * Modified at: Sat Oct 30 12:58:45 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include - -#include -#include - -/* - * Function irlan_filter_request (self, skb) - * - * Handle filter request from client peer device - * - */ -void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - if ((self->provider.filter_type == IRLAN_DIRECTED) && - (self->provider.filter_operation == DYNAMIC)) - { - pr_debug("Giving peer a dynamic Ethernet address\n"); - self->provider.mac_address[0] = 0x40; - self->provider.mac_address[1] = 0x00; - self->provider.mac_address[2] = 0x00; - self->provider.mac_address[3] = 0x00; - - /* Use arbitration value to generate MAC address */ - if (self->provider.access_type == ACCESS_PEER) { - self->provider.mac_address[4] = - self->provider.send_arb_val & 0xff; - self->provider.mac_address[5] = - (self->provider.send_arb_val >> 8) & 0xff; - } else { - /* Just generate something for now */ - get_random_bytes(self->provider.mac_address+4, 1); - get_random_bytes(self->provider.mac_address+5, 1); - } - - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x03; - irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - irlan_insert_short_param(skb, "MAX_ENTRY", 0x0001); - irlan_insert_array_param(skb, "FILTER_ENTRY", - self->provider.mac_address, 6); - return; - } - - if ((self->provider.filter_type == IRLAN_DIRECTED) && - (self->provider.filter_mode == FILTER)) - { - pr_debug("Directed filter on\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - if ((self->provider.filter_type == IRLAN_DIRECTED) && - (self->provider.filter_mode == NONE)) - { - pr_debug("Directed filter off\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - - if ((self->provider.filter_type == IRLAN_BROADCAST) && - (self->provider.filter_mode == FILTER)) - { - pr_debug("Broadcast filter on\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - if ((self->provider.filter_type == IRLAN_BROADCAST) && - (self->provider.filter_mode == NONE)) - { - pr_debug("Broadcast filter off\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - if ((self->provider.filter_type == IRLAN_MULTICAST) && - (self->provider.filter_mode == FILTER)) - { - pr_debug("Multicast filter on\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - if ((self->provider.filter_type == IRLAN_MULTICAST) && - (self->provider.filter_mode == NONE)) - { - pr_debug("Multicast filter off\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - if ((self->provider.filter_type == IRLAN_MULTICAST) && - (self->provider.filter_operation == GET)) - { - pr_debug("Multicast filter get\n"); - skb->data[0] = 0x00; /* Success? */ - skb->data[1] = 0x02; - irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - irlan_insert_short_param(skb, "MAX_ENTRY", 16); - return; - } - skb->data[0] = 0x00; /* Command not supported */ - skb->data[1] = 0x00; - - pr_debug("Not implemented!\n"); -} - -/* - * Function check_request_param (self, param, value) - * - * Check parameters in request from peer device - * - */ -void irlan_check_command_param(struct irlan_cb *self, char *param, char *value) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - pr_debug("%s, %s\n", param, value); - - /* - * This is experimental!! DB. - */ - if (strcmp(param, "MODE") == 0) { - self->use_udata = TRUE; - return; - } - - /* - * FILTER_TYPE - */ - if (strcmp(param, "FILTER_TYPE") == 0) { - if (strcmp(value, "DIRECTED") == 0) { - self->provider.filter_type = IRLAN_DIRECTED; - return; - } - if (strcmp(value, "MULTICAST") == 0) { - self->provider.filter_type = IRLAN_MULTICAST; - return; - } - if (strcmp(value, "BROADCAST") == 0) { - self->provider.filter_type = IRLAN_BROADCAST; - return; - } - } - /* - * FILTER_MODE - */ - if (strcmp(param, "FILTER_MODE") == 0) { - if (strcmp(value, "ALL") == 0) { - self->provider.filter_mode = ALL; - return; - } - if (strcmp(value, "FILTER") == 0) { - self->provider.filter_mode = FILTER; - return; - } - if (strcmp(value, "NONE") == 0) { - self->provider.filter_mode = FILTER; - return; - } - } - /* - * FILTER_OPERATION - */ - if (strcmp(param, "FILTER_OPERATION") == 0) { - if (strcmp(value, "DYNAMIC") == 0) { - self->provider.filter_operation = DYNAMIC; - return; - } - if (strcmp(value, "GET") == 0) { - self->provider.filter_operation = GET; - return; - } - } -} - -/* - * Function irlan_print_filter (filter_type, buf) - * - * Print status of filter. Used by /proc file system - * - */ -#ifdef CONFIG_PROC_FS -#define MASK2STR(m,s) { .mask = m, .str = s } - -void irlan_print_filter(struct seq_file *seq, int filter_type) -{ - static struct { - int mask; - const char *str; - } filter_mask2str[] = { - MASK2STR(IRLAN_DIRECTED, "DIRECTED"), - MASK2STR(IRLAN_FUNCTIONAL, "FUNCTIONAL"), - MASK2STR(IRLAN_GROUP, "GROUP"), - MASK2STR(IRLAN_MAC_FRAME, "MAC_FRAME"), - MASK2STR(IRLAN_MULTICAST, "MULTICAST"), - MASK2STR(IRLAN_BROADCAST, "BROADCAST"), - MASK2STR(IRLAN_IPX_SOCKET, "IPX_SOCKET"), - MASK2STR(0, NULL) - }, *p; - - for (p = filter_mask2str; p->str; p++) { - if (filter_type & p->mask) - seq_printf(seq, "%s ", p->str); - } - seq_putc(seq, '\n'); -} -#undef MASK2STR -#endif diff --git a/drivers/staging/irda/net/irlan/irlan_provider.c b/drivers/staging/irda/net/irlan/irlan_provider.c deleted file mode 100644 index 15c292cf2644..000000000000 --- a/drivers/staging/irda/net/irlan/irlan_provider.c +++ /dev/null @@ -1,408 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_provider.c - * Version: 0.9 - * Description: IrDA LAN Access Protocol Implementation - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Sat Oct 30 12:52:10 1999 - * Modified by: Dag Brattli - * Sources: skeleton.c by Donald Becker - * slip.c by Laurence Culhane, - * Fred N. van Kempen, - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static void irlan_provider_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb); - -/* - * Function irlan_provider_control_data_indication (handle, skb) - * - * This function gets the data that is received on the control channel - * - */ -static int irlan_provider_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct irlan_cb *self; - __u8 code; - - self = instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - IRDA_ASSERT(skb != NULL, return -1;); - - code = skb->data[0]; - switch(code) { - case CMD_GET_PROVIDER_INFO: - pr_debug("Got GET_PROVIDER_INFO command!\n"); - irlan_do_provider_event(self, IRLAN_GET_INFO_CMD, skb); - break; - - case CMD_GET_MEDIA_CHAR: - pr_debug("Got GET_MEDIA_CHAR command!\n"); - irlan_do_provider_event(self, IRLAN_GET_MEDIA_CMD, skb); - break; - case CMD_OPEN_DATA_CHANNEL: - pr_debug("Got OPEN_DATA_CHANNEL command!\n"); - irlan_do_provider_event(self, IRLAN_OPEN_DATA_CMD, skb); - break; - case CMD_FILTER_OPERATION: - pr_debug("Got FILTER_OPERATION command!\n"); - irlan_do_provider_event(self, IRLAN_FILTER_CONFIG_CMD, skb); - break; - case CMD_RECONNECT_DATA_CHAN: - pr_debug("%s(), Got RECONNECT_DATA_CHAN command\n", __func__); - pr_debug("%s(), NOT IMPLEMENTED\n", __func__); - break; - case CMD_CLOSE_DATA_CHAN: - pr_debug("Got CLOSE_DATA_CHAN command!\n"); - pr_debug("%s(), NOT IMPLEMENTED\n", __func__); - break; - default: - pr_debug("%s(), Unknown command!\n", __func__); - break; - } - return 0; -} - -/* - * Function irlan_provider_connect_indication (handle, skb, priv) - * - * Got connection from peer IrLAN client - * - */ -static void irlan_provider_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct irlan_cb *self; - struct tsap_cb *tsap; - - self = instance; - tsap = sap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - IRDA_ASSERT(tsap == self->provider.tsap_ctrl,return;); - IRDA_ASSERT(self->provider.state == IRLAN_IDLE, return;); - - self->provider.max_sdu_size = max_sdu_size; - self->provider.max_header_size = max_header_size; - - irlan_do_provider_event(self, IRLAN_CONNECT_INDICATION, NULL); - - /* - * If we are in peer mode, the client may not have got the discovery - * indication it needs to make progress. If the client is still in - * IDLE state, we must kick it. - */ - if ((self->provider.access_type == ACCESS_PEER) && - (self->client.state == IRLAN_IDLE)) - { - irlan_client_wakeup(self, self->saddr, self->daddr); - } -} - -/* - * Function irlan_provider_connect_response (handle) - * - * Accept incoming connection - * - */ -void irlan_provider_connect_response(struct irlan_cb *self, - struct tsap_cb *tsap) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Just accept */ - irttp_connect_response(tsap, IRLAN_MTU, NULL); -} - -static void irlan_provider_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *userdata) -{ - struct irlan_cb *self; - struct tsap_cb *tsap; - - pr_debug("%s(), reason=%d\n", __func__ , reason); - - self = instance; - tsap = sap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - IRDA_ASSERT(tsap != NULL, return;); - IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;); - - IRDA_ASSERT(tsap == self->provider.tsap_ctrl, return;); - - irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); -} - -/* - * Function irlan_parse_open_data_cmd (self, skb) - * - * - * - */ -int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb) -{ - int ret; - - ret = irlan_provider_parse_command(self, CMD_OPEN_DATA_CHANNEL, skb); - - /* Open data channel */ - irlan_open_data_tsap(self); - - return ret; -} - -/* - * Function parse_command (skb) - * - * Extract all parameters from received buffer, then feed them to - * check_params for parsing - * - */ -int irlan_provider_parse_command(struct irlan_cb *self, int cmd, - struct sk_buff *skb) -{ - __u8 *frame; - __u8 *ptr; - int count; - __u16 val_len; - int i; - char *name; - char *value; - int ret = RSP_SUCCESS; - - IRDA_ASSERT(skb != NULL, return -RSP_PROTOCOL_ERROR;); - - pr_debug("%s(), skb->len=%d\n", __func__ , (int)skb->len); - - IRDA_ASSERT(self != NULL, return -RSP_PROTOCOL_ERROR;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -RSP_PROTOCOL_ERROR;); - - if (!skb) - return -RSP_PROTOCOL_ERROR; - - frame = skb->data; - - name = kmalloc(255, GFP_ATOMIC); - if (!name) - return -RSP_INSUFFICIENT_RESOURCES; - value = kmalloc(1016, GFP_ATOMIC); - if (!value) { - kfree(name); - return -RSP_INSUFFICIENT_RESOURCES; - } - - /* How many parameters? */ - count = frame[1]; - - pr_debug("Got %d parameters\n", count); - - ptr = frame+2; - - /* For all parameters */ - for (i=0; imagic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - /* Bigger param length comes from CMD_GET_MEDIA_CHAR */ - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") + - IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "HOSTED"), - GFP_ATOMIC); - - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->provider.max_header_size); - skb_put(skb, 2); - - switch (command) { - case CMD_GET_PROVIDER_INFO: - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x02; /* 2 parameters */ - switch (self->media) { - case MEDIA_802_3: - irlan_insert_string_param(skb, "MEDIA", "802.3"); - break; - case MEDIA_802_5: - irlan_insert_string_param(skb, "MEDIA", "802.5"); - break; - default: - pr_debug("%s(), unknown media type!\n", __func__); - break; - } - irlan_insert_short_param(skb, "IRLAN_VER", 0x0101); - break; - - case CMD_GET_MEDIA_CHAR: - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x05; /* 5 parameters */ - irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); - irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST"); - irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST"); - - switch (self->provider.access_type) { - case ACCESS_DIRECT: - irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT"); - break; - case ACCESS_PEER: - irlan_insert_string_param(skb, "ACCESS_TYPE", "PEER"); - break; - case ACCESS_HOSTED: - irlan_insert_string_param(skb, "ACCESS_TYPE", "HOSTED"); - break; - default: - pr_debug("%s(), Unknown access type\n", __func__); - break; - } - irlan_insert_short_param(skb, "MAX_FRAME", 0x05ee); - break; - case CMD_OPEN_DATA_CHANNEL: - skb->data[0] = 0x00; /* Success */ - if (self->provider.send_arb_val) { - skb->data[1] = 0x03; /* 3 parameters */ - irlan_insert_short_param(skb, "CON_ARB", - self->provider.send_arb_val); - } else - skb->data[1] = 0x02; /* 2 parameters */ - irlan_insert_byte_param(skb, "DATA_CHAN", self->stsap_sel_data); - irlan_insert_string_param(skb, "RECONNECT_KEY", "LINUX RULES!"); - break; - case CMD_FILTER_OPERATION: - irlan_filter_request(self, skb); - break; - default: - pr_debug("%s(), Unknown command!\n", __func__); - break; - } - - irttp_data_request(self->provider.tsap_ctrl, skb); -} - -/* - * Function irlan_provider_register(void) - * - * Register provider support so we can accept incoming connections. - * - */ -int irlan_provider_open_ctrl_tsap(struct irlan_cb *self) -{ - struct tsap_cb *tsap; - notify_t notify; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - /* Check if already open */ - if (self->provider.tsap_ctrl) - return -1; - - /* - * First register well known control TSAP - */ - irda_notify_init(¬ify); - notify.data_indication = irlan_provider_data_indication; - notify.connect_indication = irlan_provider_connect_indication; - notify.disconnect_indication = irlan_provider_disconnect_indication; - notify.instance = self; - strlcpy(notify.name, "IrLAN ctrl (p)", sizeof(notify.name)); - - tsap = irttp_open_tsap(LSAP_ANY, 1, ¬ify); - if (!tsap) { - pr_debug("%s(), Got no tsap!\n", __func__); - return -1; - } - self->provider.tsap_ctrl = tsap; - - /* Register with LM-IAS */ - irlan_ias_register(self, tsap->stsap_sel); - - return 0; -} - diff --git a/drivers/staging/irda/net/irlan/irlan_provider_event.c b/drivers/staging/irda/net/irlan/irlan_provider_event.c deleted file mode 100644 index 9c4f7f51d6b5..000000000000 --- a/drivers/staging/irda/net/irlan/irlan_provider_event.c +++ /dev/null @@ -1,233 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_provider_event.c - * Version: 0.9 - * Description: IrLAN provider state machine) - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Sat Oct 30 12:52:41 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include - -#include -#include - -static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); - -static int (*state[])(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) = -{ - irlan_provider_state_idle, - NULL, /* Query */ - NULL, /* Info */ - irlan_provider_state_info, - NULL, /* Media */ - irlan_provider_state_open, - NULL, /* Wait */ - NULL, /* Arb */ - irlan_provider_state_data, - NULL, /* Close */ - NULL, /* Sync */ -}; - -void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(*state[ self->provider.state] != NULL, return;); - - (*state[self->provider.state]) (self, event, skb); -} - -/* - * Function irlan_provider_state_idle (event, skb, info) - * - * IDLE, We are waiting for an indication that there is a provider - * available. - */ -static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_CONNECT_INDICATION: - irlan_provider_connect_response( self, self->provider.tsap_ctrl); - irlan_next_provider_state( self, IRLAN_INFO); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_provider_state_info (self, event, skb, info) - * - * INFO, We have issued a GetInfo command and is awaiting a reply. - */ -static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_GET_INFO_CMD: - /* Be sure to use 802.3 in case of peer mode */ - if (self->provider.access_type == ACCESS_PEER) { - self->media = MEDIA_802_3; - - /* Check if client has started yet */ - if (self->client.state == IRLAN_IDLE) { - /* This should get the client going */ - irlmp_discovery_request(8); - } - } - - irlan_provider_send_reply(self, CMD_GET_PROVIDER_INFO, - RSP_SUCCESS); - /* Keep state */ - break; - case IRLAN_GET_MEDIA_CMD: - irlan_provider_send_reply(self, CMD_GET_MEDIA_CHAR, - RSP_SUCCESS); - /* Keep state */ - break; - case IRLAN_OPEN_DATA_CMD: - ret = irlan_parse_open_data_cmd(self, skb); - if (self->provider.access_type == ACCESS_PEER) { - /* FIXME: make use of random functions! */ - self->provider.send_arb_val = (jiffies & 0xffff); - } - irlan_provider_send_reply(self, CMD_OPEN_DATA_CHANNEL, ret); - - if (ret == RSP_SUCCESS) { - irlan_next_provider_state(self, IRLAN_OPEN); - - /* Signal client that we are now open */ - irlan_do_client_event(self, IRLAN_PROVIDER_SIGNAL, NULL); - } - break; - case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ - case IRLAN_LAP_DISCONNECT: - irlan_next_provider_state(self, IRLAN_IDLE); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_provider_state_open (self, event, skb, info) - * - * OPEN, The client has issued a OpenData command and is awaiting a - * reply - * - */ -static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_FILTER_CONFIG_CMD: - irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb); - irlan_provider_send_reply(self, CMD_FILTER_OPERATION, - RSP_SUCCESS); - /* Keep state */ - break; - case IRLAN_DATA_CONNECT_INDICATION: - irlan_next_provider_state(self, IRLAN_DATA); - irlan_provider_connect_response(self, self->tsap_data); - break; - case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ - case IRLAN_LAP_DISCONNECT: - irlan_next_provider_state(self, IRLAN_IDLE); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_provider_state_data (self, event, skb, info) - * - * DATA, The data channel is connected, allowing data transfers between - * the local and remote machines. - * - */ -static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - switch(event) { - case IRLAN_FILTER_CONFIG_CMD: - irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb); - irlan_provider_send_reply(self, CMD_FILTER_OPERATION, - RSP_SUCCESS); - break; - case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ - case IRLAN_LAP_DISCONNECT: - irlan_next_provider_state(self, IRLAN_IDLE); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - - - - - - - - - - diff --git a/drivers/staging/irda/net/irlap.c b/drivers/staging/irda/net/irlap.c deleted file mode 100644 index d7d894423b4f..000000000000 --- a/drivers/staging/irda/net/irlap.c +++ /dev/null @@ -1,1207 +0,0 @@ -/********************************************************************* - * - * Filename: irlap.c - * Version: 1.0 - * Description: IrLAP implementation for Linux - * Status: Stable - * Author: Dag Brattli - * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Tue Dec 14 09:26:44 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static hashbin_t *irlap = NULL; -int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ; - -/* This is the delay of missed pf period before generating an event - * to the application. The spec mandate 3 seconds, but in some cases - * it's way too long. - Jean II */ -int sysctl_warn_noreply_time = 3; - -extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb); -static void __irlap_close(struct irlap_cb *self); -static void irlap_init_qos_capabilities(struct irlap_cb *self, - struct qos_info *qos_user); - -static const char *const lap_reasons[] __maybe_unused = { - "ERROR, NOT USED", - "LAP_DISC_INDICATION", - "LAP_NO_RESPONSE", - "LAP_RESET_INDICATION", - "LAP_FOUND_NONE", - "LAP_MEDIA_BUSY", - "LAP_PRIMARY_CONFLICT", - "ERROR, NOT USED", -}; - -int __init irlap_init(void) -{ - /* Check if the compiler did its job properly. - * May happen on some ARM configuration, check with Russell King. */ - IRDA_ASSERT(sizeof(struct xid_frame) == 14, ;); - IRDA_ASSERT(sizeof(struct test_frame) == 10, ;); - IRDA_ASSERT(sizeof(struct ua_frame) == 10, ;); - IRDA_ASSERT(sizeof(struct snrm_frame) == 11, ;); - - /* Allocate master array */ - irlap = hashbin_new(HB_LOCK); - if (irlap == NULL) { - net_err_ratelimited("%s: can't allocate irlap hashbin!\n", - __func__); - return -ENOMEM; - } - - return 0; -} - -void irlap_cleanup(void) -{ - IRDA_ASSERT(irlap != NULL, return;); - - hashbin_delete(irlap, (FREE_FUNC) __irlap_close); -} - -/* - * Function irlap_open (driver) - * - * Initialize IrLAP layer - * - */ -struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, - const char *hw_name) -{ - struct irlap_cb *self; - - /* Initialize the irlap structure. */ - self = kzalloc(sizeof(struct irlap_cb), GFP_KERNEL); - if (self == NULL) - return NULL; - - self->magic = LAP_MAGIC; - - /* Make a binding between the layers */ - self->netdev = dev; - self->qos_dev = qos; - /* Copy hardware name */ - if(hw_name != NULL) { - strlcpy(self->hw_name, hw_name, sizeof(self->hw_name)); - } else { - self->hw_name[0] = '\0'; - } - - /* FIXME: should we get our own field? */ - dev->atalk_ptr = self; - - self->state = LAP_OFFLINE; - - /* Initialize transmit queue */ - skb_queue_head_init(&self->txq); - skb_queue_head_init(&self->txq_ultra); - skb_queue_head_init(&self->wx_list); - - /* My unique IrLAP device address! */ - /* We don't want the broadcast address, neither the NULL address - * (most often used to signify "invalid"), and we don't want an - * address already in use (otherwise connect won't be able - * to select the proper link). - Jean II */ - do { - get_random_bytes(&self->saddr, sizeof(self->saddr)); - } while ((self->saddr == 0x0) || (self->saddr == BROADCAST) || - (hashbin_lock_find(irlap, self->saddr, NULL)) ); - /* Copy to the driver */ - memcpy(dev->dev_addr, &self->saddr, 4); - - timer_setup(&self->slot_timer, NULL, 0); - timer_setup(&self->query_timer, NULL, 0); - timer_setup(&self->discovery_timer, NULL, 0); - timer_setup(&self->final_timer, NULL, 0); - timer_setup(&self->poll_timer, NULL, 0); - timer_setup(&self->wd_timer, NULL, 0); - timer_setup(&self->backoff_timer, NULL, 0); - timer_setup(&self->media_busy_timer, NULL, 0); - - irlap_apply_default_connection_parameters(self); - - self->N3 = 3; /* # connections attempts to try before giving up */ - - self->state = LAP_NDM; - - hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL); - - irlmp_register_link(self, self->saddr, &self->notify); - - return self; -} -EXPORT_SYMBOL(irlap_open); - -/* - * Function __irlap_close (self) - * - * Remove IrLAP and all allocated memory. Stop any pending timers. - * - */ -static void __irlap_close(struct irlap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Stop timers */ - del_timer(&self->slot_timer); - del_timer(&self->query_timer); - del_timer(&self->discovery_timer); - del_timer(&self->final_timer); - del_timer(&self->poll_timer); - del_timer(&self->wd_timer); - del_timer(&self->backoff_timer); - del_timer(&self->media_busy_timer); - - irlap_flush_all_queues(self); - - self->magic = 0; - - kfree(self); -} - -/* - * Function irlap_close (self) - * - * Remove IrLAP instance - * - */ -void irlap_close(struct irlap_cb *self) -{ - struct irlap_cb *lap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* We used to send a LAP_DISC_INDICATION here, but this was - * racy. This has been move within irlmp_unregister_link() - * itself. Jean II */ - - /* Kill the LAP and all LSAPs on top of it */ - irlmp_unregister_link(self->saddr); - self->notify.instance = NULL; - - /* Be sure that we manage to remove ourself from the hash */ - lap = hashbin_remove(irlap, self->saddr, NULL); - if (!lap) { - pr_debug("%s(), Didn't find myself!\n", __func__); - return; - } - __irlap_close(lap); -} -EXPORT_SYMBOL(irlap_close); - -/* - * Function irlap_connect_indication (self, skb) - * - * Another device is attempting to make a connection - * - */ -void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_init_qos_capabilities(self, NULL); /* No user QoS! */ - - irlmp_link_connect_indication(self->notify.instance, self->saddr, - self->daddr, &self->qos_tx, skb); -} - -/* - * Function irlap_connect_response (self, skb) - * - * Service user has accepted incoming connection - * - */ -void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata) -{ - irlap_do_event(self, CONNECT_RESPONSE, userdata, NULL); -} - -/* - * Function irlap_connect_request (self, daddr, qos_user, sniff) - * - * Request connection with another device, sniffing is not implemented - * yet. - * - */ -void irlap_connect_request(struct irlap_cb *self, __u32 daddr, - struct qos_info *qos_user, int sniff) -{ - pr_debug("%s(), daddr=0x%08x\n", __func__, daddr); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - self->daddr = daddr; - - /* - * If the service user specifies QoS values for this connection, - * then use them - */ - irlap_init_qos_capabilities(self, qos_user); - - if ((self->state == LAP_NDM) && !self->media_busy) - irlap_do_event(self, CONNECT_REQUEST, NULL, NULL); - else - self->connect_pending = TRUE; -} - -/* - * Function irlap_connect_confirm (self, skb) - * - * Connection request has been accepted - * - */ -void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlmp_link_connect_confirm(self->notify.instance, &self->qos_tx, skb); -} - -/* - * Function irlap_data_indication (self, skb) - * - * Received data frames from IR-port, so we just pass them up to - * IrLMP for further processing - * - */ -void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb, - int unreliable) -{ - /* Hide LAP header from IrLMP layer */ - skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); - - irlmp_link_data_indication(self->notify.instance, skb, unreliable); -} - - -/* - * Function irlap_data_request (self, skb) - * - * Queue data for transmission, must wait until XMIT state - * - */ -void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb, - int unreliable) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), - return;); - skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); - - /* - * Must set frame format now so that the rest of the code knows - * if its dealing with an I or an UI frame - */ - if (unreliable) - skb->data[1] = UI_FRAME; - else - skb->data[1] = I_FRAME; - - /* Don't forget to refcount it - see irlmp_connect_request(). */ - skb_get(skb); - - /* Add at the end of the queue (keep ordering) - Jean II */ - skb_queue_tail(&self->txq, skb); - - /* - * Send event if this frame only if we are in the right state - * FIXME: udata should be sent first! (skb_queue_head?) - */ - if ((self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) { - /* If we are not already processing the Tx queue, trigger - * transmission immediately - Jean II */ - if((skb_queue_len(&self->txq) <= 1) && (!self->local_busy)) - irlap_do_event(self, DATA_REQUEST, skb, NULL); - /* Otherwise, the packets will be sent normally at the - * next pf-poll - Jean II */ - } -} - -/* - * Function irlap_unitdata_request (self, skb) - * - * Send Ultra data. This is data that must be sent outside any connection - * - */ -#ifdef CONFIG_IRDA_ULTRA -void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), - return;); - skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); - - skb->data[0] = CBROADCAST; - skb->data[1] = UI_FRAME; - - /* Don't need to refcount, see irlmp_connless_data_request() */ - - skb_queue_tail(&self->txq_ultra, skb); - - irlap_do_event(self, SEND_UI_FRAME, NULL, NULL); -} -#endif /*CONFIG_IRDA_ULTRA */ - -/* - * Function irlap_udata_indication (self, skb) - * - * Receive Ultra data. This is data that is received outside any connection - * - */ -#ifdef CONFIG_IRDA_ULTRA -void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Hide LAP header from IrLMP layer */ - skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); - - irlmp_link_unitdata_indication(self->notify.instance, skb); -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irlap_disconnect_request (void) - * - * Request to disconnect connection by service user - */ -void irlap_disconnect_request(struct irlap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Don't disconnect until all data frames are successfully sent */ - if (!skb_queue_empty(&self->txq)) { - self->disconnect_pending = TRUE; - return; - } - - /* Check if we are in the right state for disconnecting */ - switch (self->state) { - case LAP_XMIT_P: /* FALLTHROUGH */ - case LAP_XMIT_S: /* FALLTHROUGH */ - case LAP_CONN: /* FALLTHROUGH */ - case LAP_RESET_WAIT: /* FALLTHROUGH */ - case LAP_RESET_CHECK: - irlap_do_event(self, DISCONNECT_REQUEST, NULL, NULL); - break; - default: - pr_debug("%s(), disconnect pending!\n", __func__); - self->disconnect_pending = TRUE; - break; - } -} - -/* - * Function irlap_disconnect_indication (void) - * - * Disconnect request from other device - * - */ -void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason) -{ - pr_debug("%s(), reason=%s\n", __func__, lap_reasons[reason]); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Flush queues */ - irlap_flush_all_queues(self); - - switch (reason) { - case LAP_RESET_INDICATION: - pr_debug("%s(), Sending reset request!\n", __func__); - irlap_do_event(self, RESET_REQUEST, NULL, NULL); - break; - case LAP_NO_RESPONSE: /* FALLTHROUGH */ - case LAP_DISC_INDICATION: /* FALLTHROUGH */ - case LAP_FOUND_NONE: /* FALLTHROUGH */ - case LAP_MEDIA_BUSY: - irlmp_link_disconnect_indication(self->notify.instance, self, - reason, NULL); - break; - default: - net_err_ratelimited("%s: Unknown reason %d\n", - __func__, reason); - } -} - -/* - * Function irlap_discovery_request (gen_addr_bit) - * - * Start one single discovery operation. - * - */ -void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery) -{ - struct irlap_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(discovery != NULL, return;); - - pr_debug("%s(), nslots = %d\n", __func__, discovery->nslots); - - IRDA_ASSERT((discovery->nslots == 1) || (discovery->nslots == 6) || - (discovery->nslots == 8) || (discovery->nslots == 16), - return;); - - /* Discovery is only possible in NDM mode */ - if (self->state != LAP_NDM) { - pr_debug("%s(), discovery only possible in NDM mode\n", - __func__); - irlap_discovery_confirm(self, NULL); - /* Note : in theory, if we are not in NDM, we could postpone - * the discovery like we do for connection request. - * In practice, it's not worth it. If the media was busy, - * it's likely next time around it won't be busy. If we are - * in REPLY state, we will get passive discovery info & event. - * Jean II */ - return; - } - - /* Check if last discovery request finished in time, or if - * it was aborted due to the media busy flag. */ - if (self->discovery_log != NULL) { - hashbin_delete(self->discovery_log, (FREE_FUNC) kfree); - self->discovery_log = NULL; - } - - /* All operations will occur at predictable time, no need to lock */ - self->discovery_log = hashbin_new(HB_NOLOCK); - - if (self->discovery_log == NULL) { - net_warn_ratelimited("%s(), Unable to allocate discovery log!\n", - __func__); - return; - } - - info.S = discovery->nslots; /* Number of slots */ - info.s = 0; /* Current slot */ - - self->discovery_cmd = discovery; - info.discovery = discovery; - - /* sysctl_slot_timeout bounds are checked in irsysctl.c - Jean II */ - self->slot_timeout = msecs_to_jiffies(sysctl_slot_timeout); - - irlap_do_event(self, DISCOVERY_REQUEST, NULL, &info); -} - -/* - * Function irlap_discovery_confirm (log) - * - * A device has been discovered in front of this station, we - * report directly to LMP. - */ -void irlap_discovery_confirm(struct irlap_cb *self, hashbin_t *discovery_log) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - IRDA_ASSERT(self->notify.instance != NULL, return;); - - /* - * Check for successful discovery, since we are then allowed to clear - * the media busy condition (IrLAP 6.13.4 - p.94). This should allow - * us to make connection attempts much faster and easier (i.e. no - * collisions). - * Setting media busy to false will also generate an event allowing - * to process pending events in NDM state machine. - * Note : the spec doesn't define what's a successful discovery is. - * If we want Ultra to work, it's successful even if there is - * nobody discovered - Jean II - */ - if (discovery_log) - irda_device_set_media_busy(self->netdev, FALSE); - - /* Inform IrLMP */ - irlmp_link_discovery_confirm(self->notify.instance, discovery_log); -} - -/* - * Function irlap_discovery_indication (log) - * - * Somebody is trying to discover us! - * - */ -void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(discovery != NULL, return;); - - IRDA_ASSERT(self->notify.instance != NULL, return;); - - /* A device is very likely to connect immediately after it performs - * a successful discovery. This means that in our case, we are much - * more likely to receive a connection request over the medium. - * So, we backoff to avoid collisions. - * IrLAP spec 6.13.4 suggest 100ms... - * Note : this little trick actually make a *BIG* difference. If I set - * my Linux box with discovery enabled and one Ultra frame sent every - * second, my Palm has no trouble connecting to it every time ! - * Jean II */ - irda_device_set_media_busy(self->netdev, SMALL); - - irlmp_link_discovery_indication(self->notify.instance, discovery); -} - -/* - * Function irlap_status_indication (quality_of_link) - */ -void irlap_status_indication(struct irlap_cb *self, int quality_of_link) -{ - switch (quality_of_link) { - case STATUS_NO_ACTIVITY: - net_info_ratelimited("IrLAP, no activity on link!\n"); - break; - case STATUS_NOISY: - net_info_ratelimited("IrLAP, noisy link!\n"); - break; - default: - break; - } - irlmp_status_indication(self->notify.instance, - quality_of_link, LOCK_NO_CHANGE); -} - -/* - * Function irlap_reset_indication (void) - */ -void irlap_reset_indication(struct irlap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - if (self->state == LAP_RESET_WAIT) - irlap_do_event(self, RESET_REQUEST, NULL, NULL); - else - irlap_do_event(self, RESET_RESPONSE, NULL, NULL); -} - -/* - * Function irlap_reset_confirm (void) - */ -void irlap_reset_confirm(void) -{ -} - -/* - * Function irlap_generate_rand_time_slot (S, s) - * - * Generate a random time slot between s and S-1 where - * S = Number of slots (0 -> S-1) - * s = Current slot - */ -int irlap_generate_rand_time_slot(int S, int s) -{ - static int rand; - int slot; - - IRDA_ASSERT((S - s) > 0, return 0;); - - rand += jiffies; - rand ^= (rand << 12); - rand ^= (rand >> 20); - - slot = s + rand % (S-s); - - IRDA_ASSERT((slot >= s) || (slot < S), return 0;); - - return slot; -} - -/* - * Function irlap_update_nr_received (nr) - * - * Remove all acknowledged frames in current window queue. This code is - * not intuitive and you should not try to change it. If you think it - * contains bugs, please mail a patch to the author instead. - */ -void irlap_update_nr_received(struct irlap_cb *self, int nr) -{ - struct sk_buff *skb = NULL; - int count = 0; - - /* - * Remove all the ack-ed frames from the window queue. - */ - - /* - * Optimize for the common case. It is most likely that the receiver - * will acknowledge all the frames we have sent! So in that case we - * delete all frames stored in window. - */ - if (nr == self->vs) { - while ((skb = skb_dequeue(&self->wx_list)) != NULL) { - dev_kfree_skb(skb); - } - /* The last acked frame is the next to send minus one */ - self->va = nr - 1; - } else { - /* Remove all acknowledged frames in current window */ - while ((skb_peek(&self->wx_list) != NULL) && - (((self->va+1) % 8) != nr)) - { - skb = skb_dequeue(&self->wx_list); - dev_kfree_skb(skb); - - self->va = (self->va + 1) % 8; - count++; - } - } - - /* Advance window */ - self->window = self->window_size - skb_queue_len(&self->wx_list); -} - -/* - * Function irlap_validate_ns_received (ns) - * - * Validate the next to send (ns) field from received frame. - */ -int irlap_validate_ns_received(struct irlap_cb *self, int ns) -{ - /* ns as expected? */ - if (ns == self->vr) - return NS_EXPECTED; - /* - * Stations are allowed to treat invalid NS as unexpected NS - * IrLAP, Recv ... with-invalid-Ns. p. 84 - */ - return NS_UNEXPECTED; - - /* return NR_INVALID; */ -} -/* - * Function irlap_validate_nr_received (nr) - * - * Validate the next to receive (nr) field from received frame. - * - */ -int irlap_validate_nr_received(struct irlap_cb *self, int nr) -{ - /* nr as expected? */ - if (nr == self->vs) { - pr_debug("%s(), expected!\n", __func__); - return NR_EXPECTED; - } - - /* - * unexpected nr? (but within current window), first we check if the - * ns numbers of the frames in the current window wrap. - */ - if (self->va < self->vs) { - if ((nr >= self->va) && (nr <= self->vs)) - return NR_UNEXPECTED; - } else { - if ((nr >= self->va) || (nr <= self->vs)) - return NR_UNEXPECTED; - } - - /* Invalid nr! */ - return NR_INVALID; -} - -/* - * Function irlap_initiate_connection_state () - * - * Initialize the connection state parameters - * - */ -void irlap_initiate_connection_state(struct irlap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Next to send and next to receive */ - self->vs = self->vr = 0; - - /* Last frame which got acked (0 - 1) % 8 */ - self->va = 7; - - self->window = 1; - - self->remote_busy = FALSE; - self->retry_count = 0; -} - -/* - * Function irlap_wait_min_turn_around (self, qos) - * - * Wait negotiated minimum turn around time, this function actually sets - * the number of BOS's that must be sent before the next transmitted - * frame in order to delay for the specified amount of time. This is - * done to avoid using timers, and the forbidden udelay! - */ -void irlap_wait_min_turn_around(struct irlap_cb *self, struct qos_info *qos) -{ - __u32 min_turn_time; - __u32 speed; - - /* Get QoS values. */ - speed = qos->baud_rate.value; - min_turn_time = qos->min_turn_time.value; - - /* No need to calculate XBOFs for speeds over 115200 bps */ - if (speed > 115200) { - self->mtt_required = min_turn_time; - return; - } - - /* - * Send additional BOF's for the next frame for the requested - * min turn time, so now we must calculate how many chars (XBOF's) we - * must send for the requested time period (min turn time) - */ - self->xbofs_delay = irlap_min_turn_time_in_bytes(speed, min_turn_time); -} - -/* - * Function irlap_flush_all_queues (void) - * - * Flush all queues - * - */ -void irlap_flush_all_queues(struct irlap_cb *self) -{ - struct sk_buff* skb; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Free transmission queue */ - while ((skb = skb_dequeue(&self->txq)) != NULL) - dev_kfree_skb(skb); - - while ((skb = skb_dequeue(&self->txq_ultra)) != NULL) - dev_kfree_skb(skb); - - /* Free sliding window buffered packets */ - while ((skb = skb_dequeue(&self->wx_list)) != NULL) - dev_kfree_skb(skb); -} - -/* - * Function irlap_setspeed (self, speed) - * - * Change the speed of the IrDA port - * - */ -static void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now) -{ - struct sk_buff *skb; - - pr_debug("%s(), setting speed to %d\n", __func__, speed); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - self->speed = speed; - - /* Change speed now, or just piggyback speed on frames */ - if (now) { - /* Send down empty frame to trigger speed change */ - skb = alloc_skb(0, GFP_ATOMIC); - if (skb) - irlap_queue_xmit(self, skb); - } -} - -/* - * Function irlap_init_qos_capabilities (self, qos) - * - * Initialize QoS for this IrLAP session, What we do is to compute the - * intersection of the QoS capabilities for the user, driver and for - * IrLAP itself. Normally, IrLAP will not specify any values, but it can - * be used to restrict certain values. - */ -static void irlap_init_qos_capabilities(struct irlap_cb *self, - struct qos_info *qos_user) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(self->netdev != NULL, return;); - - /* Start out with the maximum QoS support possible */ - irda_init_max_qos_capabilies(&self->qos_rx); - - /* Apply drivers QoS capabilities */ - irda_qos_compute_intersection(&self->qos_rx, self->qos_dev); - - /* - * Check for user supplied QoS parameters. The service user is only - * allowed to supply these values. We check each parameter since the - * user may not have set all of them. - */ - if (qos_user) { - pr_debug("%s(), Found user specified QoS!\n", __func__); - - if (qos_user->baud_rate.bits) - self->qos_rx.baud_rate.bits &= qos_user->baud_rate.bits; - - if (qos_user->max_turn_time.bits) - self->qos_rx.max_turn_time.bits &= qos_user->max_turn_time.bits; - if (qos_user->data_size.bits) - self->qos_rx.data_size.bits &= qos_user->data_size.bits; - - if (qos_user->link_disc_time.bits) - self->qos_rx.link_disc_time.bits &= qos_user->link_disc_time.bits; - } - - /* Use 500ms in IrLAP for now */ - self->qos_rx.max_turn_time.bits &= 0x01; - - /* Set data size */ - /*self->qos_rx.data_size.bits &= 0x03;*/ - - irda_qos_bits_to_value(&self->qos_rx); -} - -/* - * Function irlap_apply_default_connection_parameters (void, now) - * - * Use the default connection and transmission parameters - */ -void irlap_apply_default_connection_parameters(struct irlap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* xbofs : Default value in NDM */ - self->next_bofs = 12; - self->bofs_count = 12; - - /* NDM Speed is 9600 */ - irlap_change_speed(self, 9600, TRUE); - - /* Set mbusy when going to NDM state */ - irda_device_set_media_busy(self->netdev, TRUE); - - /* - * Generate random connection address for this session, which must - * be 7 bits wide and different from 0x00 and 0xfe - */ - while ((self->caddr == 0x00) || (self->caddr == 0xfe)) { - get_random_bytes(&self->caddr, sizeof(self->caddr)); - self->caddr &= 0xfe; - } - - /* Use default values until connection has been negitiated */ - self->slot_timeout = sysctl_slot_timeout; - self->final_timeout = FINAL_TIMEOUT; - self->poll_timeout = POLL_TIMEOUT; - self->wd_timeout = WD_TIMEOUT; - - /* Set some default values */ - self->qos_tx.baud_rate.value = 9600; - self->qos_rx.baud_rate.value = 9600; - self->qos_tx.max_turn_time.value = 0; - self->qos_rx.max_turn_time.value = 0; - self->qos_tx.min_turn_time.value = 0; - self->qos_rx.min_turn_time.value = 0; - self->qos_tx.data_size.value = 64; - self->qos_rx.data_size.value = 64; - self->qos_tx.window_size.value = 1; - self->qos_rx.window_size.value = 1; - self->qos_tx.additional_bofs.value = 12; - self->qos_rx.additional_bofs.value = 12; - self->qos_tx.link_disc_time.value = 0; - self->qos_rx.link_disc_time.value = 0; - - irlap_flush_all_queues(self); - - self->disconnect_pending = FALSE; - self->connect_pending = FALSE; -} - -/* - * Function irlap_apply_connection_parameters (qos, now) - * - * Initialize IrLAP with the negotiated QoS values - * - * If 'now' is false, the speed and xbofs will be changed after the next - * frame is sent. - * If 'now' is true, the speed and xbofs is changed immediately - */ -void irlap_apply_connection_parameters(struct irlap_cb *self, int now) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Set the negotiated xbofs value */ - self->next_bofs = self->qos_tx.additional_bofs.value; - if (now) - self->bofs_count = self->next_bofs; - - /* Set the negotiated link speed (may need the new xbofs value) */ - irlap_change_speed(self, self->qos_tx.baud_rate.value, now); - - self->window_size = self->qos_tx.window_size.value; - self->window = self->qos_tx.window_size.value; - -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - /* - * Calculate how many bytes it is possible to transmit before the - * link must be turned around - */ - self->line_capacity = - irlap_max_line_capacity(self->qos_tx.baud_rate.value, - self->qos_tx.max_turn_time.value); - self->bytes_left = self->line_capacity; -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - - - /* - * Initialize timeout values, some of the rules are listed on - * page 92 in IrLAP. - */ - IRDA_ASSERT(self->qos_tx.max_turn_time.value != 0, return;); - IRDA_ASSERT(self->qos_rx.max_turn_time.value != 0, return;); - /* The poll timeout applies only to the primary station. - * It defines the maximum time the primary stay in XMIT mode - * before timeout and turning the link around (sending a RR). - * Or, this is how much we can keep the pf bit in primary mode. - * Therefore, it must be lower or equal than our *OWN* max turn around. - * Jean II */ - self->poll_timeout = msecs_to_jiffies( - self->qos_tx.max_turn_time.value); - /* The Final timeout applies only to the primary station. - * It defines the maximum time the primary wait (mostly in RECV mode) - * for an answer from the secondary station before polling it again. - * Therefore, it must be greater or equal than our *PARTNER* - * max turn around time - Jean II */ - self->final_timeout = msecs_to_jiffies( - self->qos_rx.max_turn_time.value); - /* The Watchdog Bit timeout applies only to the secondary station. - * It defines the maximum time the secondary wait (mostly in RECV mode) - * for poll from the primary station before getting annoyed. - * Therefore, it must be greater or equal than our *PARTNER* - * max turn around time - Jean II */ - self->wd_timeout = self->final_timeout * 2; - - /* - * N1 and N2 are maximum retry count for *both* the final timer - * and the wd timer (with a factor 2) as defined above. - * After N1 retry of a timer, we give a warning to the user. - * After N2 retry, we consider the link dead and disconnect it. - * Jean II - */ - - /* - * Set N1 to 0 if Link Disconnect/Threshold Time = 3 and set it to - * 3 seconds otherwise. See page 71 in IrLAP for more details. - * Actually, it's not always 3 seconds, as we allow to set - * it via sysctl... Max maxtt is 500ms, and N1 need to be multiple - * of 2, so 1 second is minimum we can allow. - Jean II - */ - if (self->qos_tx.link_disc_time.value == sysctl_warn_noreply_time) - /* - * If we set N1 to 0, it will trigger immediately, which is - * not what we want. What we really want is to disable it, - * Jean II - */ - self->N1 = -2; /* Disable - Need to be multiple of 2*/ - else - self->N1 = sysctl_warn_noreply_time * 1000 / - self->qos_rx.max_turn_time.value; - - pr_debug("Setting N1 = %d\n", self->N1); - - /* Set N2 to match our own disconnect time */ - self->N2 = self->qos_tx.link_disc_time.value * 1000 / - self->qos_rx.max_turn_time.value; - pr_debug("Setting N2 = %d\n", self->N2); -} - -#ifdef CONFIG_PROC_FS -struct irlap_iter_state { - int id; -}; - -static void *irlap_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct irlap_iter_state *iter = seq->private; - struct irlap_cb *self; - - /* Protect our access to the tsap list */ - spin_lock_irq(&irlap->hb_spinlock); - iter->id = 0; - - for (self = (struct irlap_cb *) hashbin_get_first(irlap); - self; self = (struct irlap_cb *) hashbin_get_next(irlap)) { - if (iter->id == *pos) - break; - ++iter->id; - } - - return self; -} - -static void *irlap_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct irlap_iter_state *iter = seq->private; - - ++*pos; - ++iter->id; - return (void *) hashbin_get_next(irlap); -} - -static void irlap_seq_stop(struct seq_file *seq, void *v) -{ - spin_unlock_irq(&irlap->hb_spinlock); -} - -static int irlap_seq_show(struct seq_file *seq, void *v) -{ - const struct irlap_iter_state *iter = seq->private; - const struct irlap_cb *self = v; - - IRDA_ASSERT(self->magic == LAP_MAGIC, return -EINVAL;); - - seq_printf(seq, "irlap%d ", iter->id); - seq_printf(seq, "state: %s\n", - irlap_state[self->state]); - - seq_printf(seq, " device name: %s, ", - (self->netdev) ? self->netdev->name : "bug"); - seq_printf(seq, "hardware name: %s\n", self->hw_name); - - seq_printf(seq, " caddr: %#02x, ", self->caddr); - seq_printf(seq, "saddr: %#08x, ", self->saddr); - seq_printf(seq, "daddr: %#08x\n", self->daddr); - - seq_printf(seq, " win size: %d, ", - self->window_size); - seq_printf(seq, "win: %d, ", self->window); -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - seq_printf(seq, "line capacity: %d, ", - self->line_capacity); - seq_printf(seq, "bytes left: %d\n", self->bytes_left); -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - seq_printf(seq, " tx queue len: %d ", - skb_queue_len(&self->txq)); - seq_printf(seq, "win queue len: %d ", - skb_queue_len(&self->wx_list)); - seq_printf(seq, "rbusy: %s", self->remote_busy ? - "TRUE" : "FALSE"); - seq_printf(seq, " mbusy: %s\n", self->media_busy ? - "TRUE" : "FALSE"); - - seq_printf(seq, " retrans: %d ", self->retry_count); - seq_printf(seq, "vs: %d ", self->vs); - seq_printf(seq, "vr: %d ", self->vr); - seq_printf(seq, "va: %d\n", self->va); - - seq_printf(seq, " qos\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\tcomp\n"); - - seq_printf(seq, " tx\t%d\t", - self->qos_tx.baud_rate.value); - seq_printf(seq, "%d\t", - self->qos_tx.max_turn_time.value); - seq_printf(seq, "%d\t", - self->qos_tx.data_size.value); - seq_printf(seq, "%d\t", - self->qos_tx.window_size.value); - seq_printf(seq, "%d\t", - self->qos_tx.additional_bofs.value); - seq_printf(seq, "%d\t", - self->qos_tx.min_turn_time.value); - seq_printf(seq, "%d\t", - self->qos_tx.link_disc_time.value); - seq_printf(seq, "\n"); - - seq_printf(seq, " rx\t%d\t", - self->qos_rx.baud_rate.value); - seq_printf(seq, "%d\t", - self->qos_rx.max_turn_time.value); - seq_printf(seq, "%d\t", - self->qos_rx.data_size.value); - seq_printf(seq, "%d\t", - self->qos_rx.window_size.value); - seq_printf(seq, "%d\t", - self->qos_rx.additional_bofs.value); - seq_printf(seq, "%d\t", - self->qos_rx.min_turn_time.value); - seq_printf(seq, "%d\n", - self->qos_rx.link_disc_time.value); - - return 0; -} - -static const struct seq_operations irlap_seq_ops = { - .start = irlap_seq_start, - .next = irlap_seq_next, - .stop = irlap_seq_stop, - .show = irlap_seq_show, -}; - -static int irlap_seq_open(struct inode *inode, struct file *file) -{ - if (irlap == NULL) - return -EINVAL; - - return seq_open_private(file, &irlap_seq_ops, - sizeof(struct irlap_iter_state)); -} - -const struct file_operations irlap_seq_fops = { - .owner = THIS_MODULE, - .open = irlap_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; - -#endif /* CONFIG_PROC_FS */ diff --git a/drivers/staging/irda/net/irlap_event.c b/drivers/staging/irda/net/irlap_event.c deleted file mode 100644 index 634188b07e0a..000000000000 --- a/drivers/staging/irda/net/irlap_event.c +++ /dev/null @@ -1,2316 +0,0 @@ -/********************************************************************* - * - * Filename: irlap_event.c - * Version: 0.9 - * Description: IrLAP state machine implementation - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Aug 16 00:59:29 1997 - * Modified at: Sat Dec 25 21:07:57 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli , - * Copyright (c) 1998 Thomas Davis - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include /* irlmp_flow_indication(), ... */ - -#include - -#ifdef CONFIG_IRDA_FAST_RR -int sysctl_fast_poll_increase = 50; -#endif - -static int irlap_state_ndm (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_query (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_reply (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_conn (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_setup (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_xmit_p (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_pclose (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_nrm_p (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_reset (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_nrm_s (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_xmit_s (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_sclose (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event, - struct sk_buff *, struct irlap_info *); - -static const char *const irlap_event[] __maybe_unused = { - "DISCOVERY_REQUEST", - "CONNECT_REQUEST", - "CONNECT_RESPONSE", - "DISCONNECT_REQUEST", - "DATA_REQUEST", - "RESET_REQUEST", - "RESET_RESPONSE", - "SEND_I_CMD", - "SEND_UI_FRAME", - "RECV_DISCOVERY_XID_CMD", - "RECV_DISCOVERY_XID_RSP", - "RECV_SNRM_CMD", - "RECV_TEST_CMD", - "RECV_TEST_RSP", - "RECV_UA_RSP", - "RECV_DM_RSP", - "RECV_RD_RSP", - "RECV_I_CMD", - "RECV_I_RSP", - "RECV_UI_FRAME", - "RECV_FRMR_RSP", - "RECV_RR_CMD", - "RECV_RR_RSP", - "RECV_RNR_CMD", - "RECV_RNR_RSP", - "RECV_REJ_CMD", - "RECV_REJ_RSP", - "RECV_SREJ_CMD", - "RECV_SREJ_RSP", - "RECV_DISC_CMD", - "SLOT_TIMER_EXPIRED", - "QUERY_TIMER_EXPIRED", - "FINAL_TIMER_EXPIRED", - "POLL_TIMER_EXPIRED", - "DISCOVERY_TIMER_EXPIRED", - "WD_TIMER_EXPIRED", - "BACKOFF_TIMER_EXPIRED", - "MEDIA_BUSY_TIMER_EXPIRED", -}; - -const char *const irlap_state[] = { - "LAP_NDM", - "LAP_QUERY", - "LAP_REPLY", - "LAP_CONN", - "LAP_SETUP", - "LAP_OFFLINE", - "LAP_XMIT_P", - "LAP_PCLOSE", - "LAP_NRM_P", - "LAP_RESET_WAIT", - "LAP_RESET", - "LAP_NRM_S", - "LAP_XMIT_S", - "LAP_SCLOSE", - "LAP_RESET_CHECK", -}; - -static int (*state[])(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) = -{ - irlap_state_ndm, - irlap_state_query, - irlap_state_reply, - irlap_state_conn, - irlap_state_setup, - irlap_state_offline, - irlap_state_xmit_p, - irlap_state_pclose, - irlap_state_nrm_p, - irlap_state_reset_wait, - irlap_state_reset, - irlap_state_nrm_s, - irlap_state_xmit_s, - irlap_state_sclose, - irlap_state_reset_check, -}; - -/* - * Function irda_poll_timer_expired (data) - * - * Poll timer has expired. Normally we must now send a RR frame to the - * remote device - */ -static void irlap_poll_timer_expired(struct timer_list *t) -{ - struct irlap_cb *self = from_timer(self, t, poll_timer); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL); -} - -/* - * Calculate and set time before we will have to send back the pf bit - * to the peer. Use in primary. - * Make sure that state is XMIT_P/XMIT_S when calling this function - * (and that nobody messed up with the state). - Jean II - */ -static void irlap_start_poll_timer(struct irlap_cb *self, int timeout) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - -#ifdef CONFIG_IRDA_FAST_RR - /* - * Send out the RR frames faster if our own transmit queue is empty, or - * if the peer is busy. The effect is a much faster conversation - */ - if (skb_queue_empty(&self->txq) || self->remote_busy) { - if (self->fast_RR == TRUE) { - /* - * Assert that the fast poll timer has not reached the - * normal poll timer yet - */ - if (self->fast_RR_timeout < timeout) { - /* - * FIXME: this should be a more configurable - * function - */ - self->fast_RR_timeout += - (sysctl_fast_poll_increase * HZ/1000); - - /* Use this fast(er) timeout instead */ - timeout = self->fast_RR_timeout; - } - } else { - self->fast_RR = TRUE; - - /* Start with just 0 ms */ - self->fast_RR_timeout = 0; - timeout = 0; - } - } else - self->fast_RR = FALSE; - - pr_debug("%s(), timeout=%d (%ld)\n", __func__, timeout, jiffies); -#endif /* CONFIG_IRDA_FAST_RR */ - - if (timeout == 0) - irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL); - else - irda_start_timer(&self->poll_timer, timeout, - irlap_poll_timer_expired); -} - -/* - * Function irlap_do_event (event, skb, info) - * - * Rushes through the state machine without any delay. If state == XMIT - * then send queued data frames. - */ -void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret; - - if (!self || self->magic != LAP_MAGIC) - return; - - pr_debug("%s(), event = %s, state = %s\n", __func__, - irlap_event[event], irlap_state[self->state]); - - ret = (*state[self->state])(self, event, skb, info); - - /* - * Check if there are any pending events that needs to be executed - */ - switch (self->state) { - case LAP_XMIT_P: /* FALLTHROUGH */ - case LAP_XMIT_S: - /* - * We just received the pf bit and are at the beginning - * of a new LAP transmit window. - * Check if there are any queued data frames, and do not - * try to disconnect link if we send any data frames, since - * that will change the state away form XMIT - */ - pr_debug("%s() : queue len = %d\n", __func__, - skb_queue_len(&self->txq)); - - if (!skb_queue_empty(&self->txq)) { - /* Prevent race conditions with irlap_data_request() */ - self->local_busy = TRUE; - - /* Theory of operation. - * We send frames up to when we fill the window or - * reach line capacity. Those frames will queue up - * in the device queue, and the driver will slowly - * send them. - * After each frame that we send, we poll the higher - * layer for more data. It's the right time to do - * that because the link layer need to perform the mtt - * and then send the first frame, so we can afford - * to send a bit of time in kernel space. - * The explicit flow indication allow to minimise - * buffers (== lower latency), to avoid higher layer - * polling via timers (== less context switches) and - * to implement a crude scheduler - Jean II */ - - /* Try to send away all queued data frames */ - while ((skb = skb_dequeue(&self->txq)) != NULL) { - /* Send one frame */ - ret = (*state[self->state])(self, SEND_I_CMD, - skb, NULL); - /* Drop reference count. - * It will be increase as needed in - * irlap_send_data_xxx() */ - kfree_skb(skb); - - /* Poll the higher layers for one more frame */ - irlmp_flow_indication(self->notify.instance, - FLOW_START); - - if (ret == -EPROTO) - break; /* Try again later! */ - } - /* Finished transmitting */ - self->local_busy = FALSE; - } else if (self->disconnect_pending) { - self->disconnect_pending = FALSE; - - ret = (*state[self->state])(self, DISCONNECT_REQUEST, - NULL, NULL); - } - break; -/* case LAP_NDM: */ -/* case LAP_CONN: */ -/* case LAP_RESET_WAIT: */ -/* case LAP_RESET_CHECK: */ - default: - break; - } -} - -/* - * Function irlap_state_ndm (event, skb, frame) - * - * NDM (Normal Disconnected Mode) state - * - */ -static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - discovery_t *discovery_rsp; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case CONNECT_REQUEST: - IRDA_ASSERT(self->netdev != NULL, return -1;); - - if (self->media_busy) { - /* Note : this will never happen, because we test - * media busy in irlap_connect_request() and - * postpone the event... - Jean II */ - pr_debug("%s(), CONNECT_REQUEST: media busy!\n", - __func__); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_MEDIA_BUSY); - } else { - irlap_send_snrm_frame(self, &self->qos_rx); - - /* Start Final-bit timer */ - irlap_start_final_timer(self, self->final_timeout); - - self->retry_count = 0; - irlap_next_state(self, LAP_SETUP); - } - break; - case RECV_SNRM_CMD: - /* Check if the frame contains and I field */ - if (info) { - self->daddr = info->daddr; - self->caddr = info->caddr; - - irlap_next_state(self, LAP_CONN); - - irlap_connect_indication(self, skb); - } else { - pr_debug("%s(), SNRM frame does not contain an I field!\n", - __func__); - } - break; - case DISCOVERY_REQUEST: - IRDA_ASSERT(info != NULL, return -1;); - - if (self->media_busy) { - pr_debug("%s(), DISCOVERY_REQUEST: media busy!\n", - __func__); - /* irlap->log.condition = MEDIA_BUSY; */ - - /* This will make IrLMP try again */ - irlap_discovery_confirm(self, NULL); - /* Note : the discovery log is not cleaned up here, - * it will be done in irlap_discovery_request() - * Jean II */ - return 0; - } - - self->S = info->S; - self->s = info->s; - irlap_send_discovery_xid_frame(self, info->S, info->s, TRUE, - info->discovery); - self->frame_sent = FALSE; - self->s++; - - irlap_start_slot_timer(self, self->slot_timeout); - irlap_next_state(self, LAP_QUERY); - break; - case RECV_DISCOVERY_XID_CMD: - IRDA_ASSERT(info != NULL, return -1;); - - /* Assert that this is not the final slot */ - if (info->s <= info->S) { - self->slot = irlap_generate_rand_time_slot(info->S, - info->s); - if (self->slot == info->s) { - discovery_rsp = irlmp_get_discovery_response(); - discovery_rsp->data.daddr = info->daddr; - - irlap_send_discovery_xid_frame(self, info->S, - self->slot, - FALSE, - discovery_rsp); - self->frame_sent = TRUE; - } else - self->frame_sent = FALSE; - - /* - * Go to reply state until end of discovery to - * inhibit our own transmissions. Set the timer - * to not stay forever there... Jean II - */ - irlap_start_query_timer(self, info->S, info->s); - irlap_next_state(self, LAP_REPLY); - } else { - /* This is the final slot. How is it possible ? - * This would happen is both discoveries are just slightly - * offset (if they are in sync, all packets are lost). - * Most often, all the discovery requests will be received - * in QUERY state (see my comment there), except for the - * last frame that will come here. - * The big trouble when it happen is that active discovery - * doesn't happen, because nobody answer the discoveries - * frame of the other guy, so the log shows up empty. - * What should we do ? - * Not much. It's too late to answer those discovery frames, - * so we just pass the info to IrLMP who will put it in the - * log (and post an event). - * Another cause would be devices that do discovery much - * slower than us, however the latest fixes should minimise - * those cases... - * Jean II - */ - pr_debug("%s(), Receiving final discovery request, missed the discovery slots :-(\n", - __func__); - - /* Last discovery request -> in the log */ - irlap_discovery_indication(self, info->discovery); - } - break; - case MEDIA_BUSY_TIMER_EXPIRED: - /* A bunch of events may be postponed because the media is - * busy (usually immediately after we close a connection), - * or while we are doing discovery (state query/reply). - * In all those cases, the media busy flag will be cleared - * when it's OK for us to process those postponed events. - * This event is not mentioned in the state machines in the - * IrLAP spec. It's because they didn't consider Ultra and - * postponing connection request is optional. - * Jean II */ -#ifdef CONFIG_IRDA_ULTRA - /* Send any pending Ultra frames if any */ - if (!skb_queue_empty(&self->txq_ultra)) { - /* We don't send the frame, just post an event. - * Also, previously this code was in timer.c... - * Jean II */ - ret = (*state[self->state])(self, SEND_UI_FRAME, - NULL, NULL); - } -#endif /* CONFIG_IRDA_ULTRA */ - /* Check if we should try to connect. - * This code was previously in irlap_do_event() */ - if (self->connect_pending) { - self->connect_pending = FALSE; - - /* This one *should* not pend in this state, except - * if a socket try to connect and immediately - * disconnect. - clear - Jean II */ - if (self->disconnect_pending) - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - else - ret = (*state[self->state])(self, - CONNECT_REQUEST, - NULL, NULL); - self->disconnect_pending = FALSE; - } - /* Note : one way to test if this code works well (including - * media busy and small busy) is to create a user space - * application generating an Ultra packet every 3.05 sec (or - * 2.95 sec) and to see how it interact with discovery. - * It's fairly easy to check that no packet is lost, that the - * packets are postponed during discovery and that after - * discovery indication you have a 100ms "gap". - * As connection request and Ultra are now processed the same - * way, this avoid the tedious job of trying IrLAP connection - * in all those cases... - * Jean II */ - break; -#ifdef CONFIG_IRDA_ULTRA - case SEND_UI_FRAME: - { - int i; - /* Only allowed to repeat an operation twice */ - for (i=0; ((i<2) && (self->media_busy == FALSE)); i++) { - skb = skb_dequeue(&self->txq_ultra); - if (skb) - irlap_send_ui_frame(self, skb, CBROADCAST, - CMD_FRAME); - else - break; - /* irlap_send_ui_frame() won't increase skb reference - * count, so no dev_kfree_skb() - Jean II */ - } - if (i == 2) { - /* Force us to listen 500 ms again */ - irda_device_set_media_busy(self->netdev, TRUE); - } - break; - } - case RECV_UI_FRAME: - /* Only accept broadcast frames in NDM mode */ - if (info->caddr != CBROADCAST) { - pr_debug("%s(), not a broadcast frame!\n", - __func__); - } else - irlap_unitdata_indication(self, skb); - break; -#endif /* CONFIG_IRDA_ULTRA */ - case RECV_TEST_CMD: - /* Remove test frame header */ - skb_pull(skb, sizeof(struct test_frame)); - - /* - * Send response. This skb will not be sent out again, and - * will only be used to send out the same info as the cmd - */ - irlap_send_test_frame(self, CBROADCAST, info->daddr, skb); - break; - case RECV_TEST_RSP: - pr_debug("%s() not implemented!\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %s\n", __func__, - irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_query (event, skb, info) - * - * QUERY state - * - */ -static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case RECV_DISCOVERY_XID_RSP: - IRDA_ASSERT(info != NULL, return -1;); - IRDA_ASSERT(info->discovery != NULL, return -1;); - - pr_debug("%s(), daddr=%08x\n", __func__, - info->discovery->data.daddr); - - if (!self->discovery_log) { - net_warn_ratelimited("%s: discovery log is gone! maybe the discovery timeout has been set too short?\n", - __func__); - break; - } - hashbin_insert(self->discovery_log, - (irda_queue_t *) info->discovery, - info->discovery->data.daddr, NULL); - - /* Keep state */ - /* irlap_next_state(self, LAP_QUERY); */ - - break; - case RECV_DISCOVERY_XID_CMD: - /* Yes, it is possible to receive those frames in this mode. - * Note that most often the last discovery request won't - * occur here but in NDM state (see my comment there). - * What should we do ? - * Not much. We are currently performing our own discovery, - * therefore we can't answer those frames. We don't want - * to change state either. We just pass the info to - * IrLMP who will put it in the log (and post an event). - * Jean II - */ - - IRDA_ASSERT(info != NULL, return -1;); - - pr_debug("%s(), Receiving discovery request (s = %d) while performing discovery :-(\n", - __func__, info->s); - - /* Last discovery request ? */ - if (info->s == 0xff) - irlap_discovery_indication(self, info->discovery); - break; - case SLOT_TIMER_EXPIRED: - /* - * Wait a little longer if we detect an incoming frame. This - * is not mentioned in the spec, but is a good thing to do, - * since we want to work even with devices that violate the - * timing requirements. - */ - if (irda_device_is_receiving(self->netdev) && !self->add_wait) { - pr_debug("%s(), device is slow to answer, waiting some more!\n", - __func__); - irlap_start_slot_timer(self, msecs_to_jiffies(10)); - self->add_wait = TRUE; - return ret; - } - self->add_wait = FALSE; - - if (self->s < self->S) { - irlap_send_discovery_xid_frame(self, self->S, - self->s, TRUE, - self->discovery_cmd); - self->s++; - irlap_start_slot_timer(self, self->slot_timeout); - - /* Keep state */ - irlap_next_state(self, LAP_QUERY); - } else { - /* This is the final slot! */ - irlap_send_discovery_xid_frame(self, self->S, 0xff, - TRUE, - self->discovery_cmd); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - /* - * We are now finished with the discovery procedure, - * so now we must return the results - */ - irlap_discovery_confirm(self, self->discovery_log); - - /* IrLMP should now have taken care of the log */ - self->discovery_log = NULL; - } - break; - default: - pr_debug("%s(), Unknown event %s\n", __func__, - irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_reply (self, event, skb, info) - * - * REPLY, we have received a XID discovery frame from a device and we - * are waiting for the right time slot to send a response XID frame - * - */ -static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - discovery_t *discovery_rsp; - int ret=0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case QUERY_TIMER_EXPIRED: - pr_debug("%s(), QUERY_TIMER_EXPIRED <%ld>\n", - __func__, jiffies); - irlap_next_state(self, LAP_NDM); - break; - case RECV_DISCOVERY_XID_CMD: - IRDA_ASSERT(info != NULL, return -1;); - /* Last frame? */ - if (info->s == 0xff) { - del_timer(&self->query_timer); - - /* info->log.condition = REMOTE; */ - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_discovery_indication(self, info->discovery); - } else { - /* If it's our slot, send our reply */ - if ((info->s >= self->slot) && (!self->frame_sent)) { - discovery_rsp = irlmp_get_discovery_response(); - discovery_rsp->data.daddr = info->daddr; - - irlap_send_discovery_xid_frame(self, info->S, - self->slot, - FALSE, - discovery_rsp); - - self->frame_sent = TRUE; - } - /* Readjust our timer to accommodate devices - * doing faster or slower discovery than us... - * Jean II */ - irlap_start_query_timer(self, info->S, info->s); - - /* Keep state */ - //irlap_next_state(self, LAP_REPLY); - } - break; - default: - pr_debug("%s(), Unknown event %d, %s\n", __func__, - event, irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_conn (event, skb, info) - * - * CONN, we have received a SNRM command and is waiting for the upper - * layer to accept or refuse connection - * - */ -static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case CONNECT_RESPONSE: - skb_pull(skb, sizeof(struct snrm_frame)); - - IRDA_ASSERT(self->netdev != NULL, return -1;); - - irlap_qos_negotiate(self, skb); - - irlap_initiate_connection_state(self); - - /* - * Applying the parameters now will make sure we change speed - * *after* we have sent the next frame - */ - irlap_apply_connection_parameters(self, FALSE); - - /* - * Sending this frame will force a speed change after it has - * been sent (i.e. the frame will be sent at 9600). - */ - irlap_send_ua_response_frame(self, &self->qos_rx); - -#if 0 - /* - * We are allowed to send two frames, but this may increase - * the connect latency, so lets not do it for now. - */ - /* This is full of good intentions, but doesn't work in - * practice. - * After sending the first UA response, we switch the - * dongle to the negotiated speed, which is usually - * different than 9600 kb/s. - * From there, there is two solutions : - * 1) The other end has received the first UA response : - * it will set up the connection, move to state LAP_NRM_P, - * and will ignore and drop the second UA response. - * Actually, it's even worse : the other side will almost - * immediately send a RR that will likely collide with the - * UA response (depending on negotiated turnaround). - * 2) The other end has not received the first UA response, - * will stay at 9600 and will never see the second UA response. - * Jean II */ - irlap_send_ua_response_frame(self, &self->qos_rx); -#endif - - /* - * The WD-timer could be set to the duration of the P-timer - * for this case, but it is recommended to use twice the - * value (note 3 IrLAP p. 60). - */ - irlap_start_wd_timer(self, self->wd_timeout); - irlap_next_state(self, LAP_NRM_S); - - break; - case RECV_DISCOVERY_XID_CMD: - pr_debug("%s(), event RECV_DISCOVER_XID_CMD!\n", - __func__); - irlap_next_state(self, LAP_NDM); - - break; - case DISCONNECT_REQUEST: - pr_debug("%s(), Disconnect request!\n", __func__); - irlap_send_dm_frame(self); - irlap_next_state( self, LAP_NDM); - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - default: - pr_debug("%s(), Unknown event %d, %s\n", __func__, - event, irlap_event[event]); - - ret = -1; - break; - } - - return ret; -} - -/* - * Function irlap_state_setup (event, skb, frame) - * - * SETUP state, The local layer has transmitted a SNRM command frame to - * a remote peer layer and is awaiting a reply . - * - */ -static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case FINAL_TIMER_EXPIRED: - if (self->retry_count < self->N3) { -/* - * Perform random backoff, Wait a random number of time units, minimum - * duration half the time taken to transmitt a SNRM frame, maximum duration - * 1.5 times the time taken to transmit a SNRM frame. So this time should - * between 15 msecs and 45 msecs. - */ - irlap_start_backoff_timer(self, msecs_to_jiffies(20 + - (jiffies % 30))); - } else { - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_FOUND_NONE); - } - break; - case BACKOFF_TIMER_EXPIRED: - irlap_send_snrm_frame(self, &self->qos_rx); - irlap_start_final_timer(self, self->final_timeout); - self->retry_count++; - break; - case RECV_SNRM_CMD: - pr_debug("%s(), SNRM battle!\n", __func__); - - IRDA_ASSERT(skb != NULL, return 0;); - IRDA_ASSERT(info != NULL, return 0;); - - /* - * The device with the largest device address wins the battle - * (both have sent a SNRM command!) - */ - if (info &&(info->daddr > self->saddr)) { - del_timer(&self->final_timer); - irlap_initiate_connection_state(self); - - IRDA_ASSERT(self->netdev != NULL, return -1;); - - skb_pull(skb, sizeof(struct snrm_frame)); - - irlap_qos_negotiate(self, skb); - - /* Send UA frame and then change link settings */ - irlap_apply_connection_parameters(self, FALSE); - irlap_send_ua_response_frame(self, &self->qos_rx); - - irlap_next_state(self, LAP_NRM_S); - irlap_connect_confirm(self, skb); - - /* - * The WD-timer could be set to the duration of the - * P-timer for this case, but it is recommended - * to use twice the value (note 3 IrLAP p. 60). - */ - irlap_start_wd_timer(self, self->wd_timeout); - } else { - /* We just ignore the other device! */ - irlap_next_state(self, LAP_SETUP); - } - break; - case RECV_UA_RSP: - /* Stop F-timer */ - del_timer(&self->final_timer); - - /* Initiate connection state */ - irlap_initiate_connection_state(self); - - /* Negotiate connection parameters */ - IRDA_ASSERT(skb->len > 10, return -1;); - - skb_pull(skb, sizeof(struct ua_frame)); - - IRDA_ASSERT(self->netdev != NULL, return -1;); - - irlap_qos_negotiate(self, skb); - - /* Set the new link setting *now* (before the rr frame) */ - irlap_apply_connection_parameters(self, TRUE); - self->retry_count = 0; - - /* Wait for turnaround time to give a chance to the other - * device to be ready to receive us. - * Note : the time to switch speed is typically larger - * than the turnaround time, but as we don't have the other - * side speed switch time, that's our best guess... - * Jean II */ - irlap_wait_min_turn_around(self, &self->qos_tx); - - /* This frame will actually be sent at the new speed */ - irlap_send_rr_frame(self, CMD_FRAME); - - /* The timer is set to half the normal timer to quickly - * detect a failure to negotiate the new connection - * parameters. IrLAP 6.11.3.2, note 3. - * Note that currently we don't process this failure - * properly, as we should do a quick disconnect. - * Jean II */ - irlap_start_final_timer(self, self->final_timeout/2); - irlap_next_state(self, LAP_NRM_P); - - irlap_connect_confirm(self, skb); - break; - case RECV_DM_RSP: /* FALLTHROUGH */ - case RECV_DISC_CMD: - del_timer(&self->final_timer); - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - default: - pr_debug("%s(), Unknown event %d, %s\n", __func__, - event, irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_offline (self, event, skb, info) - * - * OFFLINE state, not used for now! - * - */ -static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - pr_debug("%s(), Unknown event\n", __func__); - - return -1; -} - -/* - * Function irlap_state_xmit_p (self, event, skb, info) - * - * XMIT, Only the primary station has right to transmit, and we - * therefore do not expect to receive any transmissions from other - * stations. - * - */ -static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - switch (event) { - case SEND_I_CMD: - /* - * Only send frame if send-window > 0. - */ - if ((self->window > 0) && (!self->remote_busy)) { - int nextfit; -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - struct sk_buff *skb_next; - - /* With DYNAMIC_WINDOW, we keep the window size - * maximum, and adapt on the packets we are sending. - * At 115k, we can send only 2 packets of 2048 bytes - * in a 500 ms turnaround. Without this option, we - * would always limit the window to 2. With this - * option, if we send smaller packets, we can send - * up to 7 of them (always depending on QoS). - * Jean II */ - - /* Look at the next skb. This is safe, as we are - * the only consumer of the Tx queue (if we are not, - * we have other problems) - Jean II */ - skb_next = skb_peek(&self->txq); - - /* Check if a subsequent skb exist and would fit in - * the current window (with respect to turnaround - * time). - * This allow us to properly mark the current packet - * with the pf bit, to avoid falling back on the - * second test below, and avoid waiting the - * end of the window and sending a extra RR. - * Note : (skb_next != NULL) <=> (skb_queue_len() > 0) - * Jean II */ - nextfit = ((skb_next != NULL) && - ((skb_next->len + skb->len) <= - self->bytes_left)); - - /* - * The current packet may not fit ! Because of test - * above, this should not happen any more !!! - * Test if we have transmitted more bytes over the - * link than its possible to do with the current - * speed and turn-around-time. - */ - if((!nextfit) && (skb->len > self->bytes_left)) { - pr_debug("%s(), Not allowed to transmit more bytes!\n", - __func__); - /* Requeue the skb */ - skb_queue_head(&self->txq, skb_get(skb)); - /* - * We should switch state to LAP_NRM_P, but - * that is not possible since we must be sure - * that we poll the other side. Since we have - * used up our time, the poll timer should - * trigger anyway now, so we just wait for it - * DB - */ - /* - * Sorry, but that's not totally true. If - * we send 2000B packets, we may wait another - * 1000B until our turnaround expire. That's - * why we need to be proactive in avoiding - * coming here. - Jean II - */ - return -EPROTO; - } - - /* Subtract space used by this skb */ - self->bytes_left -= skb->len; -#else /* CONFIG_IRDA_DYNAMIC_WINDOW */ - /* Window has been adjusted for the max packet - * size, so much simpler... - Jean II */ - nextfit = !skb_queue_empty(&self->txq); -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - /* - * Send data with poll bit cleared only if window > 1 - * and there is more frames after this one to be sent - */ - if ((self->window > 1) && (nextfit)) { - /* More packet to send in current window */ - irlap_send_data_primary(self, skb); - irlap_next_state(self, LAP_XMIT_P); - } else { - /* Final packet of window */ - irlap_send_data_primary_poll(self, skb); - - /* - * Make sure state machine does not try to send - * any more frames - */ - ret = -EPROTO; - } -#ifdef CONFIG_IRDA_FAST_RR - /* Peer may want to reply immediately */ - self->fast_RR = FALSE; -#endif /* CONFIG_IRDA_FAST_RR */ - } else { - pr_debug("%s(), Unable to send! remote busy?\n", - __func__); - skb_queue_head(&self->txq, skb_get(skb)); - - /* - * The next ret is important, because it tells - * irlap_next_state _not_ to deliver more frames - */ - ret = -EPROTO; - } - break; - case POLL_TIMER_EXPIRED: - pr_debug("%s(), POLL_TIMER_EXPIRED <%ld>\n", - __func__, jiffies); - irlap_send_rr_frame(self, CMD_FRAME); - /* Return to NRM properly - Jean II */ - self->window = self->window_size; -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - /* Allowed to transmit a maximum number of bytes again. */ - self->bytes_left = self->line_capacity; -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - irlap_start_final_timer(self, self->final_timeout); - irlap_next_state(self, LAP_NRM_P); - break; - case DISCONNECT_REQUEST: - del_timer(&self->poll_timer); - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_disc_frame(self); - irlap_flush_all_queues(self); - irlap_start_final_timer(self, self->final_timeout); - self->retry_count = 0; - irlap_next_state(self, LAP_PCLOSE); - break; - case DATA_REQUEST: - /* Nothing to do, irlap_do_event() will send the packet - * when we return... - Jean II */ - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlap_event[event]); - - ret = -EINVAL; - break; - } - return ret; -} - -/* - * Function irlap_state_pclose (event, skb, info) - * - * PCLOSE state - */ -static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case RECV_UA_RSP: /* FALLTHROUGH */ - case RECV_DM_RSP: - del_timer(&self->final_timer); - - /* Set new link parameters */ - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - case FINAL_TIMER_EXPIRED: - if (self->retry_count < self->N3) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_disc_frame(self); - irlap_start_final_timer(self, self->final_timeout); - self->retry_count++; - /* Keep state */ - } else { - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_NO_RESPONSE); - } - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__, event); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_nrm_p (self, event, skb, info) - * - * NRM_P (Normal Response Mode as Primary), The primary station has given - * permissions to a secondary station to transmit IrLAP resonse frames - * (by sending a frame with the P bit set). The primary station will not - * transmit any frames and is expecting to receive frames only from the - * secondary to which transmission permissions has been given. - */ -static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - int ns_status; - int nr_status; - - switch (event) { - case RECV_I_RSP: /* Optimize for the common case */ - if (unlikely(skb->len <= LAP_ADDR_HEADER + LAP_CTRL_HEADER)) { - /* - * Input validation check: a stir4200/mcp2150 - * combination sometimes results in an empty i:rsp. - * This makes no sense; we can just ignore the frame - * and send an rr:cmd immediately. This happens before - * changing nr or ns so triggers a retransmit - */ - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, CMD_FRAME); - /* Keep state */ - break; - } - /* FIXME: must check for remote_busy below */ -#ifdef CONFIG_IRDA_FAST_RR - /* - * Reset the fast_RR so we can use the fast RR code with - * full speed the next time since peer may have more frames - * to transmitt - */ - self->fast_RR = FALSE; -#endif /* CONFIG_IRDA_FAST_RR */ - IRDA_ASSERT( info != NULL, return -1;); - - ns_status = irlap_validate_ns_received(self, info->ns); - nr_status = irlap_validate_nr_received(self, info->nr); - - /* - * Check for expected I(nformation) frame - */ - if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) { - - /* Update Vr (next frame for us to receive) */ - self->vr = (self->vr + 1) % 8; - - /* Update Nr received, cleanup our retry queue */ - irlap_update_nr_received(self, info->nr); - - /* - * Got expected NR, so reset the - * retry_count. This is not done by IrLAP spec, - * which is strange! - */ - self->retry_count = 0; - self->ack_required = TRUE; - - /* poll bit cleared? */ - if (!info->pf) { - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_P); - - irlap_data_indication(self, skb, FALSE); - } else { - /* No longer waiting for pf */ - del_timer(&self->final_timer); - - irlap_wait_min_turn_around(self, &self->qos_tx); - - /* Call higher layer *before* changing state - * to give them a chance to send data in the - * next LAP frame. - * Jean II */ - irlap_data_indication(self, skb, FALSE); - - /* XMIT states are the most dangerous state - * to be in, because user requests are - * processed directly and may change state. - * On the other hand, in NDM_P, those - * requests are queued and we will process - * them when we return to irlap_do_event(). - * Jean II - */ - irlap_next_state(self, LAP_XMIT_P); - - /* This is the last frame. - * Make sure it's always called in XMIT state. - * - Jean II */ - irlap_start_poll_timer(self, self->poll_timeout); - } - break; - - } - /* Unexpected next to send (Ns) */ - if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED)) - { - if (!info->pf) { - irlap_update_nr_received(self, info->nr); - - /* - * Wait until the last frame before doing - * anything - */ - - /* Keep state */ - irlap_next_state(self, LAP_NRM_P); - } else { - pr_debug("%s(), missing or duplicate frame!\n", - __func__); - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, CMD_FRAME); - - self->ack_required = FALSE; - - irlap_start_final_timer(self, self->final_timeout); - irlap_next_state(self, LAP_NRM_P); - } - break; - } - /* - * Unexpected next to receive (Nr) - */ - if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED)) - { - if (info->pf) { - self->vr = (self->vr + 1) % 8; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - /* Resend rejected frames */ - irlap_resend_rejected_frames(self, CMD_FRAME); - - self->ack_required = FALSE; - - /* Make sure we account for the time - * to transmit our frames. See comemnts - * in irlap_send_data_primary_poll(). - * Jean II */ - irlap_start_final_timer(self, 2 * self->final_timeout); - - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_P); - - irlap_data_indication(self, skb, FALSE); - } else { - /* - * Do not resend frames until the last - * frame has arrived from the other - * device. This is not documented in - * IrLAP!! - */ - self->vr = (self->vr + 1) % 8; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - self->ack_required = FALSE; - - /* Keep state, do not move this line!*/ - irlap_next_state(self, LAP_NRM_P); - - irlap_data_indication(self, skb, FALSE); - } - break; - } - /* - * Unexpected next to send (Ns) and next to receive (Nr) - * Not documented by IrLAP! - */ - if ((ns_status == NS_UNEXPECTED) && - (nr_status == NR_UNEXPECTED)) - { - pr_debug("%s(), unexpected nr and ns!\n", - __func__); - if (info->pf) { - /* Resend rejected frames */ - irlap_resend_rejected_frames(self, CMD_FRAME); - - /* Give peer some time to retransmit! - * But account for our own Tx. */ - irlap_start_final_timer(self, 2 * self->final_timeout); - - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_P); - } else { - /* Update Nr received */ - /* irlap_update_nr_received( info->nr); */ - - self->ack_required = FALSE; - } - break; - } - - /* - * Invalid NR or NS - */ - if ((nr_status == NR_INVALID) || (ns_status == NS_INVALID)) { - if (info->pf) { - del_timer(&self->final_timer); - - irlap_next_state(self, LAP_RESET_WAIT); - - irlap_disconnect_indication(self, LAP_RESET_INDICATION); - self->xmitflag = TRUE; - } else { - del_timer(&self->final_timer); - - irlap_disconnect_indication(self, LAP_RESET_INDICATION); - - self->xmitflag = FALSE; - } - break; - } - pr_debug("%s(), Not implemented!\n", __func__); - pr_debug("%s(), event=%s, ns_status=%d, nr_status=%d\n", - __func__, irlap_event[event], ns_status, nr_status); - break; - case RECV_UI_FRAME: - /* Poll bit cleared? */ - if (!info->pf) { - irlap_data_indication(self, skb, TRUE); - irlap_next_state(self, LAP_NRM_P); - } else { - del_timer(&self->final_timer); - irlap_data_indication(self, skb, TRUE); - irlap_next_state(self, LAP_XMIT_P); - pr_debug("%s: RECV_UI_FRAME: next state %s\n", - __func__, irlap_state[self->state]); - irlap_start_poll_timer(self, self->poll_timeout); - } - break; - case RECV_RR_RSP: - /* - * If you get a RR, the remote isn't busy anymore, - * no matter what the NR - */ - self->remote_busy = FALSE; - - /* Stop final timer */ - del_timer(&self->final_timer); - - /* - * Nr as expected? - */ - ret = irlap_validate_nr_received(self, info->nr); - if (ret == NR_EXPECTED) { - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - /* - * Got expected NR, so reset the retry_count. This - * is not done by the IrLAP standard , which is - * strange! DB. - */ - self->retry_count = 0; - irlap_wait_min_turn_around(self, &self->qos_tx); - - irlap_next_state(self, LAP_XMIT_P); - - /* Start poll timer */ - irlap_start_poll_timer(self, self->poll_timeout); - } else if (ret == NR_UNEXPECTED) { - IRDA_ASSERT(info != NULL, return -1;); - /* - * Unexpected nr! - */ - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - pr_debug("RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n", - self->retry_count, info->nr, self->va, - self->vs, self->vr); - - /* Resend rejected frames */ - irlap_resend_rejected_frames(self, CMD_FRAME); - irlap_start_final_timer(self, self->final_timeout * 2); - - irlap_next_state(self, LAP_NRM_P); - } else if (ret == NR_INVALID) { - pr_debug("%s(), Received RR with invalid nr !\n", - __func__); - - irlap_next_state(self, LAP_RESET_WAIT); - - irlap_disconnect_indication(self, LAP_RESET_INDICATION); - self->xmitflag = TRUE; - } - break; - case RECV_RNR_RSP: - IRDA_ASSERT(info != NULL, return -1;); - - /* Stop final timer */ - del_timer(&self->final_timer); - self->remote_busy = TRUE; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - irlap_next_state(self, LAP_XMIT_P); - - /* Start poll timer */ - irlap_start_poll_timer(self, self->poll_timeout); - break; - case RECV_FRMR_RSP: - del_timer(&self->final_timer); - self->xmitflag = TRUE; - irlap_next_state(self, LAP_RESET_WAIT); - irlap_reset_indication(self); - break; - case FINAL_TIMER_EXPIRED: - /* - * We are allowed to wait for additional 300 ms if - * final timer expires when we are in the middle - * of receiving a frame (page 45, IrLAP). Check that - * we only do this once for each frame. - */ - if (irda_device_is_receiving(self->netdev) && !self->add_wait) { - pr_debug("FINAL_TIMER_EXPIRED when receiving a frame! Waiting a little bit more!\n"); - irlap_start_final_timer(self, msecs_to_jiffies(300)); - - /* - * Don't allow this to happen one more time in a row, - * or else we can get a pretty tight loop here if - * if we only receive half a frame. DB. - */ - self->add_wait = TRUE; - break; - } - self->add_wait = FALSE; - - /* N2 is the disconnect timer. Until we reach it, we retry */ - if (self->retry_count < self->N2) { - if (skb_peek(&self->wx_list) == NULL) { - /* Retry sending the pf bit to the secondary */ - pr_debug("nrm_p: resending rr"); - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, CMD_FRAME); - } else { - pr_debug("nrm_p: resend frames"); - irlap_resend_rejected_frames(self, CMD_FRAME); - } - - irlap_start_final_timer(self, self->final_timeout); - self->retry_count++; - pr_debug("irlap_state_nrm_p: FINAL_TIMER_EXPIRED: retry_count=%d\n", - self->retry_count); - - /* Early warning event. I'm using a pretty liberal - * interpretation of the spec and generate an event - * every time the timer is multiple of N1 (and not - * only the first time). This allow application - * to know precisely if connectivity restart... - * Jean II */ - if((self->retry_count % self->N1) == 0) - irlap_status_indication(self, - STATUS_NO_ACTIVITY); - - /* Keep state */ - } else { - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - irlap_disconnect_indication(self, LAP_NO_RESPONSE); - } - break; - case RECV_REJ_RSP: - irlap_update_nr_received(self, info->nr); - if (self->remote_busy) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, CMD_FRAME); - } else - irlap_resend_rejected_frames(self, CMD_FRAME); - irlap_start_final_timer(self, 2 * self->final_timeout); - break; - case RECV_SREJ_RSP: - irlap_update_nr_received(self, info->nr); - if (self->remote_busy) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, CMD_FRAME); - } else - irlap_resend_rejected_frame(self, CMD_FRAME); - irlap_start_final_timer(self, 2 * self->final_timeout); - break; - case RECV_RD_RSP: - pr_debug("%s(), RECV_RD_RSP\n", __func__); - - irlap_flush_all_queues(self); - irlap_next_state(self, LAP_XMIT_P); - /* Call back the LAP state machine to do a proper disconnect */ - irlap_disconnect_request(self); - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_reset_wait (event, skb, info) - * - * We have informed the service user of a reset condition, and is - * awaiting reset of disconnect request. - * - */ -static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - pr_debug("%s(), event = %s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case RESET_REQUEST: - if (self->xmitflag) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_snrm_frame(self, NULL); - irlap_start_final_timer(self, self->final_timeout); - irlap_next_state(self, LAP_RESET); - } else { - irlap_start_final_timer(self, self->final_timeout); - irlap_next_state(self, LAP_RESET); - } - break; - case DISCONNECT_REQUEST: - irlap_wait_min_turn_around( self, &self->qos_tx); - irlap_send_disc_frame( self); - irlap_flush_all_queues( self); - irlap_start_final_timer( self, self->final_timeout); - self->retry_count = 0; - irlap_next_state( self, LAP_PCLOSE); - break; - default: - pr_debug("%s(), Unknown event %s\n", __func__, - irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_reset (self, event, skb, info) - * - * We have sent a SNRM reset command to the peer layer, and is awaiting - * reply. - * - */ -static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - pr_debug("%s(), event = %s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case RECV_DISC_CMD: - del_timer(&self->final_timer); - - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_NO_RESPONSE); - - break; - case RECV_UA_RSP: - del_timer(&self->final_timer); - - /* Initiate connection state */ - irlap_initiate_connection_state(self); - - irlap_reset_confirm(); - - self->remote_busy = FALSE; - - irlap_next_state(self, LAP_XMIT_P); - - irlap_start_poll_timer(self, self->poll_timeout); - - break; - case FINAL_TIMER_EXPIRED: - if (self->retry_count < 3) { - irlap_wait_min_turn_around(self, &self->qos_tx); - - IRDA_ASSERT(self->netdev != NULL, return -1;); - irlap_send_snrm_frame(self, self->qos_dev); - - self->retry_count++; /* Experimental!! */ - - irlap_start_final_timer(self, self->final_timeout); - irlap_next_state(self, LAP_RESET); - } else if (self->retry_count >= self->N3) { - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_NO_RESPONSE); - } - break; - case RECV_SNRM_CMD: - /* - * SNRM frame is not allowed to contain an I-field in this - * state - */ - if (!info) { - pr_debug("%s(), RECV_SNRM_CMD\n", __func__); - irlap_initiate_connection_state(self); - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_ua_response_frame(self, &self->qos_rx); - irlap_reset_confirm(); - irlap_start_wd_timer(self, self->wd_timeout); - irlap_next_state(self, LAP_NDM); - } else { - pr_debug("%s(), SNRM frame contained an I field!\n", - __func__); - } - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_xmit_s (event, skb, info) - * - * XMIT_S, The secondary station has been given the right to transmit, - * and we therefore do not expect to receive any transmissions from other - * stations. - */ -static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -ENODEV;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); - - switch (event) { - case SEND_I_CMD: - /* - * Send frame only if send window > 0 - */ - if ((self->window > 0) && (!self->remote_busy)) { - int nextfit; -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - struct sk_buff *skb_next; - - /* - * Same deal as in irlap_state_xmit_p(), so see - * the comments at that point. - * We are the secondary, so there are only subtle - * differences. - Jean II - */ - - /* Check if a subsequent skb exist and would fit in - * the current window (with respect to turnaround - * time). - Jean II */ - skb_next = skb_peek(&self->txq); - nextfit = ((skb_next != NULL) && - ((skb_next->len + skb->len) <= - self->bytes_left)); - - /* - * Test if we have transmitted more bytes over the - * link than its possible to do with the current - * speed and turn-around-time. - */ - if((!nextfit) && (skb->len > self->bytes_left)) { - pr_debug("%s(), Not allowed to transmit more bytes!\n", - __func__); - /* Requeue the skb */ - skb_queue_head(&self->txq, skb_get(skb)); - - /* - * Switch to NRM_S, this is only possible - * when we are in secondary mode, since we - * must be sure that we don't miss any RR - * frames - */ - self->window = self->window_size; - self->bytes_left = self->line_capacity; - irlap_start_wd_timer(self, self->wd_timeout); - - irlap_next_state(self, LAP_NRM_S); - /* Slight difference with primary : - * here we would wait for the other side to - * expire the turnaround. - Jean II */ - - return -EPROTO; /* Try again later */ - } - /* Subtract space used by this skb */ - self->bytes_left -= skb->len; -#else /* CONFIG_IRDA_DYNAMIC_WINDOW */ - /* Window has been adjusted for the max packet - * size, so much simpler... - Jean II */ - nextfit = !skb_queue_empty(&self->txq); -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - /* - * Send data with final bit cleared only if window > 1 - * and there is more frames to be sent - */ - if ((self->window > 1) && (nextfit)) { - irlap_send_data_secondary(self, skb); - irlap_next_state(self, LAP_XMIT_S); - } else { - irlap_send_data_secondary_final(self, skb); - irlap_next_state(self, LAP_NRM_S); - - /* - * Make sure state machine does not try to send - * any more frames - */ - ret = -EPROTO; - } - } else { - pr_debug("%s(), Unable to send!\n", __func__); - skb_queue_head(&self->txq, skb_get(skb)); - ret = -EPROTO; - } - break; - case DISCONNECT_REQUEST: - irlap_send_rd_frame(self); - irlap_flush_all_queues(self); - irlap_start_wd_timer(self, self->wd_timeout); - irlap_next_state(self, LAP_SCLOSE); - break; - case DATA_REQUEST: - /* Nothing to do, irlap_do_event() will send the packet - * when we return... - Jean II */ - break; - default: - pr_debug("%s(), Unknown event %s\n", __func__, - irlap_event[event]); - - ret = -EINVAL; - break; - } - return ret; -} - -/* - * Function irlap_state_nrm_s (event, skb, info) - * - * NRM_S (Normal Response Mode as Secondary) state, in this state we are - * expecting to receive frames from the primary station - * - */ -static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ns_status; - int nr_status; - int ret = 0; - - pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case RECV_I_CMD: /* Optimize for the common case */ - /* FIXME: must check for remote_busy below */ - pr_debug("%s(), event=%s nr=%d, vs=%d, ns=%d, vr=%d, pf=%d\n", - __func__, irlap_event[event], info->nr, - self->vs, info->ns, self->vr, info->pf); - - self->retry_count = 0; - - ns_status = irlap_validate_ns_received(self, info->ns); - nr_status = irlap_validate_nr_received(self, info->nr); - /* - * Check for expected I(nformation) frame - */ - if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) { - - /* Update Vr (next frame for us to receive) */ - self->vr = (self->vr + 1) % 8; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - /* - * poll bit cleared? - */ - if (!info->pf) { - - self->ack_required = TRUE; - - /* - * Starting WD-timer here is optional, but - * not recommended. Note 6 IrLAP p. 83 - */ -#if 0 - irda_start_timer(WD_TIMER, self->wd_timeout); -#endif - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_S); - - irlap_data_indication(self, skb, FALSE); - break; - } else { - /* - * We should wait before sending RR, and - * also before changing to XMIT_S - * state. (note 1, IrLAP p. 82) - */ - irlap_wait_min_turn_around(self, &self->qos_tx); - - /* - * Give higher layers a chance to - * immediately reply with some data before - * we decide if we should send a RR frame - * or not - */ - irlap_data_indication(self, skb, FALSE); - - /* Any pending data requests? */ - if (!skb_queue_empty(&self->txq) && - (self->window > 0)) - { - self->ack_required = TRUE; - - del_timer(&self->wd_timer); - - irlap_next_state(self, LAP_XMIT_S); - } else { - irlap_send_rr_frame(self, RSP_FRAME); - irlap_start_wd_timer(self, - self->wd_timeout); - - /* Keep the state */ - irlap_next_state(self, LAP_NRM_S); - } - break; - } - } - /* - * Check for Unexpected next to send (Ns) - */ - if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED)) - { - /* Unexpected next to send, with final bit cleared */ - if (!info->pf) { - irlap_update_nr_received(self, info->nr); - - irlap_start_wd_timer(self, self->wd_timeout); - } else { - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, RSP_FRAME); - - irlap_start_wd_timer(self, self->wd_timeout); - } - break; - } - - /* - * Unexpected Next to Receive(NR) ? - */ - if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED)) - { - if (info->pf) { - pr_debug("RECV_I_RSP: frame(s) lost\n"); - - self->vr = (self->vr + 1) % 8; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - /* Resend rejected frames */ - irlap_resend_rejected_frames(self, RSP_FRAME); - - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_S); - - irlap_data_indication(self, skb, FALSE); - irlap_start_wd_timer(self, self->wd_timeout); - break; - } - /* - * This is not documented in IrLAP!! Unexpected NR - * with poll bit cleared - */ - if (!info->pf) { - self->vr = (self->vr + 1) % 8; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_S); - - irlap_data_indication(self, skb, FALSE); - irlap_start_wd_timer(self, self->wd_timeout); - } - break; - } - - if (ret == NR_INVALID) { - pr_debug("NRM_S, NR_INVALID not implemented!\n"); - } - if (ret == NS_INVALID) { - pr_debug("NRM_S, NS_INVALID not implemented!\n"); - } - break; - case RECV_UI_FRAME: - /* - * poll bit cleared? - */ - if (!info->pf) { - irlap_data_indication(self, skb, TRUE); - irlap_next_state(self, LAP_NRM_S); /* Keep state */ - } else { - /* - * Any pending data requests? - */ - if (!skb_queue_empty(&self->txq) && - (self->window > 0) && !self->remote_busy) - { - irlap_data_indication(self, skb, TRUE); - - del_timer(&self->wd_timer); - - irlap_next_state(self, LAP_XMIT_S); - } else { - irlap_data_indication(self, skb, TRUE); - - irlap_wait_min_turn_around(self, &self->qos_tx); - - irlap_send_rr_frame(self, RSP_FRAME); - self->ack_required = FALSE; - - irlap_start_wd_timer(self, self->wd_timeout); - - /* Keep the state */ - irlap_next_state(self, LAP_NRM_S); - } - } - break; - case RECV_RR_CMD: - self->retry_count = 0; - - /* - * Nr as expected? - */ - nr_status = irlap_validate_nr_received(self, info->nr); - if (nr_status == NR_EXPECTED) { - if (!skb_queue_empty(&self->txq) && - (self->window > 0)) { - self->remote_busy = FALSE; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - del_timer(&self->wd_timer); - - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_next_state(self, LAP_XMIT_S); - } else { - self->remote_busy = FALSE; - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_start_wd_timer(self, self->wd_timeout); - - /* Note : if the link is idle (this case), - * we never go in XMIT_S, so we never get a - * chance to process any DISCONNECT_REQUEST. - * Do it now ! - Jean II */ - if (self->disconnect_pending) { - /* Disconnect */ - irlap_send_rd_frame(self); - irlap_flush_all_queues(self); - - irlap_next_state(self, LAP_SCLOSE); - } else { - /* Just send back pf bit */ - irlap_send_rr_frame(self, RSP_FRAME); - - irlap_next_state(self, LAP_NRM_S); - } - } - } else if (nr_status == NR_UNEXPECTED) { - self->remote_busy = FALSE; - irlap_update_nr_received(self, info->nr); - irlap_resend_rejected_frames(self, RSP_FRAME); - - irlap_start_wd_timer(self, self->wd_timeout); - - /* Keep state */ - irlap_next_state(self, LAP_NRM_S); - } else { - pr_debug("%s(), invalid nr not implemented!\n", - __func__); - } - break; - case RECV_SNRM_CMD: - /* SNRM frame is not allowed to contain an I-field */ - if (!info) { - del_timer(&self->wd_timer); - pr_debug("%s(), received SNRM cmd\n", __func__); - irlap_next_state(self, LAP_RESET_CHECK); - - irlap_reset_indication(self); - } else { - pr_debug("%s(), SNRM frame contained an I-field!\n", - __func__); - - } - break; - case RECV_REJ_CMD: - irlap_update_nr_received(self, info->nr); - if (self->remote_busy) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, RSP_FRAME); - } else - irlap_resend_rejected_frames(self, RSP_FRAME); - irlap_start_wd_timer(self, self->wd_timeout); - break; - case RECV_SREJ_CMD: - irlap_update_nr_received(self, info->nr); - if (self->remote_busy) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, RSP_FRAME); - } else - irlap_resend_rejected_frame(self, RSP_FRAME); - irlap_start_wd_timer(self, self->wd_timeout); - break; - case WD_TIMER_EXPIRED: - /* - * Wait until retry_count * n matches negotiated threshold/ - * disconnect time (note 2 in IrLAP p. 82) - * - * Similar to irlap_state_nrm_p() -> FINAL_TIMER_EXPIRED - * Note : self->wd_timeout = (self->final_timeout * 2), - * which explain why we use (self->N2 / 2) here !!! - * Jean II - */ - pr_debug("%s(), retry_count = %d\n", __func__, - self->retry_count); - - if (self->retry_count < (self->N2 / 2)) { - /* No retry, just wait for primary */ - irlap_start_wd_timer(self, self->wd_timeout); - self->retry_count++; - - if((self->retry_count % (self->N1 / 2)) == 0) - irlap_status_indication(self, - STATUS_NO_ACTIVITY); - } else { - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - irlap_disconnect_indication(self, LAP_NO_RESPONSE); - } - break; - case RECV_DISC_CMD: - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - /* Send disconnect response */ - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_ua_response_frame(self, NULL); - - del_timer(&self->wd_timer); - irlap_flush_all_queues(self); - /* Set default link parameters */ - irlap_apply_default_connection_parameters(self); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - case RECV_DISCOVERY_XID_CMD: - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, RSP_FRAME); - self->ack_required = TRUE; - irlap_start_wd_timer(self, self->wd_timeout); - irlap_next_state(self, LAP_NRM_S); - - break; - case RECV_TEST_CMD: - /* Remove test frame header (only LAP header in NRM) */ - skb_pull(skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER); - - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_start_wd_timer(self, self->wd_timeout); - - /* Send response (info will be copied) */ - irlap_send_test_frame(self, self->caddr, info->daddr, skb); - break; - default: - pr_debug("%s(), Unknown event %d, (%s)\n", __func__, - event, irlap_event[event]); - - ret = -EINVAL; - break; - } - return ret; -} - -/* - * Function irlap_state_sclose (self, event, skb, info) - */ -static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - IRDA_ASSERT(self != NULL, return -ENODEV;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); - - switch (event) { - case RECV_DISC_CMD: - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - /* Send disconnect response */ - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_ua_response_frame(self, NULL); - - del_timer(&self->wd_timer); - /* Set default link parameters */ - irlap_apply_default_connection_parameters(self); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - case RECV_DM_RSP: - /* IrLAP-1.1 p.82: in SCLOSE, S and I type RSP frames - * shall take us down into default NDM state, like DM_RSP - */ - case RECV_RR_RSP: - case RECV_RNR_RSP: - case RECV_REJ_RSP: - case RECV_SREJ_RSP: - case RECV_I_RSP: - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - del_timer(&self->wd_timer); - irlap_apply_default_connection_parameters(self); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - case WD_TIMER_EXPIRED: - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_apply_default_connection_parameters(self); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - default: - /* IrLAP-1.1 p.82: in SCLOSE, basically any received frame - * with pf=1 shall restart the wd-timer and resend the rd:rsp - */ - if (info != NULL && info->pf) { - del_timer(&self->wd_timer); - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rd_frame(self); - irlap_start_wd_timer(self, self->wd_timeout); - break; /* stay in SCLOSE */ - } - - pr_debug("%s(), Unknown event %d, (%s)\n", __func__, - event, irlap_event[event]); - - break; - } - - return -1; -} - -static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, - struct irlap_info *info) -{ - int ret = 0; - - pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -ENODEV;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); - - switch (event) { - case RESET_RESPONSE: - irlap_send_ua_response_frame(self, &self->qos_rx); - irlap_initiate_connection_state(self); - irlap_start_wd_timer(self, WD_TIMEOUT); - irlap_flush_all_queues(self); - - irlap_next_state(self, LAP_NRM_S); - break; - case DISCONNECT_REQUEST: - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rd_frame(self); - irlap_start_wd_timer(self, WD_TIMEOUT); - irlap_next_state(self, LAP_SCLOSE); - break; - default: - pr_debug("%s(), Unknown event %d, (%s)\n", __func__, - event, irlap_event[event]); - - ret = -EINVAL; - break; - } - return ret; -} diff --git a/drivers/staging/irda/net/irlap_frame.c b/drivers/staging/irda/net/irlap_frame.c deleted file mode 100644 index debda3de4726..000000000000 --- a/drivers/staging/irda/net/irlap_frame.c +++ /dev/null @@ -1,1407 +0,0 @@ -/********************************************************************* - * - * Filename: irlap_frame.c - * Version: 1.0 - * Description: Build and transmit IrLAP frames - * Status: Stable - * Author: Dag Brattli - * Created at: Tue Aug 19 10:27:26 1997 - * Modified at: Wed Jan 5 08:59:04 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb, - int command); - -/* - * Function irlap_insert_info (self, skb) - * - * Insert minimum turnaround time and speed information into the skb. We - * need to do this since it's per packet relevant information. Safe to - * have this function inlined since it's only called from one place - */ -static inline void irlap_insert_info(struct irlap_cb *self, - struct sk_buff *skb) -{ - struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; - - /* - * Insert MTT (min. turn time) and speed into skb, so that the - * device driver knows which settings to use - */ - cb->magic = LAP_MAGIC; - cb->mtt = self->mtt_required; - cb->next_speed = self->speed; - - /* Reset */ - self->mtt_required = 0; - - /* - * Delay equals negotiated BOFs count, plus the number of BOFs to - * force the negotiated minimum turnaround time - */ - cb->xbofs = self->bofs_count; - cb->next_xbofs = self->next_bofs; - cb->xbofs_delay = self->xbofs_delay; - - /* Reset XBOF's delay (used only for getting min turn time) */ - self->xbofs_delay = 0; - /* Put the correct xbofs value for the next packet */ - self->bofs_count = self->next_bofs; -} - -/* - * Function irlap_queue_xmit (self, skb) - * - * A little wrapper for dev_queue_xmit, so we can insert some common - * code into it. - */ -void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb) -{ - /* Some common init stuff */ - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb->protocol = htons(ETH_P_IRDA); - skb->priority = TC_PRIO_BESTEFFORT; - - irlap_insert_info(self, skb); - - if (unlikely(self->mode & IRDA_MODE_MONITOR)) { - pr_debug("%s(): %s is in monitor mode\n", __func__, - self->netdev->name); - dev_kfree_skb(skb); - return; - } - - dev_queue_xmit(skb); -} - -/* - * Function irlap_send_snrm_cmd (void) - * - * Transmits a connect SNRM command frame - */ -void irlap_send_snrm_frame(struct irlap_cb *self, struct qos_info *qos) -{ - struct sk_buff *tx_skb; - struct snrm_frame *frame; - int ret; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Allocate frame */ - tx_skb = alloc_skb(sizeof(struct snrm_frame) + - IRLAP_NEGOCIATION_PARAMS_LEN, - GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 2); - - /* Insert connection address field */ - if (qos) - frame->caddr = CMD_FRAME | CBROADCAST; - else - frame->caddr = CMD_FRAME | self->caddr; - - /* Insert control field */ - frame->control = SNRM_CMD | PF_BIT; - - /* - * If we are establishing a connection then insert QoS parameters - */ - if (qos) { - skb_put(tx_skb, 9); /* 25 left */ - frame->saddr = cpu_to_le32(self->saddr); - frame->daddr = cpu_to_le32(self->daddr); - - frame->ncaddr = self->caddr; - - ret = irlap_insert_qos_negotiation_params(self, tx_skb); - if (ret < 0) { - dev_kfree_skb(tx_skb); - return; - } - } - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_recv_snrm_cmd (skb, info) - * - * Received SNRM (Set Normal Response Mode) command frame - * - */ -static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info) -{ - struct snrm_frame *frame; - - if (pskb_may_pull(skb,sizeof(struct snrm_frame))) { - frame = (struct snrm_frame *) skb->data; - - /* Copy the new connection address ignoring the C/R bit */ - info->caddr = frame->ncaddr & 0xFE; - - /* Check if the new connection address is valid */ - if ((info->caddr == 0x00) || (info->caddr == 0xfe)) { - pr_debug("%s(), invalid connection address!\n", - __func__); - return; - } - - /* Copy peer device address */ - info->daddr = le32_to_cpu(frame->saddr); - info->saddr = le32_to_cpu(frame->daddr); - - /* Only accept if addressed directly to us */ - if (info->saddr != self->saddr) { - pr_debug("%s(), not addressed to us!\n", - __func__); - return; - } - irlap_do_event(self, RECV_SNRM_CMD, skb, info); - } else { - /* Signal that this SNRM frame does not contain and I-field */ - irlap_do_event(self, RECV_SNRM_CMD, skb, NULL); - } -} - -/* - * Function irlap_send_ua_response_frame (qos) - * - * Send UA (Unnumbered Acknowledgement) frame - * - */ -void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos) -{ - struct sk_buff *tx_skb; - struct ua_frame *frame; - int ret; - - pr_debug("%s() <%ld>\n", __func__, jiffies); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Allocate frame */ - tx_skb = alloc_skb(sizeof(struct ua_frame) + - IRLAP_NEGOCIATION_PARAMS_LEN, - GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 10); - - /* Build UA response */ - frame->caddr = self->caddr; - frame->control = UA_RSP | PF_BIT; - - frame->saddr = cpu_to_le32(self->saddr); - frame->daddr = cpu_to_le32(self->daddr); - - /* Should we send QoS negotiation parameters? */ - if (qos) { - ret = irlap_insert_qos_negotiation_params(self, tx_skb); - if (ret < 0) { - dev_kfree_skb(tx_skb); - return; - } - } - - irlap_queue_xmit(self, tx_skb); -} - - -/* - * Function irlap_send_dm_frame (void) - * - * Send disconnected mode (DM) frame - * - */ -void irlap_send_dm_frame( struct irlap_cb *self) -{ - struct sk_buff *tx_skb = NULL; - struct dm_frame *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - tx_skb = alloc_skb(sizeof(struct dm_frame), GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 2); - - if (self->state == LAP_NDM) - frame->caddr = CBROADCAST; - else - frame->caddr = self->caddr; - - frame->control = DM_RSP | PF_BIT; - - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_send_disc_frame (void) - * - * Send disconnect (DISC) frame - * - */ -void irlap_send_disc_frame(struct irlap_cb *self) -{ - struct sk_buff *tx_skb = NULL; - struct disc_frame *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - tx_skb = alloc_skb(sizeof(struct disc_frame), GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 2); - - frame->caddr = self->caddr | CMD_FRAME; - frame->control = DISC_CMD | PF_BIT; - - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_send_discovery_xid_frame (S, s, command) - * - * Build and transmit a XID (eXchange station IDentifier) discovery - * frame. - */ -void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s, - __u8 command, discovery_t *discovery) -{ - struct sk_buff *tx_skb = NULL; - struct xid_frame *frame; - __u32 bcast = BROADCAST; - __u8 *info; - - pr_debug("%s(), s=%d, S=%d, command=%d\n", __func__, - s, S, command); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(discovery != NULL, return;); - - tx_skb = alloc_skb(sizeof(struct xid_frame) + IRLAP_DISCOVERY_INFO_LEN, - GFP_ATOMIC); - if (!tx_skb) - return; - - skb_put(tx_skb, 14); - frame = (struct xid_frame *) tx_skb->data; - - if (command) { - frame->caddr = CBROADCAST | CMD_FRAME; - frame->control = XID_CMD | PF_BIT; - } else { - frame->caddr = CBROADCAST; - frame->control = XID_RSP | PF_BIT; - } - frame->ident = XID_FORMAT; - - frame->saddr = cpu_to_le32(self->saddr); - - if (command) - frame->daddr = cpu_to_le32(bcast); - else - frame->daddr = cpu_to_le32(discovery->data.daddr); - - switch (S) { - case 1: - frame->flags = 0x00; - break; - case 6: - frame->flags = 0x01; - break; - case 8: - frame->flags = 0x02; - break; - case 16: - frame->flags = 0x03; - break; - default: - frame->flags = 0x02; - break; - } - - frame->slotnr = s; - frame->version = 0x00; - - /* - * Provide info for final slot only in commands, and for all - * responses. Send the second byte of the hint only if the - * EXTENSION bit is set in the first byte. - */ - if (!command || (frame->slotnr == 0xff)) { - int len; - - if (discovery->data.hints[0] & HINT_EXTENSION) { - info = skb_put(tx_skb, 2); - info[0] = discovery->data.hints[0]; - info[1] = discovery->data.hints[1]; - } else { - info = skb_put(tx_skb, 1); - info[0] = discovery->data.hints[0]; - } - info = skb_put(tx_skb, 1); - info[0] = discovery->data.charset; - - len = IRDA_MIN(discovery->name_len, skb_tailroom(tx_skb)); - skb_put_data(tx_skb, discovery->data.info, len); - } - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_recv_discovery_xid_rsp (skb, info) - * - * Received a XID discovery response - * - */ -static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self, - struct sk_buff *skb, - struct irlap_info *info) -{ - struct xid_frame *xid; - discovery_t *discovery = NULL; - __u8 *discovery_info; - char *text; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - if (!pskb_may_pull(skb, sizeof(struct xid_frame))) { - net_err_ratelimited("%s: frame too short!\n", __func__); - return; - } - - xid = (struct xid_frame *) skb->data; - - info->daddr = le32_to_cpu(xid->saddr); - info->saddr = le32_to_cpu(xid->daddr); - - /* Make sure frame is addressed to us */ - if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { - pr_debug("%s(), frame is not addressed to us!\n", - __func__); - return; - } - - if ((discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC)) == NULL) { - net_warn_ratelimited("%s: kmalloc failed!\n", __func__); - return; - } - - discovery->data.daddr = info->daddr; - discovery->data.saddr = self->saddr; - discovery->timestamp = jiffies; - - pr_debug("%s(), daddr=%08x\n", __func__, - discovery->data.daddr); - - discovery_info = skb_pull(skb, sizeof(struct xid_frame)); - - /* Get info returned from peer */ - discovery->data.hints[0] = discovery_info[0]; - if (discovery_info[0] & HINT_EXTENSION) { - pr_debug("EXTENSION\n"); - discovery->data.hints[1] = discovery_info[1]; - discovery->data.charset = discovery_info[2]; - text = (char *) &discovery_info[3]; - } else { - discovery->data.hints[1] = 0; - discovery->data.charset = discovery_info[1]; - text = (char *) &discovery_info[2]; - } - /* - * Terminate info string, should be safe since this is where the - * FCS bytes resides. - */ - skb->data[skb->len] = '\0'; - strncpy(discovery->data.info, text, NICKNAME_MAX_LEN); - discovery->name_len = strlen(discovery->data.info); - - info->discovery = discovery; - - irlap_do_event(self, RECV_DISCOVERY_XID_RSP, skb, info); -} - -/* - * Function irlap_recv_discovery_xid_cmd (skb, info) - * - * Received a XID discovery command - * - */ -static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self, - struct sk_buff *skb, - struct irlap_info *info) -{ - struct xid_frame *xid; - discovery_t *discovery = NULL; - __u8 *discovery_info; - char *text; - - if (!pskb_may_pull(skb, sizeof(struct xid_frame))) { - net_err_ratelimited("%s: frame too short!\n", __func__); - return; - } - - xid = (struct xid_frame *) skb->data; - - info->daddr = le32_to_cpu(xid->saddr); - info->saddr = le32_to_cpu(xid->daddr); - - /* Make sure frame is addressed to us */ - if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { - pr_debug("%s(), frame is not addressed to us!\n", - __func__); - return; - } - - switch (xid->flags & 0x03) { - case 0x00: - info->S = 1; - break; - case 0x01: - info->S = 6; - break; - case 0x02: - info->S = 8; - break; - case 0x03: - info->S = 16; - break; - default: - /* Error!! */ - return; - } - info->s = xid->slotnr; - - discovery_info = skb_pull(skb, sizeof(struct xid_frame)); - - /* - * Check if last frame - */ - if (info->s == 0xff) { - /* Check if things are sane at this point... */ - if((discovery_info == NULL) || - !pskb_may_pull(skb, 3)) { - net_err_ratelimited("%s: discovery frame too short!\n", - __func__); - return; - } - - /* - * We now have some discovery info to deliver! - */ - discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC); - if (!discovery) - return; - - discovery->data.daddr = info->daddr; - discovery->data.saddr = self->saddr; - discovery->timestamp = jiffies; - - discovery->data.hints[0] = discovery_info[0]; - if (discovery_info[0] & HINT_EXTENSION) { - discovery->data.hints[1] = discovery_info[1]; - discovery->data.charset = discovery_info[2]; - text = (char *) &discovery_info[3]; - } else { - discovery->data.hints[1] = 0; - discovery->data.charset = discovery_info[1]; - text = (char *) &discovery_info[2]; - } - /* - * Terminate string, should be safe since this is where the - * FCS bytes resides. - */ - skb->data[skb->len] = '\0'; - strncpy(discovery->data.info, text, NICKNAME_MAX_LEN); - discovery->name_len = strlen(discovery->data.info); - - info->discovery = discovery; - } else - info->discovery = NULL; - - irlap_do_event(self, RECV_DISCOVERY_XID_CMD, skb, info); -} - -/* - * Function irlap_send_rr_frame (self, command) - * - * Build and transmit RR (Receive Ready) frame. Notice that it is currently - * only possible to send RR frames with the poll bit set. - */ -void irlap_send_rr_frame(struct irlap_cb *self, int command) -{ - struct sk_buff *tx_skb; - struct rr_frame *frame; - - tx_skb = alloc_skb(sizeof(struct rr_frame), GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 2); - - frame->caddr = self->caddr; - frame->caddr |= (command) ? CMD_FRAME : 0; - - frame->control = RR | PF_BIT | (self->vr << 5); - - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_send_rd_frame (self) - * - * Request disconnect. Used by a secondary station to request the - * disconnection of the link. - */ -void irlap_send_rd_frame(struct irlap_cb *self) -{ - struct sk_buff *tx_skb; - struct rd_frame *frame; - - tx_skb = alloc_skb(sizeof(struct rd_frame), GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 2); - - frame->caddr = self->caddr; - frame->control = RD_RSP | PF_BIT; - - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_recv_rr_frame (skb, info) - * - * Received RR (Receive Ready) frame from peer station, no harm in - * making it inline since its called only from one single place - * (irlap_driver_rcv). - */ -static inline void irlap_recv_rr_frame(struct irlap_cb *self, - struct sk_buff *skb, - struct irlap_info *info, int command) -{ - info->nr = skb->data[1] >> 5; - - /* Check if this is a command or a response frame */ - if (command) - irlap_do_event(self, RECV_RR_CMD, skb, info); - else - irlap_do_event(self, RECV_RR_RSP, skb, info); -} - -/* - * Function irlap_recv_rnr_frame (self, skb, info) - * - * Received RNR (Receive Not Ready) frame from peer station - * - */ -static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info, int command) -{ - info->nr = skb->data[1] >> 5; - - pr_debug("%s(), nr=%d, %ld\n", __func__, info->nr, jiffies); - - if (command) - irlap_do_event(self, RECV_RNR_CMD, skb, info); - else - irlap_do_event(self, RECV_RNR_RSP, skb, info); -} - -static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info, int command) -{ - info->nr = skb->data[1] >> 5; - - /* Check if this is a command or a response frame */ - if (command) - irlap_do_event(self, RECV_REJ_CMD, skb, info); - else - irlap_do_event(self, RECV_REJ_RSP, skb, info); -} - -static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info, int command) -{ - info->nr = skb->data[1] >> 5; - - /* Check if this is a command or a response frame */ - if (command) - irlap_do_event(self, RECV_SREJ_CMD, skb, info); - else - irlap_do_event(self, RECV_SREJ_RSP, skb, info); -} - -static void irlap_recv_disc_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info, int command) -{ - /* Check if this is a command or a response frame */ - if (command) - irlap_do_event(self, RECV_DISC_CMD, skb, info); - else - irlap_do_event(self, RECV_RD_RSP, skb, info); -} - -/* - * Function irlap_recv_ua_frame (skb, frame) - * - * Received UA (Unnumbered Acknowledgement) frame - * - */ -static inline void irlap_recv_ua_frame(struct irlap_cb *self, - struct sk_buff *skb, - struct irlap_info *info) -{ - irlap_do_event(self, RECV_UA_RSP, skb, info); -} - -/* - * Function irlap_send_data_primary(self, skb) - * - * Send I-frames as the primary station but without the poll bit set - * - */ -void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - - if (skb->data[1] == I_FRAME) { - - /* - * Insert frame sequence number (Vs) in control field before - * inserting into transmit window queue. - */ - skb->data[1] = I_FRAME | (self->vs << 1); - - /* - * Insert frame in store, in case of retransmissions - * Increase skb reference count, see irlap_do_event() - */ - skb_get(skb); - skb_queue_tail(&self->wx_list, skb); - - /* Copy buffer */ - tx_skb = skb_clone(skb, GFP_ATOMIC); - if (tx_skb == NULL) { - return; - } - - self->vs = (self->vs + 1) % 8; - self->ack_required = FALSE; - self->window -= 1; - - irlap_send_i_frame( self, tx_skb, CMD_FRAME); - } else { - pr_debug("%s(), sending unreliable frame\n", __func__); - irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); - self->window -= 1; - } -} -/* - * Function irlap_send_data_primary_poll (self, skb) - * - * Send I(nformation) frame as primary with poll bit set - */ -void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - int transmission_time; - - /* Stop P timer */ - del_timer(&self->poll_timer); - - /* Is this reliable or unreliable data? */ - if (skb->data[1] == I_FRAME) { - - /* - * Insert frame sequence number (Vs) in control field before - * inserting into transmit window queue. - */ - skb->data[1] = I_FRAME | (self->vs << 1); - - /* - * Insert frame in store, in case of retransmissions - * Increase skb reference count, see irlap_do_event() - */ - skb_get(skb); - skb_queue_tail(&self->wx_list, skb); - - /* Copy buffer */ - tx_skb = skb_clone(skb, GFP_ATOMIC); - if (tx_skb == NULL) { - return; - } - - /* - * Set poll bit if necessary. We do this to the copied - * skb, since retransmitted need to set or clear the poll - * bit depending on when they are sent. - */ - tx_skb->data[1] |= PF_BIT; - - self->vs = (self->vs + 1) % 8; - self->ack_required = FALSE; - - irlap_next_state(self, LAP_NRM_P); - irlap_send_i_frame(self, tx_skb, CMD_FRAME); - } else { - pr_debug("%s(), sending unreliable frame\n", __func__); - - if (self->ack_required) { - irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); - irlap_next_state(self, LAP_NRM_P); - irlap_send_rr_frame(self, CMD_FRAME); - self->ack_required = FALSE; - } else { - skb->data[1] |= PF_BIT; - irlap_next_state(self, LAP_NRM_P); - irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); - } - } - - /* How much time we took for transmission of all frames. - * We don't know, so let assume we used the full window. Jean II */ - transmission_time = self->final_timeout; - - /* Reset parameter so that we can fill next window */ - self->window = self->window_size; - -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - /* Remove what we have not used. Just do a prorata of the - * bytes left in window to window capacity. - * See max_line_capacities[][] in qos.c for details. Jean II */ - transmission_time -= (self->final_timeout * self->bytes_left - / self->line_capacity); - pr_debug("%s() adjusting transmission_time : ft=%d, bl=%d, lc=%d -> tt=%d\n", - __func__, self->final_timeout, self->bytes_left, - self->line_capacity, transmission_time); - - /* We are allowed to transmit a maximum number of bytes again. */ - self->bytes_left = self->line_capacity; -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - - /* - * The network layer has a intermediate buffer between IrLAP - * and the IrDA driver which can contain 8 frames. So, even - * though IrLAP is currently sending the *last* frame of the - * tx-window, the driver most likely has only just started - * sending the *first* frame of the same tx-window. - * I.e. we are always at the very beginning of or Tx window. - * Now, we are supposed to set the final timer from the end - * of our tx-window to let the other peer reply. So, we need - * to add extra time to compensate for the fact that we - * are really at the start of tx-window, otherwise the final timer - * might expire before he can answer... - * Jean II - */ - irlap_start_final_timer(self, self->final_timeout + transmission_time); - - /* - * The clever amongst you might ask why we do this adjustement - * only here, and not in all the other cases in irlap_event.c. - * In all those other case, we only send a very short management - * frame (few bytes), so the adjustement would be lost in the - * noise... - * The exception of course is irlap_resend_rejected_frame(). - * Jean II */ -} - -/* - * Function irlap_send_data_secondary_final (self, skb) - * - * Send I(nformation) frame as secondary with final bit set - * - */ -void irlap_send_data_secondary_final(struct irlap_cb *self, - struct sk_buff *skb) -{ - struct sk_buff *tx_skb = NULL; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Is this reliable or unreliable data? */ - if (skb->data[1] == I_FRAME) { - - /* - * Insert frame sequence number (Vs) in control field before - * inserting into transmit window queue. - */ - skb->data[1] = I_FRAME | (self->vs << 1); - - /* - * Insert frame in store, in case of retransmissions - * Increase skb reference count, see irlap_do_event() - */ - skb_get(skb); - skb_queue_tail(&self->wx_list, skb); - - tx_skb = skb_clone(skb, GFP_ATOMIC); - if (tx_skb == NULL) { - return; - } - - tx_skb->data[1] |= PF_BIT; - - self->vs = (self->vs + 1) % 8; - self->ack_required = FALSE; - - irlap_send_i_frame(self, tx_skb, RSP_FRAME); - } else { - if (self->ack_required) { - irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); - irlap_send_rr_frame(self, RSP_FRAME); - self->ack_required = FALSE; - } else { - skb->data[1] |= PF_BIT; - irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); - } - } - - self->window = self->window_size; -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - /* We are allowed to transmit a maximum number of bytes again. */ - self->bytes_left = self->line_capacity; -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - - irlap_start_wd_timer(self, self->wd_timeout); -} - -/* - * Function irlap_send_data_secondary (self, skb) - * - * Send I(nformation) frame as secondary without final bit set - * - */ -void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb) -{ - struct sk_buff *tx_skb = NULL; - - /* Is this reliable or unreliable data? */ - if (skb->data[1] == I_FRAME) { - - /* - * Insert frame sequence number (Vs) in control field before - * inserting into transmit window queue. - */ - skb->data[1] = I_FRAME | (self->vs << 1); - - /* - * Insert frame in store, in case of retransmissions - * Increase skb reference count, see irlap_do_event() - */ - skb_get(skb); - skb_queue_tail(&self->wx_list, skb); - - tx_skb = skb_clone(skb, GFP_ATOMIC); - if (tx_skb == NULL) { - return; - } - - self->vs = (self->vs + 1) % 8; - self->ack_required = FALSE; - self->window -= 1; - - irlap_send_i_frame(self, tx_skb, RSP_FRAME); - } else { - irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); - self->window -= 1; - } -} - -/* - * Function irlap_resend_rejected_frames (nr) - * - * Resend frames which has not been acknowledged. Should be safe to - * traverse the list without locking it since this function will only be - * called from interrupt context (BH) - */ -void irlap_resend_rejected_frames(struct irlap_cb *self, int command) -{ - struct sk_buff *tx_skb; - struct sk_buff *skb; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Resend unacknowledged frame(s) */ - skb_queue_walk(&self->wx_list, skb) { - irlap_wait_min_turn_around(self, &self->qos_tx); - - /* We copy the skb to be retransmitted since we will have to - * modify it. Cloning will confuse packet sniffers - */ - /* tx_skb = skb_clone( skb, GFP_ATOMIC); */ - tx_skb = skb_copy(skb, GFP_ATOMIC); - if (!tx_skb) { - pr_debug("%s(), unable to copy\n", __func__); - return; - } - - /* Clear old Nr field + poll bit */ - tx_skb->data[1] &= 0x0f; - - /* - * Set poll bit on the last frame retransmitted - */ - if (skb_queue_is_last(&self->wx_list, skb)) - tx_skb->data[1] |= PF_BIT; /* Set p/f bit */ - else - tx_skb->data[1] &= ~PF_BIT; /* Clear p/f bit */ - - irlap_send_i_frame(self, tx_skb, command); - } -#if 0 /* Not yet */ - /* - * We can now fill the window with additional data frames - */ - while (!skb_queue_empty(&self->txq)) { - - pr_debug("%s(), sending additional frames!\n", __func__); - if (self->window > 0) { - skb = skb_dequeue( &self->txq); - IRDA_ASSERT(skb != NULL, return;); - - /* - * If send window > 1 then send frame with pf - * bit cleared - */ - if ((self->window > 1) && - !skb_queue_empty(&self->txq)) { - irlap_send_data_primary(self, skb); - } else { - irlap_send_data_primary_poll(self, skb); - } - kfree_skb(skb); - } - } -#endif -} - -void irlap_resend_rejected_frame(struct irlap_cb *self, int command) -{ - struct sk_buff *tx_skb; - struct sk_buff *skb; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Resend unacknowledged frame(s) */ - skb = skb_peek(&self->wx_list); - if (skb != NULL) { - irlap_wait_min_turn_around(self, &self->qos_tx); - - /* We copy the skb to be retransmitted since we will have to - * modify it. Cloning will confuse packet sniffers - */ - /* tx_skb = skb_clone( skb, GFP_ATOMIC); */ - tx_skb = skb_copy(skb, GFP_ATOMIC); - if (!tx_skb) { - pr_debug("%s(), unable to copy\n", __func__); - return; - } - - /* Clear old Nr field + poll bit */ - tx_skb->data[1] &= 0x0f; - - /* Set poll/final bit */ - tx_skb->data[1] |= PF_BIT; /* Set p/f bit */ - - irlap_send_i_frame(self, tx_skb, command); - } -} - -/* - * Function irlap_send_ui_frame (self, skb, command) - * - * Contruct and transmit an Unnumbered Information (UI) frame - * - */ -void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb, - __u8 caddr, int command) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Insert connection address */ - skb->data[0] = caddr | ((command) ? CMD_FRAME : 0); - - irlap_queue_xmit(self, skb); -} - -/* - * Function irlap_send_i_frame (skb) - * - * Contruct and transmit Information (I) frame - */ -static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb, - int command) -{ - /* Insert connection address */ - skb->data[0] = self->caddr; - skb->data[0] |= (command) ? CMD_FRAME : 0; - - /* Insert next to receive (Vr) */ - skb->data[1] |= (self->vr << 5); /* insert nr */ - - irlap_queue_xmit(self, skb); -} - -/* - * Function irlap_recv_i_frame (skb, frame) - * - * Receive and parse an I (Information) frame, no harm in making it inline - * since it's called only from one single place (irlap_driver_rcv). - */ -static inline void irlap_recv_i_frame(struct irlap_cb *self, - struct sk_buff *skb, - struct irlap_info *info, int command) -{ - info->nr = skb->data[1] >> 5; /* Next to receive */ - info->pf = skb->data[1] & PF_BIT; /* Final bit */ - info->ns = (skb->data[1] >> 1) & 0x07; /* Next to send */ - - /* Check if this is a command or a response frame */ - if (command) - irlap_do_event(self, RECV_I_CMD, skb, info); - else - irlap_do_event(self, RECV_I_RSP, skb, info); -} - -/* - * Function irlap_recv_ui_frame (self, skb, info) - * - * Receive and parse an Unnumbered Information (UI) frame - * - */ -static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info) -{ - info->pf = skb->data[1] & PF_BIT; /* Final bit */ - - irlap_do_event(self, RECV_UI_FRAME, skb, info); -} - -/* - * Function irlap_recv_frmr_frame (skb, frame) - * - * Received Frame Reject response. - * - */ -static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info) -{ - __u8 *frame; - int w, x, y, z; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(info != NULL, return;); - - if (!pskb_may_pull(skb, 4)) { - net_err_ratelimited("%s: frame too short!\n", __func__); - return; - } - - frame = skb->data; - - info->nr = frame[2] >> 5; /* Next to receive */ - info->pf = frame[2] & PF_BIT; /* Final bit */ - info->ns = (frame[2] >> 1) & 0x07; /* Next to send */ - - w = frame[3] & 0x01; - x = frame[3] & 0x02; - y = frame[3] & 0x04; - z = frame[3] & 0x08; - - if (w) { - pr_debug("Rejected control field is undefined or not implemented\n"); - } - if (x) { - pr_debug("Rejected control field was invalid because it contained a non permitted I field\n"); - } - if (y) { - pr_debug("Received I field exceeded the maximum negotiated for the existing connection or exceeded the maximum this station supports if no connection exists\n"); - } - if (z) { - pr_debug("Rejected control field control field contained an invalid Nr count\n"); - } - irlap_do_event(self, RECV_FRMR_RSP, skb, info); -} - -/* - * Function irlap_send_test_frame (self, daddr) - * - * Send a test frame response - * - */ -void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr, - struct sk_buff *cmd) -{ - struct sk_buff *tx_skb; - struct test_frame *frame; - - tx_skb = alloc_skb(cmd->len + sizeof(struct test_frame), GFP_ATOMIC); - if (!tx_skb) - return; - - /* Broadcast frames must include saddr and daddr fields */ - if (caddr == CBROADCAST) { - frame = skb_put(tx_skb, sizeof(struct test_frame)); - - /* Insert the swapped addresses */ - frame->saddr = cpu_to_le32(self->saddr); - frame->daddr = cpu_to_le32(daddr); - } else - frame = skb_put(tx_skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER); - - frame->caddr = caddr; - frame->control = TEST_RSP | PF_BIT; - - /* Copy info */ - skb_put_data(tx_skb, cmd->data, cmd->len); - - /* Return to sender */ - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_recv_test_frame (self, skb) - * - * Receive a test frame - * - */ -static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info, int command) -{ - struct test_frame *frame; - - if (!pskb_may_pull(skb, sizeof(*frame))) { - net_err_ratelimited("%s: frame too short!\n", __func__); - return; - } - frame = (struct test_frame *) skb->data; - - /* Broadcast frames must carry saddr and daddr fields */ - if (info->caddr == CBROADCAST) { - if (skb->len < sizeof(struct test_frame)) { - pr_debug("%s() test frame too short!\n", - __func__); - return; - } - - /* Read and swap addresses */ - info->daddr = le32_to_cpu(frame->saddr); - info->saddr = le32_to_cpu(frame->daddr); - - /* Make sure frame is addressed to us */ - if ((info->saddr != self->saddr) && - (info->saddr != BROADCAST)) { - return; - } - } - - if (command) - irlap_do_event(self, RECV_TEST_CMD, skb, info); - else - irlap_do_event(self, RECV_TEST_RSP, skb, info); -} - -/* - * Function irlap_driver_rcv (skb, netdev, ptype) - * - * Called when a frame is received. Dispatches the right receive function - * for processing of the frame. - * - * Note on skb management : - * After calling the higher layers of the IrDA stack, we always - * kfree() the skb, which drop the reference count (and potentially - * destroy it). - * If a higher layer of the stack want to keep the skb around (to put - * in a queue or pass it to the higher layer), it will need to use - * skb_get() to keep a reference on it. This is usually done at the - * LMP level in irlmp.c. - * Jean II - */ -int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *ptype, struct net_device *orig_dev) -{ - struct irlap_info info; - struct irlap_cb *self; - int command; - __u8 control; - int ret = -1; - - if (!net_eq(dev_net(dev), &init_net)) - goto out; - - /* FIXME: should we get our own field? */ - self = (struct irlap_cb *) dev->atalk_ptr; - - /* If the net device is down, then IrLAP is gone! */ - if (!self || self->magic != LAP_MAGIC) - goto err; - - /* We are no longer an "old" protocol, so we need to handle - * share and non linear skbs. This should never happen, so - * we don't need to be clever about it. Jean II */ - if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { - net_err_ratelimited("%s: can't clone shared skb!\n", __func__); - goto err; - } - - /* Check if frame is large enough for parsing */ - if (!pskb_may_pull(skb, 2)) { - net_err_ratelimited("%s: frame too short!\n", __func__); - goto err; - } - - command = skb->data[0] & CMD_FRAME; - info.caddr = skb->data[0] & CBROADCAST; - - info.pf = skb->data[1] & PF_BIT; - info.control = skb->data[1] & ~PF_BIT; /* Mask away poll/final bit */ - - control = info.control; - - /* First we check if this frame has a valid connection address */ - if ((info.caddr != self->caddr) && (info.caddr != CBROADCAST)) { - pr_debug("%s(), wrong connection address!\n", - __func__); - goto out; - } - /* - * Optimize for the common case and check if the frame is an - * I(nformation) frame. Only I-frames have bit 0 set to 0 - */ - if (~control & 0x01) { - irlap_recv_i_frame(self, skb, &info, command); - goto out; - } - /* - * We now check is the frame is an S(upervisory) frame. Only - * S-frames have bit 0 set to 1 and bit 1 set to 0 - */ - if (~control & 0x02) { - /* - * Received S(upervisory) frame, check which frame type it is - * only the first nibble is of interest - */ - switch (control & 0x0f) { - case RR: - irlap_recv_rr_frame(self, skb, &info, command); - break; - case RNR: - irlap_recv_rnr_frame(self, skb, &info, command); - break; - case REJ: - irlap_recv_rej_frame(self, skb, &info, command); - break; - case SREJ: - irlap_recv_srej_frame(self, skb, &info, command); - break; - default: - net_warn_ratelimited("%s: Unknown S-frame %02x received!\n", - __func__, info.control); - break; - } - goto out; - } - /* - * This must be a C(ontrol) frame - */ - switch (control) { - case XID_RSP: - irlap_recv_discovery_xid_rsp(self, skb, &info); - break; - case XID_CMD: - irlap_recv_discovery_xid_cmd(self, skb, &info); - break; - case SNRM_CMD: - irlap_recv_snrm_cmd(self, skb, &info); - break; - case DM_RSP: - irlap_do_event(self, RECV_DM_RSP, skb, &info); - break; - case DISC_CMD: /* And RD_RSP since they have the same value */ - irlap_recv_disc_frame(self, skb, &info, command); - break; - case TEST_CMD: - irlap_recv_test_frame(self, skb, &info, command); - break; - case UA_RSP: - irlap_recv_ua_frame(self, skb, &info); - break; - case FRMR_RSP: - irlap_recv_frmr_frame(self, skb, &info); - break; - case UI_FRAME: - irlap_recv_ui_frame(self, skb, &info); - break; - default: - net_warn_ratelimited("%s: Unknown frame %02x received!\n", - __func__, info.control); - break; - } -out: - ret = 0; -err: - /* Always drop our reference on the skb */ - dev_kfree_skb(skb); - return ret; -} diff --git a/drivers/staging/irda/net/irlmp.c b/drivers/staging/irda/net/irlmp.c deleted file mode 100644 index 7af618fb66c0..000000000000 --- a/drivers/staging/irda/net/irlmp.c +++ /dev/null @@ -1,1996 +0,0 @@ -/********************************************************************* - * - * Filename: irlmp.c - * Version: 1.0 - * Description: IrDA Link Management Protocol (LMP) layer - * Status: Stable. - * Author: Dag Brattli - * Created at: Sun Aug 17 20:54:32 1997 - * Modified at: Wed Jan 5 11:26:03 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -static __u8 irlmp_find_free_slsap(void); -static int irlmp_slsap_inuse(__u8 slsap_sel); - -/* Master structure */ -struct irlmp_cb *irlmp = NULL; - -/* These can be altered by the sysctl interface */ -int sysctl_discovery = 0; -int sysctl_discovery_timeout = 3; /* 3 seconds by default */ -int sysctl_discovery_slots = 6; /* 6 slots by default */ -int sysctl_lap_keepalive_time = LM_IDLE_TIMEOUT * 1000 / HZ; -char sysctl_devname[65]; - -static const char *irlmp_reasons[] = { - "ERROR, NOT USED", - "LM_USER_REQUEST", - "LM_LAP_DISCONNECT", - "LM_CONNECT_FAILURE", - "LM_LAP_RESET", - "LM_INIT_DISCONNECT", - "ERROR, NOT USED", - "UNKNOWN", -}; - -const char *irlmp_reason_str(LM_REASON reason) -{ - reason = min_t(size_t, reason, ARRAY_SIZE(irlmp_reasons) - 1); - return irlmp_reasons[reason]; -} - -/* - * Function irlmp_init (void) - * - * Create (allocate) the main IrLMP structure - * - */ -int __init irlmp_init(void) -{ - /* Initialize the irlmp structure. */ - irlmp = kzalloc( sizeof(struct irlmp_cb), GFP_KERNEL); - if (irlmp == NULL) - return -ENOMEM; - - irlmp->magic = LMP_MAGIC; - - irlmp->clients = hashbin_new(HB_LOCK); - irlmp->services = hashbin_new(HB_LOCK); - irlmp->links = hashbin_new(HB_LOCK); - irlmp->unconnected_lsaps = hashbin_new(HB_LOCK); - irlmp->cachelog = hashbin_new(HB_NOLOCK); - - if ((irlmp->clients == NULL) || - (irlmp->services == NULL) || - (irlmp->links == NULL) || - (irlmp->unconnected_lsaps == NULL) || - (irlmp->cachelog == NULL)) { - return -ENOMEM; - } - - spin_lock_init(&irlmp->cachelog->hb_spinlock); - - irlmp->last_lsap_sel = 0x0f; /* Reserved 0x00-0x0f */ - strcpy(sysctl_devname, "Linux"); - - timer_setup(&irlmp->discovery_timer, NULL, 0); - - /* Do discovery every 3 seconds, conditionally */ - if (sysctl_discovery) - irlmp_start_discovery_timer(irlmp, - sysctl_discovery_timeout*HZ); - - return 0; -} - -/* - * Function irlmp_cleanup (void) - * - * Remove IrLMP layer - * - */ -void irlmp_cleanup(void) -{ - /* Check for main structure */ - IRDA_ASSERT(irlmp != NULL, return;); - IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;); - - del_timer(&irlmp->discovery_timer); - - hashbin_delete(irlmp->links, (FREE_FUNC) kfree); - hashbin_delete(irlmp->unconnected_lsaps, (FREE_FUNC) kfree); - hashbin_delete(irlmp->clients, (FREE_FUNC) kfree); - hashbin_delete(irlmp->services, (FREE_FUNC) kfree); - hashbin_delete(irlmp->cachelog, (FREE_FUNC) kfree); - - /* De-allocate main structure */ - kfree(irlmp); - irlmp = NULL; -} - -/* - * Function irlmp_open_lsap (slsap, notify) - * - * Register with IrLMP and create a local LSAP, - * returns handle to LSAP. - */ -struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, notify_t *notify, __u8 pid) -{ - struct lsap_cb *self; - - IRDA_ASSERT(notify != NULL, return NULL;); - IRDA_ASSERT(irlmp != NULL, return NULL;); - IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return NULL;); - IRDA_ASSERT(notify->instance != NULL, return NULL;); - - /* Does the client care which Source LSAP selector it gets? */ - if (slsap_sel == LSAP_ANY) { - slsap_sel = irlmp_find_free_slsap(); - if (!slsap_sel) - return NULL; - } else if (irlmp_slsap_inuse(slsap_sel)) - return NULL; - - /* Allocate new instance of a LSAP connection */ - self = kzalloc(sizeof(struct lsap_cb), GFP_ATOMIC); - if (self == NULL) - return NULL; - - self->magic = LMP_LSAP_MAGIC; - self->slsap_sel = slsap_sel; - - /* Fix connectionless LSAP's */ - if (slsap_sel == LSAP_CONNLESS) { -#ifdef CONFIG_IRDA_ULTRA - self->dlsap_sel = LSAP_CONNLESS; - self->pid = pid; -#endif /* CONFIG_IRDA_ULTRA */ - } else - self->dlsap_sel = LSAP_ANY; - /* self->connected = FALSE; -> already NULL via memset() */ - - timer_setup(&self->watchdog_timer, NULL, 0); - - self->notify = *notify; - - self->lsap_state = LSAP_DISCONNECTED; - - /* Insert into queue of unconnected LSAPs */ - hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, - (long) self, NULL); - - return self; -} -EXPORT_SYMBOL(irlmp_open_lsap); - -/* - * Function __irlmp_close_lsap (self) - * - * Remove an instance of LSAP - */ -static void __irlmp_close_lsap(struct lsap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - - /* - * Set some of the variables to preset values - */ - self->magic = 0; - del_timer(&self->watchdog_timer); /* Important! */ - - if (self->conn_skb) - dev_kfree_skb(self->conn_skb); - - kfree(self); -} - -/* - * Function irlmp_close_lsap (self) - * - * Close and remove LSAP - * - */ -void irlmp_close_lsap(struct lsap_cb *self) -{ - struct lap_cb *lap; - struct lsap_cb *lsap = NULL; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - - /* - * Find out if we should remove this LSAP from a link or from the - * list of unconnected lsaps (not associated with a link) - */ - lap = self->lap; - if (lap) { - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); - /* We might close a LSAP before it has completed the - * connection setup. In those case, higher layers won't - * send a proper disconnect request. Harmless, except - * that we will forget to close LAP... - Jean II */ - if(self->lsap_state != LSAP_DISCONNECTED) { - self->lsap_state = LSAP_DISCONNECTED; - irlmp_do_lap_event(self->lap, - LM_LAP_DISCONNECT_REQUEST, NULL); - } - /* Now, remove from the link */ - lsap = hashbin_remove(lap->lsaps, (long) self, NULL); -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - lap->cache.valid = FALSE; -#endif - } - self->lap = NULL; - /* Check if we found the LSAP! If not then try the unconnected lsaps */ - if (!lsap) { - lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, - NULL); - } - if (!lsap) { - pr_debug("%s(), Looks like somebody has removed me already!\n", - __func__); - return; - } - __irlmp_close_lsap(self); -} -EXPORT_SYMBOL(irlmp_close_lsap); - -/* - * Function irlmp_register_irlap (saddr, notify) - * - * Register IrLAP layer with IrLMP. There is possible to have multiple - * instances of the IrLAP layer, each connected to different IrDA ports - * - */ -void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify) -{ - struct lap_cb *lap; - - IRDA_ASSERT(irlmp != NULL, return;); - IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;); - IRDA_ASSERT(notify != NULL, return;); - - /* - * Allocate new instance of a LSAP connection - */ - lap = kzalloc(sizeof(struct lap_cb), GFP_KERNEL); - if (lap == NULL) - return; - - lap->irlap = irlap; - lap->magic = LMP_LAP_MAGIC; - lap->saddr = saddr; - lap->daddr = DEV_ADDR_ANY; -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - lap->cache.valid = FALSE; -#endif - lap->lsaps = hashbin_new(HB_LOCK); - if (lap->lsaps == NULL) { - net_warn_ratelimited("%s(), unable to kmalloc lsaps\n", - __func__); - kfree(lap); - return; - } - - lap->lap_state = LAP_STANDBY; - - timer_setup(&lap->idle_timer, NULL, 0); - - /* - * Insert into queue of LMP links - */ - hashbin_insert(irlmp->links, (irda_queue_t *) lap, lap->saddr, NULL); - - /* - * We set only this variable so IrLAP can tell us on which link the - * different events happened on - */ - irda_notify_init(notify); - notify->instance = lap; -} - -/* - * Function irlmp_unregister_irlap (saddr) - * - * IrLAP layer has been removed! - * - */ -void irlmp_unregister_link(__u32 saddr) -{ - struct lap_cb *link; - - /* We must remove ourselves from the hashbin *first*. This ensure - * that no more LSAPs will be open on this link and no discovery - * will be triggered anymore. Jean II */ - link = hashbin_remove(irlmp->links, saddr, NULL); - if (link) { - IRDA_ASSERT(link->magic == LMP_LAP_MAGIC, return;); - - /* Kill all the LSAPs on this link. Jean II */ - link->reason = LAP_DISC_INDICATION; - link->daddr = DEV_ADDR_ANY; - irlmp_do_lap_event(link, LM_LAP_DISCONNECT_INDICATION, NULL); - - /* Remove all discoveries discovered at this link */ - irlmp_expire_discoveries(irlmp->cachelog, link->saddr, TRUE); - - /* Final cleanup */ - del_timer(&link->idle_timer); - link->magic = 0; - hashbin_delete(link->lsaps, (FREE_FUNC) __irlmp_close_lsap); - kfree(link); - } -} - -/* - * Function irlmp_connect_request (handle, dlsap, userdata) - * - * Connect with a peer LSAP - * - */ -int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, - __u32 saddr, __u32 daddr, - struct qos_info *qos, struct sk_buff *userdata) -{ - struct sk_buff *tx_skb = userdata; - struct lap_cb *lap; - struct lsap_cb *lsap; - int ret; - - IRDA_ASSERT(self != NULL, return -EBADR;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;); - - pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n", - __func__, self->slsap_sel, dlsap_sel, saddr, daddr); - - if (test_bit(0, &self->connected)) { - ret = -EISCONN; - goto err; - } - - /* Client must supply destination device address */ - if (!daddr) { - ret = -EINVAL; - goto err; - } - - /* Any userdata? */ - if (tx_skb == NULL) { - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - skb_reserve(tx_skb, LMP_MAX_HEADER); - } - - /* Make room for MUX control header (3 bytes) */ - IRDA_ASSERT(skb_headroom(tx_skb) >= LMP_CONTROL_HEADER, return -1;); - skb_push(tx_skb, LMP_CONTROL_HEADER); - - self->dlsap_sel = dlsap_sel; - - /* - * Find the link to where we should try to connect since there may - * be more than one IrDA port on this machine. If the client has - * passed us the saddr (and already knows which link to use), then - * we use that to find the link, if not then we have to look in the - * discovery log and check if any of the links has discovered a - * device with the given daddr - */ - if ((!saddr) || (saddr == DEV_ADDR_ANY)) { - discovery_t *discovery; - unsigned long flags; - - spin_lock_irqsave(&irlmp->cachelog->hb_spinlock, flags); - if (daddr != DEV_ADDR_ANY) - discovery = hashbin_find(irlmp->cachelog, daddr, NULL); - else { - pr_debug("%s(), no daddr\n", __func__); - discovery = (discovery_t *) - hashbin_get_first(irlmp->cachelog); - } - - if (discovery) { - saddr = discovery->data.saddr; - daddr = discovery->data.daddr; - } - spin_unlock_irqrestore(&irlmp->cachelog->hb_spinlock, flags); - } - lap = hashbin_lock_find(irlmp->links, saddr, NULL); - if (lap == NULL) { - pr_debug("%s(), Unable to find a usable link!\n", __func__); - ret = -EHOSTUNREACH; - goto err; - } - - /* Check if LAP is disconnected or already connected */ - if (lap->daddr == DEV_ADDR_ANY) - lap->daddr = daddr; - else if (lap->daddr != daddr) { - /* Check if some LSAPs are active on this LAP */ - if (HASHBIN_GET_SIZE(lap->lsaps) == 0) { - /* No active connection, but LAP hasn't been - * disconnected yet (waiting for timeout in LAP). - * Maybe we could give LAP a bit of help in this case. - */ - pr_debug("%s(), sorry, but I'm waiting for LAP to timeout!\n", - __func__); - ret = -EAGAIN; - goto err; - } - - /* LAP is already connected to a different node, and LAP - * can only talk to one node at a time */ - pr_debug("%s(), sorry, but link is busy!\n", __func__); - ret = -EBUSY; - goto err; - } - - self->lap = lap; - - /* - * Remove LSAP from list of unconnected LSAPs and insert it into the - * list of connected LSAPs for the particular link - */ - lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, NULL); - - IRDA_ASSERT(lsap != NULL, return -1;); - IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_ASSERT(lsap->lap != NULL, return -1;); - IRDA_ASSERT(lsap->lap->magic == LMP_LAP_MAGIC, return -1;); - - hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (long) self, - NULL); - - set_bit(0, &self->connected); /* TRUE */ - - /* - * User supplied qos specifications? - */ - if (qos) - self->qos = *qos; - - irlmp_do_lsap_event(self, LM_CONNECT_REQUEST, tx_skb); - - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(tx_skb); - - return 0; - -err: - /* Cleanup */ - if(tx_skb) - dev_kfree_skb(tx_skb); - return ret; -} -EXPORT_SYMBOL(irlmp_connect_request); - -/* - * Function irlmp_connect_indication (self) - * - * Incoming connection - * - */ -void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb) -{ - int max_seg_size; - int lap_header_size; - int max_header_size; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(self->lap != NULL, return;); - - pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", - __func__, self->slsap_sel, self->dlsap_sel); - - /* Note : self->lap is set in irlmp_link_data_indication(), - * (case CONNECT_CMD:) because we have no way to set it here. - * Similarly, self->dlsap_sel is usually set in irlmp_find_lsap(). - * Jean II */ - - self->qos = *self->lap->qos; - - max_seg_size = self->lap->qos->data_size.value-LMP_HEADER; - lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap); - max_header_size = LMP_HEADER + lap_header_size; - - /* Hide LMP_CONTROL_HEADER header from layer above */ - skb_pull(skb, LMP_CONTROL_HEADER); - - if (self->notify.connect_indication) { - /* Don't forget to refcount it - see irlap_driver_rcv(). */ - skb_get(skb); - self->notify.connect_indication(self->notify.instance, self, - &self->qos, max_seg_size, - max_header_size, skb); - } -} - -/* - * Function irlmp_connect_response (handle, userdata) - * - * Service user is accepting connection - * - */ -int irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_ASSERT(userdata != NULL, return -1;); - - /* We set the connected bit and move the lsap to the connected list - * in the state machine itself. Jean II */ - - pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", - __func__, self->slsap_sel, self->dlsap_sel); - - /* Make room for MUX control header (3 bytes) */ - IRDA_ASSERT(skb_headroom(userdata) >= LMP_CONTROL_HEADER, return -1;); - skb_push(userdata, LMP_CONTROL_HEADER); - - irlmp_do_lsap_event(self, LM_CONNECT_RESPONSE, userdata); - - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(userdata); - - return 0; -} -EXPORT_SYMBOL(irlmp_connect_response); - -/* - * Function irlmp_connect_confirm (handle, skb) - * - * LSAP connection confirmed peer device! - */ -void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb) -{ - int max_header_size; - int lap_header_size; - int max_seg_size; - - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - IRDA_ASSERT(self->lap != NULL, return;); - - self->qos = *self->lap->qos; - - max_seg_size = self->lap->qos->data_size.value-LMP_HEADER; - lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap); - max_header_size = LMP_HEADER + lap_header_size; - - pr_debug("%s(), max_header_size=%d\n", - __func__, max_header_size); - - /* Hide LMP_CONTROL_HEADER header from layer above */ - skb_pull(skb, LMP_CONTROL_HEADER); - - if (self->notify.connect_confirm) { - /* Don't forget to refcount it - see irlap_driver_rcv() */ - skb_get(skb); - self->notify.connect_confirm(self->notify.instance, self, - &self->qos, max_seg_size, - max_header_size, skb); - } -} - -/* - * Function irlmp_dup (orig, instance) - * - * Duplicate LSAP, can be used by servers to confirm a connection on a - * new LSAP so it can keep listening on the old one. - * - */ -struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance) -{ - struct lsap_cb *new; - unsigned long flags; - - spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); - - /* Only allowed to duplicate unconnected LSAP's, and only LSAPs - * that have received a connect indication. Jean II */ - if ((!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) || - (orig->lap == NULL)) { - pr_debug("%s(), invalid LSAP (wrong state)\n", - __func__); - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, - flags); - return NULL; - } - - /* Allocate a new instance */ - new = kmemdup(orig, sizeof(*new), GFP_ATOMIC); - if (!new) { - pr_debug("%s(), unable to kmalloc\n", __func__); - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, - flags); - return NULL; - } - /* new->lap = orig->lap; => done in the memcpy() */ - /* new->slsap_sel = orig->slsap_sel; => done in the memcpy() */ - new->conn_skb = NULL; - - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); - - /* Not everything is the same */ - new->notify.instance = instance; - - timer_setup(&new->watchdog_timer, NULL, 0); - - hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new, - (long) new, NULL); - -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - /* Make sure that we invalidate the LSAP cache */ - new->lap->cache.valid = FALSE; -#endif /* CONFIG_IRDA_CACHE_LAST_LSAP */ - - return new; -} - -/* - * Function irlmp_disconnect_request (handle, userdata) - * - * The service user is requesting disconnection, this will not remove the - * LSAP, but only mark it as disconnected - */ -int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata) -{ - struct lsap_cb *lsap; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_ASSERT(userdata != NULL, return -1;); - - /* Already disconnected ? - * There is a race condition between irlmp_disconnect_indication() - * and us that might mess up the hashbins below. This fixes it. - * Jean II */ - if (! test_and_clear_bit(0, &self->connected)) { - pr_debug("%s(), already disconnected!\n", __func__); - dev_kfree_skb(userdata); - return -1; - } - - skb_push(userdata, LMP_CONTROL_HEADER); - - /* - * Do the event before the other stuff since we must know - * which lap layer that the frame should be transmitted on - */ - irlmp_do_lsap_event(self, LM_DISCONNECT_REQUEST, userdata); - - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(userdata); - - /* - * Remove LSAP from list of connected LSAPs for the particular link - * and insert it into the list of unconnected LSAPs - */ - IRDA_ASSERT(self->lap != NULL, return -1;); - IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); - IRDA_ASSERT(self->lap->lsaps != NULL, return -1;); - - lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL); -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - self->lap->cache.valid = FALSE; -#endif - - IRDA_ASSERT(lsap != NULL, return -1;); - IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_ASSERT(lsap == self, return -1;); - - hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, - (long) self, NULL); - - /* Reset some values */ - self->dlsap_sel = LSAP_ANY; - self->lap = NULL; - - return 0; -} -EXPORT_SYMBOL(irlmp_disconnect_request); - -/* - * Function irlmp_disconnect_indication (reason, userdata) - * - * LSAP is being closed! - */ -void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason, - struct sk_buff *skb) -{ - struct lsap_cb *lsap; - - pr_debug("%s(), reason=%s [%d]\n", __func__, - irlmp_reason_str(reason), reason); - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - - pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", - __func__, self->slsap_sel, self->dlsap_sel); - - /* Already disconnected ? - * There is a race condition between irlmp_disconnect_request() - * and us that might mess up the hashbins below. This fixes it. - * Jean II */ - if (! test_and_clear_bit(0, &self->connected)) { - pr_debug("%s(), already disconnected!\n", __func__); - return; - } - - /* - * Remove association between this LSAP and the link it used - */ - IRDA_ASSERT(self->lap != NULL, return;); - IRDA_ASSERT(self->lap->lsaps != NULL, return;); - - lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL); -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - self->lap->cache.valid = FALSE; -#endif - - IRDA_ASSERT(lsap != NULL, return;); - IRDA_ASSERT(lsap == self, return;); - hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) lsap, - (long) lsap, NULL); - - self->dlsap_sel = LSAP_ANY; - self->lap = NULL; - - /* - * Inform service user - */ - if (self->notify.disconnect_indication) { - /* Don't forget to refcount it - see irlap_driver_rcv(). */ - if(skb) - skb_get(skb); - self->notify.disconnect_indication(self->notify.instance, - self, reason, skb); - } else { - pr_debug("%s(), no handler\n", __func__); - } -} - -/* - * Function irlmp_do_expiry (void) - * - * Do a cleanup of the discovery log (remove old entries) - * - * Note : separate from irlmp_do_discovery() so that we can handle - * passive discovery properly. - */ -void irlmp_do_expiry(void) -{ - struct lap_cb *lap; - - /* - * Expire discovery on all links which are *not* connected. - * On links which are connected, we can't do discovery - * anymore and can't refresh the log, so we freeze the - * discovery log to keep info about the device we are - * connected to. - * This info is mandatory if we want irlmp_connect_request() - * to work properly. - Jean II - */ - lap = (struct lap_cb *) hashbin_get_first(irlmp->links); - while (lap != NULL) { - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); - - if (lap->lap_state == LAP_STANDBY) { - /* Expire discoveries discovered on this link */ - irlmp_expire_discoveries(irlmp->cachelog, lap->saddr, - FALSE); - } - lap = (struct lap_cb *) hashbin_get_next(irlmp->links); - } -} - -/* - * Function irlmp_do_discovery (nslots) - * - * Do some discovery on all links - * - * Note : log expiry is done above. - */ -void irlmp_do_discovery(int nslots) -{ - struct lap_cb *lap; - __u16 *data_hintsp; - - /* Make sure the value is sane */ - if ((nslots != 1) && (nslots != 6) && (nslots != 8) && (nslots != 16)){ - net_warn_ratelimited("%s: invalid value for number of slots!\n", - __func__); - nslots = sysctl_discovery_slots = 8; - } - - /* Construct new discovery info to be used by IrLAP, */ - data_hintsp = (__u16 *) irlmp->discovery_cmd.data.hints; - put_unaligned(irlmp->hints.word, data_hintsp); - - /* - * Set character set for device name (we use ASCII), and - * copy device name. Remember to make room for a \0 at the - * end - */ - irlmp->discovery_cmd.data.charset = CS_ASCII; - strncpy(irlmp->discovery_cmd.data.info, sysctl_devname, - NICKNAME_MAX_LEN); - irlmp->discovery_cmd.name_len = strlen(irlmp->discovery_cmd.data.info); - irlmp->discovery_cmd.nslots = nslots; - - /* - * Try to send discovery packets on all links - */ - lap = (struct lap_cb *) hashbin_get_first(irlmp->links); - while (lap != NULL) { - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); - - if (lap->lap_state == LAP_STANDBY) { - /* Try to discover */ - irlmp_do_lap_event(lap, LM_LAP_DISCOVERY_REQUEST, - NULL); - } - lap = (struct lap_cb *) hashbin_get_next(irlmp->links); - } -} - -/* - * Function irlmp_discovery_request (nslots) - * - * Do a discovery of devices in front of the computer - * - * If the caller has registered a client discovery callback, this - * allow him to receive the full content of the discovery log through - * this callback (as normally he will receive only new discoveries). - */ -void irlmp_discovery_request(int nslots) -{ - /* Return current cached discovery log (in full) */ - irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_LOG); - - /* - * Start a single discovery operation if discovery is not already - * running - */ - if (!sysctl_discovery) { - /* Check if user wants to override the default */ - if (nslots == DISCOVERY_DEFAULT_SLOTS) - nslots = sysctl_discovery_slots; - - irlmp_do_discovery(nslots); - /* Note : we never do expiry here. Expiry will run on the - * discovery timer regardless of the state of sysctl_discovery - * Jean II */ - } -} -EXPORT_SYMBOL(irlmp_discovery_request); - -/* - * Function irlmp_get_discoveries (pn, mask, slots) - * - * Return the current discovery log - * - * If discovery is not enabled, you should call this function again - * after 1 or 2 seconds (i.e. after discovery has been done). - */ -struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots) -{ - /* If discovery is not enabled, it's likely that the discovery log - * will be empty. So, we trigger a single discovery, so that next - * time the user call us there might be some results in the log. - * Jean II - */ - if (!sysctl_discovery) { - /* Check if user wants to override the default */ - if (nslots == DISCOVERY_DEFAULT_SLOTS) - nslots = sysctl_discovery_slots; - - /* Start discovery - will complete sometime later */ - irlmp_do_discovery(nslots); - /* Note : we never do expiry here. Expiry will run on the - * discovery timer regardless of the state of sysctl_discovery - * Jean II */ - } - - /* Return current cached discovery log */ - return irlmp_copy_discoveries(irlmp->cachelog, pn, mask, TRUE); -} -EXPORT_SYMBOL(irlmp_get_discoveries); - -/* - * Function irlmp_notify_client (log) - * - * Notify all about discovered devices - * - * Clients registered with IrLMP are : - * o IrComm - * o IrLAN - * o Any socket (in any state - ouch, that may be a lot !) - * The client may have defined a callback to be notified in case of - * partial/selective discovery based on the hints that it passed to IrLMP. - */ -static inline void -irlmp_notify_client(irlmp_client_t *client, - hashbin_t *log, DISCOVERY_MODE mode) -{ - discinfo_t *discoveries; /* Copy of the discovery log */ - int number; /* Number of nodes in the log */ - int i; - - /* Check if client wants or not partial/selective log (optimisation) */ - if (!client->disco_callback) - return; - - /* - * Locking notes : - * the old code was manipulating the log directly, which was - * very racy. Now, we use copy_discoveries, that protects - * itself while dumping the log for us. - * The overhead of the copy is compensated by the fact that - * we only pass new discoveries in normal mode and don't - * pass the same old entry every 3s to the caller as we used - * to do (virtual function calling is expensive). - * Jean II - */ - - /* - * Now, check all discovered devices (if any), and notify client - * only about the services that the client is interested in - * We also notify only about the new devices unless the caller - * explicitly request a dump of the log. Jean II - */ - discoveries = irlmp_copy_discoveries(log, &number, - client->hint_mask.word, - (mode == DISCOVERY_LOG)); - /* Check if the we got some results */ - if (discoveries == NULL) - return; /* No nodes discovered */ - - /* Pass all entries to the listener */ - for(i = 0; i < number; i++) - client->disco_callback(&(discoveries[i]), mode, client->priv); - - /* Free up our buffer */ - kfree(discoveries); -} - -/* - * Function irlmp_discovery_confirm ( self, log) - * - * Some device(s) answered to our discovery request! Check to see which - * device it is, and give indication to the client(s) - * - */ -void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode) -{ - irlmp_client_t *client; - irlmp_client_t *client_next; - - IRDA_ASSERT(log != NULL, return;); - - if (!(HASHBIN_GET_SIZE(log))) - return; - - /* For each client - notify callback may touch client list */ - client = (irlmp_client_t *) hashbin_get_first(irlmp->clients); - while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL, - (void *) &client_next) ) { - /* Check if we should notify client */ - irlmp_notify_client(client, log, mode); - - client = client_next; - } -} - -/* - * Function irlmp_discovery_expiry (expiry) - * - * This device is no longer been discovered, and therefore it is being - * purged from the discovery log. Inform all clients who have - * registered for this event... - * - * Note : called exclusively from discovery.c - * Note : this is no longer called under discovery spinlock, so the - * client can do whatever he wants in the callback. - */ -void irlmp_discovery_expiry(discinfo_t *expiries, int number) -{ - irlmp_client_t *client; - irlmp_client_t *client_next; - int i; - - IRDA_ASSERT(expiries != NULL, return;); - - /* For each client - notify callback may touch client list */ - client = (irlmp_client_t *) hashbin_get_first(irlmp->clients); - while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL, - (void *) &client_next) ) { - - /* Pass all entries to the listener */ - for(i = 0; i < number; i++) { - /* Check if we should notify client */ - if ((client->expir_callback) && - (client->hint_mask.word & - get_unaligned((__u16 *)expiries[i].hints) - & 0x7f7f) ) - client->expir_callback(&(expiries[i]), - EXPIRY_TIMEOUT, - client->priv); - } - - /* Next client */ - client = client_next; - } -} - -/* - * Function irlmp_get_discovery_response () - * - * Used by IrLAP to get the discovery info it needs when answering - * discovery requests by other devices. - */ -discovery_t *irlmp_get_discovery_response(void) -{ - IRDA_ASSERT(irlmp != NULL, return NULL;); - - put_unaligned(irlmp->hints.word, (__u16 *)irlmp->discovery_rsp.data.hints); - - /* - * Set character set for device name (we use ASCII), and - * copy device name. Remember to make room for a \0 at the - * end - */ - irlmp->discovery_rsp.data.charset = CS_ASCII; - - strncpy(irlmp->discovery_rsp.data.info, sysctl_devname, - NICKNAME_MAX_LEN); - irlmp->discovery_rsp.name_len = strlen(irlmp->discovery_rsp.data.info); - - return &irlmp->discovery_rsp; -} - -/* - * Function irlmp_data_request (self, skb) - * - * Send some data to peer device - * - * Note on skb management : - * After calling the lower layers of the IrDA stack, we always - * kfree() the skb, which drop the reference count (and potentially - * destroy it). - * IrLMP and IrLAP may queue the packet, and in those cases will need - * to use skb_get() to keep it around. - * Jean II - */ -int irlmp_data_request(struct lsap_cb *self, struct sk_buff *userdata) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - /* Make room for MUX header */ - IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;); - skb_push(userdata, LMP_HEADER); - - ret = irlmp_do_lsap_event(self, LM_DATA_REQUEST, userdata); - - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(userdata); - - return ret; -} -EXPORT_SYMBOL(irlmp_data_request); - -/* - * Function irlmp_data_indication (handle, skb) - * - * Got data from LAP layer so pass it up to upper layer - * - */ -void irlmp_data_indication(struct lsap_cb *self, struct sk_buff *skb) -{ - /* Hide LMP header from layer above */ - skb_pull(skb, LMP_HEADER); - - if (self->notify.data_indication) { - /* Don't forget to refcount it - see irlap_driver_rcv(). */ - skb_get(skb); - self->notify.data_indication(self->notify.instance, self, skb); - } -} - -/* - * Function irlmp_udata_request (self, skb) - */ -int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *userdata) -{ - int ret; - - IRDA_ASSERT(userdata != NULL, return -1;); - - /* Make room for MUX header */ - IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;); - skb_push(userdata, LMP_HEADER); - - ret = irlmp_do_lsap_event(self, LM_UDATA_REQUEST, userdata); - - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(userdata); - - return ret; -} - -/* - * Function irlmp_udata_indication (self, skb) - * - * Send unreliable data (but still within the connection) - * - */ -void irlmp_udata_indication(struct lsap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Hide LMP header from layer above */ - skb_pull(skb, LMP_HEADER); - - if (self->notify.udata_indication) { - /* Don't forget to refcount it - see irlap_driver_rcv(). */ - skb_get(skb); - self->notify.udata_indication(self->notify.instance, self, - skb); - } -} - -/* - * Function irlmp_connless_data_request (self, skb) - */ -#ifdef CONFIG_IRDA_ULTRA -int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata, - __u8 pid) -{ - struct sk_buff *clone_skb; - struct lap_cb *lap; - - IRDA_ASSERT(userdata != NULL, return -1;); - - /* Make room for MUX and PID header */ - IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER+LMP_PID_HEADER, - return -1;); - - /* Insert protocol identifier */ - skb_push(userdata, LMP_PID_HEADER); - if(self != NULL) - userdata->data[0] = self->pid; - else - userdata->data[0] = pid; - - /* Connectionless sockets must use 0x70 */ - skb_push(userdata, LMP_HEADER); - userdata->data[0] = userdata->data[1] = LSAP_CONNLESS; - - /* Try to send Connectionless packets out on all links */ - lap = (struct lap_cb *) hashbin_get_first(irlmp->links); - while (lap != NULL) { - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return -1;); - - clone_skb = skb_clone(userdata, GFP_ATOMIC); - if (!clone_skb) { - dev_kfree_skb(userdata); - return -ENOMEM; - } - - irlap_unitdata_request(lap->irlap, clone_skb); - /* irlap_unitdata_request() don't increase refcount, - * so no dev_kfree_skb() - Jean II */ - - lap = (struct lap_cb *) hashbin_get_next(irlmp->links); - } - dev_kfree_skb(userdata); - - return 0; -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irlmp_connless_data_indication (self, skb) - * - * Receive unreliable data outside any connection. Mostly used by Ultra - * - */ -#ifdef CONFIG_IRDA_ULTRA -void irlmp_connless_data_indication(struct lsap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Hide LMP and PID header from layer above */ - skb_pull(skb, LMP_HEADER+LMP_PID_HEADER); - - if (self->notify.udata_indication) { - /* Don't forget to refcount it - see irlap_driver_rcv(). */ - skb_get(skb); - self->notify.udata_indication(self->notify.instance, self, - skb); - } -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Propagate status indication from LAP to LSAPs (via LMP) - * This don't trigger any change of state in lap_cb, lmp_cb or lsap_cb, - * and the event is stateless, therefore we can bypass both state machines - * and send the event direct to the LSAP user. - * Jean II - */ -void irlmp_status_indication(struct lap_cb *self, - LINK_STATUS link, LOCK_STATUS lock) -{ - struct lsap_cb *next; - struct lsap_cb *curr; - - /* Send status_indication to all LSAPs using this link */ - curr = (struct lsap_cb *) hashbin_get_first( self->lsaps); - while (NULL != hashbin_find_next(self->lsaps, (long) curr, NULL, - (void *) &next) ) { - IRDA_ASSERT(curr->magic == LMP_LSAP_MAGIC, return;); - /* - * Inform service user if he has requested it - */ - if (curr->notify.status_indication != NULL) - curr->notify.status_indication(curr->notify.instance, - link, lock); - else - pr_debug("%s(), no handler\n", __func__); - - curr = next; - } -} - -/* - * Receive flow control indication from LAP. - * LAP want us to send it one more frame. We implement a simple round - * robin scheduler between the active sockets so that we get a bit of - * fairness. Note that the round robin is far from perfect, but it's - * better than nothing. - * We then poll the selected socket so that we can do synchronous - * refilling of IrLAP (which allow to minimise the number of buffers). - * Jean II - */ -void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow) -{ - struct lsap_cb *next; - struct lsap_cb *curr; - int lsap_todo; - - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - IRDA_ASSERT(flow == FLOW_START, return;); - - /* Get the number of lsap. That's the only safe way to know - * that we have looped around... - Jean II */ - lsap_todo = HASHBIN_GET_SIZE(self->lsaps); - pr_debug("%s() : %d lsaps to scan\n", __func__, lsap_todo); - - /* Poll lsap in order until the queue is full or until we - * tried them all. - * Most often, the current LSAP will have something to send, - * so we will go through this loop only once. - Jean II */ - while((lsap_todo--) && - (IRLAP_GET_TX_QUEUE_LEN(self->irlap) < LAP_HIGH_THRESHOLD)) { - /* Try to find the next lsap we should poll. */ - next = self->flow_next; - /* If we have no lsap, restart from first one */ - if(next == NULL) - next = (struct lsap_cb *) hashbin_get_first(self->lsaps); - /* Verify current one and find the next one */ - curr = hashbin_find_next(self->lsaps, (long) next, NULL, - (void *) &self->flow_next); - /* Uh-oh... Paranoia */ - if(curr == NULL) - break; - pr_debug("%s() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n", - __func__, curr, next, self->flow_next, lsap_todo, - IRLAP_GET_TX_QUEUE_LEN(self->irlap)); - - /* Inform lsap user that it can send one more packet. */ - if (curr->notify.flow_indication != NULL) - curr->notify.flow_indication(curr->notify.instance, - curr, flow); - else - pr_debug("%s(), no handler\n", __func__); - } -} - -#if 0 -/* - * Function irlmp_hint_to_service (hint) - * - * Returns a list of all servics contained in the given hint bits. This - * function assumes that the hint bits have the size of two bytes only - */ -__u8 *irlmp_hint_to_service(__u8 *hint) -{ - __u8 *service; - int i = 0; - - /* - * Allocate array to store services in. 16 entries should be safe - * since we currently only support 2 hint bytes - */ - service = kmalloc(16, GFP_ATOMIC); - if (!service) - return NULL; - - if (!hint[0]) { - pr_debug("\n"); - kfree(service); - return NULL; - } - if (hint[0] & HINT_PNP) - pr_debug("PnP Compatible "); - if (hint[0] & HINT_PDA) - pr_debug("PDA/Palmtop "); - if (hint[0] & HINT_COMPUTER) - pr_debug("Computer "); - if (hint[0] & HINT_PRINTER) { - pr_debug("Printer "); - service[i++] = S_PRINTER; - } - if (hint[0] & HINT_MODEM) - pr_debug("Modem "); - if (hint[0] & HINT_FAX) - pr_debug("Fax "); - if (hint[0] & HINT_LAN) { - pr_debug("LAN Access "); - service[i++] = S_LAN; - } - /* - * Test if extension byte exists. This byte will usually be - * there, but this is not really required by the standard. - * (IrLMP p. 29) - */ - if (hint[0] & HINT_EXTENSION) { - if (hint[1] & HINT_TELEPHONY) { - pr_debug("Telephony "); - service[i++] = S_TELEPHONY; - } - if (hint[1] & HINT_FILE_SERVER) - pr_debug("File Server "); - - if (hint[1] & HINT_COMM) { - pr_debug("IrCOMM "); - service[i++] = S_COMM; - } - if (hint[1] & HINT_OBEX) { - pr_debug("IrOBEX "); - service[i++] = S_OBEX; - } - } - pr_debug("\n"); - - /* So that client can be notified about any discovery */ - service[i++] = S_ANY; - - service[i] = S_END; - - return service; -} -#endif - -static const __u16 service_hint_mapping[S_END][2] = { - { HINT_PNP, 0 }, /* S_PNP */ - { HINT_PDA, 0 }, /* S_PDA */ - { HINT_COMPUTER, 0 }, /* S_COMPUTER */ - { HINT_PRINTER, 0 }, /* S_PRINTER */ - { HINT_MODEM, 0 }, /* S_MODEM */ - { HINT_FAX, 0 }, /* S_FAX */ - { HINT_LAN, 0 }, /* S_LAN */ - { HINT_EXTENSION, HINT_TELEPHONY }, /* S_TELEPHONY */ - { HINT_EXTENSION, HINT_COMM }, /* S_COMM */ - { HINT_EXTENSION, HINT_OBEX }, /* S_OBEX */ - { 0xFF, 0xFF }, /* S_ANY */ -}; - -/* - * Function irlmp_service_to_hint (service) - * - * Converts a service type, to a hint bit - * - * Returns: a 16 bit hint value, with the service bit set - */ -__u16 irlmp_service_to_hint(int service) -{ - __u16_host_order hint; - - hint.byte[0] = service_hint_mapping[service][0]; - hint.byte[1] = service_hint_mapping[service][1]; - - return hint.word; -} -EXPORT_SYMBOL(irlmp_service_to_hint); - -/* - * Function irlmp_register_service (service) - * - * Register local service with IrLMP - * - */ -void *irlmp_register_service(__u16 hints) -{ - irlmp_service_t *service; - - pr_debug("%s(), hints = %04x\n", __func__, hints); - - /* Make a new registration */ - service = kmalloc(sizeof(irlmp_service_t), GFP_ATOMIC); - if (!service) - return NULL; - - service->hints.word = hints; - hashbin_insert(irlmp->services, (irda_queue_t *) service, - (long) service, NULL); - - irlmp->hints.word |= hints; - - return (void *)service; -} -EXPORT_SYMBOL(irlmp_register_service); - -/* - * Function irlmp_unregister_service (handle) - * - * Unregister service with IrLMP. - * - * Returns: 0 on success, -1 on error - */ -int irlmp_unregister_service(void *handle) -{ - irlmp_service_t *service; - unsigned long flags; - - if (!handle) - return -1; - - /* Caller may call with invalid handle (it's legal) - Jean II */ - service = hashbin_lock_find(irlmp->services, (long) handle, NULL); - if (!service) { - pr_debug("%s(), Unknown service!\n", __func__); - return -1; - } - - hashbin_remove_this(irlmp->services, (irda_queue_t *) service); - kfree(service); - - /* Remove old hint bits */ - irlmp->hints.word = 0; - - /* Refresh current hint bits */ - spin_lock_irqsave(&irlmp->services->hb_spinlock, flags); - service = (irlmp_service_t *) hashbin_get_first(irlmp->services); - while (service) { - irlmp->hints.word |= service->hints.word; - - service = (irlmp_service_t *)hashbin_get_next(irlmp->services); - } - spin_unlock_irqrestore(&irlmp->services->hb_spinlock, flags); - return 0; -} -EXPORT_SYMBOL(irlmp_unregister_service); - -/* - * Function irlmp_register_client (hint_mask, callback1, callback2) - * - * Register a local client with IrLMP - * First callback is selective discovery (based on hints) - * Second callback is for selective discovery expiries - * - * Returns: handle > 0 on success, 0 on error - */ -void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb, - DISCOVERY_CALLBACK2 expir_clb, void *priv) -{ - irlmp_client_t *client; - - IRDA_ASSERT(irlmp != NULL, return NULL;); - - /* Make a new registration */ - client = kmalloc(sizeof(irlmp_client_t), GFP_ATOMIC); - if (!client) - return NULL; - - /* Register the details */ - client->hint_mask.word = hint_mask; - client->disco_callback = disco_clb; - client->expir_callback = expir_clb; - client->priv = priv; - - hashbin_insert(irlmp->clients, (irda_queue_t *) client, - (long) client, NULL); - - return (void *) client; -} -EXPORT_SYMBOL(irlmp_register_client); - -/* - * Function irlmp_update_client (handle, hint_mask, callback1, callback2) - * - * Updates specified client (handle) with possibly new hint_mask and - * callback - * - * Returns: 0 on success, -1 on error - */ -int irlmp_update_client(void *handle, __u16 hint_mask, - DISCOVERY_CALLBACK1 disco_clb, - DISCOVERY_CALLBACK2 expir_clb, void *priv) -{ - irlmp_client_t *client; - - if (!handle) - return -1; - - client = hashbin_lock_find(irlmp->clients, (long) handle, NULL); - if (!client) { - pr_debug("%s(), Unknown client!\n", __func__); - return -1; - } - - client->hint_mask.word = hint_mask; - client->disco_callback = disco_clb; - client->expir_callback = expir_clb; - client->priv = priv; - - return 0; -} -EXPORT_SYMBOL(irlmp_update_client); - -/* - * Function irlmp_unregister_client (handle) - * - * Returns: 0 on success, -1 on error - * - */ -int irlmp_unregister_client(void *handle) -{ - struct irlmp_client *client; - - if (!handle) - return -1; - - /* Caller may call with invalid handle (it's legal) - Jean II */ - client = hashbin_lock_find(irlmp->clients, (long) handle, NULL); - if (!client) { - pr_debug("%s(), Unknown client!\n", __func__); - return -1; - } - - pr_debug("%s(), removing client!\n", __func__); - hashbin_remove_this(irlmp->clients, (irda_queue_t *) client); - kfree(client); - - return 0; -} -EXPORT_SYMBOL(irlmp_unregister_client); - -/* - * Function irlmp_slsap_inuse (slsap) - * - * Check if the given source LSAP selector is in use - * - * This function is clearly not very efficient. On the mitigating side, the - * stack make sure that in 99% of the cases, we are called only once - * for each socket allocation. We could probably keep a bitmap - * of the allocated LSAP, but I'm not sure the complexity is worth it. - * Jean II - */ -static int irlmp_slsap_inuse(__u8 slsap_sel) -{ - struct lsap_cb *self; - struct lap_cb *lap; - unsigned long flags; - - IRDA_ASSERT(irlmp != NULL, return TRUE;); - IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;); - IRDA_ASSERT(slsap_sel != LSAP_ANY, return TRUE;); - -#ifdef CONFIG_IRDA_ULTRA - /* Accept all bindings to the connectionless LSAP */ - if (slsap_sel == LSAP_CONNLESS) - return FALSE; -#endif /* CONFIG_IRDA_ULTRA */ - - /* Valid values are between 0 and 127 (0x0-0x6F) */ - if (slsap_sel > LSAP_MAX) - return TRUE; - - /* - * Check if slsap is already in use. To do this we have to loop over - * every IrLAP connection and check every LSAP associated with each - * the connection. - */ - spin_lock_irqsave_nested(&irlmp->links->hb_spinlock, flags, - SINGLE_DEPTH_NESTING); - lap = (struct lap_cb *) hashbin_get_first(irlmp->links); - while (lap != NULL) { - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, goto errlap;); - - /* Careful for priority inversions here ! - * irlmp->links is never taken while another IrDA - * spinlock is held, so we are safe. Jean II */ - spin_lock(&lap->lsaps->hb_spinlock); - - /* For this IrLAP, check all the LSAPs */ - self = (struct lsap_cb *) hashbin_get_first(lap->lsaps); - while (self != NULL) { - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, - goto errlsap;); - - if (self->slsap_sel == slsap_sel) { - pr_debug("Source LSAP selector=%02x in use\n", - self->slsap_sel); - goto errlsap; - } - self = (struct lsap_cb*) hashbin_get_next(lap->lsaps); - } - spin_unlock(&lap->lsaps->hb_spinlock); - - /* Next LAP */ - lap = (struct lap_cb *) hashbin_get_next(irlmp->links); - } - spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags); - - /* - * Server sockets are typically waiting for connections and - * therefore reside in the unconnected list. We don't want - * to give out their LSAPs for obvious reasons... - * Jean II - */ - spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); - - self = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps); - while (self != NULL) { - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, goto erruncon;); - if (self->slsap_sel == slsap_sel) { - pr_debug("Source LSAP selector=%02x in use (unconnected)\n", - self->slsap_sel); - goto erruncon; - } - self = (struct lsap_cb*) hashbin_get_next(irlmp->unconnected_lsaps); - } - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); - - return FALSE; - - /* Error exit from within one of the two nested loops. - * Make sure we release the right spinlock in the righ order. - * Jean II */ -errlsap: - spin_unlock(&lap->lsaps->hb_spinlock); -IRDA_ASSERT_LABEL(errlap:) - spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags); - return TRUE; - - /* Error exit from within the unconnected loop. - * Just one spinlock to release... Jean II */ -erruncon: - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); - return TRUE; -} - -/* - * Function irlmp_find_free_slsap () - * - * Find a free source LSAP to use. This function is called if the service - * user has requested a source LSAP equal to LM_ANY - */ -static __u8 irlmp_find_free_slsap(void) -{ - __u8 lsap_sel; - int wrapped = 0; - - IRDA_ASSERT(irlmp != NULL, return -1;); - IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return -1;); - - /* Most users don't really care which LSAPs they are given, - * and therefore we automatically give them a free LSAP. - * This function try to find a suitable LSAP, i.e. which is - * not in use and is within the acceptable range. Jean II */ - - do { - /* Always increment to LSAP number before using it. - * In theory, we could reuse the last LSAP number, as long - * as it is no longer in use. Some IrDA stack do that. - * However, the previous socket may be half closed, i.e. - * we closed it, we think it's no longer in use, but the - * other side did not receive our close and think it's - * active and still send data on it. - * This is similar to what is done with PIDs and TCP ports. - * Also, this reduce the number of calls to irlmp_slsap_inuse() - * which is an expensive function to call. - * Jean II */ - irlmp->last_lsap_sel++; - - /* Check if we need to wraparound (0x70-0x7f are reserved) */ - if (irlmp->last_lsap_sel > LSAP_MAX) { - /* 0x00-0x10 are also reserved for well know ports */ - irlmp->last_lsap_sel = 0x10; - - /* Make sure we terminate the loop */ - if (wrapped++) { - net_err_ratelimited("%s: no more free LSAPs !\n", - __func__); - return 0; - } - } - - /* If the LSAP is in use, try the next one. - * Despite the autoincrement, we need to check if the lsap - * is really in use or not, first because LSAP may be - * directly allocated in irlmp_open_lsap(), and also because - * we may wraparound on old sockets. Jean II */ - } while (irlmp_slsap_inuse(irlmp->last_lsap_sel)); - - /* Got it ! */ - lsap_sel = irlmp->last_lsap_sel; - pr_debug("%s(), found free lsap_sel=%02x\n", - __func__, lsap_sel); - - return lsap_sel; -} - -/* - * Function irlmp_convert_lap_reason (lap_reason) - * - * Converts IrLAP disconnect reason codes to IrLMP disconnect reason - * codes - * - */ -LM_REASON irlmp_convert_lap_reason( LAP_REASON lap_reason) -{ - int reason = LM_LAP_DISCONNECT; - - switch (lap_reason) { - case LAP_DISC_INDICATION: /* Received a disconnect request from peer */ - pr_debug("%s(), LAP_DISC_INDICATION\n", __func__); - reason = LM_USER_REQUEST; - break; - case LAP_NO_RESPONSE: /* To many retransmits without response */ - pr_debug("%s(), LAP_NO_RESPONSE\n", __func__); - reason = LM_LAP_DISCONNECT; - break; - case LAP_RESET_INDICATION: - pr_debug("%s(), LAP_RESET_INDICATION\n", __func__); - reason = LM_LAP_RESET; - break; - case LAP_FOUND_NONE: - case LAP_MEDIA_BUSY: - case LAP_PRIMARY_CONFLICT: - pr_debug("%s(), LAP_FOUND_NONE, LAP_MEDIA_BUSY or LAP_PRIMARY_CONFLICT\n", - __func__); - reason = LM_CONNECT_FAILURE; - break; - default: - pr_debug("%s(), Unknown IrLAP disconnect reason %d!\n", - __func__, lap_reason); - reason = LM_LAP_DISCONNECT; - break; - } - - return reason; -} - -#ifdef CONFIG_PROC_FS - -struct irlmp_iter_state { - hashbin_t *hashbin; -}; - -#define LSAP_START_TOKEN ((void *)1) -#define LINK_START_TOKEN ((void *)2) - -static void *irlmp_seq_hb_idx(struct irlmp_iter_state *iter, loff_t *off) -{ - void *element; - - spin_lock_irq(&iter->hashbin->hb_spinlock); - for (element = hashbin_get_first(iter->hashbin); - element != NULL; - element = hashbin_get_next(iter->hashbin)) { - if (!off || (*off)-- == 0) { - /* NB: hashbin left locked */ - return element; - } - } - spin_unlock_irq(&iter->hashbin->hb_spinlock); - iter->hashbin = NULL; - return NULL; -} - - -static void *irlmp_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct irlmp_iter_state *iter = seq->private; - void *v; - loff_t off = *pos; - - iter->hashbin = NULL; - if (off-- == 0) - return LSAP_START_TOKEN; - - iter->hashbin = irlmp->unconnected_lsaps; - v = irlmp_seq_hb_idx(iter, &off); - if (v) - return v; - - if (off-- == 0) - return LINK_START_TOKEN; - - iter->hashbin = irlmp->links; - return irlmp_seq_hb_idx(iter, &off); -} - -static void *irlmp_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct irlmp_iter_state *iter = seq->private; - - ++*pos; - - if (v == LSAP_START_TOKEN) { /* start of list of lsaps */ - iter->hashbin = irlmp->unconnected_lsaps; - v = irlmp_seq_hb_idx(iter, NULL); - return v ? v : LINK_START_TOKEN; - } - - if (v == LINK_START_TOKEN) { /* start of list of links */ - iter->hashbin = irlmp->links; - return irlmp_seq_hb_idx(iter, NULL); - } - - v = hashbin_get_next(iter->hashbin); - - if (v == NULL) { /* no more in this hash bin */ - spin_unlock_irq(&iter->hashbin->hb_spinlock); - - if (iter->hashbin == irlmp->unconnected_lsaps) - v = LINK_START_TOKEN; - - iter->hashbin = NULL; - } - return v; -} - -static void irlmp_seq_stop(struct seq_file *seq, void *v) -{ - struct irlmp_iter_state *iter = seq->private; - - if (iter->hashbin) - spin_unlock_irq(&iter->hashbin->hb_spinlock); -} - -static int irlmp_seq_show(struct seq_file *seq, void *v) -{ - const struct irlmp_iter_state *iter = seq->private; - struct lsap_cb *self = v; - - if (v == LSAP_START_TOKEN) - seq_puts(seq, "Unconnected LSAPs:\n"); - else if (v == LINK_START_TOKEN) - seq_puts(seq, "\nRegistered Link Layers:\n"); - else if (iter->hashbin == irlmp->unconnected_lsaps) { - self = v; - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EINVAL; ); - seq_printf(seq, "lsap state: %s, ", - irlsap_state[ self->lsap_state]); - seq_printf(seq, - "slsap_sel: %#02x, dlsap_sel: %#02x, ", - self->slsap_sel, self->dlsap_sel); - seq_printf(seq, "(%s)", self->notify.name); - seq_printf(seq, "\n"); - } else if (iter->hashbin == irlmp->links) { - struct lap_cb *lap = v; - - seq_printf(seq, "lap state: %s, ", - irlmp_state[lap->lap_state]); - - seq_printf(seq, "saddr: %#08x, daddr: %#08x, ", - lap->saddr, lap->daddr); - seq_printf(seq, "num lsaps: %d", - HASHBIN_GET_SIZE(lap->lsaps)); - seq_printf(seq, "\n"); - - /* Careful for priority inversions here ! - * All other uses of attrib spinlock are independent of - * the object spinlock, so we are safe. Jean II */ - spin_lock(&lap->lsaps->hb_spinlock); - - seq_printf(seq, "\n Connected LSAPs:\n"); - for (self = (struct lsap_cb *) hashbin_get_first(lap->lsaps); - self != NULL; - self = (struct lsap_cb *)hashbin_get_next(lap->lsaps)) { - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, - goto outloop;); - seq_printf(seq, " lsap state: %s, ", - irlsap_state[ self->lsap_state]); - seq_printf(seq, - "slsap_sel: %#02x, dlsap_sel: %#02x, ", - self->slsap_sel, self->dlsap_sel); - seq_printf(seq, "(%s)", self->notify.name); - seq_putc(seq, '\n'); - - } - IRDA_ASSERT_LABEL(outloop:) - spin_unlock(&lap->lsaps->hb_spinlock); - seq_putc(seq, '\n'); - } else - return -EINVAL; - - return 0; -} - -static const struct seq_operations irlmp_seq_ops = { - .start = irlmp_seq_start, - .next = irlmp_seq_next, - .stop = irlmp_seq_stop, - .show = irlmp_seq_show, -}; - -static int irlmp_seq_open(struct inode *inode, struct file *file) -{ - IRDA_ASSERT(irlmp != NULL, return -EINVAL;); - - return seq_open_private(file, &irlmp_seq_ops, - sizeof(struct irlmp_iter_state)); -} - -const struct file_operations irlmp_seq_fops = { - .owner = THIS_MODULE, - .open = irlmp_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; - -#endif /* PROC_FS */ diff --git a/drivers/staging/irda/net/irlmp_event.c b/drivers/staging/irda/net/irlmp_event.c deleted file mode 100644 index ddad0994b6dc..000000000000 --- a/drivers/staging/irda/net/irlmp_event.c +++ /dev/null @@ -1,886 +0,0 @@ -/********************************************************************* - * - * Filename: irlmp_event.c - * Version: 0.8 - * Description: An IrDA LMP event driver for Linux - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Tue Dec 14 23:04:16 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include - -const char *const irlmp_state[] = { - "LAP_STANDBY", - "LAP_U_CONNECT", - "LAP_ACTIVE", -}; - -const char *const irlsap_state[] = { - "LSAP_DISCONNECTED", - "LSAP_CONNECT", - "LSAP_CONNECT_PEND", - "LSAP_DATA_TRANSFER_READY", - "LSAP_SETUP", - "LSAP_SETUP_PEND", -}; - -static const char *const irlmp_event[] __maybe_unused = { - "LM_CONNECT_REQUEST", - "LM_CONNECT_CONFIRM", - "LM_CONNECT_RESPONSE", - "LM_CONNECT_INDICATION", - - "LM_DISCONNECT_INDICATION", - "LM_DISCONNECT_REQUEST", - - "LM_DATA_REQUEST", - "LM_UDATA_REQUEST", - "LM_DATA_INDICATION", - "LM_UDATA_INDICATION", - - "LM_WATCHDOG_TIMEOUT", - - /* IrLAP events */ - "LM_LAP_CONNECT_REQUEST", - "LM_LAP_CONNECT_INDICATION", - "LM_LAP_CONNECT_CONFIRM", - "LM_LAP_DISCONNECT_INDICATION", - "LM_LAP_DISCONNECT_REQUEST", - "LM_LAP_DISCOVERY_REQUEST", - "LM_LAP_DISCOVERY_CONFIRM", - "LM_LAP_IDLE_TIMEOUT", -}; - -/* LAP Connection control proto declarations */ -static void irlmp_state_standby (struct lap_cb *, IRLMP_EVENT, - struct sk_buff *); -static void irlmp_state_u_connect(struct lap_cb *, IRLMP_EVENT, - struct sk_buff *); -static void irlmp_state_active (struct lap_cb *, IRLMP_EVENT, - struct sk_buff *); - -/* LSAP Connection control proto declarations */ -static int irlmp_state_disconnected(struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); -static int irlmp_state_connect (struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); -static int irlmp_state_connect_pend(struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); -static int irlmp_state_dtr (struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); -static int irlmp_state_setup (struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); -static int irlmp_state_setup_pend (struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); - -static void (*lap_state[]) (struct lap_cb *, IRLMP_EVENT, struct sk_buff *) = -{ - irlmp_state_standby, - irlmp_state_u_connect, - irlmp_state_active, -}; - -static int (*lsap_state[])( struct lsap_cb *, IRLMP_EVENT, struct sk_buff *) = -{ - irlmp_state_disconnected, - irlmp_state_connect, - irlmp_state_connect_pend, - irlmp_state_dtr, - irlmp_state_setup, - irlmp_state_setup_pend -}; - -static inline void irlmp_next_lap_state(struct lap_cb *self, - IRLMP_STATE state) -{ - /* - pr_debug("%s(), LMP LAP = %s\n", __func__, irlmp_state[state]); - */ - self->lap_state = state; -} - -static inline void irlmp_next_lsap_state(struct lsap_cb *self, - LSAP_STATE state) -{ - /* - IRDA_ASSERT(self != NULL, return;); - pr_debug("%s(), LMP LSAP = %s\n", __func__, irlsap_state[state]); - */ - self->lsap_state = state; -} - -/* Do connection control events */ -int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - pr_debug("%s(), EVENT = %s, STATE = %s\n", - __func__, irlmp_event[event], irlsap_state[self->lsap_state]); - - return (*lsap_state[self->lsap_state]) (self, event, skb); -} - -/* - * Function do_lap_event (event, skb, info) - * - * Do IrLAP control events - * - */ -void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - - pr_debug("%s(), EVENT = %s, STATE = %s\n", __func__, - irlmp_event[event], - irlmp_state[self->lap_state]); - - (*lap_state[self->lap_state]) (self, event, skb); -} - -void irlmp_discovery_timer_expired(struct timer_list *t) -{ - /* We always cleanup the log (active & passive discovery) */ - irlmp_do_expiry(); - - irlmp_do_discovery(sysctl_discovery_slots); - - /* Restart timer */ - irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout * HZ); -} - -void irlmp_watchdog_timer_expired(struct timer_list *t) -{ - struct lsap_cb *self = from_timer(self, t, watchdog_timer); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - - irlmp_do_lsap_event(self, LM_WATCHDOG_TIMEOUT, NULL); -} - -void irlmp_idle_timer_expired(struct timer_list *t) -{ - struct lap_cb *self = from_timer(self, t, idle_timer); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - - irlmp_do_lap_event(self, LM_LAP_IDLE_TIMEOUT, NULL); -} - -/* - * Send an event on all LSAPs attached to this LAP. - */ -static inline void -irlmp_do_all_lsap_event(hashbin_t * lsap_hashbin, - IRLMP_EVENT event) -{ - struct lsap_cb *lsap; - struct lsap_cb *lsap_next; - - /* Note : this function use the new hashbin_find_next() - * function, instead of the old hashbin_get_next(). - * This make sure that we are always pointing one lsap - * ahead, so that if the current lsap is removed as the - * result of sending the event, we don't care. - * Also, as we store the context ourselves, if an enumeration - * of the same lsap hashbin happens as the result of sending the - * event, we don't care. - * The only problem is if the next lsap is removed. In that case, - * hashbin_find_next() will return NULL and we will abort the - * enumeration. - Jean II */ - - /* Also : we don't accept any skb in input. We can *NOT* pass - * the same skb to multiple clients safely, we would need to - * skb_clone() it. - Jean II */ - - lsap = (struct lsap_cb *) hashbin_get_first(lsap_hashbin); - - while (NULL != hashbin_find_next(lsap_hashbin, - (long) lsap, - NULL, - (void *) &lsap_next) ) { - irlmp_do_lsap_event(lsap, event, NULL); - lsap = lsap_next; - } -} - -/********************************************************************* - * - * LAP connection control states - * - ********************************************************************/ - -/* - * Function irlmp_state_standby (event, skb, info) - * - * STANDBY, The IrLAP connection does not exist. - * - */ -static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self->irlap != NULL, return;); - - switch (event) { - case LM_LAP_DISCOVERY_REQUEST: - /* irlmp_next_station_state( LMP_DISCOVER); */ - - irlap_discovery_request(self->irlap, &irlmp->discovery_cmd); - break; - case LM_LAP_CONNECT_INDICATION: - /* It's important to switch state first, to avoid IrLMP to - * think that the link is free since IrLMP may then start - * discovery before the connection is properly set up. DB. - */ - irlmp_next_lap_state(self, LAP_ACTIVE); - - /* Just accept connection TODO, this should be fixed */ - irlap_connect_response(self->irlap, skb); - break; - case LM_LAP_CONNECT_REQUEST: - pr_debug("%s() LS_CONNECT_REQUEST\n", __func__); - - irlmp_next_lap_state(self, LAP_U_CONNECT); - - /* FIXME: need to set users requested QoS */ - irlap_connect_request(self->irlap, self->daddr, NULL, 0); - break; - case LM_LAP_DISCONNECT_INDICATION: - pr_debug("%s(), Error LM_LAP_DISCONNECT_INDICATION\n", - __func__); - - irlmp_next_lap_state(self, LAP_STANDBY); - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlmp_event[event]); - break; - } -} - -/* - * Function irlmp_state_u_connect (event, skb, info) - * - * U_CONNECT, The layer above has tried to open an LSAP connection but - * since the IrLAP connection does not exist, we must first start an - * IrLAP connection. We are now waiting response from IrLAP. - * */ -static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), event=%s\n", __func__, irlmp_event[event]); - - switch (event) { - case LM_LAP_CONNECT_INDICATION: - /* It's important to switch state first, to avoid IrLMP to - * think that the link is free since IrLMP may then start - * discovery before the connection is properly set up. DB. - */ - irlmp_next_lap_state(self, LAP_ACTIVE); - - /* Just accept connection TODO, this should be fixed */ - irlap_connect_response(self->irlap, skb); - - /* Tell LSAPs that they can start sending data */ - irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); - - /* Note : by the time we get there (LAP retries and co), - * the lsaps may already have gone. This avoid getting stuck - * forever in LAP_ACTIVE state - Jean II */ - if (HASHBIN_GET_SIZE(self->lsaps) == 0) { - pr_debug("%s() NO LSAPs !\n", __func__); - irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT); - } - break; - case LM_LAP_CONNECT_REQUEST: - /* Already trying to connect */ - break; - case LM_LAP_CONNECT_CONFIRM: - /* For all lsap_ce E Associated do LS_Connect_confirm */ - irlmp_next_lap_state(self, LAP_ACTIVE); - - /* Tell LSAPs that they can start sending data */ - irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); - - /* Note : by the time we get there (LAP retries and co), - * the lsaps may already have gone. This avoid getting stuck - * forever in LAP_ACTIVE state - Jean II */ - if (HASHBIN_GET_SIZE(self->lsaps) == 0) { - pr_debug("%s() NO LSAPs !\n", __func__); - irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT); - } - break; - case LM_LAP_DISCONNECT_INDICATION: - pr_debug("%s(), LM_LAP_DISCONNECT_INDICATION\n", __func__); - irlmp_next_lap_state(self, LAP_STANDBY); - - /* Send disconnect event to all LSAPs using this link */ - irlmp_do_all_lsap_event(self->lsaps, - LM_LAP_DISCONNECT_INDICATION); - break; - case LM_LAP_DISCONNECT_REQUEST: - pr_debug("%s(), LM_LAP_DISCONNECT_REQUEST\n", __func__); - - /* One of the LSAP did timeout or was closed, if it was - * the last one, try to get out of here - Jean II */ - if (HASHBIN_GET_SIZE(self->lsaps) <= 1) { - irlap_disconnect_request(self->irlap); - } - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlmp_event[event]); - break; - } -} - -/* - * Function irlmp_state_active (event, skb, info) - * - * ACTIVE, IrLAP connection is active - * - */ -static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - switch (event) { - case LM_LAP_CONNECT_REQUEST: - pr_debug("%s(), LS_CONNECT_REQUEST\n", __func__); - - /* - * IrLAP may have a pending disconnect. We tried to close - * IrLAP, but it was postponed because the link was - * busy or we were still sending packets. As we now - * need it, make sure it stays on. Jean II - */ - irlap_clear_disconnect(self->irlap); - - /* - * LAP connection already active, just bounce back! Since we - * don't know which LSAP that tried to do this, we have to - * notify all LSAPs using this LAP, but that should be safe to - * do anyway. - */ - irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); - - /* Needed by connect indication */ - irlmp_do_all_lsap_event(irlmp->unconnected_lsaps, - LM_LAP_CONNECT_CONFIRM); - /* Keep state */ - break; - case LM_LAP_DISCONNECT_REQUEST: - /* - * Need to find out if we should close IrLAP or not. If there - * is only one LSAP connection left on this link, that LSAP - * must be the one that tries to close IrLAP. It will be - * removed later and moved to the list of unconnected LSAPs - */ - if (HASHBIN_GET_SIZE(self->lsaps) > 0) { - /* Timer value is checked in irsysctl - Jean II */ - irlmp_start_idle_timer(self, sysctl_lap_keepalive_time * HZ / 1000); - } else { - /* No more connections, so close IrLAP */ - - /* We don't want to change state just yet, because - * we want to reflect accurately the real state of - * the LAP, not the state we wish it was in, - * so that we don't lose LM_LAP_CONNECT_REQUEST. - * In some cases, IrLAP won't close the LAP - * immediately. For example, it might still be - * retrying packets or waiting for the pf bit. - * As the LAP always send a DISCONNECT_INDICATION - * in PCLOSE or SCLOSE, just change state on that. - * Jean II */ - irlap_disconnect_request(self->irlap); - } - break; - case LM_LAP_IDLE_TIMEOUT: - if (HASHBIN_GET_SIZE(self->lsaps) == 0) { - /* Same reasoning as above - keep state */ - irlap_disconnect_request(self->irlap); - } - break; - case LM_LAP_DISCONNECT_INDICATION: - irlmp_next_lap_state(self, LAP_STANDBY); - - /* In some case, at this point our side has already closed - * all lsaps, and we are waiting for the idle_timer to - * expire. If another device reconnect immediately, the - * idle timer will expire in the midle of the connection - * initialisation, screwing up things a lot... - * Therefore, we must stop the timer... */ - irlmp_stop_idle_timer(self); - - /* - * Inform all connected LSAP's using this link - */ - irlmp_do_all_lsap_event(self->lsaps, - LM_LAP_DISCONNECT_INDICATION); - - /* Force an expiry of the discovery log. - * Now that the LAP is free, the system may attempt to - * connect to another device. Unfortunately, our entries - * are stale. There is a small window (<3s) before the - * normal discovery will run and where irlmp_connect_request() - * can get the wrong info, so make sure things get - * cleaned *NOW* ;-) - Jean II */ - irlmp_do_expiry(); - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlmp_event[event]); - break; - } -} - -/********************************************************************* - * - * LSAP connection control states - * - ********************************************************************/ - -/* - * Function irlmp_state_disconnected (event, skb, info) - * - * DISCONNECTED - * - */ -static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - switch (event) { -#ifdef CONFIG_IRDA_ULTRA - case LM_UDATA_INDICATION: - /* This is most bizarre. Those packets are aka unreliable - * connected, aka IrLPT or SOCK_DGRAM/IRDAPROTO_UNITDATA. - * Why do we pass them as Ultra ??? Jean II */ - irlmp_connless_data_indication(self, skb); - break; -#endif /* CONFIG_IRDA_ULTRA */ - case LM_CONNECT_REQUEST: - pr_debug("%s(), LM_CONNECT_REQUEST\n", __func__); - - if (self->conn_skb) { - net_warn_ratelimited("%s: busy with another request!\n", - __func__); - return -EBUSY; - } - /* Don't forget to refcount it (see irlmp_connect_request()) */ - skb_get(skb); - self->conn_skb = skb; - - irlmp_next_lsap_state(self, LSAP_SETUP_PEND); - - /* Start watchdog timer (5 secs for now) */ - irlmp_start_watchdog_timer(self, 5*HZ); - - irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL); - break; - case LM_CONNECT_INDICATION: - if (self->conn_skb) { - net_warn_ratelimited("%s: busy with another request!\n", - __func__); - return -EBUSY; - } - /* Don't forget to refcount it (see irlap_driver_rcv()) */ - skb_get(skb); - self->conn_skb = skb; - - irlmp_next_lsap_state(self, LSAP_CONNECT_PEND); - - /* Start watchdog timer - * This is not mentionned in the spec, but there is a rare - * race condition that can get the socket stuck. - * If we receive this event while our LAP is closing down, - * the LM_LAP_CONNECT_REQUEST get lost and we get stuck in - * CONNECT_PEND state forever. - * The other cause of getting stuck down there is if the - * higher layer never reply to the CONNECT_INDICATION. - * Anyway, it make sense to make sure that we always have - * a backup plan. 1 second is plenty (should be immediate). - * Jean II */ - irlmp_start_watchdog_timer(self, 1*HZ); - - irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL); - break; - default: - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} - -/* - * Function irlmp_state_connect (self, event, skb) - * - * CONNECT - * - */ -static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - struct lsap_cb *lsap; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - switch (event) { - case LM_CONNECT_RESPONSE: - /* - * Bind this LSAP to the IrLAP link where the connect was - * received - */ - lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, - NULL); - - IRDA_ASSERT(lsap == self, return -1;); - IRDA_ASSERT(self->lap != NULL, return -1;); - IRDA_ASSERT(self->lap->lsaps != NULL, return -1;); - - hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, - (long) self, NULL); - - set_bit(0, &self->connected); /* TRUE */ - - irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, - self->slsap_sel, CONNECT_CNF, skb); - - del_timer(&self->watchdog_timer); - - irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY); - break; - case LM_WATCHDOG_TIMEOUT: - /* May happen, who knows... - * Jean II */ - pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); - - /* Disconnect, get out... - Jean II */ - self->lap = NULL; - self->dlsap_sel = LSAP_ANY; - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - break; - default: - /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we - * are *not* yet bound to the IrLAP link. Jean II */ - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} - -/* - * Function irlmp_state_connect_pend (event, skb, info) - * - * CONNECT_PEND - * - */ -static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - switch (event) { - case LM_CONNECT_REQUEST: - /* Keep state */ - break; - case LM_CONNECT_RESPONSE: - pr_debug("%s(), LM_CONNECT_RESPONSE, no indication issued yet\n", - __func__); - /* Keep state */ - break; - case LM_DISCONNECT_REQUEST: - pr_debug("%s(), LM_DISCONNECT_REQUEST, not yet bound to IrLAP connection\n", - __func__); - /* Keep state */ - break; - case LM_LAP_CONNECT_CONFIRM: - pr_debug("%s(), LS_CONNECT_CONFIRM\n", __func__); - irlmp_next_lsap_state(self, LSAP_CONNECT); - - tx_skb = self->conn_skb; - self->conn_skb = NULL; - - irlmp_connect_indication(self, tx_skb); - /* Drop reference count - see irlmp_connect_indication(). */ - dev_kfree_skb(tx_skb); - break; - case LM_WATCHDOG_TIMEOUT: - /* Will happen in some rare cases because of a race condition. - * Just make sure we don't stay there forever... - * Jean II */ - pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); - - /* Go back to disconnected mode, keep the socket waiting */ - self->lap = NULL; - self->dlsap_sel = LSAP_ANY; - if(self->conn_skb) - dev_kfree_skb(self->conn_skb); - self->conn_skb = NULL; - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - break; - default: - /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we - * are *not* yet bound to the IrLAP link. Jean II */ - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} - -/* - * Function irlmp_state_dtr (self, event, skb) - * - * DATA_TRANSFER_READY - * - */ -static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - LM_REASON reason; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_ASSERT(self->lap != NULL, return -1;); - - switch (event) { - case LM_DATA_REQUEST: /* Optimize for the common case */ - irlmp_send_data_pdu(self->lap, self->dlsap_sel, - self->slsap_sel, FALSE, skb); - break; - case LM_DATA_INDICATION: /* Optimize for the common case */ - irlmp_data_indication(self, skb); - break; - case LM_UDATA_REQUEST: - IRDA_ASSERT(skb != NULL, return -1;); - irlmp_send_data_pdu(self->lap, self->dlsap_sel, - self->slsap_sel, TRUE, skb); - break; - case LM_UDATA_INDICATION: - irlmp_udata_indication(self, skb); - break; - case LM_CONNECT_REQUEST: - pr_debug("%s(), LM_CONNECT_REQUEST, error, LSAP already connected\n", - __func__); - /* Keep state */ - break; - case LM_CONNECT_RESPONSE: - pr_debug("%s(), LM_CONNECT_RESPONSE, error, LSAP already connected\n", - __func__); - /* Keep state */ - break; - case LM_DISCONNECT_REQUEST: - irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, self->slsap_sel, - DISCONNECT, skb); - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - /* Called only from irlmp_disconnect_request(), will - * unbind from LAP over there. Jean II */ - - /* Try to close the LAP connection if its still there */ - if (self->lap) { - pr_debug("%s(), trying to close IrLAP\n", - __func__); - irlmp_do_lap_event(self->lap, - LM_LAP_DISCONNECT_REQUEST, - NULL); - } - break; - case LM_LAP_DISCONNECT_INDICATION: - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - reason = irlmp_convert_lap_reason(self->lap->reason); - - irlmp_disconnect_indication(self, reason, NULL); - break; - case LM_DISCONNECT_INDICATION: - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - IRDA_ASSERT(self->lap != NULL, return -1;); - IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); - - IRDA_ASSERT(skb != NULL, return -1;); - IRDA_ASSERT(skb->len > 3, return -1;); - reason = skb->data[3]; - - /* Try to close the LAP connection */ - pr_debug("%s(), trying to close IrLAP\n", __func__); - irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); - - irlmp_disconnect_indication(self, reason, skb); - break; - default: - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} - -/* - * Function irlmp_state_setup (event, skb, info) - * - * SETUP, Station Control has set up the underlying IrLAP connection. - * An LSAP connection request has been transmitted to the peer - * LSAP-Connection Control FSM and we are awaiting reply. - */ -static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - LM_REASON reason; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - switch (event) { - case LM_CONNECT_CONFIRM: - irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY); - - del_timer(&self->watchdog_timer); - - irlmp_connect_confirm(self, skb); - break; - case LM_DISCONNECT_INDICATION: - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - IRDA_ASSERT(self->lap != NULL, return -1;); - IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); - - IRDA_ASSERT(skb != NULL, return -1;); - IRDA_ASSERT(skb->len > 3, return -1;); - reason = skb->data[3]; - - /* Try to close the LAP connection */ - pr_debug("%s(), trying to close IrLAP\n", __func__); - irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); - - irlmp_disconnect_indication(self, reason, skb); - break; - case LM_LAP_DISCONNECT_INDICATION: - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - del_timer(&self->watchdog_timer); - - IRDA_ASSERT(self->lap != NULL, return -1;); - IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); - - reason = irlmp_convert_lap_reason(self->lap->reason); - - irlmp_disconnect_indication(self, reason, skb); - break; - case LM_WATCHDOG_TIMEOUT: - pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); - - IRDA_ASSERT(self->lap != NULL, return -1;); - irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL); - break; - default: - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} - -/* - * Function irlmp_state_setup_pend (event, skb, info) - * - * SETUP_PEND, An LM_CONNECT_REQUEST has been received from the service - * user to set up an LSAP connection. A request has been sent to the - * LAP FSM to set up the underlying IrLAP connection, and we - * are awaiting confirm. - */ -static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - LM_REASON reason; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(irlmp != NULL, return -1;); - - switch (event) { - case LM_LAP_CONNECT_CONFIRM: - IRDA_ASSERT(self->conn_skb != NULL, return -1;); - - tx_skb = self->conn_skb; - self->conn_skb = NULL; - - irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, - self->slsap_sel, CONNECT_CMD, tx_skb); - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(tx_skb); - - irlmp_next_lsap_state(self, LSAP_SETUP); - break; - case LM_WATCHDOG_TIMEOUT: - pr_debug("%s() : WATCHDOG_TIMEOUT !\n", __func__); - - IRDA_ASSERT(self->lap != NULL, return -1;); - irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL); - break; - case LM_LAP_DISCONNECT_INDICATION: /* LS_Disconnect.indication */ - del_timer( &self->watchdog_timer); - - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - reason = irlmp_convert_lap_reason(self->lap->reason); - - irlmp_disconnect_indication(self, reason, NULL); - break; - default: - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} diff --git a/drivers/staging/irda/net/irlmp_frame.c b/drivers/staging/irda/net/irlmp_frame.c deleted file mode 100644 index 38b0f994bc7b..000000000000 --- a/drivers/staging/irda/net/irlmp_frame.c +++ /dev/null @@ -1,476 +0,0 @@ -/********************************************************************* - * - * Filename: irlmp_frame.c - * Version: 0.9 - * Description: IrLMP frame implementation - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Aug 19 02:09:59 1997 - * Modified at: Mon Dec 13 13:41:12 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include -#include - -static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap, - __u8 slsap, int status, hashbin_t *); - -inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, - int expedited, struct sk_buff *skb) -{ - skb->data[0] = dlsap; - skb->data[1] = slsap; - - if (expedited) { - pr_debug("%s(), sending expedited data\n", __func__); - irlap_data_request(self->irlap, skb, TRUE); - } else - irlap_data_request(self->irlap, skb, FALSE); -} - -/* - * Function irlmp_send_lcf_pdu (dlsap, slsap, opcode,skb) - * - * Send Link Control Frame to IrLAP - */ -void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, - __u8 opcode, struct sk_buff *skb) -{ - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - frame = skb->data; - - frame[0] = dlsap | CONTROL_BIT; - frame[1] = slsap; - - frame[2] = opcode; - - if (opcode == DISCONNECT) - frame[3] = 0x01; /* Service user request */ - else - frame[3] = 0x00; /* rsvd */ - - irlap_data_request(self->irlap, skb, FALSE); -} - -/* - * Function irlmp_input (skb) - * - * Used by IrLAP to pass received data frames to IrLMP layer - * - */ -void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb, - int unreliable) -{ - struct lsap_cb *lsap; - __u8 slsap_sel; /* Source (this) LSAP address */ - __u8 dlsap_sel; /* Destination LSAP address */ - __u8 *fp; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - IRDA_ASSERT(skb->len > 2, return;); - - fp = skb->data; - - /* - * The next statements may be confusing, but we do this so that - * destination LSAP of received frame is source LSAP in our view - */ - slsap_sel = fp[0] & LSAP_MASK; - dlsap_sel = fp[1]; - - /* - * Check if this is an incoming connection, since we must deal with - * it in a different way than other established connections. - */ - if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) { - pr_debug("%s(), incoming connection, source LSAP=%d, dest LSAP=%d\n", - __func__, slsap_sel, dlsap_sel); - - /* Try to find LSAP among the unconnected LSAPs */ - lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, CONNECT_CMD, - irlmp->unconnected_lsaps); - - /* Maybe LSAP was already connected, so try one more time */ - if (!lsap) { - pr_debug("%s(), incoming connection for LSAP already connected\n", - __func__); - lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0, - self->lsaps); - } - } else - lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0, - self->lsaps); - - if (lsap == NULL) { - pr_debug("IrLMP, Sorry, no LSAP for received frame!\n"); - pr_debug("%s(), slsap_sel = %02x, dlsap_sel = %02x\n", - __func__, slsap_sel, dlsap_sel); - if (fp[0] & CONTROL_BIT) { - pr_debug("%s(), received control frame %02x\n", - __func__, fp[2]); - } else { - pr_debug("%s(), received data frame\n", __func__); - } - return; - } - - /* - * Check if we received a control frame? - */ - if (fp[0] & CONTROL_BIT) { - switch (fp[2]) { - case CONNECT_CMD: - lsap->lap = self; - irlmp_do_lsap_event(lsap, LM_CONNECT_INDICATION, skb); - break; - case CONNECT_CNF: - irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb); - break; - case DISCONNECT: - pr_debug("%s(), Disconnect indication!\n", - __func__); - irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION, - skb); - break; - case ACCESSMODE_CMD: - pr_debug("Access mode cmd not implemented!\n"); - break; - case ACCESSMODE_CNF: - pr_debug("Access mode cnf not implemented!\n"); - break; - default: - pr_debug("%s(), Unknown control frame %02x\n", - __func__, fp[2]); - break; - } - } else if (unreliable) { - /* Optimize and bypass the state machine if possible */ - if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY) - irlmp_udata_indication(lsap, skb); - else - irlmp_do_lsap_event(lsap, LM_UDATA_INDICATION, skb); - } else { - /* Optimize and bypass the state machine if possible */ - if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY) - irlmp_data_indication(lsap, skb); - else - irlmp_do_lsap_event(lsap, LM_DATA_INDICATION, skb); - } -} - -/* - * Function irlmp_link_unitdata_indication (self, skb) - * - * - * - */ -#ifdef CONFIG_IRDA_ULTRA -void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb) -{ - struct lsap_cb *lsap; - __u8 slsap_sel; /* Source (this) LSAP address */ - __u8 dlsap_sel; /* Destination LSAP address */ - __u8 pid; /* Protocol identifier */ - __u8 *fp; - unsigned long flags; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - IRDA_ASSERT(skb->len > 2, return;); - - fp = skb->data; - - /* - * The next statements may be confusing, but we do this so that - * destination LSAP of received frame is source LSAP in our view - */ - slsap_sel = fp[0] & LSAP_MASK; - dlsap_sel = fp[1]; - pid = fp[2]; - - if (pid & 0x80) { - pr_debug("%s(), extension in PID not supp!\n", - __func__); - return; - } - - /* Check if frame is addressed to the connectionless LSAP */ - if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) { - pr_debug("%s(), dropping frame!\n", __func__); - return; - } - - /* Search the connectionless LSAP */ - spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); - lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps); - while (lsap != NULL) { - /* - * Check if source LSAP and dest LSAP selectors and PID match. - */ - if ((lsap->slsap_sel == slsap_sel) && - (lsap->dlsap_sel == dlsap_sel) && - (lsap->pid == pid)) - { - break; - } - lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps); - } - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); - - if (lsap) - irlmp_connless_data_indication(lsap, skb); - else { - pr_debug("%s(), found no matching LSAP!\n", __func__); - } -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irlmp_link_disconnect_indication (reason, userdata) - * - * IrLAP has disconnected - * - */ -void irlmp_link_disconnect_indication(struct lap_cb *lap, - struct irlap_cb *irlap, - LAP_REASON reason, - struct sk_buff *skb) -{ - IRDA_ASSERT(lap != NULL, return;); - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); - - lap->reason = reason; - lap->daddr = DEV_ADDR_ANY; - - /* FIXME: must do something with the skb if any */ - - /* - * Inform station state machine - */ - irlmp_do_lap_event(lap, LM_LAP_DISCONNECT_INDICATION, NULL); -} - -/* - * Function irlmp_link_connect_indication (qos) - * - * Incoming LAP connection! - * - */ -void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr, - __u32 daddr, struct qos_info *qos, - struct sk_buff *skb) -{ - /* Copy QoS settings for this session */ - self->qos = qos; - - /* Update destination device address */ - self->daddr = daddr; - IRDA_ASSERT(self->saddr == saddr, return;); - - irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb); -} - -/* - * Function irlmp_link_connect_confirm (qos) - * - * LAP connection confirmed! - * - */ -void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - IRDA_ASSERT(qos != NULL, return;); - - /* Don't need use the skb for now */ - - /* Copy QoS settings for this session */ - self->qos = qos; - - irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL); -} - -/* - * Function irlmp_link_discovery_indication (self, log) - * - * Device is discovering us - * - * It's not an answer to our own discoveries, just another device trying - * to perform discovery, but we don't want to miss the opportunity - * to exploit this information, because : - * o We may not actively perform discovery (just passive discovery) - * o This type of discovery is much more reliable. In some cases, it - * seem that less than 50% of our discoveries get an answer, while - * we always get ~100% of these. - * o Make faster discovery, statistically divide time of discovery - * events by 2 (important for the latency aspect and user feel) - * o Even is we do active discovery, the other node might not - * answer our discoveries (ex: Palm). The Palm will just perform - * one active discovery and connect directly to us. - * - * However, when both devices discover each other, they might attempt to - * connect to each other following the discovery event, and it would create - * collisions on the medium (SNRM battle). - * The "fix" for that is to disable all connection requests in IrLAP - * for 100ms after a discovery indication by setting the media_busy flag. - * Previously, we used to postpone the event which was quite ugly. Now - * that IrLAP takes care of this problem, just pass the event up... - * - * Jean II - */ -void irlmp_link_discovery_indication(struct lap_cb *self, - discovery_t *discovery) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - - /* Add to main log, cleanup */ - irlmp_add_discovery(irlmp->cachelog, discovery); - - /* Just handle it the same way as a discovery confirm, - * bypass the LM_LAP state machine (see below) */ - irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_PASSIVE); -} - -/* - * Function irlmp_link_discovery_confirm (self, log) - * - * Called by IrLAP with a list of discoveries after the discovery - * request has been carried out. A NULL log is received if IrLAP - * was unable to carry out the discovery request - * - */ -void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - - /* Add to main log, cleanup */ - irlmp_add_discovery_log(irlmp->cachelog, log); - - /* Propagate event to various LSAPs registered for it. - * We bypass the LM_LAP state machine because - * 1) We do it regardless of the LM_LAP state - * 2) It doesn't affect the LM_LAP state - * 3) Faster, slimer, simpler, ... - * Jean II */ - irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_ACTIVE); -} - -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP -static inline void irlmp_update_cache(struct lap_cb *lap, - struct lsap_cb *lsap) -{ - /* Prevent concurrent read to get garbage */ - lap->cache.valid = FALSE; - /* Update cache entry */ - lap->cache.dlsap_sel = lsap->dlsap_sel; - lap->cache.slsap_sel = lsap->slsap_sel; - lap->cache.lsap = lsap; - lap->cache.valid = TRUE; -} -#endif - -/* - * Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue) - * - * Find handle associated with destination and source LSAP - * - * Any IrDA connection (LSAP/TSAP) is uniquely identified by - * 3 parameters, the local lsap, the remote lsap and the remote address. - * We may initiate multiple connections to the same remote service - * (they will have different local lsap), a remote device may initiate - * multiple connections to the same local service (they will have - * different remote lsap), or multiple devices may connect to the same - * service and may use the same remote lsap (and they will have - * different remote address). - * So, where is the remote address ? Each LAP connection is made with - * a single remote device, so imply a specific remote address. - * Jean II - */ -static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel, - __u8 slsap_sel, int status, - hashbin_t *queue) -{ - struct lsap_cb *lsap; - unsigned long flags; - - /* - * Optimize for the common case. We assume that the last frame - * received is in the same connection as the last one, so check in - * cache first to avoid the linear search - */ -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - if ((self->cache.valid) && - (self->cache.slsap_sel == slsap_sel) && - (self->cache.dlsap_sel == dlsap_sel)) - { - return self->cache.lsap; - } -#endif - - spin_lock_irqsave(&queue->hb_spinlock, flags); - - lsap = (struct lsap_cb *) hashbin_get_first(queue); - while (lsap != NULL) { - /* - * If this is an incoming connection, then the destination - * LSAP selector may have been specified as LM_ANY so that - * any client can connect. In that case we only need to check - * if the source LSAP (in our view!) match! - */ - if ((status == CONNECT_CMD) && - (lsap->slsap_sel == slsap_sel) && - (lsap->dlsap_sel == LSAP_ANY)) { - /* This is where the dest lsap sel is set on incoming - * lsaps */ - lsap->dlsap_sel = dlsap_sel; - break; - } - /* - * Check if source LSAP and dest LSAP selectors match. - */ - if ((lsap->slsap_sel == slsap_sel) && - (lsap->dlsap_sel == dlsap_sel)) - break; - - lsap = (struct lsap_cb *) hashbin_get_next(queue); - } -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - if(lsap) - irlmp_update_cache(self, lsap); -#endif - spin_unlock_irqrestore(&queue->hb_spinlock, flags); - - /* Return what we've found or NULL */ - return lsap; -} diff --git a/drivers/staging/irda/net/irmod.c b/drivers/staging/irda/net/irmod.c deleted file mode 100644 index 4319f4ff66b0..000000000000 --- a/drivers/staging/irda/net/irmod.c +++ /dev/null @@ -1,199 +0,0 @@ -/********************************************************************* - * - * Filename: irmod.c - * Version: 0.9 - * Description: IrDA stack main entry points - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Dec 15 13:55:39 1997 - * Modified at: Wed Jan 5 15:12:41 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2004 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -/* - * This file contains the main entry points of the IrDA stack. - * They are in this file and not af_irda.c because some developpers - * are using the IrDA stack without the socket API (compiling out - * af_irda.c). - * Jean II - */ - -#include -#include - -#include -#include /* notify_t */ -#include /* irlap_init */ -#include /* irlmp_init */ -#include /* iriap_init */ -#include /* irttp_init */ -#include /* irda_device_init */ - -/* Packet type handler. - * Tell the kernel how IrDA packets should be handled. - */ -static struct packet_type irda_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_IRDA), - .func = irlap_driver_rcv, /* Packet type handler irlap_frame.c */ -}; - -/* - * Function irda_notify_init (notify) - * - * Used for initializing the notify structure - * - */ -void irda_notify_init(notify_t *notify) -{ - notify->data_indication = NULL; - notify->udata_indication = NULL; - notify->connect_confirm = NULL; - notify->connect_indication = NULL; - notify->disconnect_indication = NULL; - notify->flow_indication = NULL; - notify->status_indication = NULL; - notify->instance = NULL; - strlcpy(notify->name, "Unknown", sizeof(notify->name)); -} -EXPORT_SYMBOL(irda_notify_init); - -/* - * Function irda_init (void) - * - * Protocol stack initialisation entry point. - * Initialise the various components of the IrDA stack - */ -static int __init irda_init(void) -{ - int ret = 0; - - /* Lower layer of the stack */ - irlmp_init(); - irlap_init(); - - /* Driver/dongle support */ - irda_device_init(); - - /* Higher layers of the stack */ - iriap_init(); - irttp_init(); - ret = irsock_init(); - if (ret < 0) - goto out_err_1; - - /* Add IrDA packet type (Start receiving packets) */ - dev_add_pack(&irda_packet_type); - - /* External APIs */ -#ifdef CONFIG_PROC_FS - irda_proc_register(); -#endif -#ifdef CONFIG_SYSCTL - ret = irda_sysctl_register(); - if (ret < 0) - goto out_err_2; -#endif - - ret = irda_nl_register(); - if (ret < 0) - goto out_err_3; - - return 0; - - out_err_3: -#ifdef CONFIG_SYSCTL - irda_sysctl_unregister(); - out_err_2: -#endif -#ifdef CONFIG_PROC_FS - irda_proc_unregister(); -#endif - - /* Remove IrDA packet type (stop receiving packets) */ - dev_remove_pack(&irda_packet_type); - - /* Remove higher layers */ - irsock_cleanup(); - out_err_1: - irttp_cleanup(); - iriap_cleanup(); - - /* Remove lower layers */ - irda_device_cleanup(); - irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */ - - /* Remove middle layer */ - irlmp_cleanup(); - - - return ret; -} - -/* - * Function irda_cleanup (void) - * - * Protocol stack cleanup/removal entry point. - * Cleanup the various components of the IrDA stack - */ -static void __exit irda_cleanup(void) -{ - /* Remove External APIs */ - irda_nl_unregister(); - -#ifdef CONFIG_SYSCTL - irda_sysctl_unregister(); -#endif -#ifdef CONFIG_PROC_FS - irda_proc_unregister(); -#endif - - /* Remove IrDA packet type (stop receiving packets) */ - dev_remove_pack(&irda_packet_type); - - /* Remove higher layers */ - irsock_cleanup(); - irttp_cleanup(); - iriap_cleanup(); - - /* Remove lower layers */ - irda_device_cleanup(); - irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */ - - /* Remove middle layer */ - irlmp_cleanup(); -} - -/* - * The IrDA stack must be initialised *before* drivers get initialised, - * and *before* higher protocols (IrLAN/IrCOMM/IrNET) get initialised, - * otherwise bad things will happen (hashbins will be NULL for example). - * Those modules are at module_init()/device_initcall() level. - * - * On the other hand, it needs to be initialised *after* the basic - * networking, the /proc/net filesystem and sysctl module. Those are - * currently initialised in .../init/main.c (before initcalls). - * Also, IrDA drivers needs to be initialised *after* the random number - * generator (main stack and higher layer init don't need it anymore). - * - * Jean II - */ -device_initcall(irda_init); -module_exit(irda_cleanup); - -MODULE_AUTHOR("Dag Brattli & Jean Tourrilhes "); -MODULE_DESCRIPTION("The Linux IrDA Protocol Stack"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_IRDA); diff --git a/drivers/staging/irda/net/irnet/Kconfig b/drivers/staging/irda/net/irnet/Kconfig deleted file mode 100644 index 28c557f0fdd2..000000000000 --- a/drivers/staging/irda/net/irnet/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -config IRNET - tristate "IrNET protocol" - depends on IRDA && PPP - help - Say Y here if you want to build support for the IrNET protocol. - To compile it as a module, choose M here: the module will be - called irnet. IrNET is a PPP driver, so you will also need a - working PPP subsystem (driver, daemon and config)... - - IrNET is an alternate way to transfer TCP/IP traffic over IrDA. It - uses synchronous PPP over a set of point to point IrDA sockets. You - can use it between Linux machine or with W2k. - diff --git a/drivers/staging/irda/net/irnet/Makefile b/drivers/staging/irda/net/irnet/Makefile deleted file mode 100644 index 61c365c8a2a0..000000000000 --- a/drivers/staging/irda/net/irnet/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux IrDA IrNET protocol layer. -# - -obj-$(CONFIG_IRNET) += irnet.o - -irnet-y := irnet_ppp.o irnet_irda.o diff --git a/drivers/staging/irda/net/irnet/irnet.h b/drivers/staging/irda/net/irnet/irnet.h deleted file mode 100644 index 9d451f8ed47a..000000000000 --- a/drivers/staging/irda/net/irnet/irnet.h +++ /dev/null @@ -1,522 +0,0 @@ -/* - * IrNET protocol module : Synchronous PPP over an IrDA socket. - * - * Jean II - HPL `00 - - * - * This file contains definitions and declarations global to the IrNET module, - * all grouped in one place... - * This file is a *private* header, so other modules don't want to know - * what's in there... - * - * Note : as most part of the Linux kernel, this module is available - * under the GNU General Public License (GPL). - */ - -#ifndef IRNET_H -#define IRNET_H - -/************************** DOCUMENTATION ***************************/ -/* - * What is IrNET - * ------------- - * IrNET is a protocol allowing to carry TCP/IP traffic between two - * IrDA peers in an efficient fashion. It is a thin layer, passing PPP - * packets to IrTTP and vice versa. It uses PPP in synchronous mode, - * because IrTTP offer a reliable sequenced packet service (as opposed - * to a byte stream). In fact, you could see IrNET as carrying TCP/IP - * in a IrDA socket, using PPP to provide the glue. - * - * The main difference with traditional PPP over IrCOMM is that we - * avoid the framing and serial emulation which are a performance - * bottleneck. It also allows multipoint communications in a sensible - * fashion. - * - * The main difference with IrLAN is that we use PPP for the link - * management, which is more standard, interoperable and flexible than - * the IrLAN protocol. For example, PPP adds authentication, - * encryption, compression, header compression and automated routing - * setup. And, as IrNET let PPP do the hard work, the implementation - * is much simpler than IrLAN. - * - * The Linux implementation - * ------------------------ - * IrNET is written on top of the Linux-IrDA stack, and interface with - * the generic Linux PPP driver. Because IrNET depend on recent - * changes of the PPP driver interface, IrNET will work only with very - * recent kernel (2.3.99-pre6 and up). - * - * The present implementation offer the following features : - * o simple user interface using pppd - * o efficient implementation (interface directly to PPP and IrTTP) - * o addressing (you can specify the name of the IrNET recipient) - * o multipoint operation (limited by IrLAP specification) - * o information in /proc/net/irda/irnet - * o IrNET events on /dev/irnet (for user space daemon) - * o IrNET daemon (irnetd) to automatically handle incoming requests - * o Windows 2000 compatibility (tested, but need more work) - * Currently missing : - * o Lot's of testing (that's your job) - * o Connection retries (may be too hard to do) - * o Check pppd persist mode - * o User space daemon (to automatically handle incoming requests) - * - * The setup is not currently the most easy, but this should get much - * better when everything will get integrated... - * - * Acknowledgements - * ---------------- - * This module is based on : - * o The PPP driver (ppp_synctty/ppp_generic) by Paul Mackerras - * o The IrLAN protocol (irlan_common/XXX) by Dag Brattli - * o The IrSock interface (af_irda) by Dag Brattli - * o Some other bits from the kernel and my drivers... - * Infinite thanks to those brave souls for providing the infrastructure - * upon which IrNET is built. - * - * Thanks to all my colleagues in HP for helping me. In particular, - * thanks to Salil Pradhan and Bill Serra for W2k testing... - * Thanks to Luiz Magalhaes for irnetd and much testing... - * - * Thanks to Alan Cox for answering lot's of my stupid questions, and - * to Paul Mackerras answering my questions on how to best integrate - * IrNET and pppd. - * - * Jean II - * - * Note on some implementations choices... - * ------------------------------------ - * 1) Direct interface vs tty/socket - * I could have used a tty interface to hook to ppp and use the full - * socket API to connect to IrDA. The code would have been easier to - * maintain, and maybe the code would have been smaller... - * Instead, we hook directly to ppp_generic and to IrTTP, which make - * things more complicated... - * - * The first reason is flexibility : this allow us to create IrNET - * instances on demand (no /dev/ircommX crap) and to allow linkname - * specification on pppd command line... - * - * Second reason is speed optimisation. If you look closely at the - * transmit and receive paths, you will notice that they are "super lean" - * (that's why they look ugly), with no function calls and as little data - * copy and modification as I could... - * - * 2) irnetd in user space - * irnetd is implemented in user space, which is necessary to call pppd. - * This also give maximum benefits in term of flexibility and customability, - * and allow to offer the event channel, useful for other stuff like debug. - * - * On the other hand, this require a loose coordination between the - * present module and irnetd. One critical area is how incoming request - * are handled. - * When irnet receive an incoming request, it send an event to irnetd and - * drop the incoming IrNET socket. - * irnetd start a pppd instance, which create a new IrNET socket. This new - * socket is then connected in the originating node to the pppd instance. - * At this point, in the originating node, the first socket is closed. - * - * I admit, this is a bit messy and waste some resources. The alternative - * is caching incoming socket, and that's also quite messy and waste - * resources. - * We also make connection time slower. For example, on a 115 kb/s link it - * adds 60ms to the connection time (770 ms). However, this is slower than - * the time it takes to fire up pppd on my P133... - * - * - * History : - * ------- - * - * v1 - 15.5.00 - Jean II - * o Basic IrNET (hook to ppp_generic & IrTTP - incl. multipoint) - * o control channel on /dev/irnet (set name/address) - * o event channel on /dev/irnet (for user space daemon) - * - * v2 - 5.6.00 - Jean II - * o Enable DROP_NOT_READY to avoid PPP timeouts & other weirdness... - * o Add DISCONNECT_TO event and rename DISCONNECT_FROM. - * o Set official device number alloaction on /dev/irnet - * - * v3 - 30.8.00 - Jean II - * o Update to latest Linux-IrDA changes : - * - queue_t => irda_queue_t - * o Update to ppp-2.4.0 : - * - move irda_irnet_connect from PPPIOCATTACH to TIOCSETD - * o Add EXPIRE event (depend on new IrDA-Linux patch) - * o Switch from `hashbin_remove' to `hashbin_remove_this' to fix - * a multilink bug... (depend on new IrDA-Linux patch) - * o fix a self->daddr to self->raddr in irda_irnet_connect to fix - * another multilink bug (darn !) - * o Remove LINKNAME_IOCTL cruft - * - * v3b - 31.8.00 - Jean II - * o Dump discovery log at event channel startup - * - * v4 - 28.9.00 - Jean II - * o Fix interaction between poll/select and dump discovery log - * o Add IRNET_BLOCKED_LINK event (depend on new IrDA-Linux patch) - * o Add IRNET_NOANSWER_FROM event (mostly to help support) - * o Release flow control in disconnect_indication - * o Block packets while connecting (speed up connections) - * - * v5 - 11.01.01 - Jean II - * o Init self->max_header_size, just in case... - * o Set up ap->chan.hdrlen, to get zero copy on tx side working. - * o avoid tx->ttp->flow->ppp->tx->... loop, by checking flow state - * Thanks to Christian Gennerat for finding this bug ! - * --- - * o Declare the proper MTU/MRU that we can support - * (but PPP doesn't read the MTU value :-() - * o Declare hashbin HB_NOLOCK instead of HB_LOCAL to avoid - * disabling and enabling irq twice - * - * v6 - 31.05.01 - Jean II - * o Print source address in Found, Discovery, Expiry & Request events - * o Print requested source address in /proc/net/irnet - * o Change control channel input. Allow multiple commands in one line. - * o Add saddr command to change ap->rsaddr (and use that in IrDA) - * --- - * o Make the IrDA connection procedure totally asynchronous. - * Heavy rewrite of the IAS query code and the whole connection - * procedure. Now, irnet_connect() no longer need to be called from - * a process context... - * o Enable IrDA connect retries in ppp_irnet_send(). The good thing - * is that IrDA connect retries are directly driven by PPP LCP - * retries (we retry for each LCP packet), so that everything - * is transparently controlled from pppd lcp-max-configure. - * o Add ttp_connect flag to prevent rentry on the connect procedure - * o Test and fixups to eliminate side effects of retries - * - * v7 - 22.08.01 - Jean II - * o Cleanup : Change "saddr = 0x0" to "saddr = DEV_ADDR_ANY" - * o Fix bug in BLOCK_WHEN_CONNECT introduced in v6 : due to the - * asynchronous IAS query, self->tsap is NULL when PPP send the - * first packet. This was preventing "connect-delay 0" to work. - * Change the test in ppp_irnet_send() to self->ttp_connect. - * - * v8 - 1.11.01 - Jean II - * o Tighten the use of self->ttp_connect and self->ttp_open to - * prevent various race conditions. - * o Avoid leaking discovery log and skb - * o Replace "self" with "server" in irnet_connect_indication() to - * better detect cut'n'paste error ;-) - * - * v9 - 29.11.01 - Jean II - * o Fix event generation in disconnect indication that I broke in v8 - * It was always generation "No-Answer" because I was testing ttp_open - * just after clearing it. *blush*. - * o Use newly created irttp_listen() to fix potential crash when LAP - * destroyed before irnet module removed. - * - * v10 - 4.3.2 - Jean II - * o When receiving a disconnect indication, don't reenable the - * PPP Tx queue, this will trigger a reconnect. Instead, close - * the channel, which will kill pppd... - * - * v11 - 20.3.02 - Jean II - * o Oops ! v10 fix disabled IrNET retries and passive behaviour. - * Better fix in irnet_disconnect_indication() : - * - if connected, kill pppd via hangup. - * - if not connected, reenable ppp Tx, which trigger IrNET retry. - * - * v12 - 10.4.02 - Jean II - * o Fix race condition in irnet_connect_indication(). - * If the socket was already trying to connect, drop old connection - * and use new one only if acting as primary. See comments. - * - * v13 - 30.5.02 - Jean II - * o Update module init code - * - * v14 - 20.2.03 - Jean II - * o Add discovery hint bits in the control channel. - * o Remove obsolete MOD_INC/DEC_USE_COUNT in favor of .owner - * - * v15 - 7.4.03 - Jean II - * o Replace spin_lock_irqsave() with spin_lock_bh() so that we can - * use ppp_unit_number(). It's probably also better overall... - * o Disable call to ppp_unregister_channel(), because we can't do it. - */ - -/***************************** INCLUDES *****************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include /* isspace() */ -#include /* skip_spaces() */ -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/***************************** OPTIONS *****************************/ -/* - * Define or undefine to compile or not some optional part of the - * IrNET driver... - * Note : the present defaults make sense, play with that at your - * own risk... - */ -/* IrDA side of the business... */ -#define DISCOVERY_NOMASK /* To enable W2k compatibility... */ -#define ADVERTISE_HINT /* Advertise IrLAN hint bit */ -#define ALLOW_SIMULT_CONNECT /* This seem to work, cross fingers... */ -#define DISCOVERY_EVENTS /* Query the discovery log to post events */ -#define INITIAL_DISCOVERY /* Dump current discovery log as events */ -#undef STREAM_COMPAT /* Not needed - potentially messy */ -#undef CONNECT_INDIC_KICK /* Might mess IrDA, not needed */ -#undef FAIL_SEND_DISCONNECT /* Might mess IrDA, not needed */ -#undef PASS_CONNECT_PACKETS /* Not needed ? Safe */ -#undef MISSING_PPP_API /* Stuff I wish I could do */ - -/* PPP side of the business */ -#define BLOCK_WHEN_CONNECT /* Block packets when connecting */ -#define CONNECT_IN_SEND /* Retry IrDA connection procedure */ -#undef FLUSH_TO_PPP /* Not sure about this one, let's play safe */ -#undef SECURE_DEVIRNET /* Bah... */ - -/****************************** DEBUG ******************************/ - -/* - * This set of flags enable and disable all the various warning, - * error and debug message of this driver. - * Each section can be enabled and disabled independently - */ -/* In the PPP part */ -#define DEBUG_CTRL_TRACE 0 /* Control channel */ -#define DEBUG_CTRL_INFO 0 /* various info */ -#define DEBUG_CTRL_ERROR 1 /* problems */ -#define DEBUG_FS_TRACE 0 /* filesystem callbacks */ -#define DEBUG_FS_INFO 0 /* various info */ -#define DEBUG_FS_ERROR 1 /* problems */ -#define DEBUG_PPP_TRACE 0 /* PPP related functions */ -#define DEBUG_PPP_INFO 0 /* various info */ -#define DEBUG_PPP_ERROR 1 /* problems */ -#define DEBUG_MODULE_TRACE 0 /* module insertion/removal */ -#define DEBUG_MODULE_ERROR 1 /* problems */ - -/* In the IrDA part */ -#define DEBUG_IRDA_SR_TRACE 0 /* IRDA subroutines */ -#define DEBUG_IRDA_SR_INFO 0 /* various info */ -#define DEBUG_IRDA_SR_ERROR 1 /* problems */ -#define DEBUG_IRDA_SOCK_TRACE 0 /* IRDA main socket functions */ -#define DEBUG_IRDA_SOCK_INFO 0 /* various info */ -#define DEBUG_IRDA_SOCK_ERROR 1 /* problems */ -#define DEBUG_IRDA_SERV_TRACE 0 /* The IrNET server */ -#define DEBUG_IRDA_SERV_INFO 0 /* various info */ -#define DEBUG_IRDA_SERV_ERROR 1 /* problems */ -#define DEBUG_IRDA_TCB_TRACE 0 /* IRDA IrTTP callbacks */ -#define DEBUG_IRDA_CB_INFO 0 /* various info */ -#define DEBUG_IRDA_CB_ERROR 1 /* problems */ -#define DEBUG_IRDA_OCB_TRACE 0 /* IRDA other callbacks */ -#define DEBUG_IRDA_OCB_INFO 0 /* various info */ -#define DEBUG_IRDA_OCB_ERROR 1 /* problems */ - -#define DEBUG_ASSERT 0 /* Verify all assertions */ - -/* - * These are the macros we are using to actually print the debug - * statements. Don't look at it, it's ugly... - * - * One of the trick is that, as the DEBUG_XXX are constant, the - * compiler will optimise away the if() in all cases. - */ -/* All error messages (will show up in the normal logs) */ -#define DERROR(dbg, format, args...) \ - {if(DEBUG_##dbg) \ - printk(KERN_INFO "irnet: %s(): " format, __func__ , ##args);} - -/* Normal debug message (will show up in /var/log/debug) */ -#define DEBUG(dbg, format, args...) \ - {if(DEBUG_##dbg) \ - printk(KERN_DEBUG "irnet: %s(): " format, __func__ , ##args);} - -/* Entering a function (trace) */ -#define DENTER(dbg, format, args...) \ - {if(DEBUG_##dbg) \ - printk(KERN_DEBUG "irnet: -> %s" format, __func__ , ##args);} - -/* Entering and exiting a function in one go (trace) */ -#define DPASS(dbg, format, args...) \ - {if(DEBUG_##dbg) \ - printk(KERN_DEBUG "irnet: <>%s" format, __func__ , ##args);} - -/* Exiting a function (trace) */ -#define DEXIT(dbg, format, args...) \ - {if(DEBUG_##dbg) \ - printk(KERN_DEBUG "irnet: <-%s()" format, __func__ , ##args);} - -/* Exit a function with debug */ -#define DRETURN(ret, dbg, args...) \ - {DEXIT(dbg, ": " args);\ - return ret; } - -/* Exit a function on failed condition */ -#define DABORT(cond, ret, dbg, args...) \ - {if(cond) {\ - DERROR(dbg, args);\ - return ret; }} - -/* Invalid assertion, print out an error and exit... */ -#define DASSERT(cond, ret, dbg, args...) \ - {if((DEBUG_ASSERT) && !(cond)) {\ - DERROR(dbg, "Invalid assertion: " args);\ - return ret; }} - -/************************ CONSTANTS & MACROS ************************/ - -/* Paranoia */ -#define IRNET_MAGIC 0xB00754 - -/* Number of control events in the control channel buffer... */ -#define IRNET_MAX_EVENTS 8 /* Should be more than enough... */ - -/****************************** TYPES ******************************/ - -/* - * This is the main structure where we store all the data pertaining to - * one instance of irnet. - * Note : in irnet functions, a pointer this structure is usually called - * "ap" or "self". If the code is borrowed from the IrDA stack, it tend - * to be called "self", and if it is borrowed from the PPP driver it is - * "ap". Apart from that, it's exactly the same structure ;-) - */ -typedef struct irnet_socket -{ - /* ------------------- Instance management ------------------- */ - /* We manage a linked list of IrNET socket instances */ - irda_queue_t q; /* Must be first - for hasbin */ - int magic; /* Paranoia */ - - /* --------------------- FileSystem part --------------------- */ - /* "pppd" interact directly with us on a /dev/ file */ - struct file * file; /* File descriptor of this instance */ - /* TTY stuff - to keep "pppd" happy */ - struct ktermios termios; /* Various tty flags */ - /* Stuff for the control channel */ - int event_index; /* Last read in the event log */ - - /* ------------------------- PPP part ------------------------- */ - /* We interface directly to the ppp_generic driver in the kernel */ - int ppp_open; /* registered with ppp_generic */ - struct ppp_channel chan; /* Interface to generic ppp layer */ - - int mru; /* Max size of PPP payload */ - u32 xaccm[8]; /* Asynchronous character map (just */ - u32 raccm; /* to please pppd - dummy) */ - unsigned int flags; /* PPP flags (compression, ...) */ - unsigned int rbits; /* Unused receive flags ??? */ - struct work_struct disconnect_work; /* Process context disconnection */ - /* ------------------------ IrTTP part ------------------------ */ - /* We create a pseudo "socket" over the IrDA tranport */ - unsigned long ttp_open; /* Set when IrTTP is ready */ - unsigned long ttp_connect; /* Set when IrTTP is connecting */ - struct tsap_cb * tsap; /* IrTTP instance (the connection) */ - - char rname[NICKNAME_MAX_LEN + 1]; - /* IrDA nickname of destination */ - __u32 rdaddr; /* Requested peer IrDA address */ - __u32 rsaddr; /* Requested local IrDA address */ - __u32 daddr; /* actual peer IrDA address */ - __u32 saddr; /* my local IrDA address */ - __u8 dtsap_sel; /* Remote TSAP selector */ - __u8 stsap_sel; /* Local TSAP selector */ - - __u32 max_sdu_size_rx;/* Socket parameters used for IrTTP */ - __u32 max_sdu_size_tx; - __u32 max_data_size; - __u8 max_header_size; - LOCAL_FLOW tx_flow; /* State of the Tx path in IrTTP */ - - /* ------------------- IrLMP and IrIAS part ------------------- */ - /* Used for IrDA Discovery and socket name resolution */ - void * ckey; /* IrLMP client handle */ - __u16 mask; /* Hint bits mask (filter discov.)*/ - int nslots; /* Number of slots for discovery */ - - struct iriap_cb * iriap; /* Used to query remote IAS */ - int errno; /* status of the IAS query */ - - /* -------------------- Discovery log part -------------------- */ - /* Used by initial discovery on the control channel - * and by irnet_discover_daddr_and_lsap_sel() */ - struct irda_device_info *discoveries; /* Copy of the discovery log */ - int disco_index; /* Last read in the discovery log */ - int disco_number; /* Size of the discovery log */ - - struct mutex lock; - -} irnet_socket; - -/* - * This is the various event that we will generate on the control channel - */ -typedef enum irnet_event -{ - IRNET_DISCOVER, /* New IrNET node discovered */ - IRNET_EXPIRE, /* IrNET node expired */ - IRNET_CONNECT_TO, /* IrNET socket has connected to other node */ - IRNET_CONNECT_FROM, /* Other node has connected to IrNET socket */ - IRNET_REQUEST_FROM, /* Non satisfied connection request */ - IRNET_NOANSWER_FROM, /* Failed connection request */ - IRNET_BLOCKED_LINK, /* Link (IrLAP) is blocked for > 3s */ - IRNET_DISCONNECT_FROM, /* IrNET socket has disconnected */ - IRNET_DISCONNECT_TO /* Closing IrNET socket */ -} irnet_event; - -/* - * This is the storage for an event and its arguments - */ -typedef struct irnet_log -{ - irnet_event event; - int unit; - __u32 saddr; - __u32 daddr; - char name[NICKNAME_MAX_LEN + 1]; /* 21 + 1 */ - __u16_host_order hints; /* Discovery hint bits */ -} irnet_log; - -/* - * This is the storage for all events and related stuff... - */ -typedef struct irnet_ctrl_channel -{ - irnet_log log[IRNET_MAX_EVENTS]; /* Event log */ - int index; /* Current index in log */ - spinlock_t spinlock; /* Serialize access to the event log */ - wait_queue_head_t rwait; /* processes blocked on read (or poll) */ -} irnet_ctrl_channel; - -/**************************** PROTOTYPES ****************************/ -/* - * Global functions of the IrNET module - * Note : we list here also functions called from one file to the other. - */ - -/* -------------------------- IRDA PART -------------------------- */ -int irda_irnet_create(irnet_socket *); /* Initialise an IrNET socket */ -int irda_irnet_connect(irnet_socket *); /* Try to connect over IrDA */ -void irda_irnet_destroy(irnet_socket *); /* Teardown an IrNET socket */ -int irda_irnet_init(void); /* Initialise IrDA part of IrNET */ -void irda_irnet_cleanup(void); /* Teardown IrDA part of IrNET */ - -/**************************** VARIABLES ****************************/ - -/* Control channel stuff - allocated in irnet_irda.h */ -extern struct irnet_ctrl_channel irnet_events; - -#endif /* IRNET_H */ diff --git a/drivers/staging/irda/net/irnet/irnet_irda.c b/drivers/staging/irda/net/irnet/irnet_irda.c deleted file mode 100644 index e390bceeb2f8..000000000000 --- a/drivers/staging/irda/net/irnet/irnet_irda.c +++ /dev/null @@ -1,1885 +0,0 @@ -/* - * IrNET protocol module : Synchronous PPP over an IrDA socket. - * - * Jean II - HPL `00 - - * - * This file implement the IRDA interface of IrNET. - * Basically, we sit on top of IrTTP. We set up IrTTP, IrIAS properly, - * and exchange frames with IrTTP. - */ - -#include "irnet_irda.h" /* Private header */ -#include -#include -#include -#include - -/* - * PPP disconnect work: we need to make sure we're in - * process context when calling ppp_unregister_channel(). - */ -static void irnet_ppp_disconnect(struct work_struct *work) -{ - irnet_socket * self = - container_of(work, irnet_socket, disconnect_work); - - if (self == NULL) - return; - /* - * If we were connected, cleanup & close the PPP - * channel, which will kill pppd (hangup) and the rest. - */ - if (self->ppp_open && !self->ttp_open && !self->ttp_connect) { - ppp_unregister_channel(&self->chan); - self->ppp_open = 0; - } -} - -/************************* CONTROL CHANNEL *************************/ -/* - * When ppp is not active, /dev/irnet act as a control channel. - * Writing allow to set up the IrDA destination of the IrNET channel, - * and any application may be read events happening on IrNET... - */ - -/*------------------------------------------------------------------*/ -/* - * Post an event to the control channel... - * Put the event in the log, and then wait all process blocked on read - * so they can read the log... - */ -static void -irnet_post_event(irnet_socket * ap, - irnet_event event, - __u32 saddr, - __u32 daddr, - char * name, - __u16 hints) -{ - int index; /* In the log */ - - DENTER(CTRL_TRACE, "(ap=0x%p, event=%d, daddr=%08x, name=``%s'')\n", - ap, event, daddr, name); - - /* Protect this section via spinlock. - * Note : as we are the only event producer, we only need to exclude - * ourself when touching the log, which is nice and easy. - */ - spin_lock_bh(&irnet_events.spinlock); - - /* Copy the event in the log */ - index = irnet_events.index; - irnet_events.log[index].event = event; - irnet_events.log[index].daddr = daddr; - irnet_events.log[index].saddr = saddr; - /* Try to copy IrDA nickname */ - if(name) - strcpy(irnet_events.log[index].name, name); - else - irnet_events.log[index].name[0] = '\0'; - /* Copy hints */ - irnet_events.log[index].hints.word = hints; - /* Try to get ppp unit number */ - if((ap != (irnet_socket *) NULL) && (ap->ppp_open)) - irnet_events.log[index].unit = ppp_unit_number(&ap->chan); - else - irnet_events.log[index].unit = -1; - - /* Increment the index - * Note that we increment the index only after the event is written, - * to make sure that the readers don't get garbage... */ - irnet_events.index = (index + 1) % IRNET_MAX_EVENTS; - - DEBUG(CTRL_INFO, "New event index is %d\n", irnet_events.index); - - /* Spin lock end */ - spin_unlock_bh(&irnet_events.spinlock); - - /* Now : wake up everybody waiting for events... */ - wake_up_interruptible_all(&irnet_events.rwait); - - DEXIT(CTRL_TRACE, "\n"); -} - -/************************* IRDA SUBROUTINES *************************/ -/* - * These are a bunch of subroutines called from other functions - * down there, mostly common code or to improve readability... - * - * Note : we duplicate quite heavily some routines of af_irda.c, - * because our input structure (self) is quite different - * (struct irnet instead of struct irda_sock), which make sharing - * the same code impossible (at least, without templates). - */ - -/*------------------------------------------------------------------*/ -/* - * Function irda_open_tsap (self) - * - * Open local Transport Service Access Point (TSAP) - * - * Create a IrTTP instance for us and set all the IrTTP callbacks. - */ -static inline int -irnet_open_tsap(irnet_socket * self) -{ - notify_t notify; /* Callback structure */ - - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - DABORT(self->tsap != NULL, -EBUSY, IRDA_SR_ERROR, "Already busy !\n"); - - /* Initialize IrTTP callbacks to be used by the IrDA stack */ - irda_notify_init(¬ify); - notify.connect_confirm = irnet_connect_confirm; - notify.connect_indication = irnet_connect_indication; - notify.disconnect_indication = irnet_disconnect_indication; - notify.data_indication = irnet_data_indication; - /*notify.udata_indication = NULL;*/ - notify.flow_indication = irnet_flow_indication; - notify.status_indication = irnet_status_indication; - notify.instance = self; - strlcpy(notify.name, IRNET_NOTIFY_NAME, sizeof(notify.name)); - - /* Open an IrTTP instance */ - self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, - ¬ify); - DABORT(self->tsap == NULL, -ENOMEM, - IRDA_SR_ERROR, "Unable to allocate TSAP !\n"); - - /* Remember which TSAP selector we actually got */ - self->stsap_sel = self->tsap->stsap_sel; - - DEXIT(IRDA_SR_TRACE, " - tsap=0x%p, sel=0x%X\n", - self->tsap, self->stsap_sel); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_ias_to_tsap (self, result, value) - * - * Examine an IAS object and extract TSAP - * - * We do an IAP query to find the TSAP associated with the IrNET service. - * When IrIAP pass us the result of the query, this function look at - * the return values to check for failures and extract the TSAP if - * possible. - * Also deallocate value - * The failure is in self->errno - * Return TSAP or -1 - */ -static inline __u8 -irnet_ias_to_tsap(irnet_socket * self, - int result, - struct ias_value * value) -{ - __u8 dtsap_sel = 0; /* TSAP we are looking for */ - - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - /* By default, no error */ - self->errno = 0; - - /* Check if request succeeded */ - switch(result) - { - /* Standard errors : service not available */ - case IAS_CLASS_UNKNOWN: - case IAS_ATTRIB_UNKNOWN: - DEBUG(IRDA_SR_INFO, "IAS object doesn't exist ! (%d)\n", result); - self->errno = -EADDRNOTAVAIL; - break; - - /* Other errors, most likely IrDA stack failure */ - default : - DEBUG(IRDA_SR_INFO, "IAS query failed ! (%d)\n", result); - self->errno = -EHOSTUNREACH; - break; - - /* Success : we got what we wanted */ - case IAS_SUCCESS: - break; - } - - /* Check what was returned to us */ - if(value != NULL) - { - /* What type of argument have we got ? */ - switch(value->type) - { - case IAS_INTEGER: - DEBUG(IRDA_SR_INFO, "result=%d\n", value->t.integer); - if(value->t.integer != -1) - /* Get the remote TSAP selector */ - dtsap_sel = value->t.integer; - else - self->errno = -EADDRNOTAVAIL; - break; - default: - self->errno = -EADDRNOTAVAIL; - DERROR(IRDA_SR_ERROR, "bad type ! (0x%X)\n", value->type); - break; - } - - /* Cleanup */ - irias_delete_value(value); - } - else /* value == NULL */ - { - /* Nothing returned to us - usually result != SUCCESS */ - if(!(self->errno)) - { - DERROR(IRDA_SR_ERROR, - "IrDA bug : result == SUCCESS && value == NULL\n"); - self->errno = -EHOSTUNREACH; - } - } - DEXIT(IRDA_SR_TRACE, "\n"); - - /* Return the TSAP */ - return dtsap_sel; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_find_lsap_sel (self) - * - * Try to lookup LSAP selector in remote LM-IAS - * - * Basically, we start a IAP query, and then go to sleep. When the query - * return, irnet_getvalue_confirm will wake us up, and we can examine the - * result of the query... - * Note that in some case, the query fail even before we go to sleep, - * creating some races... - */ -static inline int -irnet_find_lsap_sel(irnet_socket * self) -{ - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - /* This should not happen */ - DABORT(self->iriap, -EBUSY, IRDA_SR_ERROR, "busy with a previous query.\n"); - - /* Create an IAP instance, will be closed in irnet_getvalue_confirm() */ - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - irnet_getvalue_confirm); - - /* Treat unexpected signals as disconnect */ - self->errno = -EHOSTUNREACH; - - /* Query remote LM-IAS */ - iriap_getvaluebyclass_request(self->iriap, self->rsaddr, self->daddr, - IRNET_SERVICE_NAME, IRNET_IAS_VALUE); - - /* The above request is non-blocking. - * After a while, IrDA will call us back in irnet_getvalue_confirm() - * We will then call irnet_ias_to_tsap() and finish the - * connection procedure */ - - DEXIT(IRDA_SR_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_connect_tsap (self) - * - * Initialise the TTP socket and initiate TTP connection - * - */ -static inline int -irnet_connect_tsap(irnet_socket * self) -{ - int err; - - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - /* Open a local TSAP (an IrTTP instance) */ - err = irnet_open_tsap(self); - if(err != 0) - { - clear_bit(0, &self->ttp_connect); - DERROR(IRDA_SR_ERROR, "connect aborted!\n"); - return err; - } - - /* Connect to remote device */ - err = irttp_connect_request(self->tsap, self->dtsap_sel, - self->rsaddr, self->daddr, NULL, - self->max_sdu_size_rx, NULL); - if(err != 0) - { - clear_bit(0, &self->ttp_connect); - DERROR(IRDA_SR_ERROR, "connect aborted!\n"); - return err; - } - - /* The above call is non-blocking. - * After a while, the IrDA stack will either call us back in - * irnet_connect_confirm() or irnet_disconnect_indication() - * See you there ;-) */ - - DEXIT(IRDA_SR_TRACE, "\n"); - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_discover_next_daddr (self) - * - * Query the IrNET TSAP of the next device in the log. - * - * Used in the TSAP discovery procedure. - */ -static inline int -irnet_discover_next_daddr(irnet_socket * self) -{ - /* Close the last instance of IrIAP, and open a new one. - * We can't reuse the IrIAP instance in the IrIAP callback */ - if(self->iriap) - { - iriap_close(self->iriap); - self->iriap = NULL; - } - /* Create a new IAP instance */ - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - irnet_discovervalue_confirm); - if(self->iriap == NULL) - return -ENOMEM; - - /* Next discovery - before the call to avoid races */ - self->disco_index++; - - /* Check if we have one more address to try */ - if(self->disco_index < self->disco_number) - { - /* Query remote LM-IAS */ - iriap_getvaluebyclass_request(self->iriap, - self->discoveries[self->disco_index].saddr, - self->discoveries[self->disco_index].daddr, - IRNET_SERVICE_NAME, IRNET_IAS_VALUE); - /* The above request is non-blocking. - * After a while, IrDA will call us back in irnet_discovervalue_confirm() - * We will then call irnet_ias_to_tsap() and come back here again... */ - return 0; - } - else - return 1; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_discover_daddr_and_lsap_sel (self) - * - * This try to find a device with the requested service. - * - * Initiate a TSAP discovery procedure. - * It basically look into the discovery log. For each address in the list, - * it queries the LM-IAS of the device to find if this device offer - * the requested service. - * If there is more than one node supporting the service, we complain - * to the user (it should move devices around). - * If we find one node which have the requested TSAP, we connect to it. - * - * This function just start the whole procedure. It request the discovery - * log and submit the first IAS query. - * The bulk of the job is handled in irnet_discovervalue_confirm() - * - * Note : this procedure fails if there is more than one device in range - * on the same dongle, because IrLMP doesn't disconnect the LAP when the - * last LSAP is closed. Moreover, we would need to wait the LAP - * disconnection... - */ -static inline int -irnet_discover_daddr_and_lsap_sel(irnet_socket * self) -{ - int ret; - - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - /* Ask lmp for the current discovery log */ - self->discoveries = irlmp_get_discoveries(&self->disco_number, self->mask, - DISCOVERY_DEFAULT_SLOTS); - - /* Check if the we got some results */ - if(self->discoveries == NULL) - { - self->disco_number = -1; - clear_bit(0, &self->ttp_connect); - DRETURN(-ENETUNREACH, IRDA_SR_INFO, "No Cachelog...\n"); - } - DEBUG(IRDA_SR_INFO, "Got the log (0x%p), size is %d\n", - self->discoveries, self->disco_number); - - /* Start with the first discovery */ - self->disco_index = -1; - self->daddr = DEV_ADDR_ANY; - - /* This will fail if the log is empty - this is non-blocking */ - ret = irnet_discover_next_daddr(self); - if(ret) - { - /* Close IAP */ - if(self->iriap) - iriap_close(self->iriap); - self->iriap = NULL; - - /* Cleanup our copy of the discovery log */ - kfree(self->discoveries); - self->discoveries = NULL; - - clear_bit(0, &self->ttp_connect); - DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n"); - } - - /* Follow me in irnet_discovervalue_confirm() */ - - DEXIT(IRDA_SR_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_dname_to_daddr (self) - * - * Convert an IrDA nickname to a valid IrDA address - * - * It basically look into the discovery log until there is a match. - */ -static inline int -irnet_dname_to_daddr(irnet_socket * self) -{ - struct irda_device_info *discoveries; /* Copy of the discovery log */ - int number; /* Number of nodes in the log */ - int i; - - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - /* Ask lmp for the current discovery log */ - discoveries = irlmp_get_discoveries(&number, 0xffff, - DISCOVERY_DEFAULT_SLOTS); - /* Check if the we got some results */ - if(discoveries == NULL) - DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n"); - - /* - * Now, check all discovered devices (if any), and connect - * client only about the services that the client is - * interested in... - */ - for(i = 0; i < number; i++) - { - /* Does the name match ? */ - if(!strncmp(discoveries[i].info, self->rname, NICKNAME_MAX_LEN)) - { - /* Yes !!! Get it.. */ - self->daddr = discoveries[i].daddr; - DEBUG(IRDA_SR_INFO, "discovered device ``%s'' at address 0x%08x.\n", - self->rname, self->daddr); - kfree(discoveries); - DEXIT(IRDA_SR_TRACE, "\n"); - return 0; - } - } - /* No luck ! */ - DEBUG(IRDA_SR_INFO, "cannot discover device ``%s'' !!!\n", self->rname); - kfree(discoveries); - return -EADDRNOTAVAIL; -} - - -/************************* SOCKET ROUTINES *************************/ -/* - * This are the main operations on IrNET sockets, basically to create - * and destroy IrNET sockets. These are called from the PPP part... - */ - -/*------------------------------------------------------------------*/ -/* - * Create a IrNET instance : just initialise some parameters... - */ -int -irda_irnet_create(irnet_socket * self) -{ - DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self); - - self->magic = IRNET_MAGIC; /* Paranoia */ - - self->ttp_open = 0; /* Prevent higher layer from accessing IrTTP */ - self->ttp_connect = 0; /* Not connecting yet */ - self->rname[0] = '\0'; /* May be set via control channel */ - self->rdaddr = DEV_ADDR_ANY; /* May be set via control channel */ - self->rsaddr = DEV_ADDR_ANY; /* May be set via control channel */ - self->daddr = DEV_ADDR_ANY; /* Until we get connected */ - self->saddr = DEV_ADDR_ANY; /* Until we get connected */ - self->max_sdu_size_rx = TTP_SAR_UNBOUND; - - /* Register as a client with IrLMP */ - self->ckey = irlmp_register_client(0, NULL, NULL, NULL); -#ifdef DISCOVERY_NOMASK - self->mask = 0xffff; /* For W2k compatibility */ -#else /* DISCOVERY_NOMASK */ - self->mask = irlmp_service_to_hint(S_LAN); -#endif /* DISCOVERY_NOMASK */ - self->tx_flow = FLOW_START; /* Flow control from IrTTP */ - - INIT_WORK(&self->disconnect_work, irnet_ppp_disconnect); - - DEXIT(IRDA_SOCK_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Connect to the other side : - * o convert device name to an address - * o find the socket number (dlsap) - * o Establish the connection - * - * Note : We no longer mimic af_irda. The IAS query for finding the TSAP - * is done asynchronously, like the TTP connection. This allow us to - * call this function from any context (not only process). - * The downside is that following what's happening in there is tricky - * because it involve various functions all over the place... - */ -int -irda_irnet_connect(irnet_socket * self) -{ - int err; - - DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self); - - /* Check if we are already trying to connect. - * Because irda_irnet_connect() can be called directly by pppd plus - * packet retries in ppp_generic and connect may take time, plus we may - * race with irnet_connect_indication(), we need to be careful there... */ - if(test_and_set_bit(0, &self->ttp_connect)) - DRETURN(-EBUSY, IRDA_SOCK_INFO, "Already connecting...\n"); - if((self->iriap != NULL) || (self->tsap != NULL)) - DERROR(IRDA_SOCK_ERROR, "Socket not cleaned up...\n"); - - /* Insert ourselves in the hashbin so that the IrNET server can find us. - * Notes : 4th arg is string of 32 char max and must be null terminated - * When 4th arg is used (string), 3rd arg isn't (int) - * Can't re-insert (MUST remove first) so check for that... */ - if((irnet_server.running) && (self->q.q_next == NULL)) - { - spin_lock_bh(&irnet_server.spinlock); - hashbin_insert(irnet_server.list, (irda_queue_t *) self, 0, self->rname); - spin_unlock_bh(&irnet_server.spinlock); - DEBUG(IRDA_SOCK_INFO, "Inserted ``%s'' in hashbin...\n", self->rname); - } - - /* If we don't have anything (no address, no name) */ - if((self->rdaddr == DEV_ADDR_ANY) && (self->rname[0] == '\0')) - { - /* Try to find a suitable address */ - if((err = irnet_discover_daddr_and_lsap_sel(self)) != 0) - DRETURN(err, IRDA_SOCK_INFO, "auto-connect failed!\n"); - /* In most cases, the call above is non-blocking */ - } - else - { - /* If we have only the name (no address), try to get an address */ - if(self->rdaddr == DEV_ADDR_ANY) - { - if((err = irnet_dname_to_daddr(self)) != 0) - DRETURN(err, IRDA_SOCK_INFO, "name connect failed!\n"); - } - else - /* Use the requested destination address */ - self->daddr = self->rdaddr; - - /* Query remote LM-IAS to find LSAP selector */ - irnet_find_lsap_sel(self); - /* The above call is non blocking */ - } - - /* At this point, we are waiting for the IrDA stack to call us back, - * or we have already failed. - * We will finish the connection procedure in irnet_connect_tsap(). - */ - DEXIT(IRDA_SOCK_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_irnet_destroy(self) - * - * Destroy irnet instance - * - * Note : this need to be called from a process context. - */ -void -irda_irnet_destroy(irnet_socket * self) -{ - DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self); - if(self == NULL) - return; - - /* Remove ourselves from hashbin (if we are queued in hashbin) - * Note : `irnet_server.running' protect us from calls in hashbin_delete() */ - if((irnet_server.running) && (self->q.q_next != NULL)) - { - struct irnet_socket * entry; - DEBUG(IRDA_SOCK_INFO, "Removing from hash..\n"); - spin_lock_bh(&irnet_server.spinlock); - entry = hashbin_remove_this(irnet_server.list, (irda_queue_t *) self); - self->q.q_next = NULL; - spin_unlock_bh(&irnet_server.spinlock); - DASSERT(entry == self, , IRDA_SOCK_ERROR, "Can't remove from hash.\n"); - } - - /* If we were connected, post a message */ - if(test_bit(0, &self->ttp_open)) - { - /* Note : as the disconnect comes from ppp_generic, the unit number - * doesn't exist anymore when we post the event, so we need to pass - * NULL as the first arg... */ - irnet_post_event(NULL, IRNET_DISCONNECT_TO, - self->saddr, self->daddr, self->rname, 0); - } - - /* Prevent various IrDA callbacks from messing up things - * Need to be first */ - clear_bit(0, &self->ttp_connect); - - /* Prevent higher layer from accessing IrTTP */ - clear_bit(0, &self->ttp_open); - - /* Unregister with IrLMP */ - irlmp_unregister_client(self->ckey); - - /* Unregister with LM-IAS */ - if(self->iriap) - { - iriap_close(self->iriap); - self->iriap = NULL; - } - - /* Cleanup eventual discoveries from connection attempt or control channel */ - if(self->discoveries != NULL) - { - /* Cleanup our copy of the discovery log */ - kfree(self->discoveries); - self->discoveries = NULL; - } - - /* Close our IrTTP connection */ - if(self->tsap) - { - DEBUG(IRDA_SOCK_INFO, "Closing our TTP connection.\n"); - irttp_disconnect_request(self->tsap, NULL, P_NORMAL); - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } - self->stsap_sel = 0; - - DEXIT(IRDA_SOCK_TRACE, "\n"); -} - - -/************************** SERVER SOCKET **************************/ -/* - * The IrNET service is composed of one server socket and a variable - * number of regular IrNET sockets. The server socket is supposed to - * handle incoming connections and redirect them to one IrNET sockets. - * It's a superset of the regular IrNET socket, but has a very distinct - * behaviour... - */ - -/*------------------------------------------------------------------*/ -/* - * Function irnet_daddr_to_dname (self) - * - * Convert an IrDA address to a IrDA nickname - * - * It basically look into the discovery log until there is a match. - */ -static inline int -irnet_daddr_to_dname(irnet_socket * self) -{ - struct irda_device_info *discoveries; /* Copy of the discovery log */ - int number; /* Number of nodes in the log */ - int i; - - DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self); - - /* Ask lmp for the current discovery log */ - discoveries = irlmp_get_discoveries(&number, 0xffff, - DISCOVERY_DEFAULT_SLOTS); - /* Check if the we got some results */ - if (discoveries == NULL) - DRETURN(-ENETUNREACH, IRDA_SERV_INFO, "Cachelog empty...\n"); - - /* Now, check all discovered devices (if any) */ - for(i = 0; i < number; i++) - { - /* Does the name match ? */ - if(discoveries[i].daddr == self->daddr) - { - /* Yes !!! Get it.. */ - strlcpy(self->rname, discoveries[i].info, sizeof(self->rname)); - self->rname[sizeof(self->rname) - 1] = '\0'; - DEBUG(IRDA_SERV_INFO, "Device 0x%08x is in fact ``%s''.\n", - self->daddr, self->rname); - kfree(discoveries); - DEXIT(IRDA_SERV_TRACE, "\n"); - return 0; - } - } - /* No luck ! */ - DEXIT(IRDA_SERV_INFO, ": cannot discover device 0x%08x !!!\n", self->daddr); - kfree(discoveries); - return -EADDRNOTAVAIL; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_find_socket (self) - * - * Find the correct IrNET socket - * - * Look into the list of IrNET sockets and finds one with the right - * properties... - */ -static inline irnet_socket * -irnet_find_socket(irnet_socket * self) -{ - irnet_socket * new = (irnet_socket *) NULL; - int err; - - DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self); - - /* Get the addresses of the requester */ - self->daddr = irttp_get_daddr(self->tsap); - self->saddr = irttp_get_saddr(self->tsap); - - /* Try to get the IrDA nickname of the requester */ - err = irnet_daddr_to_dname(self); - - /* Protect access to the instance list */ - spin_lock_bh(&irnet_server.spinlock); - - /* So now, try to get an socket having specifically - * requested that nickname */ - if(err == 0) - { - new = (irnet_socket *) hashbin_find(irnet_server.list, - 0, self->rname); - if(new) - DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches rname ``%s''.\n", - new, new->rname); - } - - /* If no name matches, try to find an socket by the destination address */ - /* It can be either the requested destination address (set via the - * control channel), or the current destination address if the - * socket is in the middle of a connection request */ - if(new == (irnet_socket *) NULL) - { - new = (irnet_socket *) hashbin_get_first(irnet_server.list); - while(new !=(irnet_socket *) NULL) - { - /* Does it have the same address ? */ - if((new->rdaddr == self->daddr) || (new->daddr == self->daddr)) - { - /* Yes !!! Get it.. */ - DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches daddr %#08x.\n", - new, self->daddr); - break; - } - new = (irnet_socket *) hashbin_get_next(irnet_server.list); - } - } - - /* If we don't have any socket, get the first unconnected socket */ - if(new == (irnet_socket *) NULL) - { - new = (irnet_socket *) hashbin_get_first(irnet_server.list); - while(new !=(irnet_socket *) NULL) - { - /* Is it available ? */ - if(!(test_bit(0, &new->ttp_open)) && (new->rdaddr == DEV_ADDR_ANY) && - (new->rname[0] == '\0') && (new->ppp_open)) - { - /* Yes !!! Get it.. */ - DEBUG(IRDA_SERV_INFO, "Socket 0x%p is free.\n", - new); - break; - } - new = (irnet_socket *) hashbin_get_next(irnet_server.list); - } - } - - /* Spin lock end */ - spin_unlock_bh(&irnet_server.spinlock); - - DEXIT(IRDA_SERV_TRACE, " - new = 0x%p\n", new); - return new; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_connect_socket (self) - * - * Connect an incoming connection to the socket - * - */ -static inline int -irnet_connect_socket(irnet_socket * server, - irnet_socket * new, - struct qos_info * qos, - __u32 max_sdu_size, - __u8 max_header_size) -{ - DENTER(IRDA_SERV_TRACE, "(server=0x%p, new=0x%p)\n", - server, new); - - /* Now attach up the new socket */ - new->tsap = irttp_dup(server->tsap, new); - DABORT(new->tsap == NULL, -1, IRDA_SERV_ERROR, "dup failed!\n"); - - /* Set up all the relevant parameters on the new socket */ - new->stsap_sel = new->tsap->stsap_sel; - new->dtsap_sel = new->tsap->dtsap_sel; - new->saddr = irttp_get_saddr(new->tsap); - new->daddr = irttp_get_daddr(new->tsap); - - new->max_header_size = max_header_size; - new->max_sdu_size_tx = max_sdu_size; - new->max_data_size = max_sdu_size; -#ifdef STREAM_COMPAT - /* If we want to receive "stream sockets" */ - if(max_sdu_size == 0) - new->max_data_size = irttp_get_max_seg_size(new->tsap); -#endif /* STREAM_COMPAT */ - - /* Clean up the original one to keep it in listen state */ - irttp_listen(server->tsap); - - /* Send a connection response on the new socket */ - irttp_connect_response(new->tsap, new->max_sdu_size_rx, NULL); - - /* Allow PPP to send its junk over the new socket... */ - set_bit(0, &new->ttp_open); - - /* Not connecting anymore, and clean up last possible remains - * of connection attempts on the socket */ - clear_bit(0, &new->ttp_connect); - if(new->iriap) - { - iriap_close(new->iriap); - new->iriap = NULL; - } - if(new->discoveries != NULL) - { - kfree(new->discoveries); - new->discoveries = NULL; - } - -#ifdef CONNECT_INDIC_KICK - /* As currently we don't block packets in ppp_irnet_send() while passive, - * this is not really needed... - * Also, not doing it give IrDA a chance to finish the setup properly - * before being swamped with packets... */ - ppp_output_wakeup(&new->chan); -#endif /* CONNECT_INDIC_KICK */ - - /* Notify the control channel */ - irnet_post_event(new, IRNET_CONNECT_FROM, - new->saddr, new->daddr, server->rname, 0); - - DEXIT(IRDA_SERV_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_disconnect_server (self) - * - * Cleanup the server socket when the incoming connection abort - * - */ -static inline void -irnet_disconnect_server(irnet_socket * self, - struct sk_buff *skb) -{ - DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self); - - /* Put the received packet in the black hole */ - kfree_skb(skb); - -#ifdef FAIL_SEND_DISCONNECT - /* Tell the other party we don't want to be connected */ - /* Hum... Is it the right thing to do ? And do we need to send - * a connect response before ? It looks ok without this... */ - irttp_disconnect_request(self->tsap, NULL, P_NORMAL); -#endif /* FAIL_SEND_DISCONNECT */ - - /* Notify the control channel (see irnet_find_socket()) */ - irnet_post_event(NULL, IRNET_REQUEST_FROM, - self->saddr, self->daddr, self->rname, 0); - - /* Clean up the server to keep it in listen state */ - irttp_listen(self->tsap); - - DEXIT(IRDA_SERV_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_setup_server (self) - * - * Create a IrTTP server and set it up... - * - * Register the IrLAN hint bit, create a IrTTP instance for us, - * set all the IrTTP callbacks and create an IrIAS entry... - */ -static inline int -irnet_setup_server(void) -{ - __u16 hints; - - DENTER(IRDA_SERV_TRACE, "()\n"); - - /* Initialise the regular socket part of the server */ - irda_irnet_create(&irnet_server.s); - - /* Open a local TSAP (an IrTTP instance) for the server */ - irnet_open_tsap(&irnet_server.s); - - /* PPP part setup */ - irnet_server.s.ppp_open = 0; - irnet_server.s.chan.private = NULL; - irnet_server.s.file = NULL; - - /* Get the hint bit corresponding to IrLAN */ - /* Note : we overload the IrLAN hint bit. As it is only a "hint", and as - * we provide roughly the same functionality as IrLAN, this is ok. - * In fact, the situation is similar as JetSend overloading the Obex hint - */ - hints = irlmp_service_to_hint(S_LAN); - -#ifdef ADVERTISE_HINT - /* Register with IrLMP as a service (advertise our hint bit) */ - irnet_server.skey = irlmp_register_service(hints); -#endif /* ADVERTISE_HINT */ - - /* Register with LM-IAS (so that people can connect to us) */ - irnet_server.ias_obj = irias_new_object(IRNET_SERVICE_NAME, jiffies); - irias_add_integer_attrib(irnet_server.ias_obj, IRNET_IAS_VALUE, - irnet_server.s.stsap_sel, IAS_KERNEL_ATTR); - irias_insert_object(irnet_server.ias_obj); - -#ifdef DISCOVERY_EVENTS - /* Tell IrLMP we want to be notified of newly discovered nodes */ - irlmp_update_client(irnet_server.s.ckey, hints, - irnet_discovery_indication, irnet_expiry_indication, - (void *) &irnet_server.s); -#endif - - DEXIT(IRDA_SERV_TRACE, " - self=0x%p\n", &irnet_server.s); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_destroy_server (self) - * - * Destroy the IrTTP server... - * - * Reverse of the previous function... - */ -static inline void -irnet_destroy_server(void) -{ - DENTER(IRDA_SERV_TRACE, "()\n"); - -#ifdef ADVERTISE_HINT - /* Unregister with IrLMP */ - irlmp_unregister_service(irnet_server.skey); -#endif /* ADVERTISE_HINT */ - - /* Unregister with LM-IAS */ - if(irnet_server.ias_obj) - irias_delete_object(irnet_server.ias_obj); - - /* Cleanup the socket part */ - irda_irnet_destroy(&irnet_server.s); - - DEXIT(IRDA_SERV_TRACE, "\n"); -} - - -/************************ IRDA-TTP CALLBACKS ************************/ -/* - * When we create a IrTTP instance, we pass to it a set of callbacks - * that IrTTP will call in case of various events. - * We take care of those events here. - */ - -/*------------------------------------------------------------------*/ -/* - * Function irnet_data_indication (instance, sap, skb) - * - * Received some data from TinyTP. Just queue it on the receive queue - * - */ -static int -irnet_data_indication(void * instance, - void * sap, - struct sk_buff *skb) -{ - irnet_socket * ap = (irnet_socket *) instance; - unsigned char * p; - int code = 0; - - DENTER(IRDA_TCB_TRACE, "(self/ap=0x%p, skb=0x%p)\n", - ap, skb); - DASSERT(skb != NULL, 0, IRDA_CB_ERROR, "skb is NULL !!!\n"); - - /* Check is ppp is ready to receive our packet */ - if(!ap->ppp_open) - { - DERROR(IRDA_CB_ERROR, "PPP not ready, dropping packet...\n"); - /* When we return error, TTP will need to requeue the skb and - * will stop the sender. IrTTP will stall until we send it a - * flow control request... */ - return -ENOMEM; - } - - /* strip address/control field if present */ - p = skb->data; - if((p[0] == PPP_ALLSTATIONS) && (p[1] == PPP_UI)) - { - /* chop off address/control */ - if(skb->len < 3) - goto err_exit; - p = skb_pull(skb, 2); - } - - /* decompress protocol field if compressed */ - if(p[0] & 1) - { - /* protocol is compressed */ - *(u8 *)skb_push(skb, 1) = 0; - } - else - if(skb->len < 2) - goto err_exit; - - /* pass to generic ppp layer */ - /* Note : how do I know if ppp can accept or not the packet ? This is - * essential if I want to manage flow control smoothly... */ - ppp_input(&ap->chan, skb); - - DEXIT(IRDA_TCB_TRACE, "\n"); - return 0; - - err_exit: - DERROR(IRDA_CB_ERROR, "Packet too small, dropping...\n"); - kfree_skb(skb); - ppp_input_error(&ap->chan, code); - return 0; /* Don't return an error code, only for flow control... */ -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_disconnect_indication (instance, sap, reason, skb) - * - * Connection has been closed. Chech reason to find out why - * - * Note : there are many cases where we come here : - * o attempted to connect, timeout - * o connected, link is broken, LAP has timeout - * o connected, other side close the link - * o connection request on the server not handled - */ -static void -irnet_disconnect_indication(void * instance, - void * sap, - LM_REASON reason, - struct sk_buff *skb) -{ - irnet_socket * self = (irnet_socket *) instance; - int test_open; - int test_connect; - - DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self); - DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n"); - - /* Don't care about it, but let's not leak it */ - if(skb) - dev_kfree_skb(skb); - - /* Prevent higher layer from accessing IrTTP */ - test_open = test_and_clear_bit(0, &self->ttp_open); - /* Not connecting anymore... - * (note : TSAP is open, so IAP callbacks are no longer pending...) */ - test_connect = test_and_clear_bit(0, &self->ttp_connect); - - /* If both self->ttp_open and self->ttp_connect are NULL, it mean that we - * have a race condition with irda_irnet_destroy() or - * irnet_connect_indication(), so don't mess up tsap... - */ - if(!(test_open || test_connect)) - { - DERROR(IRDA_CB_ERROR, "Race condition detected...\n"); - return; - } - - /* If we were active, notify the control channel */ - if(test_open) - irnet_post_event(self, IRNET_DISCONNECT_FROM, - self->saddr, self->daddr, self->rname, 0); - else - /* If we were trying to connect, notify the control channel */ - if((self->tsap) && (self != &irnet_server.s)) - irnet_post_event(self, IRNET_NOANSWER_FROM, - self->saddr, self->daddr, self->rname, 0); - - /* Close our IrTTP connection, cleanup tsap */ - if((self->tsap) && (self != &irnet_server.s)) - { - DEBUG(IRDA_CB_INFO, "Closing our TTP connection.\n"); - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } - /* Cleanup the socket in case we want to reconnect in ppp_output_wakeup() */ - self->stsap_sel = 0; - self->daddr = DEV_ADDR_ANY; - self->tx_flow = FLOW_START; - - /* Deal with the ppp instance if it's still alive */ - if(self->ppp_open) - { - if(test_open) - { - /* ppp_unregister_channel() wants a user context. */ - schedule_work(&self->disconnect_work); - } - else - { - /* If we were trying to connect, flush (drain) ppp_generic - * Tx queue (most often we have blocked it), which will - * trigger an other attempt to connect. If we are passive, - * this will empty the Tx queue after last try. */ - ppp_output_wakeup(&self->chan); - } - } - - DEXIT(IRDA_TCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_connect_confirm (instance, sap, qos, max_sdu_size, skb) - * - * Connections has been confirmed by the remote device - * - */ -static void -irnet_connect_confirm(void * instance, - void * sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - irnet_socket * self = (irnet_socket *) instance; - - DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self); - - /* Check if socket is closing down (via irda_irnet_destroy()) */ - if(! test_bit(0, &self->ttp_connect)) - { - DERROR(IRDA_CB_ERROR, "Socket no longer connecting. Ouch !\n"); - return; - } - - /* How much header space do we need to reserve */ - self->max_header_size = max_header_size; - - /* IrTTP max SDU size in transmit direction */ - self->max_sdu_size_tx = max_sdu_size; - self->max_data_size = max_sdu_size; -#ifdef STREAM_COMPAT - if(max_sdu_size == 0) - self->max_data_size = irttp_get_max_seg_size(self->tsap); -#endif /* STREAM_COMPAT */ - - /* At this point, IrLMP has assigned our source address */ - self->saddr = irttp_get_saddr(self->tsap); - - /* Allow higher layer to access IrTTP */ - set_bit(0, &self->ttp_open); - clear_bit(0, &self->ttp_connect); /* Not racy, IrDA traffic is serial */ - /* Give a kick in the ass of ppp_generic so that he sends us some data */ - ppp_output_wakeup(&self->chan); - - /* Check size of received packet */ - if(skb->len > 0) - { -#ifdef PASS_CONNECT_PACKETS - DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n"); - /* Try to pass it to PPP */ - irnet_data_indication(instance, sap, skb); -#else /* PASS_CONNECT_PACKETS */ - DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n"); - kfree_skb(skb); /* Note : will be optimised with other kfree... */ -#endif /* PASS_CONNECT_PACKETS */ - } - else - kfree_skb(skb); - - /* Notify the control channel */ - irnet_post_event(self, IRNET_CONNECT_TO, - self->saddr, self->daddr, self->rname, 0); - - DEXIT(IRDA_TCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_flow_indication (instance, sap, flow) - * - * Used by TinyTP to tell us if it can accept more data or not - * - */ -static void -irnet_flow_indication(void * instance, - void * sap, - LOCAL_FLOW flow) -{ - irnet_socket * self = (irnet_socket *) instance; - LOCAL_FLOW oldflow = self->tx_flow; - - DENTER(IRDA_TCB_TRACE, "(self=0x%p, flow=%d)\n", self, flow); - - /* Update our state */ - self->tx_flow = flow; - - /* Check what IrTTP want us to do... */ - switch(flow) - { - case FLOW_START: - DEBUG(IRDA_CB_INFO, "IrTTP wants us to start again\n"); - /* Check if we really need to wake up PPP */ - if(oldflow == FLOW_STOP) - ppp_output_wakeup(&self->chan); - else - DEBUG(IRDA_CB_INFO, "But we were already transmitting !!!\n"); - break; - case FLOW_STOP: - DEBUG(IRDA_CB_INFO, "IrTTP wants us to slow down\n"); - break; - default: - DEBUG(IRDA_CB_INFO, "Unknown flow command!\n"); - break; - } - - DEXIT(IRDA_TCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_status_indication (instance, sap, reason, skb) - * - * Link (IrLAP) status report. - * - */ -static void -irnet_status_indication(void * instance, - LINK_STATUS link, - LOCK_STATUS lock) -{ - irnet_socket * self = (irnet_socket *) instance; - - DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self); - DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n"); - - /* We can only get this event if we are connected */ - switch(link) - { - case STATUS_NO_ACTIVITY: - irnet_post_event(self, IRNET_BLOCKED_LINK, - self->saddr, self->daddr, self->rname, 0); - break; - default: - DEBUG(IRDA_CB_INFO, "Unknown status...\n"); - } - - DEXIT(IRDA_TCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_connect_indication(instance, sap, qos, max_sdu_size, userdata) - * - * Incoming connection - * - * In theory, this function is called only on the server socket. - * Some other node is attempting to connect to the IrNET service, and has - * sent a connection request on our server socket. - * We just redirect the connection to the relevant IrNET socket. - * - * Note : we also make sure that between 2 irnet nodes, there can - * exist only one irnet connection. - */ -static void -irnet_connect_indication(void * instance, - void * sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - irnet_socket * server = &irnet_server.s; - irnet_socket * new = (irnet_socket *) NULL; - - DENTER(IRDA_TCB_TRACE, "(server=0x%p)\n", server); - DASSERT(instance == &irnet_server, , IRDA_CB_ERROR, - "Invalid instance (0x%p) !!!\n", instance); - DASSERT(sap == irnet_server.s.tsap, , IRDA_CB_ERROR, "Invalid sap !!!\n"); - - /* Try to find the most appropriate IrNET socket */ - new = irnet_find_socket(server); - - /* After all this hard work, do we have an socket ? */ - if(new == (irnet_socket *) NULL) - { - DEXIT(IRDA_CB_INFO, ": No socket waiting for this connection.\n"); - irnet_disconnect_server(server, skb); - return; - } - - /* Is the socket already busy ? */ - if(test_bit(0, &new->ttp_open)) - { - DEXIT(IRDA_CB_INFO, ": Socket already connected.\n"); - irnet_disconnect_server(server, skb); - return; - } - - /* The following code is a bit tricky, so need comments ;-) - */ - /* If ttp_connect is set, the socket is trying to connect to the other - * end and may have sent a IrTTP connection request and is waiting for - * a connection response (that may never come). - * Now, the pain is that the socket may have opened a tsap and is - * waiting on it, while the other end is trying to connect to it on - * another tsap. - * Because IrNET can be peer to peer, we need to workaround this. - * Furthermore, the way the irnetd script is implemented, the - * target will create a second IrNET connection back to the - * originator and expect the originator to bind this new connection - * to the original PPPD instance. - * And of course, if we don't use irnetd, we can have a race when - * both side try to connect simultaneously, which could leave both - * connections half closed (yuck). - * Conclusions : - * 1) The "originator" must accept the new connection and get rid - * of the old one so that irnetd works - * 2) One side must deny the new connection to avoid races, - * but both side must agree on which side it is... - * Most often, the originator is primary at the LAP layer. - * Jean II - */ - /* Now, let's look at the way I wrote the test... - * We need to clear up the ttp_connect flag atomically to prevent - * irnet_disconnect_indication() to mess up the tsap we are going to close. - * We want to clear the ttp_connect flag only if we close the tsap, - * otherwise we will never close it, so we need to check for primary - * *before* doing the test on the flag. - * And of course, ALLOW_SIMULT_CONNECT can disable this entirely... - * Jean II - */ - - /* Socket already connecting ? On primary ? */ - if(0 -#ifdef ALLOW_SIMULT_CONNECT - || ((irttp_is_primary(server->tsap) == 1) && /* primary */ - (test_and_clear_bit(0, &new->ttp_connect))) -#endif /* ALLOW_SIMULT_CONNECT */ - ) - { - DERROR(IRDA_CB_ERROR, "Socket already connecting, but going to reuse it !\n"); - - /* Cleanup the old TSAP if necessary - IrIAP will be cleaned up later */ - if(new->tsap != NULL) - { - /* Close the old connection the new socket was attempting, - * so that we can hook it up to the new connection. - * It's now safe to do it... */ - irttp_close_tsap(new->tsap); - new->tsap = NULL; - } - } - else - { - /* Three options : - * 1) socket was not connecting or connected : ttp_connect should be 0. - * 2) we don't want to connect the socket because we are secondary or - * ALLOW_SIMULT_CONNECT is undefined. ttp_connect should be 1. - * 3) we are half way in irnet_disconnect_indication(), and it's a - * nice race condition... Fortunately, we can detect that by checking - * if tsap is still alive. On the other hand, we can't be in - * irda_irnet_destroy() otherwise we would not have found this - * socket in the hashbin. - * Jean II */ - if((test_bit(0, &new->ttp_connect)) || (new->tsap != NULL)) - { - /* Don't mess this socket, somebody else in in charge... */ - DERROR(IRDA_CB_ERROR, "Race condition detected, socket in use, abort connect...\n"); - irnet_disconnect_server(server, skb); - return; - } - } - - /* So : at this point, we have a socket, and it is idle. Good ! */ - irnet_connect_socket(server, new, qos, max_sdu_size, max_header_size); - - /* Check size of received packet */ - if(skb->len > 0) - { -#ifdef PASS_CONNECT_PACKETS - DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n"); - /* Try to pass it to PPP */ - irnet_data_indication(new, new->tsap, skb); -#else /* PASS_CONNECT_PACKETS */ - DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n"); - kfree_skb(skb); /* Note : will be optimised with other kfree... */ -#endif /* PASS_CONNECT_PACKETS */ - } - else - kfree_skb(skb); - - DEXIT(IRDA_TCB_TRACE, "\n"); -} - - -/********************** IRDA-IAS/LMP CALLBACKS **********************/ -/* - * These are the callbacks called by other layers of the IrDA stack, - * mainly LMP for discovery and IAS for name queries. - */ - -/*------------------------------------------------------------------*/ -/* - * Function irnet_getvalue_confirm (result, obj_id, value, priv) - * - * Got answer from remote LM-IAS, just connect - * - * This is the reply to a IAS query we were doing to find the TSAP of - * the device we want to connect to. - * If we have found a valid TSAP, just initiate the TTP connection - * on this TSAP. - */ -static void -irnet_getvalue_confirm(int result, - __u16 obj_id, - struct ias_value *value, - void * priv) -{ - irnet_socket * self = (irnet_socket *) priv; - - DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); - DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n"); - - /* Check if already connected (via irnet_connect_socket()) - * or socket is closing down (via irda_irnet_destroy()) */ - if(! test_bit(0, &self->ttp_connect)) - { - DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n"); - return; - } - - /* We probably don't need to make any more queries */ - iriap_close(self->iriap); - self->iriap = NULL; - - /* Post process the IAS reply */ - self->dtsap_sel = irnet_ias_to_tsap(self, result, value); - - /* If error, just go out */ - if(self->errno) - { - clear_bit(0, &self->ttp_connect); - DERROR(IRDA_OCB_ERROR, "IAS connect failed ! (0x%X)\n", self->errno); - return; - } - - DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n", - self->daddr, self->dtsap_sel); - - /* Start up TTP - non blocking */ - irnet_connect_tsap(self); - - DEXIT(IRDA_OCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_discovervalue_confirm (result, obj_id, value, priv) - * - * Handle the TSAP discovery procedure state machine. - * Got answer from remote LM-IAS, try next device - * - * We are doing a TSAP discovery procedure, and we got an answer to - * a IAS query we were doing to find the TSAP on one of the address - * in the discovery log. - * - * If we have found a valid TSAP for the first time, save it. If it's - * not the first time we found one, complain. - * - * If we have more addresses in the log, just initiate a new query. - * Note that those query may fail (see irnet_discover_daddr_and_lsap_sel()) - * - * Otherwise, wrap up the procedure (cleanup), check if we have found - * any device and connect to it. - */ -static void -irnet_discovervalue_confirm(int result, - __u16 obj_id, - struct ias_value *value, - void * priv) -{ - irnet_socket * self = (irnet_socket *) priv; - __u8 dtsap_sel; /* TSAP we are looking for */ - - DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); - DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n"); - - /* Check if already connected (via irnet_connect_socket()) - * or socket is closing down (via irda_irnet_destroy()) */ - if(! test_bit(0, &self->ttp_connect)) - { - DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n"); - return; - } - - /* Post process the IAS reply */ - dtsap_sel = irnet_ias_to_tsap(self, result, value); - - /* Have we got something ? */ - if(self->errno == 0) - { - /* We found the requested service */ - if(self->daddr != DEV_ADDR_ANY) - { - DERROR(IRDA_OCB_ERROR, "More than one device in range supports IrNET...\n"); - } - else - { - /* First time we found that one, save it ! */ - self->daddr = self->discoveries[self->disco_index].daddr; - self->dtsap_sel = dtsap_sel; - } - } - - /* If no failure */ - if((self->errno == -EADDRNOTAVAIL) || (self->errno == 0)) - { - int ret; - - /* Search the next node */ - ret = irnet_discover_next_daddr(self); - if(!ret) - { - /* In this case, the above request was non-blocking. - * We will return here after a while... */ - return; - } - /* In this case, we have processed the last discovery item */ - } - - /* No more queries to be done (failure or last one) */ - - /* We probably don't need to make any more queries */ - iriap_close(self->iriap); - self->iriap = NULL; - - /* No more items : remove the log and signal termination */ - DEBUG(IRDA_OCB_INFO, "Cleaning up log (0x%p)\n", - self->discoveries); - if(self->discoveries != NULL) - { - /* Cleanup our copy of the discovery log */ - kfree(self->discoveries); - self->discoveries = NULL; - } - self->disco_number = -1; - - /* Check out what we found */ - if(self->daddr == DEV_ADDR_ANY) - { - self->daddr = DEV_ADDR_ANY; - clear_bit(0, &self->ttp_connect); - DEXIT(IRDA_OCB_TRACE, ": cannot discover IrNET in any device !!!\n"); - return; - } - - /* We have a valid address - just connect */ - - DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n", - self->daddr, self->dtsap_sel); - - /* Start up TTP - non blocking */ - irnet_connect_tsap(self); - - DEXIT(IRDA_OCB_TRACE, "\n"); -} - -#ifdef DISCOVERY_EVENTS -/*------------------------------------------------------------------*/ -/* - * Function irnet_discovery_indication (discovery) - * - * Got a discovery indication from IrLMP, post an event - * - * Note : IrLMP take care of matching the hint mask for us, and also - * check if it is a "new" node for us... - * - * As IrLMP filter on the IrLAN hint bit, we get both IrLAN and IrNET - * nodes, so it's only at connection time that we will know if the - * node support IrNET, IrLAN or both. The other solution is to check - * in IAS the PNP ids and service name. - * Note : even if a node support IrNET (or IrLAN), it's no guarantee - * that we will be able to connect to it, the node might already be - * busy... - * - * One last thing : in some case, this function will trigger duplicate - * discovery events. On the other hand, we should catch all - * discoveries properly (i.e. not miss one). Filtering duplicate here - * is to messy, so we leave that to user space... - */ -static void -irnet_discovery_indication(discinfo_t * discovery, - DISCOVERY_MODE mode, - void * priv) -{ - irnet_socket * self = &irnet_server.s; - - DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); - DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR, - "Invalid instance (0x%p) !!!\n", priv); - - DEBUG(IRDA_OCB_INFO, "Discovered new IrNET/IrLAN node %s...\n", - discovery->info); - - /* Notify the control channel */ - irnet_post_event(NULL, IRNET_DISCOVER, - discovery->saddr, discovery->daddr, discovery->info, - get_unaligned((__u16 *)discovery->hints)); - - DEXIT(IRDA_OCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_expiry_indication (expiry) - * - * Got a expiry indication from IrLMP, post an event - * - * Note : IrLMP take care of matching the hint mask for us, we only - * check if it is a "new" node... - */ -static void -irnet_expiry_indication(discinfo_t * expiry, - DISCOVERY_MODE mode, - void * priv) -{ - irnet_socket * self = &irnet_server.s; - - DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); - DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR, - "Invalid instance (0x%p) !!!\n", priv); - - DEBUG(IRDA_OCB_INFO, "IrNET/IrLAN node %s expired...\n", - expiry->info); - - /* Notify the control channel */ - irnet_post_event(NULL, IRNET_EXPIRE, - expiry->saddr, expiry->daddr, expiry->info, - get_unaligned((__u16 *)expiry->hints)); - - DEXIT(IRDA_OCB_TRACE, "\n"); -} -#endif /* DISCOVERY_EVENTS */ - - -/*********************** PROC ENTRY CALLBACKS ***********************/ -/* - * We create a instance in the /proc filesystem, and here we take care - * of that... - */ - -#ifdef CONFIG_PROC_FS -static int -irnet_proc_show(struct seq_file *m, void *v) -{ - irnet_socket * self; - char * state; - int i = 0; - - /* Get the IrNET server information... */ - seq_printf(m, "IrNET server - "); - seq_printf(m, "IrDA state: %s, ", - (irnet_server.running ? "running" : "dead")); - seq_printf(m, "stsap_sel: %02x, ", irnet_server.s.stsap_sel); - seq_printf(m, "dtsap_sel: %02x\n", irnet_server.s.dtsap_sel); - - /* Do we need to continue ? */ - if(!irnet_server.running) - return 0; - - /* Protect access to the instance list */ - spin_lock_bh(&irnet_server.spinlock); - - /* Get the sockets one by one... */ - self = (irnet_socket *) hashbin_get_first(irnet_server.list); - while(self != NULL) - { - /* Start printing info about the socket. */ - seq_printf(m, "\nIrNET socket %d - ", i++); - - /* First, get the requested configuration */ - seq_printf(m, "Requested IrDA name: \"%s\", ", self->rname); - seq_printf(m, "daddr: %08x, ", self->rdaddr); - seq_printf(m, "saddr: %08x\n", self->rsaddr); - - /* Second, get all the PPP info */ - seq_printf(m, " PPP state: %s", - (self->ppp_open ? "registered" : "unregistered")); - if(self->ppp_open) - { - seq_printf(m, ", unit: ppp%d", - ppp_unit_number(&self->chan)); - seq_printf(m, ", channel: %d", - ppp_channel_index(&self->chan)); - seq_printf(m, ", mru: %d", - self->mru); - /* Maybe add self->flags ? Later... */ - } - - /* Then, get all the IrDA specific info... */ - if(self->ttp_open) - state = "connected"; - else - if(self->tsap != NULL) - state = "connecting"; - else - if(self->iriap != NULL) - state = "searching"; - else - if(self->ttp_connect) - state = "weird"; - else - state = "idle"; - seq_printf(m, "\n IrDA state: %s, ", state); - seq_printf(m, "daddr: %08x, ", self->daddr); - seq_printf(m, "stsap_sel: %02x, ", self->stsap_sel); - seq_printf(m, "dtsap_sel: %02x\n", self->dtsap_sel); - - /* Next socket, please... */ - self = (irnet_socket *) hashbin_get_next(irnet_server.list); - } - - /* Spin lock end */ - spin_unlock_bh(&irnet_server.spinlock); - - return 0; -} - -static int irnet_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, irnet_proc_show, NULL); -} - -static const struct file_operations irnet_proc_fops = { - .owner = THIS_MODULE, - .open = irnet_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif /* PROC_FS */ - - -/********************** CONFIGURATION/CLEANUP **********************/ -/* - * Initialisation and teardown of the IrDA part, called at module - * insertion and removal... - */ - -/*------------------------------------------------------------------*/ -/* - * Prepare the IrNET layer for operation... - */ -int __init -irda_irnet_init(void) -{ - int err = 0; - - DENTER(MODULE_TRACE, "()\n"); - - /* Pure paranoia - should be redundant */ - memset(&irnet_server, 0, sizeof(struct irnet_root)); - - /* Setup start of irnet instance list */ - irnet_server.list = hashbin_new(HB_NOLOCK); - DABORT(irnet_server.list == NULL, -ENOMEM, - MODULE_ERROR, "Can't allocate hashbin!\n"); - /* Init spinlock for instance list */ - spin_lock_init(&irnet_server.spinlock); - - /* Initialise control channel */ - init_waitqueue_head(&irnet_events.rwait); - irnet_events.index = 0; - /* Init spinlock for event logging */ - spin_lock_init(&irnet_events.spinlock); - -#ifdef CONFIG_PROC_FS - /* Add a /proc file for irnet infos */ - proc_create("irnet", 0, proc_irda, &irnet_proc_fops); -#endif /* CONFIG_PROC_FS */ - - /* Setup the IrNET server */ - err = irnet_setup_server(); - - if(!err) - /* We are no longer functional... */ - irnet_server.running = 1; - - DEXIT(MODULE_TRACE, "\n"); - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Cleanup at exit... - */ -void __exit -irda_irnet_cleanup(void) -{ - DENTER(MODULE_TRACE, "()\n"); - - /* We are no longer there... */ - irnet_server.running = 0; - -#ifdef CONFIG_PROC_FS - /* Remove our /proc file */ - remove_proc_entry("irnet", proc_irda); -#endif /* CONFIG_PROC_FS */ - - /* Remove our IrNET server from existence */ - irnet_destroy_server(); - - /* Remove all instances of IrNET socket still present */ - hashbin_delete(irnet_server.list, (FREE_FUNC) irda_irnet_destroy); - - DEXIT(MODULE_TRACE, "\n"); -} diff --git a/drivers/staging/irda/net/irnet/irnet_irda.h b/drivers/staging/irda/net/irnet/irnet_irda.h deleted file mode 100644 index 3e408952a3f1..000000000000 --- a/drivers/staging/irda/net/irnet/irnet_irda.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * IrNET protocol module : Synchronous PPP over an IrDA socket. - * - * Jean II - HPL `00 - - * - * This file contains all definitions and declarations necessary for the - * IRDA part of the IrNET module (dealing with IrTTP, IrIAS and co). - * This file is a private header, so other modules don't want to know - * what's in there... - */ - -#ifndef IRNET_IRDA_H -#define IRNET_IRDA_H - -/***************************** INCLUDES *****************************/ -/* Please add other headers in irnet.h */ - -#include "irnet.h" /* Module global include */ - -/************************ CONSTANTS & MACROS ************************/ - -/* - * Name of the service (socket name) used by IrNET - */ -/* IAS object name (or part of it) */ -#define IRNET_SERVICE_NAME "IrNetv1" -/* IAS attribute */ -#define IRNET_IAS_VALUE "IrDA:TinyTP:LsapSel" -/* LMP notify name for client (only for /proc/net/irda/irlmp) */ -#define IRNET_NOTIFY_NAME "IrNET socket" -/* LMP notify name for server (only for /proc/net/irda/irlmp) */ -#define IRNET_NOTIFY_NAME_SERV "IrNET server" - -/****************************** TYPES ******************************/ - -/* - * This is the main structure where we store all the data pertaining to - * the IrNET server (listen for connection requests) and the root - * of the IrNET socket list - */ -typedef struct irnet_root -{ - irnet_socket s; /* To pretend we are a client... */ - - /* Generic stuff */ - int magic; /* Paranoia */ - int running; /* Are we operational ? */ - - /* Link list of all IrNET instances opened */ - hashbin_t * list; - spinlock_t spinlock; /* Serialize access to the list */ - /* Note : the way hashbin has been designed is absolutely not - * reentrant, beware... So, we blindly protect all with spinlock */ - - /* Handle for the hint bit advertised in IrLMP */ - void * skey; - - /* Server socket part */ - struct ias_object * ias_obj; /* Our service name + lsap in IAS */ - -} irnet_root; - - -/**************************** PROTOTYPES ****************************/ - -/* ----------------------- CONTROL CHANNEL ----------------------- */ -static void - irnet_post_event(irnet_socket *, - irnet_event, - __u32, - __u32, - char *, - __u16); -/* ----------------------- IRDA SUBROUTINES ----------------------- */ -static inline int - irnet_open_tsap(irnet_socket *); -static inline __u8 - irnet_ias_to_tsap(irnet_socket *, - int, - struct ias_value *); -static inline int - irnet_find_lsap_sel(irnet_socket *); -static inline int - irnet_connect_tsap(irnet_socket *); -static inline int - irnet_discover_next_daddr(irnet_socket *); -static inline int - irnet_discover_daddr_and_lsap_sel(irnet_socket *); -static inline int - irnet_dname_to_daddr(irnet_socket *); -/* ------------------------ SERVER SOCKET ------------------------ */ -static inline int - irnet_daddr_to_dname(irnet_socket *); -static inline irnet_socket * - irnet_find_socket(irnet_socket *); -static inline int - irnet_connect_socket(irnet_socket *, - irnet_socket *, - struct qos_info *, - __u32, - __u8); -static inline void - irnet_disconnect_server(irnet_socket *, - struct sk_buff *); -static inline int - irnet_setup_server(void); -static inline void - irnet_destroy_server(void); -/* ---------------------- IRDA-TTP CALLBACKS ---------------------- */ -static int - irnet_data_indication(void *, /* instance */ - void *, /* sap */ - struct sk_buff *); -static void - irnet_disconnect_indication(void *, - void *, - LM_REASON, - struct sk_buff *); -static void - irnet_connect_confirm(void *, - void *, - struct qos_info *, - __u32, - __u8, - struct sk_buff *); -static void - irnet_flow_indication(void *, - void *, - LOCAL_FLOW); -static void - irnet_status_indication(void *, - LINK_STATUS, - LOCK_STATUS); -static void - irnet_connect_indication(void *, - void *, - struct qos_info *, - __u32, - __u8, - struct sk_buff *); -/* -------------------- IRDA-IAS/LMP CALLBACKS -------------------- */ -static void - irnet_getvalue_confirm(int, - __u16, - struct ias_value *, - void *); -static void - irnet_discovervalue_confirm(int, - __u16, - struct ias_value *, - void *); -#ifdef DISCOVERY_EVENTS -static void - irnet_discovery_indication(discinfo_t *, - DISCOVERY_MODE, - void *); -static void - irnet_expiry_indication(discinfo_t *, - DISCOVERY_MODE, - void *); -#endif - -/**************************** VARIABLES ****************************/ - -/* - * The IrNET server. Listen to connection requests and co... - */ -static struct irnet_root irnet_server; - -/* Control channel stuff (note : extern) */ -struct irnet_ctrl_channel irnet_events; - -/* The /proc/net/irda directory, defined elsewhere... */ -#ifdef CONFIG_PROC_FS -extern struct proc_dir_entry *proc_irda; -#endif /* CONFIG_PROC_FS */ - -#endif /* IRNET_IRDA_H */ diff --git a/drivers/staging/irda/net/irnet/irnet_ppp.c b/drivers/staging/irda/net/irnet/irnet_ppp.c deleted file mode 100644 index c90a158af4b7..000000000000 --- a/drivers/staging/irda/net/irnet/irnet_ppp.c +++ /dev/null @@ -1,1189 +0,0 @@ -/* - * IrNET protocol module : Synchronous PPP over an IrDA socket. - * - * Jean II - HPL `00 - - * - * This file implement the PPP interface and /dev/irnet character device. - * The PPP interface hook to the ppp_generic module, handle all our - * relationship to the PPP code in the kernel (and by extension to pppd), - * and exchange PPP frames with this module (send/receive). - * The /dev/irnet device is used primarily for 2 functions : - * 1) as a stub for pppd (the ppp daemon), so that we can appropriately - * generate PPP sessions (we pretend we are a tty). - * 2) as a control channel (write commands, read events) - */ - -#include -#include - -#include "irnet_ppp.h" /* Private header */ -/* Please put other headers in irnet.h - Thanks */ - -/* Generic PPP callbacks (to call us) */ -static const struct ppp_channel_ops irnet_ppp_ops = { - .start_xmit = ppp_irnet_send, - .ioctl = ppp_irnet_ioctl -}; - -/************************* CONTROL CHANNEL *************************/ -/* - * When a pppd instance is not active on /dev/irnet, it acts as a control - * channel. - * Writing allow to set up the IrDA destination of the IrNET channel, - * and any application may be read events happening in IrNET... - */ - -/*------------------------------------------------------------------*/ -/* - * Write is used to send a command to configure a IrNET channel - * before it is open by pppd. The syntax is : "command argument" - * Currently there is only two defined commands : - * o name : set the requested IrDA nickname of the IrNET peer. - * o addr : set the requested IrDA address of the IrNET peer. - * Note : the code is crude, but effective... - */ -static inline ssize_t -irnet_ctrl_write(irnet_socket * ap, - const char __user *buf, - size_t count) -{ - char command[IRNET_MAX_COMMAND]; - char * start; /* Current command being processed */ - char * next; /* Next command to process */ - int length; /* Length of current command */ - - DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count); - - /* Check for overflow... */ - DABORT(count >= IRNET_MAX_COMMAND, -ENOMEM, - CTRL_ERROR, "Too much data !!!\n"); - - /* Get the data in the driver */ - if(copy_from_user(command, buf, count)) - { - DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); - return -EFAULT; - } - - /* Safe terminate the string */ - command[count] = '\0'; - DEBUG(CTRL_INFO, "Command line received is ``%s'' (%zd).\n", - command, count); - - /* Check every commands in the command line */ - next = command; - while(next != NULL) - { - /* Look at the next command */ - start = next; - - /* Scrap whitespaces before the command */ - start = skip_spaces(start); - - /* ',' is our command separator */ - next = strchr(start, ','); - if(next) - { - *next = '\0'; /* Terminate command */ - length = next - start; /* Length */ - next++; /* Skip the '\0' */ - } - else - length = strlen(start); - - DEBUG(CTRL_INFO, "Found command ``%s'' (%d).\n", start, length); - - /* Check if we recognised one of the known command - * We can't use "switch" with strings, so hack with "continue" */ - - /* First command : name -> Requested IrDA nickname */ - if(!strncmp(start, "name", 4)) - { - /* Copy the name only if is included and not "any" */ - if((length > 5) && (strcmp(start + 5, "any"))) - { - /* Strip out trailing whitespaces */ - while(isspace(start[length - 1])) - length--; - - DABORT(length < 5 || length > NICKNAME_MAX_LEN + 5, - -EINVAL, CTRL_ERROR, "Invalid nickname.\n"); - - /* Copy the name for later reuse */ - memcpy(ap->rname, start + 5, length - 5); - ap->rname[length - 5] = '\0'; - } - else - ap->rname[0] = '\0'; - DEBUG(CTRL_INFO, "Got rname = ``%s''\n", ap->rname); - - /* Restart the loop */ - continue; - } - - /* Second command : addr, daddr -> Requested IrDA destination address - * Also process : saddr -> Requested IrDA source address */ - if((!strncmp(start, "addr", 4)) || - (!strncmp(start, "daddr", 5)) || - (!strncmp(start, "saddr", 5))) - { - __u32 addr = DEV_ADDR_ANY; - - /* Copy the address only if is included and not "any" */ - if((length > 5) && (strcmp(start + 5, "any"))) - { - char * begp = start + 5; - char * endp; - - /* Scrap whitespaces before the command */ - begp = skip_spaces(begp); - - /* Convert argument to a number (last arg is the base) */ - addr = simple_strtoul(begp, &endp, 16); - /* Has it worked ? (endp should be start + length) */ - DABORT(endp <= (start + 5), -EINVAL, - CTRL_ERROR, "Invalid address.\n"); - } - /* Which type of address ? */ - if(start[0] == 's') - { - /* Save it */ - ap->rsaddr = addr; - DEBUG(CTRL_INFO, "Got rsaddr = %08x\n", ap->rsaddr); - } - else - { - /* Save it */ - ap->rdaddr = addr; - DEBUG(CTRL_INFO, "Got rdaddr = %08x\n", ap->rdaddr); - } - - /* Restart the loop */ - continue; - } - - /* Other possible command : connect N (number of retries) */ - - /* No command matched -> Failed... */ - DABORT(1, -EINVAL, CTRL_ERROR, "Not a recognised IrNET command.\n"); - } - - /* Success : we have parsed all commands successfully */ - return count; -} - -#ifdef INITIAL_DISCOVERY -/*------------------------------------------------------------------*/ -/* - * Function irnet_get_discovery_log (self) - * - * Query the content on the discovery log if not done - * - * This function query the current content of the discovery log - * at the startup of the event channel and save it in the internal struct. - */ -static void -irnet_get_discovery_log(irnet_socket * ap) -{ - __u16 mask = irlmp_service_to_hint(S_LAN); - - /* Ask IrLMP for the current discovery log */ - ap->discoveries = irlmp_get_discoveries(&ap->disco_number, mask, - DISCOVERY_DEFAULT_SLOTS); - - /* Check if the we got some results */ - if(ap->discoveries == NULL) - ap->disco_number = -1; - - DEBUG(CTRL_INFO, "Got the log (0x%p), size is %d\n", - ap->discoveries, ap->disco_number); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_read_discovery_log (self, event) - * - * Read the content on the discovery log - * - * This function dump the current content of the discovery log - * at the startup of the event channel. - * Return 1 if wrote an event on the control channel... - * - * State of the ap->disco_XXX variables : - * Socket creation : discoveries = NULL ; disco_index = 0 ; disco_number = 0 - * While reading : discoveries = ptr ; disco_index = X ; disco_number = Y - * After reading : discoveries = NULL ; disco_index = Y ; disco_number = -1 - */ -static inline int -irnet_read_discovery_log(irnet_socket *ap, char *event, int buf_size) -{ - int done_event = 0; - - DENTER(CTRL_TRACE, "(ap=0x%p, event=0x%p)\n", - ap, event); - - /* Test if we have some work to do or we have already finished */ - if(ap->disco_number == -1) - { - DEBUG(CTRL_INFO, "Already done\n"); - return 0; - } - - /* Test if it's the first time and therefore we need to get the log */ - if(ap->discoveries == NULL) - irnet_get_discovery_log(ap); - - /* Check if we have more item to dump */ - if(ap->disco_index < ap->disco_number) - { - /* Write an event */ - snprintf(event, buf_size, - "Found %08x (%s) behind %08x {hints %02X-%02X}\n", - ap->discoveries[ap->disco_index].daddr, - ap->discoveries[ap->disco_index].info, - ap->discoveries[ap->disco_index].saddr, - ap->discoveries[ap->disco_index].hints[0], - ap->discoveries[ap->disco_index].hints[1]); - DEBUG(CTRL_INFO, "Writing discovery %d : %s\n", - ap->disco_index, ap->discoveries[ap->disco_index].info); - - /* We have an event */ - done_event = 1; - /* Next discovery */ - ap->disco_index++; - } - - /* Check if we have done the last item */ - if(ap->disco_index >= ap->disco_number) - { - /* No more items : remove the log and signal termination */ - DEBUG(CTRL_INFO, "Cleaning up log (0x%p)\n", - ap->discoveries); - if(ap->discoveries != NULL) - { - /* Cleanup our copy of the discovery log */ - kfree(ap->discoveries); - ap->discoveries = NULL; - } - ap->disco_number = -1; - } - - return done_event; -} -#endif /* INITIAL_DISCOVERY */ - -/*------------------------------------------------------------------*/ -/* - * Read is used to get IrNET events - */ -static inline ssize_t -irnet_ctrl_read(irnet_socket * ap, - struct file * file, - char __user * buf, - size_t count) -{ - DECLARE_WAITQUEUE(wait, current); - char event[75]; - ssize_t ret = 0; - - DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count); - -#ifdef INITIAL_DISCOVERY - /* Check if we have read the log */ - if (irnet_read_discovery_log(ap, event, sizeof(event))) - { - count = min(strlen(event), count); - if (copy_to_user(buf, event, count)) - { - DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); - return -EFAULT; - } - - DEXIT(CTRL_TRACE, "\n"); - return count; - } -#endif /* INITIAL_DISCOVERY */ - - /* Put ourselves on the wait queue to be woken up */ - add_wait_queue(&irnet_events.rwait, &wait); - set_current_state(TASK_INTERRUPTIBLE); - for(;;) - { - /* If there is unread events */ - ret = 0; - if(ap->event_index != irnet_events.index) - break; - ret = -EAGAIN; - if(file->f_flags & O_NONBLOCK) - break; - ret = -ERESTARTSYS; - if(signal_pending(current)) - break; - /* Yield and wait to be woken up */ - schedule(); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&irnet_events.rwait, &wait); - - /* Did we got it ? */ - if(ret != 0) - { - /* No, return the error code */ - DEXIT(CTRL_TRACE, " - ret %zd\n", ret); - return ret; - } - - /* Which event is it ? */ - switch(irnet_events.log[ap->event_index].event) - { - case IRNET_DISCOVER: - snprintf(event, sizeof(event), - "Discovered %08x (%s) behind %08x {hints %02X-%02X}\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].saddr, - irnet_events.log[ap->event_index].hints.byte[0], - irnet_events.log[ap->event_index].hints.byte[1]); - break; - case IRNET_EXPIRE: - snprintf(event, sizeof(event), - "Expired %08x (%s) behind %08x {hints %02X-%02X}\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].saddr, - irnet_events.log[ap->event_index].hints.byte[0], - irnet_events.log[ap->event_index].hints.byte[1]); - break; - case IRNET_CONNECT_TO: - snprintf(event, sizeof(event), "Connected to %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); - break; - case IRNET_CONNECT_FROM: - snprintf(event, sizeof(event), "Connection from %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); - break; - case IRNET_REQUEST_FROM: - snprintf(event, sizeof(event), "Request from %08x (%s) behind %08x\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].saddr); - break; - case IRNET_NOANSWER_FROM: - snprintf(event, sizeof(event), "No-answer from %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); - break; - case IRNET_BLOCKED_LINK: - snprintf(event, sizeof(event), "Blocked link with %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); - break; - case IRNET_DISCONNECT_FROM: - snprintf(event, sizeof(event), "Disconnection from %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); - break; - case IRNET_DISCONNECT_TO: - snprintf(event, sizeof(event), "Disconnected to %08x (%s)\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name); - break; - default: - snprintf(event, sizeof(event), "Bug\n"); - } - /* Increment our event index */ - ap->event_index = (ap->event_index + 1) % IRNET_MAX_EVENTS; - - DEBUG(CTRL_INFO, "Event is :%s", event); - - count = min(strlen(event), count); - if (copy_to_user(buf, event, count)) - { - DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); - return -EFAULT; - } - - DEXIT(CTRL_TRACE, "\n"); - return count; -} - -/*------------------------------------------------------------------*/ -/* - * Poll : called when someone do a select on /dev/irnet. - * Just check if there are new events... - */ -static inline __poll_t -irnet_ctrl_poll(irnet_socket * ap, - struct file * file, - poll_table * wait) -{ - __poll_t mask; - - DENTER(CTRL_TRACE, "(ap=0x%p)\n", ap); - - poll_wait(file, &irnet_events.rwait, wait); - mask = EPOLLOUT | EPOLLWRNORM; - /* If there is unread events */ - if(ap->event_index != irnet_events.index) - mask |= EPOLLIN | EPOLLRDNORM; -#ifdef INITIAL_DISCOVERY - if(ap->disco_number != -1) - { - /* Test if it's the first time and therefore we need to get the log */ - if(ap->discoveries == NULL) - irnet_get_discovery_log(ap); - /* Recheck */ - if(ap->disco_number != -1) - mask |= EPOLLIN | EPOLLRDNORM; - } -#endif /* INITIAL_DISCOVERY */ - - DEXIT(CTRL_TRACE, " - mask=0x%X\n", mask); - return mask; -} - - -/*********************** FILESYSTEM CALLBACKS ***********************/ -/* - * Implement the usual open, read, write functions that will be called - * by the file system when some action is performed on /dev/irnet. - * Most of those actions will in fact be performed by "pppd" or - * the control channel, we just act as a redirector... - */ - -/*------------------------------------------------------------------*/ -/* - * Open : when somebody open /dev/irnet - * We basically create a new instance of irnet and initialise it. - */ -static int -dev_irnet_open(struct inode * inode, - struct file * file) -{ - struct irnet_socket * ap; - int err; - - DENTER(FS_TRACE, "(file=0x%p)\n", file); - -#ifdef SECURE_DEVIRNET - /* This could (should?) be enforced by the permissions on /dev/irnet. */ - if(!capable(CAP_NET_ADMIN)) - return -EPERM; -#endif /* SECURE_DEVIRNET */ - - /* Allocate a private structure for this IrNET instance */ - ap = kzalloc(sizeof(*ap), GFP_KERNEL); - DABORT(ap == NULL, -ENOMEM, FS_ERROR, "Can't allocate struct irnet...\n"); - - /* initialize the irnet structure */ - ap->file = file; - - /* PPP channel setup */ - ap->ppp_open = 0; - ap->chan.private = ap; - ap->chan.ops = &irnet_ppp_ops; - ap->chan.mtu = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN); - ap->chan.hdrlen = 2 + TTP_MAX_HEADER; /* for A/C + Max IrDA hdr */ - /* PPP parameters */ - ap->mru = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN); - ap->xaccm[0] = ~0U; - ap->xaccm[3] = 0x60000000U; - ap->raccm = ~0U; - - /* Setup the IrDA part... */ - err = irda_irnet_create(ap); - if(err) - { - DERROR(FS_ERROR, "Can't setup IrDA link...\n"); - kfree(ap); - - return err; - } - - /* For the control channel */ - ap->event_index = irnet_events.index; /* Cancel all past events */ - - mutex_init(&ap->lock); - - /* Put our stuff where we will be able to find it later */ - file->private_data = ap; - - DEXIT(FS_TRACE, " - ap=0x%p\n", ap); - - return 0; -} - - -/*------------------------------------------------------------------*/ -/* - * Close : when somebody close /dev/irnet - * Destroy the instance of /dev/irnet - */ -static int -dev_irnet_close(struct inode * inode, - struct file * file) -{ - irnet_socket * ap = file->private_data; - - DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n", - file, ap); - DABORT(ap == NULL, 0, FS_ERROR, "ap is NULL !!!\n"); - - /* Detach ourselves */ - file->private_data = NULL; - - /* Close IrDA stuff */ - irda_irnet_destroy(ap); - - /* Disconnect from the generic PPP layer if not already done */ - if(ap->ppp_open) - { - DERROR(FS_ERROR, "Channel still registered - deregistering !\n"); - ap->ppp_open = 0; - ppp_unregister_channel(&ap->chan); - } - - kfree(ap); - - DEXIT(FS_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Write does nothing. - * (we receive packet from ppp_generic through ppp_irnet_send()) - */ -static ssize_t -dev_irnet_write(struct file * file, - const char __user *buf, - size_t count, - loff_t * ppos) -{ - irnet_socket * ap = file->private_data; - - DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n", - file, ap, count); - DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n"); - - /* If we are connected to ppp_generic, let it handle the job */ - if(ap->ppp_open) - return -EAGAIN; - else - return irnet_ctrl_write(ap, buf, count); -} - -/*------------------------------------------------------------------*/ -/* - * Read doesn't do much either. - * (pppd poll us, but ultimately reads through /dev/ppp) - */ -static ssize_t -dev_irnet_read(struct file * file, - char __user * buf, - size_t count, - loff_t * ppos) -{ - irnet_socket * ap = file->private_data; - - DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n", - file, ap, count); - DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n"); - - /* If we are connected to ppp_generic, let it handle the job */ - if(ap->ppp_open) - return -EAGAIN; - else - return irnet_ctrl_read(ap, file, buf, count); -} - -/*------------------------------------------------------------------*/ -/* - * Poll : called when someone do a select on /dev/irnet - */ -static __poll_t -dev_irnet_poll(struct file * file, - poll_table * wait) -{ - irnet_socket * ap = file->private_data; - __poll_t mask; - - DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n", - file, ap); - - mask = EPOLLOUT | EPOLLWRNORM; - DABORT(ap == NULL, mask, FS_ERROR, "ap is NULL !!!\n"); - - /* If we are connected to ppp_generic, let it handle the job */ - if(!ap->ppp_open) - mask |= irnet_ctrl_poll(ap, file, wait); - - DEXIT(FS_TRACE, " - mask=0x%X\n", mask); - return mask; -} - -/*------------------------------------------------------------------*/ -/* - * IOCtl : Called when someone does some ioctls on /dev/irnet - * This is the way pppd configure us and control us while the PPP - * instance is active. - */ -static long -dev_irnet_ioctl( - struct file * file, - unsigned int cmd, - unsigned long arg) -{ - irnet_socket * ap = file->private_data; - int err; - int val; - void __user *argp = (void __user *)arg; - - DENTER(FS_TRACE, "(file=0x%p, ap=0x%p, cmd=0x%X)\n", - file, ap, cmd); - - /* Basic checks... */ - DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n"); -#ifdef SECURE_DEVIRNET - if(!capable(CAP_NET_ADMIN)) - return -EPERM; -#endif /* SECURE_DEVIRNET */ - - err = -EFAULT; - switch(cmd) - { - /* Set discipline (should be N_SYNC_PPP or N_TTY) */ - case TIOCSETD: - if(get_user(val, (int __user *)argp)) - break; - if((val == N_SYNC_PPP) || (val == N_PPP)) - { - DEBUG(FS_INFO, "Entering PPP discipline.\n"); - /* PPP channel setup (ap->chan in configured in dev_irnet_open())*/ - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - - err = ppp_register_channel(&ap->chan); - if(err == 0) - { - /* Our ppp side is active */ - ap->ppp_open = 1; - - DEBUG(FS_INFO, "Trying to establish a connection.\n"); - /* Setup the IrDA link now - may fail... */ - irda_irnet_connect(ap); - } - else - DERROR(FS_ERROR, "Can't setup PPP channel...\n"); - - mutex_unlock(&ap->lock); - } - else - { - /* In theory, should be N_TTY */ - DEBUG(FS_INFO, "Exiting PPP discipline.\n"); - /* Disconnect from the generic PPP layer */ - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - - if(ap->ppp_open) - { - ap->ppp_open = 0; - ppp_unregister_channel(&ap->chan); - } - else - DERROR(FS_ERROR, "Channel not registered !\n"); - err = 0; - - mutex_unlock(&ap->lock); - } - break; - - /* Query PPP channel and unit number */ - case PPPIOCGCHAN: - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - - if(ap->ppp_open && !put_user(ppp_channel_index(&ap->chan), - (int __user *)argp)) - err = 0; - - mutex_unlock(&ap->lock); - break; - case PPPIOCGUNIT: - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - - if(ap->ppp_open && !put_user(ppp_unit_number(&ap->chan), - (int __user *)argp)) - err = 0; - - mutex_unlock(&ap->lock); - break; - - /* All these ioctls can be passed both directly and from ppp_generic, - * so we just deal with them in one place... - */ - case PPPIOCGFLAGS: - case PPPIOCSFLAGS: - case PPPIOCGASYNCMAP: - case PPPIOCSASYNCMAP: - case PPPIOCGRASYNCMAP: - case PPPIOCSRASYNCMAP: - case PPPIOCGXASYNCMAP: - case PPPIOCSXASYNCMAP: - case PPPIOCGMRU: - case PPPIOCSMRU: - DEBUG(FS_INFO, "Standard PPP ioctl.\n"); - if(!capable(CAP_NET_ADMIN)) - err = -EPERM; - else { - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - - err = ppp_irnet_ioctl(&ap->chan, cmd, arg); - - mutex_unlock(&ap->lock); - } - break; - - /* TTY IOCTLs : Pretend that we are a tty, to keep pppd happy */ - /* Get termios */ - case TCGETS: - DEBUG(FS_INFO, "Get termios.\n"); - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - -#ifndef TCGETS2 - if(!kernel_termios_to_user_termios((struct termios __user *)argp, &ap->termios)) - err = 0; -#else - if(kernel_termios_to_user_termios_1((struct termios __user *)argp, &ap->termios)) - err = 0; -#endif - - mutex_unlock(&ap->lock); - break; - /* Set termios */ - case TCSETSF: - DEBUG(FS_INFO, "Set termios.\n"); - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - -#ifndef TCGETS2 - if(!user_termios_to_kernel_termios(&ap->termios, (struct termios __user *)argp)) - err = 0; -#else - if(!user_termios_to_kernel_termios_1(&ap->termios, (struct termios __user *)argp)) - err = 0; -#endif - - mutex_unlock(&ap->lock); - break; - - /* Set DTR/RTS */ - case TIOCMBIS: - case TIOCMBIC: - /* Set exclusive/non-exclusive mode */ - case TIOCEXCL: - case TIOCNXCL: - DEBUG(FS_INFO, "TTY compatibility.\n"); - err = 0; - break; - - case TCGETA: - DEBUG(FS_INFO, "TCGETA\n"); - break; - - case TCFLSH: - DEBUG(FS_INFO, "TCFLSH\n"); - /* Note : this will flush buffers in PPP, so it *must* be done - * We should also worry that we don't accept junk here and that - * we get rid of our own buffers */ -#ifdef FLUSH_TO_PPP - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - ppp_output_wakeup(&ap->chan); - mutex_unlock(&ap->lock); -#endif /* FLUSH_TO_PPP */ - err = 0; - break; - - case FIONREAD: - DEBUG(FS_INFO, "FIONREAD\n"); - val = 0; - if(put_user(val, (int __user *)argp)) - break; - err = 0; - break; - - default: - DERROR(FS_ERROR, "Unsupported ioctl (0x%X)\n", cmd); - err = -ENOTTY; - } - - DEXIT(FS_TRACE, " - err = 0x%X\n", err); - return err; -} - -/************************** PPP CALLBACKS **************************/ -/* - * This are the functions that the generic PPP driver in the kernel - * will call to communicate to us. - */ - -/*------------------------------------------------------------------*/ -/* - * Prepare the ppp frame for transmission over the IrDA socket. - * We make sure that the header space is enough, and we change ppp header - * according to flags passed by pppd. - * This is not a callback, but just a helper function used in ppp_irnet_send() - */ -static inline struct sk_buff * -irnet_prepare_skb(irnet_socket * ap, - struct sk_buff * skb) -{ - unsigned char * data; - int proto; /* PPP protocol */ - int islcp; /* Protocol == LCP */ - int needaddr; /* Need PPP address */ - - DENTER(PPP_TRACE, "(ap=0x%p, skb=0x%p)\n", - ap, skb); - - /* Extract PPP protocol from the frame */ - data = skb->data; - proto = (data[0] << 8) + data[1]; - - /* LCP packets with codes between 1 (configure-request) - * and 7 (code-reject) must be sent as though no options - * have been negotiated. */ - islcp = (proto == PPP_LCP) && (1 <= data[2]) && (data[2] <= 7); - - /* compress protocol field if option enabled */ - if((data[0] == 0) && (ap->flags & SC_COMP_PROT) && (!islcp)) - skb_pull(skb,1); - - /* Check if we need address/control fields */ - needaddr = 2*((ap->flags & SC_COMP_AC) == 0 || islcp); - - /* Is the skb headroom large enough to contain all IrDA-headers? */ - if((skb_headroom(skb) < (ap->max_header_size + needaddr)) || - (skb_shared(skb))) - { - struct sk_buff * new_skb; - - DEBUG(PPP_INFO, "Reallocating skb\n"); - - /* Create a new skb */ - new_skb = skb_realloc_headroom(skb, ap->max_header_size + needaddr); - - /* We have to free the original skb anyway */ - dev_kfree_skb(skb); - - /* Did the realloc succeed ? */ - DABORT(new_skb == NULL, NULL, PPP_ERROR, "Could not realloc skb\n"); - - /* Use the new skb instead */ - skb = new_skb; - } - - /* prepend address/control fields if necessary */ - if(needaddr) - { - skb_push(skb, 2); - skb->data[0] = PPP_ALLSTATIONS; - skb->data[1] = PPP_UI; - } - - DEXIT(PPP_TRACE, "\n"); - - return skb; -} - -/*------------------------------------------------------------------*/ -/* - * Send a packet to the peer over the IrTTP connection. - * Returns 1 iff the packet was accepted. - * Returns 0 iff packet was not consumed. - * If the packet was not accepted, we will call ppp_output_wakeup - * at some later time to reactivate flow control in ppp_generic. - */ -static int -ppp_irnet_send(struct ppp_channel * chan, - struct sk_buff * skb) -{ - irnet_socket * self = (struct irnet_socket *) chan->private; - int ret; - - DENTER(PPP_TRACE, "(channel=0x%p, ap/self=0x%p)\n", - chan, self); - - /* Check if things are somewhat valid... */ - DASSERT(self != NULL, 0, PPP_ERROR, "Self is NULL !!!\n"); - - /* Check if we are connected */ - if(!(test_bit(0, &self->ttp_open))) - { -#ifdef CONNECT_IN_SEND - /* Let's try to connect one more time... */ - /* Note : we won't be connected after this call, but we should be - * ready for next packet... */ - /* If we are already connecting, this will fail */ - irda_irnet_connect(self); -#endif /* CONNECT_IN_SEND */ - - DEBUG(PPP_INFO, "IrTTP not ready ! (%ld-%ld)\n", - self->ttp_open, self->ttp_connect); - - /* Note : we can either drop the packet or block the packet. - * - * Blocking the packet allow us a better connection time, - * because by calling ppp_output_wakeup() we can have - * ppp_generic resending the LCP request immediately to us, - * rather than waiting for one of pppd periodic transmission of - * LCP request. - * - * On the other hand, if we block all packet, all those periodic - * transmissions of pppd accumulate in ppp_generic, creating a - * backlog of LCP request. When we eventually connect later on, - * we have to transmit all this backlog before we can connect - * proper (if we don't timeout before). - * - * The current strategy is as follow : - * While we are attempting to connect, we block packets to get - * a better connection time. - * If we fail to connect, we drain the queue and start dropping packets - */ -#ifdef BLOCK_WHEN_CONNECT - /* If we are attempting to connect */ - if(test_bit(0, &self->ttp_connect)) - { - /* Blocking packet, ppp_generic will retry later */ - return 0; - } -#endif /* BLOCK_WHEN_CONNECT */ - - /* Dropping packet, pppd will retry later */ - dev_kfree_skb(skb); - return 1; - } - - /* Check if the queue can accept any packet, otherwise block */ - if(self->tx_flow != FLOW_START) - DRETURN(0, PPP_INFO, "IrTTP queue full (%d skbs)...\n", - skb_queue_len(&self->tsap->tx_queue)); - - /* Prepare ppp frame for transmission */ - skb = irnet_prepare_skb(self, skb); - DABORT(skb == NULL, 1, PPP_ERROR, "Prepare skb for Tx failed.\n"); - - /* Send the packet to IrTTP */ - ret = irttp_data_request(self->tsap, skb); - if(ret < 0) - { - /* - * > IrTTPs tx queue is full, so we just have to - * > drop the frame! You might think that we should - * > just return -1 and don't deallocate the frame, - * > but that is dangerous since it's possible that - * > we have replaced the original skb with a new - * > one with larger headroom, and that would really - * > confuse do_dev_queue_xmit() in dev.c! I have - * > tried :-) DB - * Correction : we verify the flow control above (self->tx_flow), - * so we come here only if IrTTP doesn't like the packet (empty, - * too large, IrTTP not connected). In those rare cases, it's ok - * to drop it, we don't want to see it here again... - * Jean II - */ - DERROR(PPP_ERROR, "IrTTP doesn't like this packet !!! (0x%X)\n", ret); - /* irttp_data_request already free the packet */ - } - - DEXIT(PPP_TRACE, "\n"); - return 1; /* Packet has been consumed */ -} - -/*------------------------------------------------------------------*/ -/* - * Take care of the ioctls that ppp_generic doesn't want to deal with... - * Note : we are also called from dev_irnet_ioctl(). - */ -static int -ppp_irnet_ioctl(struct ppp_channel * chan, - unsigned int cmd, - unsigned long arg) -{ - irnet_socket * ap = (struct irnet_socket *) chan->private; - int err; - int val; - u32 accm[8]; - void __user *argp = (void __user *)arg; - - DENTER(PPP_TRACE, "(channel=0x%p, ap=0x%p, cmd=0x%X)\n", - chan, ap, cmd); - - /* Basic checks... */ - DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n"); - - err = -EFAULT; - switch(cmd) - { - /* PPP flags */ - case PPPIOCGFLAGS: - val = ap->flags | ap->rbits; - if(put_user(val, (int __user *) argp)) - break; - err = 0; - break; - case PPPIOCSFLAGS: - if(get_user(val, (int __user *) argp)) - break; - ap->flags = val & ~SC_RCV_BITS; - ap->rbits = val & SC_RCV_BITS; - err = 0; - break; - - /* Async map stuff - all dummy to please pppd */ - case PPPIOCGASYNCMAP: - if(put_user(ap->xaccm[0], (u32 __user *) argp)) - break; - err = 0; - break; - case PPPIOCSASYNCMAP: - if(get_user(ap->xaccm[0], (u32 __user *) argp)) - break; - err = 0; - break; - case PPPIOCGRASYNCMAP: - if(put_user(ap->raccm, (u32 __user *) argp)) - break; - err = 0; - break; - case PPPIOCSRASYNCMAP: - if(get_user(ap->raccm, (u32 __user *) argp)) - break; - err = 0; - break; - case PPPIOCGXASYNCMAP: - if(copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm))) - break; - err = 0; - break; - case PPPIOCSXASYNCMAP: - if(copy_from_user(accm, argp, sizeof(accm))) - break; - accm[2] &= ~0x40000000U; /* can't escape 0x5e */ - accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ - memcpy(ap->xaccm, accm, sizeof(ap->xaccm)); - err = 0; - break; - - /* Max PPP frame size */ - case PPPIOCGMRU: - if(put_user(ap->mru, (int __user *) argp)) - break; - err = 0; - break; - case PPPIOCSMRU: - if(get_user(val, (int __user *) argp)) - break; - if(val < PPP_MRU) - val = PPP_MRU; - ap->mru = val; - err = 0; - break; - - default: - DEBUG(PPP_INFO, "Unsupported ioctl (0x%X)\n", cmd); - err = -ENOIOCTLCMD; - } - - DEXIT(PPP_TRACE, " - err = 0x%X\n", err); - return err; -} - -/************************** INITIALISATION **************************/ -/* - * Module initialisation and all that jazz... - */ - -/*------------------------------------------------------------------*/ -/* - * Hook our device callbacks in the filesystem, to connect our code - * to /dev/irnet - */ -static inline int __init -ppp_irnet_init(void) -{ - int err = 0; - - DENTER(MODULE_TRACE, "()\n"); - - /* Allocate ourselves as a minor in the misc range */ - err = misc_register(&irnet_misc_device); - - DEXIT(MODULE_TRACE, "\n"); - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Cleanup at exit... - */ -static inline void __exit -ppp_irnet_cleanup(void) -{ - DENTER(MODULE_TRACE, "()\n"); - - /* De-allocate /dev/irnet minor in misc range */ - misc_deregister(&irnet_misc_device); - - DEXIT(MODULE_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Module main entry point - */ -static int __init -irnet_init(void) -{ - int err; - - /* Initialise both parts... */ - err = irda_irnet_init(); - if(!err) - err = ppp_irnet_init(); - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Module exit - */ -static void __exit -irnet_cleanup(void) -{ - irda_irnet_cleanup(); - ppp_irnet_cleanup(); -} - -/*------------------------------------------------------------------*/ -/* - * Module magic - */ -module_init(irnet_init); -module_exit(irnet_cleanup); -MODULE_AUTHOR("Jean Tourrilhes "); -MODULE_DESCRIPTION("IrNET : Synchronous PPP over IrDA"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV(10, 187); diff --git a/drivers/staging/irda/net/irnet/irnet_ppp.h b/drivers/staging/irda/net/irnet/irnet_ppp.h deleted file mode 100644 index e6d5aa2a8aac..000000000000 --- a/drivers/staging/irda/net/irnet/irnet_ppp.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * IrNET protocol module : Synchronous PPP over an IrDA socket. - * - * Jean II - HPL `00 - - * - * This file contains all definitions and declarations necessary for the - * PPP part of the IrNET module. - * This file is a private header, so other modules don't want to know - * what's in there... - */ - -#ifndef IRNET_PPP_H -#define IRNET_PPP_H - -/***************************** INCLUDES *****************************/ - -#include "irnet.h" /* Module global include */ -#include - -/************************ CONSTANTS & MACROS ************************/ - -/* IrNET control channel stuff */ -#define IRNET_MAX_COMMAND 256 /* Max length of a command line */ - -/* PPP hardcore stuff */ - -/* Bits in rbits (PPP flags in irnet struct) */ -#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) - -/* Bit numbers in busy */ -#define XMIT_BUSY 0 -#define RECV_BUSY 1 -#define XMIT_WAKEUP 2 -#define XMIT_FULL 3 - -/* Queue management */ -#define PPPSYNC_MAX_RQLEN 32 /* arbitrary */ - -/****************************** TYPES ******************************/ - - -/**************************** PROTOTYPES ****************************/ - -/* ----------------------- CONTROL CHANNEL ----------------------- */ -static inline ssize_t - irnet_ctrl_write(irnet_socket *, - const char *, - size_t); -static inline ssize_t - irnet_ctrl_read(irnet_socket *, - struct file *, - char *, - size_t); -static inline unsigned int - irnet_ctrl_poll(irnet_socket *, - struct file *, - poll_table *); -/* ----------------------- CHARACTER DEVICE ----------------------- */ -static int - dev_irnet_open(struct inode *, /* fs callback : open */ - struct file *), - dev_irnet_close(struct inode *, - struct file *); -static ssize_t - dev_irnet_write(struct file *, - const char __user *, - size_t, - loff_t *), - dev_irnet_read(struct file *, - char __user *, - size_t, - loff_t *); -static __poll_t - dev_irnet_poll(struct file *, - poll_table *); -static long - dev_irnet_ioctl(struct file *, - unsigned int, - unsigned long); -/* ------------------------ PPP INTERFACE ------------------------ */ -static inline struct sk_buff * - irnet_prepare_skb(irnet_socket *, - struct sk_buff *); -static int - ppp_irnet_send(struct ppp_channel *, - struct sk_buff *); -static int - ppp_irnet_ioctl(struct ppp_channel *, - unsigned int, - unsigned long); - -/**************************** VARIABLES ****************************/ - -/* Filesystem callbacks (to call us) */ -static const struct file_operations irnet_device_fops = -{ - .owner = THIS_MODULE, - .read = dev_irnet_read, - .write = dev_irnet_write, - .poll = dev_irnet_poll, - .unlocked_ioctl = dev_irnet_ioctl, - .open = dev_irnet_open, - .release = dev_irnet_close, - .llseek = noop_llseek, - /* Also : llseek, readdir, mmap, flush, fsync, fasync, lock, readv, writev */ -}; - -/* Structure so that the misc major (drivers/char/misc.c) take care of us... */ -static struct miscdevice irnet_misc_device = -{ - .minor = IRNET_MINOR, - .name = "irnet", - .fops = &irnet_device_fops -}; - -#endif /* IRNET_PPP_H */ diff --git a/drivers/staging/irda/net/irnetlink.c b/drivers/staging/irda/net/irnetlink.c deleted file mode 100644 index 7fc340e574cf..000000000000 --- a/drivers/staging/irda/net/irnetlink.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * IrDA netlink layer, for stack configuration. - * - * Copyright (c) 2007 Samuel Ortiz - * - * Partly based on the 802.11 nelink implementation - * (see net/wireless/nl80211.c) which is: - * Copyright 2006 Johannes Berg - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - - - -static struct genl_family irda_nl_family; - -static struct net_device * ifname_to_netdev(struct net *net, struct genl_info *info) -{ - char * ifname; - - if (!info->attrs[IRDA_NL_ATTR_IFNAME]) - return NULL; - - ifname = nla_data(info->attrs[IRDA_NL_ATTR_IFNAME]); - - pr_debug("%s(): Looking for %s\n", __func__, ifname); - - return dev_get_by_name(net, ifname); -} - -static int irda_nl_set_mode(struct sk_buff *skb, struct genl_info *info) -{ - struct net_device * dev; - struct irlap_cb * irlap; - u32 mode; - - if (!info->attrs[IRDA_NL_ATTR_MODE]) - return -EINVAL; - - mode = nla_get_u32(info->attrs[IRDA_NL_ATTR_MODE]); - - pr_debug("%s(): Switching to mode: %d\n", __func__, mode); - - dev = ifname_to_netdev(&init_net, info); - if (!dev) - return -ENODEV; - - irlap = (struct irlap_cb *)dev->atalk_ptr; - if (!irlap) { - dev_put(dev); - return -ENODEV; - } - - irlap->mode = mode; - - dev_put(dev); - - return 0; -} - -static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info) -{ - struct net_device * dev; - struct irlap_cb * irlap; - struct sk_buff *msg; - void *hdr; - int ret = -ENOBUFS; - - dev = ifname_to_netdev(&init_net, info); - if (!dev) - return -ENODEV; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) { - dev_put(dev); - return -ENOMEM; - } - - irlap = (struct irlap_cb *)dev->atalk_ptr; - if (!irlap) { - ret = -ENODEV; - goto err_out; - } - - hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, - &irda_nl_family, 0, IRDA_NL_CMD_GET_MODE); - if (hdr == NULL) { - ret = -EMSGSIZE; - goto err_out; - } - - if(nla_put_string(msg, IRDA_NL_ATTR_IFNAME, - dev->name)) - goto err_out; - - if(nla_put_u32(msg, IRDA_NL_ATTR_MODE, irlap->mode)) - goto err_out; - - genlmsg_end(msg, hdr); - - return genlmsg_reply(msg, info); - - err_out: - nlmsg_free(msg); - dev_put(dev); - - return ret; -} - -static const struct nla_policy irda_nl_policy[IRDA_NL_ATTR_MAX + 1] = { - [IRDA_NL_ATTR_IFNAME] = { .type = NLA_NUL_STRING, - .len = IFNAMSIZ-1 }, - [IRDA_NL_ATTR_MODE] = { .type = NLA_U32 }, -}; - -static const struct genl_ops irda_nl_ops[] = { - { - .cmd = IRDA_NL_CMD_SET_MODE, - .doit = irda_nl_set_mode, - .policy = irda_nl_policy, - .flags = GENL_ADMIN_PERM, - }, - { - .cmd = IRDA_NL_CMD_GET_MODE, - .doit = irda_nl_get_mode, - .policy = irda_nl_policy, - /* can be retrieved by unprivileged users */ - }, - -}; - -static struct genl_family irda_nl_family __ro_after_init = { - .name = IRDA_NL_NAME, - .hdrsize = 0, - .version = IRDA_NL_VERSION, - .maxattr = IRDA_NL_CMD_MAX, - .module = THIS_MODULE, - .ops = irda_nl_ops, - .n_ops = ARRAY_SIZE(irda_nl_ops), -}; - -int __init irda_nl_register(void) -{ - return genl_register_family(&irda_nl_family); -} - -void irda_nl_unregister(void) -{ - genl_unregister_family(&irda_nl_family); -} diff --git a/drivers/staging/irda/net/irproc.c b/drivers/staging/irda/net/irproc.c deleted file mode 100644 index 77cfdde9d82f..000000000000 --- a/drivers/staging/irda/net/irproc.c +++ /dev/null @@ -1,96 +0,0 @@ -/********************************************************************* - * - * Filename: irproc.c - * Version: 1.0 - * Description: Various entries in the /proc file system - * Status: Experimental. - * Author: Thomas Davis, - * Created at: Sat Feb 21 21:33:24 1998 - * Modified at: Sun Nov 14 08:54:54 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999, Dag Brattli - * Copyright (c) 1998, Thomas Davis, , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * I, Thomas Davis, provide no warranty for any of this software. - * This material is provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include - -#include -#include -#include - -extern const struct file_operations discovery_seq_fops; -extern const struct file_operations irlap_seq_fops; -extern const struct file_operations irlmp_seq_fops; -extern const struct file_operations irttp_seq_fops; -extern const struct file_operations irias_seq_fops; - -struct irda_entry { - const char *name; - const struct file_operations *fops; -}; - -struct proc_dir_entry *proc_irda; -EXPORT_SYMBOL(proc_irda); - -static const struct irda_entry irda_dirs[] = { - {"discovery", &discovery_seq_fops}, - {"irttp", &irttp_seq_fops}, - {"irlmp", &irlmp_seq_fops}, - {"irlap", &irlap_seq_fops}, - {"irias", &irias_seq_fops}, -}; - -/* - * Function irda_proc_register (void) - * - * Register irda entry in /proc file system - * - */ -void __init irda_proc_register(void) -{ - int i; - - proc_irda = proc_mkdir("irda", init_net.proc_net); - if (proc_irda == NULL) - return; - - for (i = 0; i < ARRAY_SIZE(irda_dirs); i++) - (void) proc_create(irda_dirs[i].name, 0, proc_irda, - irda_dirs[i].fops); -} - -/* - * Function irda_proc_unregister (void) - * - * Unregister irda entry in /proc file system - * - */ -void irda_proc_unregister(void) -{ - int i; - - if (proc_irda) { - for (i=0; i - * Created at: Tue Jun 9 13:29:31 1998 - * Modified at: Sun Dec 12 13:48:22 1999 - * Modified by: Dag Brattli - * Modified at: Thu Jan 4 14:29:10 CET 2001 - * Modified by: Marc Zyngier - * - * Copyright (C) 1998-1999, Aage Kvalnes - * Copyright (C) 1998, Dag Brattli, - * All Rights Reserved. - * - * This code is taken from the Vortex Operating System written by Aage - * Kvalnes. Aage has agreed that this code can use the GPL licence, - * although he does not use that licence in his own code. - * - * This copyright does however _not_ include the ELF hash() function - * which I currently don't know which licence or copyright it - * has. Please inform me if you know. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -/* - * NOTE : - * There are various problems with this package : - * o the hash function for ints is pathetic (but could be changed) - * o locking is sometime suspicious (especially during enumeration) - * o most users have only a few elements (== overhead) - * o most users never use search, so don't benefit from hashing - * Problem already fixed : - * o not 64 bit compliant (most users do hashv = (int) self) - * o hashbin_remove() is broken => use hashbin_remove_this() - * I think most users would be better served by a simple linked list - * (like include/linux/list.h) with a global spinlock per list. - * Jean II - */ - -/* - * Notes on the concurrent access to hashbin and other SMP issues - * ------------------------------------------------------------- - * Hashbins are very often in the IrDA stack a global repository of - * information, and therefore used in a very asynchronous manner following - * various events (driver calls, timers, user calls...). - * Therefore, very often it is highly important to consider the - * management of concurrent access to the hashbin and how to guarantee the - * consistency of the operations on it. - * - * First, we need to define the objective of locking : - * 1) Protect user data (content pointed by the hashbin) - * 2) Protect hashbin structure itself (linked list in each bin) - * - * OLD LOCKING - * ----------- - * - * The previous locking strategy, either HB_LOCAL or HB_GLOBAL were - * both inadequate in *both* aspect. - * o HB_GLOBAL was using a spinlock for each bin (local locking). - * o HB_LOCAL was disabling irq on *all* CPUs, so use a single - * global semaphore. - * The problems were : - * A) Global irq disabling is no longer supported by the kernel - * B) No protection for the hashbin struct global data - * o hashbin_delete() - * o hb_current - * C) No protection for user data in some cases - * - * A) HB_LOCAL use global irq disabling, so doesn't work on kernel - * 2.5.X. Even when it is supported (kernel 2.4.X and earlier), its - * performance is not satisfactory on SMP setups. Most hashbins were - * HB_LOCAL, so (A) definitely need fixing. - * B) HB_LOCAL could be modified to fix (B). However, because HB_GLOBAL - * lock only the individual bins, it will never be able to lock the - * global data, so can't do (B). - * C) Some functions return pointer to data that is still in the - * hashbin : - * o hashbin_find() - * o hashbin_get_first() - * o hashbin_get_next() - * As the data is still in the hashbin, it may be changed or free'd - * while the caller is examinimg the data. In those case, locking can't - * be done within the hashbin, but must include use of the data within - * the caller. - * The caller can easily do this with HB_LOCAL (just disable irqs). - * However, this is impossible with HB_GLOBAL because the caller has no - * way to know the proper bin, so don't know which spinlock to use. - * - * Quick summary : can no longer use HB_LOCAL, and HB_GLOBAL is - * fundamentally broken and will never work. - * - * NEW LOCKING - * ----------- - * - * To fix those problems, I've introduce a few changes in the - * hashbin locking : - * 1) New HB_LOCK scheme - * 2) hashbin->hb_spinlock - * 3) New hashbin usage policy - * - * HB_LOCK : - * ------- - * HB_LOCK is a locking scheme intermediate between the old HB_LOCAL - * and HB_GLOBAL. It uses a single spinlock to protect the whole content - * of the hashbin. As it is a single spinlock, it can protect the global - * data of the hashbin and not only the bins themselves. - * HB_LOCK can only protect some of the hashbin calls, so it only lock - * call that can be made 100% safe and leave other call unprotected. - * HB_LOCK in theory is slower than HB_GLOBAL, but as the hashbin - * content is always small contention is not high, so it doesn't matter - * much. HB_LOCK is probably faster than HB_LOCAL. - * - * hashbin->hb_spinlock : - * -------------------- - * The spinlock that HB_LOCK uses is available for caller, so that - * the caller can protect unprotected calls (see below). - * If the caller want to do entirely its own locking (HB_NOLOCK), he - * can do so and may use safely this spinlock. - * Locking is done like this : - * spin_lock_irqsave(&hashbin->hb_spinlock, flags); - * Releasing the lock : - * spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - * - * Safe & Protected calls : - * ---------------------- - * The following calls are safe or protected via HB_LOCK : - * o hashbin_new() -> safe - * o hashbin_delete() - * o hashbin_insert() - * o hashbin_remove_first() - * o hashbin_remove() - * o hashbin_remove_this() - * o HASHBIN_GET_SIZE() -> atomic - * - * The following calls only protect the hashbin itself : - * o hashbin_lock_find() - * o hashbin_find_next() - * - * Unprotected calls : - * ----------------- - * The following calls need to be protected by the caller : - * o hashbin_find() - * o hashbin_get_first() - * o hashbin_get_next() - * - * Locking Policy : - * -------------- - * If the hashbin is used only in a single thread of execution - * (explicitly or implicitely), you can use HB_NOLOCK - * If the calling module already provide concurrent access protection, - * you may use HB_NOLOCK. - * - * In all other cases, you need to use HB_LOCK and lock the hashbin - * every time before calling one of the unprotected calls. You also must - * use the pointer returned by the unprotected call within the locked - * region. - * - * Extra care for enumeration : - * -------------------------- - * hashbin_get_first() and hashbin_get_next() use the hashbin to - * store the current position, in hb_current. - * As long as the hashbin remains locked, this is safe. If you unlock - * the hashbin, the current position may change if anybody else modify - * or enumerate the hashbin. - * Summary : do the full enumeration while locked. - * - * Alternatively, you may use hashbin_find_next(). But, this will - * be slower, is more complex to use and doesn't protect the hashbin - * content. So, care is needed here as well. - * - * Other issues : - * ------------ - * I believe that we are overdoing it by using spin_lock_irqsave() - * and we should use only spin_lock_bh() or similar. But, I don't have - * the balls to try it out. - * Don't believe that because hashbin are now (somewhat) SMP safe - * that the rest of the code is. Higher layers tend to be safest, - * but LAP and LMP would need some serious dedicated love. - * - * Jean II - */ -#include -#include - -#include -#include - -/************************ QUEUE SUBROUTINES ************************/ - -/* - * Hashbin - */ -#define GET_HASHBIN(x) ( x & HASHBIN_MASK ) - -/* - * Function hash (name) - * - * This function hash the input string 'name' using the ELF hash - * function for strings. - */ -static __u32 hash( const char* name) -{ - __u32 h = 0; - __u32 g; - - while(*name) { - h = (h<<4) + *name++; - g = h & 0xf0000000; - if (g) - h ^=g>>24; - h &=~g; - } - return h; -} - -/* - * Function enqueue_first (queue, proc) - * - * Insert item first in queue. - * - */ -static void enqueue_first(irda_queue_t **queue, irda_queue_t* element) -{ - - /* - * Check if queue is empty. - */ - if ( *queue == NULL ) { - /* - * Queue is empty. Insert one element into the queue. - */ - element->q_next = element->q_prev = *queue = element; - - } else { - /* - * Queue is not empty. Insert element into front of queue. - */ - element->q_next = (*queue); - (*queue)->q_prev->q_next = element; - element->q_prev = (*queue)->q_prev; - (*queue)->q_prev = element; - (*queue) = element; - } -} - - -/* - * Function dequeue (queue) - * - * Remove first entry in queue - * - */ -static irda_queue_t *dequeue_first(irda_queue_t **queue) -{ - irda_queue_t *ret; - - pr_debug("dequeue_first()\n"); - - /* - * Set return value - */ - ret = *queue; - - if ( *queue == NULL ) { - /* - * Queue was empty. - */ - } else if ( (*queue)->q_next == *queue ) { - /* - * Queue only contained a single element. It will now be - * empty. - */ - *queue = NULL; - } else { - /* - * Queue contained several element. Remove the first one. - */ - (*queue)->q_prev->q_next = (*queue)->q_next; - (*queue)->q_next->q_prev = (*queue)->q_prev; - *queue = (*queue)->q_next; - } - - /* - * Return the removed entry (or NULL of queue was empty). - */ - return ret; -} - -/* - * Function dequeue_general (queue, element) - * - * - */ -static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element) -{ - irda_queue_t *ret; - - pr_debug("dequeue_general()\n"); - - /* - * Set return value - */ - ret = *queue; - - if ( *queue == NULL ) { - /* - * Queue was empty. - */ - } else if ( (*queue)->q_next == *queue ) { - /* - * Queue only contained a single element. It will now be - * empty. - */ - *queue = NULL; - - } else { - /* - * Remove specific element. - */ - element->q_prev->q_next = element->q_next; - element->q_next->q_prev = element->q_prev; - if ( (*queue) == element) - (*queue) = element->q_next; - } - - /* - * Return the removed entry (or NULL of queue was empty). - */ - return ret; -} - -/************************ HASHBIN MANAGEMENT ************************/ - -/* - * Function hashbin_create ( type, name ) - * - * Create hashbin! - * - */ -hashbin_t *hashbin_new(int type) -{ - hashbin_t* hashbin; - - /* - * Allocate new hashbin - */ - hashbin = kzalloc(sizeof(*hashbin), GFP_ATOMIC); - if (!hashbin) - return NULL; - - /* - * Initialize structure - */ - hashbin->hb_type = type; - hashbin->magic = HB_MAGIC; - //hashbin->hb_current = NULL; - - /* Make sure all spinlock's are unlocked */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_init(&hashbin->hb_spinlock); - } - - return hashbin; -} -EXPORT_SYMBOL(hashbin_new); - - -/* - * Function hashbin_delete (hashbin, free_func) - * - * Destroy hashbin, the free_func can be a user supplied special routine - * for deallocating this structure if it's complex. If not the user can - * just supply kfree, which should take care of the job. - */ -int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func) -{ - irda_queue_t* queue; - unsigned long flags = 0; - int i; - - IRDA_ASSERT(hashbin != NULL, return -1;); - IRDA_ASSERT(hashbin->magic == HB_MAGIC, return -1;); - - /* Synchronize */ - if (hashbin->hb_type & HB_LOCK) - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - - /* - * Free the entries in the hashbin, TODO: use hashbin_clear when - * it has been shown to work - */ - for (i = 0; i < HASHBIN_SIZE; i ++ ) { - while (1) { - queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]); - - if (!queue) - break; - - if (free_func) { - if (hashbin->hb_type & HB_LOCK) - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - free_func(queue); - if (hashbin->hb_type & HB_LOCK) - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - } - } - } - - /* Cleanup local data */ - hashbin->hb_current = NULL; - hashbin->magic = ~HB_MAGIC; - - /* Release lock */ - if (hashbin->hb_type & HB_LOCK) - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - - /* - * Free the hashbin structure - */ - kfree(hashbin); - - return 0; -} -EXPORT_SYMBOL(hashbin_delete); - -/********************* HASHBIN LIST OPERATIONS *********************/ - -/* - * Function hashbin_insert (hashbin, entry, name) - * - * Insert an entry into the hashbin - * - */ -void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, - const char* name) -{ - unsigned long flags = 0; - int bin; - - IRDA_ASSERT( hashbin != NULL, return;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return;); - - /* - * Locate hashbin - */ - if ( name ) - hashv = hash( name ); - bin = GET_HASHBIN( hashv ); - - /* Synchronize */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - /* - * Store name and key - */ - entry->q_hash = hashv; - if ( name ) - strlcpy( entry->q_name, name, sizeof(entry->q_name)); - - /* - * Insert new entry first - */ - enqueue_first( (irda_queue_t**) &hashbin->hb_queue[ bin ], - entry); - hashbin->hb_size++; - - /* Release lock */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ -} -EXPORT_SYMBOL(hashbin_insert); - -/* - * Function hashbin_remove_first (hashbin) - * - * Remove first entry of the hashbin - * - * Note : this function no longer use hashbin_remove(), but does things - * similar to hashbin_remove_this(), so can be considered safe. - * Jean II - */ -void *hashbin_remove_first( hashbin_t *hashbin) -{ - unsigned long flags = 0; - irda_queue_t *entry = NULL; - - /* Synchronize */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - entry = hashbin_get_first( hashbin); - if ( entry != NULL) { - int bin; - long hashv; - /* - * Locate hashbin - */ - hashv = entry->q_hash; - bin = GET_HASHBIN( hashv ); - - /* - * Dequeue the entry... - */ - dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ], - entry); - hashbin->hb_size--; - entry->q_next = NULL; - entry->q_prev = NULL; - - /* - * Check if this item is the currently selected item, and in - * that case we must reset hb_current - */ - if ( entry == hashbin->hb_current) - hashbin->hb_current = NULL; - } - - /* Release lock */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - return entry; -} - - -/* - * Function hashbin_remove (hashbin, hashv, name) - * - * Remove entry with the given name - * - * The use of this function is highly discouraged, because the whole - * concept behind hashbin_remove() is broken. In many cases, it's not - * possible to guarantee the unicity of the index (either hashv or name), - * leading to removing the WRONG entry. - * The only simple safe use is : - * hashbin_remove(hasbin, (int) self, NULL); - * In other case, you must think hard to guarantee unicity of the index. - * Jean II - */ -void* hashbin_remove( hashbin_t* hashbin, long hashv, const char* name) -{ - int bin, found = FALSE; - unsigned long flags = 0; - irda_queue_t* entry; - - IRDA_ASSERT( hashbin != NULL, return NULL;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); - - /* - * Locate hashbin - */ - if ( name ) - hashv = hash( name ); - bin = GET_HASHBIN( hashv ); - - /* Synchronize */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - /* - * Search for entry - */ - entry = hashbin->hb_queue[ bin ]; - if ( entry ) { - do { - /* - * Check for key - */ - if ( entry->q_hash == hashv ) { - /* - * Name compare too? - */ - if ( name ) { - if ( strcmp( entry->q_name, name) == 0) - { - found = TRUE; - break; - } - } else { - found = TRUE; - break; - } - } - entry = entry->q_next; - } while ( entry != hashbin->hb_queue[ bin ] ); - } - - /* - * If entry was found, dequeue it - */ - if ( found ) { - dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ], - entry); - hashbin->hb_size--; - - /* - * Check if this item is the currently selected item, and in - * that case we must reset hb_current - */ - if ( entry == hashbin->hb_current) - hashbin->hb_current = NULL; - } - - /* Release lock */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - - /* Return */ - if ( found ) - return entry; - else - return NULL; - -} -EXPORT_SYMBOL(hashbin_remove); - -/* - * Function hashbin_remove_this (hashbin, entry) - * - * Remove entry with the given name - * - * In some cases, the user of hashbin can't guarantee the unicity - * of either the hashv or name. - * In those cases, using the above function is guaranteed to cause troubles, - * so we use this one instead... - * And by the way, it's also faster, because we skip the search phase ;-) - */ -void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry) -{ - unsigned long flags = 0; - int bin; - long hashv; - - IRDA_ASSERT( hashbin != NULL, return NULL;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); - IRDA_ASSERT( entry != NULL, return NULL;); - - /* Synchronize */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - /* Check if valid and not already removed... */ - if((entry->q_next == NULL) || (entry->q_prev == NULL)) { - entry = NULL; - goto out; - } - - /* - * Locate hashbin - */ - hashv = entry->q_hash; - bin = GET_HASHBIN( hashv ); - - /* - * Dequeue the entry... - */ - dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ], - entry); - hashbin->hb_size--; - entry->q_next = NULL; - entry->q_prev = NULL; - - /* - * Check if this item is the currently selected item, and in - * that case we must reset hb_current - */ - if ( entry == hashbin->hb_current) - hashbin->hb_current = NULL; -out: - /* Release lock */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - return entry; -} -EXPORT_SYMBOL(hashbin_remove_this); - -/*********************** HASHBIN ENUMERATION ***********************/ - -/* - * Function hashbin_common_find (hashbin, hashv, name) - * - * Find item with the given hashv or name - * - */ -void* hashbin_find( hashbin_t* hashbin, long hashv, const char* name ) -{ - int bin; - irda_queue_t* entry; - - pr_debug("hashbin_find()\n"); - - IRDA_ASSERT( hashbin != NULL, return NULL;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); - - /* - * Locate hashbin - */ - if ( name ) - hashv = hash( name ); - bin = GET_HASHBIN( hashv ); - - /* - * Search for entry - */ - entry = hashbin->hb_queue[ bin]; - if ( entry ) { - do { - /* - * Check for key - */ - if ( entry->q_hash == hashv ) { - /* - * Name compare too? - */ - if ( name ) { - if ( strcmp( entry->q_name, name ) == 0 ) { - return entry; - } - } else { - return entry; - } - } - entry = entry->q_next; - } while ( entry != hashbin->hb_queue[ bin ] ); - } - - return NULL; -} -EXPORT_SYMBOL(hashbin_find); - -/* - * Function hashbin_lock_find (hashbin, hashv, name) - * - * Find item with the given hashv or name - * - * Same, but with spinlock protection... - * I call it safe, but it's only safe with respect to the hashbin, not its - * content. - Jean II - */ -void* hashbin_lock_find( hashbin_t* hashbin, long hashv, const char* name ) -{ - unsigned long flags = 0; - irda_queue_t* entry; - - /* Synchronize */ - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - - /* - * Search for entry - */ - entry = hashbin_find(hashbin, hashv, name); - - /* Release lock */ - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - - return entry; -} -EXPORT_SYMBOL(hashbin_lock_find); - -/* - * Function hashbin_find (hashbin, hashv, name, pnext) - * - * Find an item with the given hashv or name, and its successor - * - * This function allow to do concurrent enumerations without the - * need to lock over the whole session, because the caller keep the - * context of the search. On the other hand, it might fail and return - * NULL if the entry is removed. - Jean II - */ -void* hashbin_find_next( hashbin_t* hashbin, long hashv, const char* name, - void ** pnext) -{ - unsigned long flags = 0; - irda_queue_t* entry; - - /* Synchronize */ - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - - /* - * Search for current entry - * This allow to check if the current item is still in the - * hashbin or has been removed. - */ - entry = hashbin_find(hashbin, hashv, name); - - /* - * Trick hashbin_get_next() to return what we want - */ - if(entry) { - hashbin->hb_current = entry; - *pnext = hashbin_get_next( hashbin ); - } else - *pnext = NULL; - - /* Release lock */ - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - - return entry; -} - -/* - * Function hashbin_get_first (hashbin) - * - * Get a pointer to first element in hashbin, this function must be - * called before any calls to hashbin_get_next()! - * - */ -irda_queue_t *hashbin_get_first( hashbin_t* hashbin) -{ - irda_queue_t *entry; - int i; - - IRDA_ASSERT( hashbin != NULL, return NULL;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); - - if ( hashbin == NULL) - return NULL; - - for ( i = 0; i < HASHBIN_SIZE; i ++ ) { - entry = hashbin->hb_queue[ i]; - if ( entry) { - hashbin->hb_current = entry; - return entry; - } - } - /* - * Did not find any item in hashbin - */ - return NULL; -} -EXPORT_SYMBOL(hashbin_get_first); - -/* - * Function hashbin_get_next (hashbin) - * - * Get next item in hashbin. A series of hashbin_get_next() calls must - * be started by a call to hashbin_get_first(). The function returns - * NULL when all items have been traversed - * - * The context of the search is stored within the hashbin, so you must - * protect yourself from concurrent enumerations. - Jean II - */ -irda_queue_t *hashbin_get_next( hashbin_t *hashbin) -{ - irda_queue_t* entry; - int bin; - int i; - - IRDA_ASSERT( hashbin != NULL, return NULL;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); - - if ( hashbin->hb_current == NULL) { - IRDA_ASSERT( hashbin->hb_current != NULL, return NULL;); - return NULL; - } - entry = hashbin->hb_current->q_next; - bin = GET_HASHBIN( entry->q_hash); - - /* - * Make sure that we are not back at the beginning of the queue - * again - */ - if ( entry != hashbin->hb_queue[ bin ]) { - hashbin->hb_current = entry; - - return entry; - } - - /* - * Check that this is not the last queue in hashbin - */ - if ( bin >= HASHBIN_SIZE) - return NULL; - - /* - * Move to next queue in hashbin - */ - bin++; - for ( i = bin; i < HASHBIN_SIZE; i++ ) { - entry = hashbin->hb_queue[ i]; - if ( entry) { - hashbin->hb_current = entry; - - return entry; - } - } - return NULL; -} -EXPORT_SYMBOL(hashbin_get_next); diff --git a/drivers/staging/irda/net/irsysctl.c b/drivers/staging/irda/net/irsysctl.c deleted file mode 100644 index 873da5e7d428..000000000000 --- a/drivers/staging/irda/net/irsysctl.c +++ /dev/null @@ -1,258 +0,0 @@ -/********************************************************************* - * - * Filename: irsysctl.c - * Version: 1.0 - * Description: Sysctl interface for IrDA - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun May 24 22:12:06 1998 - * Modified at: Fri Jun 4 02:50:15 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include - -#include /* irda_debug */ -#include -#include -#include - -extern int sysctl_discovery; -extern int sysctl_discovery_slots; -extern int sysctl_discovery_timeout; -extern int sysctl_slot_timeout; -extern int sysctl_fast_poll_increase; -extern char sysctl_devname[]; -extern int sysctl_max_baud_rate; -extern unsigned int sysctl_min_tx_turn_time; -extern unsigned int sysctl_max_tx_data_size; -extern unsigned int sysctl_max_tx_window; -extern int sysctl_max_noreply_time; -extern int sysctl_warn_noreply_time; -extern int sysctl_lap_keepalive_time; - -extern struct irlmp_cb *irlmp; - -/* this is needed for the proc_dointvec_minmax - Jean II */ -static int max_discovery_slots = 16; /* ??? */ -static int min_discovery_slots = 1; -/* IrLAP 6.13.2 says 25ms to 10+70ms - allow higher since some devices - * seems to require it. (from Dag's comment) */ -static int max_slot_timeout = 160; -static int min_slot_timeout = 20; -static int max_max_baud_rate = 16000000; /* See qos.c - IrLAP spec */ -static int min_max_baud_rate = 2400; -static int max_min_tx_turn_time = 10000; /* See qos.c - IrLAP spec */ -static int min_min_tx_turn_time; -static int max_max_tx_data_size = 2048; /* See qos.c - IrLAP spec */ -static int min_max_tx_data_size = 64; -static int max_max_tx_window = 7; /* See qos.c - IrLAP spec */ -static int min_max_tx_window = 1; -static int max_max_noreply_time = 40; /* See qos.c - IrLAP spec */ -static int min_max_noreply_time = 3; -static int max_warn_noreply_time = 3; /* 3s == standard */ -static int min_warn_noreply_time = 1; /* 1s == min WD_TIMER */ -static int max_lap_keepalive_time = 10000; /* 10s */ -static int min_lap_keepalive_time = 100; /* 100us */ -/* For other sysctl, I've no idea of the range. Maybe Dag could help - * us on that - Jean II */ - -static int do_devname(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - int ret; - - ret = proc_dostring(table, write, buffer, lenp, ppos); - if (ret == 0 && write) { - struct ias_value *val; - - val = irias_new_string_value(sysctl_devname); - if (val) - irias_object_change_attribute("Device", "DeviceName", val); - } - return ret; -} - - -static int do_discovery(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - int ret; - - ret = proc_dointvec(table, write, buffer, lenp, ppos); - if (ret) - return ret; - - if (irlmp == NULL) - return -ENODEV; - - if (sysctl_discovery) - irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout*HZ); - else - del_timer_sync(&irlmp->discovery_timer); - - return ret; -} - -/* One file */ -static struct ctl_table irda_table[] = { - { - .procname = "discovery", - .data = &sysctl_discovery, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = do_discovery, - }, - { - .procname = "devname", - .data = sysctl_devname, - .maxlen = 65, - .mode = 0644, - .proc_handler = do_devname, - }, -#ifdef CONFIG_IRDA_FAST_RR - { - .procname = "fast_poll_increase", - .data = &sysctl_fast_poll_increase, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, -#endif - { - .procname = "discovery_slots", - .data = &sysctl_discovery_slots, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_discovery_slots, - .extra2 = &max_discovery_slots - }, - { - .procname = "discovery_timeout", - .data = &sysctl_discovery_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, - { - .procname = "slot_timeout", - .data = &sysctl_slot_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_slot_timeout, - .extra2 = &max_slot_timeout - }, - { - .procname = "max_baud_rate", - .data = &sysctl_max_baud_rate, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_max_baud_rate, - .extra2 = &max_max_baud_rate - }, - { - .procname = "min_tx_turn_time", - .data = &sysctl_min_tx_turn_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_min_tx_turn_time, - .extra2 = &max_min_tx_turn_time - }, - { - .procname = "max_tx_data_size", - .data = &sysctl_max_tx_data_size, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_max_tx_data_size, - .extra2 = &max_max_tx_data_size - }, - { - .procname = "max_tx_window", - .data = &sysctl_max_tx_window, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_max_tx_window, - .extra2 = &max_max_tx_window - }, - { - .procname = "max_noreply_time", - .data = &sysctl_max_noreply_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_max_noreply_time, - .extra2 = &max_max_noreply_time - }, - { - .procname = "warn_noreply_time", - .data = &sysctl_warn_noreply_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_warn_noreply_time, - .extra2 = &max_warn_noreply_time - }, - { - .procname = "lap_keepalive_time", - .data = &sysctl_lap_keepalive_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_lap_keepalive_time, - .extra2 = &max_lap_keepalive_time - }, - { } -}; - -static struct ctl_table_header *irda_table_header; - -/* - * Function irda_sysctl_register (void) - * - * Register our sysctl interface - * - */ -int __init irda_sysctl_register(void) -{ - irda_table_header = register_net_sysctl(&init_net, "net/irda", irda_table); - if (!irda_table_header) - return -ENOMEM; - - return 0; -} - -/* - * Function irda_sysctl_unregister (void) - * - * Unregister our sysctl interface - * - */ -void irda_sysctl_unregister(void) -{ - unregister_net_sysctl_table(irda_table_header); -} - - - diff --git a/drivers/staging/irda/net/irttp.c b/drivers/staging/irda/net/irttp.c deleted file mode 100644 index 741a94f39b4e..000000000000 --- a/drivers/staging/irda/net/irttp.c +++ /dev/null @@ -1,1886 +0,0 @@ -/********************************************************************* - * - * Filename: irttp.c - * Version: 1.2 - * Description: Tiny Transport Protocol (TTP) implementation - * Status: Stable - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:31 1997 - * Modified at: Wed Jan 5 11:31:27 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -static struct irttp_cb *irttp; - -static void __irttp_close_tsap(struct tsap_cb *self); - -static int irttp_data_indication(void *instance, void *sap, - struct sk_buff *skb); -static int irttp_udata_indication(void *instance, void *sap, - struct sk_buff *skb); -static void irttp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, struct sk_buff *); -static void irttp_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_sdu_size, - __u8 header_size, struct sk_buff *skb); -static void irttp_connect_confirm(void *instance, void *sap, - struct qos_info *qos, __u32 max_sdu_size, - __u8 header_size, struct sk_buff *skb); -static void irttp_run_tx_queue(struct tsap_cb *self); -static void irttp_run_rx_queue(struct tsap_cb *self); - -static void irttp_flush_queues(struct tsap_cb *self); -static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb); -static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self); -static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, - int get); - -static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow); -static void irttp_status_indication(void *instance, - LINK_STATUS link, LOCK_STATUS lock); - -/* Information for parsing parameters in IrTTP */ -static const pi_minor_info_t pi_minor_call_table[] = { - { NULL, 0 }, /* 0x00 */ - { irttp_param_max_sdu_size, PV_INTEGER | PV_BIG_ENDIAN } /* 0x01 */ -}; -static const pi_major_info_t pi_major_call_table[] = { - { pi_minor_call_table, 2 } -}; -static pi_param_info_t param_info = { pi_major_call_table, 1, 0x0f, 4 }; - -/************************ GLOBAL PROCEDURES ************************/ - -/* - * Function irttp_init (void) - * - * Initialize the IrTTP layer. Called by module initialization code - * - */ -int __init irttp_init(void) -{ - irttp = kzalloc(sizeof(struct irttp_cb), GFP_KERNEL); - if (irttp == NULL) - return -ENOMEM; - - irttp->magic = TTP_MAGIC; - - irttp->tsaps = hashbin_new(HB_LOCK); - if (!irttp->tsaps) { - net_err_ratelimited("%s: can't allocate IrTTP hashbin!\n", - __func__); - kfree(irttp); - return -ENOMEM; - } - - return 0; -} - -/* - * Function irttp_cleanup (void) - * - * Called by module destruction/cleanup code - * - */ -void irttp_cleanup(void) -{ - /* Check for main structure */ - IRDA_ASSERT(irttp->magic == TTP_MAGIC, return;); - - /* - * Delete hashbin and close all TSAP instances in it - */ - hashbin_delete(irttp->tsaps, (FREE_FUNC) __irttp_close_tsap); - - irttp->magic = 0; - - /* De-allocate main structure */ - kfree(irttp); - - irttp = NULL; -} - -/*************************** SUBROUTINES ***************************/ - -/* - * Function irttp_start_todo_timer (self, timeout) - * - * Start todo timer. - * - * Made it more effient and unsensitive to race conditions - Jean II - */ -static inline void irttp_start_todo_timer(struct tsap_cb *self, int timeout) -{ - /* Set new value for timer */ - mod_timer(&self->todo_timer, jiffies + timeout); -} - -/* - * Function irttp_todo_expired (data) - * - * Todo timer has expired! - * - * One of the restriction of the timer is that it is run only on the timer - * interrupt which run every 10ms. This mean that even if you set the timer - * with a delay of 0, it may take up to 10ms before it's run. - * So, to minimise latency and keep cache fresh, we try to avoid using - * it as much as possible. - * Note : we can't use tasklets, because they can't be asynchronously - * killed (need user context), and we can't guarantee that here... - * Jean II - */ -static void irttp_todo_expired(struct timer_list *t) -{ - struct tsap_cb *self = from_timer(self, t, todo_timer); - - /* Check that we still exist */ - if (!self || self->magic != TTP_TSAP_MAGIC) - return; - - pr_debug("%s(instance=%p)\n", __func__, self); - - /* Try to make some progress, especially on Tx side - Jean II */ - irttp_run_rx_queue(self); - irttp_run_tx_queue(self); - - /* Check if time for disconnect */ - if (test_bit(0, &self->disconnect_pend)) { - /* Check if it's possible to disconnect yet */ - if (skb_queue_empty(&self->tx_queue)) { - /* Make sure disconnect is not pending anymore */ - clear_bit(0, &self->disconnect_pend); /* FALSE */ - - /* Note : self->disconnect_skb may be NULL */ - irttp_disconnect_request(self, self->disconnect_skb, - P_NORMAL); - self->disconnect_skb = NULL; - } else { - /* Try again later */ - irttp_start_todo_timer(self, HZ/10); - - /* No reason to try and close now */ - return; - } - } - - /* Check if it's closing time */ - if (self->close_pend) - /* Finish cleanup */ - irttp_close_tsap(self); -} - -/* - * Function irttp_flush_queues (self) - * - * Flushes (removes all frames) in transitt-buffer (tx_list) - */ -static void irttp_flush_queues(struct tsap_cb *self) -{ - struct sk_buff *skb; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - /* Deallocate frames waiting to be sent */ - while ((skb = skb_dequeue(&self->tx_queue)) != NULL) - dev_kfree_skb(skb); - - /* Deallocate received frames */ - while ((skb = skb_dequeue(&self->rx_queue)) != NULL) - dev_kfree_skb(skb); - - /* Deallocate received fragments */ - while ((skb = skb_dequeue(&self->rx_fragments)) != NULL) - dev_kfree_skb(skb); -} - -/* - * Function irttp_reassemble (self) - * - * Makes a new (continuous) skb of all the fragments in the fragment - * queue - * - */ -static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self) -{ - struct sk_buff *skb, *frag; - int n = 0; /* Fragment index */ - - IRDA_ASSERT(self != NULL, return NULL;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return NULL;); - - pr_debug("%s(), self->rx_sdu_size=%d\n", __func__, - self->rx_sdu_size); - - skb = dev_alloc_skb(TTP_HEADER + self->rx_sdu_size); - if (!skb) - return NULL; - - /* - * Need to reserve space for TTP header in case this skb needs to - * be requeued in case delivery failes - */ - skb_reserve(skb, TTP_HEADER); - skb_put(skb, self->rx_sdu_size); - - /* - * Copy all fragments to a new buffer - */ - while ((frag = skb_dequeue(&self->rx_fragments)) != NULL) { - skb_copy_to_linear_data_offset(skb, n, frag->data, frag->len); - n += frag->len; - - dev_kfree_skb(frag); - } - - pr_debug("%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n", - __func__, n, self->rx_sdu_size, self->rx_max_sdu_size); - /* Note : irttp_run_rx_queue() calculate self->rx_sdu_size - * by summing the size of all fragments, so we should always - * have n == self->rx_sdu_size, except in cases where we - * droped the last fragment (when self->rx_sdu_size exceed - * self->rx_max_sdu_size), where n < self->rx_sdu_size. - * Jean II */ - IRDA_ASSERT(n <= self->rx_sdu_size, n = self->rx_sdu_size;); - - /* Set the new length */ - skb_trim(skb, n); - - self->rx_sdu_size = 0; - - return skb; -} - -/* - * Function irttp_fragment_skb (skb) - * - * Fragments a frame and queues all the fragments for transmission - * - */ -static inline void irttp_fragment_skb(struct tsap_cb *self, - struct sk_buff *skb) -{ - struct sk_buff *frag; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* - * Split frame into a number of segments - */ - while (skb->len > self->max_seg_size) { - pr_debug("%s(), fragmenting ...\n", __func__); - - /* Make new segment */ - frag = alloc_skb(self->max_seg_size+self->max_header_size, - GFP_ATOMIC); - if (!frag) - return; - - skb_reserve(frag, self->max_header_size); - - /* Copy data from the original skb into this fragment. */ - skb_copy_from_linear_data(skb, skb_put(frag, self->max_seg_size), - self->max_seg_size); - - /* Insert TTP header, with the more bit set */ - frame = skb_push(frag, TTP_HEADER); - frame[0] = TTP_MORE; - - /* Hide the copied data from the original skb */ - skb_pull(skb, self->max_seg_size); - - /* Queue fragment */ - skb_queue_tail(&self->tx_queue, frag); - } - /* Queue what is left of the original skb */ - pr_debug("%s(), queuing last segment\n", __func__); - - frame = skb_push(skb, TTP_HEADER); - frame[0] = 0x00; /* Clear more bit */ - - /* Queue fragment */ - skb_queue_tail(&self->tx_queue, skb); -} - -/* - * Function irttp_param_max_sdu_size (self, param) - * - * Handle the MaxSduSize parameter in the connect frames, this function - * will be called both when this parameter needs to be inserted into, and - * extracted from the connect frames - */ -static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, - int get) -{ - struct tsap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->tx_max_sdu_size; - else - self->tx_max_sdu_size = param->pv.i; - - pr_debug("%s(), MaxSduSize=%d\n", __func__, param->pv.i); - - return 0; -} - -/*************************** CLIENT CALLS ***************************/ -/************************** LMP CALLBACKS **************************/ -/* Everything is happily mixed up. Waiting for next clean up - Jean II */ - -/* - * Initialization, that has to be done on new tsap - * instance allocation and on duplication - */ -static void irttp_init_tsap(struct tsap_cb *tsap) -{ - spin_lock_init(&tsap->lock); - timer_setup(&tsap->todo_timer, irttp_todo_expired, 0); - - skb_queue_head_init(&tsap->rx_queue); - skb_queue_head_init(&tsap->tx_queue); - skb_queue_head_init(&tsap->rx_fragments); -} - -/* - * Function irttp_open_tsap (stsap, notify) - * - * Create TSAP connection endpoint, - */ -struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify) -{ - struct tsap_cb *self; - struct lsap_cb *lsap; - notify_t ttp_notify; - - IRDA_ASSERT(irttp->magic == TTP_MAGIC, return NULL;); - - /* The IrLMP spec (IrLMP 1.1 p10) says that we have the right to - * use only 0x01-0x6F. Of course, we can use LSAP_ANY as well. - * JeanII */ - if ((stsap_sel != LSAP_ANY) && - ((stsap_sel < 0x01) || (stsap_sel >= 0x70))) { - pr_debug("%s(), invalid tsap!\n", __func__); - return NULL; - } - - self = kzalloc(sizeof(struct tsap_cb), GFP_ATOMIC); - if (self == NULL) - return NULL; - - /* Initialize internal objects */ - irttp_init_tsap(self); - - /* Initialize callbacks for IrLMP to use */ - irda_notify_init(&ttp_notify); - ttp_notify.connect_confirm = irttp_connect_confirm; - ttp_notify.connect_indication = irttp_connect_indication; - ttp_notify.disconnect_indication = irttp_disconnect_indication; - ttp_notify.data_indication = irttp_data_indication; - ttp_notify.udata_indication = irttp_udata_indication; - ttp_notify.flow_indication = irttp_flow_indication; - if (notify->status_indication != NULL) - ttp_notify.status_indication = irttp_status_indication; - ttp_notify.instance = self; - strncpy(ttp_notify.name, notify->name, NOTIFY_MAX_NAME); - - self->magic = TTP_TSAP_MAGIC; - self->connected = FALSE; - - /* - * Create LSAP at IrLMP layer - */ - lsap = irlmp_open_lsap(stsap_sel, &ttp_notify, 0); - if (lsap == NULL) { - pr_debug("%s: unable to allocate LSAP!!\n", __func__); - __irttp_close_tsap(self); - return NULL; - } - - /* - * If user specified LSAP_ANY as source TSAP selector, then IrLMP - * will replace it with whatever source selector which is free, so - * the stsap_sel we have might not be valid anymore - */ - self->stsap_sel = lsap->slsap_sel; - pr_debug("%s(), stsap_sel=%02x\n", __func__, self->stsap_sel); - - self->notify = *notify; - self->lsap = lsap; - - hashbin_insert(irttp->tsaps, (irda_queue_t *) self, (long) self, NULL); - - if (credit > TTP_RX_MAX_CREDIT) - self->initial_credit = TTP_RX_MAX_CREDIT; - else - self->initial_credit = credit; - - return self; -} -EXPORT_SYMBOL(irttp_open_tsap); - -/* - * Function irttp_close (handle) - * - * Remove an instance of a TSAP. This function should only deal with the - * deallocation of the TSAP, and resetting of the TSAPs values; - * - */ -static void __irttp_close_tsap(struct tsap_cb *self) -{ - /* First make sure we're connected. */ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - irttp_flush_queues(self); - - del_timer(&self->todo_timer); - - /* This one won't be cleaned up if we are disconnect_pend + close_pend - * and we receive a disconnect_indication */ - if (self->disconnect_skb) - dev_kfree_skb(self->disconnect_skb); - - self->connected = FALSE; - self->magic = ~TTP_TSAP_MAGIC; - - kfree(self); -} - -/* - * Function irttp_close (self) - * - * Remove TSAP from list of all TSAPs and then deallocate all resources - * associated with this TSAP - * - * Note : because we *free* the tsap structure, it is the responsibility - * of the caller to make sure we are called only once and to deal with - * possible race conditions. - Jean II - */ -int irttp_close_tsap(struct tsap_cb *self) -{ - struct tsap_cb *tsap; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - - /* Make sure tsap has been disconnected */ - if (self->connected) { - /* Check if disconnect is not pending */ - if (!test_bit(0, &self->disconnect_pend)) { - net_warn_ratelimited("%s: TSAP still connected!\n", - __func__); - irttp_disconnect_request(self, NULL, P_NORMAL); - } - self->close_pend = TRUE; - irttp_start_todo_timer(self, HZ/10); - - return 0; /* Will be back! */ - } - - tsap = hashbin_remove(irttp->tsaps, (long) self, NULL); - - IRDA_ASSERT(tsap == self, return -1;); - - /* Close corresponding LSAP */ - if (self->lsap) { - irlmp_close_lsap(self->lsap); - self->lsap = NULL; - } - - __irttp_close_tsap(self); - - return 0; -} -EXPORT_SYMBOL(irttp_close_tsap); - -/* - * Function irttp_udata_request (self, skb) - * - * Send unreliable data on this TSAP - * - */ -int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - /* Take shortcut on zero byte packets */ - if (skb->len == 0) { - ret = 0; - goto err; - } - - /* Check that nothing bad happens */ - if (!self->connected) { - net_warn_ratelimited("%s(), Not connected\n", __func__); - ret = -ENOTCONN; - goto err; - } - - if (skb->len > self->max_seg_size) { - net_err_ratelimited("%s(), UData is too large for IrLAP!\n", - __func__); - ret = -EMSGSIZE; - goto err; - } - - irlmp_udata_request(self->lsap, skb); - self->stats.tx_packets++; - - return 0; - -err: - dev_kfree_skb(skb); - return ret; -} -EXPORT_SYMBOL(irttp_udata_request); - - -/* - * Function irttp_data_request (handle, skb) - * - * Queue frame for transmission. If SAR is enabled, fragement the frame - * and queue the fragments for transmission - */ -int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) -{ - __u8 *frame; - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - pr_debug("%s() : queue len = %d\n", __func__, - skb_queue_len(&self->tx_queue)); - - /* Take shortcut on zero byte packets */ - if (skb->len == 0) { - ret = 0; - goto err; - } - - /* Check that nothing bad happens */ - if (!self->connected) { - net_warn_ratelimited("%s: Not connected\n", __func__); - ret = -ENOTCONN; - goto err; - } - - /* - * Check if SAR is disabled, and the frame is larger than what fits - * inside an IrLAP frame - */ - if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) { - net_err_ratelimited("%s: SAR disabled, and data is too large for IrLAP!\n", - __func__); - ret = -EMSGSIZE; - goto err; - } - - /* - * Check if SAR is enabled, and the frame is larger than the - * TxMaxSduSize - */ - if ((self->tx_max_sdu_size != 0) && - (self->tx_max_sdu_size != TTP_SAR_UNBOUND) && - (skb->len > self->tx_max_sdu_size)) { - net_err_ratelimited("%s: SAR enabled, but data is larger than TxMaxSduSize!\n", - __func__); - ret = -EMSGSIZE; - goto err; - } - /* - * Check if transmit queue is full - */ - if (skb_queue_len(&self->tx_queue) >= TTP_TX_MAX_QUEUE) { - /* - * Give it a chance to empty itself - */ - irttp_run_tx_queue(self); - - /* Drop packet. This error code should trigger the caller - * to resend the data in the client code - Jean II */ - ret = -ENOBUFS; - goto err; - } - - /* Queue frame, or queue frame segments */ - if ((self->tx_max_sdu_size == 0) || (skb->len < self->max_seg_size)) { - /* Queue frame */ - IRDA_ASSERT(skb_headroom(skb) >= TTP_HEADER, return -1;); - frame = skb_push(skb, TTP_HEADER); - frame[0] = 0x00; /* Clear more bit */ - - skb_queue_tail(&self->tx_queue, skb); - } else { - /* - * Fragment the frame, this function will also queue the - * fragments, we don't care about the fact the transmit - * queue may be overfilled by all the segments for a little - * while - */ - irttp_fragment_skb(self, skb); - } - - /* Check if we can accept more data from client */ - if ((!self->tx_sdu_busy) && - (skb_queue_len(&self->tx_queue) > TTP_TX_HIGH_THRESHOLD)) { - /* Tx queue filling up, so stop client. */ - if (self->notify.flow_indication) { - self->notify.flow_indication(self->notify.instance, - self, FLOW_STOP); - } - /* self->tx_sdu_busy is the state of the client. - * Update state after notifying client to avoid - * race condition with irttp_flow_indication(). - * If the queue empty itself after our test but before - * we set the flag, we will fix ourselves below in - * irttp_run_tx_queue(). - * Jean II */ - self->tx_sdu_busy = TRUE; - } - - /* Try to make some progress */ - irttp_run_tx_queue(self); - - return 0; - -err: - dev_kfree_skb(skb); - return ret; -} -EXPORT_SYMBOL(irttp_data_request); - -/* - * Function irttp_run_tx_queue (self) - * - * Transmit packets queued for transmission (if possible) - * - */ -static void irttp_run_tx_queue(struct tsap_cb *self) -{ - struct sk_buff *skb; - unsigned long flags; - int n; - - pr_debug("%s() : send_credit = %d, queue_len = %d\n", - __func__, - self->send_credit, skb_queue_len(&self->tx_queue)); - - /* Get exclusive access to the tx queue, otherwise don't touch it */ - if (irda_lock(&self->tx_queue_lock) == FALSE) - return; - - /* Try to send out frames as long as we have credits - * and as long as LAP is not full. If LAP is full, it will - * poll us through irttp_flow_indication() - Jean II */ - while ((self->send_credit > 0) && - (!irlmp_lap_tx_queue_full(self->lsap)) && - (skb = skb_dequeue(&self->tx_queue))) { - /* - * Since we can transmit and receive frames concurrently, - * the code below is a critical region and we must assure that - * nobody messes with the credits while we update them. - */ - spin_lock_irqsave(&self->lock, flags); - - n = self->avail_credit; - self->avail_credit = 0; - - /* Only room for 127 credits in frame */ - if (n > 127) { - self->avail_credit = n-127; - n = 127; - } - self->remote_credit += n; - self->send_credit--; - - spin_unlock_irqrestore(&self->lock, flags); - - /* - * More bit must be set by the data_request() or fragment() - * functions - */ - skb->data[0] |= (n & 0x7f); - - /* Detach from socket. - * The current skb has a reference to the socket that sent - * it (skb->sk). When we pass it to IrLMP, the skb will be - * stored in in IrLAP (self->wx_list). When we are within - * IrLAP, we lose the notion of socket, so we should not - * have a reference to a socket. So, we drop it here. - * - * Why does it matter ? - * When the skb is freed (kfree_skb), if it is associated - * with a socket, it release buffer space on the socket - * (through sock_wfree() and sock_def_write_space()). - * If the socket no longer exist, we may crash. Hard. - * When we close a socket, we make sure that associated packets - * in IrTTP are freed. However, we have no way to cancel - * the packet that we have passed to IrLAP. So, if a packet - * remains in IrLAP (retry on the link or else) after we - * close the socket, we are dead ! - * Jean II */ - if (skb->sk != NULL) { - /* IrSOCK application, IrOBEX, ... */ - skb_orphan(skb); - } - /* IrCOMM over IrTTP, IrLAN, ... */ - - /* Pass the skb to IrLMP - done */ - irlmp_data_request(self->lsap, skb); - self->stats.tx_packets++; - } - - /* Check if we can accept more frames from client. - * We don't want to wait until the todo timer to do that, and we - * can't use tasklets (grr...), so we are obliged to give control - * to client. That's ok, this test will be true not too often - * (max once per LAP window) and we are called from places - * where we can spend a bit of time doing stuff. - Jean II */ - if ((self->tx_sdu_busy) && - (skb_queue_len(&self->tx_queue) < TTP_TX_LOW_THRESHOLD) && - (!self->close_pend)) { - if (self->notify.flow_indication) - self->notify.flow_indication(self->notify.instance, - self, FLOW_START); - - /* self->tx_sdu_busy is the state of the client. - * We don't really have a race here, but it's always safer - * to update our state after the client - Jean II */ - self->tx_sdu_busy = FALSE; - } - - /* Reset lock */ - self->tx_queue_lock = 0; -} - -/* - * Function irttp_give_credit (self) - * - * Send a dataless flowdata TTP-PDU and give available credit to peer - * TSAP - */ -static inline void irttp_give_credit(struct tsap_cb *self) -{ - struct sk_buff *tx_skb = NULL; - unsigned long flags; - int n; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - pr_debug("%s() send=%d,avail=%d,remote=%d\n", - __func__, - self->send_credit, self->avail_credit, self->remote_credit); - - /* Give credit to peer */ - tx_skb = alloc_skb(TTP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return; - - /* Reserve space for LMP, and LAP header */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - - /* - * Since we can transmit and receive frames concurrently, - * the code below is a critical region and we must assure that - * nobody messes with the credits while we update them. - */ - spin_lock_irqsave(&self->lock, flags); - - n = self->avail_credit; - self->avail_credit = 0; - - /* Only space for 127 credits in frame */ - if (n > 127) { - self->avail_credit = n - 127; - n = 127; - } - self->remote_credit += n; - - spin_unlock_irqrestore(&self->lock, flags); - - skb_put(tx_skb, 1); - tx_skb->data[0] = (__u8) (n & 0x7f); - - irlmp_data_request(self->lsap, tx_skb); - self->stats.tx_packets++; -} - -/* - * Function irttp_udata_indication (instance, sap, skb) - * - * Received some unit-data (unreliable) - * - */ -static int irttp_udata_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct tsap_cb *self; - int err; - - self = instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - self->stats.rx_packets++; - - /* Just pass data to layer above */ - if (self->notify.udata_indication) { - err = self->notify.udata_indication(self->notify.instance, - self, skb); - /* Same comment as in irttp_do_data_indication() */ - if (!err) - return 0; - } - /* Either no handler, or handler returns an error */ - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irttp_data_indication (instance, sap, skb) - * - * Receive segment from IrLMP. - * - */ -static int irttp_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct tsap_cb *self; - unsigned long flags; - int n; - - self = instance; - - n = skb->data[0] & 0x7f; /* Extract the credits */ - - self->stats.rx_packets++; - - /* Deal with inbound credit - * Since we can transmit and receive frames concurrently, - * the code below is a critical region and we must assure that - * nobody messes with the credits while we update them. - */ - spin_lock_irqsave(&self->lock, flags); - self->send_credit += n; - if (skb->len > 1) - self->remote_credit--; - spin_unlock_irqrestore(&self->lock, flags); - - /* - * Data or dataless packet? Dataless frames contains only the - * TTP_HEADER. - */ - if (skb->len > 1) { - /* - * We don't remove the TTP header, since we must preserve the - * more bit, so the defragment routing knows what to do - */ - skb_queue_tail(&self->rx_queue, skb); - } else { - /* Dataless flowdata TTP-PDU */ - dev_kfree_skb(skb); - } - - - /* Push data to the higher layer. - * We do it synchronously because running the todo timer for each - * receive packet would be too much overhead and latency. - * By passing control to the higher layer, we run the risk that - * it may take time or grab a lock. Most often, the higher layer - * will only put packet in a queue. - * Anyway, packets are only dripping through the IrDA, so we can - * have time before the next packet. - * Further, we are run from NET_BH, so the worse that can happen is - * us missing the optimal time to send back the PF bit in LAP. - * Jean II */ - irttp_run_rx_queue(self); - - /* We now give credits to peer in irttp_run_rx_queue(). - * We need to send credit *NOW*, otherwise we are going - * to miss the next Tx window. The todo timer may take - * a while before it's run... - Jean II */ - - /* - * If the peer device has given us some credits and we didn't have - * anyone from before, then we need to shedule the tx queue. - * We need to do that because our Tx have stopped (so we may not - * get any LAP flow indication) and the user may be stopped as - * well. - Jean II - */ - if (self->send_credit == n) { - /* Restart pushing stuff to LAP */ - irttp_run_tx_queue(self); - /* Note : we don't want to schedule the todo timer - * because it has horrible latency. No tasklets - * because the tasklet API is broken. - Jean II */ - } - - return 0; -} - -/* - * Function irttp_status_indication (self, reason) - * - * Status_indication, just pass to the higher layer... - * - */ -static void irttp_status_indication(void *instance, - LINK_STATUS link, LOCK_STATUS lock) -{ - struct tsap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - /* Check if client has already closed the TSAP and gone away */ - if (self->close_pend) - return; - - /* - * Inform service user if he has requested it - */ - if (self->notify.status_indication != NULL) - self->notify.status_indication(self->notify.instance, - link, lock); - else - pr_debug("%s(), no handler\n", __func__); -} - -/* - * Function irttp_flow_indication (self, reason) - * - * Flow_indication : IrLAP tells us to send more data. - * - */ -static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) -{ - struct tsap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - pr_debug("%s(instance=%p)\n", __func__, self); - - /* We are "polled" directly from LAP, and the LAP want to fill - * its Tx window. We want to do our best to send it data, so that - * we maximise the window. On the other hand, we want to limit the - * amount of work here so that LAP doesn't hang forever waiting - * for packets. - Jean II */ - - /* Try to send some packets. Currently, LAP calls us every time - * there is one free slot, so we will send only one packet. - * This allow the scheduler to do its round robin - Jean II */ - irttp_run_tx_queue(self); - - /* Note regarding the interraction with higher layer. - * irttp_run_tx_queue() may call the client when its queue - * start to empty, via notify.flow_indication(). Initially. - * I wanted this to happen in a tasklet, to avoid client - * grabbing the CPU, but we can't use tasklets safely. And timer - * is definitely too slow. - * This will happen only once per LAP window, and usually at - * the third packet (unless window is smaller). LAP is still - * doing mtt and sending first packet so it's sort of OK - * to do that. Jean II */ - - /* If we need to send disconnect. try to do it now */ - if (self->disconnect_pend) - irttp_start_todo_timer(self, 0); -} - -/* - * Function irttp_flow_request (self, command) - * - * This function could be used by the upper layers to tell IrTTP to stop - * delivering frames if the receive queues are starting to get full, or - * to tell IrTTP to start delivering frames again. - */ -void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - switch (flow) { - case FLOW_STOP: - pr_debug("%s(), flow stop\n", __func__); - self->rx_sdu_busy = TRUE; - break; - case FLOW_START: - pr_debug("%s(), flow start\n", __func__); - self->rx_sdu_busy = FALSE; - - /* Client say he can accept more data, try to free our - * queues ASAP - Jean II */ - irttp_run_rx_queue(self); - - break; - default: - pr_debug("%s(), Unknown flow command!\n", __func__); - } -} -EXPORT_SYMBOL(irttp_flow_request); - -/* - * Function irttp_connect_request (self, dtsap_sel, daddr, qos) - * - * Try to connect to remote destination TSAP selector - * - */ -int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel, - __u32 saddr, __u32 daddr, - struct qos_info *qos, __u32 max_sdu_size, - struct sk_buff *userdata) -{ - struct sk_buff *tx_skb; - __u8 *frame; - __u8 n; - - pr_debug("%s(), max_sdu_size=%d\n", __func__, max_sdu_size); - - IRDA_ASSERT(self != NULL, return -EBADR;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -EBADR;); - - if (self->connected) { - if (userdata) - dev_kfree_skb(userdata); - return -EISCONN; - } - - /* Any userdata supplied? */ - if (userdata == NULL) { - tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, - GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER); - } else { - tx_skb = userdata; - /* - * Check that the client has reserved enough space for - * headers - */ - IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, - { dev_kfree_skb(userdata); return -1; }); - } - - /* Initialize connection parameters */ - self->connected = FALSE; - self->avail_credit = 0; - self->rx_max_sdu_size = max_sdu_size; - self->rx_sdu_size = 0; - self->rx_sdu_busy = FALSE; - self->dtsap_sel = dtsap_sel; - - n = self->initial_credit; - - self->remote_credit = 0; - self->send_credit = 0; - - /* - * Give away max 127 credits for now - */ - if (n > 127) { - self->avail_credit = n - 127; - n = 127; - } - - self->remote_credit = n; - - /* SAR enabled? */ - if (max_sdu_size > 0) { - IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER), - { dev_kfree_skb(tx_skb); return -1; }); - - /* Insert SAR parameters */ - frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER); - - frame[0] = TTP_PARAMETERS | n; - frame[1] = 0x04; /* Length */ - frame[2] = 0x01; /* MaxSduSize */ - frame[3] = 0x02; /* Value length */ - - put_unaligned(cpu_to_be16((__u16) max_sdu_size), - (__be16 *)(frame+4)); - } else { - /* Insert plain TTP header */ - frame = skb_push(tx_skb, TTP_HEADER); - - /* Insert initial credit in frame */ - frame[0] = n & 0x7f; - } - - /* Connect with IrLMP. No QoS parameters for now */ - return irlmp_connect_request(self->lsap, dtsap_sel, saddr, daddr, qos, - tx_skb); -} -EXPORT_SYMBOL(irttp_connect_request); - -/* - * Function irttp_connect_confirm (handle, qos, skb) - * - * Service user confirms TSAP connection with peer. - * - */ -static void irttp_connect_confirm(void *instance, void *sap, - struct qos_info *qos, __u32 max_seg_size, - __u8 max_header_size, struct sk_buff *skb) -{ - struct tsap_cb *self; - int parameters; - int ret; - __u8 plen; - __u8 n; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - self->max_seg_size = max_seg_size - TTP_HEADER; - self->max_header_size = max_header_size + TTP_HEADER; - - /* - * Check if we have got some QoS parameters back! This should be the - * negotiated QoS for the link. - */ - if (qos) { - pr_debug("IrTTP, Negotiated BAUD_RATE: %02x\n", - qos->baud_rate.bits); - pr_debug("IrTTP, Negotiated BAUD_RATE: %d bps.\n", - qos->baud_rate.value); - } - - n = skb->data[0] & 0x7f; - - pr_debug("%s(), Initial send_credit=%d\n", __func__, n); - - self->send_credit = n; - self->tx_max_sdu_size = 0; - self->connected = TRUE; - - parameters = skb->data[0] & 0x80; - - IRDA_ASSERT(skb->len >= TTP_HEADER, return;); - skb_pull(skb, TTP_HEADER); - - if (parameters) { - plen = skb->data[0]; - - ret = irda_param_extract_all(self, skb->data+1, - IRDA_MIN(skb->len-1, plen), - ¶m_info); - - /* Any errors in the parameter list? */ - if (ret < 0) { - net_warn_ratelimited("%s: error extracting parameters\n", - __func__); - dev_kfree_skb(skb); - - /* Do not accept this connection attempt */ - return; - } - /* Remove parameters */ - skb_pull(skb, IRDA_MIN(skb->len, plen+1)); - } - - pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__, - self->send_credit, self->avail_credit, self->remote_credit); - - pr_debug("%s(), MaxSduSize=%d\n", __func__, - self->tx_max_sdu_size); - - if (self->notify.connect_confirm) { - self->notify.connect_confirm(self->notify.instance, self, qos, - self->tx_max_sdu_size, - self->max_header_size, skb); - } else - dev_kfree_skb(skb); -} - -/* - * Function irttp_connect_indication (handle, skb) - * - * Some other device is connecting to this TSAP - * - */ -static void irttp_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_seg_size, __u8 max_header_size, - struct sk_buff *skb) -{ - struct tsap_cb *self; - struct lsap_cb *lsap; - int parameters; - int ret; - __u8 plen; - __u8 n; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - lsap = sap; - - self->max_seg_size = max_seg_size - TTP_HEADER; - self->max_header_size = max_header_size+TTP_HEADER; - - pr_debug("%s(), TSAP sel=%02x\n", __func__, self->stsap_sel); - - /* Need to update dtsap_sel if its equal to LSAP_ANY */ - self->dtsap_sel = lsap->dlsap_sel; - - n = skb->data[0] & 0x7f; - - self->send_credit = n; - self->tx_max_sdu_size = 0; - - parameters = skb->data[0] & 0x80; - - IRDA_ASSERT(skb->len >= TTP_HEADER, return;); - skb_pull(skb, TTP_HEADER); - - if (parameters) { - plen = skb->data[0]; - - ret = irda_param_extract_all(self, skb->data+1, - IRDA_MIN(skb->len-1, plen), - ¶m_info); - - /* Any errors in the parameter list? */ - if (ret < 0) { - net_warn_ratelimited("%s: error extracting parameters\n", - __func__); - dev_kfree_skb(skb); - - /* Do not accept this connection attempt */ - return; - } - - /* Remove parameters */ - skb_pull(skb, IRDA_MIN(skb->len, plen+1)); - } - - if (self->notify.connect_indication) { - self->notify.connect_indication(self->notify.instance, self, - qos, self->tx_max_sdu_size, - self->max_header_size, skb); - } else - dev_kfree_skb(skb); -} - -/* - * Function irttp_connect_response (handle, userdata) - * - * Service user is accepting the connection, just pass it down to - * IrLMP! - * - */ -int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size, - struct sk_buff *userdata) -{ - struct sk_buff *tx_skb; - __u8 *frame; - int ret; - __u8 n; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - - pr_debug("%s(), Source TSAP selector=%02x\n", __func__, - self->stsap_sel); - - /* Any userdata supplied? */ - if (userdata == NULL) { - tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, - GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER); - } else { - tx_skb = userdata; - /* - * Check that the client has reserved enough space for - * headers - */ - IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, - { dev_kfree_skb(userdata); return -1; }); - } - - self->avail_credit = 0; - self->remote_credit = 0; - self->rx_max_sdu_size = max_sdu_size; - self->rx_sdu_size = 0; - self->rx_sdu_busy = FALSE; - - n = self->initial_credit; - - /* Frame has only space for max 127 credits (7 bits) */ - if (n > 127) { - self->avail_credit = n - 127; - n = 127; - } - - self->remote_credit = n; - self->connected = TRUE; - - /* SAR enabled? */ - if (max_sdu_size > 0) { - IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER), - { dev_kfree_skb(tx_skb); return -1; }); - - /* Insert TTP header with SAR parameters */ - frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER); - - frame[0] = TTP_PARAMETERS | n; - frame[1] = 0x04; /* Length */ - - /* irda_param_insert(self, IRTTP_MAX_SDU_SIZE, frame+1, */ -/* TTP_SAR_HEADER, ¶m_info) */ - - frame[2] = 0x01; /* MaxSduSize */ - frame[3] = 0x02; /* Value length */ - - put_unaligned(cpu_to_be16((__u16) max_sdu_size), - (__be16 *)(frame+4)); - } else { - /* Insert TTP header */ - frame = skb_push(tx_skb, TTP_HEADER); - - frame[0] = n & 0x7f; - } - - ret = irlmp_connect_response(self->lsap, tx_skb); - - return ret; -} -EXPORT_SYMBOL(irttp_connect_response); - -/* - * Function irttp_dup (self, instance) - * - * Duplicate TSAP, can be used by servers to confirm a connection on a - * new TSAP so it can keep listening on the old one. - */ -struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance) -{ - struct tsap_cb *new; - unsigned long flags; - - /* Protect our access to the old tsap instance */ - spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags); - - /* Find the old instance */ - if (!hashbin_find(irttp->tsaps, (long) orig, NULL)) { - pr_debug("%s(), unable to find TSAP\n", __func__); - spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); - return NULL; - } - - /* Allocate a new instance */ - new = kmemdup(orig, sizeof(struct tsap_cb), GFP_ATOMIC); - if (!new) { - pr_debug("%s(), unable to kmalloc\n", __func__); - spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); - return NULL; - } - spin_lock_init(&new->lock); - - /* We don't need the old instance any more */ - spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); - - /* Try to dup the LSAP (may fail if we were too slow) */ - new->lsap = irlmp_dup(orig->lsap, new); - if (!new->lsap) { - pr_debug("%s(), dup failed!\n", __func__); - kfree(new); - return NULL; - } - - /* Not everything should be copied */ - new->notify.instance = instance; - - /* Initialize internal objects */ - irttp_init_tsap(new); - - /* This is locked */ - hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (long) new, NULL); - - return new; -} -EXPORT_SYMBOL(irttp_dup); - -/* - * Function irttp_disconnect_request (self) - * - * Close this connection please! If priority is high, the queued data - * segments, if any, will be deallocated first - * - */ -int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata, - int priority) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - - /* Already disconnected? */ - if (!self->connected) { - pr_debug("%s(), already disconnected!\n", __func__); - if (userdata) - dev_kfree_skb(userdata); - return -1; - } - - /* Disconnect already pending ? - * We need to use an atomic operation to prevent reentry. This - * function may be called from various context, like user, timer - * for following a disconnect_indication() (i.e. net_bh). - * Jean II */ - if (test_and_set_bit(0, &self->disconnect_pend)) { - pr_debug("%s(), disconnect already pending\n", - __func__); - if (userdata) - dev_kfree_skb(userdata); - - /* Try to make some progress */ - irttp_run_tx_queue(self); - return -1; - } - - /* - * Check if there is still data segments in the transmit queue - */ - if (!skb_queue_empty(&self->tx_queue)) { - if (priority == P_HIGH) { - /* - * No need to send the queued data, if we are - * disconnecting right now since the data will - * not have any usable connection to be sent on - */ - pr_debug("%s(): High priority!!()\n", __func__); - irttp_flush_queues(self); - } else if (priority == P_NORMAL) { - /* - * Must delay disconnect until after all data segments - * have been sent and the tx_queue is empty - */ - /* We'll reuse this one later for the disconnect */ - self->disconnect_skb = userdata; /* May be NULL */ - - irttp_run_tx_queue(self); - - irttp_start_todo_timer(self, HZ/10); - return -1; - } - } - /* Note : we don't need to check if self->rx_queue is full and the - * state of self->rx_sdu_busy because the disconnect response will - * be sent at the LMP level (so even if the peer has its Tx queue - * full of data). - Jean II */ - - pr_debug("%s(), Disconnecting ...\n", __func__); - self->connected = FALSE; - - if (!userdata) { - struct sk_buff *tx_skb; - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* - * Reserve space for MUX and LAP header - */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - - userdata = tx_skb; - } - ret = irlmp_disconnect_request(self->lsap, userdata); - - /* The disconnect is no longer pending */ - clear_bit(0, &self->disconnect_pend); /* FALSE */ - - return ret; -} -EXPORT_SYMBOL(irttp_disconnect_request); - -/* - * Function irttp_disconnect_indication (self, reason) - * - * Disconnect indication, TSAP disconnected by peer? - * - */ -static void irttp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, struct sk_buff *skb) -{ - struct tsap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - /* Prevent higher layer to send more data */ - self->connected = FALSE; - - /* Check if client has already tried to close the TSAP */ - if (self->close_pend) { - /* In this case, the higher layer is probably gone. Don't - * bother it and clean up the remains - Jean II */ - if (skb) - dev_kfree_skb(skb); - irttp_close_tsap(self); - return; - } - - /* If we are here, we assume that is the higher layer is still - * waiting for the disconnect notification and able to process it, - * even if he tried to disconnect. Otherwise, it would have already - * attempted to close the tsap and self->close_pend would be TRUE. - * Jean II */ - - /* No need to notify the client if has already tried to disconnect */ - if (self->notify.disconnect_indication) - self->notify.disconnect_indication(self->notify.instance, self, - reason, skb); - else - if (skb) - dev_kfree_skb(skb); -} - -/* - * Function irttp_do_data_indication (self, skb) - * - * Try to deliver reassembled skb to layer above, and requeue it if that - * for some reason should fail. We mark rx sdu as busy to apply back - * pressure is necessary. - */ -static void irttp_do_data_indication(struct tsap_cb *self, struct sk_buff *skb) -{ - int err; - - /* Check if client has already closed the TSAP and gone away */ - if (self->close_pend) { - dev_kfree_skb(skb); - return; - } - - err = self->notify.data_indication(self->notify.instance, self, skb); - - /* Usually the layer above will notify that it's input queue is - * starting to get filled by using the flow request, but this may - * be difficult, so it can instead just refuse to eat it and just - * give an error back - */ - if (err) { - pr_debug("%s() requeueing skb!\n", __func__); - - /* Make sure we take a break */ - self->rx_sdu_busy = TRUE; - - /* Need to push the header in again */ - skb_push(skb, TTP_HEADER); - skb->data[0] = 0x00; /* Make sure MORE bit is cleared */ - - /* Put skb back on queue */ - skb_queue_head(&self->rx_queue, skb); - } -} - -/* - * Function irttp_run_rx_queue (self) - * - * Check if we have any frames to be transmitted, or if we have any - * available credit to give away. - */ -static void irttp_run_rx_queue(struct tsap_cb *self) -{ - struct sk_buff *skb; - int more = 0; - - pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__, - self->send_credit, self->avail_credit, self->remote_credit); - - /* Get exclusive access to the rx queue, otherwise don't touch it */ - if (irda_lock(&self->rx_queue_lock) == FALSE) - return; - - /* - * Reassemble all frames in receive queue and deliver them - */ - while (!self->rx_sdu_busy && (skb = skb_dequeue(&self->rx_queue))) { - /* This bit will tell us if it's the last fragment or not */ - more = skb->data[0] & 0x80; - - /* Remove TTP header */ - skb_pull(skb, TTP_HEADER); - - /* Add the length of the remaining data */ - self->rx_sdu_size += skb->len; - - /* - * If SAR is disabled, or user has requested no reassembly - * of received fragments then we just deliver them - * immediately. This can be requested by clients that - * implements byte streams without any message boundaries - */ - if (self->rx_max_sdu_size == TTP_SAR_DISABLE) { - irttp_do_data_indication(self, skb); - self->rx_sdu_size = 0; - - continue; - } - - /* Check if this is a fragment, and not the last fragment */ - if (more) { - /* - * Queue the fragment if we still are within the - * limits of the maximum size of the rx_sdu - */ - if (self->rx_sdu_size <= self->rx_max_sdu_size) { - pr_debug("%s(), queueing frag\n", - __func__); - skb_queue_tail(&self->rx_fragments, skb); - } else { - /* Free the part of the SDU that is too big */ - dev_kfree_skb(skb); - } - continue; - } - /* - * This is the last fragment, so time to reassemble! - */ - if ((self->rx_sdu_size <= self->rx_max_sdu_size) || - (self->rx_max_sdu_size == TTP_SAR_UNBOUND)) { - /* - * A little optimizing. Only queue the fragment if - * there are other fragments. Since if this is the - * last and only fragment, there is no need to - * reassemble :-) - */ - if (!skb_queue_empty(&self->rx_fragments)) { - skb_queue_tail(&self->rx_fragments, - skb); - - skb = irttp_reassemble_skb(self); - } - - /* Now we can deliver the reassembled skb */ - irttp_do_data_indication(self, skb); - } else { - pr_debug("%s(), Truncated frame\n", __func__); - - /* Free the part of the SDU that is too big */ - dev_kfree_skb(skb); - - /* Deliver only the valid but truncated part of SDU */ - skb = irttp_reassemble_skb(self); - - irttp_do_data_indication(self, skb); - } - self->rx_sdu_size = 0; - } - - /* - * It's not trivial to keep track of how many credits are available - * by incrementing at each packet, because delivery may fail - * (irttp_do_data_indication() may requeue the frame) and because - * we need to take care of fragmentation. - * We want the other side to send up to initial_credit packets. - * We have some frames in our queues, and we have already allowed it - * to send remote_credit. - * No need to spinlock, write is atomic and self correcting... - * Jean II - */ - self->avail_credit = (self->initial_credit - - (self->remote_credit + - skb_queue_len(&self->rx_queue) + - skb_queue_len(&self->rx_fragments))); - - /* Do we have too much credits to send to peer ? */ - if ((self->remote_credit <= TTP_RX_MIN_CREDIT) && - (self->avail_credit > 0)) { - /* Send explicit credit frame */ - irttp_give_credit(self); - /* Note : do *NOT* check if tx_queue is non-empty, that - * will produce deadlocks. I repeat : send a credit frame - * even if we have something to send in our Tx queue. - * If we have credits, it means that our Tx queue is blocked. - * - * Let's suppose the peer can't keep up with our Tx. He will - * flow control us by not sending us any credits, and we - * will stop Tx and start accumulating credits here. - * Up to the point where the peer will stop its Tx queue, - * for lack of credits. - * Let's assume the peer application is single threaded. - * It will block on Tx and never consume any Rx buffer. - * Deadlock. Guaranteed. - Jean II - */ - } - - /* Reset lock */ - self->rx_queue_lock = 0; -} - -#ifdef CONFIG_PROC_FS -struct irttp_iter_state { - int id; -}; - -static void *irttp_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct irttp_iter_state *iter = seq->private; - struct tsap_cb *self; - - /* Protect our access to the tsap list */ - spin_lock_irq(&irttp->tsaps->hb_spinlock); - iter->id = 0; - - for (self = (struct tsap_cb *) hashbin_get_first(irttp->tsaps); - self != NULL; - self = (struct tsap_cb *) hashbin_get_next(irttp->tsaps)) { - if (iter->id == *pos) - break; - ++iter->id; - } - - return self; -} - -static void *irttp_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct irttp_iter_state *iter = seq->private; - - ++*pos; - ++iter->id; - return (void *) hashbin_get_next(irttp->tsaps); -} - -static void irttp_seq_stop(struct seq_file *seq, void *v) -{ - spin_unlock_irq(&irttp->tsaps->hb_spinlock); -} - -static int irttp_seq_show(struct seq_file *seq, void *v) -{ - const struct irttp_iter_state *iter = seq->private; - const struct tsap_cb *self = v; - - seq_printf(seq, "TSAP %d, ", iter->id); - seq_printf(seq, "stsap_sel: %02x, ", - self->stsap_sel); - seq_printf(seq, "dtsap_sel: %02x\n", - self->dtsap_sel); - seq_printf(seq, " connected: %s, ", - self->connected ? "TRUE" : "FALSE"); - seq_printf(seq, "avail credit: %d, ", - self->avail_credit); - seq_printf(seq, "remote credit: %d, ", - self->remote_credit); - seq_printf(seq, "send credit: %d\n", - self->send_credit); - seq_printf(seq, " tx packets: %lu, ", - self->stats.tx_packets); - seq_printf(seq, "rx packets: %lu, ", - self->stats.rx_packets); - seq_printf(seq, "tx_queue len: %u ", - skb_queue_len(&self->tx_queue)); - seq_printf(seq, "rx_queue len: %u\n", - skb_queue_len(&self->rx_queue)); - seq_printf(seq, " tx_sdu_busy: %s, ", - self->tx_sdu_busy ? "TRUE" : "FALSE"); - seq_printf(seq, "rx_sdu_busy: %s\n", - self->rx_sdu_busy ? "TRUE" : "FALSE"); - seq_printf(seq, " max_seg_size: %u, ", - self->max_seg_size); - seq_printf(seq, "tx_max_sdu_size: %u, ", - self->tx_max_sdu_size); - seq_printf(seq, "rx_max_sdu_size: %u\n", - self->rx_max_sdu_size); - - seq_printf(seq, " Used by (%s)\n\n", - self->notify.name); - return 0; -} - -static const struct seq_operations irttp_seq_ops = { - .start = irttp_seq_start, - .next = irttp_seq_next, - .stop = irttp_seq_stop, - .show = irttp_seq_show, -}; - -static int irttp_seq_open(struct inode *inode, struct file *file) -{ - return seq_open_private(file, &irttp_seq_ops, - sizeof(struct irttp_iter_state)); -} - -const struct file_operations irttp_seq_fops = { - .owner = THIS_MODULE, - .open = irttp_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; - -#endif /* PROC_FS */ diff --git a/drivers/staging/irda/net/parameters.c b/drivers/staging/irda/net/parameters.c deleted file mode 100644 index 16ce32ffe004..000000000000 --- a/drivers/staging/irda/net/parameters.c +++ /dev/null @@ -1,584 +0,0 @@ -/********************************************************************* - * - * Filename: parameters.c - * Version: 1.0 - * Description: A more general way to handle (pi,pl,pv) parameters - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Jun 7 10:25:11 1999 - * Modified at: Sun Jan 30 14:08:39 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include -#include - -#include -#include - -#include -#include - -static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); -static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); -static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); -static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); - -static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); -static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); - -static int irda_param_unpack(__u8 *buf, char *fmt, ...); - -/* Parameter value call table. Must match PV_TYPE */ -static const PV_HANDLER pv_extract_table[] = { - irda_extract_integer, /* Handler for any length integers */ - irda_extract_integer, /* Handler for 8 bits integers */ - irda_extract_integer, /* Handler for 16 bits integers */ - irda_extract_string, /* Handler for strings */ - irda_extract_integer, /* Handler for 32 bits integers */ - irda_extract_octseq, /* Handler for octet sequences */ - irda_extract_no_value /* Handler for no value parameters */ -}; - -static const PV_HANDLER pv_insert_table[] = { - irda_insert_integer, /* Handler for any length integers */ - irda_insert_integer, /* Handler for 8 bits integers */ - irda_insert_integer, /* Handler for 16 bits integers */ - NULL, /* Handler for strings */ - irda_insert_integer, /* Handler for 32 bits integers */ - NULL, /* Handler for octet sequences */ - irda_insert_no_value /* Handler for no value parameters */ -}; - -/* - * Function irda_insert_no_value (self, buf, len, pi, type, func) - */ -static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - irda_param_t p; - int ret; - - p.pi = pi; - p.pl = 0; - - /* Call handler for this parameter */ - ret = (*func)(self, &p, PV_GET); - - /* Extract values anyway, since handler may need them */ - irda_param_pack(buf, "bb", p.pi, p.pl); - - if (ret < 0) - return ret; - - return 2; /* Inserted pl+2 bytes */ -} - -/* - * Function irda_extract_no_value (self, buf, len, type, func) - * - * Extracts a parameter without a pv field (pl=0) - * - */ -static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - irda_param_t p; - int ret; - - /* Extract values anyway, since handler may need them */ - irda_param_unpack(buf, "bb", &p.pi, &p.pl); - - /* Call handler for this parameter */ - ret = (*func)(self, &p, PV_PUT); - - if (ret < 0) - return ret; - - return 2; /* Extracted pl+2 bytes */ -} - -/* - * Function irda_insert_integer (self, buf, len, pi, type, func) - */ -static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - irda_param_t p; - int n = 0; - int err; - - p.pi = pi; /* In case handler needs to know */ - p.pl = type & PV_MASK; /* The integer type codes the length as well */ - p.pv.i = 0; /* Clear value */ - - /* Call handler for this parameter */ - err = (*func)(self, &p, PV_GET); - if (err < 0) - return err; - - /* - * If parameter length is still 0, then (1) this is an any length - * integer, and (2) the handler function does not care which length - * we choose to use, so we pick the one the gives the fewest bytes. - */ - if (p.pl == 0) { - if (p.pv.i < 0xff) { - pr_debug("%s(), using 1 byte\n", __func__); - p.pl = 1; - } else if (p.pv.i < 0xffff) { - pr_debug("%s(), using 2 bytes\n", __func__); - p.pl = 2; - } else { - pr_debug("%s(), using 4 bytes\n", __func__); - p.pl = 4; /* Default length */ - } - } - /* Check if buffer is long enough for insertion */ - if (len < (2+p.pl)) { - net_warn_ratelimited("%s: buffer too short for insertion!\n", - __func__); - return -1; - } - pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__, - p.pi, p.pl, p.pv.i); - switch (p.pl) { - case 1: - n += irda_param_pack(buf, "bbb", p.pi, p.pl, (__u8) p.pv.i); - break; - case 2: - if (type & PV_BIG_ENDIAN) - p.pv.i = cpu_to_be16((__u16) p.pv.i); - else - p.pv.i = cpu_to_le16((__u16) p.pv.i); - n += irda_param_pack(buf, "bbs", p.pi, p.pl, (__u16) p.pv.i); - break; - case 4: - if (type & PV_BIG_ENDIAN) - cpu_to_be32s(&p.pv.i); - else - cpu_to_le32s(&p.pv.i); - n += irda_param_pack(buf, "bbi", p.pi, p.pl, p.pv.i); - - break; - default: - net_warn_ratelimited("%s: length %d not supported\n", - __func__, p.pl); - /* Skip parameter */ - return -1; - } - - return p.pl+2; /* Inserted pl+2 bytes */ -} - -/* - * Function irda_extract integer (self, buf, len, pi, type, func) - * - * Extract a possibly variable length integer from buffer, and call - * handler for processing of the parameter - */ -static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - irda_param_t p; - int n = 0; - int extract_len; /* Real length we extract */ - int err; - - p.pi = pi; /* In case handler needs to know */ - p.pl = buf[1]; /* Extract length of value */ - p.pv.i = 0; /* Clear value */ - extract_len = p.pl; /* Default : extract all */ - - /* Check if buffer is long enough for parsing */ - if (len < (2+p.pl)) { - net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", - __func__, p.pl, len); - return -1; - } - - /* - * Check that the integer length is what we expect it to be. If the - * handler want a 16 bits integer then a 32 bits is not good enough - * PV_INTEGER means that the handler is flexible. - */ - if (((type & PV_MASK) != PV_INTEGER) && ((type & PV_MASK) != p.pl)) { - net_err_ratelimited("%s: invalid parameter length! Expected %d bytes, but value had %d bytes!\n", - __func__, type & PV_MASK, p.pl); - - /* Most parameters are bit/byte fields or little endian, - * so it's ok to only extract a subset of it (the subset - * that the handler expect). This is necessary, as some - * broken implementations seems to add extra undefined bits. - * If the parameter is shorter than we expect or is big - * endian, we can't play those tricks. Jean II */ - if((p.pl < (type & PV_MASK)) || (type & PV_BIG_ENDIAN)) { - /* Skip parameter */ - return p.pl+2; - } else { - /* Extract subset of it, fallthrough */ - extract_len = type & PV_MASK; - } - } - - - switch (extract_len) { - case 1: - n += irda_param_unpack(buf+2, "b", &p.pv.i); - break; - case 2: - n += irda_param_unpack(buf+2, "s", &p.pv.i); - if (type & PV_BIG_ENDIAN) - p.pv.i = be16_to_cpu((__u16) p.pv.i); - else - p.pv.i = le16_to_cpu((__u16) p.pv.i); - break; - case 4: - n += irda_param_unpack(buf+2, "i", &p.pv.i); - if (type & PV_BIG_ENDIAN) - be32_to_cpus(&p.pv.i); - else - le32_to_cpus(&p.pv.i); - break; - default: - net_warn_ratelimited("%s: length %d not supported\n", - __func__, p.pl); - - /* Skip parameter */ - return p.pl+2; - } - - pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__, - p.pi, p.pl, p.pv.i); - /* Call handler for this parameter */ - err = (*func)(self, &p, PV_PUT); - if (err < 0) - return err; - - return p.pl+2; /* Extracted pl+2 bytes */ -} - -/* - * Function irda_extract_string (self, buf, len, type, func) - */ -static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - char str[33]; - irda_param_t p; - int err; - - p.pi = pi; /* In case handler needs to know */ - p.pl = buf[1]; /* Extract length of value */ - if (p.pl > 32) - p.pl = 32; - - pr_debug("%s(), pi=%#x, pl=%d\n", __func__, - p.pi, p.pl); - - /* Check if buffer is long enough for parsing */ - if (len < (2+p.pl)) { - net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", - __func__, p.pl, len); - return -1; - } - - /* Should be safe to copy string like this since we have already - * checked that the buffer is long enough */ - strncpy(str, buf+2, p.pl); - - pr_debug("%s(), str=0x%02x 0x%02x\n", - __func__, (__u8)str[0], (__u8)str[1]); - - /* Null terminate string */ - str[p.pl] = '\0'; - - p.pv.c = str; /* Handler will need to take a copy */ - - /* Call handler for this parameter */ - err = (*func)(self, &p, PV_PUT); - if (err < 0) - return err; - - return p.pl+2; /* Extracted pl+2 bytes */ -} - -/* - * Function irda_extract_octseq (self, buf, len, type, func) - */ -static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - irda_param_t p; - - p.pi = pi; /* In case handler needs to know */ - p.pl = buf[1]; /* Extract length of value */ - - /* Check if buffer is long enough for parsing */ - if (len < (2+p.pl)) { - net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", - __func__, p.pl, len); - return -1; - } - - pr_debug("%s(), not impl\n", __func__); - - return p.pl+2; /* Extracted pl+2 bytes */ -} - -/* - * Function irda_param_pack (skb, fmt, ...) - * - * Format: - * 'i' = 32 bits integer - * 's' = string - * - */ -int irda_param_pack(__u8 *buf, char *fmt, ...) -{ - irda_pv_t arg; - va_list args; - char *p; - int n = 0; - - va_start(args, fmt); - - for (p = fmt; *p != '\0'; p++) { - switch (*p) { - case 'b': /* 8 bits unsigned byte */ - buf[n++] = (__u8)va_arg(args, int); - break; - case 's': /* 16 bits unsigned short */ - arg.i = (__u16)va_arg(args, int); - put_unaligned((__u16)arg.i, (__u16 *)(buf+n)); n+=2; - break; - case 'i': /* 32 bits unsigned integer */ - arg.i = va_arg(args, __u32); - put_unaligned(arg.i, (__u32 *)(buf+n)); n+=4; - break; -#if 0 - case 'c': /* \0 terminated string */ - arg.c = va_arg(args, char *); - strcpy(buf+n, arg.c); - n += strlen(arg.c) + 1; - break; -#endif - default: - va_end(args); - return -1; - } - } - va_end(args); - - return 0; -} -EXPORT_SYMBOL(irda_param_pack); - -/* - * Function irda_param_unpack (skb, fmt, ...) - */ -static int irda_param_unpack(__u8 *buf, char *fmt, ...) -{ - irda_pv_t arg; - va_list args; - char *p; - int n = 0; - - va_start(args, fmt); - - for (p = fmt; *p != '\0'; p++) { - switch (*p) { - case 'b': /* 8 bits byte */ - arg.ip = va_arg(args, __u32 *); - *arg.ip = buf[n++]; - break; - case 's': /* 16 bits short */ - arg.ip = va_arg(args, __u32 *); - *arg.ip = get_unaligned((__u16 *)(buf+n)); n+=2; - break; - case 'i': /* 32 bits unsigned integer */ - arg.ip = va_arg(args, __u32 *); - *arg.ip = get_unaligned((__u32 *)(buf+n)); n+=4; - break; -#if 0 - case 'c': /* \0 terminated string */ - arg.c = va_arg(args, char *); - strcpy(arg.c, buf+n); - n += strlen(arg.c) + 1; - break; -#endif - default: - va_end(args); - return -1; - } - - } - va_end(args); - - return 0; -} - -/* - * Function irda_param_insert (self, pi, buf, len, info) - * - * Insert the specified parameter (pi) into buffer. Returns number of - * bytes inserted - */ -int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len, - pi_param_info_t *info) -{ - const pi_minor_info_t *pi_minor_info; - __u8 pi_minor; - __u8 pi_major; - int type; - int ret = -1; - int n = 0; - - IRDA_ASSERT(buf != NULL, return ret;); - IRDA_ASSERT(info != NULL, return ret;); - - pi_minor = pi & info->pi_mask; - pi_major = pi >> info->pi_major_offset; - - /* Check if the identifier value (pi) is valid */ - if ((pi_major > info->len-1) || - (pi_minor > info->tables[pi_major].len-1)) - { - pr_debug("%s(), no handler for parameter=0x%02x\n", - __func__, pi); - - /* Skip this parameter */ - return -1; - } - - /* Lookup the info on how to parse this parameter */ - pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor]; - - /* Find expected data type for this parameter identifier (pi)*/ - type = pi_minor_info->type; - - /* Check if handler has been implemented */ - if (!pi_minor_info->func) { - net_info_ratelimited("%s: no handler for pi=%#x\n", - __func__, pi); - /* Skip this parameter */ - return -1; - } - - /* Insert parameter value */ - ret = (*pv_insert_table[type & PV_MASK])(self, buf+n, len, pi, type, - pi_minor_info->func); - return ret; -} -EXPORT_SYMBOL(irda_param_insert); - -/* - * Function irda_param_extract (self, buf, len, info) - * - * Parse all parameters. If len is correct, then everything should be - * safe. Returns the number of bytes that was parsed - * - */ -static int irda_param_extract(void *self, __u8 *buf, int len, - pi_param_info_t *info) -{ - const pi_minor_info_t *pi_minor_info; - __u8 pi_minor; - __u8 pi_major; - int type; - int ret = -1; - int n = 0; - - IRDA_ASSERT(buf != NULL, return ret;); - IRDA_ASSERT(info != NULL, return ret;); - - pi_minor = buf[n] & info->pi_mask; - pi_major = buf[n] >> info->pi_major_offset; - - /* Check if the identifier value (pi) is valid */ - if ((pi_major > info->len-1) || - (pi_minor > info->tables[pi_major].len-1)) - { - pr_debug("%s(), no handler for parameter=0x%02x\n", - __func__, buf[0]); - - /* Skip this parameter */ - return 2 + buf[n + 1]; /* Continue */ - } - - /* Lookup the info on how to parse this parameter */ - pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor]; - - /* Find expected data type for this parameter identifier (pi)*/ - type = pi_minor_info->type; - - pr_debug("%s(), pi=[%d,%d], type=%d\n", __func__, - pi_major, pi_minor, type); - - /* Check if handler has been implemented */ - if (!pi_minor_info->func) { - net_info_ratelimited("%s: no handler for pi=%#x\n", - __func__, buf[n]); - /* Skip this parameter */ - return 2 + buf[n + 1]; /* Continue */ - } - - /* Parse parameter value */ - ret = (*pv_extract_table[type & PV_MASK])(self, buf+n, len, buf[n], - type, pi_minor_info->func); - return ret; -} - -/* - * Function irda_param_extract_all (self, buf, len, info) - * - * Parse all parameters. If len is correct, then everything should be - * safe. Returns the number of bytes that was parsed - * - */ -int irda_param_extract_all(void *self, __u8 *buf, int len, - pi_param_info_t *info) -{ - int ret = -1; - int n = 0; - - IRDA_ASSERT(buf != NULL, return ret;); - IRDA_ASSERT(info != NULL, return ret;); - - /* - * Parse all parameters. Each parameter must be at least two bytes - * long or else there is no point in trying to parse it - */ - while (len > 2) { - ret = irda_param_extract(self, buf+n, len, info); - if (ret < 0) - return ret; - - n += ret; - len -= ret; - } - return n; -} -EXPORT_SYMBOL(irda_param_extract_all); diff --git a/drivers/staging/irda/net/qos.c b/drivers/staging/irda/net/qos.c deleted file mode 100644 index 25ba8509ad3e..000000000000 --- a/drivers/staging/irda/net/qos.c +++ /dev/null @@ -1,771 +0,0 @@ -/********************************************************************* - * - * Filename: qos.c - * Version: 1.0 - * Description: IrLAP QoS parameter negotiation - * Status: Stable - * Author: Dag Brattli - * Created at: Tue Sep 9 00:00:26 1997 - * Modified at: Sun Jan 30 14:29:16 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - ********************************************************************/ - -#include - -#include - -#include -#include -#include -#include -#include - -/* - * Maximum values of the baud rate we negotiate with the other end. - * Most often, you don't have to change that, because Linux-IrDA will - * use the maximum offered by the link layer, which usually works fine. - * In some very rare cases, you may want to limit it to lower speeds... - */ -int sysctl_max_baud_rate = 16000000; -/* - * Maximum value of the lap disconnect timer we negotiate with the other end. - * Most often, the value below represent the best compromise, but some user - * may want to keep the LAP alive longer or shorter in case of link failure. - * Remember that the threshold time (early warning) is fixed to 3s... - */ -int sysctl_max_noreply_time = 12; -/* - * Minimum turn time to be applied before transmitting to the peer. - * Nonzero values (usec) are used as lower limit to the per-connection - * mtt value which was announced by the other end during negotiation. - * Might be helpful if the peer device provides too short mtt. - * Default is 10us which means using the unmodified value given by the - * peer except if it's 0 (0 is likely a bug in the other stack). - */ -unsigned int sysctl_min_tx_turn_time = 10; -/* - * Maximum data size to be used in transmission in payload of LAP frame. - * There is a bit of confusion in the IrDA spec : - * The LAP spec defines the payload of a LAP frame (I field) to be - * 2048 bytes max (IrLAP 1.1, chapt 6.6.5, p40). - * On the other hand, the PHY mention frames of 2048 bytes max (IrPHY - * 1.2, chapt 5.3.2.1, p41). But, this number includes the LAP header - * (2 bytes), and CRC (32 bits at 4 Mb/s). So, for the I field (LAP - * payload), that's only 2042 bytes. Oups ! - * My nsc-ircc hardware has troubles receiving 2048 bytes frames at 4 Mb/s, - * so adjust to 2042... I don't know if this bug applies only for 2048 - * bytes frames or all negotiated frame sizes, but you can use the sysctl - * to play with this value anyway. - * Jean II */ -unsigned int sysctl_max_tx_data_size = 2042; -/* - * Maximum transmit window, i.e. number of LAP frames between turn-around. - * This allow to override what the peer told us. Some peers are buggy and - * don't always support what they tell us. - * Jean II */ -unsigned int sysctl_max_tx_window = 7; - -static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get); -static int irlap_param_link_disconnect(void *instance, irda_param_t *parm, - int get); -static int irlap_param_max_turn_time(void *instance, irda_param_t *param, - int get); -static int irlap_param_data_size(void *instance, irda_param_t *param, int get); -static int irlap_param_window_size(void *instance, irda_param_t *param, - int get); -static int irlap_param_additional_bofs(void *instance, irda_param_t *parm, - int get); -static int irlap_param_min_turn_time(void *instance, irda_param_t *param, - int get); - -#ifndef CONFIG_IRDA_DYNAMIC_WINDOW -static __u32 irlap_requested_line_capacity(struct qos_info *qos); -#endif - -static __u32 min_turn_times[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */ -static __u32 baud_rates[] = { 2400, 9600, 19200, 38400, 57600, 115200, 576000, - 1152000, 4000000, 16000000 }; /* bps */ -static __u32 data_sizes[] = { 64, 128, 256, 512, 1024, 2048 }; /* bytes */ -static __u32 add_bofs[] = { 48, 24, 12, 5, 3, 2, 1, 0 }; /* bytes */ -static __u32 max_turn_times[] = { 500, 250, 100, 50 }; /* ms */ -static __u32 link_disc_times[] = { 3, 8, 12, 16, 20, 25, 30, 40 }; /* secs */ - -static __u32 max_line_capacities[10][4] = { - /* 500 ms 250 ms 100 ms 50 ms (max turn time) */ - { 100, 0, 0, 0 }, /* 2400 bps */ - { 400, 0, 0, 0 }, /* 9600 bps */ - { 800, 0, 0, 0 }, /* 19200 bps */ - { 1600, 0, 0, 0 }, /* 38400 bps */ - { 2360, 0, 0, 0 }, /* 57600 bps */ - { 4800, 2400, 960, 480 }, /* 115200 bps */ - { 28800, 11520, 5760, 2880 }, /* 576000 bps */ - { 57600, 28800, 11520, 5760 }, /* 1152000 bps */ - { 200000, 100000, 40000, 20000 }, /* 4000000 bps */ - { 800000, 400000, 160000, 80000 }, /* 16000000 bps */ -}; - -static const pi_minor_info_t pi_minor_call_table_type_0[] = { - { NULL, 0 }, -/* 01 */{ irlap_param_baud_rate, PV_INTEGER | PV_LITTLE_ENDIAN }, - { NULL, 0 }, - { NULL, 0 }, - { NULL, 0 }, - { NULL, 0 }, - { NULL, 0 }, - { NULL, 0 }, -/* 08 */{ irlap_param_link_disconnect, PV_INT_8_BITS } -}; - -static const pi_minor_info_t pi_minor_call_table_type_1[] = { - { NULL, 0 }, - { NULL, 0 }, -/* 82 */{ irlap_param_max_turn_time, PV_INT_8_BITS }, -/* 83 */{ irlap_param_data_size, PV_INT_8_BITS }, -/* 84 */{ irlap_param_window_size, PV_INT_8_BITS }, -/* 85 */{ irlap_param_additional_bofs, PV_INT_8_BITS }, -/* 86 */{ irlap_param_min_turn_time, PV_INT_8_BITS }, -}; - -static const pi_major_info_t pi_major_call_table[] = { - { pi_minor_call_table_type_0, 9 }, - { pi_minor_call_table_type_1, 7 }, -}; - -static pi_param_info_t irlap_param_info = { pi_major_call_table, 2, 0x7f, 7 }; - -/* ---------------------- LOCAL SUBROUTINES ---------------------- */ -/* Note : we start with a bunch of local subroutines. - * As the compiler is "one pass", this is the only way to get them to - * inline properly... - * Jean II - */ -/* - * Function value_index (value, array, size) - * - * Returns the index to the value in the specified array - */ -static inline int value_index(__u32 value, __u32 *array, int size) -{ - int i; - - for (i=0; i < size; i++) - if (array[i] == value) - break; - return i; -} - -/* - * Function index_value (index, array) - * - * Returns value to index in array, easy! - * - */ -static inline __u32 index_value(int index, __u32 *array) -{ - return array[index]; -} - -/* - * Function msb_index (word) - * - * Returns index to most significant bit (MSB) in word - * - */ -static int msb_index (__u16 word) -{ - __u16 msb = 0x8000; - int index = 15; /* Current MSB */ - - /* Check for buggy peers. - * Note : there is a small probability that it could be us, but I - * would expect driver authors to catch that pretty early and be - * able to check precisely what's going on. If a end user sees this, - * it's very likely the peer. - Jean II */ - if (word == 0) { - net_warn_ratelimited("%s(), Detected buggy peer, adjust null PV to 0x1!\n", - __func__); - /* The only safe choice (we don't know the array size) */ - word = 0x1; - } - - while (msb) { - if (word & msb) - break; /* Found it! */ - msb >>=1; - index--; - } - return index; -} - -/* - * Function value_lower_bits (value, array) - * - * Returns a bit field marking all possibility lower than value. - */ -static inline int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field) -{ - int i; - __u16 mask = 0x1; - __u16 result = 0x0; - - for (i=0; i < size; i++) { - /* Add the current value to the bit field, shift mask */ - result |= mask; - mask <<= 1; - /* Finished ? */ - if (array[i] >= value) - break; - } - /* Send back a valid index */ - if(i >= size) - i = size - 1; /* Last item */ - *field = result; - return i; -} - -/* - * Function value_highest_bit (value, array) - * - * Returns a bit field marking the highest possibility lower than value. - */ -static inline int value_highest_bit(__u32 value, __u32 *array, int size, __u16 *field) -{ - int i; - __u16 mask = 0x1; - __u16 result = 0x0; - - for (i=0; i < size; i++) { - /* Finished ? */ - if (array[i] <= value) - break; - /* Shift mask */ - mask <<= 1; - } - /* Set the current value to the bit field */ - result |= mask; - /* Send back a valid index */ - if(i >= size) - i = size - 1; /* Last item */ - *field = result; - return i; -} - -/* -------------------------- MAIN CALLS -------------------------- */ - -/* - * Function irda_qos_compute_intersection (qos, new) - * - * Compute the intersection of the old QoS capabilities with new ones - * - */ -void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new) -{ - IRDA_ASSERT(qos != NULL, return;); - IRDA_ASSERT(new != NULL, return;); - - /* Apply */ - qos->baud_rate.bits &= new->baud_rate.bits; - qos->window_size.bits &= new->window_size.bits; - qos->min_turn_time.bits &= new->min_turn_time.bits; - qos->max_turn_time.bits &= new->max_turn_time.bits; - qos->data_size.bits &= new->data_size.bits; - qos->link_disc_time.bits &= new->link_disc_time.bits; - qos->additional_bofs.bits &= new->additional_bofs.bits; - - irda_qos_bits_to_value(qos); -} - -/* - * Function irda_init_max_qos_capabilies (qos) - * - * The purpose of this function is for layers and drivers to be able to - * set the maximum QoS possible and then "and in" their own limitations - * - */ -void irda_init_max_qos_capabilies(struct qos_info *qos) -{ - int i; - /* - * These are the maximum supported values as specified on pages - * 39-43 in IrLAP - */ - - /* Use sysctl to set some configurable values... */ - /* Set configured max speed */ - i = value_lower_bits(sysctl_max_baud_rate, baud_rates, 10, - &qos->baud_rate.bits); - sysctl_max_baud_rate = index_value(i, baud_rates); - - /* Set configured max disc time */ - i = value_lower_bits(sysctl_max_noreply_time, link_disc_times, 8, - &qos->link_disc_time.bits); - sysctl_max_noreply_time = index_value(i, link_disc_times); - - /* LSB is first byte, MSB is second byte */ - qos->baud_rate.bits &= 0x03ff; - - qos->window_size.bits = 0x7f; - qos->min_turn_time.bits = 0xff; - qos->max_turn_time.bits = 0x0f; - qos->data_size.bits = 0x3f; - qos->link_disc_time.bits &= 0xff; - qos->additional_bofs.bits = 0xff; -} -EXPORT_SYMBOL(irda_init_max_qos_capabilies); - -/* - * Function irlap_adjust_qos_settings (qos) - * - * Adjust QoS settings in case some values are not possible to use because - * of other settings - */ -static void irlap_adjust_qos_settings(struct qos_info *qos) -{ - __u32 line_capacity; - int index; - - /* - * Make sure the mintt is sensible. - * Main culprit : Ericsson T39. - Jean II - */ - if (sysctl_min_tx_turn_time > qos->min_turn_time.value) { - int i; - - net_warn_ratelimited("%s(), Detected buggy peer, adjust mtt to %dus!\n", - __func__, sysctl_min_tx_turn_time); - - /* We don't really need bits, but easier this way */ - i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times, - 8, &qos->min_turn_time.bits); - sysctl_min_tx_turn_time = index_value(i, min_turn_times); - qos->min_turn_time.value = sysctl_min_tx_turn_time; - } - - /* - * Not allowed to use a max turn time less than 500 ms if the baudrate - * is less than 115200 - */ - if ((qos->baud_rate.value < 115200) && - (qos->max_turn_time.value < 500)) - { - pr_debug("%s(), adjusting max turn time from %d to 500 ms\n", - __func__, qos->max_turn_time.value); - qos->max_turn_time.value = 500; - } - - /* - * The data size must be adjusted according to the baud rate and max - * turn time - */ - index = value_index(qos->data_size.value, data_sizes, 6); - line_capacity = irlap_max_line_capacity(qos->baud_rate.value, - qos->max_turn_time.value); - -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - while ((qos->data_size.value > line_capacity) && (index > 0)) { - qos->data_size.value = data_sizes[index--]; - pr_debug("%s(), reducing data size to %d\n", - __func__, qos->data_size.value); - } -#else /* Use method described in section 6.6.11 of IrLAP */ - while (irlap_requested_line_capacity(qos) > line_capacity) { - IRDA_ASSERT(index != 0, return;); - - /* Must be able to send at least one frame */ - if (qos->window_size.value > 1) { - qos->window_size.value--; - pr_debug("%s(), reducing window size to %d\n", - __func__, qos->window_size.value); - } else if (index > 1) { - qos->data_size.value = data_sizes[index--]; - pr_debug("%s(), reducing data size to %d\n", - __func__, qos->data_size.value); - } else { - net_warn_ratelimited("%s(), nothing more we can do!\n", - __func__); - } - } -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - /* - * Fix tx data size according to user limits - Jean II - */ - if (qos->data_size.value > sysctl_max_tx_data_size) - /* Allow non discrete adjustement to avoid losing capacity */ - qos->data_size.value = sysctl_max_tx_data_size; - /* - * Override Tx window if user request it. - Jean II - */ - if (qos->window_size.value > sysctl_max_tx_window) - qos->window_size.value = sysctl_max_tx_window; -} - -/* - * Function irlap_negotiate (qos_device, qos_session, skb) - * - * Negotiate QoS values, not really that much negotiation :-) - * We just set the QoS capabilities for the peer station - * - */ -int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb) -{ - int ret; - - ret = irda_param_extract_all(self, skb->data, skb->len, - &irlap_param_info); - - /* Convert the negotiated bits to values */ - irda_qos_bits_to_value(&self->qos_tx); - irda_qos_bits_to_value(&self->qos_rx); - - irlap_adjust_qos_settings(&self->qos_tx); - - pr_debug("Setting BAUD_RATE to %d bps.\n", - self->qos_tx.baud_rate.value); - pr_debug("Setting DATA_SIZE to %d bytes\n", - self->qos_tx.data_size.value); - pr_debug("Setting WINDOW_SIZE to %d\n", - self->qos_tx.window_size.value); - pr_debug("Setting XBOFS to %d\n", - self->qos_tx.additional_bofs.value); - pr_debug("Setting MAX_TURN_TIME to %d ms.\n", - self->qos_tx.max_turn_time.value); - pr_debug("Setting MIN_TURN_TIME to %d usecs.\n", - self->qos_tx.min_turn_time.value); - pr_debug("Setting LINK_DISC to %d secs.\n", - self->qos_tx.link_disc_time.value); - return ret; -} - -/* - * Function irlap_insert_negotiation_params (qos, fp) - * - * Insert QoS negotiaion pararameters into frame - * - */ -int irlap_insert_qos_negotiation_params(struct irlap_cb *self, - struct sk_buff *skb) -{ - int ret; - - /* Insert data rate */ - ret = irda_param_insert(self, PI_BAUD_RATE, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert max turnaround time */ - ret = irda_param_insert(self, PI_MAX_TURN_TIME, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert data size */ - ret = irda_param_insert(self, PI_DATA_SIZE, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert window size */ - ret = irda_param_insert(self, PI_WINDOW_SIZE, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert additional BOFs */ - ret = irda_param_insert(self, PI_ADD_BOFS, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert minimum turnaround time */ - ret = irda_param_insert(self, PI_MIN_TURN_TIME, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert link disconnect/threshold time */ - ret = irda_param_insert(self, PI_LINK_DISC, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - return 0; -} - -/* - * Function irlap_param_baud_rate (instance, param, get) - * - * Negotiate data-rate - * - */ -static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get) -{ - __u16 final; - - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) { - param->pv.i = self->qos_rx.baud_rate.bits; - pr_debug("%s(), baud rate = 0x%02x\n", - __func__, param->pv.i); - } else { - /* - * Stations must agree on baud rate, so calculate - * intersection - */ - pr_debug("Requested BAUD_RATE: 0x%04x\n", (__u16)param->pv.i); - final = (__u16) param->pv.i & self->qos_rx.baud_rate.bits; - - pr_debug("Final BAUD_RATE: 0x%04x\n", final); - self->qos_tx.baud_rate.bits = final; - self->qos_rx.baud_rate.bits = final; - } - - return 0; -} - -/* - * Function irlap_param_link_disconnect (instance, param, get) - * - * Negotiate link disconnect/threshold time. - * - */ -static int irlap_param_link_disconnect(void *instance, irda_param_t *param, - int get) -{ - __u16 final; - - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.link_disc_time.bits; - else { - /* - * Stations must agree on link disconnect/threshold - * time. - */ - pr_debug("LINK_DISC: %02x\n", (__u8)param->pv.i); - final = (__u8) param->pv.i & self->qos_rx.link_disc_time.bits; - - pr_debug("Final LINK_DISC: %02x\n", final); - self->qos_tx.link_disc_time.bits = final; - self->qos_rx.link_disc_time.bits = final; - } - return 0; -} - -/* - * Function irlap_param_max_turn_time (instance, param, get) - * - * Negotiate the maximum turnaround time. This is a type 1 parameter and - * will be negotiated independently for each station - * - */ -static int irlap_param_max_turn_time(void *instance, irda_param_t *param, - int get) -{ - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.max_turn_time.bits; - else - self->qos_tx.max_turn_time.bits = (__u8) param->pv.i; - - return 0; -} - -/* - * Function irlap_param_data_size (instance, param, get) - * - * Negotiate the data size. This is a type 1 parameter and - * will be negotiated independently for each station - * - */ -static int irlap_param_data_size(void *instance, irda_param_t *param, int get) -{ - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.data_size.bits; - else - self->qos_tx.data_size.bits = (__u8) param->pv.i; - - return 0; -} - -/* - * Function irlap_param_window_size (instance, param, get) - * - * Negotiate the window size. This is a type 1 parameter and - * will be negotiated independently for each station - * - */ -static int irlap_param_window_size(void *instance, irda_param_t *param, - int get) -{ - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.window_size.bits; - else - self->qos_tx.window_size.bits = (__u8) param->pv.i; - - return 0; -} - -/* - * Function irlap_param_additional_bofs (instance, param, get) - * - * Negotiate additional BOF characters. This is a type 1 parameter and - * will be negotiated independently for each station. - */ -static int irlap_param_additional_bofs(void *instance, irda_param_t *param, int get) -{ - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.additional_bofs.bits; - else - self->qos_tx.additional_bofs.bits = (__u8) param->pv.i; - - return 0; -} - -/* - * Function irlap_param_min_turn_time (instance, param, get) - * - * Negotiate the minimum turn around time. This is a type 1 parameter and - * will be negotiated independently for each station - */ -static int irlap_param_min_turn_time(void *instance, irda_param_t *param, - int get) -{ - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.min_turn_time.bits; - else - self->qos_tx.min_turn_time.bits = (__u8) param->pv.i; - - return 0; -} - -/* - * Function irlap_max_line_capacity (speed, max_turn_time, min_turn_time) - * - * Calculate the maximum line capacity - * - */ -__u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time) -{ - __u32 line_capacity; - int i,j; - - pr_debug("%s(), speed=%d, max_turn_time=%d\n", - __func__, speed, max_turn_time); - - i = value_index(speed, baud_rates, 10); - j = value_index(max_turn_time, max_turn_times, 4); - - IRDA_ASSERT(((i >=0) && (i <10)), return 0;); - IRDA_ASSERT(((j >=0) && (j <4)), return 0;); - - line_capacity = max_line_capacities[i][j]; - - pr_debug("%s(), line capacity=%d bytes\n", - __func__, line_capacity); - - return line_capacity; -} - -#ifndef CONFIG_IRDA_DYNAMIC_WINDOW -static __u32 irlap_requested_line_capacity(struct qos_info *qos) -{ - __u32 line_capacity; - - line_capacity = qos->window_size.value * - (qos->data_size.value + 6 + qos->additional_bofs.value) + - irlap_min_turn_time_in_bytes(qos->baud_rate.value, - qos->min_turn_time.value); - - pr_debug("%s(), requested line capacity=%d\n", - __func__, line_capacity); - - return line_capacity; -} -#endif - -void irda_qos_bits_to_value(struct qos_info *qos) -{ - int index; - - IRDA_ASSERT(qos != NULL, return;); - - index = msb_index(qos->baud_rate.bits); - qos->baud_rate.value = baud_rates[index]; - - index = msb_index(qos->data_size.bits); - qos->data_size.value = data_sizes[index]; - - index = msb_index(qos->window_size.bits); - qos->window_size.value = index+1; - - index = msb_index(qos->min_turn_time.bits); - qos->min_turn_time.value = min_turn_times[index]; - - index = msb_index(qos->max_turn_time.bits); - qos->max_turn_time.value = max_turn_times[index]; - - index = msb_index(qos->link_disc_time.bits); - qos->link_disc_time.value = link_disc_times[index]; - - index = msb_index(qos->additional_bofs.bits); - qos->additional_bofs.value = add_bofs[index]; -} -EXPORT_SYMBOL(irda_qos_bits_to_value); diff --git a/drivers/staging/irda/net/timer.c b/drivers/staging/irda/net/timer.c deleted file mode 100644 index cf00c0d848aa..000000000000 --- a/drivers/staging/irda/net/timer.c +++ /dev/null @@ -1,231 +0,0 @@ -/********************************************************************* - * - * Filename: timer.c - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Aug 16 00:59:29 1997 - * Modified at: Wed Dec 8 12:50:34 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include - -#include -#include -#include -#include -#include - -extern int sysctl_slot_timeout; - -static void irlap_slot_timer_expired(struct timer_list *t); -static void irlap_query_timer_expired(struct timer_list *t); -static void irlap_final_timer_expired(struct timer_list *t); -static void irlap_wd_timer_expired(struct timer_list *t); -static void irlap_backoff_timer_expired(struct timer_list *t); -static void irlap_media_busy_expired(struct timer_list *t); - -void irlap_start_slot_timer(struct irlap_cb *self, int timeout) -{ - irda_start_timer(&self->slot_timer, timeout, - irlap_slot_timer_expired); -} - -void irlap_start_query_timer(struct irlap_cb *self, int S, int s) -{ - int timeout; - - /* Calculate when the peer discovery should end. Normally, we - * get the end-of-discovery frame, so this is just in case - * we miss it. - * Basically, we multiply the number of remaining slots by our - * slot time, plus add some extra time to properly receive the last - * discovery packet (which is longer due to extra discovery info), - * to avoid messing with for incoming connections requests and - * to accommodate devices that perform discovery slower than us. - * Jean II */ - timeout = msecs_to_jiffies(sysctl_slot_timeout) * (S - s) - + XIDEXTRA_TIMEOUT + SMALLBUSY_TIMEOUT; - - /* Set or re-set the timer. We reset the timer for each received - * discovery query, which allow us to automatically adjust to - * the speed of the peer discovery (faster or slower). Jean II */ - irda_start_timer(&self->query_timer, timeout, - irlap_query_timer_expired); -} - -void irlap_start_final_timer(struct irlap_cb *self, int timeout) -{ - irda_start_timer(&self->final_timer, timeout, - irlap_final_timer_expired); -} - -void irlap_start_wd_timer(struct irlap_cb *self, int timeout) -{ - irda_start_timer(&self->wd_timer, timeout, - irlap_wd_timer_expired); -} - -void irlap_start_backoff_timer(struct irlap_cb *self, int timeout) -{ - irda_start_timer(&self->backoff_timer, timeout, - irlap_backoff_timer_expired); -} - -void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout) -{ - irda_start_timer(&self->media_busy_timer, timeout, - irlap_media_busy_expired); -} - -void irlap_stop_mbusy_timer(struct irlap_cb *self) -{ - /* If timer is activated, kill it! */ - del_timer(&self->media_busy_timer); - - /* If we are in NDM, there is a bunch of events in LAP that - * that be pending due to the media_busy condition, such as - * CONNECT_REQUEST and SEND_UI_FRAME. If we don't generate - * an event, they will wait forever... - * Jean II */ - if (self->state == LAP_NDM) - irlap_do_event(self, MEDIA_BUSY_TIMER_EXPIRED, NULL, NULL); -} - -void irlmp_start_watchdog_timer(struct lsap_cb *self, int timeout) -{ - irda_start_timer(&self->watchdog_timer, timeout, - irlmp_watchdog_timer_expired); -} - -void irlmp_start_discovery_timer(struct irlmp_cb *self, int timeout) -{ - irda_start_timer(&self->discovery_timer, timeout, - irlmp_discovery_timer_expired); -} - -void irlmp_start_idle_timer(struct lap_cb *self, int timeout) -{ - irda_start_timer(&self->idle_timer, timeout, - irlmp_idle_timer_expired); -} - -void irlmp_stop_idle_timer(struct lap_cb *self) -{ - /* If timer is activated, kill it! */ - del_timer(&self->idle_timer); -} - -/* - * Function irlap_slot_timer_expired (data) - * - * IrLAP slot timer has expired - * - */ -static void irlap_slot_timer_expired(struct timer_list *t) -{ - struct irlap_cb *self = from_timer(self, t, slot_timer); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, SLOT_TIMER_EXPIRED, NULL, NULL); -} - -/* - * Function irlap_query_timer_expired (data) - * - * IrLAP query timer has expired - * - */ -static void irlap_query_timer_expired(struct timer_list *t) -{ - struct irlap_cb *self = from_timer(self, t, query_timer); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, QUERY_TIMER_EXPIRED, NULL, NULL); -} - -/* - * Function irda_final_timer_expired (data) - * - * - * - */ -static void irlap_final_timer_expired(struct timer_list *t) -{ - struct irlap_cb *self = from_timer(self, t, final_timer); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, FINAL_TIMER_EXPIRED, NULL, NULL); -} - -/* - * Function irda_wd_timer_expired (data) - * - * - * - */ -static void irlap_wd_timer_expired(struct timer_list *t) -{ - struct irlap_cb *self = from_timer(self, t, wd_timer); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, WD_TIMER_EXPIRED, NULL, NULL); -} - -/* - * Function irda_backoff_timer_expired (data) - * - * - * - */ -static void irlap_backoff_timer_expired(struct timer_list *t) -{ - struct irlap_cb *self = from_timer(self, t, backoff_timer); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, BACKOFF_TIMER_EXPIRED, NULL, NULL); -} - - -/* - * Function irtty_media_busy_expired (data) - * - * - */ -static void irlap_media_busy_expired(struct timer_list *t) -{ - struct irlap_cb *self = from_timer(self, t, media_busy_timer); - - IRDA_ASSERT(self != NULL, return;); - - irda_device_set_media_busy(self->netdev, FALSE); - /* Note : the LAP event will be send in irlap_stop_mbusy_timer(), - * to catch other cases where the flag is cleared (for example - * after a discovery) - Jean II */ -} diff --git a/drivers/staging/irda/net/wrapper.c b/drivers/staging/irda/net/wrapper.c deleted file mode 100644 index 40a0f993bf13..000000000000 --- a/drivers/staging/irda/net/wrapper.c +++ /dev/null @@ -1,492 +0,0 @@ -/********************************************************************* - * - * Filename: wrapper.c - * Version: 1.2 - * Description: IrDA SIR async wrapper layer - * Status: Stable - * Author: Dag Brattli - * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Fri Jan 28 13:21:09 2000 - * Modified by: Dag Brattli - * Modified at: Fri May 28 3:11 CST 1999 - * Modified by: Horst von Brand - * - * Copyright (c) 1998-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/************************** FRAME WRAPPING **************************/ -/* - * Unwrap and unstuff SIR frames - * - * Note : at FIR and MIR, HDLC framing is used and usually handled - * by the controller, so we come here only for SIR... Jean II - */ - -/* - * Function stuff_byte (byte, buf) - * - * Byte stuff one single byte and put the result in buffer pointed to by - * buf. The buffer must at all times be able to have two bytes inserted. - * - * This is in a tight loop, better inline it, so need to be prior to callers. - * (2000 bytes on P6 200MHz, non-inlined ~370us, inline ~170us) - Jean II - */ -static inline int stuff_byte(__u8 byte, __u8 *buf) -{ - switch (byte) { - case BOF: /* FALLTHROUGH */ - case EOF: /* FALLTHROUGH */ - case CE: - /* Insert transparently coded */ - buf[0] = CE; /* Send link escape */ - buf[1] = byte^IRDA_TRANS; /* Complement bit 5 */ - return 2; - /* break; */ - default: - /* Non-special value, no transparency required */ - buf[0] = byte; - return 1; - /* break; */ - } -} - -/* - * Function async_wrap (skb, *tx_buff, buffsize) - * - * Makes a new buffer with wrapping and stuffing, should check that - * we don't get tx buffer overflow. - */ -int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) -{ - struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; - int xbofs; - int i; - int n; - union { - __u16 value; - __u8 bytes[2]; - } fcs; - - /* Initialize variables */ - fcs.value = INIT_FCS; - n = 0; - - /* - * Send XBOF's for required min. turn time and for the negotiated - * additional XBOFS - */ - - if (cb->magic != LAP_MAGIC) { - /* - * This will happen for all frames sent from user-space. - * Nothing to worry about, but we set the default number of - * BOF's - */ - pr_debug("%s(), wrong magic in skb!\n", __func__); - xbofs = 10; - } else - xbofs = cb->xbofs + cb->xbofs_delay; - - pr_debug("%s(), xbofs=%d\n", __func__, xbofs); - - /* Check that we never use more than 115 + 48 xbofs */ - if (xbofs > 163) { - pr_debug("%s(), too many xbofs (%d)\n", __func__, - xbofs); - xbofs = 163; - } - - memset(tx_buff + n, XBOF, xbofs); - n += xbofs; - - /* Start of packet character BOF */ - tx_buff[n++] = BOF; - - /* Insert frame and calc CRC */ - for (i=0; i < skb->len; i++) { - /* - * Check for the possibility of tx buffer overflow. We use - * bufsize-5 since the maximum number of bytes that can be - * transmitted after this point is 5. - */ - if(n >= (buffsize-5)) { - net_err_ratelimited("%s(), tx buffer overflow (n=%d)\n", - __func__, n); - return n; - } - - n += stuff_byte(skb->data[i], tx_buff+n); - fcs.value = irda_fcs(fcs.value, skb->data[i]); - } - - /* Insert CRC in little endian format (LSB first) */ - fcs.value = ~fcs.value; -#ifdef __LITTLE_ENDIAN - n += stuff_byte(fcs.bytes[0], tx_buff+n); - n += stuff_byte(fcs.bytes[1], tx_buff+n); -#else /* ifdef __BIG_ENDIAN */ - n += stuff_byte(fcs.bytes[1], tx_buff+n); - n += stuff_byte(fcs.bytes[0], tx_buff+n); -#endif - tx_buff[n++] = EOF; - - return n; -} -EXPORT_SYMBOL(async_wrap_skb); - -/************************* FRAME UNWRAPPING *************************/ -/* - * Unwrap and unstuff SIR frames - * - * Complete rewrite by Jean II : - * More inline, faster, more compact, more logical. Jean II - * (16 bytes on P6 200MHz, old 5 to 7 us, new 4 to 6 us) - * (24 bytes on P6 200MHz, old 9 to 10 us, new 7 to 8 us) - * (for reference, 115200 b/s is 1 byte every 69 us) - * And reduce wrapper.o by ~900B in the process ;-) - * - * Then, we have the addition of ZeroCopy, which is optional - * (i.e. the driver must initiate it) and improve final processing. - * (2005 B frame + EOF on P6 200MHz, without 30 to 50 us, with 10 to 25 us) - * - * Note : at FIR and MIR, HDLC framing is used and usually handled - * by the controller, so we come here only for SIR... Jean II - */ - -/* - * We can also choose where we want to do the CRC calculation. We can - * do it "inline", as we receive the bytes, or "postponed", when - * receiving the End-Of-Frame. - * (16 bytes on P6 200MHz, inlined 4 to 6 us, postponed 4 to 5 us) - * (24 bytes on P6 200MHz, inlined 7 to 8 us, postponed 5 to 7 us) - * With ZeroCopy : - * (2005 B frame on P6 200MHz, inlined 10 to 25 us, postponed 140 to 180 us) - * Without ZeroCopy : - * (2005 B frame on P6 200MHz, inlined 30 to 50 us, postponed 150 to 180 us) - * (Note : numbers taken with irq disabled) - * - * From those numbers, it's not clear which is the best strategy, because - * we end up running through a lot of data one way or another (i.e. cache - * misses). I personally prefer to avoid the huge latency spike of the - * "postponed" solution, because it come just at the time when we have - * lot's of protocol processing to do and it will hurt our ability to - * reach low link turnaround times... Jean II - */ -//#define POSTPONE_RX_CRC - -/* - * Function async_bump (buf, len, stats) - * - * Got a frame, make a copy of it, and pass it up the stack! We can try - * to inline it since it's only called from state_inside_frame - */ -static inline void -async_bump(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff) -{ - struct sk_buff *newskb; - struct sk_buff *dataskb; - int docopy; - - /* Check if we need to copy the data to a new skb or not. - * If the driver doesn't use ZeroCopy Rx, we have to do it. - * With ZeroCopy Rx, the rx_buff already point to a valid - * skb. But, if the frame is small, it is more efficient to - * copy it to save memory (copy will be fast anyway - that's - * called Rx-copy-break). Jean II */ - docopy = ((rx_buff->skb == NULL) || - (rx_buff->len < IRDA_RX_COPY_THRESHOLD)); - - /* Allocate a new skb */ - newskb = dev_alloc_skb(docopy ? rx_buff->len + 1 : rx_buff->truesize); - if (!newskb) { - stats->rx_dropped++; - /* We could deliver the current skb if doing ZeroCopy Rx, - * but this would stall the Rx path. Better drop the - * packet... Jean II */ - return; - } - - /* Align IP header to 20 bytes (i.e. increase skb->data) - * Note this is only useful with IrLAN, as PPP has a variable - * header size (2 or 1 bytes) - Jean II */ - skb_reserve(newskb, 1); - - if(docopy) { - /* Copy data without CRC (length already checked) */ - skb_copy_to_linear_data(newskb, rx_buff->data, - rx_buff->len - 2); - /* Deliver this skb */ - dataskb = newskb; - } else { - /* We are using ZeroCopy. Deliver old skb */ - dataskb = rx_buff->skb; - /* And hook the new skb to the rx_buff */ - rx_buff->skb = newskb; - rx_buff->head = newskb->data; /* NOT newskb->head */ - //printk(KERN_DEBUG "ZeroCopy : len = %d, dataskb = %p, newskb = %p\n", rx_buff->len, dataskb, newskb); - } - - /* Set proper length on skb (without CRC) */ - skb_put(dataskb, rx_buff->len - 2); - - /* Feed it to IrLAP layer */ - dataskb->dev = dev; - skb_reset_mac_header(dataskb); - dataskb->protocol = htons(ETH_P_IRDA); - - netif_rx(dataskb); - - stats->rx_packets++; - stats->rx_bytes += rx_buff->len; - - /* Clean up rx_buff (redundant with async_unwrap_bof() ???) */ - rx_buff->data = rx_buff->head; - rx_buff->len = 0; -} - -/* - * Function async_unwrap_bof(dev, byte) - * - * Handle Beginning Of Frame character received within a frame - * - */ -static inline void -async_unwrap_bof(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff, __u8 byte) -{ - switch(rx_buff->state) { - case LINK_ESCAPE: - case INSIDE_FRAME: - /* Not supposed to happen, the previous frame is not - * finished - Jean II */ - pr_debug("%s(), Discarding incomplete frame\n", - __func__); - stats->rx_errors++; - stats->rx_missed_errors++; - irda_device_set_media_busy(dev, TRUE); - break; - - case OUTSIDE_FRAME: - case BEGIN_FRAME: - default: - /* We may receive multiple BOF at the start of frame */ - break; - } - - /* Now receiving frame */ - rx_buff->state = BEGIN_FRAME; - rx_buff->in_frame = TRUE; - - /* Time to initialize receive buffer */ - rx_buff->data = rx_buff->head; - rx_buff->len = 0; - rx_buff->fcs = INIT_FCS; -} - -/* - * Function async_unwrap_eof(dev, byte) - * - * Handle End Of Frame character received within a frame - * - */ -static inline void -async_unwrap_eof(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff, __u8 byte) -{ -#ifdef POSTPONE_RX_CRC - int i; -#endif - - switch(rx_buff->state) { - case OUTSIDE_FRAME: - /* Probably missed the BOF */ - stats->rx_errors++; - stats->rx_missed_errors++; - irda_device_set_media_busy(dev, TRUE); - break; - - case BEGIN_FRAME: - case LINK_ESCAPE: - case INSIDE_FRAME: - default: - /* Note : in the case of BEGIN_FRAME and LINK_ESCAPE, - * the fcs will most likely not match and generate an - * error, as expected - Jean II */ - rx_buff->state = OUTSIDE_FRAME; - rx_buff->in_frame = FALSE; - -#ifdef POSTPONE_RX_CRC - /* If we haven't done the CRC as we receive bytes, we - * must do it now... Jean II */ - for(i = 0; i < rx_buff->len; i++) - rx_buff->fcs = irda_fcs(rx_buff->fcs, - rx_buff->data[i]); -#endif - - /* Test FCS and signal success if the frame is good */ - if (rx_buff->fcs == GOOD_FCS) { - /* Deliver frame */ - async_bump(dev, stats, rx_buff); - break; - } else { - /* Wrong CRC, discard frame! */ - irda_device_set_media_busy(dev, TRUE); - - pr_debug("%s(), crc error\n", __func__); - stats->rx_errors++; - stats->rx_crc_errors++; - } - break; - } -} - -/* - * Function async_unwrap_ce(dev, byte) - * - * Handle Character Escape character received within a frame - * - */ -static inline void -async_unwrap_ce(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff, __u8 byte) -{ - switch(rx_buff->state) { - case OUTSIDE_FRAME: - /* Activate carrier sense */ - irda_device_set_media_busy(dev, TRUE); - break; - - case LINK_ESCAPE: - net_warn_ratelimited("%s: state not defined\n", __func__); - break; - - case BEGIN_FRAME: - case INSIDE_FRAME: - default: - /* Stuffed byte coming */ - rx_buff->state = LINK_ESCAPE; - break; - } -} - -/* - * Function async_unwrap_other(dev, byte) - * - * Handle other characters received within a frame - * - */ -static inline void -async_unwrap_other(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff, __u8 byte) -{ - switch(rx_buff->state) { - /* This is on the critical path, case are ordered by - * probability (most frequent first) - Jean II */ - case INSIDE_FRAME: - /* Must be the next byte of the frame */ - if (rx_buff->len < rx_buff->truesize) { - rx_buff->data[rx_buff->len++] = byte; -#ifndef POSTPONE_RX_CRC - rx_buff->fcs = irda_fcs(rx_buff->fcs, byte); -#endif - } else { - pr_debug("%s(), Rx buffer overflow, aborting\n", - __func__); - rx_buff->state = OUTSIDE_FRAME; - } - break; - - case LINK_ESCAPE: - /* - * Stuffed char, complement bit 5 of byte - * following CE, IrLAP p.114 - */ - byte ^= IRDA_TRANS; - if (rx_buff->len < rx_buff->truesize) { - rx_buff->data[rx_buff->len++] = byte; -#ifndef POSTPONE_RX_CRC - rx_buff->fcs = irda_fcs(rx_buff->fcs, byte); -#endif - rx_buff->state = INSIDE_FRAME; - } else { - pr_debug("%s(), Rx buffer overflow, aborting\n", - __func__); - rx_buff->state = OUTSIDE_FRAME; - } - break; - - case OUTSIDE_FRAME: - /* Activate carrier sense */ - if(byte != XBOF) - irda_device_set_media_busy(dev, TRUE); - break; - - case BEGIN_FRAME: - default: - rx_buff->data[rx_buff->len++] = byte; -#ifndef POSTPONE_RX_CRC - rx_buff->fcs = irda_fcs(rx_buff->fcs, byte); -#endif - rx_buff->state = INSIDE_FRAME; - break; - } -} - -/* - * Function async_unwrap_char (dev, rx_buff, byte) - * - * Parse and de-stuff frame received from the IrDA-port - * - * This is the main entry point for SIR drivers. - */ -void async_unwrap_char(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff, __u8 byte) -{ - switch(byte) { - case CE: - async_unwrap_ce(dev, stats, rx_buff, byte); - break; - case BOF: - async_unwrap_bof(dev, stats, rx_buff, byte); - break; - case EOF: - async_unwrap_eof(dev, stats, rx_buff, byte); - break; - default: - async_unwrap_other(dev, stats, rx_buff, byte); - break; - } -} -EXPORT_SYMBOL(async_unwrap_char); - diff --git a/include/uapi/linux/irda.h b/include/uapi/linux/irda.h deleted file mode 100644 index 2105c266aa6e..000000000000 --- a/include/uapi/linux/irda.h +++ /dev/null @@ -1,252 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ -/********************************************************************* - * - * Filename: irda.h - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Mar 8 14:06:12 1999 - * Modified at: Sat Dec 25 16:06:42 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#ifndef KERNEL_IRDA_H -#define KERNEL_IRDA_H - -#include -#include - -/* Note that this file is shared with user space. */ - -/* Hint bit positions for first hint byte */ -#define HINT_PNP 0x01 -#define HINT_PDA 0x02 -#define HINT_COMPUTER 0x04 -#define HINT_PRINTER 0x08 -#define HINT_MODEM 0x10 -#define HINT_FAX 0x20 -#define HINT_LAN 0x40 -#define HINT_EXTENSION 0x80 - -/* Hint bit positions for second hint byte (first extension byte) */ -#define HINT_TELEPHONY 0x01 -#define HINT_FILE_SERVER 0x02 -#define HINT_COMM 0x04 -#define HINT_MESSAGE 0x08 -#define HINT_HTTP 0x10 -#define HINT_OBEX 0x20 - -/* IrLMP character code values */ -#define CS_ASCII 0x00 -#define CS_ISO_8859_1 0x01 -#define CS_ISO_8859_2 0x02 -#define CS_ISO_8859_3 0x03 -#define CS_ISO_8859_4 0x04 -#define CS_ISO_8859_5 0x05 -#define CS_ISO_8859_6 0x06 -#define CS_ISO_8859_7 0x07 -#define CS_ISO_8859_8 0x08 -#define CS_ISO_8859_9 0x09 -#define CS_UNICODE 0xff - -/* These are the currently known dongles */ -typedef enum { - IRDA_TEKRAM_DONGLE = 0, - IRDA_ESI_DONGLE = 1, - IRDA_ACTISYS_DONGLE = 2, - IRDA_ACTISYS_PLUS_DONGLE = 3, - IRDA_GIRBIL_DONGLE = 4, - IRDA_LITELINK_DONGLE = 5, - IRDA_AIRPORT_DONGLE = 6, - IRDA_OLD_BELKIN_DONGLE = 7, - IRDA_EP7211_IR = 8, - IRDA_MCP2120_DONGLE = 9, - IRDA_ACT200L_DONGLE = 10, - IRDA_MA600_DONGLE = 11, - IRDA_TOIM3232_DONGLE = 12, - IRDA_EP7211_DONGLE = 13, -} IRDA_DONGLE; - -/* Protocol types to be used for SOCK_DGRAM */ -enum { - IRDAPROTO_UNITDATA = 0, - IRDAPROTO_ULTRA = 1, - IRDAPROTO_MAX -}; - -#define SOL_IRLMP 266 /* Same as SOL_IRDA for now */ -#define SOL_IRTTP 266 /* Same as SOL_IRDA for now */ - -#define IRLMP_ENUMDEVICES 1 /* Return discovery log */ -#define IRLMP_IAS_SET 2 /* Set an attribute in local IAS */ -#define IRLMP_IAS_QUERY 3 /* Query remote IAS for attribute */ -#define IRLMP_HINTS_SET 4 /* Set hint bits advertised */ -#define IRLMP_QOS_SET 5 -#define IRLMP_QOS_GET 6 -#define IRLMP_MAX_SDU_SIZE 7 -#define IRLMP_IAS_GET 8 /* Get an attribute from local IAS */ -#define IRLMP_IAS_DEL 9 /* Remove attribute from local IAS */ -#define IRLMP_HINT_MASK_SET 10 /* Set discovery filter */ -#define IRLMP_WAITDEVICE 11 /* Wait for a new discovery */ - -#define IRTTP_MAX_SDU_SIZE IRLMP_MAX_SDU_SIZE /* Compatibility */ - -#define IAS_MAX_STRING 256 /* See IrLMP 1.1, 4.3.3.2 */ -#define IAS_MAX_OCTET_STRING 1024 /* See IrLMP 1.1, 4.3.3.2 */ -#define IAS_MAX_CLASSNAME 60 /* See IrLMP 1.1, 4.3.1 */ -#define IAS_MAX_ATTRIBNAME 60 /* See IrLMP 1.1, 4.3.3.1 */ -#define IAS_MAX_ATTRIBNUMBER 256 /* See IrLMP 1.1, 4.3.3.1 */ -/* For user space backward compatibility - may be fixed in kernel 2.5.X - * Note : need 60+1 ('\0'), make it 64 for alignement - Jean II */ -#define IAS_EXPORT_CLASSNAME 64 -#define IAS_EXPORT_ATTRIBNAME 256 - -/* Attribute type needed for struct irda_ias_set */ -#define IAS_MISSING 0 -#define IAS_INTEGER 1 -#define IAS_OCT_SEQ 2 -#define IAS_STRING 3 - -#define LSAP_ANY 0xff - -struct sockaddr_irda { - __kernel_sa_family_t sir_family; /* AF_IRDA */ - __u8 sir_lsap_sel; /* LSAP selector */ - __u32 sir_addr; /* Device address */ - char sir_name[25]; /* Usually :IrDA:TinyTP */ -}; - -struct irda_device_info { - __u32 saddr; /* Address of local interface */ - __u32 daddr; /* Address of remote device */ - char info[22]; /* Description */ - __u8 charset; /* Charset used for description */ - __u8 hints[2]; /* Hint bits */ -}; - -struct irda_device_list { - __u32 len; - struct irda_device_info dev[1]; -}; - -struct irda_ias_set { - char irda_class_name[IAS_EXPORT_CLASSNAME]; - char irda_attrib_name[IAS_EXPORT_ATTRIBNAME]; - unsigned int irda_attrib_type; - union { - unsigned int irda_attrib_int; - struct { - unsigned short len; - __u8 octet_seq[IAS_MAX_OCTET_STRING]; - } irda_attrib_octet_seq; - struct { - __u8 len; - __u8 charset; - __u8 string[IAS_MAX_STRING]; - } irda_attrib_string; - } attribute; - __u32 daddr; /* Address of device (for some queries only) */ -}; - -/* Some private IOCTL's (max 16) */ -#define SIOCSDONGLE (SIOCDEVPRIVATE + 0) -#define SIOCGDONGLE (SIOCDEVPRIVATE + 1) -#define SIOCSBANDWIDTH (SIOCDEVPRIVATE + 2) -#define SIOCSMEDIABUSY (SIOCDEVPRIVATE + 3) -#define SIOCGMEDIABUSY (SIOCDEVPRIVATE + 4) -#define SIOCGRECEIVING (SIOCDEVPRIVATE + 5) -#define SIOCSMODE (SIOCDEVPRIVATE + 6) -#define SIOCGMODE (SIOCDEVPRIVATE + 7) -#define SIOCSDTRRTS (SIOCDEVPRIVATE + 8) -#define SIOCGQOS (SIOCDEVPRIVATE + 9) - -/* No reason to include just because of this one ;-) */ -#define IRNAMSIZ 16 - -/* IrDA quality of service information (must not exceed 16 bytes) */ -struct if_irda_qos { - unsigned long baudrate; - unsigned short data_size; - unsigned short window_size; - unsigned short min_turn_time; - unsigned short max_turn_time; - unsigned char add_bofs; - unsigned char link_disc; -}; - -/* For setting RTS and DTR lines of a dongle */ -struct if_irda_line { - __u8 dtr; - __u8 rts; -}; - -/* IrDA interface configuration (data part must not exceed 16 bytes) */ -struct if_irda_req { - union { - char ifrn_name[IRNAMSIZ]; /* if name, e.g. "irda0" */ - } ifr_ifrn; - - /* Data part */ - union { - struct if_irda_line ifru_line; - struct if_irda_qos ifru_qos; - unsigned short ifru_flags; - unsigned int ifru_receiving; - unsigned int ifru_mode; - unsigned int ifru_dongle; - } ifr_ifru; -}; - -#define ifr_baudrate ifr_ifru.ifru_qos.baudrate -#define ifr_receiving ifr_ifru.ifru_receiving -#define ifr_dongle ifr_ifru.ifru_dongle -#define ifr_mode ifr_ifru.ifru_mode -#define ifr_dtr ifr_ifru.ifru_line.dtr -#define ifr_rts ifr_ifru.ifru_line.rts - - -/* IrDA netlink definitions */ -#define IRDA_NL_NAME "irda" -#define IRDA_NL_VERSION 1 - -enum irda_nl_commands { - IRDA_NL_CMD_UNSPEC, - IRDA_NL_CMD_SET_MODE, - IRDA_NL_CMD_GET_MODE, - - __IRDA_NL_CMD_AFTER_LAST -}; -#define IRDA_NL_CMD_MAX (__IRDA_NL_CMD_AFTER_LAST - 1) - -enum nl80211_attrs { - IRDA_NL_ATTR_UNSPEC, - IRDA_NL_ATTR_IFNAME, - IRDA_NL_ATTR_MODE, - - __IRDA_NL_ATTR_AFTER_LAST -}; -#define IRDA_NL_ATTR_MAX (__IRDA_NL_ATTR_AFTER_LAST - 1) - -/* IrDA modes */ -#define IRDA_MODE_PRIMARY 0x1 -#define IRDA_MODE_SECONDARY 0x2 -#define IRDA_MODE_MONITOR 0x4 - -#endif /* KERNEL_IRDA_H */ - - - - -- cgit v1.2.3 From 3ff547c06a7d75d72d37dae2c064fcf0672e56c0 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 14 Mar 2018 19:05:31 +0800 Subject: sctp: add support for SCTP AUTH Information for sendmsg This patch is to add support for SCTP AUTH Information for sendmsg, as described in section 5.3.8 of RFC6458. With this option, you can provide shared key identifier used for sending the user message. It's also a necessary send info for sctp_sendv. Note that it reuses sinfo->sinfo_tsn to indicate if this option is set and sinfo->sinfo_ssn to save the shkey ID which can be 0. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 1 + include/uapi/linux/sctp.h | 14 +++++++++++++- net/sctp/chunk.c | 11 ++++++++++- net/sctp/socket.c | 23 +++++++++++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 49ad67bbdbb5..012fb3e2f4cf 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -2118,6 +2118,7 @@ struct sctp_cmsgs { struct sctp_sndrcvinfo *srinfo; struct sctp_sndinfo *sinfo; struct sctp_prinfo *prinfo; + struct sctp_authinfo *authinfo; struct msghdr *addrs_msg; }; diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index e94b6d297ad9..47e781ebde05 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -273,6 +273,18 @@ struct sctp_prinfo { __u32 pr_value; }; +/* 5.3.8 SCTP AUTH Information Structure (SCTP_AUTHINFO) + * + * This cmsghdr structure specifies SCTP options for sendmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_AUTHINFO struct sctp_authinfo + */ +struct sctp_authinfo { + __u16 auth_keynumber; +}; + /* * sinfo_flags: 16 bits (unsigned integer) * @@ -310,7 +322,7 @@ typedef enum sctp_cmsg_type { #define SCTP_NXTINFO SCTP_NXTINFO SCTP_PRINFO, /* 5.3.7 SCTP PR-SCTP Information Structure */ #define SCTP_PRINFO SCTP_PRINFO - SCTP_AUTHINFO, /* 5.3.8 SCTP AUTH Information Structure (RESERVED) */ + SCTP_AUTHINFO, /* 5.3.8 SCTP AUTH Information Structure */ #define SCTP_AUTHINFO SCTP_AUTHINFO SCTP_DSTADDRV4, /* 5.3.9 SCTP Destination IPv4 Address Structure */ #define SCTP_DSTADDRV4 SCTP_DSTADDRV4 diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 9f28a9aae976..f889a84f264d 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -206,7 +206,16 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, max_data -= SCTP_PAD4(sizeof(struct sctp_auth_chunk) + hmac_desc->hmac_len); - shkey = asoc->shkey; + if (sinfo->sinfo_tsn && + sinfo->sinfo_ssn != asoc->active_key_id) { + shkey = sctp_auth_get_shkey(asoc, sinfo->sinfo_ssn); + if (!shkey) { + err = -EINVAL; + goto errout; + } + } else { + shkey = asoc->shkey; + } } /* Check what's our max considering the above */ diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 003a4ad89c01..9ffdecbc3531 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1987,6 +1987,14 @@ static void sctp_sendmsg_update_sinfo(struct sctp_association *asoc, if (!cmsgs->srinfo && !cmsgs->prinfo) sinfo->sinfo_timetolive = asoc->default_timetolive; + + if (cmsgs->authinfo) { + /* Reuse sinfo_tsn to indicate that authinfo was set and + * sinfo_ssn to save the keyid on tx path. + */ + sinfo->sinfo_tsn = 1; + sinfo->sinfo_ssn = cmsgs->authinfo->auth_keynumber; + } } static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) @@ -7874,6 +7882,21 @@ static int sctp_msghdr_parse(const struct msghdr *msg, struct sctp_cmsgs *cmsgs) if (cmsgs->prinfo->pr_policy == SCTP_PR_SCTP_NONE) cmsgs->prinfo->pr_value = 0; break; + case SCTP_AUTHINFO: + /* SCTP Socket API Extension + * 5.3.8 SCTP AUTH Information Structure (SCTP_AUTHINFO) + * + * This cmsghdr structure specifies SCTP options for sendmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ --------------------- + * IPPROTO_SCTP SCTP_AUTHINFO struct sctp_authinfo + */ + if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_authinfo))) + return -EINVAL; + + cmsgs->authinfo = CMSG_DATA(cmsg); + break; case SCTP_DSTADDRV4: case SCTP_DSTADDRV6: /* SCTP Socket API Extension -- cgit v1.2.3 From 601590ec155aadf5daa17a6f63a06d1bba5b5ce9 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 14 Mar 2018 19:05:32 +0800 Subject: sctp: add sockopt SCTP_AUTH_DEACTIVATE_KEY This patch is to add sockopt SCTP_AUTH_DEACTIVATE_KEY, as described in section 8.3.4 of RFC6458. This set option indicates that the application will no longer send user messages using the indicated key identifier. Note that RFC requires that only deactivated keys that are no longer used by an association can be deleted, but for the backward compatibility, it is not to check deactivated when deleting or replacing one sh_key. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/auth.h | 12 ++++++------ include/uapi/linux/sctp.h | 1 + net/sctp/auth.c | 46 +++++++++++++++++++++++++++++++++++++++++++--- net/sctp/socket.c | 31 +++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 9 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/sctp/auth.h b/include/net/sctp/auth.h index 017c1aa3a2c9..687e7f80037d 100644 --- a/include/net/sctp/auth.h +++ b/include/net/sctp/auth.h @@ -65,6 +65,7 @@ struct sctp_shared_key { struct sctp_auth_bytes *key; refcount_t refcnt; __u16 key_id; + __u8 deactivated; }; #define key_for_each(__key, __list_head) \ @@ -113,14 +114,13 @@ void sctp_auth_shkey_hold(struct sctp_shared_key *sh_key); int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id); int sctp_auth_ep_set_hmacs(struct sctp_endpoint *ep, struct sctp_hmacalgo *hmacs); -int sctp_auth_set_key(struct sctp_endpoint *ep, - struct sctp_association *asoc, +int sctp_auth_set_key(struct sctp_endpoint *ep, struct sctp_association *asoc, struct sctp_authkey *auth_key); int sctp_auth_set_active_key(struct sctp_endpoint *ep, - struct sctp_association *asoc, - __u16 key_id); + struct sctp_association *asoc, __u16 key_id); int sctp_auth_del_key_id(struct sctp_endpoint *ep, - struct sctp_association *asoc, - __u16 key_id); + struct sctp_association *asoc, __u16 key_id); +int sctp_auth_deact_key_id(struct sctp_endpoint *ep, + struct sctp_association *asoc, __u16 key_id); #endif diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 47e781ebde05..08fc313829f4 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -99,6 +99,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_RECVRCVINFO 32 #define SCTP_RECVNXTINFO 33 #define SCTP_DEFAULT_SNDINFO 34 +#define SCTP_AUTH_DEACTIVATE_KEY 35 /* Internal Socket Options. Some of the sctp library functions are * implemented using these socket options. diff --git a/net/sctp/auth.c b/net/sctp/auth.c index e5214fd7f650..a073123fc485 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -449,8 +449,11 @@ struct sctp_shared_key *sctp_auth_get_shkey( /* First search associations set of endpoint pair shared keys */ key_for_each(key, &asoc->endpoint_shared_keys) { - if (key->key_id == key_id) - return key; + if (key->key_id == key_id) { + if (!key->deactivated) + return key; + break; + } } return NULL; @@ -905,7 +908,7 @@ int sctp_auth_set_active_key(struct sctp_endpoint *ep, } } - if (!found) + if (!found || key->deactivated) return -EINVAL; if (asoc) { @@ -956,3 +959,40 @@ int sctp_auth_del_key_id(struct sctp_endpoint *ep, return 0; } + +int sctp_auth_deact_key_id(struct sctp_endpoint *ep, + struct sctp_association *asoc, __u16 key_id) +{ + struct sctp_shared_key *key; + struct list_head *sh_keys; + int found = 0; + + /* The key identifier MUST NOT be the current active key + * The key identifier MUST correst to an existing key + */ + if (asoc) { + if (asoc->active_key_id == key_id) + return -EINVAL; + + sh_keys = &asoc->endpoint_shared_keys; + } else { + if (ep->active_key_id == key_id) + return -EINVAL; + + sh_keys = &ep->endpoint_shared_keys; + } + + key_for_each(key, sh_keys) { + if (key->key_id == key_id) { + found = 1; + break; + } + } + + if (!found) + return -EINVAL; + + key->deactivated = 1; + + return 0; +} diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 9ffdecbc3531..65cc354c520f 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3646,6 +3646,33 @@ static int sctp_setsockopt_del_key(struct sock *sk, } +/* + * 8.3.4 Deactivate a Shared Key (SCTP_AUTH_DEACTIVATE_KEY) + * + * This set option will deactivate a shared secret key. + */ +static int sctp_setsockopt_deactivate_key(struct sock *sk, char __user *optval, + unsigned int optlen) +{ + struct sctp_endpoint *ep = sctp_sk(sk)->ep; + struct sctp_authkeyid val; + struct sctp_association *asoc; + + if (!ep->auth_enable) + return -EACCES; + + if (optlen != sizeof(struct sctp_authkeyid)) + return -EINVAL; + if (copy_from_user(&val, optval, optlen)) + return -EFAULT; + + asoc = sctp_id2assoc(sk, val.scact_assoc_id); + if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP)) + return -EINVAL; + + return sctp_auth_deact_key_id(ep, asoc, val.scact_keynumber); +} + /* * 8.1.23 SCTP_AUTO_ASCONF * @@ -4238,6 +4265,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, case SCTP_AUTH_DELETE_KEY: retval = sctp_setsockopt_del_key(sk, optval, optlen); break; + case SCTP_AUTH_DEACTIVATE_KEY: + retval = sctp_setsockopt_deactivate_key(sk, optval, optlen); + break; case SCTP_AUTO_ASCONF: retval = sctp_setsockopt_auto_asconf(sk, optval, optlen); break; @@ -7212,6 +7242,7 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, case SCTP_AUTH_KEY: case SCTP_AUTH_CHUNK: case SCTP_AUTH_DELETE_KEY: + case SCTP_AUTH_DEACTIVATE_KEY: retval = -EOPNOTSUPP; break; case SCTP_HMAC_IDENT: -- cgit v1.2.3 From ec2e506c680deaa8e1a087986db6d73ba63a04be Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 14 Mar 2018 19:05:33 +0800 Subject: sctp: add SCTP_AUTH_FREE_KEY type for AUTHENTICATION_EVENT This patch is to add SCTP_AUTH_FREE_KEY type for AUTHENTICATION_EVENT, as described in section 6.1.8 of RFC6458. SCTP_AUTH_FREE_KEY: This report indicates that the SCTP implementation will no longer use the key identifier specified in auth_keynumber. After deactivating a key, it would never be used again, which means it's refcnt can't be held/increased by new chunks. But there may be some chunks in out queue still using it. So only when refcnt is 1, which means no chunk in outqueue is using/holding this key either, this EVENT would be sent. When users receive this notification, they could do DEL_KEY sockopt to remove this shkey, and also tell the peer that this key won't be used in any chunk thoroughly from now on, then the peer can remove it as well safely. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/uapi/linux/sctp.h | 6 +++++- net/sctp/auth.c | 14 ++++++++++++++ net/sctp/sm_make_chunk.c | 20 +++++++++++++++++++- net/sctp/sm_statefuns.c | 2 +- net/sctp/socket.c | 19 ++++++++++++++++++- 5 files changed, 57 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 08fc313829f4..18ebbfeee4af 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -518,7 +518,11 @@ struct sctp_authkey_event { sctp_assoc_t auth_assoc_id; }; -enum { SCTP_AUTH_NEWKEY = 0, }; +enum { + SCTP_AUTH_NEW_KEY, +#define SCTP_AUTH_NEWKEY SCTP_AUTH_NEW_KEY /* compatible with before */ + SCTP_AUTH_FREE_KEY, +}; /* * 6.1.9. SCTP_SENDER_DRY_EVENT diff --git a/net/sctp/auth.c b/net/sctp/auth.c index a073123fc485..e64630cd3331 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -992,6 +992,20 @@ int sctp_auth_deact_key_id(struct sctp_endpoint *ep, if (!found) return -EINVAL; + /* refcnt == 1 and !list_empty mean it's not being used anywhere + * and deactivated will be set, so it's time to notify userland + * that this shkey can be freed. + */ + if (asoc && !list_empty(&key->key_list) && + refcount_read(&key->refcnt) == 1) { + struct sctp_ulpevent *ev; + + ev = sctp_ulpevent_make_authkey(asoc, key->key_id, + SCTP_AUTH_FREE_KEY, GFP_KERNEL); + if (ev) + asoc->stream.si->enqueue_event(&asoc->ulpq, ev); + } + key->deactivated = 1; return 0; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 10f071cdf188..cc20bc39ee7c 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -89,8 +89,26 @@ static void sctp_control_release_owner(struct sk_buff *skb) { struct sctp_chunk *chunk = skb_shinfo(skb)->destructor_arg; - if (chunk->shkey) + if (chunk->shkey) { + struct sctp_shared_key *shkey = chunk->shkey; + struct sctp_association *asoc = chunk->asoc; + + /* refcnt == 2 and !list_empty mean after this release, it's + * not being used anywhere, and it's time to notify userland + * that this shkey can be freed if it's been deactivated. + */ + if (shkey->deactivated && !list_empty(&shkey->key_list) && + refcount_read(&shkey->refcnt) == 2) { + struct sctp_ulpevent *ev; + + ev = sctp_ulpevent_make_authkey(asoc, shkey->key_id, + SCTP_AUTH_FREE_KEY, + GFP_KERNEL); + if (ev) + asoc->stream.si->enqueue_event(&asoc->ulpq, ev); + } sctp_auth_shkey_release(chunk->shkey); + } } static void sctp_control_set_owner_w(struct sctp_chunk *chunk) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 792e0e2be320..1e41dee70b51 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -4246,7 +4246,7 @@ enum sctp_disposition sctp_sf_eat_auth(struct net *net, struct sctp_ulpevent *ev; ev = sctp_ulpevent_make_authkey(asoc, ntohs(auth_hdr->shkey_id), - SCTP_AUTH_NEWKEY, GFP_ATOMIC); + SCTP_AUTH_NEW_KEY, GFP_ATOMIC); if (!ev) return -ENOMEM; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 65cc354c520f..aeecdd620c45 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -8166,8 +8166,25 @@ static void sctp_wfree(struct sk_buff *skb) sk->sk_wmem_queued -= skb->truesize; sk_mem_uncharge(sk, skb->truesize); - if (chunk->shkey) + if (chunk->shkey) { + struct sctp_shared_key *shkey = chunk->shkey; + + /* refcnt == 2 and !list_empty mean after this release, it's + * not being used anywhere, and it's time to notify userland + * that this shkey can be freed if it's been deactivated. + */ + if (shkey->deactivated && !list_empty(&shkey->key_list) && + refcount_read(&shkey->refcnt) == 2) { + struct sctp_ulpevent *ev; + + ev = sctp_ulpevent_make_authkey(asoc, shkey->key_id, + SCTP_AUTH_FREE_KEY, + GFP_KERNEL); + if (ev) + asoc->stream.si->enqueue_event(&asoc->ulpq, ev); + } sctp_auth_shkey_release(chunk->shkey); + } sock_wfree(skb); sctp_wake_up_waiters(sk, asoc); -- cgit v1.2.3 From 30f6ebf65bc46161c5aaff1db2e6e7c76aa4a06b Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 14 Mar 2018 19:05:34 +0800 Subject: sctp: add SCTP_AUTH_NO_AUTH type for AUTHENTICATION_EVENT This patch is to add SCTP_AUTH_NO_AUTH type for AUTHENTICATION_EVENT, as described in section 6.1.8 of RFC6458. SCTP_AUTH_NO_AUTH: This report indicates that the peer does not support SCTP authentication as defined in [RFC4895]. Note that the implementation is quite similar as that of SCTP_ADAPTATION_INDICATION. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/command.h | 1 + include/uapi/linux/sctp.h | 1 + net/sctp/sm_sideeffect.c | 13 +++++++++++++ net/sctp/sm_statefuns.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 56 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index b55c6a48a206..6640f84fe536 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -100,6 +100,7 @@ enum sctp_verb { SCTP_CMD_SET_SK_ERR, /* Set sk_err */ SCTP_CMD_ASSOC_CHANGE, /* generate and send assoc_change event */ SCTP_CMD_ADAPTATION_IND, /* generate and send adaptation event */ + SCTP_CMD_PEER_NO_AUTH, /* generate and send authentication event */ SCTP_CMD_ASSOC_SHKEY, /* generate the association shared keys */ SCTP_CMD_T1_RETRAN, /* Mark for retransmission after T1 timeout */ SCTP_CMD_UPDATE_INITTAG, /* Update peer inittag */ diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 18ebbfeee4af..afd4346386e0 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -522,6 +522,7 @@ enum { SCTP_AUTH_NEW_KEY, #define SCTP_AUTH_NEWKEY SCTP_AUTH_NEW_KEY /* compatible with before */ SCTP_AUTH_FREE_KEY, + SCTP_AUTH_NO_AUTH, }; /* diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index b71e7fb0a20a..298112ca8c06 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1049,6 +1049,16 @@ static void sctp_cmd_assoc_change(struct sctp_cmd_seq *commands, asoc->stream.si->enqueue_event(&asoc->ulpq, ev); } +static void sctp_cmd_peer_no_auth(struct sctp_cmd_seq *commands, + struct sctp_association *asoc) +{ + struct sctp_ulpevent *ev; + + ev = sctp_ulpevent_make_authkey(asoc, 0, SCTP_AUTH_NO_AUTH, GFP_ATOMIC); + if (ev) + asoc->stream.si->enqueue_event(&asoc->ulpq, ev); +} + /* Helper function to generate an adaptation indication event */ static void sctp_cmd_adaptation_ind(struct sctp_cmd_seq *commands, struct sctp_association *asoc) @@ -1755,6 +1765,9 @@ static int sctp_cmd_interpreter(enum sctp_event event_type, case SCTP_CMD_ADAPTATION_IND: sctp_cmd_adaptation_ind(commands, asoc); break; + case SCTP_CMD_PEER_NO_AUTH: + sctp_cmd_peer_no_auth(commands, asoc); + break; case SCTP_CMD_ASSOC_SHKEY: error = sctp_auth_asoc_init_active_key(asoc, diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 1e41dee70b51..cc56a67dbb4d 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -659,7 +659,7 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, void *arg, struct sctp_cmd_seq *commands) { - struct sctp_ulpevent *ev, *ai_ev = NULL; + struct sctp_ulpevent *ev, *ai_ev = NULL, *auth_ev = NULL; struct sctp_association *new_asoc; struct sctp_init_chunk *peer_init; struct sctp_chunk *chunk = arg; @@ -820,6 +820,14 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, goto nomem_aiev; } + if (!new_asoc->peer.auth_capable) { + auth_ev = sctp_ulpevent_make_authkey(new_asoc, 0, + SCTP_AUTH_NO_AUTH, + GFP_ATOMIC); + if (!auth_ev) + goto nomem_authev; + } + /* Add all the state machine commands now since we've created * everything. This way we don't introduce memory corruptions * during side-effect processing and correclty count established @@ -847,8 +855,14 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ai_ev)); + if (auth_ev) + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, + SCTP_ULPEVENT(auth_ev)); + return SCTP_DISPOSITION_CONSUME; +nomem_authev: + sctp_ulpevent_free(ai_ev); nomem_aiev: sctp_ulpevent_free(ev); nomem_ev: @@ -953,6 +967,15 @@ enum sctp_disposition sctp_sf_do_5_1E_ca(struct net *net, SCTP_ULPEVENT(ev)); } + if (!asoc->peer.auth_capable) { + ev = sctp_ulpevent_make_authkey(asoc, 0, SCTP_AUTH_NO_AUTH, + GFP_ATOMIC); + if (!ev) + goto nomem; + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, + SCTP_ULPEVENT(ev)); + } + return SCTP_DISPOSITION_CONSUME; nomem: return SCTP_DISPOSITION_NOMEM; @@ -1908,6 +1931,9 @@ static enum sctp_disposition sctp_sf_do_dupcook_b( if (asoc->peer.adaptation_ind) sctp_add_cmd_sf(commands, SCTP_CMD_ADAPTATION_IND, SCTP_NULL()); + if (!asoc->peer.auth_capable) + sctp_add_cmd_sf(commands, SCTP_CMD_PEER_NO_AUTH, SCTP_NULL()); + return SCTP_DISPOSITION_CONSUME; nomem: @@ -1954,7 +1980,7 @@ static enum sctp_disposition sctp_sf_do_dupcook_d( struct sctp_cmd_seq *commands, struct sctp_association *new_asoc) { - struct sctp_ulpevent *ev = NULL, *ai_ev = NULL; + struct sctp_ulpevent *ev = NULL, *ai_ev = NULL, *auth_ev = NULL; struct sctp_chunk *repl; /* Clarification from Implementor's Guide: @@ -2001,6 +2027,14 @@ static enum sctp_disposition sctp_sf_do_dupcook_d( goto nomem; } + + if (!asoc->peer.auth_capable) { + auth_ev = sctp_ulpevent_make_authkey(asoc, 0, + SCTP_AUTH_NO_AUTH, + GFP_ATOMIC); + if (!auth_ev) + goto nomem; + } } repl = sctp_make_cookie_ack(new_asoc, chunk); @@ -2015,10 +2049,15 @@ static enum sctp_disposition sctp_sf_do_dupcook_d( if (ai_ev) sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ai_ev)); + if (auth_ev) + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, + SCTP_ULPEVENT(auth_ev)); return SCTP_DISPOSITION_CONSUME; nomem: + if (auth_ev) + sctp_ulpevent_free(auth_ev); if (ai_ev) sctp_ulpevent_free(ai_ev); if (ev) -- cgit v1.2.3 From 615755a77b2461ed78dfafb8a6649456201949c7 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Wed, 14 Mar 2018 10:23:21 -0700 Subject: bpf: extend stackmap to save binary_build_id+offset instead of address Currently, bpf stackmap store address for each entry in the call trace. To map these addresses to user space files, it is necessary to maintain the mapping from these virtual address to symbols in the binary. Usually, the user space profiler (such as perf) has to scan /proc/pid/maps at the beginning of profiling, and monitor mmap2() calls afterwards. Given the cost of maintaining the address map, this solution is not practical for system wide profiling that is always on. This patch tries to solve this problem with a variation of stackmap. This variation is enabled by flag BPF_F_STACK_BUILD_ID. Instead of storing addresses, the variation stores ELF file build_id + offset. Build ID is a 20-byte unique identifier for ELF files. The following command shows the Build ID of /bin/bash: [user@]$ readelf -n /bin/bash ... Build ID: XXXXXXXXXX ... With BPF_F_STACK_BUILD_ID, bpf_get_stackid() tries to parse Build ID for each entry in the call trace, and translate it into the following struct: struct bpf_stack_build_id_offset { __s32 status; unsigned char build_id[BPF_BUILD_ID_SIZE]; union { __u64 offset; __u64 ip; }; }; The search of build_id is limited to the first page of the file, and this page should be in page cache. Otherwise, we fallback to store ip for this entry (ip field in struct bpf_stack_build_id_offset). This requires the build_id to be stored in the first page. A quick survey of binary and dynamic library files in a few different systems shows that almost all binary and dynamic library files have build_id in the first page. Build_id is only meaningful for user stack. If a kernel stack is added to a stackmap with BPF_F_STACK_BUILD_ID, it will automatically fallback to only store ip (status == BPF_STACK_BUILD_ID_IP). Similarly, if build_id lookup failed for some reason, it will also fallback to store ip. User space can access struct bpf_stack_build_id_offset with bpf syscall BPF_MAP_LOOKUP_ELEM. It is necessary for user space to maintain mapping from build id to binary files. This mostly static mapping is much easier to maintain than per process address maps. Note: Stackmap with build_id only works in non-nmi context at this time. This is because we need to take mm->mmap_sem for find_vma(). If this changes, we would like to allow build_id lookup in nmi context. Signed-off-by: Song Liu Signed-off-by: Daniel Borkmann --- include/uapi/linux/bpf.h | 22 ++++ kernel/bpf/stackmap.c | 257 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 257 insertions(+), 22 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 2a66769e5875..1e15d1724d89 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -231,6 +231,28 @@ enum bpf_attach_type { #define BPF_F_RDONLY (1U << 3) #define BPF_F_WRONLY (1U << 4) +/* Flag for stack_map, store build_id+offset instead of pointer */ +#define BPF_F_STACK_BUILD_ID (1U << 5) + +enum bpf_stack_build_id_status { + /* user space need an empty entry to identify end of a trace */ + BPF_STACK_BUILD_ID_EMPTY = 0, + /* with valid build_id and offset */ + BPF_STACK_BUILD_ID_VALID = 1, + /* couldn't get build_id, fallback to ip */ + BPF_STACK_BUILD_ID_IP = 2, +}; + +#define BPF_BUILD_ID_SIZE 20 +struct bpf_stack_build_id { + __s32 status; + unsigned char build_id[BPF_BUILD_ID_SIZE]; + union { + __u64 offset; + __u64 ip; + }; +}; + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index b0ecf43f5894..57eeb1234b67 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -9,16 +9,19 @@ #include #include #include +#include +#include #include "percpu_freelist.h" -#define STACK_CREATE_FLAG_MASK \ - (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) +#define STACK_CREATE_FLAG_MASK \ + (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY | \ + BPF_F_STACK_BUILD_ID) struct stack_map_bucket { struct pcpu_freelist_node fnode; u32 hash; u32 nr; - u64 ip[]; + u64 data[]; }; struct bpf_stack_map { @@ -29,6 +32,17 @@ struct bpf_stack_map { struct stack_map_bucket *buckets[]; }; +static inline bool stack_map_use_build_id(struct bpf_map *map) +{ + return (map->map_flags & BPF_F_STACK_BUILD_ID); +} + +static inline int stack_map_data_size(struct bpf_map *map) +{ + return stack_map_use_build_id(map) ? + sizeof(struct bpf_stack_build_id) : sizeof(u64); +} + static int prealloc_elems_and_freelist(struct bpf_stack_map *smap) { u32 elem_size = sizeof(struct stack_map_bucket) + smap->map.value_size; @@ -68,8 +82,16 @@ static struct bpf_map *stack_map_alloc(union bpf_attr *attr) /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 4 || - value_size < 8 || value_size % 8 || - value_size / 8 > sysctl_perf_event_max_stack) + value_size < 8 || value_size % 8) + return ERR_PTR(-EINVAL); + + BUILD_BUG_ON(sizeof(struct bpf_stack_build_id) % sizeof(u64)); + if (attr->map_flags & BPF_F_STACK_BUILD_ID) { + if (value_size % sizeof(struct bpf_stack_build_id) || + value_size / sizeof(struct bpf_stack_build_id) + > sysctl_perf_event_max_stack) + return ERR_PTR(-EINVAL); + } else if (value_size / 8 > sysctl_perf_event_max_stack) return ERR_PTR(-EINVAL); /* hash table size must be power of 2 */ @@ -114,13 +136,184 @@ free_smap: return ERR_PTR(err); } +#define BPF_BUILD_ID 3 +/* + * Parse build id from the note segment. This logic can be shared between + * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are + * identical. + */ +static inline int stack_map_parse_build_id(void *page_addr, + unsigned char *build_id, + void *note_start, + Elf32_Word note_size) +{ + Elf32_Word note_offs = 0, new_offs; + + /* check for overflow */ + if (note_start < page_addr || note_start + note_size < note_start) + return -EINVAL; + + /* only supports note that fits in the first page */ + if (note_start + note_size > page_addr + PAGE_SIZE) + return -EINVAL; + + while (note_offs + sizeof(Elf32_Nhdr) < note_size) { + Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs); + + if (nhdr->n_type == BPF_BUILD_ID && + nhdr->n_namesz == sizeof("GNU") && + nhdr->n_descsz == BPF_BUILD_ID_SIZE) { + memcpy(build_id, + note_start + note_offs + + ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), + BPF_BUILD_ID_SIZE); + return 0; + } + new_offs = note_offs + sizeof(Elf32_Nhdr) + + ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4); + if (new_offs <= note_offs) /* overflow */ + break; + note_offs = new_offs; + } + return -EINVAL; +} + +/* Parse build ID from 32-bit ELF */ +static int stack_map_get_build_id_32(void *page_addr, + unsigned char *build_id) +{ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr; + Elf32_Phdr *phdr; + int i; + + /* only supports phdr that fits in one page */ + if (ehdr->e_phnum > + (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr)) + return -EINVAL; + + phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr)); + + for (i = 0; i < ehdr->e_phnum; ++i) + if (phdr[i].p_type == PT_NOTE) + return stack_map_parse_build_id(page_addr, build_id, + page_addr + phdr[i].p_offset, + phdr[i].p_filesz); + return -EINVAL; +} + +/* Parse build ID from 64-bit ELF */ +static int stack_map_get_build_id_64(void *page_addr, + unsigned char *build_id) +{ + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr; + Elf64_Phdr *phdr; + int i; + + /* only supports phdr that fits in one page */ + if (ehdr->e_phnum > + (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr)) + return -EINVAL; + + phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr)); + + for (i = 0; i < ehdr->e_phnum; ++i) + if (phdr[i].p_type == PT_NOTE) + return stack_map_parse_build_id(page_addr, build_id, + page_addr + phdr[i].p_offset, + phdr[i].p_filesz); + return -EINVAL; +} + +/* Parse build ID of ELF file mapped to vma */ +static int stack_map_get_build_id(struct vm_area_struct *vma, + unsigned char *build_id) +{ + Elf32_Ehdr *ehdr; + struct page *page; + void *page_addr; + int ret; + + /* only works for page backed storage */ + if (!vma->vm_file) + return -EINVAL; + + page = find_get_page(vma->vm_file->f_mapping, 0); + if (!page) + return -EFAULT; /* page not mapped */ + + ret = -EINVAL; + page_addr = page_address(page); + ehdr = (Elf32_Ehdr *)page_addr; + + /* compare magic x7f "ELF" */ + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) + goto out; + + /* only support executable file and shared object file */ + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + goto out; + + if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) + ret = stack_map_get_build_id_32(page_addr, build_id); + else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) + ret = stack_map_get_build_id_64(page_addr, build_id); +out: + put_page(page); + return ret; +} + +static void stack_map_get_build_id_offset(struct bpf_map *map, + struct stack_map_bucket *bucket, + u64 *ips, u32 trace_nr, bool user) +{ + int i; + struct vm_area_struct *vma; + struct bpf_stack_build_id *id_offs; + + bucket->nr = trace_nr; + id_offs = (struct bpf_stack_build_id *)bucket->data; + + /* + * We cannot do up_read() in nmi context, so build_id lookup is + * only supported for non-nmi events. If at some point, it is + * possible to run find_vma() without taking the semaphore, we + * would like to allow build_id lookup in nmi context. + * + * Same fallback is used for kernel stack (!user) on a stackmap + * with build_id. + */ + if (!user || !current || !current->mm || in_nmi() || + down_read_trylock(¤t->mm->mmap_sem) == 0) { + /* cannot access current->mm, fall back to ips */ + for (i = 0; i < trace_nr; i++) { + id_offs[i].status = BPF_STACK_BUILD_ID_IP; + id_offs[i].ip = ips[i]; + } + return; + } + + for (i = 0; i < trace_nr; i++) { + vma = find_vma(current->mm, ips[i]); + if (!vma || stack_map_get_build_id(vma, id_offs[i].build_id)) { + /* per entry fall back to ips */ + id_offs[i].status = BPF_STACK_BUILD_ID_IP; + id_offs[i].ip = ips[i]; + continue; + } + id_offs[i].offset = (vma->vm_pgoff << PAGE_SHIFT) + ips[i] + - vma->vm_start; + id_offs[i].status = BPF_STACK_BUILD_ID_VALID; + } + up_read(¤t->mm->mmap_sem); +} + BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map, u64, flags) { struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); struct perf_callchain_entry *trace; struct stack_map_bucket *bucket, *new_bucket, *old_bucket; - u32 max_depth = map->value_size / 8; + u32 max_depth = map->value_size / stack_map_data_size(map); /* stack_map_alloc() checks that max_depth <= sysctl_perf_event_max_stack */ u32 init_nr = sysctl_perf_event_max_stack - max_depth; u32 skip = flags & BPF_F_SKIP_FIELD_MASK; @@ -128,6 +321,7 @@ BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map, bool user = flags & BPF_F_USER_STACK; bool kernel = !user; u64 *ips; + bool hash_matches; if (unlikely(flags & ~(BPF_F_SKIP_FIELD_MASK | BPF_F_USER_STACK | BPF_F_FAST_STACK_CMP | BPF_F_REUSE_STACKID))) @@ -156,24 +350,43 @@ BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map, id = hash & (smap->n_buckets - 1); bucket = READ_ONCE(smap->buckets[id]); - if (bucket && bucket->hash == hash) { - if (flags & BPF_F_FAST_STACK_CMP) + hash_matches = bucket && bucket->hash == hash; + /* fast cmp */ + if (hash_matches && flags & BPF_F_FAST_STACK_CMP) + return id; + + if (stack_map_use_build_id(map)) { + /* for build_id+offset, pop a bucket before slow cmp */ + new_bucket = (struct stack_map_bucket *) + pcpu_freelist_pop(&smap->freelist); + if (unlikely(!new_bucket)) + return -ENOMEM; + stack_map_get_build_id_offset(map, new_bucket, ips, + trace_nr, user); + trace_len = trace_nr * sizeof(struct bpf_stack_build_id); + if (hash_matches && bucket->nr == trace_nr && + memcmp(bucket->data, new_bucket->data, trace_len) == 0) { + pcpu_freelist_push(&smap->freelist, &new_bucket->fnode); return id; - if (bucket->nr == trace_nr && - memcmp(bucket->ip, ips, trace_len) == 0) + } + if (bucket && !(flags & BPF_F_REUSE_STACKID)) { + pcpu_freelist_push(&smap->freelist, &new_bucket->fnode); + return -EEXIST; + } + } else { + if (hash_matches && bucket->nr == trace_nr && + memcmp(bucket->data, ips, trace_len) == 0) return id; + if (bucket && !(flags & BPF_F_REUSE_STACKID)) + return -EEXIST; + + new_bucket = (struct stack_map_bucket *) + pcpu_freelist_pop(&smap->freelist); + if (unlikely(!new_bucket)) + return -ENOMEM; + memcpy(new_bucket->data, ips, trace_len); } - /* this call stack is not in the map, try to add it */ - if (bucket && !(flags & BPF_F_REUSE_STACKID)) - return -EEXIST; - - new_bucket = (struct stack_map_bucket *) - pcpu_freelist_pop(&smap->freelist); - if (unlikely(!new_bucket)) - return -ENOMEM; - - memcpy(new_bucket->ip, ips, trace_len); new_bucket->hash = hash; new_bucket->nr = trace_nr; @@ -212,8 +425,8 @@ int bpf_stackmap_copy(struct bpf_map *map, void *key, void *value) if (!bucket) return -ENOENT; - trace_len = bucket->nr * sizeof(u64); - memcpy(value, bucket->ip, trace_len); + trace_len = bucket->nr * stack_map_data_size(map); + memcpy(value, bucket->data, trace_len); memset(value + trace_len, 0, map->value_size - trace_len); old_bucket = xchg(&smap->buckets[id], bucket); -- cgit v1.2.3 From c26dd817d99bc50acf2667ee27c39414a7a6638e Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 22 Feb 2018 21:44:53 +0200 Subject: uapi: remove telephony headers ixjuser.h includes the telephony.h header. Other than that no kernel code uses any of these headers. The last user of the ixjuser.h header has been removed in commit 7326446c728 (Staging: remove telephony drivers), more than 5 years ago. Signed-off-by: Baruch Siach Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/ixjuser.h | 721 ----------------------------------------- include/uapi/linux/telephony.h | 263 --------------- 2 files changed, 984 deletions(-) delete mode 100644 include/uapi/linux/ixjuser.h delete mode 100644 include/uapi/linux/telephony.h (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/ixjuser.h b/include/uapi/linux/ixjuser.h deleted file mode 100644 index ba245007cfe7..000000000000 --- a/include/uapi/linux/ixjuser.h +++ /dev/null @@ -1,721 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ -#ifndef __LINUX_IXJUSER_H -#define __LINUX_IXJUSER_H - -/****************************************************************************** - * - * ixjuser.h - * - * Device Driver for Quicknet Technologies, Inc.'s Telephony cards - * including the Internet PhoneJACK, Internet PhoneJACK Lite, - * Internet PhoneJACK PCI, Internet LineJACK, Internet PhoneCARD and - * SmartCABLE - * - * (c) Copyright 1999-2001 Quicknet Technologies, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Author: Ed Okerson, - * - * Contributors: Greg Herlein, - * David W. Erhart, - * John Sellers, - * Mike Preston, - * - * More information about the hardware related to this driver can be found - * at our website: http://www.quicknet.net - * - * Fixes: - * - * IN NO EVENT SHALL QUICKNET TECHNOLOGIES, INC. BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT - * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF QUICKNET - * TECHNOLOGIES, INC.HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * QUICKNET TECHNOLOGIES, INC. SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND QUICKNET TECHNOLOGIES, INC. HAS NO OBLIGATION - * TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - *****************************************************************************/ - -#include - - -/****************************************************************************** -* -* IOCTL's used for the Quicknet Telephony Cards -* -* If you use the IXJCTL_TESTRAM command, the card must be power cycled to -* reset the SRAM values before further use. -* -******************************************************************************/ - -#define IXJCTL_DSP_RESET _IO ('q', 0xC0) - -#define IXJCTL_RING PHONE_RING -#define IXJCTL_HOOKSTATE PHONE_HOOKSTATE -#define IXJCTL_MAXRINGS PHONE_MAXRINGS -#define IXJCTL_RING_CADENCE PHONE_RING_CADENCE -#define IXJCTL_RING_START PHONE_RING_START -#define IXJCTL_RING_STOP PHONE_RING_STOP - -#define IXJCTL_CARDTYPE _IOR ('q', 0xC1, int) -#define IXJCTL_SERIAL _IOR ('q', 0xC2, int) -#define IXJCTL_DSP_TYPE _IOR ('q', 0xC3, int) -#define IXJCTL_DSP_VERSION _IOR ('q', 0xC4, int) -#define IXJCTL_VERSION _IOR ('q', 0xDA, char *) -#define IXJCTL_DSP_IDLE _IO ('q', 0xC5) -#define IXJCTL_TESTRAM _IO ('q', 0xC6) - -/****************************************************************************** -* -* This group of IOCTLs deal with the record settings of the DSP -* -* The IXJCTL_REC_DEPTH command sets the internal buffer depth of the DSP. -* Setting a lower depth reduces latency, but increases the demand of the -* application to service the driver without frame loss. The DSP has 480 -* bytes of physical buffer memory for the record channel so the true -* maximum limit is determined by how many frames will fit in the buffer. -* -* 1 uncompressed (480 byte) 16-bit linear frame. -* 2 uncompressed (240 byte) 8-bit A-law/mu-law frames. -* 15 TrueSpeech 8.5 frames. -* 20 TrueSpeech 6.3,5.3,4.8 or 4.1 frames. -* -* The default in the driver is currently set to 2 frames. -* -* The IXJCTL_REC_VOLUME and IXJCTL_PLAY_VOLUME commands both use a Q8 -* number as a parameter, 0x100 scales the signal by 1.0, 0x200 scales the -* signal by 2.0, 0x80 scales the signal by 0.5. No protection is given -* against over-scaling, if the multiplication factor times the input -* signal exceeds 16 bits, overflow distortion will occur. The default -* setting is 0x100 (1.0). -* -* The IXJCTL_REC_LEVEL returns the average signal level (not r.m.s.) on -* the most recently recorded frame as a 16 bit value. -******************************************************************************/ - -#define IXJCTL_REC_CODEC PHONE_REC_CODEC -#define IXJCTL_REC_START PHONE_REC_START -#define IXJCTL_REC_STOP PHONE_REC_STOP -#define IXJCTL_REC_DEPTH PHONE_REC_DEPTH -#define IXJCTL_FRAME PHONE_FRAME -#define IXJCTL_REC_VOLUME PHONE_REC_VOLUME -#define IXJCTL_REC_LEVEL PHONE_REC_LEVEL - -typedef enum { - f300_640 = 4, f300_500, f1100, f350, f400, f480, f440, f620, f20_50, - f133_200, f300, f300_420, f330, f300_425, f330_440, f340, f350_400, - f350_440, f350_450, f360, f380_420, f392, f400_425, f400_440, f400_450, - f420, f425, f425_450, f425_475, f435, f440_450, f440_480, f445, f450, - f452, f475, f480_620, f494, f500, f520, f523, f525, f540_660, f587, - f590, f600, f660, f700, f740, f750, f750_1450, f770, f800, f816, f850, - f857_1645, f900, f900_1300, f935_1215, f941_1477, f942, f950, f950_1400, - f975, f1000, f1020, f1050, f1100_1750, f1140, f1200, f1209, f1330, f1336, - lf1366, f1380, f1400, f1477, f1600, f1633_1638, f1800, f1860 -} IXJ_FILTER_FREQ; - -typedef struct { - unsigned int filter; - IXJ_FILTER_FREQ freq; - char enable; -} IXJ_FILTER; - -typedef struct { - char enable; - char en_filter; - unsigned int filter; - unsigned int on1; - unsigned int off1; - unsigned int on2; - unsigned int off2; - unsigned int on3; - unsigned int off3; -} IXJ_FILTER_CADENCE; - -#define IXJCTL_SET_FILTER _IOW ('q', 0xC7, IXJ_FILTER *) -#define IXJCTL_SET_FILTER_RAW _IOW ('q', 0xDD, IXJ_FILTER_RAW *) -#define IXJCTL_GET_FILTER_HIST _IOW ('q', 0xC8, int) -#define IXJCTL_FILTER_CADENCE _IOW ('q', 0xD6, IXJ_FILTER_CADENCE *) -#define IXJCTL_PLAY_CID _IO ('q', 0xD7) -/****************************************************************************** -* -* This IOCTL allows you to reassign values in the tone index table. The -* tone table has 32 entries (0 - 31), but the driver only allows entries -* 13 - 27 to be modified, entry 0 is reserved for silence and 1 - 12 are -* the standard DTMF digits and 28 - 31 are the DTMF tones for A, B, C & D. -* The positions used internally for Call Progress Tones are as follows: -* Dial Tone - 25 -* Ring Back - 26 -* Busy Signal - 27 -* -* The freq values are calculated as: -* freq = cos(2 * PI * frequency / 8000) -* -* The most commonly needed values are already calculated and listed in the -* enum IXJ_TONE_FREQ. Each tone index can have two frequencies with -* different gains, if you are only using a single frequency set the unused -* one to 0. -* -* The gain values range from 0 to 15 indicating +6dB to -24dB in 2dB -* increments. -* -******************************************************************************/ - -typedef enum { - hz20 = 0x7ffa, - hz50 = 0x7fe5, - hz133 = 0x7f4c, - hz200 = 0x7e6b, - hz261 = 0x7d50, /* .63 C1 */ - hz277 = 0x7cfa, /* .18 CS1 */ - hz293 = 0x7c9f, /* .66 D1 */ - hz300 = 0x7c75, - hz311 = 0x7c32, /* .13 DS1 */ - hz329 = 0x7bbf, /* .63 E1 */ - hz330 = 0x7bb8, - hz340 = 0x7b75, - hz349 = 0x7b37, /* .23 F1 */ - hz350 = 0x7b30, - hz360 = 0x7ae9, - hz369 = 0x7aa8, /* .99 FS1 */ - hz380 = 0x7a56, - hz392 = 0x79fa, /* .00 G1 */ - hz400 = 0x79bb, - hz415 = 0x7941, /* .30 GS1 */ - hz420 = 0x7918, - hz425 = 0x78ee, - hz435 = 0x7899, - hz440 = 0x786d, /* .00 A1 */ - hz445 = 0x7842, - hz450 = 0x7815, - hz452 = 0x7803, - hz466 = 0x7784, /* .16 AS1 */ - hz475 = 0x7731, - hz480 = 0x7701, - hz493 = 0x7685, /* .88 B1 */ - hz494 = 0x767b, - hz500 = 0x7640, - hz520 = 0x7578, - hz523 = 0x7559, /* .25 C2 */ - hz525 = 0x7544, - hz540 = 0x74a7, - hz554 = 0x7411, /* .37 CS2 */ - hz587 = 0x72a1, /* .33 D2 */ - hz590 = 0x727f, - hz600 = 0x720b, - hz620 = 0x711e, - hz622 = 0x7106, /* .25 DS2 */ - hz659 = 0x6f3b, /* .26 E2 */ - hz660 = 0x6f2e, - hz698 = 0x6d3d, /* .46 F2 */ - hz700 = 0x6d22, - hz739 = 0x6b09, /* .99 FS2 */ - hz740 = 0x6afa, - hz750 = 0x6a6c, - hz770 = 0x694b, - hz783 = 0x688b, /* .99 G2 */ - hz800 = 0x678d, - hz816 = 0x6698, - hz830 = 0x65bf, /* .61 GS2 */ - hz850 = 0x6484, - hz857 = 0x6414, - hz880 = 0x629f, /* .00 A2 */ - hz900 = 0x6154, - hz932 = 0x5f35, /* .33 AS2 */ - hz935 = 0x5f01, - hz941 = 0x5e9a, - hz942 = 0x5e88, - hz950 = 0x5dfd, - hz975 = 0x5c44, - hz1000 = 0x5a81, - hz1020 = 0x5912, - hz1050 = 0x56e2, - hz1100 = 0x5320, - hz1140 = 0x5007, - hz1200 = 0x4b3b, - hz1209 = 0x4a80, - hz1215 = 0x4a02, - hz1250 = 0x471c, - hz1300 = 0x42e0, - hz1330 = 0x4049, - hz1336 = 0x3fc4, - hz1366 = 0x3d22, - hz1380 = 0x3be4, - hz1400 = 0x3a1b, - hz1450 = 0x3596, - hz1477 = 0x331c, - hz1500 = 0x30fb, - hz1600 = 0x278d, - hz1633 = 0x2462, - hz1638 = 0x23e7, - hz1645 = 0x233a, - hz1750 = 0x18f8, - hz1800 = 0x1405, - hz1860 = 0xe0b, - hz2100 = 0xf5f6, - hz2130 = 0xf2f5, - hz2450 = 0xd3b3, - hz2750 = 0xb8e4 -} IXJ_FREQ; - -typedef enum { - C1 = hz261, - CS1 = hz277, - D1 = hz293, - DS1 = hz311, - E1 = hz329, - F1 = hz349, - FS1 = hz369, - G1 = hz392, - GS1 = hz415, - A1 = hz440, - AS1 = hz466, - B1 = hz493, - C2 = hz523, - CS2 = hz554, - D2 = hz587, - DS2 = hz622, - E2 = hz659, - F2 = hz698, - FS2 = hz739, - G2 = hz783, - GS2 = hz830, - A2 = hz880, - AS2 = hz932, -} IXJ_NOTE; - -typedef struct { - int tone_index; - int freq0; - int gain0; - int freq1; - int gain1; -} IXJ_TONE; - -#define IXJCTL_INIT_TONE _IOW ('q', 0xC9, IXJ_TONE *) - -/****************************************************************************** -* -* The IXJCTL_TONE_CADENCE ioctl defines tone sequences used for various -* Call Progress Tones (CPT). This is accomplished by setting up an array of -* IXJ_CADENCE_ELEMENT structures that sequentially define the states of -* the tone sequence. The tone_on_time and tone_off time are in -* 250 microsecond intervals. A pointer to this array is passed to the -* driver as the ce element of an IXJ_CADENCE structure. The elements_used -* must be set to the number of IXJ_CADENCE_ELEMENTS in the array. The -* termination variable defines what to do at the end of a cadence, the -* options are to play the cadence once and stop, to repeat the last -* element of the cadence indefinitely, or to repeat the entire cadence -* indefinitely. The ce variable is a pointer to the array of IXJ_TONE -* structures. If the freq0 variable is non-zero, the tone table contents -* for the tone_index are updated to the frequencies and gains defined. It -* should be noted that DTMF tones cannot be reassigned, so if DTMF tone -* table indexes are used in a cadence the frequency and gain variables will -* be ignored. -* -* If the array elements contain frequency parameters the driver will -* initialize the needed tone table elements and begin playing the tone, -* there is no preset limit on the number of elements in the cadence. If -* there is more than one frequency used in the cadence, sequential elements -* of different frequencies MUST use different tone table indexes. Only one -* cadence can be played at a time. It is possible to build complex -* cadences with multiple frequencies using 2 tone table indexes by -* alternating between them. -* -******************************************************************************/ - -typedef struct { - int index; - int tone_on_time; - int tone_off_time; - int freq0; - int gain0; - int freq1; - int gain1; -} IXJ_CADENCE_ELEMENT; - -typedef enum { - PLAY_ONCE, - REPEAT_LAST_ELEMENT, - REPEAT_ALL -} IXJ_CADENCE_TERM; - -typedef struct { - int elements_used; - IXJ_CADENCE_TERM termination; - IXJ_CADENCE_ELEMENT __user *ce; -} IXJ_CADENCE; - -#define IXJCTL_TONE_CADENCE _IOW ('q', 0xCA, IXJ_CADENCE *) -/****************************************************************************** -* -* This group of IOCTLs deal with the playback settings of the DSP -* -******************************************************************************/ - -#define IXJCTL_PLAY_CODEC PHONE_PLAY_CODEC -#define IXJCTL_PLAY_START PHONE_PLAY_START -#define IXJCTL_PLAY_STOP PHONE_PLAY_STOP -#define IXJCTL_PLAY_DEPTH PHONE_PLAY_DEPTH -#define IXJCTL_PLAY_VOLUME PHONE_PLAY_VOLUME -#define IXJCTL_PLAY_LEVEL PHONE_PLAY_LEVEL - -/****************************************************************************** -* -* This group of IOCTLs deal with the Acoustic Echo Cancellation settings -* of the DSP -* -* Issuing the IXJCTL_AEC_START command with a value of AEC_OFF has the -* same effect as IXJCTL_AEC_STOP. This is to simplify slider bar -* controls. IXJCTL_AEC_GET_LEVEL returns the current setting of the AEC. -******************************************************************************/ -#define IXJCTL_AEC_START _IOW ('q', 0xCB, int) -#define IXJCTL_AEC_STOP _IO ('q', 0xCC) -#define IXJCTL_AEC_GET_LEVEL _IO ('q', 0xCD) - -#define AEC_OFF 0 -#define AEC_LOW 1 -#define AEC_MED 2 -#define AEC_HIGH 3 -#define AEC_AUTO 4 -#define AEC_AGC 5 -/****************************************************************************** -* -* Call Progress Tones, DTMF, etc. -* IXJCTL_DTMF_OOB determines if DTMF signaling is sent as Out-Of-Band -* only. If you pass a 1, DTMF is suppressed from the audio stream. -* Tone on and off times are in 250 microsecond intervals so -* ioctl(ixj1, IXJCTL_SET_TONE_ON_TIME, 360); -* will set the tone on time of board ixj1 to 360 * 250us = 90ms -* the default values of tone on and off times is 840 or 210ms -******************************************************************************/ - -#define IXJCTL_DTMF_READY PHONE_DTMF_READY -#define IXJCTL_GET_DTMF PHONE_GET_DTMF -#define IXJCTL_GET_DTMF_ASCII PHONE_GET_DTMF_ASCII -#define IXJCTL_DTMF_OOB PHONE_DTMF_OOB -#define IXJCTL_EXCEPTION PHONE_EXCEPTION -#define IXJCTL_PLAY_TONE PHONE_PLAY_TONE -#define IXJCTL_SET_TONE_ON_TIME PHONE_SET_TONE_ON_TIME -#define IXJCTL_SET_TONE_OFF_TIME PHONE_SET_TONE_OFF_TIME -#define IXJCTL_GET_TONE_ON_TIME PHONE_GET_TONE_ON_TIME -#define IXJCTL_GET_TONE_OFF_TIME PHONE_GET_TONE_OFF_TIME -#define IXJCTL_GET_TONE_STATE PHONE_GET_TONE_STATE -#define IXJCTL_BUSY PHONE_BUSY -#define IXJCTL_RINGBACK PHONE_RINGBACK -#define IXJCTL_DIALTONE PHONE_DIALTONE -#define IXJCTL_CPT_STOP PHONE_CPT_STOP - -/****************************************************************************** -* LineJACK specific IOCTLs -* -* The lsb 4 bits of the LED argument represent the state of each of the 4 -* LED's on the LineJACK -******************************************************************************/ - -#define IXJCTL_SET_LED _IOW ('q', 0xCE, int) -#define IXJCTL_MIXER _IOW ('q', 0xCF, int) - -/****************************************************************************** -* -* The master volume controls use attenuation with 32 levels from 0 to -62dB -* with steps of 2dB each, the defines should be OR'ed together then sent -* as the parameter to the mixer command to change the mixer settings. -* -******************************************************************************/ -#define MIXER_MASTER_L 0x0000 -#define MIXER_MASTER_R 0x0100 -#define ATT00DB 0x00 -#define ATT02DB 0x01 -#define ATT04DB 0x02 -#define ATT06DB 0x03 -#define ATT08DB 0x04 -#define ATT10DB 0x05 -#define ATT12DB 0x06 -#define ATT14DB 0x07 -#define ATT16DB 0x08 -#define ATT18DB 0x09 -#define ATT20DB 0x0A -#define ATT22DB 0x0B -#define ATT24DB 0x0C -#define ATT26DB 0x0D -#define ATT28DB 0x0E -#define ATT30DB 0x0F -#define ATT32DB 0x10 -#define ATT34DB 0x11 -#define ATT36DB 0x12 -#define ATT38DB 0x13 -#define ATT40DB 0x14 -#define ATT42DB 0x15 -#define ATT44DB 0x16 -#define ATT46DB 0x17 -#define ATT48DB 0x18 -#define ATT50DB 0x19 -#define ATT52DB 0x1A -#define ATT54DB 0x1B -#define ATT56DB 0x1C -#define ATT58DB 0x1D -#define ATT60DB 0x1E -#define ATT62DB 0x1F -#define MASTER_MUTE 0x80 - -/****************************************************************************** -* -* The input volume controls use gain with 32 levels from +12dB to -50dB -* with steps of 2dB each, the defines should be OR'ed together then sent -* as the parameter to the mixer command to change the mixer settings. -* -******************************************************************************/ -#define MIXER_PORT_CD_L 0x0600 -#define MIXER_PORT_CD_R 0x0700 -#define MIXER_PORT_LINE_IN_L 0x0800 -#define MIXER_PORT_LINE_IN_R 0x0900 -#define MIXER_PORT_POTS_REC 0x0C00 -#define MIXER_PORT_MIC 0x0E00 - -#define GAIN12DB 0x00 -#define GAIN10DB 0x01 -#define GAIN08DB 0x02 -#define GAIN06DB 0x03 -#define GAIN04DB 0x04 -#define GAIN02DB 0x05 -#define GAIN00DB 0x06 -#define GAIN_02DB 0x07 -#define GAIN_04DB 0x08 -#define GAIN_06DB 0x09 -#define GAIN_08DB 0x0A -#define GAIN_10DB 0x0B -#define GAIN_12DB 0x0C -#define GAIN_14DB 0x0D -#define GAIN_16DB 0x0E -#define GAIN_18DB 0x0F -#define GAIN_20DB 0x10 -#define GAIN_22DB 0x11 -#define GAIN_24DB 0x12 -#define GAIN_26DB 0x13 -#define GAIN_28DB 0x14 -#define GAIN_30DB 0x15 -#define GAIN_32DB 0x16 -#define GAIN_34DB 0x17 -#define GAIN_36DB 0x18 -#define GAIN_38DB 0x19 -#define GAIN_40DB 0x1A -#define GAIN_42DB 0x1B -#define GAIN_44DB 0x1C -#define GAIN_46DB 0x1D -#define GAIN_48DB 0x1E -#define GAIN_50DB 0x1F -#define INPUT_MUTE 0x80 - -/****************************************************************************** -* -* The POTS volume control use attenuation with 8 levels from 0dB to -28dB -* with steps of 4dB each, the defines should be OR'ed together then sent -* as the parameter to the mixer command to change the mixer settings. -* -******************************************************************************/ -#define MIXER_PORT_POTS_PLAY 0x0F00 - -#define POTS_ATT_00DB 0x00 -#define POTS_ATT_04DB 0x01 -#define POTS_ATT_08DB 0x02 -#define POTS_ATT_12DB 0x03 -#define POTS_ATT_16DB 0x04 -#define POTS_ATT_20DB 0x05 -#define POTS_ATT_24DB 0x06 -#define POTS_ATT_28DB 0x07 -#define POTS_MUTE 0x80 - -/****************************************************************************** -* -* The DAA controls the interface to the PSTN port. The driver loads the -* US coefficients by default, so if you live in a different country you -* need to load the set for your countries phone system. -* -******************************************************************************/ -#define IXJCTL_DAA_COEFF_SET _IOW ('q', 0xD0, int) - -#define DAA_US 1 /*PITA 8kHz */ -#define DAA_UK 2 /*ISAR34 8kHz */ -#define DAA_FRANCE 3 /* */ -#define DAA_GERMANY 4 -#define DAA_AUSTRALIA 5 -#define DAA_JAPAN 6 - -/****************************************************************************** -* -* Use IXJCTL_PORT to set or query the port the card is set to. If the -* argument is set to PORT_QUERY, the return value of the ioctl will -* indicate which port is currently in use, otherwise it will change the -* port. -* -******************************************************************************/ -#define IXJCTL_PORT _IOW ('q', 0xD1, int) - -#define PORT_QUERY 0 -#define PORT_POTS 1 -#define PORT_PSTN 2 -#define PORT_SPEAKER 3 -#define PORT_HANDSET 4 - -#define IXJCTL_PSTN_SET_STATE PHONE_PSTN_SET_STATE -#define IXJCTL_PSTN_GET_STATE PHONE_PSTN_GET_STATE - -#define PSTN_ON_HOOK 0 -#define PSTN_RINGING 1 -#define PSTN_OFF_HOOK 2 -#define PSTN_PULSE_DIAL 3 - -/****************************************************************************** -* -* The DAA Analog GAIN sets 2 parameters at one time, the receive gain (AGRR), -* and the transmit gain (AGX). OR together the components and pass them -* as the parameter to IXJCTL_DAA_AGAIN. The default setting is both at 0dB. -* -******************************************************************************/ -#define IXJCTL_DAA_AGAIN _IOW ('q', 0xD2, int) - -#define AGRR00DB 0x00 /* Analog gain in receive direction 0dB */ -#define AGRR3_5DB 0x10 /* Analog gain in receive direction 3.5dB */ -#define AGRR06DB 0x30 /* Analog gain in receive direction 6dB */ - -#define AGX00DB 0x00 /* Analog gain in transmit direction 0dB */ -#define AGX_6DB 0x04 /* Analog gain in transmit direction -6dB */ -#define AGX3_5DB 0x08 /* Analog gain in transmit direction 3.5dB */ -#define AGX_2_5B 0x0C /* Analog gain in transmit direction -2.5dB */ - -#define IXJCTL_PSTN_LINETEST _IO ('q', 0xD3) - -#define IXJCTL_CID _IOR ('q', 0xD4, PHONE_CID *) -#define IXJCTL_VMWI _IOR ('q', 0xD8, int) -#define IXJCTL_CIDCW _IOW ('q', 0xD9, PHONE_CID *) -/****************************************************************************** -* -* The wink duration is tunable with this ioctl. The default wink duration -* is 320ms. You do not need to use this ioctl if you do not require a -* different wink duration. -* -******************************************************************************/ -#define IXJCTL_WINK_DURATION PHONE_WINK_DURATION - -/****************************************************************************** -* -* This ioctl will connect the POTS port to the PSTN port on the LineJACK -* In order for this to work properly the port selection should be set to -* the PSTN port with IXJCTL_PORT prior to calling this ioctl. This will -* enable conference calls between PSTN callers and network callers. -* Passing a 1 to this ioctl enables the POTS<->PSTN connection while -* passing a 0 turns it back off. -* -******************************************************************************/ -#define IXJCTL_POTS_PSTN _IOW ('q', 0xD5, int) - -/****************************************************************************** -* -* IOCTLs added by request. -* -* IXJCTL_HZ sets the value your Linux kernel uses for HZ as defined in -* /usr/include/asm/param.h, this determines the fundamental -* frequency of the clock ticks on your Linux system. The kernel -* must be rebuilt if you change this value, also all modules you -* use (except this one) must be recompiled. The default value -* is 100, and you only need to use this IOCTL if you use some -* other value. -* -* -* IXJCTL_RATE sets the number of times per second that the driver polls -* the DSP. This value cannot be larger than HZ. By -* increasing both of these values, you may be able to reduce -* latency because the max hang time that can exist between the -* driver and the DSP will be reduced. -* -******************************************************************************/ - -#define IXJCTL_HZ _IOW ('q', 0xE0, int) -#define IXJCTL_RATE _IOW ('q', 0xE1, int) -#define IXJCTL_FRAMES_READ _IOR ('q', 0xE2, unsigned long) -#define IXJCTL_FRAMES_WRITTEN _IOR ('q', 0xE3, unsigned long) -#define IXJCTL_READ_WAIT _IOR ('q', 0xE4, unsigned long) -#define IXJCTL_WRITE_WAIT _IOR ('q', 0xE5, unsigned long) -#define IXJCTL_DRYBUFFER_READ _IOR ('q', 0xE6, unsigned long) -#define IXJCTL_DRYBUFFER_CLEAR _IO ('q', 0xE7) -#define IXJCTL_DTMF_PRESCALE _IOW ('q', 0xE8, int) - -/****************************************************************************** -* -* This ioctl allows the user application to control what events the driver -* will send signals for, and what signals it will send for which event. -* By default, if signaling is enabled, all events will send SIGIO when -* they occur. To disable signals for an event set the signal to 0. -* -******************************************************************************/ -typedef enum { - SIG_DTMF_READY, - SIG_HOOKSTATE, - SIG_FLASH, - SIG_PSTN_RING, - SIG_CALLER_ID, - SIG_PSTN_WINK, - SIG_F0, SIG_F1, SIG_F2, SIG_F3, - SIG_FC0, SIG_FC1, SIG_FC2, SIG_FC3, - SIG_READ_READY = 33, - SIG_WRITE_READY = 34 -} IXJ_SIGEVENT; - -typedef struct { - unsigned int event; - int signal; -} IXJ_SIGDEF; - -#define IXJCTL_SIGCTL _IOW ('q', 0xE9, IXJ_SIGDEF *) - -/****************************************************************************** -* -* These ioctls allow the user application to change the gain in the -* Smart Cable of the Internet Phone Card. Sending -1 as a value will cause -* return value to be the current setting. Valid values to set are 0x00 - 0x1F -* -* 11111 = +12 dB -* 10111 = 0 dB -* 00000 = -34.5 dB -* -* IXJCTL_SC_RXG sets the Receive gain -* IXJCTL_SC_TXG sets the Transmit gain -* -******************************************************************************/ -#define IXJCTL_SC_RXG _IOW ('q', 0xEA, int) -#define IXJCTL_SC_TXG _IOW ('q', 0xEB, int) - -/****************************************************************************** -* -* The intercom IOCTL's short the output from one card to the input of the -* other and vice versa (actually done in the DSP read function). It is only -* necessary to execute the IOCTL on one card, but it is necessary to have -* both devices open to be able to detect hook switch changes. The record -* codec and rate of each card must match the playback codec and rate of -* the other card for this to work properly. -* -******************************************************************************/ - -#define IXJCTL_INTERCOM_START _IOW ('q', 0xFD, int) -#define IXJCTL_INTERCOM_STOP _IOW ('q', 0xFE, int) - -/****************************************************************************** - * - * new structure for accessing raw filter information - * - ******************************************************************************/ - -typedef struct { - unsigned int filter; - char enable; - unsigned int coeff[19]; -} IXJ_FILTER_RAW; - -#endif diff --git a/include/uapi/linux/telephony.h b/include/uapi/linux/telephony.h deleted file mode 100644 index d2c9f7105f4b..000000000000 --- a/include/uapi/linux/telephony.h +++ /dev/null @@ -1,263 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ -/****************************************************************************** - * - * telephony.h - * - * Basic Linux Telephony Interface - * - * (c) Copyright 1999-2001 Quicknet Technologies, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Authors: Ed Okerson, - * Greg Herlein, - * - * Contributors: Alan Cox, - * David W. Erhart, - * - * IN NO EVENT SHALL QUICKNET TECHNOLOGIES, INC. BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT - * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF QUICKNET - * TECHNOLOGIES, INC. HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * QUICKNET TECHNOLOGIES, INC. SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND QUICKNET TECHNOLOGIES, INC. HAS NO OBLIGATION - * TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - *****************************************************************************/ - -#ifndef TELEPHONY_H -#define TELEPHONY_H - -#define TELEPHONY_VERSION 3013 - -#define PHONE_VENDOR_IXJ 1 -#define PHONE_VENDOR_QUICKNET PHONE_VENDOR_IXJ -#define PHONE_VENDOR_VOICETRONIX 2 -#define PHONE_VENDOR_ACULAB 3 -#define PHONE_VENDOR_DIGI 4 -#define PHONE_VENDOR_FRANKLIN 5 - -/****************************************************************************** - * Vendor Summary Information Area - * - * Quicknet Technologies, Inc. - makes low density analog telephony cards - * with audio compression, POTS and PSTN interfaces (www.quicknet.net) - * - * (other vendors following this API shuld add a short description of - * the telephony products they support under Linux) - * - *****************************************************************************/ -#define QTI_PHONEJACK 100 -#define QTI_LINEJACK 300 -#define QTI_PHONEJACK_LITE 400 -#define QTI_PHONEJACK_PCI 500 -#define QTI_PHONECARD 600 - -/****************************************************************************** -* -* The capabilities ioctls can inform you of the capabilities of each phone -* device installed in your system. The PHONECTL_CAPABILITIES ioctl -* returns an integer value indicating the number of capabilities the -* device has. The PHONECTL_CAPABILITIES_LIST will fill an array of -* capability structs with all of its capabilities. The -* PHONECTL_CAPABILITIES_CHECK takes a single capability struct and returns -* a TRUE if the device has that capability, otherwise it returns false. -* -******************************************************************************/ -typedef enum { - vendor = 0, - device, - port, - codec, - dsp -} phone_cap; - -struct phone_capability { - char desc[80]; - phone_cap captype; - int cap; - int handle; -}; - -typedef enum { - pots = 0, - pstn, - handset, - speaker -} phone_ports; - -#define PHONE_CAPABILITIES _IO ('q', 0x80) -#define PHONE_CAPABILITIES_LIST _IOR ('q', 0x81, struct phone_capability *) -#define PHONE_CAPABILITIES_CHECK _IOW ('q', 0x82, struct phone_capability *) - -typedef struct { - char month[3]; - char day[3]; - char hour[3]; - char min[3]; - int numlen; - char number[11]; - int namelen; - char name[80]; -} PHONE_CID; - -#define PHONE_RING _IO ('q', 0x83) -#define PHONE_HOOKSTATE _IO ('q', 0x84) -#define PHONE_MAXRINGS _IOW ('q', 0x85, char) -#define PHONE_RING_CADENCE _IOW ('q', 0x86, short) -#define OLD_PHONE_RING_START _IO ('q', 0x87) -#define PHONE_RING_START _IOW ('q', 0x87, PHONE_CID *) -#define PHONE_RING_STOP _IO ('q', 0x88) - -#define USA_RING_CADENCE 0xC0C0 - -#define PHONE_REC_CODEC _IOW ('q', 0x89, int) -#define PHONE_REC_START _IO ('q', 0x8A) -#define PHONE_REC_STOP _IO ('q', 0x8B) -#define PHONE_REC_DEPTH _IOW ('q', 0x8C, int) -#define PHONE_FRAME _IOW ('q', 0x8D, int) -#define PHONE_REC_VOLUME _IOW ('q', 0x8E, int) -#define PHONE_REC_VOLUME_LINEAR _IOW ('q', 0xDB, int) -#define PHONE_REC_LEVEL _IO ('q', 0x8F) - -#define PHONE_PLAY_CODEC _IOW ('q', 0x90, int) -#define PHONE_PLAY_START _IO ('q', 0x91) -#define PHONE_PLAY_STOP _IO ('q', 0x92) -#define PHONE_PLAY_DEPTH _IOW ('q', 0x93, int) -#define PHONE_PLAY_VOLUME _IOW ('q', 0x94, int) -#define PHONE_PLAY_VOLUME_LINEAR _IOW ('q', 0xDC, int) -#define PHONE_PLAY_LEVEL _IO ('q', 0x95) -#define PHONE_DTMF_READY _IOR ('q', 0x96, int) -#define PHONE_GET_DTMF _IOR ('q', 0x97, int) -#define PHONE_GET_DTMF_ASCII _IOR ('q', 0x98, int) -#define PHONE_DTMF_OOB _IOW ('q', 0x99, int) -#define PHONE_EXCEPTION _IOR ('q', 0x9A, int) -#define PHONE_PLAY_TONE _IOW ('q', 0x9B, char) -#define PHONE_SET_TONE_ON_TIME _IOW ('q', 0x9C, int) -#define PHONE_SET_TONE_OFF_TIME _IOW ('q', 0x9D, int) -#define PHONE_GET_TONE_ON_TIME _IO ('q', 0x9E) -#define PHONE_GET_TONE_OFF_TIME _IO ('q', 0x9F) -#define PHONE_GET_TONE_STATE _IO ('q', 0xA0) -#define PHONE_BUSY _IO ('q', 0xA1) -#define PHONE_RINGBACK _IO ('q', 0xA2) -#define PHONE_DIALTONE _IO ('q', 0xA3) -#define PHONE_CPT_STOP _IO ('q', 0xA4) - -#define PHONE_PSTN_SET_STATE _IOW ('q', 0xA4, int) -#define PHONE_PSTN_GET_STATE _IO ('q', 0xA5) - -#define PSTN_ON_HOOK 0 -#define PSTN_RINGING 1 -#define PSTN_OFF_HOOK 2 -#define PSTN_PULSE_DIAL 3 - -/****************************************************************************** -* -* The wink duration is tunable with this ioctl. The default wink duration -* is 320ms. You do not need to use this ioctl if you do not require a -* different wink duration. -* -******************************************************************************/ -#define PHONE_WINK_DURATION _IOW ('q', 0xA6, int) -#define PHONE_WINK _IOW ('q', 0xAA, int) - -/****************************************************************************** -* -* Codec Definitions -* -******************************************************************************/ -typedef enum { - G723_63 = 1, - G723_53 = 2, - TS85 = 3, - TS48 = 4, - TS41 = 5, - G728 = 6, - G729 = 7, - ULAW = 8, - ALAW = 9, - LINEAR16 = 10, - LINEAR8 = 11, - WSS = 12, - G729B = 13 -} phone_codec; - -struct phone_codec_data -{ - phone_codec type; - unsigned short buf_min, buf_opt, buf_max; -}; - -#define PHONE_QUERY_CODEC _IOWR ('q', 0xA7, struct phone_codec_data *) -#define PHONE_PSTN_LINETEST _IO ('q', 0xA8) - -/****************************************************************************** -* -* This controls the VAD/CNG functionality of G.723.1. The driver will -* always pass full size frames, any unused bytes will be padded with zeros, -* and frames passed to the driver should also be padded with zeros. The -* frame type is encoded in the least significant two bits of the first -* WORD of the frame as follows: -* -* bits 1-0 Frame Type Data Rate Significant Words -* 00 0 G.723.1 6.3 12 -* 01 1 G.723.1 5.3 10 -* 10 2 VAD/CNG 2 -* 11 3 Repeat last CNG 2 bits -* -******************************************************************************/ -#define PHONE_VAD _IOW ('q', 0xA9, int) - - -/****************************************************************************** -* -* The exception structure allows us to multiplex multiple events onto the -* select() exception set. If any of these flags are set select() will -* return with a positive indication on the exception set. The dtmf_ready -* bit indicates if there is data waiting in the DTMF buffer. The -* hookstate bit is set if there is a change in hookstate status, it does not -* indicate the current state of the hookswitch. The pstn_ring bit -* indicates that the DAA on a LineJACK card has detected ring voltage on -* the PSTN port. The caller_id bit indicates that caller_id data has been -* received and is available. The pstn_wink bit indicates that the DAA on -* the LineJACK has received a wink from the telco switch. The f0, f1, f2 -* and f3 bits indicate that the filter has been triggered by detecting the -* frequency programmed into that filter. -* -* The remaining bits should be set to zero. They will become defined over time -* for other interface cards and their needs. -* -******************************************************************************/ -struct phone_except -{ - unsigned int dtmf_ready:1; - unsigned int hookstate:1; - unsigned int pstn_ring:1; - unsigned int caller_id:1; - unsigned int pstn_wink:1; - unsigned int f0:1; - unsigned int f1:1; - unsigned int f2:1; - unsigned int f3:1; - unsigned int flash:1; - unsigned int fc0:1; - unsigned int fc1:1; - unsigned int fc2:1; - unsigned int fc3:1; - unsigned int reserved:18; -}; - -union telephony_exception { - struct phone_except bits; - unsigned int bytes; -}; - - -#endif /* TELEPHONY_H */ - -- cgit v1.2.3 From c7bcbfa4f8d1e0e1078adfe959d4b65542bccf66 Mon Sep 17 00:00:00 2001 From: Felix Kuehling Date: Thu, 15 Mar 2018 17:27:46 -0400 Subject: drm/amdkfd: Remove limit on number of GPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the number of GPUs is limited by aperture placement options available on GFX7 and GFX8 hardware. This limitation is not necessary. Scratch and LDS represent per-work-item and per-work-group storage respectively. Different work-items and work-groups use the same virtual address to access their own data. Work running on different GPUs is by definition in different work-groups (different dispatches, in fact). That means the same virtual addresses can be used for these apertures on different GPUs. Add a new AMDKFD_IOC_GET_PROCESS_APERTURES_NEW ioctl that removes the artificial limitation on the number of GPUs that can be supported. The new ioctl allows user mode to query the number of GPUs to allocate enough memory for all GPUs to be reported. This deprecates AMDKFD_IOC_GET_PROCESS_APERTURES. Signed-off-by: Felix Kuehling Acked-by: Christian König Signed-off-by: Oded Gabbay --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 94 ++++++++++++++++++++++++++++ drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c | 22 +++---- include/uapi/linux/kfd_ioctl.h | 27 +++++++- 3 files changed, 128 insertions(+), 15 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 6fe24964540b..7d4009418ec3 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -825,6 +825,97 @@ static int kfd_ioctl_get_process_apertures(struct file *filp, return 0; } +static int kfd_ioctl_get_process_apertures_new(struct file *filp, + struct kfd_process *p, void *data) +{ + struct kfd_ioctl_get_process_apertures_new_args *args = data; + struct kfd_process_device_apertures *pa; + struct kfd_process_device *pdd; + uint32_t nodes = 0; + int ret; + + dev_dbg(kfd_device, "get apertures for PASID %d", p->pasid); + + if (args->num_of_nodes == 0) { + /* Return number of nodes, so that user space can alloacate + * sufficient memory + */ + mutex_lock(&p->mutex); + + if (!kfd_has_process_device_data(p)) + goto out_unlock; + + /* Run over all pdd of the process */ + pdd = kfd_get_first_process_device_data(p); + do { + args->num_of_nodes++; + pdd = kfd_get_next_process_device_data(p, pdd); + } while (pdd); + + goto out_unlock; + } + + /* Fill in process-aperture information for all available + * nodes, but not more than args->num_of_nodes as that is + * the amount of memory allocated by user + */ + pa = kzalloc((sizeof(struct kfd_process_device_apertures) * + args->num_of_nodes), GFP_KERNEL); + if (!pa) + return -ENOMEM; + + mutex_lock(&p->mutex); + + if (!kfd_has_process_device_data(p)) { + args->num_of_nodes = 0; + kfree(pa); + goto out_unlock; + } + + /* Run over all pdd of the process */ + pdd = kfd_get_first_process_device_data(p); + do { + pa[nodes].gpu_id = pdd->dev->id; + pa[nodes].lds_base = pdd->lds_base; + pa[nodes].lds_limit = pdd->lds_limit; + pa[nodes].gpuvm_base = pdd->gpuvm_base; + pa[nodes].gpuvm_limit = pdd->gpuvm_limit; + pa[nodes].scratch_base = pdd->scratch_base; + pa[nodes].scratch_limit = pdd->scratch_limit; + + dev_dbg(kfd_device, + "gpu id %u\n", pdd->dev->id); + dev_dbg(kfd_device, + "lds_base %llX\n", pdd->lds_base); + dev_dbg(kfd_device, + "lds_limit %llX\n", pdd->lds_limit); + dev_dbg(kfd_device, + "gpuvm_base %llX\n", pdd->gpuvm_base); + dev_dbg(kfd_device, + "gpuvm_limit %llX\n", pdd->gpuvm_limit); + dev_dbg(kfd_device, + "scratch_base %llX\n", pdd->scratch_base); + dev_dbg(kfd_device, + "scratch_limit %llX\n", pdd->scratch_limit); + nodes++; + + pdd = kfd_get_next_process_device_data(p, pdd); + } while (pdd && (nodes < args->num_of_nodes)); + mutex_unlock(&p->mutex); + + args->num_of_nodes = nodes; + ret = copy_to_user( + (void __user *)args->kfd_process_device_apertures_ptr, + pa, + (nodes * sizeof(struct kfd_process_device_apertures))); + kfree(pa); + return ret ? -EFAULT : 0; + +out_unlock: + mutex_unlock(&p->mutex); + return 0; +} + static int kfd_ioctl_create_event(struct file *filp, struct kfd_process *p, void *data) { @@ -1017,6 +1108,9 @@ static const struct amdkfd_ioctl_desc amdkfd_ioctls[] = { AMDKFD_IOCTL_DEF(AMDKFD_IOC_SET_TRAP_HANDLER, kfd_ioctl_set_trap_handler, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_GET_PROCESS_APERTURES_NEW, + kfd_ioctl_get_process_apertures_new, 0), }; #define AMDKFD_CORE_IOCTL_COUNT ARRAY_SIZE(amdkfd_ioctls) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c b/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c index 7377513050e6..a06b0100af96 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c @@ -282,14 +282,14 @@ (((uint64_t)(base) & \ 0xFFFFFF0000000000UL) | 0xFFFFFFFFFFL) -#define MAKE_SCRATCH_APP_BASE(gpu_num) \ - (((uint64_t)(gpu_num) << 61) + 0x100000000L) +#define MAKE_SCRATCH_APP_BASE() \ + (((uint64_t)(0x1UL) << 61) + 0x100000000L) #define MAKE_SCRATCH_APP_LIMIT(base) \ (((uint64_t)base & 0xFFFFFFFF00000000UL) | 0xFFFFFFFF) -#define MAKE_LDS_APP_BASE(gpu_num) \ - (((uint64_t)(gpu_num) << 61) + 0x0) +#define MAKE_LDS_APP_BASE() \ + (((uint64_t)(0x1UL) << 61) + 0x0) #define MAKE_LDS_APP_LIMIT(base) \ (((uint64_t)(base) & 0xFFFFFFFF00000000UL) | 0xFFFFFFFF) @@ -314,7 +314,7 @@ int kfd_init_apertures(struct kfd_process *process) return -1; } /* - * For 64 bit process aperture will be statically reserved in + * For 64 bit process apertures will be statically reserved in * the x86_64 non canonical process address space * amdkfd doesn't currently support apertures for 32 bit process */ @@ -323,12 +323,11 @@ int kfd_init_apertures(struct kfd_process *process) pdd->gpuvm_base = pdd->gpuvm_limit = 0; pdd->scratch_base = pdd->scratch_limit = 0; } else { - /* - * node id couldn't be 0 - the three MSB bits of - * aperture shoudn't be 0 + /* Same LDS and scratch apertures can be used + * on all GPUs. This allows using more dGPUs + * than placement options for apertures. */ - pdd->lds_base = MAKE_LDS_APP_BASE(id + 1); - + pdd->lds_base = MAKE_LDS_APP_BASE(); pdd->lds_limit = MAKE_LDS_APP_LIMIT(pdd->lds_base); pdd->gpuvm_base = MAKE_GPUVM_APP_BASE(id + 1); @@ -336,8 +335,7 @@ int kfd_init_apertures(struct kfd_process *process) pdd->gpuvm_limit = MAKE_GPUVM_APP_LIMIT(pdd->gpuvm_base); - pdd->scratch_base = MAKE_SCRATCH_APP_BASE(id + 1); - + pdd->scratch_base = MAKE_SCRATCH_APP_BASE(); pdd->scratch_limit = MAKE_SCRATCH_APP_LIMIT(pdd->scratch_base); } diff --git a/include/uapi/linux/kfd_ioctl.h b/include/uapi/linux/kfd_ioctl.h index 111d73ba2d96..52014370e2e5 100644 --- a/include/uapi/linux/kfd_ioctl.h +++ b/include/uapi/linux/kfd_ioctl.h @@ -107,8 +107,6 @@ struct kfd_ioctl_get_clock_counters_args { __u32 pad; }; -#define NUM_OF_SUPPORTED_GPUS 7 - struct kfd_process_device_apertures { __u64 lds_base; /* from KFD */ __u64 lds_limit; /* from KFD */ @@ -120,6 +118,12 @@ struct kfd_process_device_apertures { __u32 pad; }; +/* + * AMDKFD_IOC_GET_PROCESS_APERTURES is deprecated. Use + * AMDKFD_IOC_GET_PROCESS_APERTURES_NEW instead, which supports an + * unlimited number of GPUs. + */ +#define NUM_OF_SUPPORTED_GPUS 7 struct kfd_ioctl_get_process_apertures_args { struct kfd_process_device_apertures process_apertures[NUM_OF_SUPPORTED_GPUS];/* from KFD */ @@ -129,6 +133,19 @@ struct kfd_ioctl_get_process_apertures_args { __u32 pad; }; +struct kfd_ioctl_get_process_apertures_new_args { + /* User allocated. Pointer to struct kfd_process_device_apertures + * filled in by Kernel + */ + __u64 kfd_process_device_apertures_ptr; + /* to KFD - indicates amount of memory present in + * kfd_process_device_apertures_ptr + * from KFD - Number of entries filled by KFD. + */ + __u32 num_of_nodes; + __u32 pad; +}; + #define MAX_ALLOWED_NUM_POINTS 100 #define MAX_ALLOWED_AW_BUFF_SIZE 4096 #define MAX_ALLOWED_WAC_BUFF_SIZE 128 @@ -332,7 +349,11 @@ struct kfd_ioctl_set_trap_handler_args { #define AMDKFD_IOC_SET_TRAP_HANDLER \ AMDKFD_IOW(0x13, struct kfd_ioctl_set_trap_handler_args) +#define AMDKFD_IOC_GET_PROCESS_APERTURES_NEW \ + AMDKFD_IOWR(0x14, \ + struct kfd_ioctl_get_process_apertures_new_args) + #define AMDKFD_COMMAND_START 0x01 -#define AMDKFD_COMMAND_END 0x14 +#define AMDKFD_COMMAND_END 0x15 #endif -- cgit v1.2.3 From 5ec7e02854b3b9b55936c3b44b8acfb85e333f49 Mon Sep 17 00:00:00 2001 From: Felix Kuehling Date: Thu, 15 Mar 2018 17:27:51 -0400 Subject: drm/amdkfd: Add ioctls for GPUVM memory management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v2: * Fix error handling after kfd_bind_process_to_device in kfd_ioctl_map_memory_to_gpu v3: * Add ioctl to acquire VM from a DRM FD v4: * Return number of successful map/unmap operations in failure cases * Facilitate partial retry after failed map/unmap * Added comments with parameter descriptions to new APIs * Defined AMDKFD_IOC_FREE_MEMORY_OF_GPU write-only Signed-off-by: Felix Kuehling Acked-by: Christian König Signed-off-by: Oded Gabbay --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 377 ++++++++++++++++++++++++ drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 8 + drivers/gpu/drm/amd/include/kgd_kfd_interface.h | 2 + include/uapi/linux/kfd_ioctl.h | 97 +++++- 4 files changed, 483 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 7d4009418ec3..a563ff2ca7dd 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1046,6 +1047,366 @@ static int kfd_ioctl_get_tile_config(struct file *filep, return 0; } +static int kfd_ioctl_acquire_vm(struct file *filep, struct kfd_process *p, + void *data) +{ + struct kfd_ioctl_acquire_vm_args *args = data; + struct kfd_process_device *pdd; + struct kfd_dev *dev; + struct file *drm_file; + int ret; + + dev = kfd_device_by_id(args->gpu_id); + if (!dev) + return -EINVAL; + + drm_file = fget(args->drm_fd); + if (!drm_file) + return -EINVAL; + + mutex_lock(&p->mutex); + + pdd = kfd_get_process_device_data(dev, p); + if (!pdd) { + ret = -EINVAL; + goto err_unlock; + } + + if (pdd->drm_file) { + ret = pdd->drm_file == drm_file ? 0 : -EBUSY; + goto err_unlock; + } + + ret = kfd_process_device_init_vm(pdd, drm_file); + if (ret) + goto err_unlock; + /* On success, the PDD keeps the drm_file reference */ + mutex_unlock(&p->mutex); + + return 0; + +err_unlock: + mutex_unlock(&p->mutex); + fput(drm_file); + return ret; +} + +bool kfd_dev_is_large_bar(struct kfd_dev *dev) +{ + struct kfd_local_mem_info mem_info; + + if (dev->device_info->needs_iommu_device) + return false; + + dev->kfd2kgd->get_local_mem_info(dev->kgd, &mem_info); + if (mem_info.local_mem_size_private == 0 && + mem_info.local_mem_size_public > 0) + return true; + return false; +} + +static int kfd_ioctl_alloc_memory_of_gpu(struct file *filep, + struct kfd_process *p, void *data) +{ + struct kfd_ioctl_alloc_memory_of_gpu_args *args = data; + struct kfd_process_device *pdd; + void *mem; + struct kfd_dev *dev; + int idr_handle; + long err; + uint64_t offset = args->mmap_offset; + uint32_t flags = args->flags; + + if (args->size == 0) + return -EINVAL; + + dev = kfd_device_by_id(args->gpu_id); + if (!dev) + return -EINVAL; + + if ((flags & KFD_IOC_ALLOC_MEM_FLAGS_PUBLIC) && + (flags & KFD_IOC_ALLOC_MEM_FLAGS_VRAM) && + !kfd_dev_is_large_bar(dev)) { + pr_err("Alloc host visible vram on small bar is not allowed\n"); + return -EINVAL; + } + + mutex_lock(&p->mutex); + + pdd = kfd_bind_process_to_device(dev, p); + if (IS_ERR(pdd)) { + err = PTR_ERR(pdd); + goto err_unlock; + } + + err = dev->kfd2kgd->alloc_memory_of_gpu( + dev->kgd, args->va_addr, args->size, + pdd->vm, (struct kgd_mem **) &mem, &offset, + flags); + + if (err) + goto err_unlock; + + idr_handle = kfd_process_device_create_obj_handle(pdd, mem); + if (idr_handle < 0) { + err = -EFAULT; + goto err_free; + } + + mutex_unlock(&p->mutex); + + args->handle = MAKE_HANDLE(args->gpu_id, idr_handle); + args->mmap_offset = offset; + + return 0; + +err_free: + dev->kfd2kgd->free_memory_of_gpu(dev->kgd, (struct kgd_mem *)mem); +err_unlock: + mutex_unlock(&p->mutex); + return err; +} + +static int kfd_ioctl_free_memory_of_gpu(struct file *filep, + struct kfd_process *p, void *data) +{ + struct kfd_ioctl_free_memory_of_gpu_args *args = data; + struct kfd_process_device *pdd; + void *mem; + struct kfd_dev *dev; + int ret; + + dev = kfd_device_by_id(GET_GPU_ID(args->handle)); + if (!dev) + return -EINVAL; + + mutex_lock(&p->mutex); + + pdd = kfd_get_process_device_data(dev, p); + if (!pdd) { + pr_err("Process device data doesn't exist\n"); + ret = -EINVAL; + goto err_unlock; + } + + mem = kfd_process_device_translate_handle( + pdd, GET_IDR_HANDLE(args->handle)); + if (!mem) { + ret = -EINVAL; + goto err_unlock; + } + + ret = dev->kfd2kgd->free_memory_of_gpu(dev->kgd, (struct kgd_mem *)mem); + + /* If freeing the buffer failed, leave the handle in place for + * clean-up during process tear-down. + */ + if (!ret) + kfd_process_device_remove_obj_handle( + pdd, GET_IDR_HANDLE(args->handle)); + +err_unlock: + mutex_unlock(&p->mutex); + return ret; +} + +static int kfd_ioctl_map_memory_to_gpu(struct file *filep, + struct kfd_process *p, void *data) +{ + struct kfd_ioctl_map_memory_to_gpu_args *args = data; + struct kfd_process_device *pdd, *peer_pdd; + void *mem; + struct kfd_dev *dev, *peer; + long err = 0; + int i; + uint32_t *devices_arr = NULL; + + dev = kfd_device_by_id(GET_GPU_ID(args->handle)); + if (!dev) + return -EINVAL; + + if (!args->n_devices) { + pr_debug("Device IDs array empty\n"); + return -EINVAL; + } + if (args->n_success > args->n_devices) { + pr_debug("n_success exceeds n_devices\n"); + return -EINVAL; + } + + devices_arr = kmalloc(args->n_devices * sizeof(*devices_arr), + GFP_KERNEL); + if (!devices_arr) + return -ENOMEM; + + err = copy_from_user(devices_arr, + (void __user *)args->device_ids_array_ptr, + args->n_devices * sizeof(*devices_arr)); + if (err != 0) { + err = -EFAULT; + goto copy_from_user_failed; + } + + mutex_lock(&p->mutex); + + pdd = kfd_bind_process_to_device(dev, p); + if (IS_ERR(pdd)) { + err = PTR_ERR(pdd); + goto bind_process_to_device_failed; + } + + mem = kfd_process_device_translate_handle(pdd, + GET_IDR_HANDLE(args->handle)); + if (!mem) { + err = -ENOMEM; + goto get_mem_obj_from_handle_failed; + } + + for (i = args->n_success; i < args->n_devices; i++) { + peer = kfd_device_by_id(devices_arr[i]); + if (!peer) { + pr_debug("Getting device by id failed for 0x%x\n", + devices_arr[i]); + err = -EINVAL; + goto get_mem_obj_from_handle_failed; + } + + peer_pdd = kfd_bind_process_to_device(peer, p); + if (IS_ERR(peer_pdd)) { + err = PTR_ERR(peer_pdd); + goto get_mem_obj_from_handle_failed; + } + err = peer->kfd2kgd->map_memory_to_gpu( + peer->kgd, (struct kgd_mem *)mem, peer_pdd->vm); + if (err) { + pr_err("Failed to map to gpu %d/%d\n", + i, args->n_devices); + goto map_memory_to_gpu_failed; + } + args->n_success = i+1; + } + + mutex_unlock(&p->mutex); + + err = dev->kfd2kgd->sync_memory(dev->kgd, (struct kgd_mem *) mem, true); + if (err) { + pr_debug("Sync memory failed, wait interrupted by user signal\n"); + goto sync_memory_failed; + } + + /* Flush TLBs after waiting for the page table updates to complete */ + for (i = 0; i < args->n_devices; i++) { + peer = kfd_device_by_id(devices_arr[i]); + if (WARN_ON_ONCE(!peer)) + continue; + peer_pdd = kfd_get_process_device_data(peer, p); + if (WARN_ON_ONCE(!peer_pdd)) + continue; + kfd_flush_tlb(peer_pdd); + } + + kfree(devices_arr); + + return err; + +bind_process_to_device_failed: +get_mem_obj_from_handle_failed: +map_memory_to_gpu_failed: + mutex_unlock(&p->mutex); +copy_from_user_failed: +sync_memory_failed: + kfree(devices_arr); + + return err; +} + +static int kfd_ioctl_unmap_memory_from_gpu(struct file *filep, + struct kfd_process *p, void *data) +{ + struct kfd_ioctl_unmap_memory_from_gpu_args *args = data; + struct kfd_process_device *pdd, *peer_pdd; + void *mem; + struct kfd_dev *dev, *peer; + long err = 0; + uint32_t *devices_arr = NULL, i; + + dev = kfd_device_by_id(GET_GPU_ID(args->handle)); + if (!dev) + return -EINVAL; + + if (!args->n_devices) { + pr_debug("Device IDs array empty\n"); + return -EINVAL; + } + if (args->n_success > args->n_devices) { + pr_debug("n_success exceeds n_devices\n"); + return -EINVAL; + } + + devices_arr = kmalloc(args->n_devices * sizeof(*devices_arr), + GFP_KERNEL); + if (!devices_arr) + return -ENOMEM; + + err = copy_from_user(devices_arr, + (void __user *)args->device_ids_array_ptr, + args->n_devices * sizeof(*devices_arr)); + if (err != 0) { + err = -EFAULT; + goto copy_from_user_failed; + } + + mutex_lock(&p->mutex); + + pdd = kfd_get_process_device_data(dev, p); + if (!pdd) { + err = PTR_ERR(pdd); + goto bind_process_to_device_failed; + } + + mem = kfd_process_device_translate_handle(pdd, + GET_IDR_HANDLE(args->handle)); + if (!mem) { + err = -ENOMEM; + goto get_mem_obj_from_handle_failed; + } + + for (i = args->n_success; i < args->n_devices; i++) { + peer = kfd_device_by_id(devices_arr[i]); + if (!peer) { + err = -EINVAL; + goto get_mem_obj_from_handle_failed; + } + + peer_pdd = kfd_get_process_device_data(peer, p); + if (!peer_pdd) { + err = -ENODEV; + goto get_mem_obj_from_handle_failed; + } + err = dev->kfd2kgd->unmap_memory_to_gpu( + peer->kgd, (struct kgd_mem *)mem, peer_pdd->vm); + if (err) { + pr_err("Failed to unmap from gpu %d/%d\n", + i, args->n_devices); + goto unmap_memory_from_gpu_failed; + } + args->n_success = i+1; + } + kfree(devices_arr); + + mutex_unlock(&p->mutex); + + return 0; + +bind_process_to_device_failed: +get_mem_obj_from_handle_failed: +unmap_memory_from_gpu_failed: + mutex_unlock(&p->mutex); +copy_from_user_failed: + kfree(devices_arr); + return err; +} + #define AMDKFD_IOCTL_DEF(ioctl, _func, _flags) \ [_IOC_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, \ .cmd_drv = 0, .name = #ioctl} @@ -1111,6 +1472,22 @@ static const struct amdkfd_ioctl_desc amdkfd_ioctls[] = { AMDKFD_IOCTL_DEF(AMDKFD_IOC_GET_PROCESS_APERTURES_NEW, kfd_ioctl_get_process_apertures_new, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_ACQUIRE_VM, + kfd_ioctl_acquire_vm, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_ALLOC_MEMORY_OF_GPU, + kfd_ioctl_alloc_memory_of_gpu, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_FREE_MEMORY_OF_GPU, + kfd_ioctl_free_memory_of_gpu, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_MAP_MEMORY_TO_GPU, + kfd_ioctl_map_memory_to_gpu, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_UNMAP_MEMORY_FROM_GPU, + kfd_ioctl_unmap_memory_from_gpu, 0), + }; #define AMDKFD_CORE_IOCTL_COUNT ARRAY_SIZE(amdkfd_ioctls) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index aaed005ce1f5..1542807373d7 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -509,6 +509,14 @@ struct qcm_process_device { int kgd2kfd_schedule_evict_and_restore_process(struct mm_struct *mm, struct dma_fence *fence); +/* 8 byte handle containing GPU ID in the most significant 4 bytes and + * idr_handle in the least significant 4 bytes + */ +#define MAKE_HANDLE(gpu_id, idr_handle) \ + (((uint64_t)(gpu_id) << 32) + idr_handle) +#define GET_GPU_ID(handle) (handle >> 32) +#define GET_IDR_HANDLE(handle) (handle & 0xFFFFFFFF) + enum kfd_pdd_bound { PDD_UNBOUND = 0, PDD_BOUND, diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index b1f35c8be2cf..237289a72bb7 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -130,6 +130,7 @@ struct tile_config { /* * Allocation flag domains + * NOTE: This must match the corresponding definitions in kfd_ioctl.h. */ #define ALLOC_MEM_FLAGS_VRAM (1 << 0) #define ALLOC_MEM_FLAGS_GTT (1 << 1) @@ -138,6 +139,7 @@ struct tile_config { /* * Allocation flags attributes/access options. + * NOTE: This must match the corresponding definitions in kfd_ioctl.h. */ #define ALLOC_MEM_FLAGS_WRITABLE (1 << 31) #define ALLOC_MEM_FLAGS_EXECUTABLE (1 << 30) diff --git a/include/uapi/linux/kfd_ioctl.h b/include/uapi/linux/kfd_ioctl.h index 52014370e2e5..b4f5073dbac2 100644 --- a/include/uapi/linux/kfd_ioctl.h +++ b/include/uapi/linux/kfd_ioctl.h @@ -286,6 +286,86 @@ struct kfd_ioctl_set_trap_handler_args { __u32 pad; }; +struct kfd_ioctl_acquire_vm_args { + __u32 drm_fd; /* to KFD */ + __u32 gpu_id; /* to KFD */ +}; + +/* Allocation flags: memory types */ +#define KFD_IOC_ALLOC_MEM_FLAGS_VRAM (1 << 0) +#define KFD_IOC_ALLOC_MEM_FLAGS_GTT (1 << 1) +#define KFD_IOC_ALLOC_MEM_FLAGS_USERPTR (1 << 2) +#define KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL (1 << 3) +/* Allocation flags: attributes/access options */ +#define KFD_IOC_ALLOC_MEM_FLAGS_WRITABLE (1 << 31) +#define KFD_IOC_ALLOC_MEM_FLAGS_EXECUTABLE (1 << 30) +#define KFD_IOC_ALLOC_MEM_FLAGS_PUBLIC (1 << 29) +#define KFD_IOC_ALLOC_MEM_FLAGS_NO_SUBSTITUTE (1 << 28) +#define KFD_IOC_ALLOC_MEM_FLAGS_AQL_QUEUE_MEM (1 << 27) +#define KFD_IOC_ALLOC_MEM_FLAGS_COHERENT (1 << 26) + +/* Allocate memory for later SVM (shared virtual memory) mapping. + * + * @va_addr: virtual address of the memory to be allocated + * all later mappings on all GPUs will use this address + * @size: size in bytes + * @handle: buffer handle returned to user mode, used to refer to + * this allocation for mapping, unmapping and freeing + * @mmap_offset: for CPU-mapping the allocation by mmapping a render node + * for userptrs this is overloaded to specify the CPU address + * @gpu_id: device identifier + * @flags: memory type and attributes. See KFD_IOC_ALLOC_MEM_FLAGS above + */ +struct kfd_ioctl_alloc_memory_of_gpu_args { + __u64 va_addr; /* to KFD */ + __u64 size; /* to KFD */ + __u64 handle; /* from KFD */ + __u64 mmap_offset; /* to KFD (userptr), from KFD (mmap offset) */ + __u32 gpu_id; /* to KFD */ + __u32 flags; +}; + +/* Free memory allocated with kfd_ioctl_alloc_memory_of_gpu + * + * @handle: memory handle returned by alloc + */ +struct kfd_ioctl_free_memory_of_gpu_args { + __u64 handle; /* to KFD */ +}; + +/* Map memory to one or more GPUs + * + * @handle: memory handle returned by alloc + * @device_ids_array_ptr: array of gpu_ids (__u32 per device) + * @n_devices: number of devices in the array + * @n_success: number of devices mapped successfully + * + * @n_success returns information to the caller how many devices from + * the start of the array have mapped the buffer successfully. It can + * be passed into a subsequent retry call to skip those devices. For + * the first call the caller should initialize it to 0. + * + * If the ioctl completes with return code 0 (success), n_success == + * n_devices. + */ +struct kfd_ioctl_map_memory_to_gpu_args { + __u64 handle; /* to KFD */ + __u64 device_ids_array_ptr; /* to KFD */ + __u32 n_devices; /* to KFD */ + __u32 n_success; /* to/from KFD */ +}; + +/* Unmap memory from one or more GPUs + * + * same arguments as for mapping + */ +struct kfd_ioctl_unmap_memory_from_gpu_args { + __u64 handle; /* to KFD */ + __u64 device_ids_array_ptr; /* to KFD */ + __u32 n_devices; /* to KFD */ + __u32 n_success; /* to/from KFD */ +}; + #define AMDKFD_IOCTL_BASE 'K' #define AMDKFD_IO(nr) _IO(AMDKFD_IOCTL_BASE, nr) #define AMDKFD_IOR(nr, type) _IOR(AMDKFD_IOCTL_BASE, nr, type) @@ -353,7 +433,22 @@ struct kfd_ioctl_set_trap_handler_args { AMDKFD_IOWR(0x14, \ struct kfd_ioctl_get_process_apertures_new_args) +#define AMDKFD_IOC_ACQUIRE_VM \ + AMDKFD_IOW(0x15, struct kfd_ioctl_acquire_vm_args) + +#define AMDKFD_IOC_ALLOC_MEMORY_OF_GPU \ + AMDKFD_IOWR(0x16, struct kfd_ioctl_alloc_memory_of_gpu_args) + +#define AMDKFD_IOC_FREE_MEMORY_OF_GPU \ + AMDKFD_IOW(0x17, struct kfd_ioctl_free_memory_of_gpu_args) + +#define AMDKFD_IOC_MAP_MEMORY_TO_GPU \ + AMDKFD_IOWR(0x18, struct kfd_ioctl_map_memory_to_gpu_args) + +#define AMDKFD_IOC_UNMAP_MEMORY_FROM_GPU \ + AMDKFD_IOWR(0x19, struct kfd_ioctl_unmap_memory_from_gpu_args) + #define AMDKFD_COMMAND_START 0x01 -#define AMDKFD_COMMAND_END 0x15 +#define AMDKFD_COMMAND_END 0x1A #endif -- cgit v1.2.3 From 4bbb3e0e8239f9079bf1fe20b3c0cb598714ae61 Mon Sep 17 00:00:00 2001 From: Toshiaki Makita Date: Tue, 13 Mar 2018 14:51:27 +0900 Subject: net: Fix vlan untag for bridge and vlan_dev with reorder_hdr off When we have a bridge with vlan_filtering on and a vlan device on top of it, packets would be corrupted in skb_vlan_untag() called from br_dev_xmit(). The problem sits in skb_reorder_vlan_header() used in skb_vlan_untag(), which makes use of skb->mac_len. In this function mac_len is meant for handling rx path with vlan devices with reorder_header disabled, but in tx path mac_len is typically 0 and cannot be used, which is the problem in this case. The current code even does not properly handle rx path (skb_vlan_untag() called from __netif_receive_skb_core()) with reorder_header off actually. In rx path single tag case, it works as follows: - Before skb_reorder_vlan_header() mac_header data v v +-------------------+-------------+------+---- | ETH | VLAN | ETH | | ADDRS | TPID | TCI | TYPE | +-------------------+-------------+------+---- <-------- mac_len ---------> <-------------> to be removed - After skb_reorder_vlan_header() mac_header data v v +-------------------+------+---- | ETH | ETH | | ADDRS | TYPE | +-------------------+------+---- <-------- mac_len ---------> This is ok, but in rx double tag case, it corrupts packets: - Before skb_reorder_vlan_header() mac_header data v v +-------------------+-------------+-------------+------+---- | ETH | VLAN | VLAN | ETH | | ADDRS | TPID | TCI | TPID | TCI | TYPE | +-------------------+-------------+-------------+------+---- <--------------- mac_len ----------------> <-------------> should be removed <---------------------------> actually will be removed - After skb_reorder_vlan_header() mac_header data v v +-------------------+------+---- | ETH | ETH | | ADDRS | TYPE | +-------------------+------+---- <--------------- mac_len ----------------> So, two of vlan tags are both removed while only inner one should be removed and mac_header (and mac_len) is broken. skb_vlan_untag() is meant for removing the vlan header at (skb->data - 2), so use skb->data and skb->mac_header to calculate the right offset. Reported-by: Brandon Carpenter Fixes: a6e18ff11170 ("vlan: Fix untag operations of stacked vlans with REORDER_HEADER off") Signed-off-by: Toshiaki Makita Signed-off-by: David S. Miller --- include/uapi/linux/if_ether.h | 1 + net/core/skbuff.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index 8bbbcb5cd94b..820de5d222d2 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -30,6 +30,7 @@ */ #define ETH_ALEN 6 /* Octets in one ethernet addr */ +#define ETH_TLEN 2 /* Octets in ethernet type field */ #define ETH_HLEN 14 /* Total octets in header. */ #define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ #define ETH_DATA_LEN 1500 /* Max. octets in payload */ diff --git a/net/core/skbuff.c b/net/core/skbuff.c index baf990528943..b103f46ec512 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -5020,13 +5020,16 @@ EXPORT_SYMBOL_GPL(skb_gso_validate_mac_len); static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb) { + int mac_len; + if (skb_cow(skb, skb_headroom(skb)) < 0) { kfree_skb(skb); return NULL; } - memmove(skb->data - ETH_HLEN, skb->data - skb->mac_len - VLAN_HLEN, - 2 * ETH_ALEN); + mac_len = skb->data - skb_mac_header(skb); + memmove(skb_mac_header(skb) + VLAN_HLEN, skb_mac_header(skb), + mac_len - VLAN_HLEN - ETH_TLEN); skb->mac_header += VLAN_HLEN; return skb; } -- cgit v1.2.3 From 7156d194a0772f733865267e7207e0b08f81b02b Mon Sep 17 00:00:00 2001 From: Yousuk Seung Date: Fri, 16 Mar 2018 10:51:07 -0700 Subject: tcp: add snd_ssthresh stat in SCM_TIMESTAMPING_OPT_STATS This patch adds TCP_NLA_SND_SSTHRESH stat into SCM_TIMESTAMPING_OPT_STATS that reports tcp_sock.snd_ssthresh. Signed-off-by: Yousuk Seung Signed-off-by: Neal Cardwell Signed-off-by: Priyaranjan Jha Signed-off-by: Soheil Hassas Yeganeh Signed-off-by: Yuchung Cheng Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- include/uapi/linux/tcp.h | 1 + net/ipv4/tcp.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 4c0ae0faf7ca..560374c978f9 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -243,6 +243,7 @@ enum { TCP_NLA_DELIVERY_RATE_APP_LMT, /* delivery rate application limited ? */ TCP_NLA_SNDQ_SIZE, /* Data (bytes) pending in send queue */ TCP_NLA_CA_STATE, /* ca_state of socket */ + TCP_NLA_SND_SSTHRESH, /* Slow start size threshold */ }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index fb350f740f69..e553f84bde83 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3031,7 +3031,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk) u32 rate; stats = alloc_skb(7 * nla_total_size_64bit(sizeof(u64)) + - 4 * nla_total_size(sizeof(u32)) + + 5 * nla_total_size(sizeof(u32)) + 3 * nla_total_size(sizeof(u8)), GFP_ATOMIC); if (!stats) return NULL; @@ -3061,6 +3061,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk) nla_put_u8(stats, TCP_NLA_RECUR_RETRANS, inet_csk(sk)->icsk_retransmits); nla_put_u8(stats, TCP_NLA_DELIVERY_RATE_APP_LMT, !!tp->rate_app_limited); + nla_put_u32(stats, TCP_NLA_SND_SSTHRESH, tp->snd_ssthresh); nla_put_u32(stats, TCP_NLA_SNDQ_SIZE, tp->write_seq - tp->snd_una); nla_put_u8(stats, TCP_NLA_CA_STATE, inet_csk(sk)->icsk_ca_state); -- cgit v1.2.3 From 4d5422cea3b61f158d58924cbb43feada456ba5c Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Mon, 12 Mar 2018 04:53:02 -0700 Subject: KVM: X86: Provide a capability to disable MWAIT intercepts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allowing a guest to execute MWAIT without interception enables a guest to put a (physical) CPU into a power saving state, where it takes longer to return from than what may be desired by the host. Don't give a guest that power over a host by default. (Especially, since nothing prevents a guest from using MWAIT even when it is not advertised via CPUID.) Cc: Paolo Bonzini Cc: Radim Krčmář Cc: Jan H. Schönherr Signed-off-by: Wanpeng Li Signed-off-by: Paolo Bonzini --- Documentation/virtual/kvm/api.txt | 27 ++++++++++++++++++--------- arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/svm.c | 2 +- arch/x86/kvm/vmx.c | 9 +++++---- arch/x86/kvm/x86.c | 24 ++++++++++++++++++++---- arch/x86/kvm/x86.h | 10 +++++----- include/uapi/linux/kvm.h | 2 +- tools/include/uapi/linux/kvm.h | 2 +- 8 files changed, 53 insertions(+), 25 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 786c1b4ecb59..744a202cca31 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -4358,6 +4358,24 @@ enables QEMU to build error log and branch to guest kernel registered machine check handling routine. Without this capability KVM will branch to guests' 0x200 interrupt vector. +7.13 KVM_CAP_X86_DISABLE_EXITS + +Architectures: x86 +Parameters: args[0] defines which exits are disabled +Returns: 0 on success, -EINVAL when args[0] contains invalid exits + +Valid bits in args[0] are + +#define KVM_X86_DISABLE_EXITS_MWAIT (1 << 0) + +Enabling this capability on a VM provides userspace with a way to no +longer intercept some instructions for improved latency in some +workloads, and is suggested when vCPUs are associated to dedicated +physical CPUs. More bits can be added in the future; userspace can +just pass the KVM_CHECK_EXTENSION result to KVM_ENABLE_CAP to disable +all such vmexits. + + 8. Other capabilities. ---------------------- @@ -4470,15 +4488,6 @@ reserved. Both registers and addresses are 64-bits wide. It will be possible to run 64-bit or 32-bit guest code. -8.8 KVM_CAP_X86_GUEST_MWAIT - -Architectures: x86 - -This capability indicates that guest using memory monotoring instructions -(MWAIT/MWAITX) to stop the virtual CPU will not cause a VM exit. As such time -spent while virtual CPU is halted in this way will then be accounted for as -guest running time on the host (as opposed to e.g. HLT). - 8.9 KVM_CAP_ARM_USER_IRQ Architectures: arm, arm64 diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 480a75b22b69..a85b640aee1e 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -811,6 +811,8 @@ struct kvm_arch { gpa_t wall_clock; + bool mwait_in_guest; + bool ept_identity_pagetable_done; gpa_t ept_identity_map_addr; diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index fa1c4977e1c2..f6578cee6bb6 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -1398,7 +1398,7 @@ static void init_vmcb(struct vcpu_svm *svm) set_intercept(svm, INTERCEPT_XSETBV); set_intercept(svm, INTERCEPT_RSM); - if (!kvm_mwait_in_guest()) { + if (!kvm_mwait_in_guest(svm->vcpu.kvm)) { set_intercept(svm, INTERCEPT_MONITOR); set_intercept(svm, INTERCEPT_MWAIT); } diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index b4d8da6c62c8..7cef183993ba 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3746,13 +3746,11 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf) CPU_BASED_UNCOND_IO_EXITING | CPU_BASED_MOV_DR_EXITING | CPU_BASED_USE_TSC_OFFSETING | + CPU_BASED_MWAIT_EXITING | + CPU_BASED_MONITOR_EXITING | CPU_BASED_INVLPG_EXITING | CPU_BASED_RDPMC_EXITING; - if (!kvm_mwait_in_guest()) - min |= CPU_BASED_MWAIT_EXITING | - CPU_BASED_MONITOR_EXITING; - opt = CPU_BASED_TPR_SHADOW | CPU_BASED_USE_MSR_BITMAPS | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS; @@ -5544,6 +5542,9 @@ static u32 vmx_exec_control(struct vcpu_vmx *vmx) exec_control |= CPU_BASED_CR3_STORE_EXITING | CPU_BASED_CR3_LOAD_EXITING | CPU_BASED_INVLPG_EXITING; + if (kvm_mwait_in_guest(vmx->vcpu.kvm)) + exec_control &= ~(CPU_BASED_MWAIT_EXITING | + CPU_BASED_MONITOR_EXITING); return exec_control; } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 9e1496cb2345..db95d4d6f57b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2813,9 +2813,15 @@ out: return r; } +static inline bool kvm_can_mwait_in_guest(void) +{ + return boot_cpu_has(X86_FEATURE_MWAIT) && + !boot_cpu_has_bug(X86_BUG_MONITOR); +} + int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) { - int r; + int r = 0; switch (ext) { case KVM_CAP_IRQCHIP: @@ -2871,8 +2877,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_ADJUST_CLOCK: r = KVM_CLOCK_TSC_STABLE; break; - case KVM_CAP_X86_GUEST_MWAIT: - r = kvm_mwait_in_guest(); + case KVM_CAP_X86_DISABLE_EXITS: + if(kvm_can_mwait_in_guest()) + r |= KVM_X86_DISABLE_EXITS_MWAIT; break; case KVM_CAP_X86_SMM: /* SMBASE is usually relocated above 1M on modern chipsets, @@ -2913,7 +2920,6 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) r = KVM_X2APIC_API_VALID_FLAGS; break; default: - r = 0; break; } return r; @@ -4218,6 +4224,16 @@ split_irqchip_unlock: r = 0; break; + case KVM_CAP_X86_DISABLE_EXITS: + r = -EINVAL; + if (cap->args[0] & ~KVM_X86_DISABLE_VALID_EXITS) + break; + + if ((cap->args[0] & KVM_X86_DISABLE_EXITS_MWAIT) && + kvm_can_mwait_in_guest()) + kvm->arch.mwait_in_guest = true; + r = 0; + break; default: r = -EINVAL; break; diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 18e2e0a91edc..026b239bf058 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -2,8 +2,6 @@ #ifndef ARCH_X86_KVM_X86_H #define ARCH_X86_KVM_X86_H -#include -#include #include #include #include "kvm_cache_regs.h" @@ -266,10 +264,12 @@ static inline u64 nsec_to_cycles(struct kvm_vcpu *vcpu, u64 nsec) __rem; \ }) -static inline bool kvm_mwait_in_guest(void) +#define KVM_X86_DISABLE_EXITS_MWAIT (1 << 0) +#define KVM_X86_DISABLE_VALID_EXITS (KVM_X86_DISABLE_EXITS_MWAIT) + +static inline bool kvm_mwait_in_guest(struct kvm *kvm) { - return boot_cpu_has(X86_FEATURE_MWAIT) && - !boot_cpu_has_bug(X86_BUG_MONITOR); + return kvm->arch.mwait_in_guest; } #endif diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 088c2c92db55..1065006c9bf5 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -929,7 +929,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_GS 140 #define KVM_CAP_S390_AIS 141 #define KVM_CAP_SPAPR_TCE_VFIO 142 -#define KVM_CAP_X86_GUEST_MWAIT 143 +#define KVM_CAP_X86_DISABLE_EXITS 143 #define KVM_CAP_ARM_USER_IRQ 144 #define KVM_CAP_S390_CMMA_MIGRATION 145 #define KVM_CAP_PPC_FWNMI 146 diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index 0fb5ef939732..b13c257261af 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -924,7 +924,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_GS 140 #define KVM_CAP_S390_AIS 141 #define KVM_CAP_SPAPR_TCE_VFIO 142 -#define KVM_CAP_X86_GUEST_MWAIT 143 +#define KVM_CAP_X86_DISABLE_EXITS 143 #define KVM_CAP_ARM_USER_IRQ 144 #define KVM_CAP_S390_CMMA_MIGRATION 145 #define KVM_CAP_PPC_FWNMI 146 -- cgit v1.2.3 From 233bde21aa43516baa013ef7ac33f3427056db3e Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 14 Mar 2018 15:48:06 -0700 Subject: block: Move SECTOR_SIZE and SECTOR_SHIFT definitions into It happens often while I'm preparing a patch for a block driver that I'm wondering: is a definition of SECTOR_SIZE and/or SECTOR_SHIFT available for this driver? Do I have to introduce definitions of these constants before I can use these constants? To avoid this confusion, move the existing definitions of SECTOR_SIZE and SECTOR_SHIFT into the header file such that these become available for all block drivers. Make the SECTOR_SIZE definition in the uapi msdos_fs.h header file conditional to avoid that including that header file after causes the compiler to complain about a SECTOR_SIZE redefinition. Note: the SECTOR_SIZE / SECTOR_SHIFT / SECTOR_BITS definitions have not been removed from uapi header files nor from NAND drivers in which these constants are used for another purpose than converting block layer offsets and sizes into a number of sectors. Cc: David S. Miller Cc: Mike Snitzer Cc: Dan Williams Cc: Minchan Kim Cc: Nitin Gupta Reviewed-by: Sergey Senozhatsky Reviewed-by: Christoph Hellwig Reviewed-by: Johannes Thumshirn Reviewed-by: Martin K. Petersen Signed-off-by: Bart Van Assche Signed-off-by: Jens Axboe --- arch/xtensa/platforms/iss/simdisk.c | 1 - drivers/block/brd.c | 1 - drivers/block/null_blk.c | 2 -- drivers/block/rbd.c | 9 -------- drivers/block/zram/zram_drv.h | 1 - drivers/ide/ide-cd.c | 8 +++---- drivers/ide/ide-cd.h | 6 +----- drivers/nvdimm/nd.h | 1 - drivers/scsi/gdth.h | 3 --- include/linux/blkdev.h | 42 +++++++++++++++++++++++++++---------- include/linux/device-mapper.h | 2 -- include/linux/ide.h | 1 - include/uapi/linux/msdos_fs.h | 2 ++ 13 files changed, 38 insertions(+), 41 deletions(-) (limited to 'include/uapi/linux') diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c index 1b6418407467..026211e7ab09 100644 --- a/arch/xtensa/platforms/iss/simdisk.c +++ b/arch/xtensa/platforms/iss/simdisk.c @@ -21,7 +21,6 @@ #include #define SIMDISK_MAJOR 240 -#define SECTOR_SHIFT 9 #define SIMDISK_MINORS 1 #define MAX_SIMDISK_COUNT 10 diff --git a/drivers/block/brd.c b/drivers/block/brd.c index deea78e485da..66cb0f857f64 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -24,7 +24,6 @@ #include -#define SECTOR_SHIFT 9 #define PAGE_SECTORS_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) #define PAGE_SECTORS (1 << PAGE_SECTORS_SHIFT) diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c index 0517613afccb..a76553293a31 100644 --- a/drivers/block/null_blk.c +++ b/drivers/block/null_blk.c @@ -16,10 +16,8 @@ #include #include -#define SECTOR_SHIFT 9 #define PAGE_SECTORS_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) #define PAGE_SECTORS (1 << PAGE_SECTORS_SHIFT) -#define SECTOR_SIZE (1 << SECTOR_SHIFT) #define SECTOR_MASK (PAGE_SECTORS - 1) #define FREE_BATCH 16 diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 0016170cde0a..1e03b04819c8 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -50,15 +50,6 @@ #define RBD_DEBUG /* Activate rbd_assert() calls */ -/* - * The basic unit of block I/O is a sector. It is interpreted in a - * number of contexts in Linux (blk, bio, genhd), but the default is - * universally 512 bytes. These symbols are just slightly more - * meaningful than the bare numbers they represent. - */ -#define SECTOR_SHIFT 9 -#define SECTOR_SIZE (1ULL << SECTOR_SHIFT) - /* * Increment the given counter and return its updated value. * If the counter is already 0 it will not be incremented. diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 31762db861e3..1e9bf65c0bfb 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -37,7 +37,6 @@ static const size_t max_zpage_size = PAGE_SIZE / 4 * 3; /*-- End of configurable params */ -#define SECTOR_SHIFT 9 #define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) #define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT) #define ZRAM_LOGICAL_BLOCK_SHIFT 12 diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 5613cc2d51fc..5a8e8e3c22cd 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -712,7 +712,7 @@ static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq) struct request_queue *q = drive->queue; int write = rq_data_dir(rq) == WRITE; unsigned short sectors_per_frame = - queue_logical_block_size(q) >> SECTOR_BITS; + queue_logical_block_size(q) >> SECTOR_SHIFT; ide_debug_log(IDE_DBG_RQ, "rq->cmd[0]: 0x%x, rq->cmd_flags: 0x%x, " "secs_per_frame: %u", @@ -919,7 +919,7 @@ static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, * end up being bogus. */ blocklen = be32_to_cpu(capbuf.blocklen); - blocklen = (blocklen >> SECTOR_BITS) << SECTOR_BITS; + blocklen = (blocklen >> SECTOR_SHIFT) << SECTOR_SHIFT; switch (blocklen) { case 512: case 1024: @@ -935,7 +935,7 @@ static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, } *capacity = 1 + be32_to_cpu(capbuf.lba); - *sectors_per_frame = blocklen >> SECTOR_BITS; + *sectors_per_frame = blocklen >> SECTOR_SHIFT; ide_debug_log(IDE_DBG_PROBE, "cap: %lu, sectors_per_frame: %lu", *capacity, *sectors_per_frame); @@ -1012,7 +1012,7 @@ int ide_cd_read_toc(ide_drive_t *drive, struct request_sense *sense) drive->probed_capacity = toc->capacity * sectors_per_frame; blk_queue_logical_block_size(drive->queue, - sectors_per_frame << SECTOR_BITS); + sectors_per_frame << SECTOR_SHIFT); /* first read just the header, so we know how long the TOC is */ stat = cdrom_read_tocentry(drive, 0, 1, 0, (char *) &toc->hdr, diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h index 264e822eba58..04f0f310a856 100644 --- a/drivers/ide/ide-cd.h +++ b/drivers/ide/ide-cd.h @@ -21,11 +21,7 @@ /************************************************************************/ -#define SECTOR_BITS 9 -#ifndef SECTOR_SIZE -#define SECTOR_SIZE (1 << SECTOR_BITS) -#endif -#define SECTORS_PER_FRAME (CD_FRAMESIZE >> SECTOR_BITS) +#define SECTORS_PER_FRAME (CD_FRAMESIZE >> SECTOR_SHIFT) #define SECTOR_BUFFER_SIZE (CD_FRAMESIZE * 32) /* Capabilities Page size including 8 bytes of Mode Page Header */ diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 8d6375ee0fda..184e070d50a2 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -29,7 +29,6 @@ enum { * BTT instance */ ND_MAX_LANES = 256, - SECTOR_SHIFT = 9, INT_LBASIZE_ALIGNMENT = 64, NVDIMM_IO_ATOMIC = 1, }; diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h index 95fc720c1b30..e6e5ccb1e0f3 100644 --- a/drivers/scsi/gdth.h +++ b/drivers/scsi/gdth.h @@ -178,9 +178,6 @@ #define MSG_SIZE 34 /* size of message structure */ #define MSG_REQUEST 0 /* async. event: message */ -/* cacheservice defines */ -#define SECTOR_SIZE 0x200 /* always 512 bytes per sec. */ - /* DPMEM constants */ #define DPMEM_MAGIC 0xC0FFEE11 #define IC_HEADER_BYTES 48 diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 19eaf8d89368..9af3e0f430bc 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1021,6 +1021,19 @@ static inline struct request_queue *bdev_get_queue(struct block_device *bdev) return bdev->bd_disk->queue; /* this is never NULL */ } +/* + * The basic unit of block I/O is a sector. It is used in a number of contexts + * in Linux (blk, bio, genhd). The size of one sector is 512 = 2**9 + * bytes. Variables of type sector_t represent an offset or size that is a + * multiple of 512 bytes. Hence these two constants. + */ +#ifndef SECTOR_SHIFT +#define SECTOR_SHIFT 9 +#endif +#ifndef SECTOR_SIZE +#define SECTOR_SIZE (1 << SECTOR_SHIFT) +#endif + /* * blk_rq_pos() : the current sector * blk_rq_bytes() : bytes left in the entire request @@ -1048,12 +1061,12 @@ extern unsigned int blk_rq_err_bytes(const struct request *rq); static inline unsigned int blk_rq_sectors(const struct request *rq) { - return blk_rq_bytes(rq) >> 9; + return blk_rq_bytes(rq) >> SECTOR_SHIFT; } static inline unsigned int blk_rq_cur_sectors(const struct request *rq) { - return blk_rq_cur_bytes(rq) >> 9; + return blk_rq_cur_bytes(rq) >> SECTOR_SHIFT; } static inline unsigned int blk_rq_zone_no(struct request *rq) @@ -1083,7 +1096,8 @@ static inline unsigned int blk_queue_get_max_sectors(struct request_queue *q, int op) { if (unlikely(op == REQ_OP_DISCARD || op == REQ_OP_SECURE_ERASE)) - return min(q->limits.max_discard_sectors, UINT_MAX >> 9); + return min(q->limits.max_discard_sectors, + UINT_MAX >> SECTOR_SHIFT); if (unlikely(op == REQ_OP_WRITE_SAME)) return q->limits.max_write_same_sectors; @@ -1395,16 +1409,21 @@ extern int blkdev_issue_zeroout(struct block_device *bdev, sector_t sector, static inline int sb_issue_discard(struct super_block *sb, sector_t block, sector_t nr_blocks, gfp_t gfp_mask, unsigned long flags) { - return blkdev_issue_discard(sb->s_bdev, block << (sb->s_blocksize_bits - 9), - nr_blocks << (sb->s_blocksize_bits - 9), + return blkdev_issue_discard(sb->s_bdev, + block << (sb->s_blocksize_bits - + SECTOR_SHIFT), + nr_blocks << (sb->s_blocksize_bits - + SECTOR_SHIFT), gfp_mask, flags); } static inline int sb_issue_zeroout(struct super_block *sb, sector_t block, sector_t nr_blocks, gfp_t gfp_mask) { return blkdev_issue_zeroout(sb->s_bdev, - block << (sb->s_blocksize_bits - 9), - nr_blocks << (sb->s_blocksize_bits - 9), + block << (sb->s_blocksize_bits - + SECTOR_SHIFT), + nr_blocks << (sb->s_blocksize_bits - + SECTOR_SHIFT), gfp_mask, 0); } @@ -1511,7 +1530,8 @@ static inline int queue_alignment_offset(struct request_queue *q) static inline int queue_limit_alignment_offset(struct queue_limits *lim, sector_t sector) { unsigned int granularity = max(lim->physical_block_size, lim->io_min); - unsigned int alignment = sector_div(sector, granularity >> 9) << 9; + unsigned int alignment = sector_div(sector, granularity >> SECTOR_SHIFT) + << SECTOR_SHIFT; return (granularity + lim->alignment_offset - alignment) % granularity; } @@ -1545,8 +1565,8 @@ static inline int queue_limit_discard_alignment(struct queue_limits *lim, sector return 0; /* Why are these in bytes, not sectors? */ - alignment = lim->discard_alignment >> 9; - granularity = lim->discard_granularity >> 9; + alignment = lim->discard_alignment >> SECTOR_SHIFT; + granularity = lim->discard_granularity >> SECTOR_SHIFT; if (!granularity) return 0; @@ -1557,7 +1577,7 @@ static inline int queue_limit_discard_alignment(struct queue_limits *lim, sector offset = (granularity + alignment - offset) % granularity; /* Turn it back into bytes, gaah */ - return offset << 9; + return offset << SECTOR_SHIFT; } static inline int bdev_discard_alignment(struct block_device *bdev) diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index da83f64952e7..4384433b50e7 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -542,8 +542,6 @@ do { \ #define DMEMIT(x...) sz += ((sz >= maxlen) ? \ 0 : scnprintf(result + sz, maxlen - sz, x)) -#define SECTOR_SHIFT 9 - /* * Definitions of return values from target end_io function. */ diff --git a/include/linux/ide.h b/include/linux/ide.h index 771989d25ef8..0acfa62b1d44 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -165,7 +165,6 @@ struct ide_io_ports { */ #define PARTN_BITS 6 /* number of minor dev bits for partitions */ #define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */ -#define SECTOR_SIZE 512 /* * Timeouts for various operations: diff --git a/include/uapi/linux/msdos_fs.h b/include/uapi/linux/msdos_fs.h index a45d0754102e..fde753735aba 100644 --- a/include/uapi/linux/msdos_fs.h +++ b/include/uapi/linux/msdos_fs.h @@ -10,7 +10,9 @@ * The MS-DOS filesystem constants/structures */ +#ifndef SECTOR_SIZE #define SECTOR_SIZE 512 /* sector size (bytes) */ +#endif #define SECTOR_BITS 9 /* log2(SECTOR_SIZE) */ #define MSDOS_DPB (MSDOS_DPS) /* dir entries per block */ #define MSDOS_DPB_BITS 4 /* log2(MSDOS_DPB) */ -- cgit v1.2.3 From 928df1880e24bcd47d6359ff86df24db3dfba3c3 Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Thu, 15 Mar 2018 16:48:51 +0100 Subject: tipc: obsolete TIPC_ZONE_SCOPE Publications for TIPC_CLUSTER_SCOPE and TIPC_ZONE_SCOPE are in all aspects handled the same way, both on the publishing node and on the receiving nodes. Despite previous ambitions to the contrary, this is never going to change, so we take the conseqeunce of this and obsolete TIPC_ZONE_SCOPE and related macros/functions. Whenever a user is doing a bind() or a sendmsg() attempt using ZONE_SCOPE we translate this internally to CLUSTER_SCOPE, while we remain compatible with users and remote nodes still using ZONE_SCOPE. Furthermore, the non-formalized scope value 0 has always been permitted for use during lookup, with the same meaning as ZONE_SCOPE/CLUSTER_SCOPE. We now permit it even as binding scope, but for compatibility reasons we choose to not change the value of TIPC_CLUSTER_SCOPE. Acked-by: Ying Xue Signed-off-by: Jon Maloy Signed-off-by: David S. Miller --- include/uapi/linux/tipc.h | 102 ++++++++++++++++++++++++---------------------- net/tipc/addr.c | 31 -------------- net/tipc/addr.h | 10 +++++ net/tipc/msg.c | 2 +- net/tipc/name_table.c | 3 +- net/tipc/net.c | 2 +- net/tipc/socket.c | 15 ++++--- 7 files changed, 77 insertions(+), 88 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/tipc.h b/include/uapi/linux/tipc.h index 14bacc7e6cef..4ac9f1f02b06 100644 --- a/include/uapi/linux/tipc.h +++ b/include/uapi/linux/tipc.h @@ -61,50 +61,6 @@ struct tipc_name_seq { __u32 upper; }; -/* TIPC Address Size, Offset, Mask specification for Z.C.N - */ -#define TIPC_NODE_BITS 12 -#define TIPC_CLUSTER_BITS 12 -#define TIPC_ZONE_BITS 8 - -#define TIPC_NODE_OFFSET 0 -#define TIPC_CLUSTER_OFFSET TIPC_NODE_BITS -#define TIPC_ZONE_OFFSET (TIPC_CLUSTER_OFFSET + TIPC_CLUSTER_BITS) - -#define TIPC_NODE_SIZE ((1UL << TIPC_NODE_BITS) - 1) -#define TIPC_CLUSTER_SIZE ((1UL << TIPC_CLUSTER_BITS) - 1) -#define TIPC_ZONE_SIZE ((1UL << TIPC_ZONE_BITS) - 1) - -#define TIPC_NODE_MASK (TIPC_NODE_SIZE << TIPC_NODE_OFFSET) -#define TIPC_CLUSTER_MASK (TIPC_CLUSTER_SIZE << TIPC_CLUSTER_OFFSET) -#define TIPC_ZONE_MASK (TIPC_ZONE_SIZE << TIPC_ZONE_OFFSET) - -#define TIPC_ZONE_CLUSTER_MASK (TIPC_ZONE_MASK | TIPC_CLUSTER_MASK) - -static inline __u32 tipc_addr(unsigned int zone, - unsigned int cluster, - unsigned int node) -{ - return (zone << TIPC_ZONE_OFFSET) | - (cluster << TIPC_CLUSTER_OFFSET) | - node; -} - -static inline unsigned int tipc_zone(__u32 addr) -{ - return addr >> TIPC_ZONE_OFFSET; -} - -static inline unsigned int tipc_cluster(__u32 addr) -{ - return (addr & TIPC_CLUSTER_MASK) >> TIPC_CLUSTER_OFFSET; -} - -static inline unsigned int tipc_node(__u32 addr) -{ - return addr & TIPC_NODE_MASK; -} - /* * Application-accessible port name types */ @@ -117,9 +73,10 @@ static inline unsigned int tipc_node(__u32 addr) /* * Publication scopes when binding port names and port name sequences */ -#define TIPC_ZONE_SCOPE 1 -#define TIPC_CLUSTER_SCOPE 2 -#define TIPC_NODE_SCOPE 3 +enum tipc_scope { + TIPC_CLUSTER_SCOPE = 2, /* 0 can also be used */ + TIPC_NODE_SCOPE = 3 +}; /* * Limiting values for messages @@ -243,7 +200,7 @@ struct sockaddr_tipc { struct tipc_group_req { __u32 type; /* group id */ __u32 instance; /* member id */ - __u32 scope; /* zone/cluster/node */ + __u32 scope; /* cluster/node */ __u32 flags; }; @@ -268,4 +225,53 @@ struct tipc_sioc_ln_req { __u32 bearer_id; char linkname[TIPC_MAX_LINK_NAME]; }; + + +/* The macros and functions below are deprecated: + */ + +#define TIPC_ZONE_SCOPE 1 + +#define TIPC_NODE_BITS 12 +#define TIPC_CLUSTER_BITS 12 +#define TIPC_ZONE_BITS 8 + +#define TIPC_NODE_OFFSET 0 +#define TIPC_CLUSTER_OFFSET TIPC_NODE_BITS +#define TIPC_ZONE_OFFSET (TIPC_CLUSTER_OFFSET + TIPC_CLUSTER_BITS) + +#define TIPC_NODE_SIZE ((1UL << TIPC_NODE_BITS) - 1) +#define TIPC_CLUSTER_SIZE ((1UL << TIPC_CLUSTER_BITS) - 1) +#define TIPC_ZONE_SIZE ((1UL << TIPC_ZONE_BITS) - 1) + +#define TIPC_NODE_MASK (TIPC_NODE_SIZE << TIPC_NODE_OFFSET) +#define TIPC_CLUSTER_MASK (TIPC_CLUSTER_SIZE << TIPC_CLUSTER_OFFSET) +#define TIPC_ZONE_MASK (TIPC_ZONE_SIZE << TIPC_ZONE_OFFSET) + +#define TIPC_ZONE_CLUSTER_MASK (TIPC_ZONE_MASK | TIPC_CLUSTER_MASK) + +static inline __u32 tipc_addr(unsigned int zone, + unsigned int cluster, + unsigned int node) +{ + return (zone << TIPC_ZONE_OFFSET) | + (cluster << TIPC_CLUSTER_OFFSET) | + node; +} + +static inline unsigned int tipc_zone(__u32 addr) +{ + return addr >> TIPC_ZONE_OFFSET; +} + +static inline unsigned int tipc_cluster(__u32 addr) +{ + return (addr & TIPC_CLUSTER_MASK) >> TIPC_CLUSTER_OFFSET; +} + +static inline unsigned int tipc_node(__u32 addr) +{ + return addr & TIPC_NODE_MASK; +} + #endif diff --git a/net/tipc/addr.c b/net/tipc/addr.c index 48fd3b5a73fb..97cd857d7f43 100644 --- a/net/tipc/addr.c +++ b/net/tipc/addr.c @@ -63,23 +63,6 @@ int in_own_node(struct net *net, u32 addr) return (addr == tn->own_addr) || !addr; } -/** - * addr_domain - convert 2-bit scope value to equivalent message lookup domain - * - * Needed when address of a named message must be looked up a second time - * after a network hop. - */ -u32 addr_domain(struct net *net, u32 sc) -{ - struct tipc_net *tn = net_generic(net, tipc_net_id); - - if (likely(sc == TIPC_NODE_SCOPE)) - return tn->own_addr; - if (sc == TIPC_CLUSTER_SCOPE) - return tipc_cluster_mask(tn->own_addr); - return tipc_zone_mask(tn->own_addr); -} - /** * tipc_addr_domain_valid - validates a network domain address * @@ -124,20 +107,6 @@ int tipc_in_scope(u32 domain, u32 addr) return 0; } -/** - * tipc_addr_scope - convert message lookup domain to a 2-bit scope value - */ -int tipc_addr_scope(u32 domain) -{ - if (likely(!domain)) - return TIPC_ZONE_SCOPE; - if (tipc_node(domain)) - return TIPC_NODE_SCOPE; - if (tipc_cluster(domain)) - return TIPC_CLUSTER_SCOPE; - return TIPC_ZONE_SCOPE; -} - char *tipc_addr_string_fill(char *string, u32 addr) { snprintf(string, 16, "<%u.%u.%u>", diff --git a/net/tipc/addr.h b/net/tipc/addr.h index bebb347803ce..2ecf5a5d40dd 100644 --- a/net/tipc/addr.h +++ b/net/tipc/addr.h @@ -60,6 +60,16 @@ static inline u32 tipc_cluster_mask(u32 addr) return addr & TIPC_ZONE_CLUSTER_MASK; } +static inline int tipc_node2scope(u32 node) +{ + return node ? TIPC_NODE_SCOPE : TIPC_CLUSTER_SCOPE; +} + +static inline int tipc_scope2node(struct net *net, int sc) +{ + return sc != TIPC_NODE_SCOPE ? 0 : tipc_own_addr(net); +} + u32 tipc_own_addr(struct net *net); int in_own_cluster(struct net *net, u32 addr); int in_own_cluster_exact(struct net *net, u32 addr); diff --git a/net/tipc/msg.c b/net/tipc/msg.c index 4e1c6f6450bb..b6c45dccba3d 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -580,7 +580,7 @@ bool tipc_msg_lookup_dest(struct net *net, struct sk_buff *skb, int *err) msg = buf_msg(skb); if (msg_reroute_cnt(msg)) return false; - dnode = addr_domain(net, msg_lookup_scope(msg)); + dnode = tipc_scope2node(net, msg_lookup_scope(msg)); dport = tipc_nametbl_translate(net, msg_nametype(msg), msg_nameinst(msg), &dnode); if (!dport) diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index e01c9c691ba2..6772390fcb00 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -473,8 +473,7 @@ struct publication *tipc_nametbl_insert_publ(struct net *net, u32 type, struct name_seq *seq = nametbl_find_seq(net, type); int index = hash(type); - if ((scope < TIPC_ZONE_SCOPE) || (scope > TIPC_NODE_SCOPE) || - (lower > upper)) { + if (scope > TIPC_NODE_SCOPE || lower > upper) { pr_debug("Failed to publish illegal {%u,%u,%u} with scope %u\n", type, lower, upper, scope); return NULL; diff --git a/net/tipc/net.c b/net/tipc/net.c index 1a2fde0d6f61..5c4c4405b78e 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -118,7 +118,7 @@ int tipc_net_start(struct net *net, u32 addr) tipc_sk_reinit(net); tipc_nametbl_publish(net, TIPC_CFG_SRV, tn->own_addr, tn->own_addr, - TIPC_ZONE_SCOPE, 0, tn->own_addr); + TIPC_CLUSTER_SCOPE, 0, tn->own_addr); pr_info("Started in network mode\n"); pr_info("Own node address %s, network identity %u\n", diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 8b04e601311c..910d3827f499 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -644,7 +644,7 @@ static int tipc_bind(struct socket *sock, struct sockaddr *uaddr, goto exit; } - res = (addr->scope > 0) ? + res = (addr->scope >= 0) ? tipc_sk_publish(tsk, addr->scope, &addr->addr.nameseq) : tipc_sk_withdraw(tsk, -addr->scope, &addr->addr.nameseq); exit: @@ -1280,8 +1280,8 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) struct tipc_msg *hdr = &tsk->phdr; struct tipc_name_seq *seq; struct sk_buff_head pkts; - u32 type, inst, domain; u32 dnode, dport; + u32 type, inst; int mtu, rc; if (unlikely(dlen > TIPC_MAX_USER_MSG_SIZE)) @@ -1332,13 +1332,12 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) if (dest->addrtype == TIPC_ADDR_NAME) { type = dest->addr.name.name.type; inst = dest->addr.name.name.instance; - domain = dest->addr.name.domain; - dnode = domain; + dnode = dest->addr.name.domain; msg_set_type(hdr, TIPC_NAMED_MSG); msg_set_hdr_sz(hdr, NAMED_H_SIZE); msg_set_nametype(hdr, type); msg_set_nameinst(hdr, inst); - msg_set_lookup_scope(hdr, tipc_addr_scope(domain)); + msg_set_lookup_scope(hdr, tipc_node2scope(dnode)); dport = tipc_nametbl_translate(net, type, inst, &dnode); msg_set_destnode(hdr, dnode); msg_set_destport(hdr, dport); @@ -2592,6 +2591,9 @@ static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, struct publication *publ; u32 key; + if (scope != TIPC_NODE_SCOPE) + scope = TIPC_CLUSTER_SCOPE; + if (tipc_sk_connected(sk)) return -EINVAL; key = tsk->portid + tsk->pub_count + 1; @@ -2617,6 +2619,9 @@ static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope, struct publication *safe; int rc = -EINVAL; + if (scope != TIPC_NODE_SCOPE) + scope = TIPC_CLUSTER_SCOPE; + list_for_each_entry_safe(publ, safe, &tsk->publications, pport_list) { if (seq) { if (publ->scope != scope) -- cgit v1.2.3 From a84d1169164b274f13b97a23ff235c000efe3b49 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 15 Mar 2018 17:12:40 +0100 Subject: y2038: Introduce struct __kernel_old_timeval Dealing with 'struct timeval' users in the y2038 series is a bit tricky: We have two definitions of timeval that are visible to user space, one comes from glibc (or some other C library), the other comes from linux/time.h. The kernel copy is what we want to be used for a number of structures defined by the kernel itself, e.g. elf_prstatus (used it core dumps), sysinfo and rusage (used in system calls). These generally tend to be used for passing time intervals rather than absolute (epoch-based) times, so they do not suffer from the y2038 overflow. Some of them could be changed to use 64-bit timestamps by creating new system calls, others like the core files cannot easily be changed. An application using these interfaces likely also uses gettimeofday() or other interfaces that use absolute times, and pass 'struct timeval' pointers directly into kernel interfaces, so glibc must redefine their timeval based on a 64-bit time_t when they introduce their y2038-safe interfaces. The only reasonable way forward I see is to remove the 'timeval' definion from the kernel's uapi headers, and change the interfaces that we do not want to (or cannot) duplicate for 64-bit times to use a new __kernel_old_timeval definition instead. This type should be avoided for all new interfaces (those can use 64-bit nanoseconds, or the 64-bit version of timespec instead), and should be used with great care when converting existing interfaces from timeval, to be sure they don't suffer from the y2038 overflow, and only with consensus for the particular user that using __kernel_old_timeval is better than moving to a 64-bit based interface. The structure name is intentionally chosen to not conflict with user space types, and to be ugly enough to discourage its use. Note that ioctl based interfaces that pass a bare 'timeval' pointer cannot change to '__kernel_old_timeval' because the user space source code refers to 'timeval' instead, and we don't want to modify the user space sources if possible. However, any application that relies on a structure to contain an embedded 'timeval' (e.g. by passing a pointer to the member into a function call that expects a timeval pointer) is broken when that structure gets converted to __kernel_old_timeval. I don't see any way around that, and we have to rely on the compiler to produce a warning or compile failure that will alert users when they recompile their sources against a new libc. Signed-off-by: Arnd Bergmann Signed-off-by: Thomas Gleixner Cc: Stephen Boyd Cc: John Stultz Cc: Al Viro Link: https://lkml.kernel.org/r/20180315161739.576085-1-arnd@arndb.de --- include/linux/time32.h | 1 + include/uapi/linux/time.h | 12 ++++++++++++ kernel/time/time.c | 12 ++++++++++++ 3 files changed, 25 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/linux/time32.h b/include/linux/time32.h index 65b1de25198d..d2bcd4377b56 100644 --- a/include/linux/time32.h +++ b/include/linux/time32.h @@ -217,5 +217,6 @@ static inline s64 timeval_to_ns(const struct timeval *tv) * Returns the timeval representation of the nsec parameter. */ extern struct timeval ns_to_timeval(const s64 nsec); +extern struct __kernel_old_timeval ns_to_kernel_old_timeval(s64 nsec); #endif diff --git a/include/uapi/linux/time.h b/include/uapi/linux/time.h index 61a187df8da2..16a296612ba4 100644 --- a/include/uapi/linux/time.h +++ b/include/uapi/linux/time.h @@ -42,6 +42,18 @@ struct itimerval { struct timeval it_value; /* current value */ }; +/* + * legacy timeval structure, only embedded in structures that + * traditionally used 'timeval' to pass time intervals (not absolute + * times). Do not add new users. If user space fails to compile + * here, this is probably because it is not y2038 safe and needs to + * be changed to use another interface. + */ +struct __kernel_old_timeval { + __kernel_long_t tv_sec; + __kernel_long_t tv_usec; +}; + /* * The IDs of the various system clocks (for POSIX.1b interval timers): */ diff --git a/kernel/time/time.c b/kernel/time/time.c index bd4e6c7dd689..3044d48ebe56 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -488,6 +488,18 @@ struct timeval ns_to_timeval(const s64 nsec) } EXPORT_SYMBOL(ns_to_timeval); +struct __kernel_old_timeval ns_to_kernel_old_timeval(const s64 nsec) +{ + struct timespec64 ts = ns_to_timespec64(nsec); + struct __kernel_old_timeval tv; + + tv.tv_sec = ts.tv_sec; + tv.tv_usec = (suseconds_t)ts.tv_nsec / 1000; + + return tv; +} +EXPORT_SYMBOL(ns_to_kernel_old_timeval); + /** * set_normalized_timespec - set timespec sec and nsec parts and normalize * -- cgit v1.2.3 From a6618f4aedb2b60932d766bd82ae7ce866e842aa Mon Sep 17 00:00:00 2001 From: Kirill Marinushkin Date: Mon, 19 Mar 2018 07:11:08 +0100 Subject: ALSA: usb-audio: Fix parsing descriptor of UAC2 processing unit Currently, the offsets in the UAC2 processing unit descriptor are calculated incorrectly. It causes an issue when connecting the device which provides such a feature: ~~~~ [84126.724420] usb 1-1.3.1: invalid Processing Unit descriptor (id 18) ~~~~ After this patch is applied, the UAC2 processing unit inits w/o this error. Fixes: 23caaf19b11e ("ALSA: usb-mixer: Add support for Audio Class v2.0") Signed-off-by: Kirill Marinushkin Cc: Signed-off-by: Takashi Iwai --- include/uapi/linux/usb/audio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h index 17a022c5b414..da3315ed1bcd 100644 --- a/include/uapi/linux/usb/audio.h +++ b/include/uapi/linux/usb/audio.h @@ -370,7 +370,7 @@ static inline __u8 uac_processing_unit_bControlSize(struct uac_processing_unit_d { return (protocol == UAC_VERSION_1) ? desc->baSourceID[desc->bNrInPins + 4] : - desc->baSourceID[desc->bNrInPins + 6]; + 2; /* in UAC2, this value is constant */ } static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_descriptor *desc, @@ -378,7 +378,7 @@ static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_de { return (protocol == UAC_VERSION_1) ? &desc->baSourceID[desc->bNrInPins + 5] : - &desc->baSourceID[desc->bNrInPins + 7]; + &desc->baSourceID[desc->bNrInPins + 6]; } static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_descriptor *desc, -- cgit v1.2.3 From 4f738adba30a7cfc006f605707e7aee847ffefa0 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Sun, 18 Mar 2018 12:57:10 -0700 Subject: bpf: create tcp_bpf_ulp allowing BPF to monitor socket TX/RX data This implements a BPF ULP layer to allow policy enforcement and monitoring at the socket layer. In order to support this a new program type BPF_PROG_TYPE_SK_MSG is used to run the policy at the sendmsg/sendpage hook. To attach the policy to sockets a sockmap is used with a new program attach type BPF_SK_MSG_VERDICT. Similar to previous sockmap usages when a sock is added to a sockmap, via a map update, if the map contains a BPF_SK_MSG_VERDICT program type attached then the BPF ULP layer is created on the socket and the attached BPF_PROG_TYPE_SK_MSG program is run for every msg in sendmsg case and page/offset in sendpage case. BPF_PROG_TYPE_SK_MSG Semantics/API: BPF_PROG_TYPE_SK_MSG supports only two return codes SK_PASS and SK_DROP. Returning SK_DROP free's the copied data in the sendmsg case and in the sendpage case leaves the data untouched. Both cases return -EACESS to the user. Returning SK_PASS will allow the msg to be sent. In the sendmsg case data is copied into kernel space buffers before running the BPF program. The kernel space buffers are stored in a scatterlist object where each element is a kernel memory buffer. Some effort is made to coalesce data from the sendmsg call here. For example a sendmsg call with many one byte iov entries will likely be pushed into a single entry. The BPF program is run with data pointers (start/end) pointing to the first sg element. In the sendpage case data is not copied. We opt not to copy the data by default here, because the BPF infrastructure does not know what bytes will be needed nor when they will be needed. So copying all bytes may be wasteful. Because of this the initial start/end data pointers are (0,0). Meaning no data can be read or written. This avoids reading data that may be modified by the user. A new helper is added later in this series if reading and writing the data is needed. The helper call will do a copy by default so that the page is exclusively owned by the BPF call. The verdict from the BPF_PROG_TYPE_SK_MSG applies to the entire msg in the sendmsg() case and the entire page/offset in the sendpage case. This avoids ambiguity on how to handle mixed return codes in the sendmsg case. Again a helper is added later in the series if a verdict needs to apply to multiple system calls and/or only a subpart of the currently being processed message. The helper msg_redirect_map() can be used to select the socket to send the data on. This is used similar to existing redirect use cases. This allows policy to redirect msgs. Pseudo code simple example: The basic logic to attach a program to a socket is as follows, // load the programs bpf_prog_load(SOCKMAP_TCP_MSG_PROG, BPF_PROG_TYPE_SK_MSG, &obj, &msg_prog); // lookup the sockmap bpf_map_msg = bpf_object__find_map_by_name(obj, "my_sock_map"); // get fd for sockmap map_fd_msg = bpf_map__fd(bpf_map_msg); // attach program to sockmap bpf_prog_attach(msg_prog, map_fd_msg, BPF_SK_MSG_VERDICT, 0); Adding sockets to the map is done in the normal way, // Add a socket 'fd' to sockmap at location 'i' bpf_map_update_elem(map_fd_msg, &i, fd, BPF_ANY); After the above any socket attached to "my_sock_map", in this case 'fd', will run the BPF msg verdict program (msg_prog) on every sendmsg and sendpage system call. For a complete example see BPF selftests or sockmap samples. Implementation notes: It seemed the simplest, to me at least, to use a refcnt to ensure psock is not lost across the sendmsg copy into the sg, the bpf program running on the data in sg_data, and the final pass to the TCP stack. Some performance testing may show a better method to do this and avoid the refcnt cost, but for now use the simpler method. Another item that will come after basic support is in place is supporting MSG_MORE flag. At the moment we call sendpages even if the MSG_MORE flag is set. An enhancement would be to collect the pages into a larger scatterlist and pass down the stack. Notice that bpf_tcp_sendmsg() could support this with some additional state saved across sendmsg calls. I built the code to support this without having to do refactoring work. Other features TBD include ZEROCOPY and the TCP_RECV_QUEUE/TCP_NO_QUEUE support. This will follow initial series shortly. Future work could improve size limits on the scatterlist rings used here. Currently, we use MAX_SKB_FRAGS simply because this was being used already in the TLS case. Future work could extend the kernel sk APIs to tune this depending on workload. This is a trade-off between memory usage and throughput performance. Signed-off-by: John Fastabend Acked-by: David S. Miller Acked-by: Alexei Starovoitov Signed-off-by: Daniel Borkmann --- include/linux/bpf.h | 1 + include/linux/bpf_types.h | 1 + include/linux/filter.h | 17 ++ include/uapi/linux/bpf.h | 22 +- kernel/bpf/sockmap.c | 712 +++++++++++++++++++++++++++++++++++++++++++++- kernel/bpf/syscall.c | 14 +- kernel/bpf/verifier.c | 5 +- net/core/filter.c | 106 +++++++ 8 files changed, 857 insertions(+), 21 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 66df387106de..819229c80eca 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -21,6 +21,7 @@ struct bpf_verifier_env; struct perf_event; struct bpf_prog; struct bpf_map; +struct sock; /* map is generic key/value storage optionally accesible by eBPF programs */ struct bpf_map_ops { diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 19b8349a3809..5e2e8a49fb21 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -13,6 +13,7 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_OUT, lwt_inout) BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_XMIT, lwt_xmit) BPF_PROG_TYPE(BPF_PROG_TYPE_SOCK_OPS, sock_ops) BPF_PROG_TYPE(BPF_PROG_TYPE_SK_SKB, sk_skb) +BPF_PROG_TYPE(BPF_PROG_TYPE_SK_MSG, sk_msg) #endif #ifdef CONFIG_BPF_EVENTS BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe) diff --git a/include/linux/filter.h b/include/linux/filter.h index fdb691b520c0..109d05ccea9a 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -507,6 +507,22 @@ struct xdp_buff { struct xdp_rxq_info *rxq; }; +struct sk_msg_buff { + void *data; + void *data_end; + __u32 apply_bytes; + __u32 cork_bytes; + int sg_copybreak; + int sg_start; + int sg_curr; + int sg_end; + struct scatterlist sg_data[MAX_SKB_FRAGS]; + bool sg_copy[MAX_SKB_FRAGS]; + __u32 key; + __u32 flags; + struct bpf_map *map; +}; + /* Compute the linear packet data range [data, data_end) which * will be accessed by various program types (cls_bpf, act_bpf, * lwt, ...). Subsystems allowing direct data access must (!) @@ -771,6 +787,7 @@ xdp_data_meta_unsupported(const struct xdp_buff *xdp) void bpf_warn_invalid_xdp_action(u32 act); struct sock *do_sk_redirect_map(struct sk_buff *skb); +struct sock *do_msg_redirect_map(struct sk_msg_buff *md); #ifdef CONFIG_BPF_JIT extern int bpf_jit_enable; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1e15d1724d89..ef529539abde 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -133,6 +133,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_SOCK_OPS, BPF_PROG_TYPE_SK_SKB, BPF_PROG_TYPE_CGROUP_DEVICE, + BPF_PROG_TYPE_SK_MSG, }; enum bpf_attach_type { @@ -143,6 +144,7 @@ enum bpf_attach_type { BPF_SK_SKB_STREAM_PARSER, BPF_SK_SKB_STREAM_VERDICT, BPF_CGROUP_DEVICE, + BPF_SK_MSG_VERDICT, __MAX_BPF_ATTACH_TYPE }; @@ -718,6 +720,15 @@ union bpf_attr { * int bpf_override_return(pt_regs, rc) * @pt_regs: pointer to struct pt_regs * @rc: the return value to set + * + * int bpf_msg_redirect_map(map, key, flags) + * Redirect msg to a sock in map using key as a lookup key for the + * sock in map. + * @map: pointer to sockmap + * @key: key to lookup sock in map + * @flags: reserved for future use + * Return: SK_PASS + * */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -779,7 +790,8 @@ union bpf_attr { FN(perf_prog_read_value), \ FN(getsockopt), \ FN(override_return), \ - FN(sock_ops_cb_flags_set), + FN(sock_ops_cb_flags_set), \ + FN(msg_redirect_map), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -942,6 +954,14 @@ enum sk_action { SK_PASS, }; +/* user accessible metadata for SK_MSG packet hook, new fields must + * be added to the end of this structure + */ +struct sk_msg_md { + void *data; + void *data_end; +}; + #define BPF_TAG_SIZE 8 struct bpf_prog_info { diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c index 051b224270e3..69c5bccabd22 100644 --- a/kernel/bpf/sockmap.c +++ b/kernel/bpf/sockmap.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,7 @@ struct bpf_stab { struct bpf_map map; struct sock **sock_map; + struct bpf_prog *bpf_tx_msg; struct bpf_prog *bpf_parse; struct bpf_prog *bpf_verdict; }; @@ -73,7 +75,16 @@ struct smap_psock { int save_off; struct sk_buff *save_skb; + /* datapath variables for tx_msg ULP */ + struct sock *sk_redir; + int apply_bytes; + int cork_bytes; + int sg_size; + int eval; + struct sk_msg_buff *cork; + struct strparser strp; + struct bpf_prog *bpf_tx_msg; struct bpf_prog *bpf_parse; struct bpf_prog *bpf_verdict; struct list_head maps; @@ -91,6 +102,11 @@ struct smap_psock { void (*save_write_space)(struct sock *sk); }; +static void smap_release_sock(struct smap_psock *psock, struct sock *sock); +static int bpf_tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size); +static int bpf_tcp_sendpage(struct sock *sk, struct page *page, + int offset, size_t size, int flags); + static inline struct smap_psock *smap_psock_sk(const struct sock *sk) { return rcu_dereference_sk_user_data(sk); @@ -115,27 +131,41 @@ static int bpf_tcp_init(struct sock *sk) psock->save_close = sk->sk_prot->close; psock->sk_proto = sk->sk_prot; + + if (psock->bpf_tx_msg) { + tcp_bpf_proto.sendmsg = bpf_tcp_sendmsg; + tcp_bpf_proto.sendpage = bpf_tcp_sendpage; + } + sk->sk_prot = &tcp_bpf_proto; rcu_read_unlock(); return 0; } +static void smap_release_sock(struct smap_psock *psock, struct sock *sock); +static int free_start_sg(struct sock *sk, struct sk_msg_buff *md); + static void bpf_tcp_release(struct sock *sk) { struct smap_psock *psock; rcu_read_lock(); psock = smap_psock_sk(sk); + if (unlikely(!psock)) + goto out; - if (likely(psock)) { - sk->sk_prot = psock->sk_proto; - psock->sk_proto = NULL; + if (psock->cork) { + free_start_sg(psock->sock, psock->cork); + kfree(psock->cork); + psock->cork = NULL; } + + sk->sk_prot = psock->sk_proto; + psock->sk_proto = NULL; +out: rcu_read_unlock(); } -static void smap_release_sock(struct smap_psock *psock, struct sock *sock); - static void bpf_tcp_close(struct sock *sk, long timeout) { void (*close_fun)(struct sock *sk, long timeout); @@ -174,6 +204,7 @@ enum __sk_action { __SK_DROP = 0, __SK_PASS, __SK_REDIRECT, + __SK_NONE, }; static struct tcp_ulp_ops bpf_tcp_ulp_ops __read_mostly = { @@ -185,10 +216,621 @@ static struct tcp_ulp_ops bpf_tcp_ulp_ops __read_mostly = { .release = bpf_tcp_release, }; +static int memcopy_from_iter(struct sock *sk, + struct sk_msg_buff *md, + struct iov_iter *from, int bytes) +{ + struct scatterlist *sg = md->sg_data; + int i = md->sg_curr, rc = -ENOSPC; + + do { + int copy; + char *to; + + if (md->sg_copybreak >= sg[i].length) { + md->sg_copybreak = 0; + + if (++i == MAX_SKB_FRAGS) + i = 0; + + if (i == md->sg_end) + break; + } + + copy = sg[i].length - md->sg_copybreak; + to = sg_virt(&sg[i]) + md->sg_copybreak; + md->sg_copybreak += copy; + + if (sk->sk_route_caps & NETIF_F_NOCACHE_COPY) + rc = copy_from_iter_nocache(to, copy, from); + else + rc = copy_from_iter(to, copy, from); + + if (rc != copy) { + rc = -EFAULT; + goto out; + } + + bytes -= copy; + if (!bytes) + break; + + md->sg_copybreak = 0; + if (++i == MAX_SKB_FRAGS) + i = 0; + } while (i != md->sg_end); +out: + md->sg_curr = i; + return rc; +} + +static int bpf_tcp_push(struct sock *sk, int apply_bytes, + struct sk_msg_buff *md, + int flags, bool uncharge) +{ + bool apply = apply_bytes; + struct scatterlist *sg; + int offset, ret = 0; + struct page *p; + size_t size; + + while (1) { + sg = md->sg_data + md->sg_start; + size = (apply && apply_bytes < sg->length) ? + apply_bytes : sg->length; + offset = sg->offset; + + tcp_rate_check_app_limited(sk); + p = sg_page(sg); +retry: + ret = do_tcp_sendpages(sk, p, offset, size, flags); + if (ret != size) { + if (ret > 0) { + if (apply) + apply_bytes -= ret; + size -= ret; + offset += ret; + if (uncharge) + sk_mem_uncharge(sk, ret); + goto retry; + } + + sg->length = size; + sg->offset = offset; + return ret; + } + + if (apply) + apply_bytes -= ret; + sg->offset += ret; + sg->length -= ret; + if (uncharge) + sk_mem_uncharge(sk, ret); + + if (!sg->length) { + put_page(p); + md->sg_start++; + if (md->sg_start == MAX_SKB_FRAGS) + md->sg_start = 0; + memset(sg, 0, sizeof(*sg)); + + if (md->sg_start == md->sg_end) + break; + } + + if (apply && !apply_bytes) + break; + } + return 0; +} + +static inline void bpf_compute_data_pointers_sg(struct sk_msg_buff *md) +{ + struct scatterlist *sg = md->sg_data + md->sg_start; + + if (md->sg_copy[md->sg_start]) { + md->data = md->data_end = 0; + } else { + md->data = sg_virt(sg); + md->data_end = md->data + sg->length; + } +} + +static void return_mem_sg(struct sock *sk, int bytes, struct sk_msg_buff *md) +{ + struct scatterlist *sg = md->sg_data; + int i = md->sg_start; + + do { + int uncharge = (bytes < sg[i].length) ? bytes : sg[i].length; + + sk_mem_uncharge(sk, uncharge); + bytes -= uncharge; + if (!bytes) + break; + i++; + if (i == MAX_SKB_FRAGS) + i = 0; + } while (i != md->sg_end); +} + +static void free_bytes_sg(struct sock *sk, int bytes, struct sk_msg_buff *md) +{ + struct scatterlist *sg = md->sg_data; + int i = md->sg_start, free; + + while (bytes && sg[i].length) { + free = sg[i].length; + if (bytes < free) { + sg[i].length -= bytes; + sg[i].offset += bytes; + sk_mem_uncharge(sk, bytes); + break; + } + + sk_mem_uncharge(sk, sg[i].length); + put_page(sg_page(&sg[i])); + bytes -= sg[i].length; + sg[i].length = 0; + sg[i].page_link = 0; + sg[i].offset = 0; + i++; + + if (i == MAX_SKB_FRAGS) + i = 0; + } +} + +static int free_sg(struct sock *sk, int start, struct sk_msg_buff *md) +{ + struct scatterlist *sg = md->sg_data; + int i = start, free = 0; + + while (sg[i].length) { + free += sg[i].length; + sk_mem_uncharge(sk, sg[i].length); + put_page(sg_page(&sg[i])); + sg[i].length = 0; + sg[i].page_link = 0; + sg[i].offset = 0; + i++; + + if (i == MAX_SKB_FRAGS) + i = 0; + } + + return free; +} + +static int free_start_sg(struct sock *sk, struct sk_msg_buff *md) +{ + int free = free_sg(sk, md->sg_start, md); + + md->sg_start = md->sg_end; + return free; +} + +static int free_curr_sg(struct sock *sk, struct sk_msg_buff *md) +{ + return free_sg(sk, md->sg_curr, md); +} + +static int bpf_map_msg_verdict(int _rc, struct sk_msg_buff *md) +{ + return ((_rc == SK_PASS) ? + (md->map ? __SK_REDIRECT : __SK_PASS) : + __SK_DROP); +} + +static unsigned int smap_do_tx_msg(struct sock *sk, + struct smap_psock *psock, + struct sk_msg_buff *md) +{ + struct bpf_prog *prog; + unsigned int rc, _rc; + + preempt_disable(); + rcu_read_lock(); + + /* If the policy was removed mid-send then default to 'accept' */ + prog = READ_ONCE(psock->bpf_tx_msg); + if (unlikely(!prog)) { + _rc = SK_PASS; + goto verdict; + } + + bpf_compute_data_pointers_sg(md); + rc = (*prog->bpf_func)(md, prog->insnsi); + psock->apply_bytes = md->apply_bytes; + + /* Moving return codes from UAPI namespace into internal namespace */ + _rc = bpf_map_msg_verdict(rc, md); + + /* The psock has a refcount on the sock but not on the map and because + * we need to drop rcu read lock here its possible the map could be + * removed between here and when we need it to execute the sock + * redirect. So do the map lookup now for future use. + */ + if (_rc == __SK_REDIRECT) { + if (psock->sk_redir) + sock_put(psock->sk_redir); + psock->sk_redir = do_msg_redirect_map(md); + if (!psock->sk_redir) { + _rc = __SK_DROP; + goto verdict; + } + sock_hold(psock->sk_redir); + } +verdict: + rcu_read_unlock(); + preempt_enable(); + + return _rc; +} + +static int bpf_tcp_sendmsg_do_redirect(struct sock *sk, int send, + struct sk_msg_buff *md, + int flags) +{ + struct smap_psock *psock; + struct scatterlist *sg; + int i, err, free = 0; + + sg = md->sg_data; + + rcu_read_lock(); + psock = smap_psock_sk(sk); + if (unlikely(!psock)) + goto out_rcu; + + if (!refcount_inc_not_zero(&psock->refcnt)) + goto out_rcu; + + rcu_read_unlock(); + lock_sock(sk); + err = bpf_tcp_push(sk, send, md, flags, false); + release_sock(sk); + smap_release_sock(psock, sk); + if (unlikely(err)) + goto out; + return 0; +out_rcu: + rcu_read_unlock(); +out: + i = md->sg_start; + while (sg[i].length) { + free += sg[i].length; + put_page(sg_page(&sg[i])); + sg[i].length = 0; + i++; + if (i == MAX_SKB_FRAGS) + i = 0; + } + return free; +} + +static inline void bpf_md_init(struct smap_psock *psock) +{ + if (!psock->apply_bytes) { + psock->eval = __SK_NONE; + if (psock->sk_redir) { + sock_put(psock->sk_redir); + psock->sk_redir = NULL; + } + } +} + +static void apply_bytes_dec(struct smap_psock *psock, int i) +{ + if (psock->apply_bytes) { + if (psock->apply_bytes < i) + psock->apply_bytes = 0; + else + psock->apply_bytes -= i; + } +} + +static int bpf_exec_tx_verdict(struct smap_psock *psock, + struct sk_msg_buff *m, + struct sock *sk, + int *copied, int flags) +{ + bool cork = false, enospc = (m->sg_start == m->sg_end); + struct sock *redir; + int err = 0; + int send; + +more_data: + if (psock->eval == __SK_NONE) + psock->eval = smap_do_tx_msg(sk, psock, m); + + if (m->cork_bytes && + m->cork_bytes > psock->sg_size && !enospc) { + psock->cork_bytes = m->cork_bytes - psock->sg_size; + if (!psock->cork) { + psock->cork = kcalloc(1, + sizeof(struct sk_msg_buff), + GFP_ATOMIC | __GFP_NOWARN); + + if (!psock->cork) { + err = -ENOMEM; + goto out_err; + } + } + memcpy(psock->cork, m, sizeof(*m)); + goto out_err; + } + + send = psock->sg_size; + if (psock->apply_bytes && psock->apply_bytes < send) + send = psock->apply_bytes; + + switch (psock->eval) { + case __SK_PASS: + err = bpf_tcp_push(sk, send, m, flags, true); + if (unlikely(err)) { + *copied -= free_start_sg(sk, m); + break; + } + + apply_bytes_dec(psock, send); + psock->sg_size -= send; + break; + case __SK_REDIRECT: + redir = psock->sk_redir; + apply_bytes_dec(psock, send); + + if (psock->cork) { + cork = true; + psock->cork = NULL; + } + + return_mem_sg(sk, send, m); + release_sock(sk); + + err = bpf_tcp_sendmsg_do_redirect(redir, send, m, flags); + lock_sock(sk); + + if (cork) { + free_start_sg(sk, m); + kfree(m); + m = NULL; + } + if (unlikely(err)) + *copied -= err; + else + psock->sg_size -= send; + break; + case __SK_DROP: + default: + free_bytes_sg(sk, send, m); + apply_bytes_dec(psock, send); + *copied -= send; + psock->sg_size -= send; + err = -EACCES; + break; + } + + if (likely(!err)) { + bpf_md_init(psock); + if (m && + m->sg_data[m->sg_start].page_link && + m->sg_data[m->sg_start].length) + goto more_data; + } + +out_err: + return err; +} + +static int bpf_tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) +{ + int flags = msg->msg_flags | MSG_NO_SHARED_FRAGS; + struct sk_msg_buff md = {0}; + unsigned int sg_copy = 0; + struct smap_psock *psock; + int copied = 0, err = 0; + struct scatterlist *sg; + long timeo; + + /* Its possible a sock event or user removed the psock _but_ the ops + * have not been reprogrammed yet so we get here. In this case fallback + * to tcp_sendmsg. Note this only works because we _only_ ever allow + * a single ULP there is no hierarchy here. + */ + rcu_read_lock(); + psock = smap_psock_sk(sk); + if (unlikely(!psock)) { + rcu_read_unlock(); + return tcp_sendmsg(sk, msg, size); + } + + /* Increment the psock refcnt to ensure its not released while sending a + * message. Required because sk lookup and bpf programs are used in + * separate rcu critical sections. Its OK if we lose the map entry + * but we can't lose the sock reference. + */ + if (!refcount_inc_not_zero(&psock->refcnt)) { + rcu_read_unlock(); + return tcp_sendmsg(sk, msg, size); + } + + sg = md.sg_data; + sg_init_table(sg, MAX_SKB_FRAGS); + rcu_read_unlock(); + + lock_sock(sk); + timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); + + while (msg_data_left(msg)) { + struct sk_msg_buff *m; + bool enospc = false; + int copy; + + if (sk->sk_err) { + err = sk->sk_err; + goto out_err; + } + + copy = msg_data_left(msg); + if (!sk_stream_memory_free(sk)) + goto wait_for_sndbuf; + + m = psock->cork_bytes ? psock->cork : &md; + m->sg_curr = m->sg_copybreak ? m->sg_curr : m->sg_end; + err = sk_alloc_sg(sk, copy, m->sg_data, + m->sg_start, &m->sg_end, &sg_copy, + m->sg_end - 1); + if (err) { + if (err != -ENOSPC) + goto wait_for_memory; + enospc = true; + copy = sg_copy; + } + + err = memcopy_from_iter(sk, m, &msg->msg_iter, copy); + if (err < 0) { + free_curr_sg(sk, m); + goto out_err; + } + + psock->sg_size += copy; + copied += copy; + sg_copy = 0; + + /* When bytes are being corked skip running BPF program and + * applying verdict unless there is no more buffer space. In + * the ENOSPC case simply run BPF prorgram with currently + * accumulated data. We don't have much choice at this point + * we could try extending the page frags or chaining complex + * frags but even in these cases _eventually_ we will hit an + * OOM scenario. More complex recovery schemes may be + * implemented in the future, but BPF programs must handle + * the case where apply_cork requests are not honored. The + * canonical method to verify this is to check data length. + */ + if (psock->cork_bytes) { + if (copy > psock->cork_bytes) + psock->cork_bytes = 0; + else + psock->cork_bytes -= copy; + + if (psock->cork_bytes && !enospc) + goto out_cork; + + /* All cork bytes accounted for re-run filter */ + psock->eval = __SK_NONE; + psock->cork_bytes = 0; + } + + err = bpf_exec_tx_verdict(psock, m, sk, &copied, flags); + if (unlikely(err < 0)) + goto out_err; + continue; +wait_for_sndbuf: + set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); +wait_for_memory: + err = sk_stream_wait_memory(sk, &timeo); + if (err) + goto out_err; + } +out_err: + if (err < 0) + err = sk_stream_error(sk, msg->msg_flags, err); +out_cork: + release_sock(sk); + smap_release_sock(psock, sk); + return copied ? copied : err; +} + +static int bpf_tcp_sendpage(struct sock *sk, struct page *page, + int offset, size_t size, int flags) +{ + struct sk_msg_buff md = {0}, *m = NULL; + int err = 0, copied = 0; + struct smap_psock *psock; + struct scatterlist *sg; + bool enospc = false; + + rcu_read_lock(); + psock = smap_psock_sk(sk); + if (unlikely(!psock)) + goto accept; + + if (!refcount_inc_not_zero(&psock->refcnt)) + goto accept; + rcu_read_unlock(); + + lock_sock(sk); + + if (psock->cork_bytes) + m = psock->cork; + else + m = &md; + + /* Catch case where ring is full and sendpage is stalled. */ + if (unlikely(m->sg_end == m->sg_start && + m->sg_data[m->sg_end].length)) + goto out_err; + + psock->sg_size += size; + sg = &m->sg_data[m->sg_end]; + sg_set_page(sg, page, size, offset); + get_page(page); + m->sg_copy[m->sg_end] = true; + sk_mem_charge(sk, size); + m->sg_end++; + copied = size; + + if (m->sg_end == MAX_SKB_FRAGS) + m->sg_end = 0; + + if (m->sg_end == m->sg_start) + enospc = true; + + if (psock->cork_bytes) { + if (size > psock->cork_bytes) + psock->cork_bytes = 0; + else + psock->cork_bytes -= size; + + if (psock->cork_bytes && !enospc) + goto out_err; + + /* All cork bytes accounted for re-run filter */ + psock->eval = __SK_NONE; + psock->cork_bytes = 0; + } + + err = bpf_exec_tx_verdict(psock, m, sk, &copied, flags); +out_err: + release_sock(sk); + smap_release_sock(psock, sk); + return copied ? copied : err; +accept: + rcu_read_unlock(); + return tcp_sendpage(sk, page, offset, size, flags); +} + +static void bpf_tcp_msg_add(struct smap_psock *psock, + struct sock *sk, + struct bpf_prog *tx_msg) +{ + struct bpf_prog *orig_tx_msg; + + orig_tx_msg = xchg(&psock->bpf_tx_msg, tx_msg); + if (orig_tx_msg) + bpf_prog_put(orig_tx_msg); +} + static int bpf_tcp_ulp_register(void) { tcp_bpf_proto = tcp_prot; tcp_bpf_proto.close = bpf_tcp_close; + /* Once BPF TX ULP is registered it is never unregistered. It + * will be in the ULP list for the lifetime of the system. Doing + * duplicate registers is not a problem. + */ return tcp_register_ulp(&bpf_tcp_ulp_ops); } @@ -412,7 +1054,6 @@ static int smap_parse_func_strparser(struct strparser *strp, return rc; } - static int smap_read_sock_done(struct strparser *strp, int err) { return err; @@ -482,12 +1123,22 @@ static void smap_gc_work(struct work_struct *w) bpf_prog_put(psock->bpf_parse); if (psock->bpf_verdict) bpf_prog_put(psock->bpf_verdict); + if (psock->bpf_tx_msg) + bpf_prog_put(psock->bpf_tx_msg); + + if (psock->cork) { + free_start_sg(psock->sock, psock->cork); + kfree(psock->cork); + } list_for_each_entry_safe(e, tmp, &psock->maps, list) { list_del(&e->list); kfree(e); } + if (psock->sk_redir) + sock_put(psock->sk_redir); + sock_put(psock->sock); kfree(psock); } @@ -503,6 +1154,7 @@ static struct smap_psock *smap_init_psock(struct sock *sock, if (!psock) return ERR_PTR(-ENOMEM); + psock->eval = __SK_NONE; psock->sock = sock; skb_queue_head_init(&psock->rxqueue); INIT_WORK(&psock->tx_work, smap_tx_work); @@ -711,10 +1363,11 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops, { struct bpf_stab *stab = container_of(map, struct bpf_stab, map); struct smap_psock_map_entry *e = NULL; - struct bpf_prog *verdict, *parse; + struct bpf_prog *verdict, *parse, *tx_msg; struct sock *osock, *sock; struct smap_psock *psock; u32 i = *(u32 *)key; + bool new = false; int err; if (unlikely(flags > BPF_EXIST)) @@ -737,6 +1390,7 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops, */ verdict = READ_ONCE(stab->bpf_verdict); parse = READ_ONCE(stab->bpf_parse); + tx_msg = READ_ONCE(stab->bpf_tx_msg); if (parse && verdict) { /* bpf prog refcnt may be zero if a concurrent attach operation @@ -755,6 +1409,17 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops, } } + if (tx_msg) { + tx_msg = bpf_prog_inc_not_zero(stab->bpf_tx_msg); + if (IS_ERR(tx_msg)) { + if (verdict) + bpf_prog_put(verdict); + if (parse) + bpf_prog_put(parse); + return PTR_ERR(tx_msg); + } + } + write_lock_bh(&sock->sk_callback_lock); psock = smap_psock_sk(sock); @@ -769,7 +1434,14 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops, err = -EBUSY; goto out_progs; } - refcount_inc(&psock->refcnt); + if (READ_ONCE(psock->bpf_tx_msg) && tx_msg) { + err = -EBUSY; + goto out_progs; + } + if (!refcount_inc_not_zero(&psock->refcnt)) { + err = -EAGAIN; + goto out_progs; + } } else { psock = smap_init_psock(sock, stab); if (IS_ERR(psock)) { @@ -777,11 +1449,8 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops, goto out_progs; } - err = tcp_set_ulp_id(sock, TCP_ULP_BPF); - if (err) - goto out_progs; - set_bit(SMAP_TX_RUNNING, &psock->state); + new = true; } e = kzalloc(sizeof(*e), GFP_ATOMIC | __GFP_NOWARN); @@ -794,6 +1463,14 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops, /* 3. At this point we have a reference to a valid psock that is * running. Attach any BPF programs needed. */ + if (tx_msg) + bpf_tcp_msg_add(psock, sock, tx_msg); + if (new) { + err = tcp_set_ulp_id(sock, TCP_ULP_BPF); + if (err) + goto out_free; + } + if (parse && verdict && !psock->strp_enabled) { err = smap_init_sock(psock, sock); if (err) @@ -815,8 +1492,6 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops, struct smap_psock *opsock = smap_psock_sk(osock); write_lock_bh(&osock->sk_callback_lock); - if (osock != sock && parse) - smap_stop_sock(opsock, osock); smap_list_remove(opsock, &stab->sock_map[i]); smap_release_sock(opsock, osock); write_unlock_bh(&osock->sk_callback_lock); @@ -829,6 +1504,8 @@ out_progs: bpf_prog_put(verdict); if (parse) bpf_prog_put(parse); + if (tx_msg) + bpf_prog_put(tx_msg); write_unlock_bh(&sock->sk_callback_lock); kfree(e); return err; @@ -843,6 +1520,9 @@ int sock_map_prog(struct bpf_map *map, struct bpf_prog *prog, u32 type) return -EINVAL; switch (type) { + case BPF_SK_MSG_VERDICT: + orig = xchg(&stab->bpf_tx_msg, prog); + break; case BPF_SK_SKB_STREAM_PARSER: orig = xchg(&stab->bpf_parse, prog); break; @@ -904,6 +1584,10 @@ static void sock_map_release(struct bpf_map *map, struct file *map_file) orig = xchg(&stab->bpf_verdict, NULL); if (orig) bpf_prog_put(orig); + + orig = xchg(&stab->bpf_tx_msg, NULL); + if (orig) + bpf_prog_put(orig); } const struct bpf_map_ops sock_map_ops = { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index e24aa3241387..3aeb4ea2a93a 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1315,7 +1315,8 @@ static int bpf_obj_get(const union bpf_attr *attr) #define BPF_PROG_ATTACH_LAST_FIELD attach_flags -static int sockmap_get_from_fd(const union bpf_attr *attr, bool attach) +static int sockmap_get_from_fd(const union bpf_attr *attr, + int type, bool attach) { struct bpf_prog *prog = NULL; int ufd = attr->target_fd; @@ -1329,8 +1330,7 @@ static int sockmap_get_from_fd(const union bpf_attr *attr, bool attach) return PTR_ERR(map); if (attach) { - prog = bpf_prog_get_type(attr->attach_bpf_fd, - BPF_PROG_TYPE_SK_SKB); + prog = bpf_prog_get_type(attr->attach_bpf_fd, type); if (IS_ERR(prog)) { fdput(f); return PTR_ERR(prog); @@ -1382,9 +1382,11 @@ static int bpf_prog_attach(const union bpf_attr *attr) case BPF_CGROUP_DEVICE: ptype = BPF_PROG_TYPE_CGROUP_DEVICE; break; + case BPF_SK_MSG_VERDICT: + return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_MSG, true); case BPF_SK_SKB_STREAM_PARSER: case BPF_SK_SKB_STREAM_VERDICT: - return sockmap_get_from_fd(attr, true); + return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, true); default: return -EINVAL; } @@ -1437,9 +1439,11 @@ static int bpf_prog_detach(const union bpf_attr *attr) case BPF_CGROUP_DEVICE: ptype = BPF_PROG_TYPE_CGROUP_DEVICE; break; + case BPF_SK_MSG_VERDICT: + return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_MSG, false); case BPF_SK_SKB_STREAM_PARSER: case BPF_SK_SKB_STREAM_VERDICT: - return sockmap_get_from_fd(attr, false); + return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, false); default: return -EINVAL; } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index eb79a34359c0..e9f7c20691c1 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1248,6 +1248,7 @@ static bool may_access_direct_pkt_data(struct bpf_verifier_env *env, case BPF_PROG_TYPE_XDP: case BPF_PROG_TYPE_LWT_XMIT: case BPF_PROG_TYPE_SK_SKB: + case BPF_PROG_TYPE_SK_MSG: if (meta) return meta->pkt_access; @@ -2071,7 +2072,8 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, case BPF_MAP_TYPE_SOCKMAP: if (func_id != BPF_FUNC_sk_redirect_map && func_id != BPF_FUNC_sock_map_update && - func_id != BPF_FUNC_map_delete_elem) + func_id != BPF_FUNC_map_delete_elem && + func_id != BPF_FUNC_msg_redirect_map) goto error; break; default: @@ -2109,6 +2111,7 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, goto error; break; case BPF_FUNC_sk_redirect_map: + case BPF_FUNC_msg_redirect_map: if (map->map_type != BPF_MAP_TYPE_SOCKMAP) goto error; break; diff --git a/net/core/filter.c b/net/core/filter.c index 33edfa8372fd..2b6c47597eab 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1890,6 +1890,44 @@ static const struct bpf_func_proto bpf_sk_redirect_map_proto = { .arg4_type = ARG_ANYTHING, }; +BPF_CALL_4(bpf_msg_redirect_map, struct sk_msg_buff *, msg, + struct bpf_map *, map, u32, key, u64, flags) +{ + /* If user passes invalid input drop the packet. */ + if (unlikely(flags)) + return SK_DROP; + + msg->key = key; + msg->flags = flags; + msg->map = map; + + return SK_PASS; +} + +struct sock *do_msg_redirect_map(struct sk_msg_buff *msg) +{ + struct sock *sk = NULL; + + if (msg->map) { + sk = __sock_map_lookup_elem(msg->map, msg->key); + + msg->key = 0; + msg->map = NULL; + } + + return sk; +} + +static const struct bpf_func_proto bpf_msg_redirect_map_proto = { + .func = bpf_msg_redirect_map, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_ANYTHING, +}; + BPF_CALL_1(bpf_get_cgroup_classid, const struct sk_buff *, skb) { return task_get_classid(skb); @@ -3591,6 +3629,16 @@ static const struct bpf_func_proto * } } +static const struct bpf_func_proto *sk_msg_func_proto(enum bpf_func_id func_id) +{ + switch (func_id) { + case BPF_FUNC_msg_redirect_map: + return &bpf_msg_redirect_map_proto; + default: + return bpf_base_func_proto(func_id); + } +} + static const struct bpf_func_proto *sk_skb_func_proto(enum bpf_func_id func_id) { switch (func_id) { @@ -3980,6 +4028,32 @@ static bool sk_skb_is_valid_access(int off, int size, return bpf_skb_is_valid_access(off, size, type, info); } +static bool sk_msg_is_valid_access(int off, int size, + enum bpf_access_type type, + struct bpf_insn_access_aux *info) +{ + if (type == BPF_WRITE) + return false; + + switch (off) { + case offsetof(struct sk_msg_md, data): + info->reg_type = PTR_TO_PACKET; + break; + case offsetof(struct sk_msg_md, data_end): + info->reg_type = PTR_TO_PACKET_END; + break; + } + + if (off < 0 || off >= sizeof(struct sk_msg_md)) + return false; + if (off % size != 0) + return false; + if (size != sizeof(__u64)) + return false; + + return true; +} + static u32 bpf_convert_ctx_access(enum bpf_access_type type, const struct bpf_insn *si, struct bpf_insn *insn_buf, @@ -4778,6 +4852,29 @@ static u32 sk_skb_convert_ctx_access(enum bpf_access_type type, return insn - insn_buf; } +static u32 sk_msg_convert_ctx_access(enum bpf_access_type type, + const struct bpf_insn *si, + struct bpf_insn *insn_buf, + struct bpf_prog *prog, u32 *target_size) +{ + struct bpf_insn *insn = insn_buf; + + switch (si->off) { + case offsetof(struct sk_msg_md, data): + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_msg_buff, data), + si->dst_reg, si->src_reg, + offsetof(struct sk_msg_buff, data)); + break; + case offsetof(struct sk_msg_md, data_end): + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_msg_buff, data_end), + si->dst_reg, si->src_reg, + offsetof(struct sk_msg_buff, data_end)); + break; + } + + return insn - insn_buf; +} + const struct bpf_verifier_ops sk_filter_verifier_ops = { .get_func_proto = sk_filter_func_proto, .is_valid_access = sk_filter_is_valid_access, @@ -4868,6 +4965,15 @@ const struct bpf_verifier_ops sk_skb_verifier_ops = { const struct bpf_prog_ops sk_skb_prog_ops = { }; +const struct bpf_verifier_ops sk_msg_verifier_ops = { + .get_func_proto = sk_msg_func_proto, + .is_valid_access = sk_msg_is_valid_access, + .convert_ctx_access = sk_msg_convert_ctx_access, +}; + +const struct bpf_prog_ops sk_msg_prog_ops = { +}; + int sk_detach_filter(struct sock *sk) { int ret = -ENOENT; -- cgit v1.2.3 From 2a100317c9ebc204a166f16294884fbf9da074ce Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Sun, 18 Mar 2018 12:57:15 -0700 Subject: bpf: sockmap, add bpf_msg_apply_bytes() helper A single sendmsg or sendfile system call can contain multiple logical messages that a BPF program may want to read and apply a verdict. But, without an apply_bytes helper any verdict on the data applies to all bytes in the sendmsg/sendfile. Alternatively, a BPF program may only care to read the first N bytes of a msg. If the payload is large say MB or even GB setting up and calling the BPF program repeatedly for all bytes, even though the verdict is already known, creates unnecessary overhead. To allow BPF programs to control how many bytes a given verdict applies to we implement a bpf_msg_apply_bytes() helper. When called from within a BPF program this sets a counter, internal to the BPF infrastructure, that applies the last verdict to the next N bytes. If the N is smaller than the current data being processed from a sendmsg/sendfile call, the first N bytes will be sent and the BPF program will be re-run with start_data pointing to the N+1 byte. If N is larger than the current data being processed the BPF verdict will be applied to multiple sendmsg/sendfile calls until N bytes are consumed. Note1 if a socket closes with apply_bytes counter non-zero this is not a problem because data is not being buffered for N bytes and is sent as its received. Signed-off-by: John Fastabend Acked-by: David S. Miller Acked-by: Alexei Starovoitov Signed-off-by: Daniel Borkmann --- include/uapi/linux/bpf.h | 3 ++- net/core/filter.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index ef529539abde..a557a2a5d72d 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -791,7 +791,8 @@ union bpf_attr { FN(getsockopt), \ FN(override_return), \ FN(sock_ops_cb_flags_set), \ - FN(msg_redirect_map), + FN(msg_redirect_map), \ + FN(msg_apply_bytes), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/net/core/filter.c b/net/core/filter.c index 2b6c47597eab..17d6775f5431 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1928,6 +1928,20 @@ static const struct bpf_func_proto bpf_msg_redirect_map_proto = { .arg4_type = ARG_ANYTHING, }; +BPF_CALL_2(bpf_msg_apply_bytes, struct sk_msg_buff *, msg, u32, bytes) +{ + msg->apply_bytes = bytes; + return 0; +} + +static const struct bpf_func_proto bpf_msg_apply_bytes_proto = { + .func = bpf_msg_apply_bytes, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + BPF_CALL_1(bpf_get_cgroup_classid, const struct sk_buff *, skb) { return task_get_classid(skb); @@ -3634,6 +3648,8 @@ static const struct bpf_func_proto *sk_msg_func_proto(enum bpf_func_id func_id) switch (func_id) { case BPF_FUNC_msg_redirect_map: return &bpf_msg_redirect_map_proto; + case BPF_FUNC_msg_apply_bytes: + return &bpf_msg_apply_bytes_proto; default: return bpf_base_func_proto(func_id); } -- cgit v1.2.3 From 91843d540a139eb8070bcff8aa10089164436deb Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Sun, 18 Mar 2018 12:57:20 -0700 Subject: bpf: sockmap, add msg_cork_bytes() helper In the case where we need a specific number of bytes before a verdict can be assigned, even if the data spans multiple sendmsg or sendfile calls. The BPF program may use msg_cork_bytes(). The extreme case is a user can call sendmsg repeatedly with 1-byte msg segments. Obviously, this is bad for performance but is still valid. If the BPF program needs N bytes to validate a header it can use msg_cork_bytes to specify N bytes and the BPF program will not be called again until N bytes have been accumulated. The infrastructure will attempt to coalesce data if possible so in many cases (most my use cases at least) the data will be in a single scatterlist element with data pointers pointing to start/end of the element. However, this is dependent on available memory so is not guaranteed. So BPF programs must validate data pointer ranges, but this is the case anyways to convince the verifier the accesses are valid. Signed-off-by: John Fastabend Acked-by: David S. Miller Acked-by: Alexei Starovoitov Signed-off-by: Daniel Borkmann --- include/uapi/linux/bpf.h | 3 ++- net/core/filter.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index a557a2a5d72d..1765cfb16c99 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -792,7 +792,8 @@ union bpf_attr { FN(override_return), \ FN(sock_ops_cb_flags_set), \ FN(msg_redirect_map), \ - FN(msg_apply_bytes), + FN(msg_apply_bytes), \ + FN(msg_cork_bytes), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/net/core/filter.c b/net/core/filter.c index 17d6775f5431..0c9daf6ee555 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1942,6 +1942,20 @@ static const struct bpf_func_proto bpf_msg_apply_bytes_proto = { .arg2_type = ARG_ANYTHING, }; +BPF_CALL_2(bpf_msg_cork_bytes, struct sk_msg_buff *, msg, u32, bytes) +{ + msg->cork_bytes = bytes; + return 0; +} + +static const struct bpf_func_proto bpf_msg_cork_bytes_proto = { + .func = bpf_msg_cork_bytes, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + BPF_CALL_1(bpf_get_cgroup_classid, const struct sk_buff *, skb) { return task_get_classid(skb); @@ -3650,6 +3664,8 @@ static const struct bpf_func_proto *sk_msg_func_proto(enum bpf_func_id func_id) return &bpf_msg_redirect_map_proto; case BPF_FUNC_msg_apply_bytes: return &bpf_msg_apply_bytes_proto; + case BPF_FUNC_msg_cork_bytes: + return &bpf_msg_cork_bytes_proto; default: return bpf_base_func_proto(func_id); } -- cgit v1.2.3 From 015632bb30daaaee64e1bcac07570860e0bf3092 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Sun, 18 Mar 2018 12:57:25 -0700 Subject: bpf: sk_msg program helper bpf_sk_msg_pull_data Currently, if a bpf sk msg program is run the program can only parse data that the (start,end) pointers already consumed. For sendmsg hooks this is likely the first scatterlist element. For sendpage this will be the range (0,0) because the data is shared with userspace and by default we want to avoid allowing userspace to modify data while (or after) BPF verdict is being decided. To support pulling in additional bytes for parsing use a new helper bpf_sk_msg_pull(start, end, flags) which works similar to cls tc logic. This helper will attempt to point the data start pointer at 'start' bytes offest into msg and data end pointer at 'end' bytes offset into message. After basic sanity checks to ensure 'start' <= 'end' and 'end' <= msg_length there are a few cases we need to handle. First the sendmsg hook has already copied the data from userspace and has exclusive access to it. Therefor, it is not necessesary to copy the data. However, it may be required. After finding the scatterlist element with 'start' offset byte in it there are two cases. One the range (start,end) is entirely contained in the sg element and is already linear. All that is needed is to update the data pointers, no allocate/copy is needed. The other case is (start, end) crosses sg element boundaries. In this case we allocate a block of size 'end - start' and copy the data to linearize it. Next sendpage hook has not copied any data in initial state so that data pointers are (0,0). In this case we handle it similar to the above sendmsg case except the allocation/copy must always happen. Then when sending the data we have possibly three memory regions that need to be sent, (0, start - 1), (start, end), and (end + 1, msg_length). This is required to ensure any writes by the BPF program are correctly transmitted. Lastly this operation will invalidate any previous data checks so BPF programs will have to revalidate pointers after making this BPF call. Signed-off-by: John Fastabend Acked-by: David S. Miller Signed-off-by: Daniel Borkmann --- include/uapi/linux/bpf.h | 3 +- net/core/filter.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1765cfb16c99..18b7c510c511 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -793,7 +793,8 @@ union bpf_attr { FN(sock_ops_cb_flags_set), \ FN(msg_redirect_map), \ FN(msg_apply_bytes), \ - FN(msg_cork_bytes), + FN(msg_cork_bytes), \ + FN(msg_pull_data), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/net/core/filter.c b/net/core/filter.c index 0c9daf6ee555..c86f03fd9ea5 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1956,6 +1956,136 @@ static const struct bpf_func_proto bpf_msg_cork_bytes_proto = { .arg2_type = ARG_ANYTHING, }; +BPF_CALL_4(bpf_msg_pull_data, + struct sk_msg_buff *, msg, u32, start, u32, end, u64, flags) +{ + unsigned int len = 0, offset = 0, copy = 0; + struct scatterlist *sg = msg->sg_data; + int first_sg, last_sg, i, shift; + unsigned char *p, *to, *from; + int bytes = end - start; + struct page *page; + + if (unlikely(flags || end <= start)) + return -EINVAL; + + /* First find the starting scatterlist element */ + i = msg->sg_start; + do { + len = sg[i].length; + offset += len; + if (start < offset + len) + break; + i++; + if (i == MAX_SKB_FRAGS) + i = 0; + } while (i != msg->sg_end); + + if (unlikely(start >= offset + len)) + return -EINVAL; + + if (!msg->sg_copy[i] && bytes <= len) + goto out; + + first_sg = i; + + /* At this point we need to linearize multiple scatterlist + * elements or a single shared page. Either way we need to + * copy into a linear buffer exclusively owned by BPF. Then + * place the buffer in the scatterlist and fixup the original + * entries by removing the entries now in the linear buffer + * and shifting the remaining entries. For now we do not try + * to copy partial entries to avoid complexity of running out + * of sg_entry slots. The downside is reading a single byte + * will copy the entire sg entry. + */ + do { + copy += sg[i].length; + i++; + if (i == MAX_SKB_FRAGS) + i = 0; + if (bytes < copy) + break; + } while (i != msg->sg_end); + last_sg = i; + + if (unlikely(copy < end - start)) + return -EINVAL; + + page = alloc_pages(__GFP_NOWARN | GFP_ATOMIC, get_order(copy)); + if (unlikely(!page)) + return -ENOMEM; + p = page_address(page); + offset = 0; + + i = first_sg; + do { + from = sg_virt(&sg[i]); + len = sg[i].length; + to = p + offset; + + memcpy(to, from, len); + offset += len; + sg[i].length = 0; + put_page(sg_page(&sg[i])); + + i++; + if (i == MAX_SKB_FRAGS) + i = 0; + } while (i != last_sg); + + sg[first_sg].length = copy; + sg_set_page(&sg[first_sg], page, copy, 0); + + /* To repair sg ring we need to shift entries. If we only + * had a single entry though we can just replace it and + * be done. Otherwise walk the ring and shift the entries. + */ + shift = last_sg - first_sg - 1; + if (!shift) + goto out; + + i = first_sg + 1; + do { + int move_from; + + if (i + shift >= MAX_SKB_FRAGS) + move_from = i + shift - MAX_SKB_FRAGS; + else + move_from = i + shift; + + if (move_from == msg->sg_end) + break; + + sg[i] = sg[move_from]; + sg[move_from].length = 0; + sg[move_from].page_link = 0; + sg[move_from].offset = 0; + + i++; + if (i == MAX_SKB_FRAGS) + i = 0; + } while (1); + msg->sg_end -= shift; + if (msg->sg_end < 0) + msg->sg_end += MAX_SKB_FRAGS; +out: + msg->data = sg_virt(&sg[i]) + start - offset; + msg->data_end = msg->data + bytes; + + return 0; +} + +static const struct bpf_func_proto bpf_msg_pull_data_proto = { + .func = bpf_msg_pull_data, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_ANYTHING, +}; + BPF_CALL_1(bpf_get_cgroup_classid, const struct sk_buff *, skb) { return task_get_classid(skb); @@ -2897,7 +3027,8 @@ bool bpf_helper_changes_pkt_data(void *func) func == bpf_l3_csum_replace || func == bpf_l4_csum_replace || func == bpf_xdp_adjust_head || - func == bpf_xdp_adjust_meta) + func == bpf_xdp_adjust_meta || + func == bpf_msg_pull_data) return true; return false; @@ -3666,6 +3797,8 @@ static const struct bpf_func_proto *sk_msg_func_proto(enum bpf_func_id func_id) return &bpf_msg_apply_bytes_proto; case BPF_FUNC_msg_cork_bytes: return &bpf_msg_cork_bytes_proto; + case BPF_FUNC_msg_pull_data: + return &bpf_msg_pull_data_proto; default: return bpf_base_func_proto(func_id); } -- cgit v1.2.3 From 1f57bc12d87dda2d56b564d35f21b9e6bdb2bb2c Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Wed, 28 Feb 2018 16:06:11 +0100 Subject: fw_cfg: add a public uapi header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a common header file for well-known values and structures to be shared by the Linux kernel with qemu or other projects. It is based from qemu/docs/specs/fw_cfg.txt which references qemu/include/hw/nvram/fw_cfg_keys.h "for the most up-to-date and authoritative list" & vmcoreinfo.txt. Those files don't have an explicit license, but qemu/hw/nvram/fw_cfg.c is BSD-license, so Michael S. Tsirkin suggested to use the same license. The patch intentionally left out DMA & vmcoreinfo structures & defines, which are added in the commits making usage of it. Suggested-by: Michael S. Tsirkin Signed-off-by: Marc-André Lureau Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 1 + drivers/firmware/qemu_fw_cfg.c | 22 ++------------ include/uapi/linux/qemu_fw_cfg.h | 66 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 include/uapi/linux/qemu_fw_cfg.h (limited to 'include/uapi/linux') diff --git a/MAINTAINERS b/MAINTAINERS index 4623caf8d72d..fc2c373f24a0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11368,6 +11368,7 @@ M: "Michael S. Tsirkin" L: qemu-devel@nongnu.org S: Maintained F: drivers/firmware/qemu_fw_cfg.c +F: include/uapi/linux/qemu_fw_cfg.h QIB DRIVER M: Dennis Dalessandro diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 45bfc389b226..5de6bb406fb6 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -32,30 +32,12 @@ #include #include #include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); -/* selector key values for "well-known" fw_cfg entries */ -#define FW_CFG_SIGNATURE 0x00 -#define FW_CFG_ID 0x01 -#define FW_CFG_FILE_DIR 0x19 - -/* size in bytes of fw_cfg signature */ -#define FW_CFG_SIG_SIZE 4 - -/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ -#define FW_CFG_MAX_FILE_PATH 56 - -/* fw_cfg file directory entry type */ -struct fw_cfg_file { - u32 size; - u16 select; - u16 reserved; - char name[FW_CFG_MAX_FILE_PATH]; -}; - /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -616,7 +598,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); #ifdef CONFIG_ACPI static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { - { "QEMU0002", }, + { FW_CFG_ACPI_DEVICE_ID, }, {}, }; MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); diff --git a/include/uapi/linux/qemu_fw_cfg.h b/include/uapi/linux/qemu_fw_cfg.h new file mode 100644 index 000000000000..c698ac3812f6 --- /dev/null +++ b/include/uapi/linux/qemu_fw_cfg.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef _LINUX_FW_CFG_H +#define _LINUX_FW_CFG_H + +#include + +#define FW_CFG_ACPI_DEVICE_ID "QEMU0002" + +/* selector key values for "well-known" fw_cfg entries */ +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID 0x02 +#define FW_CFG_RAM_SIZE 0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA 0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS 0x0f +#define FW_CFG_KERNEL_ENTRY 0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR 0x13 +#define FW_CFG_CMDLINE_SIZE 0x14 +#define FW_CFG_CMDLINE_DATA 0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR 0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS_MIN 0x10 + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK (~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)) + +#define FW_CFG_INVALID 0xffff + +/* width in bytes of fw_cfg control register */ +#define FW_CFG_CTL_SIZE 0x02 + +/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ +#define FW_CFG_MAX_FILE_PATH 56 + +/* size in bytes of fw_cfg signature */ +#define FW_CFG_SIG_SIZE 4 + +/* FW_CFG_ID bits */ +#define FW_CFG_VERSION 0x01 + +/* fw_cfg file directory entry type */ +struct fw_cfg_file { + __be32 size; + __be16 select; + __u16 reserved; + char name[FW_CFG_MAX_FILE_PATH]; +}; + +#endif -- cgit v1.2.3 From 2d6d60a3d3eca50bbb20052278cb11dabcf4dff3 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Wed, 28 Feb 2018 16:06:14 +0100 Subject: fw_cfg: write vmcoreinfo details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the "etc/vmcoreinfo" fw_cfg file is present and we are not running the kdump kernel, write the addr/size of the vmcoreinfo ELF note. The DMA operation is expected to run synchronously with today qemu, but the specification states that it may become async, so we run "control" field check in a loop for eventual changes. Signed-off-by: Marc-André Lureau Signed-off-by: Michael S. Tsirkin --- drivers/firmware/qemu_fw_cfg.c | 145 ++++++++++++++++++++++++++++++++++++++- include/uapi/linux/qemu_fw_cfg.h | 31 +++++++++ 2 files changed, 173 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index df028faa2d00..14fedbeca724 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -34,11 +34,17 @@ #include #include #include +#include +#include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ +static u32 fw_cfg_rev; + /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -60,6 +66,66 @@ static void fw_cfg_sel_endianness(u16 key) iowrite16(key, fw_cfg_reg_ctrl); } +#ifdef CONFIG_CRASH_CORE +static inline bool fw_cfg_dma_enabled(void) +{ + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; +} + +/* qemu fw_cfg device is sync today, but spec says it may become async */ +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) +{ + for (;;) { + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); + + /* do not reorder the read to d->control */ + rmb(); + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + cpu_relax(); + } +} + +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + phys_addr_t dma; + struct fw_cfg_dma_access *d = NULL; + ssize_t ret = length; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + + /* fw_cfg device does not need IOMMU protection, so use physical addresses */ + *d = (struct fw_cfg_dma_access) { + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), + .length = cpu_to_be32(length), + .control = cpu_to_be32(control) + }; + + dma = virt_to_phys(d); + + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); + /* force memory to sync before notifying device via MMIO */ + wmb(); + iowrite32be(dma, fw_cfg_reg_dma + 4); + + fw_cfg_wait_for_control(d); + + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { + ret = -EIO; + } + +end: + kfree(d); + + return ret; +} +#endif + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count) @@ -89,6 +155,47 @@ static ssize_t fw_cfg_read_blob(u16 key, return count; } +#ifdef CONFIG_CRASH_CORE +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ +static ssize_t fw_cfg_write_blob(u16 key, + void *buf, loff_t pos, size_t count) +{ + u32 glk = -1U; + acpi_status status; + ssize_t ret = count; + + /* If we have ACPI, ensure mutual exclusion against any potential + * device access by the firmware, e.g. via AML methods: + */ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + /* Should never get here */ + WARN(1, "%s: Failed to lock ACPI!\n", __func__); + return -EINVAL; + } + + mutex_lock(&fw_cfg_dev_lock); + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_WRITE); + } else { + fw_cfg_sel_endianness(key); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + goto end; + ret = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE); + } + +end: + mutex_unlock(&fw_cfg_dev_lock); + + acpi_release_global_lock(glk); + + return ret; +} +#endif /* CONFIG_CRASH_CORE */ + /* clean up fw_cfg device i/o */ static void fw_cfg_io_cleanup(void) { @@ -188,9 +295,6 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) return 0; } -/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ -static u32 fw_cfg_rev; - static ssize_t fw_cfg_showrev(struct kobject *k, struct attribute *a, char *buf) { return sprintf(buf, "%u\n", fw_cfg_rev); @@ -213,6 +317,32 @@ struct fw_cfg_sysfs_entry { struct list_head list; }; +#ifdef CONFIG_CRASH_CORE +static ssize_t fw_cfg_write_vmcoreinfo(const struct fw_cfg_file *f) +{ + static struct fw_cfg_vmcoreinfo *data; + ssize_t ret; + + data = kmalloc(sizeof(struct fw_cfg_vmcoreinfo), GFP_KERNEL); + if (!data) + return -ENOMEM; + + *data = (struct fw_cfg_vmcoreinfo) { + .guest_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF), + .size = cpu_to_le32(VMCOREINFO_NOTE_SIZE), + .paddr = cpu_to_le64(paddr_vmcoreinfo_note()) + }; + /* spare ourself reading host format support for now since we + * don't know what else to format - host may ignore ours + */ + ret = fw_cfg_write_blob(be16_to_cpu(f->select), data, + 0, sizeof(struct fw_cfg_vmcoreinfo)); + + kfree(data); + return ret; +} +#endif /* CONFIG_CRASH_CORE */ + /* get fw_cfg_sysfs_entry from kobject member */ static inline struct fw_cfg_sysfs_entry *to_entry(struct kobject *kobj) { @@ -452,6 +582,15 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) int err; struct fw_cfg_sysfs_entry *entry; +#ifdef CONFIG_CRASH_CORE + if (fw_cfg_dma_enabled() && + strcmp(f->name, FW_CFG_VMCOREINFO_FILENAME) == 0 && + !is_kdump_kernel()) { + if (fw_cfg_write_vmcoreinfo(f) < 0) + pr_warn("fw_cfg: failed to write vmcoreinfo"); + } +#endif + /* allocate new entry */ entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) diff --git a/include/uapi/linux/qemu_fw_cfg.h b/include/uapi/linux/qemu_fw_cfg.h index c698ac3812f6..e089c0159ec2 100644 --- a/include/uapi/linux/qemu_fw_cfg.h +++ b/include/uapi/linux/qemu_fw_cfg.h @@ -54,6 +54,7 @@ /* FW_CFG_ID bits */ #define FW_CFG_VERSION 0x01 +#define FW_CFG_VERSION_DMA 0x02 /* fw_cfg file directory entry type */ struct fw_cfg_file { @@ -63,4 +64,34 @@ struct fw_cfg_file { char name[FW_CFG_MAX_FILE_PATH]; }; +/* FW_CFG_DMA_CONTROL bits */ +#define FW_CFG_DMA_CTL_ERROR 0x01 +#define FW_CFG_DMA_CTL_READ 0x02 +#define FW_CFG_DMA_CTL_SKIP 0x04 +#define FW_CFG_DMA_CTL_SELECT 0x08 +#define FW_CFG_DMA_CTL_WRITE 0x10 + +#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */ + +/* Control as first field allows for different structures selected by this + * field, which might be useful in the future + */ +struct fw_cfg_dma_access { + __be32 control; + __be32 length; + __be64 address; +}; + +#define FW_CFG_VMCOREINFO_FILENAME "etc/vmcoreinfo" + +#define FW_CFG_VMCOREINFO_FORMAT_NONE 0x0 +#define FW_CFG_VMCOREINFO_FORMAT_ELF 0x1 + +struct fw_cfg_vmcoreinfo { + __le16 host_format; + __le16 guest_format; + __le32 size; + __le64 paddr; +}; + #endif -- cgit v1.2.3 From d719e3f21cf91d3f82bd827d46199ba41af2f73a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 9 Mar 2018 11:57:20 +0100 Subject: netfilter: nft_ct: add NFT_CT_{SRC,DST}_{IP,IP6} All existing keys, except the NFT_CT_SRC and NFT_CT_DST are assumed to have strict datatypes. This is causing problems with sets and concatenations given the specific length of these keys is not known. Signed-off-by: Pablo Neira Ayuso Acked-by: Florian Westphal --- include/uapi/linux/netfilter/nf_tables.h | 12 ++++++++-- net/netfilter/nft_ct.c | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 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 66dceee0ae30..6a3d653d5b27 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -909,8 +909,8 @@ enum nft_rt_attributes { * @NFT_CT_EXPIRATION: relative conntrack expiration time in ms * @NFT_CT_HELPER: connection tracking helper assigned to conntrack * @NFT_CT_L3PROTOCOL: conntrack layer 3 protocol - * @NFT_CT_SRC: conntrack layer 3 protocol source (IPv4/IPv6 address) - * @NFT_CT_DST: conntrack layer 3 protocol destination (IPv4/IPv6 address) + * @NFT_CT_SRC: conntrack layer 3 protocol source (IPv4/IPv6 address, deprecated) + * @NFT_CT_DST: conntrack layer 3 protocol destination (IPv4/IPv6 address, deprecated) * @NFT_CT_PROTOCOL: conntrack layer 4 protocol * @NFT_CT_PROTO_SRC: conntrack layer 4 protocol source * @NFT_CT_PROTO_DST: conntrack layer 4 protocol destination @@ -920,6 +920,10 @@ enum nft_rt_attributes { * @NFT_CT_AVGPKT: conntrack average bytes per packet * @NFT_CT_ZONE: conntrack zone * @NFT_CT_EVENTMASK: ctnetlink events to be generated for this conntrack + * @NFT_CT_SRC_IP: conntrack layer 3 protocol source (IPv4 address) + * @NFT_CT_DST_IP: conntrack layer 3 protocol destination (IPv4 address) + * @NFT_CT_SRC_IP6: conntrack layer 3 protocol source (IPv6 address) + * @NFT_CT_DST_IP6: conntrack layer 3 protocol destination (IPv6 address) */ enum nft_ct_keys { NFT_CT_STATE, @@ -941,6 +945,10 @@ enum nft_ct_keys { NFT_CT_AVGPKT, NFT_CT_ZONE, NFT_CT_EVENTMASK, + NFT_CT_SRC_IP, + NFT_CT_DST_IP, + NFT_CT_SRC_IP6, + NFT_CT_DST_IP6, }; /** diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 6ab274b14484..ea737fd789e8 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -196,6 +196,26 @@ static void nft_ct_get_eval(const struct nft_expr *expr, case NFT_CT_PROTO_DST: nft_reg_store16(dest, (__force u16)tuple->dst.u.all); return; + case NFT_CT_SRC_IP: + if (nf_ct_l3num(ct) != NFPROTO_IPV4) + goto err; + *dest = tuple->src.u3.ip; + return; + case NFT_CT_DST_IP: + if (nf_ct_l3num(ct) != NFPROTO_IPV4) + goto err; + *dest = tuple->dst.u3.ip; + return; + case NFT_CT_SRC_IP6: + if (nf_ct_l3num(ct) != NFPROTO_IPV6) + goto err; + memcpy(dest, tuple->src.u3.ip6, sizeof(struct in6_addr)); + return; + case NFT_CT_DST_IP6: + if (nf_ct_l3num(ct) != NFPROTO_IPV6) + goto err; + memcpy(dest, tuple->dst.u3.ip6, sizeof(struct in6_addr)); + return; default: break; } @@ -419,6 +439,20 @@ static int nft_ct_get_init(const struct nft_ctx *ctx, return -EAFNOSUPPORT; } break; + case NFT_CT_SRC_IP: + case NFT_CT_DST_IP: + if (tb[NFTA_CT_DIRECTION] == NULL) + return -EINVAL; + + len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u3.ip); + break; + case NFT_CT_SRC_IP6: + case NFT_CT_DST_IP6: + if (tb[NFTA_CT_DIRECTION] == NULL) + return -EINVAL; + + len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u3.ip6); + break; case NFT_CT_PROTO_SRC: case NFT_CT_PROTO_DST: if (tb[NFTA_CT_DIRECTION] == NULL) @@ -588,6 +622,10 @@ static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr) switch (priv->key) { case NFT_CT_SRC: case NFT_CT_DST: + case NFT_CT_SRC_IP: + case NFT_CT_DST_IP: + case NFT_CT_SRC_IP6: + case NFT_CT_DST_IP6: case NFT_CT_PROTO_SRC: case NFT_CT_PROTO_DST: if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) -- cgit v1.2.3 From 472a73e00757b971d613d796374d2727b2e4954d Mon Sep 17 00:00:00 2001 From: Jack Ma Date: Mon, 19 Mar 2018 09:41:59 +1300 Subject: netfilter: xt_conntrack: Support bit-shifting for CONNMARK & MARK targets. This patch introduces a new feature that allows bitshifting (left and right) operations to co-operate with existing iptables options. Reviewed-by: Florian Westphal Signed-off-by: Jack Ma Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/xt_connmark.h | 10 ++++ net/netfilter/xt_connmark.c | 77 +++++++++++++++++++++++------- 2 files changed, 70 insertions(+), 17 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/netfilter/xt_connmark.h b/include/uapi/linux/netfilter/xt_connmark.h index 408a9654f05c..1aa5c955ee1e 100644 --- a/include/uapi/linux/netfilter/xt_connmark.h +++ b/include/uapi/linux/netfilter/xt_connmark.h @@ -19,11 +19,21 @@ enum { XT_CONNMARK_RESTORE }; +enum { + D_SHIFT_LEFT = 0, + D_SHIFT_RIGHT, +}; + struct xt_connmark_tginfo1 { __u32 ctmark, ctmask, nfmask; __u8 mode; }; +struct xt_connmark_tginfo2 { + __u32 ctmark, ctmask, nfmask; + __u8 shift_dir, shift_bits, mode; +}; + struct xt_connmark_mtinfo1 { __u32 mark, mask; __u8 invert; diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 809639ce6f5a..773da82190dc 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -36,9 +36,10 @@ MODULE_ALIAS("ipt_connmark"); MODULE_ALIAS("ip6t_connmark"); static unsigned int -connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) +connmark_tg_shift(struct sk_buff *skb, + const struct xt_connmark_tginfo1 *info, + u8 shift_bits, u8 shift_dir) { - const struct xt_connmark_tginfo1 *info = par->targinfo; enum ip_conntrack_info ctinfo; struct nf_conn *ct; u_int32_t newmark; @@ -50,6 +51,10 @@ connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) switch (info->mode) { case XT_CONNMARK_SET: newmark = (ct->mark & ~info->ctmask) ^ info->ctmark; + if (shift_dir == D_SHIFT_RIGHT) + newmark >>= shift_bits; + else + newmark <<= shift_bits; if (ct->mark != newmark) { ct->mark = newmark; nf_conntrack_event_cache(IPCT_MARK, ct); @@ -57,7 +62,11 @@ connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) break; case XT_CONNMARK_SAVE: newmark = (ct->mark & ~info->ctmask) ^ - (skb->mark & info->nfmask); + (skb->mark & info->nfmask); + if (shift_dir == D_SHIFT_RIGHT) + newmark >>= shift_bits; + else + newmark <<= shift_bits; if (ct->mark != newmark) { ct->mark = newmark; nf_conntrack_event_cache(IPCT_MARK, ct); @@ -65,14 +74,34 @@ connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) break; case XT_CONNMARK_RESTORE: newmark = (skb->mark & ~info->nfmask) ^ - (ct->mark & info->ctmask); + (ct->mark & info->ctmask); + if (shift_dir == D_SHIFT_RIGHT) + newmark >>= shift_bits; + else + newmark <<= shift_bits; skb->mark = newmark; break; } - return XT_CONTINUE; } +static unsigned int +connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_connmark_tginfo1 *info = par->targinfo; + + return connmark_tg_shift(skb, info, 0, 0); +} + +static unsigned int +connmark_tg_v2(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_connmark_tginfo2 *info = par->targinfo; + + return connmark_tg_shift(skb, (const struct xt_connmark_tginfo1 *)info, + info->shift_bits, info->shift_dir); +} + static int connmark_tg_check(const struct xt_tgchk_param *par) { int ret; @@ -119,15 +148,27 @@ static void connmark_mt_destroy(const struct xt_mtdtor_param *par) nf_ct_netns_put(par->net, par->family); } -static struct xt_target connmark_tg_reg __read_mostly = { - .name = "CONNMARK", - .revision = 1, - .family = NFPROTO_UNSPEC, - .checkentry = connmark_tg_check, - .target = connmark_tg, - .targetsize = sizeof(struct xt_connmark_tginfo1), - .destroy = connmark_tg_destroy, - .me = THIS_MODULE, +static struct xt_target connmark_tg_reg[] __read_mostly = { + { + .name = "CONNMARK", + .revision = 1, + .family = NFPROTO_UNSPEC, + .checkentry = connmark_tg_check, + .target = connmark_tg, + .targetsize = sizeof(struct xt_connmark_tginfo1), + .destroy = connmark_tg_destroy, + .me = THIS_MODULE, + }, + { + .name = "CONNMARK", + .revision = 2, + .family = NFPROTO_UNSPEC, + .checkentry = connmark_tg_check, + .target = connmark_tg_v2, + .targetsize = sizeof(struct xt_connmark_tginfo2), + .destroy = connmark_tg_destroy, + .me = THIS_MODULE, + } }; static struct xt_match connmark_mt_reg __read_mostly = { @@ -145,12 +186,14 @@ static int __init connmark_mt_init(void) { int ret; - ret = xt_register_target(&connmark_tg_reg); + ret = xt_register_targets(connmark_tg_reg, + ARRAY_SIZE(connmark_tg_reg)); if (ret < 0) return ret; ret = xt_register_match(&connmark_mt_reg); if (ret < 0) { - xt_unregister_target(&connmark_tg_reg); + xt_unregister_targets(connmark_tg_reg, + ARRAY_SIZE(connmark_tg_reg)); return ret; } return 0; @@ -159,7 +202,7 @@ static int __init connmark_mt_init(void) static void __exit connmark_mt_exit(void) { xt_unregister_match(&connmark_mt_reg); - xt_unregister_target(&connmark_tg_reg); + xt_unregister_target(connmark_tg_reg); } module_init(connmark_mt_init); -- cgit v1.2.3 From 20710b3b81895c89e92bcc32ce85c0bede1171f8 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 20 Mar 2018 12:33:51 +0100 Subject: netfilter: ctnetlink: synproxy support This patch exposes synproxy information per-conntrack. Moreover, send sequence adjustment events once server sends us the SYN,ACK packet, so we can synchronize the sequence adjustment too for packets going as reply from the server, as part of the synproxy logic. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_conntrack_common.h | 1 + include/uapi/linux/netfilter/nfnetlink_conntrack.h | 10 +++ net/ipv4/netfilter/ipt_SYNPROXY.c | 8 +- net/ipv6/netfilter/ip6t_SYNPROXY.c | 8 +- net/netfilter/nf_conntrack_netlink.c | 87 +++++++++++++++++++++- 5 files changed, 109 insertions(+), 5 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h index 9574bd40870b..c712eb6879f1 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_common.h +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h @@ -129,6 +129,7 @@ enum ip_conntrack_events { IPCT_NATSEQADJ = IPCT_SEQADJ, IPCT_SECMARK, /* new security mark has been set */ IPCT_LABEL, /* new connlabel has been set */ + IPCT_SYNPROXY, /* synproxy has been set */ #ifdef __KERNEL__ __IPCT_MAX #endif diff --git a/include/uapi/linux/netfilter/nfnetlink_conntrack.h b/include/uapi/linux/netfilter/nfnetlink_conntrack.h index 7397e022ce6e..77987111cab0 100644 --- a/include/uapi/linux/netfilter/nfnetlink_conntrack.h +++ b/include/uapi/linux/netfilter/nfnetlink_conntrack.h @@ -54,6 +54,7 @@ enum ctattr_type { CTA_MARK_MASK, CTA_LABELS, CTA_LABELS_MASK, + CTA_SYNPROXY, __CTA_MAX }; #define CTA_MAX (__CTA_MAX - 1) @@ -190,6 +191,15 @@ enum ctattr_natseq { }; #define CTA_NAT_SEQ_MAX (__CTA_NAT_SEQ_MAX - 1) +enum ctattr_synproxy { + CTA_SYNPROXY_UNSPEC, + CTA_SYNPROXY_ISN, + CTA_SYNPROXY_ITS, + CTA_SYNPROXY_TSOFF, + __CTA_SYNPROXY_MAX, +}; +#define CTA_SYNPROXY_MAX (__CTA_SYNPROXY_MAX - 1) + enum ctattr_expect { CTA_EXPECT_UNSPEC, CTA_EXPECT_MASTER, diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index f75fc6b53115..690b17ef6a44 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -16,6 +16,7 @@ #include #include #include +#include static struct iphdr * synproxy_build_ip(struct net *net, struct sk_buff *skb, __be32 saddr, @@ -384,6 +385,8 @@ static unsigned int ipv4_synproxy_hook(void *priv, synproxy->isn = ntohl(th->ack_seq); if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) synproxy->its = opts.tsecr; + + nf_conntrack_event_cache(IPCT_SYNPROXY, ct); break; case TCP_CONNTRACK_SYN_RECV: if (!th->syn || !th->ack) @@ -392,8 +395,10 @@ static unsigned int ipv4_synproxy_hook(void *priv, if (!synproxy_parse_options(skb, thoff, th, &opts)) return NF_DROP; - if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) + if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) { synproxy->tsoff = opts.tsval - synproxy->its; + nf_conntrack_event_cache(IPCT_SYNPROXY, ct); + } opts.options &= ~(XT_SYNPROXY_OPT_MSS | XT_SYNPROXY_OPT_WSCALE | @@ -403,6 +408,7 @@ static unsigned int ipv4_synproxy_hook(void *priv, synproxy_send_server_ack(net, state, skb, th, &opts); nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq)); + nf_conntrack_event_cache(IPCT_SEQADJ, ct); swap(opts.tsval, opts.tsecr); synproxy_send_client_ack(net, skb, th, &opts); diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index 437af8c95277..cb6d42b03cb5 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -18,6 +18,7 @@ #include #include #include +#include static struct ipv6hdr * synproxy_build_ip(struct net *net, struct sk_buff *skb, @@ -405,6 +406,8 @@ static unsigned int ipv6_synproxy_hook(void *priv, synproxy->isn = ntohl(th->ack_seq); if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) synproxy->its = opts.tsecr; + + nf_conntrack_event_cache(IPCT_SYNPROXY, ct); break; case TCP_CONNTRACK_SYN_RECV: if (!th->syn || !th->ack) @@ -413,8 +416,10 @@ static unsigned int ipv6_synproxy_hook(void *priv, if (!synproxy_parse_options(skb, thoff, th, &opts)) return NF_DROP; - if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) + if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) { synproxy->tsoff = opts.tsval - synproxy->its; + nf_conntrack_event_cache(IPCT_SYNPROXY, ct); + } opts.options &= ~(XT_SYNPROXY_OPT_MSS | XT_SYNPROXY_OPT_WSCALE | @@ -424,6 +429,7 @@ static unsigned int ipv6_synproxy_hook(void *priv, synproxy_send_server_ack(net, state, skb, th, &opts); nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq)); + nf_conntrack_event_cache(IPCT_SEQADJ, ct); swap(opts.tsval, opts.tsecr); synproxy_send_client_ack(net, skb, th, &opts); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 8884d302d33a..11ef85a57244 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -440,6 +440,31 @@ err: return -1; } +static int ctnetlink_dump_ct_synproxy(struct sk_buff *skb, struct nf_conn *ct) +{ + struct nf_conn_synproxy *synproxy = nfct_synproxy(ct); + struct nlattr *nest_parms; + + if (!synproxy) + return 0; + + nest_parms = nla_nest_start(skb, CTA_SYNPROXY | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + + if (nla_put_be32(skb, CTA_SYNPROXY_ISN, htonl(synproxy->isn)) || + nla_put_be32(skb, CTA_SYNPROXY_ITS, htonl(synproxy->its)) || + nla_put_be32(skb, CTA_SYNPROXY_TSOFF, htonl(synproxy->tsoff))) + goto nla_put_failure; + + nla_nest_end(skb, nest_parms); + + return 0; + +nla_put_failure: + return -1; +} + static int ctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct) { if (nla_put_be32(skb, CTA_ID, htonl((unsigned long)ct))) @@ -518,7 +543,8 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, ctnetlink_dump_id(skb, ct) < 0 || ctnetlink_dump_use(skb, ct) < 0 || ctnetlink_dump_master(skb, ct) < 0 || - ctnetlink_dump_ct_seq_adj(skb, ct) < 0) + ctnetlink_dump_ct_seq_adj(skb, ct) < 0 || + ctnetlink_dump_ct_synproxy(skb, ct) < 0) goto nla_put_failure; nlmsg_end(skb, nlh); @@ -730,6 +756,10 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) if (events & (1 << IPCT_SEQADJ) && ctnetlink_dump_ct_seq_adj(skb, ct) < 0) goto nla_put_failure; + + if (events & (1 << IPCT_SYNPROXY) && + ctnetlink_dump_ct_synproxy(skb, ct) < 0) + goto nla_put_failure; } #ifdef CONFIG_NF_CONNTRACK_MARK @@ -1689,6 +1719,39 @@ err: return ret; } +static const struct nla_policy synproxy_policy[CTA_SYNPROXY_MAX + 1] = { + [CTA_SYNPROXY_ISN] = { .type = NLA_U32 }, + [CTA_SYNPROXY_ITS] = { .type = NLA_U32 }, + [CTA_SYNPROXY_TSOFF] = { .type = NLA_U32 }, +}; + +static int ctnetlink_change_synproxy(struct nf_conn *ct, + const struct nlattr * const cda[]) +{ + struct nf_conn_synproxy *synproxy = nfct_synproxy(ct); + struct nlattr *tb[CTA_SYNPROXY_MAX + 1]; + int err; + + if (!synproxy) + return 0; + + err = nla_parse_nested(tb, CTA_SYNPROXY_MAX, cda[CTA_SYNPROXY], + synproxy_policy, NULL); + if (err < 0) + return err; + + if (!tb[CTA_SYNPROXY_ISN] || + !tb[CTA_SYNPROXY_ITS] || + !tb[CTA_SYNPROXY_TSOFF]) + return -EINVAL; + + synproxy->isn = ntohl(nla_get_be32(tb[CTA_SYNPROXY_ISN])); + synproxy->its = ntohl(nla_get_be32(tb[CTA_SYNPROXY_ITS])); + synproxy->tsoff = ntohl(nla_get_be32(tb[CTA_SYNPROXY_TSOFF])); + + return 0; +} + static int ctnetlink_attach_labels(struct nf_conn *ct, const struct nlattr * const cda[]) { @@ -1759,6 +1822,12 @@ ctnetlink_change_conntrack(struct nf_conn *ct, return err; } + if (cda[CTA_SYNPROXY]) { + err = ctnetlink_change_synproxy(ct, cda); + if (err < 0) + return err; + } + if (cda[CTA_LABELS]) { err = ctnetlink_attach_labels(ct, cda); if (err < 0) @@ -1880,6 +1949,12 @@ ctnetlink_create_conntrack(struct net *net, goto err2; } + if (cda[CTA_SYNPROXY]) { + err = ctnetlink_change_synproxy(ct, cda); + if (err < 0) + goto err2; + } + #if defined(CONFIG_NF_CONNTRACK_MARK) if (cda[CTA_MARK]) ct->mark = ntohl(nla_get_be32(cda[CTA_MARK])); @@ -1991,7 +2066,9 @@ static int ctnetlink_new_conntrack(struct net *net, struct sock *ctnl, (1 << IPCT_HELPER) | (1 << IPCT_PROTOINFO) | (1 << IPCT_SEQADJ) | - (1 << IPCT_MARK) | events, + (1 << IPCT_MARK) | + (1 << IPCT_SYNPROXY) | + events, ct, NETLINK_CB(skb).portid, nlmsg_report(nlh)); nf_ct_put(ct); @@ -2012,7 +2089,8 @@ static int ctnetlink_new_conntrack(struct net *net, struct sock *ctnl, (1 << IPCT_LABEL) | (1 << IPCT_PROTOINFO) | (1 << IPCT_SEQADJ) | - (1 << IPCT_MARK), + (1 << IPCT_MARK) | + (1 << IPCT_SYNPROXY), ct, NETLINK_CB(skb).portid, nlmsg_report(nlh)); } @@ -2282,6 +2360,9 @@ static int __ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct) ctnetlink_dump_ct_seq_adj(skb, ct) < 0) goto nla_put_failure; + if (ctnetlink_dump_ct_synproxy(skb, ct) < 0) + goto nla_put_failure; + #ifdef CONFIG_NF_CONNTRACK_MARK if (ct->mark && ctnetlink_dump_mark(skb, ct) < 0) goto nla_put_failure; -- cgit v1.2.3 From 5adc1668ddc42bb44fd6d006cacad74ed0cbf49d Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 4 Mar 2018 09:28:53 +0100 Subject: netfilter: ebtables: add support for matching ICMP type and code We already have ICMPv6 type/code matches. This adds support for IPv4 ICMP matches in the same way. Signed-off-by: Matthias Schiffer Acked-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter_bridge/ebt_ip.h | 13 +++++++-- net/bridge/netfilter/ebt_ip.c | 43 +++++++++++++++++++++------- 2 files changed, 43 insertions(+), 13 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/netfilter_bridge/ebt_ip.h b/include/uapi/linux/netfilter_bridge/ebt_ip.h index 8e462fb1983f..4ed7fbb0a482 100644 --- a/include/uapi/linux/netfilter_bridge/ebt_ip.h +++ b/include/uapi/linux/netfilter_bridge/ebt_ip.h @@ -24,8 +24,9 @@ #define EBT_IP_PROTO 0x08 #define EBT_IP_SPORT 0x10 #define EBT_IP_DPORT 0x20 +#define EBT_IP_ICMP 0x40 #define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\ - EBT_IP_SPORT | EBT_IP_DPORT ) + EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP) #define EBT_IP_MATCH "ip" /* the same values are used for the invflags */ @@ -38,8 +39,14 @@ struct ebt_ip_info { __u8 protocol; __u8 bitmask; __u8 invflags; - __u16 sport[2]; - __u16 dport[2]; + union { + __u16 sport[2]; + __u8 icmp_type[2]; + }; + union { + __u16 dport[2]; + __u8 icmp_code[2]; + }; }; #endif diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index 2b46c50abce0..8cb8f8395768 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -19,9 +19,15 @@ #include #include -struct tcpudphdr { - __be16 src; - __be16 dst; +union pkthdr { + struct { + __be16 src; + __be16 dst; + } tcpudphdr; + struct { + u8 type; + u8 code; + } icmphdr; }; static bool @@ -30,8 +36,8 @@ ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par) const struct ebt_ip_info *info = par->matchinfo; const struct iphdr *ih; struct iphdr _iph; - const struct tcpudphdr *pptr; - struct tcpudphdr _ports; + const union pkthdr *pptr; + union pkthdr _pkthdr; ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); if (ih == NULL) @@ -50,29 +56,38 @@ ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par) if (info->bitmask & EBT_IP_PROTO) { if (NF_INVF(info, EBT_IP_PROTO, info->protocol != ih->protocol)) return false; - if (!(info->bitmask & EBT_IP_DPORT) && - !(info->bitmask & EBT_IP_SPORT)) + if (!(info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT | + EBT_IP_ICMP))) return true; if (ntohs(ih->frag_off) & IP_OFFSET) return false; + + /* min icmp headersize is 4, so sizeof(_pkthdr) is ok. */ pptr = skb_header_pointer(skb, ih->ihl*4, - sizeof(_ports), &_ports); + sizeof(_pkthdr), &_pkthdr); if (pptr == NULL) return false; if (info->bitmask & EBT_IP_DPORT) { - u32 dst = ntohs(pptr->dst); + u32 dst = ntohs(pptr->tcpudphdr.dst); if (NF_INVF(info, EBT_IP_DPORT, dst < info->dport[0] || dst > info->dport[1])) return false; } if (info->bitmask & EBT_IP_SPORT) { - u32 src = ntohs(pptr->src); + u32 src = ntohs(pptr->tcpudphdr.src); if (NF_INVF(info, EBT_IP_SPORT, src < info->sport[0] || src > info->sport[1])) return false; } + if ((info->bitmask & EBT_IP_ICMP) && + NF_INVF(info, EBT_IP_ICMP, + pptr->icmphdr.type < info->icmp_type[0] || + pptr->icmphdr.type > info->icmp_type[1] || + pptr->icmphdr.code < info->icmp_code[0] || + pptr->icmphdr.code > info->icmp_code[1])) + return false; } return true; } @@ -101,6 +116,14 @@ static int ebt_ip_mt_check(const struct xt_mtchk_param *par) return -EINVAL; if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1]) return -EINVAL; + if (info->bitmask & EBT_IP_ICMP) { + if ((info->invflags & EBT_IP_PROTO) || + info->protocol != IPPROTO_ICMP) + return -EINVAL; + if (info->icmp_type[0] > info->icmp_type[1] || + info->icmp_code[0] > info->icmp_code[1]) + return -EINVAL; + } return 0; } -- cgit v1.2.3 From 78d9f4d49bbecd101b4e5faf19f8f70719fee2ca Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 4 Mar 2018 09:28:54 +0100 Subject: netfilter: ebtables: add support for matching IGMP type We already have ICMPv6 type/code matches (which can be used to distinguish different types of MLD packets). Add support for IPv4 IGMP matches in the same way. Signed-off-by: Matthias Schiffer Acked-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter_bridge/ebt_ip.h | 4 +++- net/bridge/netfilter/ebt_ip.c | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/netfilter_bridge/ebt_ip.h b/include/uapi/linux/netfilter_bridge/ebt_ip.h index 4ed7fbb0a482..46d6261370b0 100644 --- a/include/uapi/linux/netfilter_bridge/ebt_ip.h +++ b/include/uapi/linux/netfilter_bridge/ebt_ip.h @@ -25,8 +25,9 @@ #define EBT_IP_SPORT 0x10 #define EBT_IP_DPORT 0x20 #define EBT_IP_ICMP 0x40 +#define EBT_IP_IGMP 0x80 #define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\ - EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP) + EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP | EBT_IP_IGMP) #define EBT_IP_MATCH "ip" /* the same values are used for the invflags */ @@ -42,6 +43,7 @@ struct ebt_ip_info { union { __u16 sport[2]; __u8 icmp_type[2]; + __u8 igmp_type[2]; }; union { __u16 dport[2]; diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index 8cb8f8395768..ffaa8ce2e724 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -28,6 +28,9 @@ union pkthdr { u8 type; u8 code; } icmphdr; + struct { + u8 type; + } igmphdr; }; static bool @@ -57,12 +60,12 @@ ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par) if (NF_INVF(info, EBT_IP_PROTO, info->protocol != ih->protocol)) return false; if (!(info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT | - EBT_IP_ICMP))) + EBT_IP_ICMP | EBT_IP_IGMP))) return true; if (ntohs(ih->frag_off) & IP_OFFSET) return false; - /* min icmp headersize is 4, so sizeof(_pkthdr) is ok. */ + /* min icmp/igmp headersize is 4, so sizeof(_pkthdr) is ok. */ pptr = skb_header_pointer(skb, ih->ihl*4, sizeof(_pkthdr), &_pkthdr); if (pptr == NULL) @@ -88,6 +91,11 @@ ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par) pptr->icmphdr.code < info->icmp_code[0] || pptr->icmphdr.code > info->icmp_code[1])) return false; + if ((info->bitmask & EBT_IP_IGMP) && + NF_INVF(info, EBT_IP_IGMP, + pptr->igmphdr.type < info->igmp_type[0] || + pptr->igmphdr.type > info->igmp_type[1])) + return false; } return true; } @@ -124,6 +132,13 @@ static int ebt_ip_mt_check(const struct xt_mtchk_param *par) info->icmp_code[0] > info->icmp_code[1]) return -EINVAL; } + if (info->bitmask & EBT_IP_IGMP) { + if ((info->invflags & EBT_IP_PROTO) || + info->protocol != IPPROTO_IGMP) + return -EINVAL; + if (info->igmp_type[0] > info->igmp_type[1]) + return -EINVAL; + } return 0; } -- cgit v1.2.3 From 2cb021f5de55b1d158fa18b0215a4613c3289a82 Mon Sep 17 00:00:00 2001 From: Dmitry Lebed Date: Thu, 1 Mar 2018 12:39:16 +0300 Subject: cfg80211/nl80211: add CAC_STARTED event CAC_STARTED event is needed for DFS offload feature and should be generated by driver/HW if DFS_OFFLOAD is enabled. Signed-off-by: Dmitry Lebed Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 3 +++ net/wireless/mlme.c | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index c13c84304be3..b8e989073cbb 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -5204,6 +5204,8 @@ enum nl80211_smps_mode { * non-operating channel is expired and no longer valid. New CAC must * be done on this channel before starting the operation. This is not * applicable for ETSI dfs domain where pre-CAC is valid for ever. + * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started, + * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled. */ enum nl80211_radar_event { NL80211_RADAR_DETECTED, @@ -5211,6 +5213,7 @@ enum nl80211_radar_event { NL80211_RADAR_CAC_ABORTED, NL80211_RADAR_NOP_FINISHED, NL80211_RADAR_PRE_CAC_EXPIRED, + NL80211_RADAR_CAC_STARTED, }; /** diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index bbb9907bfa86..6b6818dd76bd 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -888,14 +888,17 @@ void cfg80211_cac_event(struct net_device *netdev, sizeof(struct cfg80211_chan_def)); queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk); cfg80211_sched_dfs_chan_update(rdev); - break; + /* fall through */ case NL80211_RADAR_CAC_ABORTED: + wdev->cac_started = false; + break; + case NL80211_RADAR_CAC_STARTED: + wdev->cac_started = true; break; default: WARN_ON(1); return; } - wdev->cac_started = false; nl80211_radar_notify(rdev, chandef, event, netdev, gfp); } -- cgit v1.2.3 From 13cf6dec93e021ebd297619ea1926aea31b6430b Mon Sep 17 00:00:00 2001 From: Dmitry Lebed Date: Thu, 1 Mar 2018 12:39:15 +0300 Subject: cfg80211/nl80211: add DFS offload flag Add wiphy EXT_FEATURE flag to indicate that HW or driver does all DFS actions by itself. User-space functionality already implemented in hostapd using vendor-specific (QCA) OUI to advertise DFS offload support. Need to introduce generic flag to inform about DFS offload support. For devices with DFS_OFFLOAD flag set user-space will no longer need to issue CAC or do any actions in response to "radar detected" events. HW will do everything by itself and send events to user-space to indicate that CAC was started/finished, etc. Signed-off-by: Dmitrii Lebed Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 7 +++++++ net/wireless/nl80211.c | 12 ++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index b8e989073cbb..60fefc5a2ea4 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4999,6 +4999,12 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan. * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan. * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan. + * @NL80211_EXT_FEATURE_DFS_OFFLOAD: HW/driver will offload DFS actions. + * Device or driver will do all DFS-related actions by itself, + * informing user-space about CAC progress, radar detection event, + * channel change triggered by radar detection event. + * No need to start CAC from user-space, no need to react to + * "radar detected" event. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -5029,6 +5035,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_LOW_SPAN_SCAN, NL80211_EXT_FEATURE_LOW_POWER_SCAN, NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN, + NL80211_EXT_FEATURE_DFS_OFFLOAD, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a910150f8169..fe27ab443d01 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7551,12 +7551,13 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_chan_def chandef; enum nl80211_dfs_regions dfs_region; unsigned int cac_time_ms; int err; - dfs_region = reg_get_dfs_region(wdev->wiphy); + dfs_region = reg_get_dfs_region(wiphy); if (dfs_region == NL80211_DFS_UNSET) return -EINVAL; @@ -7570,17 +7571,20 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, if (wdev->cac_started) return -EBUSY; - err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, - wdev->iftype); + err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype); if (err < 0) return err; if (err == 0) return -EINVAL; - if (!cfg80211_chandef_dfs_usable(wdev->wiphy, &chandef)) + if (!cfg80211_chandef_dfs_usable(wiphy, &chandef)) return -EINVAL; + /* CAC start is offloaded to HW and can't be started manually */ + if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) + return -EOPNOTSUPP; + if (!rdev->ops->start_radar_detection) return -EOPNOTSUPP; -- cgit v1.2.3 From 9a2fe9b801f585baccf8352d82839dcd54b300cf Mon Sep 17 00:00:00 2001 From: Ruslan Bilovol Date: Wed, 21 Mar 2018 02:03:59 +0200 Subject: ALSA: usb: initial USB Audio Device Class 3.0 support Recently released USB Audio Class 3.0 specification introduces many significant changes comparing to previous versions, like - new Power Domains, support for LPM/L1 - new Cluster descriptor - changed layout of all class-specific descriptors - new High Capability descriptors - New class-specific String descriptors - new and removed units - additional sources for interrupts - removed Type II Audio Data Formats - ... and many other things (check spec) It also provides backward compatibility through multiple configurations, as well as requires mandatory support for BADD (Basic Audio Device Definition) on each ADC3.0 compliant device This patch adds initial support of UAC3 specification that is enough for Generic I/O Profile (BAOF, BAIF) device support from BADD document. Signed-off-by: Ruslan Bilovol Reviewed-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai --- include/linux/usb/audio-v2.h | 4 +- include/linux/usb/audio-v3.h | 395 +++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/usb/audio.h | 1 + sound/usb/card.c | 7 +- sound/usb/card.h | 2 +- sound/usb/clock.c | 228 +++++++++++++++++++++--- sound/usb/clock.h | 4 +- sound/usb/format.c | 91 ++++++++-- sound/usb/format.h | 6 +- sound/usb/mixer.c | 337 +++++++++++++++++++++++------------ sound/usb/stream.c | 365 +++++++++++++++++++++++++++++++++---- 11 files changed, 1246 insertions(+), 194 deletions(-) create mode 100644 include/linux/usb/audio-v3.h (limited to 'include/uapi/linux') diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index 3119d0ace7aa..2db83a191e78 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -34,12 +34,12 @@ * */ -static inline bool uac2_control_is_readable(u32 bmControls, u8 control) +static inline bool uac_v2v3_control_is_readable(u32 bmControls, u8 control) { return (bmControls >> (control * 2)) & 0x1; } -static inline bool uac2_control_is_writeable(u32 bmControls, u8 control) +static inline bool uac_v2v3_control_is_writeable(u32 bmControls, u8 control) { return (bmControls >> (control * 2)) & 0x2; } diff --git a/include/linux/usb/audio-v3.h b/include/linux/usb/audio-v3.h new file mode 100644 index 000000000000..a8959aaba0ae --- /dev/null +++ b/include/linux/usb/audio-v3.h @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Ruslan Bilovol + * + * This file holds USB constants and structures defined + * by the USB DEVICE CLASS DEFINITION FOR AUDIO DEVICES Release 3.0. + */ + +#ifndef __LINUX_USB_AUDIO_V3_H +#define __LINUX_USB_AUDIO_V3_H + +#include + +/* + * v1.0, v2.0 and v3.0 of this standard have many things in common. For the rest + * of the definitions, please refer to audio.h and audio-v2.h + */ + +/* All High Capability descriptors have these 2 fields at the beginning */ +struct uac3_hc_descriptor_header { + __le16 wLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __le16 wDescriptorID; +} __attribute__ ((packed)); + +/* 4.3.1 CLUSTER DESCRIPTOR HEADER */ +struct uac3_cluster_header_descriptor { + __le16 wLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __le16 wDescriptorID; + __u8 bNrChannels; +} __attribute__ ((packed)); + +/* 4.3.2.1 SEGMENTS */ +struct uac3_cluster_segment_descriptor { + __le16 wLength; + __u8 bSegmentType; + /* __u8[0]; segment-specific data */ +} __attribute__ ((packed)); + +/* 4.3.2.1.1 END SEGMENT */ +struct uac3_cluster_end_segment_descriptor { + __le16 wLength; + __u8 bSegmentType; /* Constant END_SEGMENT */ +} __attribute__ ((packed)); + +/* 4.3.2.1.3.1 INFORMATION SEGMENT */ +struct uac3_cluster_information_segment_descriptor { + __le16 wLength; + __u8 bSegmentType; + __u8 bChPurpose; + __u8 bChRelationship; + __u8 bChGroupID; +} __attribute__ ((packed)); + +/* 4.5.2 CLASS-SPECIFIC AC INTERFACE DESCRIPTOR */ +struct uac3_ac_header_descriptor { + __u8 bLength; /* 10 */ + __u8 bDescriptorType; /* CS_INTERFACE descriptor type */ + __u8 bDescriptorSubtype; /* HEADER descriptor subtype */ + __u8 bCategory; + + /* includes Clock Source, Unit, Terminal, and Power Domain desc. */ + __le16 wTotalLength; + + __le32 bmControls; +} __attribute__ ((packed)); + +/* 4.5.2.1 INPUT TERMINAL DESCRIPTOR */ +struct uac3_input_terminal_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bTerminalID; + __le16 wTerminalType; + __u8 bAssocTerminal; + __u8 bCSourceID; + __le32 bmControls; + __le16 wClusterDescrID; + __le16 wExTerminalDescrID; + __le16 wConnectorsDescrID; + __le16 wTerminalDescrStr; +} __attribute__((packed)); + +/* 4.5.2.2 OUTPUT TERMINAL DESCRIPTOR */ +struct uac3_output_terminal_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bTerminalID; + __le16 wTerminalType; + __u8 bAssocTerminal; + __u8 bSourceID; + __u8 bCSourceID; + __le32 bmControls; + __le16 wExTerminalDescrID; + __le16 wConnectorsDescrID; + __le16 wTerminalDescrStr; +} __attribute__((packed)); + +/* 4.5.2.7 FEATURE UNIT DESCRIPTOR */ +struct uac3_feature_unit_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bUnitID; + __u8 bSourceID; + /* bmaControls is actually u32, + * but u8 is needed for the hybrid parser */ + __u8 bmaControls[0]; /* variable length */ + /* wFeatureDescrStr omitted */ +} __attribute__((packed)); + +#define UAC3_DT_FEATURE_UNIT_SIZE(ch) (7 + ((ch) + 1) * 4) + +/* As above, but more useful for defining your own descriptors */ +#define DECLARE_UAC3_FEATURE_UNIT_DESCRIPTOR(ch) \ +struct uac3_feature_unit_descriptor_##ch { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubtype; \ + __u8 bUnitID; \ + __u8 bSourceID; \ + __le32 bmaControls[ch + 1]; \ + __le16 wFeatureDescrStr; \ +} __attribute__ ((packed)) + +/* 4.5.2.12 CLOCK SOURCE DESCRIPTOR */ +struct uac3_clock_source_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bClockID; + __u8 bmAttributes; + __le32 bmControls; + __u8 bReferenceTerminal; + __le16 wClockSourceStr; +} __attribute__((packed)); + +/* bmAttribute fields */ +#define UAC3_CLOCK_SOURCE_TYPE_EXT 0x0 +#define UAC3_CLOCK_SOURCE_TYPE_INT 0x1 +#define UAC3_CLOCK_SOURCE_ASYNC (0 << 2) +#define UAC3_CLOCK_SOURCE_SYNCED_TO_SOF (1 << 1) + +/* 4.5.2.13 CLOCK SELECTOR DESCRIPTOR */ +struct uac3_clock_selector_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bClockID; + __u8 bNrInPins; + __u8 baCSourceID[]; + /* bmControls and wCSelectorDescrStr omitted */ +} __attribute__((packed)); + +/* 4.5.2.14 CLOCK MULTIPLIER DESCRIPTOR */ +struct uac3_clock_multiplier_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bClockID; + __u8 bCSourceID; + __le32 bmControls; + __le16 wCMultiplierDescrStr; +} __attribute__((packed)); + +/* 4.5.2.15 POWER DOMAIN DESCRIPTOR */ +struct uac3_power_domain_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bPowerDomainID; + __le16 waRecoveryTime1; + __le16 waRecoveryTime2; + __u8 bNrEntities; + __u8 baEntityID[]; + /* wPDomainDescrStr omitted */ +} __attribute__((packed)); + +/* As above, but more useful for defining your own descriptors */ +#define DECLARE_UAC3_POWER_DOMAIN_DESCRIPTOR(n) \ +struct uac3_power_domain_descriptor_##n { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubtype; \ + __u8 bPowerDomainID; \ + __le16 waRecoveryTime1; \ + __le16 waRecoveryTime2; \ + __u8 bNrEntities; \ + __u8 baEntityID[n]; \ + __le16 wPDomainDescrStr; \ +} __attribute__ ((packed)) + +/* 4.7.2 CLASS-SPECIFIC AS INTERFACE DESCRIPTOR */ +struct uac3_as_header_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bTerminalLink; + __le32 bmControls; + __le16 wClusterDescrID; + __le64 bmFormats; + __u8 bSubslotSize; + __u8 bBitResolution; + __le16 bmAuxProtocols; + __u8 bControlSize; +} __attribute__((packed)); + +#define UAC3_FORMAT_TYPE_I_RAW_DATA (1 << 6) + +/* 4.8.1.2 CLASS-SPECIFIC AS ISOCHRONOUS AUDIO DATA ENDPOINT DESCRIPTOR */ +struct uac3_iso_endpoint_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __le32 bmControls; + __u8 bLockDelayUnits; + __le16 wLockDelay; +} __attribute__((packed)); + +/* 6.1 INTERRUPT DATA MESSAGE */ +struct uac3_interrupt_data_msg { + __u8 bInfo; + __u8 bSourceType; + __le16 wValue; + __le16 wIndex; +} __attribute__((packed)); + +/* A.2 AUDIO AUDIO FUNCTION SUBCLASS CODES */ +#define UAC3_FUNCTION_SUBCLASS_UNDEFINED 0x00 +#define UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0 0x01 +/* BADD profiles */ +#define UAC3_FUNCTION_SUBCLASS_GENERIC_IO 0x20 +#define UAC3_FUNCTION_SUBCLASS_HEADPHONE 0x21 +#define UAC3_FUNCTION_SUBCLASS_SPEAKER 0x22 +#define UAC3_FUNCTION_SUBCLASS_MICROPHONE 0x23 +#define UAC3_FUNCTION_SUBCLASS_HEADSET 0x24 +#define UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER 0x25 +#define UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE 0x26 + +/* A.7 AUDIO FUNCTION CATEGORY CODES */ +#define UAC3_FUNCTION_SUBCLASS_UNDEFINED 0x00 +#define UAC3_FUNCTION_DESKTOP_SPEAKER 0x01 +#define UAC3_FUNCTION_HOME_THEATER 0x02 +#define UAC3_FUNCTION_MICROPHONE 0x03 +#define UAC3_FUNCTION_HEADSET 0x04 +#define UAC3_FUNCTION_TELEPHONE 0x05 +#define UAC3_FUNCTION_CONVERTER 0x06 +#define UAC3_FUNCTION_SOUND_RECORDER 0x07 +#define UAC3_FUNCTION_IO_BOX 0x08 +#define UAC3_FUNCTION_MUSICAL_INSTRUMENT 0x09 +#define UAC3_FUNCTION_PRO_AUDIO 0x0a +#define UAC3_FUNCTION_AUDIO_VIDEO 0x0b +#define UAC3_FUNCTION_CONTROL_PANEL 0x0c +#define UAC3_FUNCTION_HEADPHONE 0x0d +#define UAC3_FUNCTION_GENERIC_SPEAKER 0x0e +#define UAC3_FUNCTION_HEADSET_ADAPTER 0x0f +#define UAC3_FUNCTION_SPEAKERPHONE 0x10 +#define UAC3_FUNCTION_OTHER 0xff + +/* A.8 AUDIO CLASS-SPECIFIC DESCRIPTOR TYPES */ +#define UAC3_CS_UNDEFINED 0x20 +#define UAC3_CS_DEVICE 0x21 +#define UAC3_CS_CONFIGURATION 0x22 +#define UAC3_CS_STRING 0x23 +#define UAC3_CS_INTERFACE 0x24 +#define UAC3_CS_ENDPOINT 0x25 +#define UAC3_CS_CLUSTER 0x26 + +/* A.10 CLUSTER DESCRIPTOR SEGMENT TYPES */ +#define UAC3_SEGMENT_UNDEFINED 0x00 +#define UAC3_CLUSTER_DESCRIPTION 0x01 +#define UAC3_CLUSTER_VENDOR_DEFINED 0x1F +#define UAC3_CHANNEL_INFORMATION 0x20 +#define UAC3_CHANNEL_AMBISONIC 0x21 +#define UAC3_CHANNEL_DESCRIPTION 0x22 +#define UAC3_CHANNEL_VENDOR_DEFINED 0xFE +#define UAC3_END_SEGMENT 0xFF + +/* A.11 CHANNEL PURPOSE DEFINITIONS */ +#define UAC3_PURPOSE_UNDEFINED 0x00 +#define UAC3_PURPOSE_GENERIC_AUDIO 0x01 +#define UAC3_PURPOSE_VOICE 0x02 +#define UAC3_PURPOSE_SPEECH 0x03 +#define UAC3_PURPOSE_AMBIENT 0x04 +#define UAC3_PURPOSE_REFERENCE 0x05 +#define UAC3_PURPOSE_ULTRASONIC 0x06 +#define UAC3_PURPOSE_VIBROKINETIC 0x07 +#define UAC3_PURPOSE_NON_AUDIO 0xFF + +/* A.12 CHANNEL RELATIONSHIP DEFINITIONS */ +#define UAC3_CH_RELATIONSHIP_UNDEFINED 0x00 +#define UAC3_CH_MONO 0x01 +#define UAC3_CH_LEFT 0x02 +#define UAC3_CH_RIGHT 0x03 +#define UAC3_CH_ARRAY 0x04 +#define UAC3_CH_PATTERN_X 0x20 +#define UAC3_CH_PATTERN_Y 0x21 +#define UAC3_CH_PATTERN_A 0x22 +#define UAC3_CH_PATTERN_B 0x23 +#define UAC3_CH_PATTERN_M 0x24 +#define UAC3_CH_PATTERN_S 0x25 +#define UAC3_CH_FRONT_LEFT 0x80 +#define UAC3_CH_FRONT_RIGHT 0x81 +#define UAC3_CH_FRONT_CENTER 0x82 +#define UAC3_CH_FRONT_LEFT_OF_CENTER 0x83 +#define UAC3_CH_FRONT_RIGHT_OF_CENTER 0x84 +#define UAC3_CH_FRONT_WIDE_LEFT 0x85 +#define UAC3_CH_FRONT_WIDE_RIGHT 0x86 +#define UAC3_CH_SIDE_LEFT 0x87 +#define UAC3_CH_SIDE_RIGHT 0x88 +#define UAC3_CH_SURROUND_ARRAY_LEFT 0x89 +#define UAC3_CH_SURROUND_ARRAY_RIGHT 0x8A +#define UAC3_CH_BACK_LEFT 0x8B +#define UAC3_CH_BACK_RIGHT 0x8C +#define UAC3_CH_BACK_CENTER 0x8D +#define UAC3_CH_BACK_LEFT_OF_CENTER 0x8E +#define UAC3_CH_BACK_RIGHT_OF_CENTER 0x8F +#define UAC3_CH_BACK_WIDE_LEFT 0x90 +#define UAC3_CH_BACK_WIDE_RIGHT 0x91 +#define UAC3_CH_TOP_CENTER 0x92 +#define UAC3_CH_TOP_FRONT_LEFT 0x93 +#define UAC3_CH_TOP_FRONT_RIGHT 0x94 +#define UAC3_CH_TOP_FRONT_CENTER 0x95 +#define UAC3_CH_TOP_FRONT_LOC 0x96 +#define UAC3_CH_TOP_FRONT_ROC 0x97 +#define UAC3_CH_TOP_FRONT_WIDE_LEFT 0x98 +#define UAC3_CH_TOP_FRONT_WIDE_RIGHT 0x99 +#define UAC3_CH_TOP_SIDE_LEFT 0x9A +#define UAC3_CH_TOP_SIDE_RIGHT 0x9B +#define UAC3_CH_TOP_SURR_ARRAY_LEFT 0x9C +#define UAC3_CH_TOP_SURR_ARRAY_RIGHT 0x9D +#define UAC3_CH_TOP_BACK_LEFT 0x9E +#define UAC3_CH_TOP_BACK_RIGHT 0x9F +#define UAC3_CH_TOP_BACK_CENTER 0xA0 +#define UAC3_CH_TOP_BACK_LOC 0xA1 +#define UAC3_CH_TOP_BACK_ROC 0xA2 +#define UAC3_CH_TOP_BACK_WIDE_LEFT 0xA3 +#define UAC3_CH_TOP_BACK_WIDE_RIGHT 0xA4 +#define UAC3_CH_BOTTOM_CENTER 0xA5 +#define UAC3_CH_BOTTOM_FRONT_LEFT 0xA6 +#define UAC3_CH_BOTTOM_FRONT_RIGHT 0xA7 +#define UAC3_CH_BOTTOM_FRONT_CENTER 0xA8 +#define UAC3_CH_BOTTOM_FRONT_LOC 0xA9 +#define UAC3_CH_BOTTOM_FRONT_ROC 0xAA +#define UAC3_CH_BOTTOM_FRONT_WIDE_LEFT 0xAB +#define UAC3_CH_BOTTOM_FRONT_WIDE_RIGHT 0xAC +#define UAC3_CH_BOTTOM_SIDE_LEFT 0xAD +#define UAC3_CH_BOTTOM_SIDE_RIGHT 0xAE +#define UAC3_CH_BOTTOM_SURR_ARRAY_LEFT 0xAF +#define UAC3_CH_BOTTOM_SURR_ARRAY_RIGHT 0xB0 +#define UAC3_CH_BOTTOM_BACK_LEFT 0xB1 +#define UAC3_CH_BOTTOM_BACK_RIGHT 0xB2 +#define UAC3_CH_BOTTOM_BACK_CENTER 0xB3 +#define UAC3_CH_BOTTOM_BACK_LOC 0xB4 +#define UAC3_CH_BOTTOM_BACK_ROC 0xB5 +#define UAC3_CH_BOTTOM_BACK_WIDE_LEFT 0xB6 +#define UAC3_CH_BOTTOM_BACK_WIDE_RIGHT 0xB7 +#define UAC3_CH_LOW_FREQUENCY_EFFECTS 0xB8 +#define UAC3_CH_LFE_LEFT 0xB9 +#define UAC3_CH_LFE_RIGHT 0xBA +#define UAC3_CH_HEADPHONE_LEFT 0xBB +#define UAC3_CH_HEADPHONE_RIGHT 0xBC + +/* A.15 AUDIO CLASS-SPECIFIC AC INTERFACE DESCRIPTOR SUBTYPES */ +/* see audio.h for the rest, which is identical to v1 */ +#define UAC3_EXTENDED_TERMINAL 0x04 +#define UAC3_MIXER_UNIT 0x05 +#define UAC3_SELECTOR_UNIT 0x06 +#define UAC3_FEATURE_UNIT 0x07 +#define UAC3_EFFECT_UNIT 0x08 +#define UAC3_PROCESSING_UNIT 0x09 +#define UAC3_EXTENSION_UNIT 0x0a +#define UAC3_CLOCK_SOURCE 0x0b +#define UAC3_CLOCK_SELECTOR 0x0c +#define UAC3_CLOCK_MULTIPLIER 0x0d +#define UAC3_SAMPLE_RATE_CONVERTER 0x0e +#define UAC3_CONNECTORS 0x0f +#define UAC3_POWER_DOMAIN 0x10 + +/* A.22 AUDIO CLASS-SPECIFIC REQUEST CODES */ +/* see audio-v2.h for the rest, which is identical to v2 */ +#define UAC3_CS_REQ_INTEN 0x04 +#define UAC3_CS_REQ_STRING 0x05 +#define UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR 0x06 + +/* A.23.1 AUDIOCONTROL INTERFACE CONTROL SELECTORS */ +#define UAC3_AC_CONTROL_UNDEFINED 0x00 +#define UAC3_AC_ACTIVE_INTERFACE_CONTROL 0x01 +#define UAC3_AC_POWER_DOMAIN_CONTROL 0x02 + +#endif /* __LINUX_USB_AUDIO_V3_H */ diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h index da3315ed1bcd..3a78e7145689 100644 --- a/include/uapi/linux/usb/audio.h +++ b/include/uapi/linux/usb/audio.h @@ -27,6 +27,7 @@ /* bInterfaceProtocol values to denote the version of the standard used */ #define UAC_VERSION_1 0x00 #define UAC_VERSION_2 0x20 +#define UAC_VERSION_3 0x30 /* A.2 Audio Interface Subclass Codes */ #define USB_SUBCLASS_AUDIOCONTROL 0x01 diff --git a/sound/usb/card.c b/sound/usb/card.c index 8018d56cfecc..4a1c6bb3dfa0 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -7,6 +7,7 @@ * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch) * + * Audio Class 3.0 support by Ruslan Bilovol * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,6 +45,7 @@ #include #include #include +#include #include #include @@ -281,7 +283,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) break; } - case UAC_VERSION_2: { + case UAC_VERSION_2: + case UAC_VERSION_3: { struct usb_interface_assoc_descriptor *assoc = usb_ifnum_to_if(dev, ctrlif)->intf_assoc; @@ -301,7 +304,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) } if (!assoc) { - dev_err(&dev->dev, "Audio class v2 interfaces need an interface association\n"); + dev_err(&dev->dev, "Audio class v2/v3 interfaces need an interface association\n"); return -EINVAL; } diff --git a/sound/usb/card.h b/sound/usb/card.h index ed87cc83eb47..1406292d50ec 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -22,7 +22,7 @@ struct audioformat { unsigned char endpoint; /* endpoint */ unsigned char ep_attr; /* endpoint attributes */ unsigned char datainterval; /* log_2 of data packet interval */ - unsigned char protocol; /* UAC_VERSION_1/2 */ + unsigned char protocol; /* UAC_VERSION_1/2/3 */ unsigned int maxpacksize; /* max. packet size */ unsigned int rates; /* rate bitmasks */ unsigned int rate_min, rate_max; /* min/max rates */ diff --git a/sound/usb/clock.c b/sound/usb/clock.c index eb3396ffba4c..25de7fe285d9 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,22 @@ static struct uac_clock_source_descriptor * return NULL; } +static struct uac3_clock_source_descriptor * + snd_usb_find_clock_source_v3(struct usb_host_interface *ctrl_iface, + int clock_id) +{ + struct uac3_clock_source_descriptor *cs = NULL; + + while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, + ctrl_iface->extralen, + cs, UAC3_CLOCK_SOURCE))) { + if (cs->bClockID == clock_id) + return cs; + } + + return NULL; +} + static struct uac_clock_selector_descriptor * snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface, int clock_id) @@ -69,6 +86,22 @@ static struct uac_clock_selector_descriptor * return NULL; } +static struct uac3_clock_selector_descriptor * + snd_usb_find_clock_selector_v3(struct usb_host_interface *ctrl_iface, + int clock_id) +{ + struct uac3_clock_selector_descriptor *cs = NULL; + + while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, + ctrl_iface->extralen, + cs, UAC3_CLOCK_SELECTOR))) { + if (cs->bClockID == clock_id) + return cs; + } + + return NULL; +} + static struct uac_clock_multiplier_descriptor * snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface, int clock_id) @@ -85,6 +118,22 @@ static struct uac_clock_multiplier_descriptor * return NULL; } +static struct uac3_clock_multiplier_descriptor * + snd_usb_find_clock_multiplier_v3(struct usb_host_interface *ctrl_iface, + int clock_id) +{ + struct uac3_clock_multiplier_descriptor *cs = NULL; + + while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, + ctrl_iface->extralen, + cs, UAC3_CLOCK_MULTIPLIER))) { + if (cs->bClockID == clock_id) + return cs; + } + + return NULL; +} + static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id) { unsigned char buf; @@ -138,19 +187,33 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i return ret; } -static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) +static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, + int protocol, + int source_id) { int err; unsigned char data; struct usb_device *dev = chip->dev; - struct uac_clock_source_descriptor *cs_desc = - snd_usb_find_clock_source(chip->ctrl_intf, source_id); - - if (!cs_desc) - return 0; + u32 bmControls; + + if (protocol == UAC_VERSION_3) { + struct uac3_clock_source_descriptor *cs_desc = + snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id); + + if (!cs_desc) + return 0; + bmControls = le32_to_cpu(cs_desc->bmControls); + } else { /* UAC_VERSION_1/2 */ + struct uac_clock_source_descriptor *cs_desc = + snd_usb_find_clock_source(chip->ctrl_intf, source_id); + + if (!cs_desc) + return 0; + bmControls = cs_desc->bmControls; + } /* If a clock source can't tell us whether it's valid, we assume it is */ - if (!uac2_control_is_readable(cs_desc->bmControls, + if (!uac_v2v3_control_is_readable(bmControls, UAC2_CS_CONTROL_CLOCK_VALID - 1)) return 1; @@ -170,9 +233,8 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) return !!data; } -static int __uac_clock_find_source(struct snd_usb_audio *chip, - int entity_id, unsigned long *visited, - bool validate) +static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, + unsigned long *visited, bool validate) { struct uac_clock_source_descriptor *source; struct uac_clock_selector_descriptor *selector; @@ -191,7 +253,8 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id); if (source) { entity_id = source->bClockID; - if (validate && !uac_clock_source_is_valid(chip, entity_id)) { + if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_2, + entity_id)) { usb_audio_err(chip, "clock source %d is not valid, cannot use\n", entity_id); @@ -260,6 +323,97 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, return -EINVAL; } +static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, + unsigned long *visited, bool validate) +{ + struct uac3_clock_source_descriptor *source; + struct uac3_clock_selector_descriptor *selector; + struct uac3_clock_multiplier_descriptor *multiplier; + + entity_id &= 0xff; + + if (test_and_set_bit(entity_id, visited)) { + usb_audio_warn(chip, + "%s(): recursive clock topology detected, id %d.\n", + __func__, entity_id); + return -EINVAL; + } + + /* first, see if the ID we're looking for is a clock source already */ + source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id); + if (source) { + entity_id = source->bClockID; + if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_3, + entity_id)) { + usb_audio_err(chip, + "clock source %d is not valid, cannot use\n", + entity_id); + return -ENXIO; + } + return entity_id; + } + + selector = snd_usb_find_clock_selector_v3(chip->ctrl_intf, entity_id); + if (selector) { + int ret, i, cur; + + /* the entity ID we are looking for is a selector. + * find out what it currently selects */ + ret = uac_clock_selector_get_val(chip, selector->bClockID); + if (ret < 0) + return ret; + + /* Selector values are one-based */ + + if (ret > selector->bNrInPins || ret < 1) { + usb_audio_err(chip, + "%s(): selector reported illegal value, id %d, ret %d\n", + __func__, selector->bClockID, ret); + + return -EINVAL; + } + + cur = ret; + ret = __uac3_clock_find_source(chip, selector->baCSourceID[ret - 1], + visited, validate); + if (!validate || ret > 0 || !chip->autoclock) + return ret; + + /* The current clock source is invalid, try others. */ + for (i = 1; i <= selector->bNrInPins; i++) { + int err; + + if (i == cur) + continue; + + ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1], + visited, true); + if (ret < 0) + continue; + + err = uac_clock_selector_set_val(chip, entity_id, i); + if (err < 0) + continue; + + usb_audio_info(chip, + "found and selected valid clock source %d\n", + ret); + return ret; + } + + return -ENXIO; + } + + /* FIXME: multipliers only act as pass-thru element for now */ + multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf, + entity_id); + if (multiplier) + return __uac3_clock_find_source(chip, multiplier->bCSourceID, + visited, validate); + + return -EINVAL; +} + /* * For all kinds of sample rate settings and other device queries, * the clock source (end-leaf) must be used. However, clock selectors, @@ -271,12 +425,22 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, * * Returns the clock source UnitID (>=0) on success, or an error. */ -int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id, - bool validate) +int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol, + int entity_id, bool validate) { DECLARE_BITMAP(visited, 256); memset(visited, 0, sizeof(visited)); - return __uac_clock_find_source(chip, entity_id, visited, validate); + + switch (protocol) { + case UAC_VERSION_2: + return __uac_clock_find_source(chip, entity_id, visited, + validate); + case UAC_VERSION_3: + return __uac3_clock_find_source(chip, entity_id, visited, + validate); + default: + return -EINVAL; + } } static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, @@ -335,7 +499,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, return 0; } -static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface, +static int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, int altsetting, int clock) { struct usb_device *dev = chip->dev; @@ -348,7 +512,7 @@ static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface, snd_usb_ctrl_intf(chip) | (clock << 8), &data, sizeof(data)); if (err < 0) { - dev_warn(&dev->dev, "%d:%d: cannot get freq (v2): err %d\n", + dev_warn(&dev->dev, "%d:%d: cannot get freq (v2/v3): err %d\n", iface, altsetting, err); return 0; } @@ -356,7 +520,7 @@ static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface, return le32_to_cpu(data); } -static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, +static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt, int rate) { @@ -365,18 +529,30 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, int err, cur_rate, prev_rate; int clock; bool writeable; - struct uac_clock_source_descriptor *cs_desc; + u32 bmControls; - clock = snd_usb_clock_find_source(chip, fmt->clock, true); + clock = snd_usb_clock_find_source(chip, fmt->protocol, + fmt->clock, true); if (clock < 0) return clock; - prev_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock); + prev_rate = get_sample_rate_v2v3(chip, iface, fmt->altsetting, clock); if (prev_rate == rate) return 0; - cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock); - writeable = uac2_control_is_writeable(cs_desc->bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1); + if (fmt->protocol == UAC_VERSION_3) { + struct uac3_clock_source_descriptor *cs_desc; + + cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, clock); + bmControls = le32_to_cpu(cs_desc->bmControls); + } else { + struct uac_clock_source_descriptor *cs_desc; + + cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock); + bmControls = cs_desc->bmControls; + } + + writeable = uac_v2v3_control_is_writeable(bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1); if (writeable) { data = cpu_to_le32(rate); err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, @@ -386,12 +562,13 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, &data, sizeof(data)); if (err < 0) { usb_audio_err(chip, - "%d:%d: cannot set freq %d (v2): err %d\n", + "%d:%d: cannot set freq %d (v2/v3): err %d\n", iface, fmt->altsetting, rate, err); return err; } - cur_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock); + cur_rate = get_sample_rate_v2v3(chip, iface, + fmt->altsetting, clock); } else { cur_rate = prev_rate; } @@ -430,7 +607,8 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, return set_sample_rate_v1(chip, iface, alts, fmt, rate); case UAC_VERSION_2: - return set_sample_rate_v2(chip, iface, alts, fmt, rate); + case UAC_VERSION_3: + return set_sample_rate_v2v3(chip, iface, alts, fmt, rate); } } diff --git a/sound/usb/clock.h b/sound/usb/clock.h index 87557cae1a0b..076e31b79ee0 100644 --- a/sound/usb/clock.h +++ b/sound/usb/clock.h @@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt, int rate); -int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id, - bool validate); +int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol, + int entity_id, bool validate); #endif /* __USBAUDIO_CLOCK_H */ diff --git a/sound/usb/format.c b/sound/usb/format.c index 2c44386e5569..edbe67eeddfa 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -39,11 +40,11 @@ * @dev: usb device * @fp: audioformat record * @format: the format tag (wFormatTag) - * @fmt: the format type descriptor + * @fmt: the format type descriptor (v1/v2) or AudioStreaming descriptor (v3) */ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, struct audioformat *fp, - unsigned int format, void *_fmt) + u64 format, void *_fmt) { int sample_width, sample_bytes; u64 pcm_formats = 0; @@ -69,6 +70,18 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, format <<= 1; break; } + case UAC_VERSION_3: { + struct uac3_as_header_descriptor *as = _fmt; + + sample_width = as->bBitResolution; + sample_bytes = as->bSubslotSize; + + if (format & UAC3_FORMAT_TYPE_I_RAW_DATA) + pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL; + + format <<= 1; + break; + } } if ((pcm_formats == 0) && @@ -137,7 +150,7 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, } if (format & ~0x3f) { usb_audio_info(chip, - "%u:%d : unsupported format bits %#x\n", + "%u:%d : unsupported format bits %#llx\n", fp->iface, fp->altsetting, format); } @@ -281,15 +294,16 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, /* * parse the format descriptor and stores the possible sample rates - * on the audioformat table (audio class v2). + * on the audioformat table (audio class v2 and v3). */ -static int parse_audio_format_rates_v2(struct snd_usb_audio *chip, +static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip, struct audioformat *fp) { struct usb_device *dev = chip->dev; unsigned char tmp[2], *data; int nr_triplets, data_size, ret = 0; - int clock = snd_usb_clock_find_source(chip, fp->clock, false); + int clock = snd_usb_clock_find_source(chip, fp->protocol, + fp->clock, false); if (clock < 0) { dev_err(&dev->dev, @@ -368,13 +382,30 @@ err: * parse the format type I and III descriptors */ static int parse_audio_format_i(struct snd_usb_audio *chip, - struct audioformat *fp, unsigned int format, - struct uac_format_type_i_continuous_descriptor *fmt) + struct audioformat *fp, u64 format, + void *_fmt) { snd_pcm_format_t pcm_format; + unsigned int fmt_type; int ret; - if (fmt->bFormatType == UAC_FORMAT_TYPE_III) { + switch (fp->protocol) { + default: + case UAC_VERSION_1: + case UAC_VERSION_2: { + struct uac_format_type_i_continuous_descriptor *fmt = _fmt; + + fmt_type = fmt->bFormatType; + break; + } + case UAC_VERSION_3: { + /* fp->fmt_type is already set in this case */ + fmt_type = fp->fmt_type; + break; + } + } + + if (fmt_type == UAC_FORMAT_TYPE_III) { /* FIXME: the format type is really IECxxx * but we give normal PCM format to get the existing * apps working... @@ -393,7 +424,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, } fp->formats = pcm_format_to_bits(pcm_format); } else { - fp->formats = parse_audio_format_i_type(chip, fp, format, fmt); + fp->formats = parse_audio_format_i_type(chip, fp, format, _fmt); if (!fp->formats) return -EINVAL; } @@ -405,15 +436,20 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, */ switch (fp->protocol) { default: - case UAC_VERSION_1: + case UAC_VERSION_1: { + struct uac_format_type_i_continuous_descriptor *fmt = _fmt; + fp->channels = fmt->bNrChannels; ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7); break; + } case UAC_VERSION_2: + case UAC_VERSION_3: { /* fp->channels is already set in this case */ - ret = parse_audio_format_rates_v2(chip, fp); + ret = parse_audio_format_rates_v2v3(chip, fp); break; } + } if (fp->channels < 1) { usb_audio_err(chip, @@ -430,7 +466,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, */ static int parse_audio_format_ii(struct snd_usb_audio *chip, struct audioformat *fp, - int format, void *_fmt) + u64 format, void *_fmt) { int brate, framesize, ret; @@ -445,7 +481,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, break; default: usb_audio_info(chip, - "%u:%d : unknown format tag %#x is detected. processed as MPEG.\n", + "%u:%d : unknown format tag %#llx is detected. processed as MPEG.\n", fp->iface, fp->altsetting, format); fp->formats = SNDRV_PCM_FMTBIT_MPEG; break; @@ -470,7 +506,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, framesize = le16_to_cpu(fmt->wSamplesPerFrame); usb_audio_info(chip, "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize); fp->frame_size = framesize; - ret = parse_audio_format_rates_v2(chip, fp); + ret = parse_audio_format_rates_v2v3(chip, fp); break; } } @@ -479,7 +515,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, } int snd_usb_parse_audio_format(struct snd_usb_audio *chip, - struct audioformat *fp, unsigned int format, + struct audioformat *fp, u64 format, struct uac_format_type_i_continuous_descriptor *fmt, int stream) { @@ -520,3 +556,26 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip, return 0; } +int snd_usb_parse_audio_format_v3(struct snd_usb_audio *chip, + struct audioformat *fp, + struct uac3_as_header_descriptor *as, + int stream) +{ + u64 format = le64_to_cpu(as->bmFormats); + int err; + + /* + * Type I format bits are D0..D6 + * This test works because type IV is not supported + */ + if (format & 0x7f) + fp->fmt_type = UAC_FORMAT_TYPE_I; + else + fp->fmt_type = UAC_FORMAT_TYPE_III; + + err = parse_audio_format_i(chip, fp, format, as); + if (err < 0) + return err; + + return 0; +} diff --git a/sound/usb/format.h b/sound/usb/format.h index 8c3ff9ce0824..e70171892f32 100644 --- a/sound/usb/format.h +++ b/sound/usb/format.h @@ -3,8 +3,12 @@ #define __USBAUDIO_FORMAT_H int snd_usb_parse_audio_format(struct snd_usb_audio *chip, - struct audioformat *fp, unsigned int format, + struct audioformat *fp, u64 format, struct uac_format_type_i_continuous_descriptor *fmt, int stream); +int snd_usb_parse_audio_format_v3(struct snd_usb_audio *chip, + struct audioformat *fp, + struct uac3_as_header_descriptor *as, + int stream); #endif /* __USBAUDIO_FORMAT_H */ diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 06b22624ab7a..1c02f7373eda 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -189,7 +190,7 @@ static void *find_audio_control_unit(struct mixer_build *state, USB_DT_CS_INTERFACE)) != NULL) { if (hdr->bLength >= 4 && hdr->bDescriptorSubtype >= UAC_INPUT_TERMINAL && - hdr->bDescriptorSubtype <= UAC2_SAMPLE_RATE_CONVERTER && + hdr->bDescriptorSubtype <= UAC3_SAMPLE_RATE_CONVERTER && hdr->bUnitID == unit) return hdr; } @@ -468,9 +469,10 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, validx += cval->idx_off; + if (cval->head.mixer->protocol == UAC_VERSION_1) { val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; - } else { /* UAC_VERSION_2 */ + } else { /* UAC_VERSION_2/3 */ val_len = uac2_ctl_value_size(cval->val_type); /* FIXME */ @@ -723,6 +725,7 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term) { + int protocol = state->mixer->protocol; int err; void *p1; @@ -730,16 +733,104 @@ static int check_input_term(struct mixer_build *state, int id, while ((p1 = find_audio_control_unit(state, id)) != NULL) { unsigned char *hdr = p1; term->id = id; - switch (hdr[2]) { - case UAC_INPUT_TERMINAL: - if (state->mixer->protocol == UAC_VERSION_1) { - struct uac_input_terminal_descriptor *d = p1; - term->type = le16_to_cpu(d->wTerminalType); - term->channels = d->bNrChannels; - term->chconfig = le16_to_cpu(d->wChannelConfig); - term->name = d->iTerminal; - } else { /* UAC_VERSION_2 */ - struct uac2_input_terminal_descriptor *d = p1; + + if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { + switch (hdr[2]) { + case UAC_INPUT_TERMINAL: + if (protocol == UAC_VERSION_1) { + struct uac_input_terminal_descriptor *d = p1; + + term->type = le16_to_cpu(d->wTerminalType); + term->channels = d->bNrChannels; + term->chconfig = le16_to_cpu(d->wChannelConfig); + term->name = d->iTerminal; + } else { /* UAC_VERSION_2 */ + struct uac2_input_terminal_descriptor *d = p1; + + /* call recursively to verify that the + * referenced clock entity is valid */ + err = check_input_term(state, d->bCSourceID, term); + if (err < 0) + return err; + + /* save input term properties after recursion, + * to ensure they are not overriden by the + * recursion calls */ + term->id = id; + term->type = le16_to_cpu(d->wTerminalType); + term->channels = d->bNrChannels; + term->chconfig = le32_to_cpu(d->bmChannelConfig); + term->name = d->iTerminal; + } + return 0; + case UAC_FEATURE_UNIT: { + /* the header is the same for v1 and v2 */ + struct uac_feature_unit_descriptor *d = p1; + + id = d->bSourceID; + break; /* continue to parse */ + } + case UAC_MIXER_UNIT: { + struct uac_mixer_unit_descriptor *d = p1; + + term->type = d->bDescriptorSubtype << 16; /* virtual type */ + term->channels = uac_mixer_unit_bNrChannels(d); + term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol); + term->name = uac_mixer_unit_iMixer(d); + return 0; + } + case UAC_SELECTOR_UNIT: + case UAC2_CLOCK_SELECTOR: { + struct uac_selector_unit_descriptor *d = p1; + /* call recursively to retrieve the channel info */ + err = check_input_term(state, d->baSourceID[0], term); + if (err < 0) + return err; + term->type = d->bDescriptorSubtype << 16; /* virtual type */ + term->id = id; + term->name = uac_selector_unit_iSelector(d); + return 0; + } + case UAC1_PROCESSING_UNIT: + case UAC1_EXTENSION_UNIT: + /* UAC2_PROCESSING_UNIT_V2 */ + /* UAC2_EFFECT_UNIT */ + case UAC2_EXTENSION_UNIT_V2: { + struct uac_processing_unit_descriptor *d = p1; + + if (protocol == UAC_VERSION_2 && + hdr[2] == UAC2_EFFECT_UNIT) { + /* UAC2/UAC1 unit IDs overlap here in an + * uncompatible way. Ignore this unit for now. + */ + return 0; + } + + if (d->bNrInPins) { + id = d->baSourceID[0]; + break; /* continue to parse */ + } + term->type = d->bDescriptorSubtype << 16; /* virtual type */ + term->channels = uac_processing_unit_bNrChannels(d); + term->chconfig = uac_processing_unit_wChannelConfig(d, protocol); + term->name = uac_processing_unit_iProcessing(d, protocol); + return 0; + } + case UAC2_CLOCK_SOURCE: { + struct uac_clock_source_descriptor *d = p1; + + term->type = d->bDescriptorSubtype << 16; /* virtual type */ + term->id = id; + term->name = d->iClockSource; + return 0; + } + default: + return -ENODEV; + } + } else { /* UAC_VERSION_3 */ + switch (hdr[2]) { + case UAC_INPUT_TERMINAL: { + struct uac3_input_terminal_descriptor *d = p1; /* call recursively to verify that the * referenced clock entity is valid */ @@ -752,71 +843,31 @@ static int check_input_term(struct mixer_build *state, int id, * recursion calls */ term->id = id; term->type = le16_to_cpu(d->wTerminalType); - term->channels = d->bNrChannels; - term->chconfig = le32_to_cpu(d->bmChannelConfig); - term->name = d->iTerminal; - } - return 0; - case UAC_FEATURE_UNIT: { - /* the header is the same for v1 and v2 */ - struct uac_feature_unit_descriptor *d = p1; - id = d->bSourceID; - break; /* continue to parse */ - } - case UAC_MIXER_UNIT: { - struct uac_mixer_unit_descriptor *d = p1; - term->type = d->bDescriptorSubtype << 16; /* virtual type */ - term->channels = uac_mixer_unit_bNrChannels(d); - term->chconfig = uac_mixer_unit_wChannelConfig(d, state->mixer->protocol); - term->name = uac_mixer_unit_iMixer(d); - return 0; - } - case UAC_SELECTOR_UNIT: - case UAC2_CLOCK_SELECTOR: { - struct uac_selector_unit_descriptor *d = p1; - /* call recursively to retrieve the channel info */ - err = check_input_term(state, d->baSourceID[0], term); - if (err < 0) - return err; - term->type = d->bDescriptorSubtype << 16; /* virtual type */ - term->id = id; - term->name = uac_selector_unit_iSelector(d); - return 0; - } - case UAC1_PROCESSING_UNIT: - case UAC1_EXTENSION_UNIT: - /* UAC2_PROCESSING_UNIT_V2 */ - /* UAC2_EFFECT_UNIT */ - case UAC2_EXTENSION_UNIT_V2: { - struct uac_processing_unit_descriptor *d = p1; - - if (state->mixer->protocol == UAC_VERSION_2 && - hdr[2] == UAC2_EFFECT_UNIT) { - /* UAC2/UAC1 unit IDs overlap here in an - * uncompatible way. Ignore this unit for now. - */ + + /* REVISIT: UAC3 IT doesn't have channels/cfg */ + term->channels = 0; + term->chconfig = 0; + + term->name = le16_to_cpu(d->wTerminalDescrStr); return 0; } + case UAC3_FEATURE_UNIT: { + struct uac3_feature_unit_descriptor *d = p1; - if (d->bNrInPins) { - id = d->baSourceID[0]; + id = d->bSourceID; break; /* continue to parse */ } - term->type = d->bDescriptorSubtype << 16; /* virtual type */ - term->channels = uac_processing_unit_bNrChannels(d); - term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol); - term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol); - return 0; - } - case UAC2_CLOCK_SOURCE: { - struct uac_clock_source_descriptor *d = p1; - term->type = d->bDescriptorSubtype << 16; /* virtual type */ - term->id = id; - term->name = d->iClockSource; - return 0; - } - default: - return -ENODEV; + case UAC3_CLOCK_SOURCE: { + struct uac3_clock_source_descriptor *d = p1; + + term->type = d->bDescriptorSubtype << 16; /* virtual type */ + term->id = id; + term->name = le16_to_cpu(d->wClockSourceStr); + return 0; + } + default: + return -ENODEV; + } } } return -ENODEV; @@ -1423,7 +1474,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, * The only property of this unit we are interested in is the * clock source validity. If that isn't readable, just bail out. */ - if (!uac2_control_is_readable(hdr->bmControls, + if (!uac_v2v3_control_is_readable(hdr->bmControls, ilog2(UAC2_CS_CONTROL_CLOCK_VALID))) return 0; @@ -1439,7 +1490,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, cval->val_type = USB_MIXER_BOOLEAN; cval->control = UAC2_CS_CONTROL_CLOCK_VALID; - if (uac2_control_is_writeable(hdr->bmControls, + if (uac_v2v3_control_is_writeable(hdr->bmControls, ilog2(UAC2_CS_CONTROL_CLOCK_VALID))) kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); else { @@ -1502,7 +1553,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unitid); return -EINVAL; } - } else { + } else if (state->mixer->protocol == UAC_VERSION_2) { struct uac2_feature_unit_descriptor *ftr = _ftr; if (hdr->bLength < 6) { usb_audio_err(state->chip, @@ -1519,6 +1570,24 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unitid); return -EINVAL; } + } else { /* UAC_VERSION_3 */ + struct uac3_feature_unit_descriptor *ftr = _ftr; + + if (hdr->bLength < 7) { + usb_audio_err(state->chip, + "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n", + unitid); + return -EINVAL; + } + csize = 4; + channels = (ftr->bLength - 7) / 4 - 1; + bmaControls = ftr->bmaControls; + if (hdr->bLength < 7 + csize) { + usb_audio_err(state->chip, + "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n", + unitid); + return -EINVAL; + } } /* parse the source unit */ @@ -1577,7 +1646,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0); } - } else { /* UAC_VERSION_2 */ + } else { /* UAC_VERSION_2/3 */ for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) { unsigned int ch_bits = 0; unsigned int ch_read_only = 0; @@ -1587,9 +1656,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize); - if (uac2_control_is_readable(mask, i)) { + if (uac_v2v3_control_is_readable(mask, i)) { ch_bits |= (1 << j); - if (!uac2_control_is_writeable(mask, i)) + if (!uac_v2v3_control_is_writeable(mask, i)) ch_read_only |= (1 << j); } } @@ -1610,9 +1679,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, if (ch_bits & 1) build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, ch_read_only); - if (uac2_control_is_readable(master_bits, i)) + if (uac_v2v3_control_is_readable(master_bits, i)) build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, - !uac2_control_is_writeable(master_bits, i)); + !uac_v2v3_control_is_writeable(master_bits, i)); } } @@ -2220,6 +2289,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, static int parse_audio_unit(struct mixer_build *state, int unitid) { unsigned char *p1; + int protocol = state->mixer->protocol; if (test_and_set_bit(unitid, state->unitbitmap)) return 0; /* the unit already visited */ @@ -2230,36 +2300,61 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) return -EINVAL; } - switch (p1[2]) { - case UAC_INPUT_TERMINAL: - return 0; /* NOP */ - case UAC_MIXER_UNIT: - return parse_audio_mixer_unit(state, unitid, p1); - case UAC2_CLOCK_SOURCE: - return parse_clock_source_unit(state, unitid, p1); - case UAC_SELECTOR_UNIT: - case UAC2_CLOCK_SELECTOR: - return parse_audio_selector_unit(state, unitid, p1); - case UAC_FEATURE_UNIT: - return parse_audio_feature_unit(state, unitid, p1); - case UAC1_PROCESSING_UNIT: - /* UAC2_EFFECT_UNIT has the same value */ - if (state->mixer->protocol == UAC_VERSION_1) - return parse_audio_processing_unit(state, unitid, p1); - else - return 0; /* FIXME - effect units not implemented yet */ - case UAC1_EXTENSION_UNIT: - /* UAC2_PROCESSING_UNIT_V2 has the same value */ - if (state->mixer->protocol == UAC_VERSION_1) + if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { + switch (p1[2]) { + case UAC_INPUT_TERMINAL: + return 0; /* NOP */ + case UAC_MIXER_UNIT: + return parse_audio_mixer_unit(state, unitid, p1); + case UAC2_CLOCK_SOURCE: + return parse_clock_source_unit(state, unitid, p1); + case UAC_SELECTOR_UNIT: + case UAC2_CLOCK_SELECTOR: + return parse_audio_selector_unit(state, unitid, p1); + case UAC_FEATURE_UNIT: + return parse_audio_feature_unit(state, unitid, p1); + case UAC1_PROCESSING_UNIT: + /* UAC2_EFFECT_UNIT has the same value */ + if (protocol == UAC_VERSION_1) + return parse_audio_processing_unit(state, unitid, p1); + else + return 0; /* FIXME - effect units not implemented yet */ + case UAC1_EXTENSION_UNIT: + /* UAC2_PROCESSING_UNIT_V2 has the same value */ + if (protocol == UAC_VERSION_1) + return parse_audio_extension_unit(state, unitid, p1); + else /* UAC_VERSION_2 */ + return parse_audio_processing_unit(state, unitid, p1); + case UAC2_EXTENSION_UNIT_V2: return parse_audio_extension_unit(state, unitid, p1); - else /* UAC_VERSION_2 */ + default: + usb_audio_err(state->chip, + "unit %u: unexpected type 0x%02x\n", unitid, p1[2]); + return -EINVAL; + } + } else { /* UAC_VERSION_3 */ + switch (p1[2]) { + case UAC_INPUT_TERMINAL: + return 0; /* NOP */ + case UAC3_MIXER_UNIT: + return parse_audio_mixer_unit(state, unitid, p1); + case UAC3_CLOCK_SOURCE: + return parse_clock_source_unit(state, unitid, p1); + case UAC3_CLOCK_SELECTOR: + return parse_audio_selector_unit(state, unitid, p1); + case UAC3_FEATURE_UNIT: + return parse_audio_feature_unit(state, unitid, p1); + case UAC3_EFFECT_UNIT: + return 0; /* FIXME - effect units not implemented yet */ + case UAC3_PROCESSING_UNIT: return parse_audio_processing_unit(state, unitid, p1); - case UAC2_EXTENSION_UNIT_V2: - return parse_audio_extension_unit(state, unitid, p1); - default: - usb_audio_err(state->chip, - "unit %u: unexpected type 0x%02x\n", unitid, p1[2]); - return -EINVAL; + case UAC3_EXTENSION_UNIT: + return parse_audio_extension_unit(state, unitid, p1); + default: + usb_audio_err(state->chip, + "unit %u: unexpected type 0x%02x\n", unitid, p1[2]); + return -EINVAL; + } } } @@ -2330,7 +2425,7 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) err = parse_audio_unit(&state, desc->bSourceID); if (err < 0 && err != -EINVAL) return err; - } else { /* UAC_VERSION_2 */ + } else if (mixer->protocol == UAC_VERSION_2) { struct uac2_output_terminal_descriptor *desc = p; if (desc->bLength < sizeof(*desc)) @@ -2351,6 +2446,27 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) err = parse_audio_unit(&state, desc->bCSourceID); if (err < 0 && err != -EINVAL) return err; + } else { /* UAC_VERSION_3 */ + struct uac3_output_terminal_descriptor *desc = p; + + if (desc->bLength < sizeof(*desc)) + continue; /* invalid descriptor? */ + /* mark terminal ID as visited */ + set_bit(desc->bTerminalID, state.unitbitmap); + state.oterm.id = desc->bTerminalID; + state.oterm.type = le16_to_cpu(desc->wTerminalType); + state.oterm.name = le16_to_cpu(desc->wTerminalDescrStr); + err = parse_audio_unit(&state, desc->bSourceID); + if (err < 0 && err != -EINVAL) + return err; + + /* + * For UAC3, use the same approach to also add the + * clock selectors + */ + err = parse_audio_unit(&state, desc->bCSourceID); + if (err < 0 && err != -EINVAL) + return err; } } @@ -2597,6 +2713,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, case UAC_VERSION_2: mixer->protocol = UAC_VERSION_2; break; + case UAC_VERSION_3: + mixer->protocol = UAC_VERSION_3; + break; } if ((err = snd_usb_mixer_controls(mixer)) < 0 || diff --git a/sound/usb/stream.c b/sound/usb/stream.c index dbbe854745ab..6a8f5843334e 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -311,6 +312,153 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, return chmap; } +/* UAC3 device stores channels information in Cluster Descriptors */ +static struct +snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor + *cluster) +{ + unsigned int channels = cluster->bNrChannels; + struct snd_pcm_chmap_elem *chmap; + void *p = cluster; + int len, c; + + if (channels > ARRAY_SIZE(chmap->map)) + return NULL; + + chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); + if (!chmap) + return NULL; + + len = le16_to_cpu(cluster->wLength); + c = 0; + p += sizeof(struct uac3_cluster_header_descriptor); + + while (((p - (void *)cluster) < len) && (c < channels)) { + struct uac3_cluster_segment_descriptor *cs_desc = p; + u16 cs_len; + u8 cs_type; + + cs_len = le16_to_cpu(cs_desc->wLength); + cs_type = cs_desc->bSegmentType; + + if (cs_type == UAC3_CHANNEL_INFORMATION) { + struct uac3_cluster_information_segment_descriptor *is = p; + unsigned char map; + + /* + * TODO: this conversion is not complete, update it + * after adding UAC3 values to asound.h + */ + switch (is->bChPurpose) { + case UAC3_CH_MONO: + map = SNDRV_CHMAP_MONO; + break; + case UAC3_CH_LEFT: + case UAC3_CH_FRONT_LEFT: + case UAC3_CH_HEADPHONE_LEFT: + map = SNDRV_CHMAP_FL; + break; + case UAC3_CH_RIGHT: + case UAC3_CH_FRONT_RIGHT: + case UAC3_CH_HEADPHONE_RIGHT: + map = SNDRV_CHMAP_FR; + break; + case UAC3_CH_FRONT_CENTER: + map = SNDRV_CHMAP_FC; + break; + case UAC3_CH_FRONT_LEFT_OF_CENTER: + map = SNDRV_CHMAP_FLC; + break; + case UAC3_CH_FRONT_RIGHT_OF_CENTER: + map = SNDRV_CHMAP_FRC; + break; + case UAC3_CH_SIDE_LEFT: + map = SNDRV_CHMAP_SL; + break; + case UAC3_CH_SIDE_RIGHT: + map = SNDRV_CHMAP_SR; + break; + case UAC3_CH_BACK_LEFT: + map = SNDRV_CHMAP_RL; + break; + case UAC3_CH_BACK_RIGHT: + map = SNDRV_CHMAP_RR; + break; + case UAC3_CH_BACK_CENTER: + map = SNDRV_CHMAP_RC; + break; + case UAC3_CH_BACK_LEFT_OF_CENTER: + map = SNDRV_CHMAP_RLC; + break; + case UAC3_CH_BACK_RIGHT_OF_CENTER: + map = SNDRV_CHMAP_RRC; + break; + case UAC3_CH_TOP_CENTER: + map = SNDRV_CHMAP_TC; + break; + case UAC3_CH_TOP_FRONT_LEFT: + map = SNDRV_CHMAP_TFL; + break; + case UAC3_CH_TOP_FRONT_RIGHT: + map = SNDRV_CHMAP_TFR; + break; + case UAC3_CH_TOP_FRONT_CENTER: + map = SNDRV_CHMAP_TFC; + break; + case UAC3_CH_TOP_FRONT_LOC: + map = SNDRV_CHMAP_TFLC; + break; + case UAC3_CH_TOP_FRONT_ROC: + map = SNDRV_CHMAP_TFRC; + break; + case UAC3_CH_TOP_SIDE_LEFT: + map = SNDRV_CHMAP_TSL; + break; + case UAC3_CH_TOP_SIDE_RIGHT: + map = SNDRV_CHMAP_TSR; + break; + case UAC3_CH_TOP_BACK_LEFT: + map = SNDRV_CHMAP_TRL; + break; + case UAC3_CH_TOP_BACK_RIGHT: + map = SNDRV_CHMAP_TRR; + break; + case UAC3_CH_TOP_BACK_CENTER: + map = SNDRV_CHMAP_TRC; + break; + case UAC3_CH_BOTTOM_CENTER: + map = SNDRV_CHMAP_BC; + break; + case UAC3_CH_LOW_FREQUENCY_EFFECTS: + map = SNDRV_CHMAP_LFE; + break; + case UAC3_CH_LFE_LEFT: + map = SNDRV_CHMAP_LLFE; + break; + case UAC3_CH_LFE_RIGHT: + map = SNDRV_CHMAP_RLFE; + break; + case UAC3_CH_RELATIONSHIP_UNDEFINED: + default: + map = SNDRV_CHMAP_UNKNOWN; + break; + } + chmap->map[c++] = map; + } + p += cs_len; + } + + if (channels < c) + pr_err("%s: channel number mismatch\n", __func__); + + chmap->channels = channels; + + for (; c < channels; c++) + chmap->map[c] = SNDRV_CHMAP_UNKNOWN; + + return chmap; +} + /* * add this endpoint to the chip instance. * if a stream with the same endpoint already exists, append to it. @@ -461,10 +609,11 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, return NULL; } -static struct uac2_output_terminal_descriptor * - snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, - int terminal_id) +static void * +snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, + int terminal_id) { + /* OK to use with both UAC2 and UAC3 */ struct uac2_output_terminal_descriptor *term = NULL; while ((term = snd_usb_find_csint_desc(ctrl_iface->extra, @@ -484,10 +633,12 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) struct usb_host_interface *alts; struct usb_interface_descriptor *altsd; int i, altno, err, stream; - unsigned int format = 0, num_channels = 0; + u64 format = 0; + unsigned int num_channels = 0; struct audioformat *fp = NULL; int num, protocol, clock = 0; - struct uac_format_type_i_continuous_descriptor *fmt; + struct uac_format_type_i_continuous_descriptor *fmt = NULL; + struct snd_pcm_chmap_elem *chmap_v3 = NULL; unsigned int chconfig; dev = chip->dev; @@ -624,38 +775,158 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) iface_no, altno, as->bTerminalLink); continue; } - } - /* get format type */ - fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE); - if (!fmt) { + case UAC_VERSION_3: { + struct uac3_input_terminal_descriptor *input_term; + struct uac3_output_terminal_descriptor *output_term; + struct uac3_as_header_descriptor *as; + struct uac3_cluster_header_descriptor *cluster; + struct uac3_hc_descriptor_header hc_header; + u16 cluster_id, wLength; + + as = snd_usb_find_csint_desc(alts->extra, + alts->extralen, + NULL, UAC_AS_GENERAL); + + if (!as) { + dev_err(&dev->dev, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + continue; + } + + if (as->bLength < sizeof(*as)) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_AS_GENERAL desc\n", + iface_no, altno); + continue; + } + + cluster_id = le16_to_cpu(as->wClusterDescrID); + if (!cluster_id) { + dev_err(&dev->dev, + "%u:%d : no cluster descriptor\n", + iface_no, altno); + continue; + } + + /* + * Get number of channels and channel map through + * High Capability Cluster Descriptor + * + * First step: get High Capability header and + * read size of Cluster Descriptor + */ + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + cluster_id, + snd_usb_ctrl_intf(chip), + &hc_header, sizeof(hc_header)); + if (err < 0) + return err; + else if (err != sizeof(hc_header)) { + dev_err(&dev->dev, + "%u:%d : can't get High Capability descriptor\n", + iface_no, altno); + return -EIO; + } + + /* + * Second step: allocate needed amount of memory + * and request Cluster Descriptor + */ + wLength = le16_to_cpu(hc_header.wLength); + cluster = kzalloc(wLength, GFP_KERNEL); + if (!cluster) + return -ENOMEM; + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + cluster_id, + snd_usb_ctrl_intf(chip), + cluster, wLength); + if (err < 0) { + kfree(cluster); + return err; + } else if (err != wLength) { + dev_err(&dev->dev, + "%u:%d : can't get Cluster Descriptor\n", + iface_no, altno); + kfree(cluster); + return -EIO; + } + + num_channels = cluster->bNrChannels; + chmap_v3 = convert_chmap_v3(cluster); + + kfree(cluster); + + format = le64_to_cpu(as->bmFormats); + + /* lookup the terminal associated to this interface + * to extract the clock */ + input_term = snd_usb_find_input_terminal_descriptor( + chip->ctrl_intf, + as->bTerminalLink); + + if (input_term) { + clock = input_term->bCSourceID; + break; + } + + output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (output_term) { + clock = output_term->bCSourceID; + break; + } + dev_err(&dev->dev, - "%u:%d : no UAC_FORMAT_TYPE desc\n", - iface_no, altno); + "%u:%d : bogus bTerminalLink %d\n", + iface_no, altno, as->bTerminalLink); continue; } - if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) || - ((protocol == UAC_VERSION_2) && (fmt->bLength < 6))) { - dev_err(&dev->dev, - "%u:%d : invalid UAC_FORMAT_TYPE desc\n", - iface_no, altno); - continue; } - /* - * Blue Microphones workaround: The last altsetting is identical - * with the previous one, except for a larger packet size, but - * is actually a mislabeled two-channel setting; ignore it. - */ - if (fmt->bNrChannels == 1 && - fmt->bSubframeSize == 2 && - altno == 2 && num == 3 && - fp && fp->altsetting == 1 && fp->channels == 1 && - fp->formats == SNDRV_PCM_FMTBIT_S16_LE && - protocol == UAC_VERSION_1 && - le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == + if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { + /* get format type */ + fmt = snd_usb_find_csint_desc(alts->extra, + alts->extralen, + NULL, UAC_FORMAT_TYPE); + if (!fmt) { + dev_err(&dev->dev, + "%u:%d : no UAC_FORMAT_TYPE desc\n", + iface_no, altno); + continue; + } + if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) + || ((protocol == UAC_VERSION_2) && + (fmt->bLength < 6))) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_FORMAT_TYPE desc\n", + iface_no, altno); + continue; + } + + /* + * Blue Microphones workaround: The last altsetting is + * identical with the previous one, except for a larger + * packet size, but is actually a mislabeled two-channel + * setting; ignore it. + */ + if (fmt->bNrChannels == 1 && + fmt->bSubframeSize == 2 && + altno == 2 && num == 3 && + fp && fp->altsetting == 1 && fp->channels == 1 && + fp->formats == SNDRV_PCM_FMTBIT_S16_LE && + protocol == UAC_VERSION_1 && + le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == fp->maxpacksize * 2) - continue; + continue; + } fp = kzalloc(sizeof(*fp), GFP_KERNEL); if (!fp) @@ -681,17 +952,39 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) snd_usb_audioformat_attributes_quirk(chip, fp, stream); /* ok, let's parse further... */ - if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) < 0) { - kfree(fp->rate_table); - kfree(fp); - fp = NULL; - continue; + if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { + if (snd_usb_parse_audio_format(chip, fp, format, + fmt, stream) < 0) { + kfree(fp->rate_table); + kfree(fp); + fp = NULL; + continue; + } + } else { + struct uac3_as_header_descriptor *as; + + as = snd_usb_find_csint_desc(alts->extra, + alts->extralen, + NULL, UAC_AS_GENERAL); + + if (snd_usb_parse_audio_format_v3(chip, fp, as, + stream) < 0) { + kfree(fp->rate_table); + kfree(fp); + fp = NULL; + continue; + } } /* Create chmap */ if (fp->channels != num_channels) chconfig = 0; - fp->chmap = convert_chmap(fp->channels, chconfig, protocol); + + if (protocol == UAC_VERSION_3) + fp->chmap = chmap_v3; + else + fp->chmap = convert_chmap(fp->channels, chconfig, + protocol); dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); err = snd_usb_add_audio_stream(chip, stream, fp); -- cgit v1.2.3 From 447dcc0cf12922fcda67731559dd970bde7b35a6 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sun, 3 Dec 2017 11:06:54 -0500 Subject: media: rc: add new imon protocol decoder and encoder This makes it possible to use the various iMON remotes with any raw IR RC device. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/Kconfig | 9 ++ drivers/media/rc/Makefile | 1 + drivers/media/rc/ir-imon-decoder.c | 193 +++++++++++++++++++++++++++++++++ drivers/media/rc/keymaps/rc-imon-pad.c | 3 +- drivers/media/rc/rc-core-priv.h | 6 + drivers/media/rc/rc-main.c | 3 + include/media/rc-map.h | 8 +- include/uapi/linux/lirc.h | 2 + 8 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 drivers/media/rc/ir-imon-decoder.c (limited to 'include/uapi/linux') diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 7ad05a6ef350..eb2c3b6eca7f 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -111,6 +111,15 @@ config IR_XMP_DECODER ---help--- Enable this option if you have IR with XMP protocol, and if the IR is decoded in software + +config IR_IMON_DECODER + tristate "Enable IR raw decoder for the iMON protocol" + depends on RC_CORE + ---help--- + Enable this option if you have iMON PAD or Antec Veris infrared + remote control and you would like to use it with a raw IR + receiver, or if you wish to use an encoder to transmit this IR. + endif #RC_DECODERS menuconfig RC_DEVICES diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index e098e127b26a..2e1c87066f6c 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o +obj-$(CONFIG_IR_IMON_DECODER) += ir-imon-decoder.o # stand-alone IR receivers/transmitters obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o diff --git a/drivers/media/rc/ir-imon-decoder.c b/drivers/media/rc/ir-imon-decoder.c new file mode 100644 index 000000000000..a1ff06a26542 --- /dev/null +++ b/drivers/media/rc/ir-imon-decoder.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0+ +// ir-imon-decoder.c - handle iMon protocol +// +// Copyright (C) 2018 by Sean Young + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "rc-core-priv.h" + +#define IMON_UNIT 415662 /* ns */ +#define IMON_BITS 30 +#define IMON_CHKBITS (BIT(30) | BIT(25) | BIT(24) | BIT(22) | \ + BIT(21) | BIT(20) | BIT(19) | BIT(18) | \ + BIT(17) | BIT(16) | BIT(14) | BIT(13) | \ + BIT(12) | BIT(11) | BIT(10) | BIT(9)) + +/* + * This protocol has 30 bits. The format is one IMON_UNIT header pulse, + * followed by 30 bits. Each bit is one IMON_UNIT check field, and then + * one IMON_UNIT field with the actual bit (1=space, 0=pulse). + * The check field is always space for some bits, for others it is pulse if + * both the preceding and current bit are zero, else space. IMON_CHKBITS + * defines which bits are of type check. + * + * There is no way to distinguish an incomplete message from one where + * the lower bits are all set, iow. the last pulse is for the lowest + * bit which is 0. + */ +enum imon_state { + STATE_INACTIVE, + STATE_BIT_CHK, + STATE_BIT_START, + STATE_FINISHED +}; + +/** + * ir_imon_decode() - Decode one iMON pulse or space + * @dev: the struct rc_dev descriptor of the device + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev) +{ + struct imon_dec *data = &dev->raw->imon; + + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; + return 0; + } + + dev_dbg(&dev->dev, + "iMON decode started at state %d bitno %d (%uus %s)\n", + data->state, data->count, TO_US(ev.duration), + TO_STR(ev.pulse)); + + for (;;) { + if (!geq_margin(ev.duration, IMON_UNIT, IMON_UNIT / 2)) + return 0; + + decrease_duration(&ev, IMON_UNIT); + + switch (data->state) { + case STATE_INACTIVE: + if (ev.pulse) { + data->state = STATE_BIT_CHK; + data->bits = 0; + data->count = IMON_BITS; + } + break; + case STATE_BIT_CHK: + if (IMON_CHKBITS & BIT(data->count)) + data->last_chk = ev.pulse; + else if (ev.pulse) + goto err_out; + data->state = STATE_BIT_START; + break; + case STATE_BIT_START: + data->bits <<= 1; + if (!ev.pulse) + data->bits |= 1; + + if (IMON_CHKBITS & BIT(data->count)) { + if (data->last_chk != !(data->bits & 3)) + goto err_out; + } + + if (!data->count--) + data->state = STATE_FINISHED; + else + data->state = STATE_BIT_CHK; + break; + case STATE_FINISHED: + if (ev.pulse) + goto err_out; + rc_keydown(dev, RC_PROTO_IMON, data->bits, 0); + data->state = STATE_INACTIVE; + break; + } + } + +err_out: + dev_dbg(&dev->dev, + "iMON decode failed at state %d bitno %d (%uus %s)\n", + data->state, data->count, TO_US(ev.duration), + TO_STR(ev.pulse)); + + data->state = STATE_INACTIVE; + + return -EINVAL; +} + +/** + * ir_imon_encode() - Encode a scancode as a stream of raw events + * + * @protocol: protocol to encode + * @scancode: scancode to encode + * @events: array of raw ir events to write into + * @max: maximum size of @events + * + * Returns: The number of events written. + * -ENOBUFS if there isn't enough space in the array to fit the + * encoding. In this case all @max events will have been written. + */ +static int ir_imon_encode(enum rc_proto protocol, u32 scancode, + struct ir_raw_event *events, unsigned int max) +{ + struct ir_raw_event *e = events; + int i, pulse; + + if (!max--) + return -ENOBUFS; + init_ir_raw_event_duration(e, 1, IMON_UNIT); + + for (i = IMON_BITS; i >= 0; i--) { + if (BIT(i) & IMON_CHKBITS) + pulse = !(scancode & (BIT(i) | BIT(i + 1))); + else + pulse = 0; + + if (pulse == e->pulse) { + e->duration += IMON_UNIT; + } else { + if (!max--) + return -ENOBUFS; + init_ir_raw_event_duration(++e, pulse, IMON_UNIT); + } + + pulse = !(scancode & BIT(i)); + + if (pulse == e->pulse) { + e->duration += IMON_UNIT; + } else { + if (!max--) + return -ENOBUFS; + init_ir_raw_event_duration(++e, pulse, IMON_UNIT); + } + } + + if (e->pulse) + e++; + + return e - events; +} + +static struct ir_raw_handler imon_handler = { + .protocols = RC_PROTO_BIT_IMON, + .decode = ir_imon_decode, + .encode = ir_imon_encode, + .carrier = 38000, +}; + +static int __init ir_imon_decode_init(void) +{ + ir_raw_handler_register(&imon_handler); + + pr_info("IR iMON protocol handler initialized\n"); + return 0; +} + +static void __exit ir_imon_decode_exit(void) +{ + ir_raw_handler_unregister(&imon_handler); +} + +module_init(ir_imon_decode_init); +module_exit(ir_imon_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sean Young "); +MODULE_DESCRIPTION("iMON IR protocol decoder"); diff --git a/drivers/media/rc/keymaps/rc-imon-pad.c b/drivers/media/rc/keymaps/rc-imon-pad.c index a7296ffbf218..8501cf0a3253 100644 --- a/drivers/media/rc/keymaps/rc-imon-pad.c +++ b/drivers/media/rc/keymaps/rc-imon-pad.c @@ -134,8 +134,7 @@ static struct rc_map_list imon_pad_map = { .map = { .scan = imon_pad, .size = ARRAY_SIZE(imon_pad), - /* actual protocol details unknown, hardware decoder */ - .rc_proto = RC_PROTO_OTHER, + .rc_proto = RC_PROTO_IMON, .name = RC_MAP_IMON_PAD, } }; diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 5e80b4273e2d..e0e6a17460f6 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -118,6 +118,12 @@ struct ir_raw_event_ctrl { unsigned count; u32 durations[16]; } xmp; + struct imon_dec { + int state; + int count; + int last_chk; + unsigned int bits; + } imon; }; /* macros for IR decoders */ diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 8621761a680f..b67be33bd62f 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -68,6 +68,8 @@ static const struct { .scancode_bits = 0x1fff, .repeat_period = 250 }, [RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 250 }, [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 550 }, + [RC_PROTO_IMON] = { .name = "imon", + .scancode_bits = 0x7fffffff, .repeat_period = 250 }, }; /* Used to keep track of known keymaps */ @@ -1004,6 +1006,7 @@ static const struct { RC_PROTO_BIT_MCIR2_MSE, "mce_kbd", "ir-mce_kbd-decoder" }, { RC_PROTO_BIT_XMP, "xmp", "ir-xmp-decoder" }, { RC_PROTO_BIT_CEC, "cec", NULL }, + { RC_PROTO_BIT_IMON, "imon", "ir-imon-decoder" }, }; /** diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 7fc84991bd12..bfa3017cecba 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -36,6 +36,7 @@ #define RC_PROTO_BIT_SHARP BIT_ULL(RC_PROTO_SHARP) #define RC_PROTO_BIT_XMP BIT_ULL(RC_PROTO_XMP) #define RC_PROTO_BIT_CEC BIT_ULL(RC_PROTO_CEC) +#define RC_PROTO_BIT_IMON BIT_ULL(RC_PROTO_IMON) #define RC_PROTO_BIT_ALL \ (RC_PROTO_BIT_UNKNOWN | RC_PROTO_BIT_OTHER | \ @@ -49,7 +50,8 @@ RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ - RC_PROTO_BIT_XMP | RC_PROTO_BIT_CEC) + RC_PROTO_BIT_XMP | RC_PROTO_BIT_CEC | \ + RC_PROTO_BIT_IMON) /* All rc protocols for which we have decoders */ #define RC_PROTO_BIT_ALL_IR_DECODER \ (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ @@ -62,7 +64,7 @@ RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ - RC_PROTO_BIT_XMP) + RC_PROTO_BIT_XMP | RC_PROTO_BIT_IMON) #define RC_PROTO_BIT_ALL_IR_ENCODER \ (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ @@ -75,7 +77,7 @@ RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ RC_PROTO_BIT_RC6_6A_24 | \ RC_PROTO_BIT_RC6_6A_32 | RC_PROTO_BIT_RC6_MCE | \ - RC_PROTO_BIT_SHARP) + RC_PROTO_BIT_SHARP | RC_PROTO_BIT_IMON) #define RC_SCANCODE_UNKNOWN(x) (x) #define RC_SCANCODE_OTHER(x) (x) diff --git a/include/uapi/linux/lirc.h b/include/uapi/linux/lirc.h index 4fe580d36e41..948d9a491083 100644 --- a/include/uapi/linux/lirc.h +++ b/include/uapi/linux/lirc.h @@ -186,6 +186,7 @@ struct lirc_scancode { * @RC_PROTO_SHARP: Sharp protocol * @RC_PROTO_XMP: XMP protocol * @RC_PROTO_CEC: CEC protocol + * @RC_PROTO_IMON: iMon Pad protocol */ enum rc_proto { RC_PROTO_UNKNOWN = 0, @@ -211,6 +212,7 @@ enum rc_proto { RC_PROTO_SHARP = 20, RC_PROTO_XMP = 21, RC_PROTO_CEC = 22, + RC_PROTO_IMON = 23, }; #endif -- cgit v1.2.3 From 1acfb9b7ee0b1881bb8e875b6757976e48293ec4 Mon Sep 17 00:00:00 2001 From: Jay Fang Date: Mon, 12 Mar 2018 17:13:32 +0800 Subject: PCI: Add decoding for 16 GT/s link speed PCIe 4.0 defines the 16.0 GT/s link speed. Links can run at that speed without any Linux changes, but previously their sysfs "max_link_speed" and "current_link_speed" files contained "Unknown speed", not the expected "16.0 GT/s". Add decoding for the new 16 GT/s link speed. Signed-off-by: Jay Fang [bhelgaas: add PCI_EXP_LNKCAP2_SLS_16_0GB] Signed-off-by: Bjorn Helgaas Reviewed-by: Dongdong Liu --- drivers/pci/pci-sysfs.c | 6 ++++++ drivers/pci/probe.c | 2 +- drivers/pci/slot.c | 1 + include/linux/pci.h | 1 + include/uapi/linux/pci_regs.h | 7 +++++-- 5 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index eb6bee8724cc..7dc5be545d18 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -168,6 +168,9 @@ static ssize_t max_link_speed_show(struct device *dev, return -EINVAL; switch (linkcap & PCI_EXP_LNKCAP_SLS) { + case PCI_EXP_LNKCAP_SLS_16_0GB: + speed = "16 GT/s"; + break; case PCI_EXP_LNKCAP_SLS_8_0GB: speed = "8 GT/s"; break; @@ -213,6 +216,9 @@ static ssize_t current_link_speed_show(struct device *dev, return -EINVAL; switch (linkstat & PCI_EXP_LNKSTA_CLS) { + case PCI_EXP_LNKSTA_CLS_16_0GB: + speed = "16 GT/s"; + break; case PCI_EXP_LNKSTA_CLS_8_0GB: speed = "8 GT/s"; break; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ef5377438a1e..86bf045f3d59 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -592,7 +592,7 @@ const unsigned char pcie_link_speed[] = { PCIE_SPEED_2_5GT, /* 1 */ PCIE_SPEED_5_0GT, /* 2 */ PCIE_SPEED_8_0GT, /* 3 */ - PCI_SPEED_UNKNOWN, /* 4 */ + PCIE_SPEED_16_0GT, /* 4 */ PCI_SPEED_UNKNOWN, /* 5 */ PCI_SPEED_UNKNOWN, /* 6 */ PCI_SPEED_UNKNOWN, /* 7 */ diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index d10f556dc03e..191893e19d5c 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -76,6 +76,7 @@ static const char *pci_bus_speed_strings[] = { "2.5 GT/s PCIe", /* 0x14 */ "5.0 GT/s PCIe", /* 0x15 */ "8.0 GT/s PCIe", /* 0x16 */ + "16.0 GT/s PCIe", /* 0x17 */ }; static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf) diff --git a/include/linux/pci.h b/include/linux/pci.h index 024a1beda008..8043a5937ad0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -256,6 +256,7 @@ enum pci_bus_speed { PCIE_SPEED_2_5GT = 0x14, PCIE_SPEED_5_0GT = 0x15, PCIE_SPEED_8_0GT = 0x16, + PCIE_SPEED_16_0GT = 0x17, PCI_SPEED_UNKNOWN = 0xff, }; diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 0c79eac5e9b8..103ba797a8f3 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -520,6 +520,7 @@ #define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */ #define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ #define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */ +#define PCI_EXP_LNKCAP_SLS_16_0GB 0x00000004 /* LNKCAP2 SLS Vector bit 3 */ #define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ @@ -547,6 +548,7 @@ #define PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 /* Current Link Speed 2.5GT/s */ #define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */ #define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */ +#define PCI_EXP_LNKSTA_CLS_16_0GB 0x0004 /* Current Link Speed 16.0GT/s */ #define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */ #define PCI_EXP_LNKSTA_NLW_X1 0x0010 /* Current Link Width x1 */ #define PCI_EXP_LNKSTA_NLW_X2 0x0020 /* Current Link Width x2 */ @@ -648,8 +650,9 @@ #define PCI_CAP_EXP_RC_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints without link end here */ #define PCI_EXP_LNKCAP2 44 /* Link Capabilities 2 */ #define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */ -#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5.0GT/s */ -#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8.0GT/s */ +#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5GT/s */ +#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8GT/s */ +#define PCI_EXP_LNKCAP2_SLS_16_0GB 0x00000010 /* Supported Speed 16GT/s */ #define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ -- cgit v1.2.3 From 53b2534551f179dcd065b4ead79fa1327678a0e0 Mon Sep 17 00:00:00 2001 From: Smitha T Murthy Date: Fri, 2 Feb 2018 07:25:41 -0500 Subject: media: videodev2.h: Add v4l2 definition for HEVC Add V4L2 definition for HEVC compressed format Signed-off-by: Smitha T Murthy Reviewed-by: Andrzej Hajda Reviewed-by: Stanimir Varbanov Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 982718965180..600877be5c22 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -635,6 +635,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */ #define V4L2_PIX_FMT_VP8 v4l2_fourcc('V', 'P', '8', '0') /* VP8 */ #define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0') /* VP9 */ +#define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C') /* HEVC aka H.265 */ /* Vendor-specific formats */ #define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */ -- cgit v1.2.3 From 2c02837bd99cda8e4f8b9cb5ea74956242b5c851 Mon Sep 17 00:00:00 2001 From: Smitha T Murthy Date: Fri, 2 Feb 2018 07:25:46 -0500 Subject: media: v4l2: Add v4l2 control IDs for HEVC encoder Add v4l2 controls for HEVC encoder Signed-off-by: Smitha T Murthy Reviewed-by: Andrzej Hajda Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 119 +++++++++++++++++++++++++++++++++++ include/uapi/linux/v4l2-controls.h | 93 ++++++++++++++++++++++++++- 2 files changed, 211 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index ce08b50b8290..d29e45516eb7 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -480,6 +480,57 @@ const char * const *v4l2_ctrl_get_menu(u32 id) NULL, }; + static const char * const hevc_profile[] = { + "Main", + "Main Still Picture", + "Main 10", + NULL, + }; + static const char * const hevc_level[] = { + "1", + "2", + "2.1", + "3", + "3.1", + "4", + "4.1", + "5", + "5.1", + "5.2", + "6", + "6.1", + "6.2", + NULL, + }; + static const char * const hevc_hierarchial_coding_type[] = { + "B", + "P", + NULL, + }; + static const char * const hevc_refresh_type[] = { + "None", + "CRA", + "IDR", + NULL, + }; + static const char * const hevc_size_of_length_field[] = { + "0", + "1", + "2", + "4", + NULL, + }; + static const char * const hevc_tier[] = { + "Main", + "High", + NULL, + }; + static const char * const hevc_loop_filter_mode[] = { + "Disabled", + "Enabled", + "Disabled at slice boundary", + "NULL", + }; switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: @@ -575,6 +626,20 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return dv_it_content_type; case V4L2_CID_DETECT_MD_MODE: return detect_md_mode; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + return hevc_profile; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + return hevc_level; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE: + return hevc_hierarchial_coding_type; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: + return hevc_refresh_type; + case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: + return hevc_size_of_length_field; + case V4L2_CID_MPEG_VIDEO_HEVC_TIER: + return hevc_tier; + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: + return hevc_loop_filter_mode; default: return NULL; @@ -776,6 +841,53 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: return "VPX P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: return "VPX Profile"; + /* HEVC controls */ + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: return "HEVC I-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP: return "HEVC P-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP: return "HEVC B-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: return "HEVC Minimum QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: return "HEVC Maximum QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: return "HEVC Profile"; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: return "HEVC Level"; + case V4L2_CID_MPEG_VIDEO_HEVC_TIER: return "HEVC Tier"; + case V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION: return "HEVC Frame Rate Resolution"; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH: return "HEVC Maximum Coding Unit Depth"; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: return "HEVC Refresh Type"; + case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED: return "HEVC Constant Intra Prediction"; + case V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU: return "HEVC Lossless Encoding"; + case V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT: return "HEVC Wavefront"; + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: return "HEVC Loop Filter"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP: return "HEVC QP Values"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE: return "HEVC Hierarchical Coding Type"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER: return "HEVC Hierarchical Coding Layer"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP: return "HEVC Hierarchical Layer 0 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP: return "HEVC Hierarchical Layer 1 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP: return "HEVC Hierarchical Layer 2 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP: return "HEVC Hierarchical Layer 3 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP: return "HEVC Hierarchical Layer 4 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP: return "HEVC Hierarchical Layer 5 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP: return "HEVC Hierarchical Layer 6 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR: return "HEVC Hierarchical Lay 0 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR: return "HEVC Hierarchical Lay 1 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR: return "HEVC Hierarchical Lay 2 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR: return "HEVC Hierarchical Lay 3 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR: return "HEVC Hierarchical Lay 4 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR: return "HEVC Hierarchical Lay 5 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR: return "HEVC Hierarchical Lay 6 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB: return "HEVC General PB"; + case V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID: return "HEVC Temporal ID"; + case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING: return "HEVC Strong Intra Smoothing"; + case V4L2_CID_MPEG_VIDEO_HEVC_INTRA_PU_SPLIT: return "HEVC Intra PU Split"; + case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION: return "HEVC TMV Prediction"; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1: return "HEVC Max Num of Candidate MVs"; + case V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE: return "HEVC ENC Without Startcode"; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD: return "HEVC Num of I-Frame b/w 2 IDR"; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2: return "HEVC Loop Filter Beta Offset"; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2: return "HEVC Loop Filter TC Offset"; + case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: return "HEVC Size of Length Field"; + case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES: return "Reference Frames for a P-Frame"; + case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: return "Prepend SPS and PPS to IDR"; + /* CAMERA controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; @@ -1069,6 +1181,13 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_TUNE_DEEMPHASIS: case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: case V4L2_CID_DETECT_MD_MODE: + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE: + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: + case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: + case V4L2_CID_MPEG_VIDEO_HEVC_TIER: + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index cbbb750d87d1..8d473c979b61 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -589,6 +589,98 @@ enum v4l2_vp8_golden_frame_sel { #define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (V4L2_CID_MPEG_BASE+510) #define V4L2_CID_MPEG_VIDEO_VPX_PROFILE (V4L2_CID_MPEG_BASE+511) +/* CIDs for HEVC encoding. */ + +#define V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP (V4L2_CID_MPEG_BASE + 600) +#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP (V4L2_CID_MPEG_BASE + 601) +#define V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP (V4L2_CID_MPEG_BASE + 602) +#define V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP (V4L2_CID_MPEG_BASE + 603) +#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP (V4L2_CID_MPEG_BASE + 604) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP (V4L2_CID_MPEG_BASE + 605) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE (V4L2_CID_MPEG_BASE + 606) +enum v4l2_mpeg_video_hevc_hier_coding_type { + V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B = 0, + V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P = 1, +}; +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER (V4L2_CID_MPEG_BASE + 607) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP (V4L2_CID_MPEG_BASE + 608) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP (V4L2_CID_MPEG_BASE + 609) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP (V4L2_CID_MPEG_BASE + 610) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP (V4L2_CID_MPEG_BASE + 611) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP (V4L2_CID_MPEG_BASE + 612) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP (V4L2_CID_MPEG_BASE + 613) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP (V4L2_CID_MPEG_BASE + 614) +#define V4L2_CID_MPEG_VIDEO_HEVC_PROFILE (V4L2_CID_MPEG_BASE + 615) +enum v4l2_mpeg_video_hevc_profile { + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN = 0, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE = 1, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10 = 2, +}; +#define V4L2_CID_MPEG_VIDEO_HEVC_LEVEL (V4L2_CID_MPEG_BASE + 616) +enum v4l2_mpeg_video_hevc_level { + V4L2_MPEG_VIDEO_HEVC_LEVEL_1 = 0, + V4L2_MPEG_VIDEO_HEVC_LEVEL_2 = 1, + V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1 = 2, + V4L2_MPEG_VIDEO_HEVC_LEVEL_3 = 3, + V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1 = 4, + V4L2_MPEG_VIDEO_HEVC_LEVEL_4 = 5, + V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1 = 6, + V4L2_MPEG_VIDEO_HEVC_LEVEL_5 = 7, + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1 = 8, + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2 = 9, + V4L2_MPEG_VIDEO_HEVC_LEVEL_6 = 10, + V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1 = 11, + V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2 = 12, +}; +#define V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION (V4L2_CID_MPEG_BASE + 617) +#define V4L2_CID_MPEG_VIDEO_HEVC_TIER (V4L2_CID_MPEG_BASE + 618) +enum v4l2_mpeg_video_hevc_tier { + V4L2_MPEG_VIDEO_HEVC_TIER_MAIN = 0, + V4L2_MPEG_VIDEO_HEVC_TIER_HIGH = 1, +}; +#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH (V4L2_CID_MPEG_BASE + 619) +#define V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE (V4L2_CID_MPEG_BASE + 620) +enum v4l2_cid_mpeg_video_hevc_loop_filter_mode { + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED = 0, + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED = 1, + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY = 2, +}; +#define V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2 (V4L2_CID_MPEG_BASE + 621) +#define V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2 (V4L2_CID_MPEG_BASE + 622) +#define V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE (V4L2_CID_MPEG_BASE + 623) +enum v4l2_cid_mpeg_video_hevc_refresh_type { + V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE = 0, + V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA = 1, + V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR = 2, +}; +#define V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD (V4L2_CID_MPEG_BASE + 624) +#define V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU (V4L2_CID_MPEG_BASE + 625) +#define V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED (V4L2_CID_MPEG_BASE + 626) +#define V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT (V4L2_CID_MPEG_BASE + 627) +#define V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB (V4L2_CID_MPEG_BASE + 628) +#define V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID (V4L2_CID_MPEG_BASE + 629) +#define V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING (V4L2_CID_MPEG_BASE + 630) +#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1 (V4L2_CID_MPEG_BASE + 631) +#define V4L2_CID_MPEG_VIDEO_HEVC_INTRA_PU_SPLIT (V4L2_CID_MPEG_BASE + 632) +#define V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION (V4L2_CID_MPEG_BASE + 633) +#define V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE (V4L2_CID_MPEG_BASE + 634) +#define V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD (V4L2_CID_MPEG_BASE + 635) +enum v4l2_cid_mpeg_video_hevc_size_of_length_field { + V4L2_MPEG_VIDEO_HEVC_SIZE_0 = 0, + V4L2_MPEG_VIDEO_HEVC_SIZE_1 = 1, + V4L2_MPEG_VIDEO_HEVC_SIZE_2 = 2, + V4L2_MPEG_VIDEO_HEVC_SIZE_4 = 3, +}; +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR (V4L2_CID_MPEG_BASE + 636) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR (V4L2_CID_MPEG_BASE + 637) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR (V4L2_CID_MPEG_BASE + 638) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR (V4L2_CID_MPEG_BASE + 639) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR (V4L2_CID_MPEG_BASE + 640) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR (V4L2_CID_MPEG_BASE + 641) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR (V4L2_CID_MPEG_BASE + 642) +#define V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES (V4L2_CID_MPEG_BASE + 643) +#define V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR (V4L2_CID_MPEG_BASE + 644) + /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+0) @@ -657,7 +749,6 @@ enum v4l2_mpeg_mfc51_video_force_frame_type { #define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC (V4L2_CID_MPEG_MFC51_BASE+53) #define V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P (V4L2_CID_MPEG_MFC51_BASE+54) - /* Camera class control IDs */ #define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900) -- cgit v1.2.3 From c30b70deb5f4861f590031c33fd3ec6cc63f1df1 Mon Sep 17 00:00:00 2001 From: GhantaKrishnamurthy MohanKrishna Date: Wed, 21 Mar 2018 14:37:44 +0100 Subject: tipc: implement socket diagnostics for AF_TIPC This commit adds socket diagnostics capability for AF_TIPC in netlink family NETLINK_SOCK_DIAG in a new kernel module (diag.ko). The following are key design considerations: - config TIPC_DIAG has default y, like INET_DIAG. - only requests with flag NLM_F_DUMP is supported (dump all). - tipc_sock_diag_req message is introduced to send filter parameters. - the response attributes are of TLV, some nested. To avoid exposing data structures between diag and tipc modules and avoid code duplication, the following additions are required: - export tipc_nl_sk_walk function to reuse socket iterator. - export tipc_sk_fill_sock_diag to fill the tipc diag attributes. - create a sock_diag response message in __tipc_add_sock_diag defined in diag.c and use the above exported tipc_sk_fill_sock_diag to fill response. Acked-by: Jon Maloy Acked-by: Ying Xue Signed-off-by: GhantaKrishnamurthy MohanKrishna Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- include/uapi/linux/tipc_netlink.h | 18 ++++++ include/uapi/linux/tipc_sockets_diag.h | 17 +++++ net/tipc/Kconfig | 8 +++ net/tipc/Makefile | 5 ++ net/tipc/diag.c | 114 +++++++++++++++++++++++++++++++++ net/tipc/socket.c | 72 +++++++++++++++++++-- net/tipc/socket.h | 10 ++- 7 files changed, 238 insertions(+), 6 deletions(-) create mode 100644 include/uapi/linux/tipc_sockets_diag.h create mode 100644 net/tipc/diag.c (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/tipc_netlink.h b/include/uapi/linux/tipc_netlink.h index 469aa67a5ecb..d7cec0480d70 100644 --- a/include/uapi/linux/tipc_netlink.h +++ b/include/uapi/linux/tipc_netlink.h @@ -114,6 +114,13 @@ enum { TIPC_NLA_SOCK_REF, /* u32 */ TIPC_NLA_SOCK_CON, /* nest */ TIPC_NLA_SOCK_HAS_PUBL, /* flag */ + TIPC_NLA_SOCK_STAT, /* nest */ + TIPC_NLA_SOCK_TYPE, /* u32 */ + TIPC_NLA_SOCK_INO, /* u32 */ + TIPC_NLA_SOCK_UID, /* u32 */ + TIPC_NLA_SOCK_TIPC_STATE, /* u32 */ + TIPC_NLA_SOCK_COOKIE, /* u64 */ + TIPC_NLA_SOCK_PAD, /* flag */ __TIPC_NLA_SOCK_MAX, TIPC_NLA_SOCK_MAX = __TIPC_NLA_SOCK_MAX - 1 @@ -238,6 +245,17 @@ enum { TIPC_NLA_CON_MAX = __TIPC_NLA_CON_MAX - 1 }; +/* Nest, socket statistics info */ +enum { + TIPC_NLA_SOCK_STAT_RCVQ, /* u32 */ + TIPC_NLA_SOCK_STAT_SENDQ, /* u32 */ + TIPC_NLA_SOCK_STAT_LINK_CONG, /* flag */ + TIPC_NLA_SOCK_STAT_CONN_CONG, /* flag */ + + __TIPC_NLA_SOCK_STAT_MAX, + TIPC_NLA_SOCK_STAT_MAX = __TIPC_NLA_SOCK_STAT_MAX - 1 +}; + /* Nest, link propreties. Valid for link, media and bearer */ enum { TIPC_NLA_PROP_UNSPEC, diff --git a/include/uapi/linux/tipc_sockets_diag.h b/include/uapi/linux/tipc_sockets_diag.h new file mode 100644 index 000000000000..7678cf2f0dcc --- /dev/null +++ b/include/uapi/linux/tipc_sockets_diag.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* AF_TIPC sock_diag interface for querying open sockets */ + +#ifndef _UAPI__TIPC_SOCKETS_DIAG_H__ +#define _UAPI__TIPC_SOCKETS_DIAG_H__ + +#include +#include + +/* Request */ +struct tipc_sock_diag_req { + __u8 sdiag_family; /* must be AF_TIPC */ + __u8 sdiag_protocol; /* must be 0 */ + __u16 pad; /* must be 0 */ + __u32 tidiag_states; /* query*/ +}; +#endif /* _UAPI__TIPC_SOCKETS_DIAG_H__ */ diff --git a/net/tipc/Kconfig b/net/tipc/Kconfig index c25a3a149dc4..e450212121d2 100644 --- a/net/tipc/Kconfig +++ b/net/tipc/Kconfig @@ -34,3 +34,11 @@ config TIPC_MEDIA_UDP Saying Y here will enable support for running TIPC over IP/UDP bool default y + +config TIPC_DIAG + tristate "TIPC: socket monitoring interface" + depends on TIPC + default y + ---help--- + Support for TIPC socket monitoring interface used by ss tool. + If unsure, say Y. diff --git a/net/tipc/Makefile b/net/tipc/Makefile index 1edb7192aa2f..aca168f2abb1 100644 --- a/net/tipc/Makefile +++ b/net/tipc/Makefile @@ -14,3 +14,8 @@ tipc-y += addr.o bcast.o bearer.o \ tipc-$(CONFIG_TIPC_MEDIA_UDP) += udp_media.o tipc-$(CONFIG_TIPC_MEDIA_IB) += ib_media.o tipc-$(CONFIG_SYSCTL) += sysctl.o + + +obj-$(CONFIG_TIPC_DIAG) += diag.o + +tipc_diag-y := diag.o diff --git a/net/tipc/diag.c b/net/tipc/diag.c new file mode 100644 index 000000000000..46d9cd62f781 --- /dev/null +++ b/net/tipc/diag.c @@ -0,0 +1,114 @@ +/* + * net/tipc/diag.c: TIPC socket diag + * + * Copyright (c) 2018, Ericsson AB + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "ASIS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core.h" +#include "socket.h" +#include +#include + +static u64 __tipc_diag_gen_cookie(struct sock *sk) +{ + u32 res[2]; + + sock_diag_save_cookie(sk, res); + return *((u64 *)res); +} + +static int __tipc_add_sock_diag(struct sk_buff *skb, + struct netlink_callback *cb, + struct tipc_sock *tsk) +{ + struct tipc_sock_diag_req *req = nlmsg_data(cb->nlh); + struct nlmsghdr *nlh; + int err; + + nlh = nlmsg_put_answer(skb, cb, SOCK_DIAG_BY_FAMILY, 0, + NLM_F_MULTI); + if (!nlh) + return -EMSGSIZE; + + err = tipc_sk_fill_sock_diag(skb, tsk, req->tidiag_states, + __tipc_diag_gen_cookie); + if (err) + return err; + + nlmsg_end(skb, nlh); + return 0; +} + +static int tipc_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + return tipc_nl_sk_walk(skb, cb, __tipc_add_sock_diag); +} + +static int tipc_sock_diag_handler_dump(struct sk_buff *skb, + struct nlmsghdr *h) +{ + int hdrlen = sizeof(struct tipc_sock_diag_req); + struct net *net = sock_net(skb->sk); + + if (nlmsg_len(h) < hdrlen) + return -EINVAL; + + if (h->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = tipc_diag_dump, + }; + netlink_dump_start(net->diag_nlsk, skb, h, &c); + return 0; + } + return -EOPNOTSUPP; +} + +static const struct sock_diag_handler tipc_sock_diag_handler = { + .family = AF_TIPC, + .dump = tipc_sock_diag_handler_dump, +}; + +static int __init tipc_diag_init(void) +{ + return sock_diag_register(&tipc_sock_diag_handler); +} + +static void __exit tipc_diag_exit(void) +{ + sock_diag_unregister(&tipc_sock_diag_handler); +} + +module_init(tipc_diag_init); +module_exit(tipc_diag_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, AF_TIPC); diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 35ac0f5b9529..07559ce4b8ba 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -3213,10 +3213,10 @@ msg_cancel: return -EMSGSIZE; } -static int __tipc_nl_sk_walk(struct sk_buff *skb, struct netlink_callback *cb, - int (*skb_handler)(struct sk_buff *skb, - struct netlink_callback *cb, - struct tipc_sock *tsk)) +int tipc_nl_sk_walk(struct sk_buff *skb, struct netlink_callback *cb, + int (*skb_handler)(struct sk_buff *skb, + struct netlink_callback *cb, + struct tipc_sock *tsk)) { struct net *net = sock_net(skb->sk); struct tipc_net *tn = tipc_net(net); @@ -3255,10 +3255,72 @@ out: return skb->len; } +EXPORT_SYMBOL(tipc_nl_sk_walk); + +int tipc_sk_fill_sock_diag(struct sk_buff *skb, struct tipc_sock *tsk, + u32 sk_filter_state, + u64 (*tipc_diag_gen_cookie)(struct sock *sk)) +{ + struct sock *sk = &tsk->sk; + struct nlattr *attrs; + struct nlattr *stat; + + /*filter response w.r.t sk_state*/ + if (!(sk_filter_state & (1 << sk->sk_state))) + return 0; + + attrs = nla_nest_start(skb, TIPC_NLA_SOCK); + if (!attrs) + goto msg_cancel; + + if (__tipc_nl_add_sk_info(skb, tsk)) + goto attr_msg_cancel; + + if (nla_put_u32(skb, TIPC_NLA_SOCK_TYPE, (u32)sk->sk_type) || + nla_put_u32(skb, TIPC_NLA_SOCK_TIPC_STATE, (u32)sk->sk_state) || + nla_put_u32(skb, TIPC_NLA_SOCK_INO, sock_i_ino(sk)) || + nla_put_u32(skb, TIPC_NLA_SOCK_UID, + from_kuid_munged(sk_user_ns(sk), sock_i_uid(sk))) || + nla_put_u64_64bit(skb, TIPC_NLA_SOCK_COOKIE, + tipc_diag_gen_cookie(sk), + TIPC_NLA_SOCK_PAD)) + goto attr_msg_cancel; + + stat = nla_nest_start(skb, TIPC_NLA_SOCK_STAT); + if (!stat) + goto attr_msg_cancel; + + if (nla_put_u32(skb, TIPC_NLA_SOCK_STAT_RCVQ, + skb_queue_len(&sk->sk_receive_queue)) || + nla_put_u32(skb, TIPC_NLA_SOCK_STAT_SENDQ, + skb_queue_len(&sk->sk_write_queue))) + goto stat_msg_cancel; + + if (tsk->cong_link_cnt && + nla_put_flag(skb, TIPC_NLA_SOCK_STAT_LINK_CONG)) + goto stat_msg_cancel; + + if (tsk_conn_cong(tsk) && + nla_put_flag(skb, TIPC_NLA_SOCK_STAT_CONN_CONG)) + goto stat_msg_cancel; + + nla_nest_end(skb, stat); + nla_nest_end(skb, attrs); + + return 0; + +stat_msg_cancel: + nla_nest_cancel(skb, stat); +attr_msg_cancel: + nla_nest_cancel(skb, attrs); +msg_cancel: + return -EMSGSIZE; +} +EXPORT_SYMBOL(tipc_sk_fill_sock_diag); int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb) { - return __tipc_nl_sk_walk(skb, cb, __tipc_nl_add_sk); + return tipc_nl_sk_walk(skb, cb, __tipc_nl_add_sk); } /* Caller should hold socket lock for the passed tipc socket. */ diff --git a/net/tipc/socket.h b/net/tipc/socket.h index 06fb5944cf76..aae3fd4cd06c 100644 --- a/net/tipc/socket.h +++ b/net/tipc/socket.h @@ -49,6 +49,8 @@ #define RCVBUF_DEF (FLOWCTL_BLK_SZ * 1024 * 2) #define RCVBUF_MAX (FLOWCTL_BLK_SZ * 1024 * 16) +struct tipc_sock; + int tipc_socket_init(void); void tipc_socket_stop(void); void tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq); @@ -59,5 +61,11 @@ int tipc_sk_rht_init(struct net *net); void tipc_sk_rht_destroy(struct net *net); int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb); int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb); - +int tipc_sk_fill_sock_diag(struct sk_buff *skb, struct tipc_sock *tsk, + u32 sk_filter_state, + u64 (*tipc_diag_gen_cookie)(struct sock *sk)); +int tipc_nl_sk_walk(struct sk_buff *skb, struct netlink_callback *cb, + int (*skb_handler)(struct sk_buff *skb, + struct netlink_callback *cb, + struct tipc_sock *tsk)); #endif -- cgit v1.2.3 From 872619d8cf810c17279335ef531a2a34f3b4e589 Mon Sep 17 00:00:00 2001 From: GhantaKrishnamurthy MohanKrishna Date: Wed, 21 Mar 2018 14:37:45 +0100 Subject: tipc: step sk->sk_drops when rcv buffer is full Currently when tipc is unable to queue a received message on a socket, the message is rejected back to the sender with error TIPC_ERR_OVERLOAD. However, the application on this socket has no knowledge about these discards. In this commit, we try to step the sk_drops counter when tipc is unable to queue a received message. Export sk_drops using tipc socket diagnostics. Acked-by: Jon Maloy Acked-by: Ying Xue Signed-off-by: GhantaKrishnamurthy MohanKrishna Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- include/uapi/linux/tipc_netlink.h | 1 + net/tipc/socket.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/tipc_netlink.h b/include/uapi/linux/tipc_netlink.h index d7cec0480d70..d896ded51bcb 100644 --- a/include/uapi/linux/tipc_netlink.h +++ b/include/uapi/linux/tipc_netlink.h @@ -251,6 +251,7 @@ enum { TIPC_NLA_SOCK_STAT_SENDQ, /* u32 */ TIPC_NLA_SOCK_STAT_LINK_CONG, /* flag */ TIPC_NLA_SOCK_STAT_CONN_CONG, /* flag */ + TIPC_NLA_SOCK_STAT_DROP, /* u32 */ __TIPC_NLA_SOCK_STAT_MAX, TIPC_NLA_SOCK_STAT_MAX = __TIPC_NLA_SOCK_STAT_MAX - 1 diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 07559ce4b8ba..732ec894f69f 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -2122,8 +2122,10 @@ static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb, (!sk_conn && msg_connected(hdr)) || (!grp && msg_in_group(hdr))) err = TIPC_ERR_NO_PORT; - else if (sk_rmem_alloc_get(sk) + skb->truesize >= limit) + else if (sk_rmem_alloc_get(sk) + skb->truesize >= limit) { + atomic_inc(&sk->sk_drops); err = TIPC_ERR_OVERLOAD; + } if (unlikely(err)) { tipc_skb_reject(net, err, skb, xmitq); @@ -2202,6 +2204,7 @@ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk, /* Overload => reject message back to sender */ onode = tipc_own_addr(sock_net(sk)); + atomic_inc(&sk->sk_drops); if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD)) __skb_queue_tail(xmitq, skb); break; @@ -3293,7 +3296,9 @@ int tipc_sk_fill_sock_diag(struct sk_buff *skb, struct tipc_sock *tsk, if (nla_put_u32(skb, TIPC_NLA_SOCK_STAT_RCVQ, skb_queue_len(&sk->sk_receive_queue)) || nla_put_u32(skb, TIPC_NLA_SOCK_STAT_SENDQ, - skb_queue_len(&sk->sk_write_queue))) + skb_queue_len(&sk->sk_write_queue)) || + nla_put_u32(skb, TIPC_NLA_SOCK_STAT_DROP, + atomic_read(&sk->sk_drops))) goto stat_msg_cancel; if (tsk->cong_link_cnt && -- cgit v1.2.3 From 14452ca3b5ce304fb2fea96dbc9ca1e4e7978551 Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Wed, 21 Mar 2018 19:48:14 -0600 Subject: net: qualcomm: rmnet: Export mux_id and flags to netlink Define new netlink attributes for rmnet mux_id and flags. These flags / mux_id were earlier using vlan flags / id respectively. The flag bits are also moved to uapi and are renamed with prefix RMNET_FLAG_*. Also add the rmnet policy to handle the new netlink attributes. Signed-off-by: Subash Abhinov Kasiviswanathan Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 41 +++++++++++++--------- .../net/ethernet/qualcomm/rmnet/rmnet_handlers.c | 10 +++--- .../ethernet/qualcomm/rmnet/rmnet_map_command.c | 2 +- .../net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 2 +- .../net/ethernet/qualcomm/rmnet/rmnet_private.h | 6 ---- include/uapi/linux/if_link.h | 21 +++++++++++ 6 files changed, 53 insertions(+), 29 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c index 096301a31c4f..c5b7b2af25d8 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -43,6 +43,11 @@ /* Local Definitions and Declarations */ +static const struct nla_policy rmnet_policy[IFLA_RMNET_MAX + 1] = { + [IFLA_RMNET_MUX_ID] = { .type = NLA_U16 }, + [IFLA_RMNET_FLAGS] = { .len = sizeof(struct ifla_rmnet_flags) }, +}; + static int rmnet_is_real_dev_registered(const struct net_device *real_dev) { return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler; @@ -131,7 +136,7 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { - u32 data_format = RMNET_INGRESS_FORMAT_DEAGGREGATION; + u32 data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION; struct net_device *real_dev; int mode = RMNET_EPMODE_VND; struct rmnet_endpoint *ep; @@ -143,14 +148,14 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, if (!real_dev || !dev) return -ENODEV; - if (!data[IFLA_VLAN_ID]) + if (!data[IFLA_RMNET_MUX_ID]) return -EINVAL; ep = kzalloc(sizeof(*ep), GFP_ATOMIC); if (!ep) return -ENOMEM; - mux_id = nla_get_u16(data[IFLA_VLAN_ID]); + mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]); err = rmnet_register_real_device(real_dev); if (err) @@ -165,10 +170,10 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]); - if (data[IFLA_VLAN_FLAGS]) { - struct ifla_vlan_flags *flags; + if (data[IFLA_RMNET_FLAGS]) { + struct ifla_rmnet_flags *flags; - flags = nla_data(data[IFLA_VLAN_FLAGS]); + flags = nla_data(data[IFLA_RMNET_FLAGS]); data_format = flags->flags & flags->mask; } @@ -276,10 +281,10 @@ static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[], { u16 mux_id; - if (!data || !data[IFLA_VLAN_ID]) + if (!data || !data[IFLA_RMNET_MUX_ID]) return -EINVAL; - mux_id = nla_get_u16(data[IFLA_VLAN_ID]); + mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]); if (mux_id > (RMNET_MAX_LOGICAL_EP - 1)) return -ERANGE; @@ -304,8 +309,8 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[], port = rmnet_get_port_rtnl(real_dev); - if (data[IFLA_VLAN_ID]) { - mux_id = nla_get_u16(data[IFLA_VLAN_ID]); + if (data[IFLA_RMNET_MUX_ID]) { + mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]); ep = rmnet_get_endpoint(port, priv->mux_id); hlist_del_init_rcu(&ep->hlnode); @@ -315,10 +320,10 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[], priv->mux_id = mux_id; } - if (data[IFLA_VLAN_FLAGS]) { - struct ifla_vlan_flags *flags; + if (data[IFLA_RMNET_FLAGS]) { + struct ifla_rmnet_flags *flags; - flags = nla_data(data[IFLA_VLAN_FLAGS]); + flags = nla_data(data[IFLA_RMNET_FLAGS]); port->data_format = flags->flags & flags->mask; } @@ -327,13 +332,16 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[], static size_t rmnet_get_size(const struct net_device *dev) { - return nla_total_size(2) /* IFLA_VLAN_ID */ + - nla_total_size(sizeof(struct ifla_vlan_flags)); /* IFLA_VLAN_FLAGS */ + return + /* IFLA_RMNET_MUX_ID */ + nla_total_size(2) + + /* IFLA_RMNET_FLAGS */ + nla_total_size(sizeof(struct ifla_rmnet_flags)); } struct rtnl_link_ops rmnet_link_ops __read_mostly = { .kind = "rmnet", - .maxtype = __IFLA_VLAN_MAX, + .maxtype = __IFLA_RMNET_MAX, .priv_size = sizeof(struct rmnet_priv), .setup = rmnet_vnd_setup, .validate = rmnet_rtnl_validate, @@ -341,6 +349,7 @@ struct rtnl_link_ops rmnet_link_ops __read_mostly = { .dellink = rmnet_dellink, .get_size = rmnet_get_size, .changelink = rmnet_changelink, + .policy = rmnet_policy, }; /* Needs either rcu_read_lock() or rtnl lock */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c index c758248bf2cd..6fcd586e9804 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c @@ -70,7 +70,7 @@ __rmnet_map_ingress_handler(struct sk_buff *skb, u8 mux_id; if (RMNET_MAP_GET_CD_BIT(skb)) { - if (port->data_format & RMNET_INGRESS_FORMAT_MAP_COMMANDS) + if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS) return rmnet_map_command(skb, port); goto free_skb; @@ -93,7 +93,7 @@ __rmnet_map_ingress_handler(struct sk_buff *skb, skb_pull(skb, sizeof(struct rmnet_map_header)); rmnet_set_skb_proto(skb); - if (port->data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4) { + if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { if (!rmnet_map_checksum_downlink_packet(skb, len + pad)) skb->ip_summed = CHECKSUM_UNNECESSARY; } @@ -121,7 +121,7 @@ rmnet_map_ingress_handler(struct sk_buff *skb, skb_push(skb, ETH_HLEN); } - if (port->data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) { + if (port->data_format & RMNET_FLAGS_INGRESS_DEAGGREGATION) { while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL) __rmnet_map_ingress_handler(skbn, port); @@ -141,7 +141,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, additional_header_len = 0; required_headroom = sizeof(struct rmnet_map_header); - if (port->data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4) { + if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) { additional_header_len = sizeof(struct rmnet_map_ul_csum_header); required_headroom += additional_header_len; } @@ -151,7 +151,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, goto fail; } - if (port->data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4) + if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) rmnet_map_checksum_uplink_packet(skb, orig_dev); map_header = rmnet_map_add_map_header(skb, additional_header_len, 0); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c index afa2b862515f..78fdad0c6f76 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c @@ -69,7 +69,7 @@ static void rmnet_map_send_ack(struct sk_buff *skb, struct rmnet_map_control_command *cmd; int xmit_status; - if (port->data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4) { + if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { if (skb->len < sizeof(struct rmnet_map_header) + RMNET_MAP_GET_LENGTH(skb) + sizeof(struct rmnet_map_dl_csum_trailer)) { diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index e8f6c79157be..a6ea09416f8d 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -309,7 +309,7 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, maph = (struct rmnet_map_header *)skb->data; packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header); - if (port->data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4) + if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) packet_len += sizeof(struct rmnet_map_dl_csum_trailer); if (((int)skb->len - (int)packet_len) < 0) diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h index 98365efc2d9a..b9cc4f85f229 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h @@ -18,12 +18,6 @@ #define RMNET_NEEDED_HEADROOM 16 #define RMNET_TX_QUEUE_LEN 1000 -/* Constants */ -#define RMNET_INGRESS_FORMAT_DEAGGREGATION BIT(0) -#define RMNET_INGRESS_FORMAT_MAP_COMMANDS BIT(1) -#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 BIT(2) -#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 BIT(3) - /* Replace skb->dev to a virtual rmnet device and pass up the stack */ #define RMNET_EPMODE_VND (1) /* Pass the frame directly to another device with dev_queue_xmit() */ diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 11d0c0ea2bfa..68699f654118 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -959,4 +959,25 @@ enum { #define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) +/* rmnet section */ + +#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0) +#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1) +#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2) +#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3) + +enum { + IFLA_RMNET_UNSPEC, + IFLA_RMNET_MUX_ID, + IFLA_RMNET_FLAGS, + __IFLA_RMNET_MAX, +}; + +#define IFLA_RMNET_MAX (__IFLA_RMNET_MAX - 1) + +struct ifla_rmnet_flags { + __u32 flags; + __u32 mask; +}; + #endif /* _UAPI_LINUX_IF_LINK_H */ -- cgit v1.2.3 From c46234ebb4d1eee5e09819f49169e51cfc6eb909 Mon Sep 17 00:00:00 2001 From: Dave Watson Date: Thu, 22 Mar 2018 10:10:35 -0700 Subject: tls: RX path for ktls Add rx path for tls software implementation. recvmsg, splice_read, and poll implemented. An additional sockopt TLS_RX is added, with the same interface as TLS_TX. Either TLX_RX or TLX_TX may be provided separately, or together (with two different setsockopt calls with appropriate keys). Control messages are passed via CMSG in a similar way to transmit. If no cmsg buffer is passed, then only application data records will be passed to userspace, and EIO is returned for other types of alerts. EBADMSG is passed for decryption errors, and EMSGSIZE is passed for framing too big, and EBADMSG for framing too small (matching openssl semantics). EINVAL is returned for TLS versions that do not match the original setsockopt call. All are unrecoverable. strparser is used to parse TLS framing. Decryption is done directly in to userspace buffers if they are large enough to support it, otherwise sk_cow_data is called (similar to ipsec), and buffers are decrypted in place and copied. splice_read always decrypts in place, since no buffers are provided to decrypt in to. sk_poll is overridden, and only returns POLLIN if a full TLS message is received. Otherwise we wait for strparser to finish reading a full frame. Actual decryption is only done during recvmsg or splice_read calls. Signed-off-by: Dave Watson Signed-off-by: David S. Miller --- include/net/tls.h | 27 ++- include/uapi/linux/tls.h | 2 + net/tls/Kconfig | 1 + net/tls/tls_main.c | 62 ++++- net/tls/tls_sw.c | 587 ++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 609 insertions(+), 70 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/tls.h b/include/net/tls.h index 095b72283861..437a746300bf 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -40,6 +40,7 @@ #include #include #include +#include #include @@ -58,8 +59,18 @@ struct tls_sw_context { struct crypto_aead *aead_send; + struct crypto_aead *aead_recv; struct crypto_wait async_wait; + /* Receive context */ + struct strparser strp; + void (*saved_data_ready)(struct sock *sk); + unsigned int (*sk_poll)(struct file *file, struct socket *sock, + struct poll_table_struct *wait); + struct sk_buff *recv_pkt; + u8 control; + bool decrypted; + /* Sending context */ char aad_space[TLS_AAD_SPACE_SIZE]; @@ -96,12 +107,17 @@ struct tls_context { struct tls_crypto_info crypto_send; struct tls12_crypto_info_aes_gcm_128 crypto_send_aes_gcm_128; }; + union { + struct tls_crypto_info crypto_recv; + struct tls12_crypto_info_aes_gcm_128 crypto_recv_aes_gcm_128; + }; void *priv_ctx; u8 conf:2; struct cipher_context tx; + struct cipher_context rx; struct scatterlist *partially_sent_record; u16 partially_sent_offset; @@ -128,12 +144,19 @@ int tls_sk_attach(struct sock *sk, int optname, char __user *optval, unsigned int optlen); -int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx); +int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx); int tls_sw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size); int tls_sw_sendpage(struct sock *sk, struct page *page, int offset, size_t size, int flags); void tls_sw_close(struct sock *sk, long timeout); -void tls_sw_free_tx_resources(struct sock *sk); +void tls_sw_free_resources(struct sock *sk); +int tls_sw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, + int nonblock, int flags, int *addr_len); +unsigned int tls_sw_poll(struct file *file, struct socket *sock, + struct poll_table_struct *wait); +ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, + struct pipe_inode_info *pipe, + size_t len, unsigned int flags); void tls_sk_destruct(struct sock *sk, struct tls_context *ctx); void tls_icsk_clean_acked(struct sock *sk); diff --git a/include/uapi/linux/tls.h b/include/uapi/linux/tls.h index 293b2cdad88d..c6633e97eca4 100644 --- a/include/uapi/linux/tls.h +++ b/include/uapi/linux/tls.h @@ -38,6 +38,7 @@ /* TLS socket options */ #define TLS_TX 1 /* Set transmit parameters */ +#define TLS_RX 2 /* Set receive parameters */ /* Supported versions */ #define TLS_VERSION_MINOR(ver) ((ver) & 0xFF) @@ -59,6 +60,7 @@ #define TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE 8 #define TLS_SET_RECORD_TYPE 1 +#define TLS_GET_RECORD_TYPE 2 struct tls_crypto_info { __u16 version; diff --git a/net/tls/Kconfig b/net/tls/Kconfig index eb583038c67e..89b8745a986f 100644 --- a/net/tls/Kconfig +++ b/net/tls/Kconfig @@ -7,6 +7,7 @@ config TLS select CRYPTO select CRYPTO_AES select CRYPTO_GCM + select STREAM_PARSER default n ---help--- Enable kernel support for TLS protocol. This allows symmetric diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index c405beeda765..6f5c1146da4a 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -54,12 +54,15 @@ enum { enum { TLS_BASE, TLS_SW_TX, + TLS_SW_RX, + TLS_SW_RXTX, TLS_NUM_CONFIG, }; static struct proto *saved_tcpv6_prot; static DEFINE_MUTEX(tcpv6_prot_mutex); static struct proto tls_prots[TLS_NUM_PROTS][TLS_NUM_CONFIG]; +static struct proto_ops tls_sw_proto_ops; static inline void update_sk_prot(struct sock *sk, struct tls_context *ctx) { @@ -261,9 +264,14 @@ static void tls_sk_proto_close(struct sock *sk, long timeout) kfree(ctx->tx.rec_seq); kfree(ctx->tx.iv); + kfree(ctx->rx.rec_seq); + kfree(ctx->rx.iv); - if (ctx->conf == TLS_SW_TX) - tls_sw_free_tx_resources(sk); + if (ctx->conf == TLS_SW_TX || + ctx->conf == TLS_SW_RX || + ctx->conf == TLS_SW_RXTX) { + tls_sw_free_resources(sk); + } skip_tx_cleanup: release_sock(sk); @@ -365,8 +373,8 @@ static int tls_getsockopt(struct sock *sk, int level, int optname, return do_tls_getsockopt(sk, optname, optval, optlen); } -static int do_tls_setsockopt_tx(struct sock *sk, char __user *optval, - unsigned int optlen) +static int do_tls_setsockopt_conf(struct sock *sk, char __user *optval, + unsigned int optlen, int tx) { struct tls_crypto_info *crypto_info; struct tls_context *ctx = tls_get_ctx(sk); @@ -378,7 +386,11 @@ static int do_tls_setsockopt_tx(struct sock *sk, char __user *optval, goto out; } - crypto_info = &ctx->crypto_send; + if (tx) + crypto_info = &ctx->crypto_send; + else + crypto_info = &ctx->crypto_recv; + /* Currently we don't support set crypto info more than one time */ if (TLS_CRYPTO_INFO_READY(crypto_info)) { rc = -EBUSY; @@ -417,15 +429,31 @@ static int do_tls_setsockopt_tx(struct sock *sk, char __user *optval, } /* currently SW is default, we will have ethtool in future */ - rc = tls_set_sw_offload(sk, ctx); - conf = TLS_SW_TX; + if (tx) { + rc = tls_set_sw_offload(sk, ctx, 1); + if (ctx->conf == TLS_SW_RX) + conf = TLS_SW_RXTX; + else + conf = TLS_SW_TX; + } else { + rc = tls_set_sw_offload(sk, ctx, 0); + if (ctx->conf == TLS_SW_TX) + conf = TLS_SW_RXTX; + else + conf = TLS_SW_RX; + } + if (rc) goto err_crypto_info; ctx->conf = conf; update_sk_prot(sk, ctx); - ctx->sk_write_space = sk->sk_write_space; - sk->sk_write_space = tls_write_space; + if (tx) { + ctx->sk_write_space = sk->sk_write_space; + sk->sk_write_space = tls_write_space; + } else { + sk->sk_socket->ops = &tls_sw_proto_ops; + } goto out; err_crypto_info: @@ -441,8 +469,10 @@ static int do_tls_setsockopt(struct sock *sk, int optname, switch (optname) { case TLS_TX: + case TLS_RX: lock_sock(sk); - rc = do_tls_setsockopt_tx(sk, optval, optlen); + rc = do_tls_setsockopt_conf(sk, optval, optlen, + optname == TLS_TX); release_sock(sk); break; default: @@ -473,6 +503,14 @@ static void build_protos(struct proto *prot, struct proto *base) prot[TLS_SW_TX] = prot[TLS_BASE]; prot[TLS_SW_TX].sendmsg = tls_sw_sendmsg; prot[TLS_SW_TX].sendpage = tls_sw_sendpage; + + prot[TLS_SW_RX] = prot[TLS_BASE]; + prot[TLS_SW_RX].recvmsg = tls_sw_recvmsg; + prot[TLS_SW_RX].close = tls_sk_proto_close; + + prot[TLS_SW_RXTX] = prot[TLS_SW_TX]; + prot[TLS_SW_RXTX].recvmsg = tls_sw_recvmsg; + prot[TLS_SW_RXTX].close = tls_sk_proto_close; } static int tls_init(struct sock *sk) @@ -531,6 +569,10 @@ static int __init tls_register(void) { build_protos(tls_prots[TLSV4], &tcp_prot); + tls_sw_proto_ops = inet_stream_ops; + tls_sw_proto_ops.poll = tls_sw_poll; + tls_sw_proto_ops.splice_read = tls_sw_splice_read; + tcp_register_ulp(&tcp_tls_ulp_ops); return 0; diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 1c79d9ad1731..4dc766b03f00 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -34,11 +34,60 @@ * SOFTWARE. */ +#include #include #include +#include #include +static int tls_do_decryption(struct sock *sk, + struct scatterlist *sgin, + struct scatterlist *sgout, + char *iv_recv, + size_t data_len, + struct sk_buff *skb, + gfp_t flags) +{ + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); + struct strp_msg *rxm = strp_msg(skb); + struct aead_request *aead_req; + + int ret; + unsigned int req_size = sizeof(struct aead_request) + + crypto_aead_reqsize(ctx->aead_recv); + + aead_req = kzalloc(req_size, flags); + if (!aead_req) + return -ENOMEM; + + aead_request_set_tfm(aead_req, ctx->aead_recv); + aead_request_set_ad(aead_req, TLS_AAD_SPACE_SIZE); + aead_request_set_crypt(aead_req, sgin, sgout, + data_len + tls_ctx->rx.tag_size, + (u8 *)iv_recv); + aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &ctx->async_wait); + + ret = crypto_wait_req(crypto_aead_decrypt(aead_req), &ctx->async_wait); + + if (ret < 0) + goto out; + + rxm->offset += tls_ctx->rx.prepend_size; + rxm->full_len -= tls_ctx->rx.overhead_size; + tls_advance_record_sn(sk, &tls_ctx->rx); + + ctx->decrypted = true; + + ctx->saved_data_ready(sk); + +out: + kfree(aead_req); + return ret; +} + static void trim_sg(struct sock *sk, struct scatterlist *sg, int *sg_num_elem, unsigned int *sg_size, int target_size) { @@ -581,13 +630,404 @@ sendpage_end: return ret; } -void tls_sw_free_tx_resources(struct sock *sk) +static struct sk_buff *tls_wait_data(struct sock *sk, int flags, + long timeo, int *err) +{ + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); + struct sk_buff *skb; + DEFINE_WAIT_FUNC(wait, woken_wake_function); + + while (!(skb = ctx->recv_pkt)) { + if (sk->sk_err) { + *err = sock_error(sk); + return NULL; + } + + if (sock_flag(sk, SOCK_DONE)) + return NULL; + + if ((flags & MSG_DONTWAIT) || !timeo) { + *err = -EAGAIN; + return NULL; + } + + add_wait_queue(sk_sleep(sk), &wait); + sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); + sk_wait_event(sk, &timeo, ctx->recv_pkt != skb, &wait); + sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); + remove_wait_queue(sk_sleep(sk), &wait); + + /* Handle signals */ + if (signal_pending(current)) { + *err = sock_intr_errno(timeo); + return NULL; + } + } + + return skb; +} + +static int decrypt_skb(struct sock *sk, struct sk_buff *skb, + struct scatterlist *sgout) +{ + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); + char iv[TLS_CIPHER_AES_GCM_128_SALT_SIZE + tls_ctx->rx.iv_size]; + struct scatterlist sgin_arr[MAX_SKB_FRAGS + 2]; + struct scatterlist *sgin = &sgin_arr[0]; + struct strp_msg *rxm = strp_msg(skb); + int ret, nsg = ARRAY_SIZE(sgin_arr); + char aad_recv[TLS_AAD_SPACE_SIZE]; + struct sk_buff *unused; + + ret = skb_copy_bits(skb, rxm->offset + TLS_HEADER_SIZE, + iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, + tls_ctx->rx.iv_size); + if (ret < 0) + return ret; + + memcpy(iv, tls_ctx->rx.iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE); + if (!sgout) { + nsg = skb_cow_data(skb, 0, &unused) + 1; + sgin = kmalloc_array(nsg, sizeof(*sgin), sk->sk_allocation); + if (!sgout) + sgout = sgin; + } + + sg_init_table(sgin, nsg); + sg_set_buf(&sgin[0], aad_recv, sizeof(aad_recv)); + + nsg = skb_to_sgvec(skb, &sgin[1], + rxm->offset + tls_ctx->rx.prepend_size, + rxm->full_len - tls_ctx->rx.prepend_size); + + tls_make_aad(aad_recv, + rxm->full_len - tls_ctx->rx.overhead_size, + tls_ctx->rx.rec_seq, + tls_ctx->rx.rec_seq_size, + ctx->control); + + ret = tls_do_decryption(sk, sgin, sgout, iv, + rxm->full_len - tls_ctx->rx.overhead_size, + skb, sk->sk_allocation); + + if (sgin != &sgin_arr[0]) + kfree(sgin); + + return ret; +} + +static bool tls_sw_advance_skb(struct sock *sk, struct sk_buff *skb, + unsigned int len) +{ + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); + struct strp_msg *rxm = strp_msg(skb); + + if (len < rxm->full_len) { + rxm->offset += len; + rxm->full_len -= len; + + return false; + } + + /* Finished with message */ + ctx->recv_pkt = NULL; + kfree_skb(skb); + strp_unpause(&ctx->strp); + + return true; +} + +int tls_sw_recvmsg(struct sock *sk, + struct msghdr *msg, + size_t len, + int nonblock, + int flags, + int *addr_len) +{ + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); + unsigned char control; + struct strp_msg *rxm; + struct sk_buff *skb; + ssize_t copied = 0; + bool cmsg = false; + int err = 0; + long timeo; + + flags |= nonblock; + + if (unlikely(flags & MSG_ERRQUEUE)) + return sock_recv_errqueue(sk, msg, len, SOL_IP, IP_RECVERR); + + lock_sock(sk); + + timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); + do { + bool zc = false; + int chunk = 0; + + skb = tls_wait_data(sk, flags, timeo, &err); + if (!skb) + goto recv_end; + + rxm = strp_msg(skb); + if (!cmsg) { + int cerr; + + cerr = put_cmsg(msg, SOL_TLS, TLS_GET_RECORD_TYPE, + sizeof(ctx->control), &ctx->control); + cmsg = true; + control = ctx->control; + if (ctx->control != TLS_RECORD_TYPE_DATA) { + if (cerr || msg->msg_flags & MSG_CTRUNC) { + err = -EIO; + goto recv_end; + } + } + } else if (control != ctx->control) { + goto recv_end; + } + + if (!ctx->decrypted) { + int page_count; + int to_copy; + + page_count = iov_iter_npages(&msg->msg_iter, + MAX_SKB_FRAGS); + to_copy = rxm->full_len - tls_ctx->rx.overhead_size; + if (to_copy <= len && page_count < MAX_SKB_FRAGS && + likely(!(flags & MSG_PEEK))) { + struct scatterlist sgin[MAX_SKB_FRAGS + 1]; + char unused[21]; + int pages = 0; + + zc = true; + sg_init_table(sgin, MAX_SKB_FRAGS + 1); + sg_set_buf(&sgin[0], unused, 13); + + err = zerocopy_from_iter(sk, &msg->msg_iter, + to_copy, &pages, + &chunk, &sgin[1], + MAX_SKB_FRAGS, false); + if (err < 0) + goto fallback_to_reg_recv; + + err = decrypt_skb(sk, skb, sgin); + for (; pages > 0; pages--) + put_page(sg_page(&sgin[pages])); + if (err < 0) { + tls_err_abort(sk, EBADMSG); + goto recv_end; + } + } else { +fallback_to_reg_recv: + err = decrypt_skb(sk, skb, NULL); + if (err < 0) { + tls_err_abort(sk, EBADMSG); + goto recv_end; + } + } + ctx->decrypted = true; + } + + if (!zc) { + chunk = min_t(unsigned int, rxm->full_len, len); + err = skb_copy_datagram_msg(skb, rxm->offset, msg, + chunk); + if (err < 0) + goto recv_end; + } + + copied += chunk; + len -= chunk; + if (likely(!(flags & MSG_PEEK))) { + u8 control = ctx->control; + + if (tls_sw_advance_skb(sk, skb, chunk)) { + /* Return full control message to + * userspace before trying to parse + * another message type + */ + msg->msg_flags |= MSG_EOR; + if (control != TLS_RECORD_TYPE_DATA) + goto recv_end; + } + } + } while (len); + +recv_end: + release_sock(sk); + return copied ? : err; +} + +ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, + struct pipe_inode_info *pipe, + size_t len, unsigned int flags) +{ + struct tls_context *tls_ctx = tls_get_ctx(sock->sk); + struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); + struct strp_msg *rxm = NULL; + struct sock *sk = sock->sk; + struct sk_buff *skb; + ssize_t copied = 0; + int err = 0; + long timeo; + int chunk; + + lock_sock(sk); + + timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); + + skb = tls_wait_data(sk, flags, timeo, &err); + if (!skb) + goto splice_read_end; + + /* splice does not support reading control messages */ + if (ctx->control != TLS_RECORD_TYPE_DATA) { + err = -ENOTSUPP; + goto splice_read_end; + } + + if (!ctx->decrypted) { + err = decrypt_skb(sk, skb, NULL); + + if (err < 0) { + tls_err_abort(sk, EBADMSG); + goto splice_read_end; + } + ctx->decrypted = true; + } + rxm = strp_msg(skb); + + chunk = min_t(unsigned int, rxm->full_len, len); + copied = skb_splice_bits(skb, sk, rxm->offset, pipe, chunk, flags); + if (copied < 0) + goto splice_read_end; + + if (likely(!(flags & MSG_PEEK))) + tls_sw_advance_skb(sk, skb, copied); + +splice_read_end: + release_sock(sk); + return copied ? : err; +} + +unsigned int tls_sw_poll(struct file *file, struct socket *sock, + struct poll_table_struct *wait) +{ + unsigned int ret; + struct sock *sk = sock->sk; + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); + + /* Grab POLLOUT and POLLHUP from the underlying socket */ + ret = ctx->sk_poll(file, sock, wait); + + /* Clear POLLIN bits, and set based on recv_pkt */ + ret &= ~(POLLIN | POLLRDNORM); + if (ctx->recv_pkt) + ret |= POLLIN | POLLRDNORM; + + return ret; +} + +static int tls_read_size(struct strparser *strp, struct sk_buff *skb) +{ + struct tls_context *tls_ctx = tls_get_ctx(strp->sk); + struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); + char header[tls_ctx->rx.prepend_size]; + struct strp_msg *rxm = strp_msg(skb); + size_t cipher_overhead; + size_t data_len = 0; + int ret; + + /* Verify that we have a full TLS header, or wait for more data */ + if (rxm->offset + tls_ctx->rx.prepend_size > skb->len) + return 0; + + /* Linearize header to local buffer */ + ret = skb_copy_bits(skb, rxm->offset, header, tls_ctx->rx.prepend_size); + + if (ret < 0) + goto read_failure; + + ctx->control = header[0]; + + data_len = ((header[4] & 0xFF) | (header[3] << 8)); + + cipher_overhead = tls_ctx->rx.tag_size + tls_ctx->rx.iv_size; + + if (data_len > TLS_MAX_PAYLOAD_SIZE + cipher_overhead) { + ret = -EMSGSIZE; + goto read_failure; + } + if (data_len < cipher_overhead) { + ret = -EBADMSG; + goto read_failure; + } + + if (header[1] != TLS_VERSION_MINOR(tls_ctx->crypto_recv.version) || + header[2] != TLS_VERSION_MAJOR(tls_ctx->crypto_recv.version)) { + ret = -EINVAL; + goto read_failure; + } + + return data_len + TLS_HEADER_SIZE; + +read_failure: + tls_err_abort(strp->sk, ret); + + return ret; +} + +static void tls_queue(struct strparser *strp, struct sk_buff *skb) +{ + struct tls_context *tls_ctx = tls_get_ctx(strp->sk); + struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); + struct strp_msg *rxm; + + rxm = strp_msg(skb); + + ctx->decrypted = false; + + ctx->recv_pkt = skb; + strp_pause(strp); + + strp->sk->sk_state_change(strp->sk); +} + +static void tls_data_ready(struct sock *sk) +{ + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); + + strp_data_ready(&ctx->strp); +} + +void tls_sw_free_resources(struct sock *sk) { struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); if (ctx->aead_send) crypto_free_aead(ctx->aead_send); + if (ctx->aead_recv) { + if (ctx->recv_pkt) { + kfree_skb(ctx->recv_pkt); + ctx->recv_pkt = NULL; + } + crypto_free_aead(ctx->aead_recv); + strp_stop(&ctx->strp); + write_lock_bh(&sk->sk_callback_lock); + sk->sk_data_ready = ctx->saved_data_ready; + write_unlock_bh(&sk->sk_callback_lock); + release_sock(sk); + strp_done(&ctx->strp); + lock_sock(sk); + } tls_free_both_sg(sk); @@ -595,12 +1035,15 @@ void tls_sw_free_tx_resources(struct sock *sk) kfree(tls_ctx); } -int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx) +int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx) { char keyval[TLS_CIPHER_AES_GCM_128_KEY_SIZE]; struct tls_crypto_info *crypto_info; struct tls12_crypto_info_aes_gcm_128 *gcm_128_info; struct tls_sw_context *sw_ctx; + struct cipher_context *cctx; + struct crypto_aead **aead; + struct strp_callbacks cb; u16 nonce_size, tag_size, iv_size, rec_seq_size; char *iv, *rec_seq; int rc = 0; @@ -610,22 +1053,29 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx) goto out; } - if (ctx->priv_ctx) { - rc = -EEXIST; - goto out; - } - - sw_ctx = kzalloc(sizeof(*sw_ctx), GFP_KERNEL); - if (!sw_ctx) { - rc = -ENOMEM; - goto out; + if (!ctx->priv_ctx) { + sw_ctx = kzalloc(sizeof(*sw_ctx), GFP_KERNEL); + if (!sw_ctx) { + rc = -ENOMEM; + goto out; + } + crypto_init_wait(&sw_ctx->async_wait); + } else { + sw_ctx = ctx->priv_ctx; } - crypto_init_wait(&sw_ctx->async_wait); - ctx->priv_ctx = (struct tls_offload_context *)sw_ctx; - crypto_info = &ctx->crypto_send; + if (tx) { + crypto_info = &ctx->crypto_send; + cctx = &ctx->tx; + aead = &sw_ctx->aead_send; + } else { + crypto_info = &ctx->crypto_recv; + cctx = &ctx->rx; + aead = &sw_ctx->aead_recv; + } + switch (crypto_info->cipher_type) { case TLS_CIPHER_AES_GCM_128: { nonce_size = TLS_CIPHER_AES_GCM_128_IV_SIZE; @@ -644,48 +1094,49 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx) goto free_priv; } - ctx->tx.prepend_size = TLS_HEADER_SIZE + nonce_size; - ctx->tx.tag_size = tag_size; - ctx->tx.overhead_size = ctx->tx.prepend_size + ctx->tx.tag_size; - ctx->tx.iv_size = iv_size; - ctx->tx.iv = kmalloc(iv_size + TLS_CIPHER_AES_GCM_128_SALT_SIZE, - GFP_KERNEL); - if (!ctx->tx.iv) { + cctx->prepend_size = TLS_HEADER_SIZE + nonce_size; + cctx->tag_size = tag_size; + cctx->overhead_size = cctx->prepend_size + cctx->tag_size; + cctx->iv_size = iv_size; + cctx->iv = kmalloc(iv_size + TLS_CIPHER_AES_GCM_128_SALT_SIZE, + GFP_KERNEL); + if (!cctx->iv) { rc = -ENOMEM; goto free_priv; } - memcpy(ctx->tx.iv, gcm_128_info->salt, - TLS_CIPHER_AES_GCM_128_SALT_SIZE); - memcpy(ctx->tx.iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, iv, iv_size); - ctx->tx.rec_seq_size = rec_seq_size; - ctx->tx.rec_seq = kmalloc(rec_seq_size, GFP_KERNEL); - if (!ctx->tx.rec_seq) { + memcpy(cctx->iv, gcm_128_info->salt, TLS_CIPHER_AES_GCM_128_SALT_SIZE); + memcpy(cctx->iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, iv, iv_size); + cctx->rec_seq_size = rec_seq_size; + cctx->rec_seq = kmalloc(rec_seq_size, GFP_KERNEL); + if (!cctx->rec_seq) { rc = -ENOMEM; goto free_iv; } - memcpy(ctx->tx.rec_seq, rec_seq, rec_seq_size); - - sg_init_table(sw_ctx->sg_encrypted_data, - ARRAY_SIZE(sw_ctx->sg_encrypted_data)); - sg_init_table(sw_ctx->sg_plaintext_data, - ARRAY_SIZE(sw_ctx->sg_plaintext_data)); - - sg_init_table(sw_ctx->sg_aead_in, 2); - sg_set_buf(&sw_ctx->sg_aead_in[0], sw_ctx->aad_space, - sizeof(sw_ctx->aad_space)); - sg_unmark_end(&sw_ctx->sg_aead_in[1]); - sg_chain(sw_ctx->sg_aead_in, 2, sw_ctx->sg_plaintext_data); - sg_init_table(sw_ctx->sg_aead_out, 2); - sg_set_buf(&sw_ctx->sg_aead_out[0], sw_ctx->aad_space, - sizeof(sw_ctx->aad_space)); - sg_unmark_end(&sw_ctx->sg_aead_out[1]); - sg_chain(sw_ctx->sg_aead_out, 2, sw_ctx->sg_encrypted_data); - - if (!sw_ctx->aead_send) { - sw_ctx->aead_send = crypto_alloc_aead("gcm(aes)", 0, 0); - if (IS_ERR(sw_ctx->aead_send)) { - rc = PTR_ERR(sw_ctx->aead_send); - sw_ctx->aead_send = NULL; + memcpy(cctx->rec_seq, rec_seq, rec_seq_size); + + if (tx) { + sg_init_table(sw_ctx->sg_encrypted_data, + ARRAY_SIZE(sw_ctx->sg_encrypted_data)); + sg_init_table(sw_ctx->sg_plaintext_data, + ARRAY_SIZE(sw_ctx->sg_plaintext_data)); + + sg_init_table(sw_ctx->sg_aead_in, 2); + sg_set_buf(&sw_ctx->sg_aead_in[0], sw_ctx->aad_space, + sizeof(sw_ctx->aad_space)); + sg_unmark_end(&sw_ctx->sg_aead_in[1]); + sg_chain(sw_ctx->sg_aead_in, 2, sw_ctx->sg_plaintext_data); + sg_init_table(sw_ctx->sg_aead_out, 2); + sg_set_buf(&sw_ctx->sg_aead_out[0], sw_ctx->aad_space, + sizeof(sw_ctx->aad_space)); + sg_unmark_end(&sw_ctx->sg_aead_out[1]); + sg_chain(sw_ctx->sg_aead_out, 2, sw_ctx->sg_encrypted_data); + } + + if (!*aead) { + *aead = crypto_alloc_aead("gcm(aes)", 0, 0); + if (IS_ERR(*aead)) { + rc = PTR_ERR(*aead); + *aead = NULL; goto free_rec_seq; } } @@ -694,21 +1145,41 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx) memcpy(keyval, gcm_128_info->key, TLS_CIPHER_AES_GCM_128_KEY_SIZE); - rc = crypto_aead_setkey(sw_ctx->aead_send, keyval, + rc = crypto_aead_setkey(*aead, keyval, TLS_CIPHER_AES_GCM_128_KEY_SIZE); if (rc) goto free_aead; - rc = crypto_aead_setauthsize(sw_ctx->aead_send, ctx->tx.tag_size); - if (!rc) - return 0; + rc = crypto_aead_setauthsize(*aead, cctx->tag_size); + if (rc) + goto free_aead; + + if (!tx) { + /* Set up strparser */ + memset(&cb, 0, sizeof(cb)); + cb.rcv_msg = tls_queue; + cb.parse_msg = tls_read_size; + + strp_init(&sw_ctx->strp, sk, &cb); + + write_lock_bh(&sk->sk_callback_lock); + sw_ctx->saved_data_ready = sk->sk_data_ready; + sk->sk_data_ready = tls_data_ready; + write_unlock_bh(&sk->sk_callback_lock); + + sw_ctx->sk_poll = sk->sk_socket->ops->poll; + + strp_check_rcv(&sw_ctx->strp); + } + + goto out; free_aead: - crypto_free_aead(sw_ctx->aead_send); - sw_ctx->aead_send = NULL; + crypto_free_aead(*aead); + *aead = NULL; free_rec_seq: - kfree(ctx->tx.rec_seq); - ctx->tx.rec_seq = NULL; + kfree(cctx->rec_seq); + cctx->rec_seq = NULL; free_iv: kfree(ctx->tx.iv); ctx->tx.iv = NULL; -- cgit v1.2.3 From d50ccc2d3909fc1b4d40e4af16b026f05dc68707 Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Thu, 22 Mar 2018 20:42:50 +0100 Subject: tipc: add 128-bit node identifier We add a 128-bit node identity, as an alternative to the currently used 32-bit node address. For the sake of compatibility and to minimize message header changes we retain the existing 32-bit address field. When not set explicitly by the user, this field will be filled with a hash value generated from the much longer node identity, and be used as a shorthand value for the latter. We permit either the address or the identity to be set by configuration, but not both, so when the address value is set by a legacy user the corresponding 128-bit node identity is generated based on the that value. Acked-by: Ying Xue Signed-off-by: Jon Maloy Signed-off-by: David S. Miller --- include/uapi/linux/tipc_netlink.h | 2 + net/tipc/addr.c | 81 ++++++++++++++++++++++++++++++++------- net/tipc/addr.h | 28 +++++++++++--- net/tipc/core.c | 4 +- net/tipc/core.h | 6 ++- net/tipc/discover.c | 4 +- net/tipc/link.c | 6 ++- net/tipc/name_distr.c | 6 +-- net/tipc/net.c | 51 ++++++++++++++++-------- net/tipc/net.h | 4 +- net/tipc/node.c | 8 +--- net/tipc/node.h | 4 +- 12 files changed, 148 insertions(+), 56 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/tipc_netlink.h b/include/uapi/linux/tipc_netlink.h index d896ded51bcb..0affb682e5e3 100644 --- a/include/uapi/linux/tipc_netlink.h +++ b/include/uapi/linux/tipc_netlink.h @@ -169,6 +169,8 @@ enum { TIPC_NLA_NET_UNSPEC, TIPC_NLA_NET_ID, /* u32 */ TIPC_NLA_NET_ADDR, /* u32 */ + TIPC_NLA_NET_NODEID, /* u64 */ + TIPC_NLA_NET_NODEID_W1, /* u64 */ __TIPC_NLA_NET_MAX, TIPC_NLA_NET_MAX = __TIPC_NLA_NET_MAX - 1 diff --git a/net/tipc/addr.c b/net/tipc/addr.c index 6e06b4d981f1..4841e98591d0 100644 --- a/net/tipc/addr.c +++ b/net/tipc/addr.c @@ -1,7 +1,7 @@ /* * net/tipc/addr.c: TIPC address utility routines * - * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2000-2006, 2018, Ericsson AB * Copyright (c) 2004-2005, 2010-2011, Wind River Systems * All rights reserved. * @@ -34,18 +34,9 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include #include "addr.h" #include "core.h" -/** - * in_own_node - test for node inclusion; <0.0.0> always matches - */ -int in_own_node(struct net *net, u32 addr) -{ - return addr == tipc_own_addr(net) || !addr; -} - bool tipc_in_scope(bool legacy_format, u32 domain, u32 addr) { if (!domain || (domain == addr)) @@ -61,9 +52,71 @@ bool tipc_in_scope(bool legacy_format, u32 domain, u32 addr) return false; } -char *tipc_addr_string_fill(char *string, u32 addr) +void tipc_set_node_id(struct net *net, u8 *id) +{ + struct tipc_net *tn = tipc_net(net); + u32 *tmp = (u32 *)id; + + memcpy(tn->node_id, id, NODE_ID_LEN); + tipc_nodeid2string(tn->node_id_string, id); + tn->node_addr = tmp[0] ^ tmp[1] ^ tmp[2] ^ tmp[3]; + pr_info("Own node identity %s, cluster identity %u\n", + tipc_own_id_string(net), tn->net_id); +} + +void tipc_set_node_addr(struct net *net, u32 addr) { - snprintf(string, 16, "<%u.%u.%u>", - tipc_zone(addr), tipc_cluster(addr), tipc_node(addr)); - return string; + struct tipc_net *tn = tipc_net(net); + u8 node_id[NODE_ID_LEN] = {0,}; + + tn->node_addr = addr; + if (!tipc_own_id(net)) { + sprintf(node_id, "%x", addr); + tipc_set_node_id(net, node_id); + } + pr_info("32-bit node address hash set to %x\n", addr); +} + +char *tipc_nodeid2string(char *str, u8 *id) +{ + int i; + u8 c; + + /* Already a string ? */ + for (i = 0; i < NODE_ID_LEN; i++) { + c = id[i]; + if (c >= '0' && c <= '9') + continue; + if (c >= 'A' && c <= 'Z') + continue; + if (c >= 'a' && c <= 'z') + continue; + if (c == '.') + continue; + if (c == ':') + continue; + if (c == '_') + continue; + if (c == '-') + continue; + if (c == '@') + continue; + if (c != 0) + break; + } + if (i == NODE_ID_LEN) { + memcpy(str, id, NODE_ID_LEN); + str[NODE_ID_LEN] = 0; + return str; + } + + /* Translate to hex string */ + for (i = 0; i < NODE_ID_LEN; i++) + sprintf(&str[2 * i], "%02x", id[i]); + + /* Strip off trailing zeroes */ + for (i = NODE_ID_STR_LEN - 2; str[i] == '0'; i--) + str[i] = 0; + + return str; } diff --git a/net/tipc/addr.h b/net/tipc/addr.h index 6b48f0dc0205..31bee0ea7b3e 100644 --- a/net/tipc/addr.h +++ b/net/tipc/addr.h @@ -1,7 +1,7 @@ /* * net/tipc/addr.h: Include file for TIPC address utility routines * - * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2000-2006, 2018, Ericsson AB * Copyright (c) 2004-2005, Wind River Systems * All rights reserved. * @@ -44,10 +44,22 @@ #include "core.h" static inline u32 tipc_own_addr(struct net *net) +{ + return tipc_net(net)->node_addr; +} + +static inline u8 *tipc_own_id(struct net *net) { struct tipc_net *tn = tipc_net(net); - return tn->own_addr; + if (!strlen(tn->node_id_string)) + return NULL; + return tn->node_id; +} + +static inline char *tipc_own_id_string(struct net *net) +{ + return tipc_net(net)->node_id_string; } static inline u32 tipc_cluster_mask(u32 addr) @@ -65,9 +77,15 @@ static inline int tipc_scope2node(struct net *net, int sc) return sc != TIPC_NODE_SCOPE ? 0 : tipc_own_addr(net); } -u32 tipc_own_addr(struct net *net); -int in_own_node(struct net *net, u32 addr); +static inline int in_own_node(struct net *net, u32 addr) +{ + return addr == tipc_own_addr(net) || !addr; +} + bool tipc_in_scope(bool legacy_format, u32 domain, u32 addr); -char *tipc_addr_string_fill(char *string, u32 addr); +void tipc_set_node_id(struct net *net, u8 *id); +void tipc_set_node_addr(struct net *net, u32 addr); +char *tipc_nodeid2string(char *str, u8 *id); +u32 tipc_node_id2hash(u8 *id128); #endif diff --git a/net/tipc/core.c b/net/tipc/core.c index 04fd91bb11d7..e92fed49e095 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -56,7 +56,9 @@ static int __net_init tipc_init_net(struct net *net) int err; tn->net_id = 4711; - tn->own_addr = 0; + tn->node_addr = 0; + memset(tn->node_id, 0, sizeof(tn->node_id)); + memset(tn->node_id_string, 0, sizeof(tn->node_id_string)); tn->mon_threshold = TIPC_DEF_MON_THRESHOLD; get_random_bytes(&tn->random, sizeof(int)); INIT_LIST_HEAD(&tn->node_list); diff --git a/net/tipc/core.h b/net/tipc/core.h index bd2b112680a4..eabad41cc832 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -72,13 +72,17 @@ struct tipc_monitor; #define NODE_HTABLE_SIZE 512 #define MAX_BEARERS 3 #define TIPC_DEF_MON_THRESHOLD 32 +#define NODE_ID_LEN 16 +#define NODE_ID_STR_LEN (NODE_ID_LEN * 2 + 1) extern unsigned int tipc_net_id __read_mostly; extern int sysctl_tipc_rmem[3] __read_mostly; extern int sysctl_tipc_named_timeout __read_mostly; struct tipc_net { - u32 own_addr; + u8 node_id[NODE_ID_LEN]; + u32 node_addr; + char node_id_string[NODE_ID_STR_LEN]; int net_id; int random; bool legacy_addr_format; diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 94d524018ca5..b4c4cd176b9b 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -118,13 +118,11 @@ static void tipc_disc_msg_xmit(struct net *net, u32 mtyp, u32 dst, u32 src, static void disc_dupl_alert(struct tipc_bearer *b, u32 node_addr, struct tipc_media_addr *media_addr) { - char node_addr_str[16]; char media_addr_str[64]; - tipc_addr_string_fill(node_addr_str, node_addr); tipc_media_addr_printf(media_addr_str, sizeof(media_addr_str), media_addr); - pr_warn("Duplicate %s using %s seen on <%s>\n", node_addr_str, + pr_warn("Duplicate %x using %s seen on <%s>\n", node_addr, media_addr_str, b->name); } diff --git a/net/tipc/link.c b/net/tipc/link.c index 4aa56e3bf4fc..bcd76b1e440c 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -442,6 +442,7 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id, struct sk_buff_head *namedq, struct tipc_link **link) { + char *self_str = tipc_own_id_string(net); struct tipc_link *l; l = kzalloc(sizeof(*l), GFP_ATOMIC); @@ -451,7 +452,10 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id, l->session = session; /* Note: peer i/f name is completed by reset/activate message */ - sprintf(l->name, "%x:%s-%x:unknown", self, if_name, peer); + if (strlen(self_str) > 16) + sprintf(l->name, "%x:%s-%x:unknown", self, if_name, peer); + else + sprintf(l->name, "%s:%s-%x:unknown", self_str, if_name, peer); strcpy(l->if_name, if_name); l->addr = peer; l->peer_caps = peer_caps; diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index 7e571f4f47bc..8240a85b0d0c 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -318,7 +318,6 @@ void tipc_named_process_backlog(struct net *net) { struct distr_queue_item *e, *tmp; struct tipc_net *tn = net_generic(net, tipc_net_id); - char addr[16]; unsigned long now = get_jiffies_64(); list_for_each_entry_safe(e, tmp, &tn->dist_queue, next) { @@ -326,12 +325,11 @@ void tipc_named_process_backlog(struct net *net) if (!tipc_update_nametbl(net, &e->i, e->node, e->dtype)) continue; } else { - tipc_addr_string_fill(addr, e->node); - pr_warn_ratelimited("Dropping name table update (%d) of {%u, %u, %u} from %s key=%u\n", + pr_warn_ratelimited("Dropping name table update (%d) of {%u, %u, %u} from %x key=%u\n", e->dtype, ntohl(e->i.type), ntohl(e->i.lower), ntohl(e->i.upper), - addr, ntohl(e->i.key)); + e->node, ntohl(e->i.key)); } list_del(&e->next); kfree(e); diff --git a/net/tipc/net.c b/net/tipc/net.c index 7f140a5308ee..e78674891166 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -104,27 +104,31 @@ * - A local spin_lock protecting the queue of subscriber events. */ -int tipc_net_start(struct net *net, u32 addr) +int tipc_net_init(struct net *net, u8 *node_id, u32 addr) { - struct tipc_net *tn = tipc_net(net); - char addr_string[16]; + if (tipc_own_id(net)) { + pr_info("Cannot configure node identity twice\n"); + return -1; + } + pr_info("Started in network mode\n"); - tn->own_addr = addr; + if (node_id) { + tipc_set_node_id(net, node_id); + tipc_net_finalize(net, tipc_own_addr(net)); + } + if (addr) + tipc_net_finalize(net, addr); + return 0; +} - /* Ensure that the new address is visible before we reinit. */ +void tipc_net_finalize(struct net *net, u32 addr) +{ + tipc_set_node_addr(net, addr); smp_mb(); - tipc_named_reinit(net); tipc_sk_reinit(net); - tipc_nametbl_publish(net, TIPC_CFG_SRV, addr, addr, TIPC_CLUSTER_SCOPE, 0, addr); - - pr_info("Started in network mode\n"); - pr_info("Own node address %s, cluster identity %u\n", - tipc_addr_string_fill(addr_string, addr), - tn->net_id); - return 0; } void tipc_net_stop(struct net *net) @@ -146,8 +150,10 @@ void tipc_net_stop(struct net *net) static int __tipc_nl_add_net(struct net *net, struct tipc_nl_msg *msg) { struct tipc_net *tn = net_generic(net, tipc_net_id); - void *hdr; + u64 *w0 = (u64 *)&tn->node_id[0]; + u64 *w1 = (u64 *)&tn->node_id[8]; struct nlattr *attrs; + void *hdr; hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family, NLM_F_MULTI, TIPC_NL_NET_GET); @@ -160,7 +166,10 @@ static int __tipc_nl_add_net(struct net *net, struct tipc_nl_msg *msg) if (nla_put_u32(msg->skb, TIPC_NLA_NET_ID, tn->net_id)) goto attr_msg_full; - + if (nla_put_u64_64bit(msg->skb, TIPC_NLA_NET_NODEID, *w0, 0)) + goto attr_msg_full; + if (nla_put_u64_64bit(msg->skb, TIPC_NLA_NET_NODEID_W1, *w1, 0)) + goto attr_msg_full; nla_nest_end(msg->skb, attrs); genlmsg_end(msg->skb, hdr); @@ -212,6 +221,7 @@ int __tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_NET_MAX, info->attrs[TIPC_NLA_NET], tipc_nl_net_policy, info->extack); + if (err) return err; @@ -236,9 +246,18 @@ int __tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info) if (!addr) return -EINVAL; tn->legacy_addr_format = true; - tipc_net_start(net, addr); + tipc_net_init(net, NULL, addr); } + if (attrs[TIPC_NLA_NET_NODEID]) { + u8 node_id[NODE_ID_LEN]; + u64 *w0 = (u64 *)&node_id[0]; + u64 *w1 = (u64 *)&node_id[8]; + + *w0 = nla_get_u64(attrs[TIPC_NLA_NET_NODEID]); + *w1 = nla_get_u64(attrs[TIPC_NLA_NET_NODEID_W1]); + tipc_net_init(net, node_id, 0); + } return 0; } diff --git a/net/tipc/net.h b/net/tipc/net.h index c0306aa2374b..08efa6010022 100644 --- a/net/tipc/net.h +++ b/net/tipc/net.h @@ -41,10 +41,8 @@ extern const struct nla_policy tipc_nl_net_policy[]; -int tipc_net_start(struct net *net, u32 addr); - +void tipc_net_finalize(struct net *net, u32 addr); void tipc_net_stop(struct net *net); - int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb); int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info); int __tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info); diff --git a/net/tipc/node.c b/net/tipc/node.c index 8a4b04933ecc..7b0c99347406 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -883,11 +883,9 @@ void tipc_node_delete_links(struct net *net, int bearer_id) static void tipc_node_reset_links(struct tipc_node *n) { - char addr_string[16]; int i; - pr_warn("Resetting all links to %s\n", - tipc_addr_string_fill(addr_string, n->addr)); + pr_warn("Resetting all links to %x\n", n->addr); for (i = 0; i < MAX_BEARERS; i++) { tipc_node_link_down(n, i, false); @@ -1074,15 +1072,13 @@ illegal_evt: static void node_lost_contact(struct tipc_node *n, struct sk_buff_head *inputq) { - char addr_string[16]; struct tipc_sock_conn *conn, *safe; struct tipc_link *l; struct list_head *conns = &n->conn_sks; struct sk_buff *skb; uint i; - pr_debug("Lost contact with %s\n", - tipc_addr_string_fill(addr_string, n->addr)); + pr_debug("Lost contact with %x\n", n->addr); /* Clean up broadcast state */ tipc_bcast_remove_peer(n->net, n->bc_entry.link); diff --git a/net/tipc/node.h b/net/tipc/node.h index 5fb38cf0bb5c..e06faf4fa55e 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -49,14 +49,14 @@ enum { TIPC_BCAST_STATE_NACK = (1 << 2), TIPC_BLOCK_FLOWCTL = (1 << 3), TIPC_BCAST_RCAST = (1 << 4), - TIPC_NODE_ID32 = (1 << 5) + TIPC_NODE_ID128 = (1 << 5) }; #define TIPC_NODE_CAPABILITIES (TIPC_BCAST_SYNCH | \ TIPC_BCAST_STATE_NACK | \ TIPC_BCAST_RCAST | \ TIPC_BLOCK_FLOWCTL | \ - TIPC_NODE_ID32) + TIPC_NODE_ID128) #define INVALID_BEARER_ID -1 void tipc_node_stop(struct net *net); -- cgit v1.2.3 From 30656177c4080460b936709ff6648f201d7d2c1a Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 21 Mar 2018 12:46:21 -0600 Subject: vfio/pci: Add ioeventfd support The ioeventfd here is actually irqfd handling of an ioeventfd such as supported in KVM. A user is able to pre-program a device write to occur when the eventfd triggers. This is yet another instance of eventfd-irqfd triggering between KVM and vfio. The impetus for this is high frequency writes to pages which are virtualized in QEMU. Enabling this near-direct write path for selected registers within the virtualized page can improve performance and reduce overhead. Specifically this is initially targeted at NVIDIA graphics cards where the driver issues a write to an MMIO register within a virtualized region in order to allow the MSI interrupt to re-trigger. Reviewed-by: Peter Xu Reviewed-by: Alexey Kardashevskiy Signed-off-by: Alex Williamson --- drivers/vfio/pci/vfio_pci.c | 35 ++++++++++++ drivers/vfio/pci/vfio_pci_private.h | 19 ++++++ drivers/vfio/pci/vfio_pci_rdwr.c | 111 ++++++++++++++++++++++++++++++++++++ include/uapi/linux/vfio.h | 27 +++++++++ 4 files changed, 192 insertions(+) (limited to 'include/uapi/linux') diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index b0f759476900..c6822149b394 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -305,6 +305,7 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) { struct pci_dev *pdev = vdev->pdev; struct vfio_pci_dummy_resource *dummy_res, *tmp; + struct vfio_pci_ioeventfd *ioeventfd, *ioeventfd_tmp; int i, bar; /* Stop the device from further DMA */ @@ -314,6 +315,15 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) VFIO_IRQ_SET_ACTION_TRIGGER, vdev->irq_type, 0, 0, NULL); + /* Device closed, don't need mutex here */ + list_for_each_entry_safe(ioeventfd, ioeventfd_tmp, + &vdev->ioeventfds_list, next) { + vfio_virqfd_disable(&ioeventfd->virqfd); + list_del(&ioeventfd->next); + kfree(ioeventfd); + } + vdev->ioeventfds_nr = 0; + vdev->virq_disabled = false; for (i = 0; i < vdev->num_regions; i++) @@ -1012,6 +1022,28 @@ hot_reset_release: kfree(groups); return ret; + } else if (cmd == VFIO_DEVICE_IOEVENTFD) { + struct vfio_device_ioeventfd ioeventfd; + int count; + + minsz = offsetofend(struct vfio_device_ioeventfd, fd); + + if (copy_from_user(&ioeventfd, (void __user *)arg, minsz)) + return -EFAULT; + + if (ioeventfd.argsz < minsz) + return -EINVAL; + + if (ioeventfd.flags & ~VFIO_DEVICE_IOEVENTFD_SIZE_MASK) + return -EINVAL; + + count = ioeventfd.flags & VFIO_DEVICE_IOEVENTFD_SIZE_MASK; + + if (hweight8(count) != 1 || ioeventfd.fd < -1) + return -EINVAL; + + return vfio_pci_ioeventfd(vdev, ioeventfd.offset, + ioeventfd.data, count, ioeventfd.fd); } return -ENOTTY; @@ -1174,6 +1206,8 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) vdev->irq_type = VFIO_PCI_NUM_IRQS; mutex_init(&vdev->igate); spin_lock_init(&vdev->irqlock); + mutex_init(&vdev->ioeventfds_lock); + INIT_LIST_HEAD(&vdev->ioeventfds_list); ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev); if (ret) { @@ -1215,6 +1249,7 @@ static void vfio_pci_remove(struct pci_dev *pdev) vfio_iommu_group_put(pdev->dev.iommu_group, &pdev->dev); kfree(vdev->region); + mutex_destroy(&vdev->ioeventfds_lock); kfree(vdev); if (vfio_pci_is_vga(pdev)) { diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index f561ac1c78a0..cde3b5d3441a 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h @@ -29,6 +29,19 @@ #define PCI_CAP_ID_INVALID 0xFF /* default raw access */ #define PCI_CAP_ID_INVALID_VIRT 0xFE /* default virt access */ +/* Cap maximum number of ioeventfds per device (arbitrary) */ +#define VFIO_PCI_IOEVENTFD_MAX 1000 + +struct vfio_pci_ioeventfd { + struct list_head next; + struct virqfd *virqfd; + void __iomem *addr; + uint64_t data; + loff_t pos; + int bar; + int count; +}; + struct vfio_pci_irq_ctx { struct eventfd_ctx *trigger; struct virqfd *unmask; @@ -92,9 +105,12 @@ struct vfio_pci_device { bool nointx; struct pci_saved_state *pci_saved_state; int refcnt; + int ioeventfds_nr; struct eventfd_ctx *err_trigger; struct eventfd_ctx *req_trigger; struct list_head dummy_resources_list; + struct mutex ioeventfds_lock; + struct list_head ioeventfds_list; }; #define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX) @@ -120,6 +136,9 @@ extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, extern ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, size_t count, loff_t *ppos, bool iswrite); +extern long vfio_pci_ioeventfd(struct vfio_pci_device *vdev, loff_t offset, + uint64_t data, int count, int fd); + extern int vfio_pci_init_perm_bits(void); extern void vfio_pci_uninit_perm_bits(void); diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c index 925419e0f459..a6029d0a5524 100644 --- a/drivers/vfio/pci/vfio_pci_rdwr.c +++ b/drivers/vfio/pci/vfio_pci_rdwr.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "vfio_pci_private.h" @@ -275,3 +276,113 @@ ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, return done; } + +static int vfio_pci_ioeventfd_handler(void *opaque, void *unused) +{ + struct vfio_pci_ioeventfd *ioeventfd = opaque; + + switch (ioeventfd->count) { + case 1: + vfio_iowrite8(ioeventfd->data, ioeventfd->addr); + break; + case 2: + vfio_iowrite16(ioeventfd->data, ioeventfd->addr); + break; + case 4: + vfio_iowrite32(ioeventfd->data, ioeventfd->addr); + break; +#ifdef iowrite64 + case 8: + vfio_iowrite64(ioeventfd->data, ioeventfd->addr); + break; +#endif + } + + return 0; +} + +long vfio_pci_ioeventfd(struct vfio_pci_device *vdev, loff_t offset, + uint64_t data, int count, int fd) +{ + struct pci_dev *pdev = vdev->pdev; + loff_t pos = offset & VFIO_PCI_OFFSET_MASK; + int ret, bar = VFIO_PCI_OFFSET_TO_INDEX(offset); + struct vfio_pci_ioeventfd *ioeventfd; + + /* Only support ioeventfds into BARs */ + if (bar > VFIO_PCI_BAR5_REGION_INDEX) + return -EINVAL; + + if (pos + count > pci_resource_len(pdev, bar)) + return -EINVAL; + + /* Disallow ioeventfds working around MSI-X table writes */ + if (bar == vdev->msix_bar && + !(pos + count <= vdev->msix_offset || + pos >= vdev->msix_offset + vdev->msix_size)) + return -EINVAL; + +#ifndef iowrite64 + if (count == 8) + return -EINVAL; +#endif + + ret = vfio_pci_setup_barmap(vdev, bar); + if (ret) + return ret; + + mutex_lock(&vdev->ioeventfds_lock); + + list_for_each_entry(ioeventfd, &vdev->ioeventfds_list, next) { + if (ioeventfd->pos == pos && ioeventfd->bar == bar && + ioeventfd->data == data && ioeventfd->count == count) { + if (fd == -1) { + vfio_virqfd_disable(&ioeventfd->virqfd); + list_del(&ioeventfd->next); + vdev->ioeventfds_nr--; + kfree(ioeventfd); + ret = 0; + } else + ret = -EEXIST; + + goto out_unlock; + } + } + + if (fd < 0) { + ret = -ENODEV; + goto out_unlock; + } + + if (vdev->ioeventfds_nr >= VFIO_PCI_IOEVENTFD_MAX) { + ret = -ENOSPC; + goto out_unlock; + } + + ioeventfd = kzalloc(sizeof(*ioeventfd), GFP_KERNEL); + if (!ioeventfd) { + ret = -ENOMEM; + goto out_unlock; + } + + ioeventfd->addr = vdev->barmap[bar] + pos; + ioeventfd->data = data; + ioeventfd->pos = pos; + ioeventfd->bar = bar; + ioeventfd->count = count; + + ret = vfio_virqfd_enable(ioeventfd, vfio_pci_ioeventfd_handler, + NULL, NULL, &ioeventfd->virqfd, fd); + if (ret) { + kfree(ioeventfd); + goto out_unlock; + } + + list_add(&ioeventfd->next, &vdev->ioeventfds_list); + vdev->ioeventfds_nr++; + +out_unlock: + mutex_unlock(&vdev->ioeventfds_lock); + + return ret; +} diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index c74372163ed2..1aa7b82e8169 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -575,6 +575,33 @@ struct vfio_device_gfx_plane_info { #define VFIO_DEVICE_GET_GFX_DMABUF _IO(VFIO_TYPE, VFIO_BASE + 15) +/** + * VFIO_DEVICE_IOEVENTFD - _IOW(VFIO_TYPE, VFIO_BASE + 16, + * struct vfio_device_ioeventfd) + * + * Perform a write to the device at the specified device fd offset, with + * the specified data and width when the provided eventfd is triggered. + * vfio bus drivers may not support this for all regions, for all widths, + * or at all. vfio-pci currently only enables support for BAR regions, + * excluding the MSI-X vector table. + * + * Return: 0 on success, -errno on failure. + */ +struct vfio_device_ioeventfd { + __u32 argsz; + __u32 flags; +#define VFIO_DEVICE_IOEVENTFD_8 (1 << 0) /* 1-byte write */ +#define VFIO_DEVICE_IOEVENTFD_16 (1 << 1) /* 2-byte write */ +#define VFIO_DEVICE_IOEVENTFD_32 (1 << 2) /* 4-byte write */ +#define VFIO_DEVICE_IOEVENTFD_64 (1 << 3) /* 8-byte write */ +#define VFIO_DEVICE_IOEVENTFD_SIZE_MASK (0xf) + __u64 offset; /* device fd offset of write */ + __u64 data; /* data to be written */ + __s32 fd; /* -1 for de-assignment */ +}; + +#define VFIO_DEVICE_IOEVENTFD _IO(VFIO_TYPE, VFIO_BASE + 16) + /* -------- API for Type1 VFIO IOMMU -------- */ /** -- cgit v1.2.3 From e1577c1c881b09e9f15a743a4a1907815b74d0f7 Mon Sep 17 00:00:00 2001 From: Inbar Karmy Date: Mon, 20 Nov 2017 16:14:30 +0200 Subject: ethtool: Add support for configuring PFC stall prevention in ethtool In the event where the device unexpectedly becomes unresponsive for a long period of time, flow control mechanism may propagate pause frames which will cause congestion spreading to the entire network. To prevent this scenario, when the device is stalled for a period longer than a pre-configured timeout, flow control mechanisms are automatically disabled. This patch adds support for the ETHTOOL_PFC_STALL_PREVENTION as a tunable. This API provides support for configuring flow control storm prevention timeout (msec). Signed-off-by: Inbar Karmy Cc: Michal Kubecek Cc: Andrew Lunn Signed-off-by: Saeed Mahameed --- include/uapi/linux/ethtool.h | 4 ++++ net/core/ethtool.c | 6 ++++++ 2 files changed, 10 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 20da156aaf64..4ca65b56084f 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -217,10 +217,14 @@ struct ethtool_value { __u32 data; }; +#define PFC_STORM_PREVENTION_AUTO 0xffff +#define PFC_STORM_PREVENTION_DISABLE 0 + enum tunable_id { ETHTOOL_ID_UNSPEC, ETHTOOL_RX_COPYBREAK, ETHTOOL_TX_COPYBREAK, + ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */ /* * Add your fresh new tubale attribute above and remember to update * tunable_strings[] in net/core/ethtool.c diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 157cd9efa4be..bb6e498c6e3d 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -121,6 +121,7 @@ tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = { [ETHTOOL_ID_UNSPEC] = "Unspec", [ETHTOOL_RX_COPYBREAK] = "rx-copybreak", [ETHTOOL_TX_COPYBREAK] = "tx-copybreak", + [ETHTOOL_PFC_PREVENTION_TOUT] = "pfc-prevention-tout", }; static const char @@ -2311,6 +2312,11 @@ static int ethtool_tunable_valid(const struct ethtool_tunable *tuna) tuna->type_id != ETHTOOL_TUNABLE_U32) return -EINVAL; break; + case ETHTOOL_PFC_PREVENTION_TOUT: + if (tuna->len != sizeof(u16) || + tuna->type_id != ETHTOOL_TUNABLE_U16) + return -EINVAL; + break; default: return -EINVAL; } -- cgit v1.2.3 From 9ea393d8d8377b6da8ee25c6a114ec24c0687c7c Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 28 Mar 2018 18:43:57 +0300 Subject: stm class: Add SPDX GPL-2.0 header to replace GPLv2 boilerplate This adds SPDX GPL-2.0 header to to stm core files and removes the GPLv2 boilerplate text. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/stm/console.c | 10 +--------- drivers/hwtracing/stm/core.c | 10 +--------- drivers/hwtracing/stm/dummy_stm.c | 10 +--------- drivers/hwtracing/stm/heartbeat.c | 10 +--------- drivers/hwtracing/stm/policy.c | 10 +--------- drivers/hwtracing/stm/stm.h | 10 +--------- include/linux/stm.h | 10 +--------- include/uapi/linux/stm.h | 9 --------- 8 files changed, 7 insertions(+), 72 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/hwtracing/stm/console.c b/drivers/hwtracing/stm/console.c index c9d9a8d2ff52..a00f65e21747 100644 --- a/drivers/hwtracing/stm/console.c +++ b/drivers/hwtracing/stm/console.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Simple kernel console driver for STM devices * Copyright (c) 2014, Intel Corporation. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * * STM console will send kernel messages over STM devices to a trace host. */ diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index f129869e05a9..05386b76465e 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * System Trace Module (STM) infrastructure * Copyright (c) 2014, Intel Corporation. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * * STM class implements generic infrastructure for System Trace Module devices * as defined in MIPI STPv2 specification. */ diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c index c5f94ca31c4d..63288020ea5b 100644 --- a/drivers/hwtracing/stm/dummy_stm.c +++ b/drivers/hwtracing/stm/dummy_stm.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * A dummy STM device for stm/stm_source class testing. * Copyright (c) 2014, Intel Corporation. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * * STM class implements generic infrastructure for System Trace Module devices * as defined in MIPI STPv2 specification. */ diff --git a/drivers/hwtracing/stm/heartbeat.c b/drivers/hwtracing/stm/heartbeat.c index 3da7b673aab2..7db42395e131 100644 --- a/drivers/hwtracing/stm/heartbeat.c +++ b/drivers/hwtracing/stm/heartbeat.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Simple heartbeat STM source driver * Copyright (c) 2016, Intel Corporation. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * * Heartbeat STM source will send repetitive messages over STM devices to a * trace host. */ diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 33e9a1b6ea7c..3fd07e275b34 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * System Trace Module (STM) master/channel allocation policy management * Copyright (c) 2014, Intel Corporation. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * * A master/channel allocation policy allows mapping string identifiers to * master and channel ranges, where allocation can be done. */ diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index 4e8c6926260f..923571adc6f4 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * System Trace Module (STM) infrastructure * Copyright (c) 2014, Intel Corporation. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * * STM class implements generic infrastructure for System Trace Module devices * as defined in MIPI STPv2 specification. */ diff --git a/include/linux/stm.h b/include/linux/stm.h index 210ff2292361..c6f577ab6f21 100644 --- a/include/linux/stm.h +++ b/include/linux/stm.h @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * System Trace Module (STM) infrastructure apis * Copyright (C) 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. */ #ifndef _STM_H_ diff --git a/include/uapi/linux/stm.h b/include/uapi/linux/stm.h index dbffdc23d804..29c89be72275 100644 --- a/include/uapi/linux/stm.h +++ b/include/uapi/linux/stm.h @@ -3,15 +3,6 @@ * System Trace Module (STM) userspace interfaces * Copyright (c) 2014, Intel Corporation. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * * STM class implements generic infrastructure for System Trace Module devices * as defined in MIPI STPv2 specification. */ -- cgit v1.2.3 From 4f0c7c6a12906e3571abb3c2b93eca8f727f4c9c Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 23 Feb 2018 13:19:43 +0200 Subject: stm class: Make dummy's master/channel ranges configurable To allow for more flexible testing of the stm class, make it possible to specify the ranges of masters and channels that the dummy_stm devices cover. This is done via module parameters. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/stm/dummy_stm.c | 24 +++++++++++++++++++++--- include/uapi/linux/stm.h | 4 ++++ 2 files changed, 25 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c index 63288020ea5b..38528ffdc0b3 100644 --- a/drivers/hwtracing/stm/dummy_stm.c +++ b/drivers/hwtracing/stm/dummy_stm.c @@ -12,6 +12,7 @@ #include #include #include +#include static ssize_t notrace dummy_stm_packet(struct stm_data *stm_data, unsigned int master, @@ -44,6 +45,18 @@ static unsigned int fail_mode; module_param(fail_mode, int, 0600); +static unsigned int master_min; + +module_param(master_min, int, 0400); + +static unsigned int master_max = STP_MASTER_MAX; + +module_param(master_max, int, 0400); + +static unsigned int nr_channels = STP_CHANNEL_MAX; + +module_param(nr_channels, int, 0400); + static int dummy_stm_link(struct stm_data *data, unsigned int master, unsigned int channel) { @@ -60,14 +73,19 @@ static int dummy_stm_init(void) if (nr_dummies < 0 || nr_dummies > DUMMY_STM_MAX) return -EINVAL; + if (master_min > master_max || + master_max > STP_MASTER_MAX || + nr_channels > STP_CHANNEL_MAX) + return -EINVAL; + for (i = 0; i < nr_dummies; i++) { dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i); if (!dummy_stm[i].name) goto fail_unregister; - dummy_stm[i].sw_start = 0x0000; - dummy_stm[i].sw_end = 0xffff; - dummy_stm[i].sw_nchannels = 0xffff; + dummy_stm[i].sw_start = master_min; + dummy_stm[i].sw_end = master_max; + dummy_stm[i].sw_nchannels = nr_channels; dummy_stm[i].packet = dummy_stm_packet; dummy_stm[i].link = dummy_stm_link; diff --git a/include/uapi/linux/stm.h b/include/uapi/linux/stm.h index 29c89be72275..7bac318b4440 100644 --- a/include/uapi/linux/stm.h +++ b/include/uapi/linux/stm.h @@ -12,6 +12,10 @@ #include +/* Maximum allowed master and channel values */ +#define STP_MASTER_MAX 0xffff +#define STP_CHANNEL_MAX 0xffff + /** * struct stp_policy_id - identification for the STP policy * @size: size of the structure including real id[] length -- cgit v1.2.3 From c4f6699dfcb8558d138fe838f741b2c10f416cf9 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 28 Mar 2018 12:05:37 -0700 Subject: bpf: introduce BPF_RAW_TRACEPOINT Introduce BPF_PROG_TYPE_RAW_TRACEPOINT bpf program type to access kernel internal arguments of the tracepoints in their raw form. >From bpf program point of view the access to the arguments look like: struct bpf_raw_tracepoint_args { __u64 args[0]; }; int bpf_prog(struct bpf_raw_tracepoint_args *ctx) { // program can read args[N] where N depends on tracepoint // and statically verified at program load+attach time } kprobe+bpf infrastructure allows programs access function arguments. This feature allows programs access raw tracepoint arguments. Similar to proposed 'dynamic ftrace events' there are no abi guarantees to what the tracepoints arguments are and what their meaning is. The program needs to type cast args properly and use bpf_probe_read() helper to access struct fields when argument is a pointer. For every tracepoint __bpf_trace_##call function is prepared. In assembler it looks like: (gdb) disassemble __bpf_trace_xdp_exception Dump of assembler code for function __bpf_trace_xdp_exception: 0xffffffff81132080 <+0>: mov %ecx,%ecx 0xffffffff81132082 <+2>: jmpq 0xffffffff811231f0 where TRACE_EVENT(xdp_exception, TP_PROTO(const struct net_device *dev, const struct bpf_prog *xdp, u32 act), The above assembler snippet is casting 32-bit 'act' field into 'u64' to pass into bpf_trace_run3(), while 'dev' and 'xdp' args are passed as-is. All of ~500 of __bpf_trace_*() functions are only 5-10 byte long and in total this approach adds 7k bytes to .text. This approach gives the lowest possible overhead while calling trace_xdp_exception() from kernel C code and transitioning into bpf land. Since tracepoint+bpf are used at speeds of 1M+ events per second this is valuable optimization. The new BPF_RAW_TRACEPOINT_OPEN sys_bpf command is introduced that returns anon_inode FD of 'bpf-raw-tracepoint' object. The user space looks like: // load bpf prog with BPF_PROG_TYPE_RAW_TRACEPOINT type prog_fd = bpf_prog_load(...); // receive anon_inode fd for given bpf_raw_tracepoint with prog attached raw_tp_fd = bpf_raw_tracepoint_open("xdp_exception", prog_fd); Ctrl-C of tracing daemon or cmdline tool that uses this feature will automatically detach bpf program, unload it and unregister tracepoint probe. On the kernel side the __bpf_raw_tp_map section of pointers to tracepoint definition and to __bpf_trace_*() probe function is used to find a tracepoint with "xdp_exception" name and corresponding __bpf_trace_xdp_exception() probe function which are passed to tracepoint_probe_register() to connect probe with tracepoint. Addition of bpf_raw_tracepoint doesn't interfere with ftrace and perf tracepoint mechanisms. perf_event_open() can be used in parallel on the same tracepoint. Multiple bpf_raw_tracepoint_open("xdp_exception", prog_fd) are permitted. Each with its own bpf program. The kernel will execute all tracepoint probes and all attached bpf programs. In the future bpf_raw_tracepoints can be extended with query/introspection logic. __bpf_raw_tp_map section logic was contributed by Steven Rostedt Signed-off-by: Alexei Starovoitov Signed-off-by: Steven Rostedt (VMware) Acked-by: Steven Rostedt (VMware) Signed-off-by: Daniel Borkmann --- include/asm-generic/vmlinux.lds.h | 10 +++ include/linux/bpf_types.h | 1 + include/linux/trace_events.h | 42 +++++++++ include/linux/tracepoint-defs.h | 6 ++ include/trace/bpf_probe.h | 92 +++++++++++++++++++ include/trace/define_trace.h | 1 + include/uapi/linux/bpf.h | 11 +++ kernel/bpf/syscall.c | 78 ++++++++++++++++ kernel/trace/bpf_trace.c | 183 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 424 insertions(+) create mode 100644 include/trace/bpf_probe.h (limited to 'include/uapi/linux') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 1ab0e520d6fc..8add3493a202 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -178,6 +178,15 @@ #define TRACE_SYSCALLS() #endif +#ifdef CONFIG_BPF_EVENTS +#define BPF_RAW_TP() STRUCT_ALIGN(); \ + VMLINUX_SYMBOL(__start__bpf_raw_tp) = .; \ + KEEP(*(__bpf_raw_tp_map)) \ + VMLINUX_SYMBOL(__stop__bpf_raw_tp) = .; +#else +#define BPF_RAW_TP() +#endif + #ifdef CONFIG_SERIAL_EARLYCON #define EARLYCON_TABLE() STRUCT_ALIGN(); \ VMLINUX_SYMBOL(__earlycon_table) = .; \ @@ -249,6 +258,7 @@ LIKELY_PROFILE() \ BRANCH_PROFILE() \ TRACE_PRINTKS() \ + BPF_RAW_TP() \ TRACEPOINT_STR() /* diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 5e2e8a49fb21..6d7243bfb0ff 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -19,6 +19,7 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_SK_MSG, sk_msg) BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe) BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint) BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event) +BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT, raw_tracepoint) #endif #ifdef CONFIG_CGROUP_BPF BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev) diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 8a1442c4e513..b0357cd198b0 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -468,6 +468,9 @@ unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx); int perf_event_attach_bpf_prog(struct perf_event *event, struct bpf_prog *prog); void perf_event_detach_bpf_prog(struct perf_event *event); int perf_event_query_prog_array(struct perf_event *event, void __user *info); +int bpf_probe_register(struct bpf_raw_event_map *btp, struct bpf_prog *prog); +int bpf_probe_unregister(struct bpf_raw_event_map *btp, struct bpf_prog *prog); +struct bpf_raw_event_map *bpf_find_raw_tracepoint(const char *name); #else static inline unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx) { @@ -487,6 +490,18 @@ perf_event_query_prog_array(struct perf_event *event, void __user *info) { return -EOPNOTSUPP; } +static inline int bpf_probe_register(struct bpf_raw_event_map *btp, struct bpf_prog *p) +{ + return -EOPNOTSUPP; +} +static inline int bpf_probe_unregister(struct bpf_raw_event_map *btp, struct bpf_prog *p) +{ + return -EOPNOTSUPP; +} +static inline struct bpf_raw_event_map *bpf_find_raw_tracepoint(const char *name) +{ + return NULL; +} #endif enum { @@ -546,6 +561,33 @@ extern void ftrace_profile_free_filter(struct perf_event *event); void perf_trace_buf_update(void *record, u16 type); void *perf_trace_buf_alloc(int size, struct pt_regs **regs, int *rctxp); +void bpf_trace_run1(struct bpf_prog *prog, u64 arg1); +void bpf_trace_run2(struct bpf_prog *prog, u64 arg1, u64 arg2); +void bpf_trace_run3(struct bpf_prog *prog, u64 arg1, u64 arg2, + u64 arg3); +void bpf_trace_run4(struct bpf_prog *prog, u64 arg1, u64 arg2, + u64 arg3, u64 arg4); +void bpf_trace_run5(struct bpf_prog *prog, u64 arg1, u64 arg2, + u64 arg3, u64 arg4, u64 arg5); +void bpf_trace_run6(struct bpf_prog *prog, u64 arg1, u64 arg2, + u64 arg3, u64 arg4, u64 arg5, u64 arg6); +void bpf_trace_run7(struct bpf_prog *prog, u64 arg1, u64 arg2, + u64 arg3, u64 arg4, u64 arg5, u64 arg6, u64 arg7); +void bpf_trace_run8(struct bpf_prog *prog, u64 arg1, u64 arg2, + u64 arg3, u64 arg4, u64 arg5, u64 arg6, u64 arg7, + u64 arg8); +void bpf_trace_run9(struct bpf_prog *prog, u64 arg1, u64 arg2, + u64 arg3, u64 arg4, u64 arg5, u64 arg6, u64 arg7, + u64 arg8, u64 arg9); +void bpf_trace_run10(struct bpf_prog *prog, u64 arg1, u64 arg2, + u64 arg3, u64 arg4, u64 arg5, u64 arg6, u64 arg7, + u64 arg8, u64 arg9, u64 arg10); +void bpf_trace_run11(struct bpf_prog *prog, u64 arg1, u64 arg2, + u64 arg3, u64 arg4, u64 arg5, u64 arg6, u64 arg7, + u64 arg8, u64 arg9, u64 arg10, u64 arg11); +void bpf_trace_run12(struct bpf_prog *prog, u64 arg1, u64 arg2, + u64 arg3, u64 arg4, u64 arg5, u64 arg6, u64 arg7, + u64 arg8, u64 arg9, u64 arg10, u64 arg11, u64 arg12); void perf_trace_run_bpf_submit(void *raw_data, int size, int rctx, struct trace_event_call *call, u64 count, struct pt_regs *regs, struct hlist_head *head, diff --git a/include/linux/tracepoint-defs.h b/include/linux/tracepoint-defs.h index 64ed7064f1fa..22c5a46e9693 100644 --- a/include/linux/tracepoint-defs.h +++ b/include/linux/tracepoint-defs.h @@ -35,4 +35,10 @@ struct tracepoint { struct tracepoint_func __rcu *funcs; }; +struct bpf_raw_event_map { + struct tracepoint *tp; + void *bpf_func; + u32 num_args; +} __aligned(32); + #endif diff --git a/include/trace/bpf_probe.h b/include/trace/bpf_probe.h new file mode 100644 index 000000000000..505dae0bed80 --- /dev/null +++ b/include/trace/bpf_probe.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#undef TRACE_SYSTEM_VAR + +#ifdef CONFIG_BPF_EVENTS + +#undef __entry +#define __entry entry + +#undef __get_dynamic_array +#define __get_dynamic_array(field) \ + ((void *)__entry + (__entry->__data_loc_##field & 0xffff)) + +#undef __get_dynamic_array_len +#define __get_dynamic_array_len(field) \ + ((__entry->__data_loc_##field >> 16) & 0xffff) + +#undef __get_str +#define __get_str(field) ((char *)__get_dynamic_array(field)) + +#undef __get_bitmask +#define __get_bitmask(field) (char *)__get_dynamic_array(field) + +#undef __perf_count +#define __perf_count(c) (c) + +#undef __perf_task +#define __perf_task(t) (t) + +/* cast any integer, pointer, or small struct to u64 */ +#define UINTTYPE(size) \ + __typeof__(__builtin_choose_expr(size == 1, (u8)1, \ + __builtin_choose_expr(size == 2, (u16)2, \ + __builtin_choose_expr(size == 4, (u32)3, \ + __builtin_choose_expr(size == 8, (u64)4, \ + (void)5))))) +#define __CAST_TO_U64(x) ({ \ + typeof(x) __src = (x); \ + UINTTYPE(sizeof(x)) __dst; \ + memcpy(&__dst, &__src, sizeof(__dst)); \ + (u64)__dst; }) + +#define __CAST1(a,...) __CAST_TO_U64(a) +#define __CAST2(a,...) __CAST_TO_U64(a), __CAST1(__VA_ARGS__) +#define __CAST3(a,...) __CAST_TO_U64(a), __CAST2(__VA_ARGS__) +#define __CAST4(a,...) __CAST_TO_U64(a), __CAST3(__VA_ARGS__) +#define __CAST5(a,...) __CAST_TO_U64(a), __CAST4(__VA_ARGS__) +#define __CAST6(a,...) __CAST_TO_U64(a), __CAST5(__VA_ARGS__) +#define __CAST7(a,...) __CAST_TO_U64(a), __CAST6(__VA_ARGS__) +#define __CAST8(a,...) __CAST_TO_U64(a), __CAST7(__VA_ARGS__) +#define __CAST9(a,...) __CAST_TO_U64(a), __CAST8(__VA_ARGS__) +#define __CAST10(a,...) __CAST_TO_U64(a), __CAST9(__VA_ARGS__) +#define __CAST11(a,...) __CAST_TO_U64(a), __CAST10(__VA_ARGS__) +#define __CAST12(a,...) __CAST_TO_U64(a), __CAST11(__VA_ARGS__) +/* tracepoints with more than 12 arguments will hit build error */ +#define CAST_TO_U64(...) CONCATENATE(__CAST, COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__) + +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ +static notrace void \ +__bpf_trace_##call(void *__data, proto) \ +{ \ + struct bpf_prog *prog = __data; \ + CONCATENATE(bpf_trace_run, COUNT_ARGS(args))(prog, CAST_TO_U64(args)); \ +} + +/* + * This part is compiled out, it is only here as a build time check + * to make sure that if the tracepoint handling changes, the + * bpf probe will fail to compile unless it too is updated. + */ +#undef DEFINE_EVENT +#define DEFINE_EVENT(template, call, proto, args) \ +static inline void bpf_test_probe_##call(void) \ +{ \ + check_trace_callback_type_##call(__bpf_trace_##template); \ +} \ +static struct bpf_raw_event_map __used \ + __attribute__((section("__bpf_raw_tp_map"))) \ +__bpf_trace_tp_map_##call = { \ + .tp = &__tracepoint_##call, \ + .bpf_func = (void *)__bpf_trace_##template, \ + .num_args = COUNT_ARGS(args), \ +}; + + +#undef DEFINE_EVENT_PRINT +#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \ + DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args)) + +#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) +#endif /* CONFIG_BPF_EVENTS */ diff --git a/include/trace/define_trace.h b/include/trace/define_trace.h index d9e3d4aa3f6e..cb30c5532144 100644 --- a/include/trace/define_trace.h +++ b/include/trace/define_trace.h @@ -95,6 +95,7 @@ #ifdef TRACEPOINTS_ENABLED #include #include +#include #endif #undef TRACE_EVENT diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 18b7c510c511..1878201c2d77 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -94,6 +94,7 @@ enum bpf_cmd { BPF_MAP_GET_FD_BY_ID, BPF_OBJ_GET_INFO_BY_FD, BPF_PROG_QUERY, + BPF_RAW_TRACEPOINT_OPEN, }; enum bpf_map_type { @@ -134,6 +135,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_SK_SKB, BPF_PROG_TYPE_CGROUP_DEVICE, BPF_PROG_TYPE_SK_MSG, + BPF_PROG_TYPE_RAW_TRACEPOINT, }; enum bpf_attach_type { @@ -344,6 +346,11 @@ union bpf_attr { __aligned_u64 prog_ids; __u32 prog_cnt; } query; + + struct { + __u64 name; + __u32 prog_fd; + } raw_tracepoint; } __attribute__((aligned(8))); /* BPF helper function descriptions: @@ -1152,4 +1159,8 @@ struct bpf_cgroup_dev_ctx { __u32 minor; }; +struct bpf_raw_tracepoint_args { + __u64 args[0]; +}; + #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 77d45bd9f507..95ca2523fa6e 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1315,6 +1315,81 @@ static int bpf_obj_get(const union bpf_attr *attr) attr->file_flags); } +struct bpf_raw_tracepoint { + struct bpf_raw_event_map *btp; + struct bpf_prog *prog; +}; + +static int bpf_raw_tracepoint_release(struct inode *inode, struct file *filp) +{ + struct bpf_raw_tracepoint *raw_tp = filp->private_data; + + if (raw_tp->prog) { + bpf_probe_unregister(raw_tp->btp, raw_tp->prog); + bpf_prog_put(raw_tp->prog); + } + kfree(raw_tp); + return 0; +} + +static const struct file_operations bpf_raw_tp_fops = { + .release = bpf_raw_tracepoint_release, + .read = bpf_dummy_read, + .write = bpf_dummy_write, +}; + +#define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd + +static int bpf_raw_tracepoint_open(const union bpf_attr *attr) +{ + struct bpf_raw_tracepoint *raw_tp; + struct bpf_raw_event_map *btp; + struct bpf_prog *prog; + char tp_name[128]; + int tp_fd, err; + + if (strncpy_from_user(tp_name, u64_to_user_ptr(attr->raw_tracepoint.name), + sizeof(tp_name) - 1) < 0) + return -EFAULT; + tp_name[sizeof(tp_name) - 1] = 0; + + btp = bpf_find_raw_tracepoint(tp_name); + if (!btp) + return -ENOENT; + + raw_tp = kzalloc(sizeof(*raw_tp), GFP_USER); + if (!raw_tp) + return -ENOMEM; + raw_tp->btp = btp; + + prog = bpf_prog_get_type(attr->raw_tracepoint.prog_fd, + BPF_PROG_TYPE_RAW_TRACEPOINT); + if (IS_ERR(prog)) { + err = PTR_ERR(prog); + goto out_free_tp; + } + + err = bpf_probe_register(raw_tp->btp, prog); + if (err) + goto out_put_prog; + + raw_tp->prog = prog; + tp_fd = anon_inode_getfd("bpf-raw-tracepoint", &bpf_raw_tp_fops, raw_tp, + O_CLOEXEC); + if (tp_fd < 0) { + bpf_probe_unregister(raw_tp->btp, prog); + err = tp_fd; + goto out_put_prog; + } + return tp_fd; + +out_put_prog: + bpf_prog_put(prog); +out_free_tp: + kfree(raw_tp); + return err; +} + #ifdef CONFIG_CGROUP_BPF #define BPF_PROG_ATTACH_LAST_FIELD attach_flags @@ -1925,6 +2000,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz case BPF_OBJ_GET_INFO_BY_FD: err = bpf_obj_get_info_by_fd(&attr, uattr); break; + case BPF_RAW_TRACEPOINT_OPEN: + err = bpf_raw_tracepoint_open(&attr); + break; default: err = -EINVAL; break; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 7f9691c86b6e..463e72d18c4c 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -735,6 +735,86 @@ static const struct bpf_func_proto *pe_prog_func_proto(enum bpf_func_id func_id) } } +/* + * bpf_raw_tp_regs are separate from bpf_pt_regs used from skb/xdp + * to avoid potential recursive reuse issue when/if tracepoints are added + * inside bpf_*_event_output and/or bpf_get_stack_id + */ +static DEFINE_PER_CPU(struct pt_regs, bpf_raw_tp_regs); +BPF_CALL_5(bpf_perf_event_output_raw_tp, struct bpf_raw_tracepoint_args *, args, + struct bpf_map *, map, u64, flags, void *, data, u64, size) +{ + struct pt_regs *regs = this_cpu_ptr(&bpf_raw_tp_regs); + + perf_fetch_caller_regs(regs); + return ____bpf_perf_event_output(regs, map, flags, data, size); +} + +static const struct bpf_func_proto bpf_perf_event_output_proto_raw_tp = { + .func = bpf_perf_event_output_raw_tp, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_PTR_TO_MEM, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, +}; + +BPF_CALL_3(bpf_get_stackid_raw_tp, struct bpf_raw_tracepoint_args *, args, + struct bpf_map *, map, u64, flags) +{ + struct pt_regs *regs = this_cpu_ptr(&bpf_raw_tp_regs); + + perf_fetch_caller_regs(regs); + /* similar to bpf_perf_event_output_tp, but pt_regs fetched differently */ + return bpf_get_stackid((unsigned long) regs, (unsigned long) map, + flags, 0, 0); +} + +static const struct bpf_func_proto bpf_get_stackid_proto_raw_tp = { + .func = bpf_get_stackid_raw_tp, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, +}; + +static const struct bpf_func_proto *raw_tp_prog_func_proto(enum bpf_func_id func_id) +{ + switch (func_id) { + case BPF_FUNC_perf_event_output: + return &bpf_perf_event_output_proto_raw_tp; + case BPF_FUNC_get_stackid: + return &bpf_get_stackid_proto_raw_tp; + default: + return tracing_func_proto(func_id); + } +} + +static bool raw_tp_prog_is_valid_access(int off, int size, + enum bpf_access_type type, + struct bpf_insn_access_aux *info) +{ + /* largest tracepoint in the kernel has 12 args */ + if (off < 0 || off >= sizeof(__u64) * 12) + return false; + if (type != BPF_READ) + return false; + if (off % size != 0) + return false; + return true; +} + +const struct bpf_verifier_ops raw_tracepoint_verifier_ops = { + .get_func_proto = raw_tp_prog_func_proto, + .is_valid_access = raw_tp_prog_is_valid_access, +}; + +const struct bpf_prog_ops raw_tracepoint_prog_ops = { +}; + static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type, struct bpf_insn_access_aux *info) { @@ -908,3 +988,106 @@ int perf_event_query_prog_array(struct perf_event *event, void __user *info) return ret; } + +extern struct bpf_raw_event_map __start__bpf_raw_tp[]; +extern struct bpf_raw_event_map __stop__bpf_raw_tp[]; + +struct bpf_raw_event_map *bpf_find_raw_tracepoint(const char *name) +{ + struct bpf_raw_event_map *btp = __start__bpf_raw_tp; + + for (; btp < __stop__bpf_raw_tp; btp++) { + if (!strcmp(btp->tp->name, name)) + return btp; + } + return NULL; +} + +static __always_inline +void __bpf_trace_run(struct bpf_prog *prog, u64 *args) +{ + rcu_read_lock(); + preempt_disable(); + (void) BPF_PROG_RUN(prog, args); + preempt_enable(); + rcu_read_unlock(); +} + +#define UNPACK(...) __VA_ARGS__ +#define REPEAT_1(FN, DL, X, ...) FN(X) +#define REPEAT_2(FN, DL, X, ...) FN(X) UNPACK DL REPEAT_1(FN, DL, __VA_ARGS__) +#define REPEAT_3(FN, DL, X, ...) FN(X) UNPACK DL REPEAT_2(FN, DL, __VA_ARGS__) +#define REPEAT_4(FN, DL, X, ...) FN(X) UNPACK DL REPEAT_3(FN, DL, __VA_ARGS__) +#define REPEAT_5(FN, DL, X, ...) FN(X) UNPACK DL REPEAT_4(FN, DL, __VA_ARGS__) +#define REPEAT_6(FN, DL, X, ...) FN(X) UNPACK DL REPEAT_5(FN, DL, __VA_ARGS__) +#define REPEAT_7(FN, DL, X, ...) FN(X) UNPACK DL REPEAT_6(FN, DL, __VA_ARGS__) +#define REPEAT_8(FN, DL, X, ...) FN(X) UNPACK DL REPEAT_7(FN, DL, __VA_ARGS__) +#define REPEAT_9(FN, DL, X, ...) FN(X) UNPACK DL REPEAT_8(FN, DL, __VA_ARGS__) +#define REPEAT_10(FN, DL, X, ...) FN(X) UNPACK DL REPEAT_9(FN, DL, __VA_ARGS__) +#define REPEAT_11(FN, DL, X, ...) FN(X) UNPACK DL REPEAT_10(FN, DL, __VA_ARGS__) +#define REPEAT_12(FN, DL, X, ...) FN(X) UNPACK DL REPEAT_11(FN, DL, __VA_ARGS__) +#define REPEAT(X, FN, DL, ...) REPEAT_##X(FN, DL, __VA_ARGS__) + +#define SARG(X) u64 arg##X +#define COPY(X) args[X] = arg##X + +#define __DL_COM (,) +#define __DL_SEM (;) + +#define __SEQ_0_11 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + +#define BPF_TRACE_DEFN_x(x) \ + void bpf_trace_run##x(struct bpf_prog *prog, \ + REPEAT(x, SARG, __DL_COM, __SEQ_0_11)) \ + { \ + u64 args[x]; \ + REPEAT(x, COPY, __DL_SEM, __SEQ_0_11); \ + __bpf_trace_run(prog, args); \ + } \ + EXPORT_SYMBOL_GPL(bpf_trace_run##x) +BPF_TRACE_DEFN_x(1); +BPF_TRACE_DEFN_x(2); +BPF_TRACE_DEFN_x(3); +BPF_TRACE_DEFN_x(4); +BPF_TRACE_DEFN_x(5); +BPF_TRACE_DEFN_x(6); +BPF_TRACE_DEFN_x(7); +BPF_TRACE_DEFN_x(8); +BPF_TRACE_DEFN_x(9); +BPF_TRACE_DEFN_x(10); +BPF_TRACE_DEFN_x(11); +BPF_TRACE_DEFN_x(12); + +static int __bpf_probe_register(struct bpf_raw_event_map *btp, struct bpf_prog *prog) +{ + struct tracepoint *tp = btp->tp; + + /* + * check that program doesn't access arguments beyond what's + * available in this tracepoint + */ + if (prog->aux->max_ctx_offset > btp->num_args * sizeof(u64)) + return -EINVAL; + + return tracepoint_probe_register(tp, (void *)btp->bpf_func, prog); +} + +int bpf_probe_register(struct bpf_raw_event_map *btp, struct bpf_prog *prog) +{ + int err; + + mutex_lock(&bpf_event_mutex); + err = __bpf_probe_register(btp, prog); + mutex_unlock(&bpf_event_mutex); + return err; +} + +int bpf_probe_unregister(struct bpf_raw_event_map *btp, struct bpf_prog *prog) +{ + int err; + + mutex_lock(&bpf_event_mutex); + err = tracepoint_probe_unregister(btp->tp, (void *)btp->bpf_func, prog); + mutex_unlock(&bpf_event_mutex); + return err; +} -- cgit v1.2.3 From f8d16d3edb4dbae080df04318423c360de3c594d Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Mon, 26 Mar 2018 12:52:45 -0500 Subject: nl80211: Add SOCKET_OWNER support to JOIN_IBSS Signed-off-by: Denis Kenzior [johannes: fix race with wdev lock/unlock by just acquiring once] Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 2 ++ net/wireless/core.h | 8 ++++---- net/wireless/ibss.c | 27 ++++++--------------------- net/wireless/nl80211.c | 7 ++++++- 4 files changed, 18 insertions(+), 26 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 60fefc5a2ea4..266b43c4f6e1 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1962,6 +1962,8 @@ enum nl80211_commands { * multicast group. * If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the * station will deauthenticate when the socket is closed. + * If set during %NL80211_CMD_JOIN_IBSS the IBSS will be automatically + * torn down when the socket is closed. * * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is * the TDLS link initiator. diff --git a/net/wireless/core.h b/net/wireless/core.h index eaff636169c2..b5cf3ea8d4df 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -282,10 +282,10 @@ void cfg80211_bss_age(struct cfg80211_registered_device *rdev, unsigned long age_secs); /* IBSS */ -int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params, - struct cfg80211_cached_keys *connkeys); +int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys); void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index a1d10993d08a..d1743e6abc34 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -84,14 +84,15 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, } EXPORT_SYMBOL(cfg80211_ibss_joined); -static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params, - struct cfg80211_cached_keys *connkeys) +int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + ASSERT_RTNL(); ASSERT_WDEV_LOCK(wdev); if (wdev->ssid_len) @@ -146,23 +147,6 @@ static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, return 0; } -int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params, - struct cfg80211_cached_keys *connkeys) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - ASSERT_RTNL(); - - wdev_lock(wdev); - err = __cfg80211_join_ibss(rdev, dev, params, connkeys); - wdev_unlock(wdev); - - return err; -} - static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -224,6 +208,7 @@ int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, if (err) return err; + wdev->conn_owner_nlportid = 0; __cfg80211_clear_ibss(dev, nowext); return 0; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index fe27ab443d01..13f7c002f562 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8679,9 +8679,14 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ibss.userspace_handles_dfs = nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]); - err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_join_ibss(rdev, dev, &ibss, connkeys); if (err) kzfree(connkeys); + else if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) + dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; + wdev_unlock(dev->ieee80211_ptr); + return err; } -- cgit v1.2.3 From 188c1b3c04d69e842122daf201f07a34fcfad039 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Mon, 26 Mar 2018 12:52:46 -0500 Subject: nl80211: Add SOCKET_OWNER support to JOIN_MESH Signed-off-by: Denis Kenzior [johannes: fix race with wdev lock/unlock by just acquiring once] Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 2 ++ net/wireless/core.h | 4 ---- net/wireless/mesh.c | 16 +--------------- net/wireless/nl80211.c | 10 ++++++++-- 4 files changed, 11 insertions(+), 21 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 266b43c4f6e1..98eb37def37d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1964,6 +1964,8 @@ enum nl80211_commands { * station will deauthenticate when the socket is closed. * If set during %NL80211_CMD_JOIN_IBSS the IBSS will be automatically * torn down when the socket is closed. + * If set during %NL80211_CMD_JOIN_MESH the mesh setup will be + * automatically torn down when the socket is closed. * * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is * the TDLS link initiator. diff --git a/net/wireless/core.h b/net/wireless/core.h index b5cf3ea8d4df..63eb1b5fdd04 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -303,10 +303,6 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, struct mesh_setup *setup, const struct mesh_config *conf); -int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct mesh_setup *setup, - const struct mesh_config *conf); int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev); int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index b12da6ef3c12..eac5aa1419fc 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -217,21 +217,6 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, return err; } -int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct mesh_setup *setup, - const struct mesh_config *conf) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_join_mesh(rdev, dev, setup, conf); - wdev_unlock(wdev); - - return err; -} - int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct cfg80211_chan_def *chandef) @@ -286,6 +271,7 @@ int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, err = rdev_leave_mesh(rdev, dev); if (!err) { + wdev->conn_owner_nlportid = 0; wdev->mesh_id_len = 0; wdev->beacon_interval = 0; memset(&wdev->chandef, 0, sizeof(wdev->chandef)); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 13f7c002f562..1d6e81e5b2c8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10092,7 +10092,7 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) if (err) return err; } else { - /* cfg80211_join_mesh() will sort it out */ + /* __cfg80211_join_mesh() will sort it out */ setup.chandef.chan = NULL; } @@ -10130,7 +10130,13 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) setup.userspace_handles_dfs = nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]); - return cfg80211_join_mesh(rdev, dev, &setup, &cfg); + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_join_mesh(rdev, dev, &setup, &cfg); + if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) + dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; + wdev_unlock(dev->ieee80211_ptr); + + return err; } static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) -- cgit v1.2.3 From 466a306142c002b40deaa58da94741af4153d1c4 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Mon, 26 Mar 2018 12:52:47 -0500 Subject: nl80211: Add SOCKET_OWNER support to START_AP Signed-off-by: Denis Kenzior Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 2 ++ net/wireless/ap.c | 1 + net/wireless/nl80211.c | 3 +++ 3 files changed, 6 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 98eb37def37d..9ea3d6039eca 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1966,6 +1966,8 @@ enum nl80211_commands { * torn down when the socket is closed. * If set during %NL80211_CMD_JOIN_MESH the mesh setup will be * automatically torn down when the socket is closed. + * If set during %NL80211_CMD_START_AP the AP will be automatically + * disabled when the socket is closed. * * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is * the TDLS link initiator. diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 63682176c96c..882d97bdc6bf 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -27,6 +27,7 @@ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, err = rdev_stop_ap(rdev, dev); if (!err) { + wdev->conn_owner_nlportid = 0; wdev->beacon_interval = 0; memset(&wdev->chandef, 0, sizeof(wdev->chandef)); wdev->ssid_len = 0; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1d6e81e5b2c8..cfaf2aeb9783 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4134,6 +4134,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) wdev->chandef = params.chandef; wdev->ssid_len = params.ssid_len; memcpy(wdev->ssid, params.ssid, wdev->ssid_len); + + if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) + wdev->conn_owner_nlportid = info->snd_portid; } wdev_unlock(wdev); -- cgit v1.2.3 From 6a671a50f8199b3e1fe49fa8afff0fc8335da79c Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Mon, 26 Mar 2018 12:52:41 -0500 Subject: nl80211: Add CMD_CONTROL_PORT_FRAME API This commit also adds cfg80211_rx_control_port function. This is used to generate a CMD_CONTROL_PORT_FRAME event out to userspace. The conn_owner_nlportid is used as the unicast destination. This means that userspace must specify NL80211_ATTR_SOCKET_OWNER flag if control port over nl80211 routing is requested in NL80211_CMD_CONNECT, NL80211_CMD_ASSOCIATE, NL80211_CMD_START_AP or IBSS/mesh join. Signed-off-by: Denis Kenzior [johannes: fix return value of cfg80211_rx_control_port()] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 22 +++++++++++++++++ include/uapi/linux/nl80211.h | 13 ++++++++++ net/wireless/nl80211.c | 58 ++++++++++++++++++++++++++++++++++++++++++++ net/wireless/trace.h | 21 ++++++++++++++++ 4 files changed, 114 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bfe174896fcf..df145f76adad 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5721,6 +5721,28 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp); +/** + * cfg80211_rx_control_port - notification about a received control port frame + * @dev: The device the frame matched to + * @buf: control port frame + * @len: length of the frame data + * @addr: The peer from which the frame was received + * @proto: frame protocol, typically PAE or Pre-authentication + * @unencrypted: Whether the frame was received unencrypted + * + * This function is used to inform userspace about a received control port + * frame. It should only be used if userspace indicated it wants to receive + * control port frames over nl80211. + * + * The frame is the data portion of the 802.3 or 802.11 data frame with all + * network layer headers removed (e.g. the raw EAPoL frame). + * + * Return: %true if the frame was passed to userspace + */ +bool cfg80211_rx_control_port(struct net_device *dev, + const u8 *buf, size_t len, + const u8 *addr, u16 proto, bool unencrypted); + /** * cfg80211_cqm_rssi_notify - connection quality monitoring rssi event * @dev: network device diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 9ea3d6039eca..6a3cc7a635b5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -990,6 +990,17 @@ * &NL80211_CMD_CONNECT or &NL80211_CMD_ROAM. If the 4 way handshake failed * &NL80211_CMD_DISCONNECT should be indicated instead. * + * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request + * and RX notification. This command is used both as a request to transmit + * a control port frame and as a notification that a control port frame + * has been received. %NL80211_ATTR_FRAME is used to specify the + * frame contents. The frame is the raw EAPoL data, without ethernet or + * 802.11 headers. + * When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added + * indicating the protocol type of the received frame; whether the frame + * was received unencrypted and the MAC address of the peer respectively. + * * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded. * * @NL80211_CMD_EXTERNAL_AUTH: This interface is exclusively defined for host @@ -1228,6 +1239,8 @@ enum nl80211_commands { NL80211_CMD_STA_OPMODE_CHANGED, + NL80211_CMD_CONTROL_PORT_FRAME, + /* 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 cfaf2aeb9783..0870447fbd55 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14553,6 +14553,64 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie, } EXPORT_SYMBOL(cfg80211_mgmt_tx_status); +static int __nl80211_rx_control_port(struct net_device *dev, + const u8 *buf, size_t len, + const u8 *addr, u16 proto, + bool unencrypted, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + u32 nlportid = READ_ONCE(wdev->conn_owner_nlportid); + + if (!nlportid) + return -ENOENT; + + msg = nlmsg_new(100 + len, gfp); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONTROL_PORT_FRAME); + if (!hdr) { + nlmsg_free(msg); + return -ENOBUFS; + } + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || + nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), + NL80211_ATTR_PAD) || + nla_put(msg, NL80211_ATTR_FRAME, len, buf) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, proto) || + (unencrypted && nla_put_flag(msg, + NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); + + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + +bool cfg80211_rx_control_port(struct net_device *dev, + const u8 *buf, size_t len, + const u8 *addr, u16 proto, bool unencrypted) +{ + int ret; + + trace_cfg80211_rx_control_port(dev, buf, len, addr, proto, unencrypted); + ret = __nl80211_rx_control_port(dev, buf, len, addr, proto, + unencrypted, GFP_ATOMIC); + trace_cfg80211_return_bool(ret == 0); + return ret == 0; +} +EXPORT_SYMBOL(cfg80211_rx_control_port); + static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev, const char *mac, gfp_t gfp) { diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 5152938b358d..42fd338f879e 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2600,6 +2600,27 @@ TRACE_EVENT(cfg80211_mgmt_tx_status, WDEV_PR_ARG, __entry->cookie, BOOL_TO_STR(__entry->ack)) ); +TRACE_EVENT(cfg80211_rx_control_port, + TP_PROTO(struct net_device *netdev, const u8 *buf, size_t len, + const u8 *addr, u16 proto, bool unencrypted), + TP_ARGS(netdev, buf, len, addr, proto, unencrypted), + TP_STRUCT__entry( + NETDEV_ENTRY + MAC_ENTRY(addr) + __field(u16, proto) + __field(bool, unencrypted) + ), + TP_fast_assign( + NETDEV_ASSIGN; + MAC_ASSIGN(addr, addr); + __entry->proto = proto; + __entry->unencrypted = unencrypted; + ), + TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT " proto: 0x%x, unencrypted: %s", + NETDEV_PR_ARG, MAC_PR_ARG(addr), + __entry->proto, BOOL_TO_STR(__entry->unencrypted)) +); + TRACE_EVENT(cfg80211_cqm_rssi_notify, TP_PROTO(struct net_device *netdev, enum nl80211_cqm_rssi_threshold_event rssi_event, -- cgit v1.2.3 From 2576a9ace47eba28a682d249d1d6402f891808c9 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Mon, 26 Mar 2018 12:52:42 -0500 Subject: nl80211: Implement TX of control port frames This commit implements the TX side of NL80211_CMD_CONTROL_PORT_FRAME. Userspace provides the raw EAPoL frame using NL80211_ATTR_FRAME. Userspace should also provide the destination address and the protocol type to use when sending the frame. This is used to implement TX of Pre-authentication frames. If CONTROL_PORT_ETHERTYPE_NO_ENCRYPT is specified, then the driver will be asked not to encrypt the outgoing frame. A new EXT_FEATURE flag is introduced so that nl80211 code can check whether a given wiphy has capability to pass EAPoL frames over nl80211. Signed-off-by: Denis Kenzior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 9 ++++++ include/uapi/linux/nl80211.h | 3 ++ net/wireless/nl80211.c | 71 +++++++++++++++++++++++++++++++++++++++++++- net/wireless/rdev-ops.h | 15 ++++++++++ net/wireless/trace.h | 26 ++++++++++++++++ 5 files changed, 123 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index df145f76adad..de2894a4ad10 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2961,6 +2961,9 @@ struct cfg80211_external_auth_params { * * @external_auth: indicates result of offloaded authentication processing from * user space + * + * @tx_control_port: TX a control port frame (EAPoL). The noencrypt parameter + * tells the driver that the frame should not be encrypted. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -3256,6 +3259,12 @@ struct cfg80211_ops { const u8 *aa); int (*external_auth)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_external_auth_params *params); + + int (*tx_control_port)(struct wiphy *wiphy, + struct net_device *dev, + const u8 *buf, size_t len, + const u8 *dest, const __be16 proto, + const bool noencrypt); }; /* diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 6a3cc7a635b5..3167d6f7fc68 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -5024,6 +5024,8 @@ enum nl80211_feature_flags { * channel change triggered by radar detection event. * No need to start CAC from user-space, no need to react to * "radar detected" event. + * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and + * receiving control port frames over nl80211 instead of the netdevice. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -5055,6 +5057,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_LOW_POWER_SCAN, NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN, NL80211_EXT_FEATURE_DFS_OFFLOAD, + NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0870447fbd55..6eb286784924 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -12535,6 +12535,68 @@ static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info) return rdev_external_auth(rdev, dev, ¶ms); } +static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + const u8 *buf; + size_t len; + u8 *dest; + u16 proto; + bool noencrypt; + int err; + + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211)) + return -EOPNOTSUPP; + + if (!rdev->ops->tx_control_port) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_FRAME] || + !info->attrs[NL80211_ATTR_MAC] || + !info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) { + GENL_SET_ERR_MSG(info, "Frame, MAC or ethertype missing"); + return -EINVAL; + } + + wdev_lock(wdev); + + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_MESH_POINT: + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + if (wdev->current_bss) + break; + err = -ENOTCONN; + goto out; + default: + err = -EOPNOTSUPP; + goto out; + } + + wdev_unlock(wdev); + + buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); + len = nla_len(info->attrs[NL80211_ATTR_FRAME]); + dest = nla_data(info->attrs[NL80211_ATTR_MAC]); + proto = nla_get_u16(info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]); + noencrypt = + nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]); + + return rdev_tx_control_port(rdev, dev, buf, len, + dest, cpu_to_be16(proto), noencrypt); + + out: + wdev_unlock(wdev); + return err; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -13438,7 +13500,14 @@ static const struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, - + { + .cmd = NL80211_CMD_CONTROL_PORT_FRAME, + .doit = nl80211_tx_control_port, + .policy = nl80211_policy, + .flags = GENL_UNS_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_family nl80211_fam __ro_after_init = { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 84f23ae015fc..87479a53411b 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -714,6 +714,21 @@ static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev, return ret; } +static inline int rdev_tx_control_port(struct cfg80211_registered_device *rdev, + struct net_device *dev, + const void *buf, size_t len, + const u8 *dest, __be16 proto, + const bool noencrypt) +{ + int ret; + trace_rdev_tx_control_port(&rdev->wiphy, dev, buf, len, + dest, proto, noencrypt); + ret = rdev->ops->tx_control_port(&rdev->wiphy, dev, buf, len, + dest, proto, noencrypt); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_mgmt_tx_cancel_wait(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 42fd338f879e..a64291ae52a6 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1882,6 +1882,32 @@ TRACE_EVENT(rdev_mgmt_tx, BOOL_TO_STR(__entry->dont_wait_for_ack)) ); +TRACE_EVENT(rdev_tx_control_port, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const u8 *buf, size_t len, const u8 *dest, __be16 proto, + bool unencrypted), + TP_ARGS(wiphy, netdev, buf, len, dest, proto, unencrypted), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(dest) + __field(__be16, proto) + __field(bool, unencrypted) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(dest, dest); + __entry->proto = proto; + __entry->unencrypted = unencrypted; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT "," + " proto: 0x%x, unencrypted: %s", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dest), + be16_to_cpu(__entry->proto), + BOOL_TO_STR(__entry->unencrypted)) +); + TRACE_EVENT(rdev_set_noack_map, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u16 noack_map), -- cgit v1.2.3 From 64bf3d4bc2b0725b3c5ffadd982a9746bfc738b7 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Mon, 26 Mar 2018 12:52:43 -0500 Subject: nl80211: Add CONTROL_PORT_OVER_NL80211 attribute Signed-off-by: Denis Kenzior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 +++ include/uapi/linux/nl80211.h | 14 +++++++++++++- net/wireless/nl80211.c | 26 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index de2894a4ad10..0bd957b37208 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -647,6 +647,8 @@ struct survey_info { * allowed through even on unauthorized ports * @control_port_no_encrypt: TRUE to prevent encryption of control port * protocol frames. + * @control_port_over_nl80211: TRUE if userspace expects to exchange control + * port frames over NL80211 instead of the network interface. * @wep_keys: static WEP keys, if not NULL points to an array of * CFG80211_MAX_WEP_KEYS WEP keys * @wep_tx_key: key index (0..3) of the default TX static WEP key @@ -662,6 +664,7 @@ struct cfg80211_crypto_settings { bool control_port; __be16 control_port_ethertype; bool control_port_no_encrypt; + bool control_port_over_nl80211; struct key_params *wep_keys; int wep_tx_key; const u8 *psk; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 3167d6f7fc68..15daf5e2638d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -542,7 +542,8 @@ * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP, * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, - * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, + * %NL80211_ATTR_CONTROL_PORT_OVER_NL80211, %NL80211_ATTR_MAC_HINT, and * %NL80211_ATTR_WIPHY_FREQ_HINT. * If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are * restrictions on BSS selection, i.e., they effectively prevent roaming @@ -1488,6 +1489,15 @@ enum nl80211_commands { * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom * ethertype frames used for key negotiation must not be encrypted. + * @NL80211_ATTR_CONTROL_PORT_OVER_NL80211: A flag indicating whether control + * port frames (e.g. of type given in %NL80211_ATTR_CONTROL_PORT_ETHERTYPE) + * will be sent directly to the network interface or sent via the NL80211 + * socket. If this attribute is missing, then legacy behavior of sending + * control port frames directly to the network interface is used. If the + * flag is included, then control port frames are sent over NL80211 instead + * using %CMD_CONTROL_PORT_FRAME. If control port routing over NL80211 is + * to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER + * flag. * * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. * We recommend using nested, driver-specific attributes within this. @@ -2647,6 +2657,8 @@ enum nl80211_attrs { NL80211_ATTR_NSS, NL80211_ATTR_ACK_SIGNAL, + NL80211_ATTR_CONTROL_PORT_OVER_NL80211, + /* 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 6eb286784924..d3b14d9d002a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -287,6 +287,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 }, [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG }, + [NL80211_ATTR_CONTROL_PORT_OVER_NL80211] = { .type = NLA_FLAG }, [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, @@ -8211,6 +8212,22 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) return err; } +static int validate_pae_over_nl80211(struct cfg80211_registered_device *rdev, + struct genl_info *info) +{ + if (!info->attrs[NL80211_ATTR_SOCKET_OWNER]) { + GENL_SET_ERR_MSG(info, "SOCKET_OWNER not set"); + return -EINVAL; + } + + if (!rdev->ops->tx_control_port || + !wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211)) + return -EOPNOTSUPP; + + return 0; +} + static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, struct genl_info *info, struct cfg80211_crypto_settings *settings, @@ -8234,6 +8251,15 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, } else settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE); + if (info->attrs[NL80211_ATTR_CONTROL_PORT_OVER_NL80211]) { + int r = validate_pae_over_nl80211(rdev, info); + + if (r < 0) + return r; + + settings->control_port_over_nl80211 = true; + } + if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { void *data; int len, i; -- cgit v1.2.3 From 39c202d228c3da5a5531be847e9b06cc9b787f31 Mon Sep 17 00:00:00 2001 From: Bernie Harris Date: Wed, 21 Mar 2018 15:42:15 +1300 Subject: netfilter: ebtables: Add support for specifying match revision Currently ebtables assumes that the revision number of all match modules is 0, which is an issue when trying to use existing xtables matches with ebtables. The solution is to modify ebtables to allow extensions to specify a revision number, similar to iptables. This gets passed down to the kernel, which is then able to find the match module correctly. To main binary backwards compatibility, the size of the ebt_entry structures is not changed, only the size of the name field is decreased by 1 byte to make room for the revision field. Signed-off-by: Bernie Harris Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter_bridge/ebtables.h | 16 +++++++-- net/bridge/netfilter/ebtables.c | 47 ++++++++++++++++---------- 2 files changed, 42 insertions(+), 21 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/netfilter_bridge/ebtables.h b/include/uapi/linux/netfilter_bridge/ebtables.h index 9ff57c0a0199..0c7dc8315013 100644 --- a/include/uapi/linux/netfilter_bridge/ebtables.h +++ b/include/uapi/linux/netfilter_bridge/ebtables.h @@ -20,6 +20,7 @@ #define EBT_TABLE_MAXNAMELEN 32 #define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN #define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN +#define EBT_EXTENSION_MAXNAMELEN 31 /* verdicts >0 are "branches" */ #define EBT_ACCEPT -1 @@ -120,7 +121,10 @@ struct ebt_entries { struct ebt_entry_match { union { - char name[EBT_FUNCTION_MAXNAMELEN]; + struct { + char name[EBT_EXTENSION_MAXNAMELEN]; + uint8_t revision; + }; struct xt_match *match; } u; /* size of data */ @@ -130,7 +134,10 @@ struct ebt_entry_match { struct ebt_entry_watcher { union { - char name[EBT_FUNCTION_MAXNAMELEN]; + struct { + char name[EBT_EXTENSION_MAXNAMELEN]; + uint8_t revision; + }; struct xt_target *watcher; } u; /* size of data */ @@ -140,7 +147,10 @@ struct ebt_entry_watcher { struct ebt_entry_target { union { - char name[EBT_FUNCTION_MAXNAMELEN]; + struct { + char name[EBT_EXTENSION_MAXNAMELEN]; + uint8_t revision; + }; struct xt_target *target; } u; /* size of data */ diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 9a26d2b7420f..a8cb543e3296 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -356,12 +356,12 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par, left - sizeof(struct ebt_entry_match) < m->match_size) return -EINVAL; - match = xt_find_match(NFPROTO_BRIDGE, m->u.name, 0); + match = xt_find_match(NFPROTO_BRIDGE, m->u.name, m->u.revision); if (IS_ERR(match) || match->family != NFPROTO_BRIDGE) { if (!IS_ERR(match)) module_put(match->me); request_module("ebt_%s", m->u.name); - match = xt_find_match(NFPROTO_BRIDGE, m->u.name, 0); + match = xt_find_match(NFPROTO_BRIDGE, m->u.name, m->u.revision); } if (IS_ERR(match)) return PTR_ERR(match); @@ -1350,16 +1350,17 @@ static int update_counters(struct net *net, const void __user *user, static inline int ebt_obj_to_user(char __user *um, const char *_name, const char *data, int entrysize, - int usersize, int datasize) + int usersize, int datasize, u8 revision) { - char name[EBT_FUNCTION_MAXNAMELEN] = {0}; + char name[EBT_EXTENSION_MAXNAMELEN] = {0}; - /* ebtables expects 32 bytes long names but xt_match names are 29 bytes + /* ebtables expects 31 bytes long names but xt_match names are 29 bytes * long. Copy 29 bytes and fill remaining bytes with zeroes. */ strlcpy(name, _name, sizeof(name)); - if (copy_to_user(um, name, EBT_FUNCTION_MAXNAMELEN) || - put_user(datasize, (int __user *)(um + EBT_FUNCTION_MAXNAMELEN)) || + if (copy_to_user(um, name, EBT_EXTENSION_MAXNAMELEN) || + put_user(revision, (u8 __user *)(um + EBT_EXTENSION_MAXNAMELEN)) || + put_user(datasize, (int __user *)(um + EBT_EXTENSION_MAXNAMELEN + 1)) || xt_data_to_user(um + entrysize, data, usersize, datasize, XT_ALIGN(datasize))) return -EFAULT; @@ -1372,7 +1373,8 @@ static inline int ebt_match_to_user(const struct ebt_entry_match *m, { return ebt_obj_to_user(ubase + ((char *)m - base), m->u.match->name, m->data, sizeof(*m), - m->u.match->usersize, m->match_size); + m->u.match->usersize, m->match_size, + m->u.match->revision); } static inline int ebt_watcher_to_user(const struct ebt_entry_watcher *w, @@ -1380,7 +1382,8 @@ static inline int ebt_watcher_to_user(const struct ebt_entry_watcher *w, { return ebt_obj_to_user(ubase + ((char *)w - base), w->u.watcher->name, w->data, sizeof(*w), - w->u.watcher->usersize, w->watcher_size); + w->u.watcher->usersize, w->watcher_size, + w->u.watcher->revision); } static inline int ebt_entry_to_user(struct ebt_entry *e, const char *base, @@ -1411,7 +1414,8 @@ static inline int ebt_entry_to_user(struct ebt_entry *e, const char *base, if (ret != 0) return ret; ret = ebt_obj_to_user(hlp, t->u.target->name, t->data, sizeof(*t), - t->u.target->usersize, t->target_size); + t->u.target->usersize, t->target_size, + t->u.target->revision); if (ret != 0) return ret; @@ -1599,7 +1603,10 @@ struct compat_ebt_replace { /* struct ebt_entry_match, _target and _watcher have same layout */ struct compat_ebt_entry_mwt { union { - char name[EBT_FUNCTION_MAXNAMELEN]; + struct { + char name[EBT_EXTENSION_MAXNAMELEN]; + u8 revision; + }; compat_uptr_t ptr; } u; compat_uint_t match_size; @@ -1638,8 +1645,9 @@ static int compat_match_to_user(struct ebt_entry_match *m, void __user **dstptr, BUG_ON(off >= m->match_size); - if (copy_to_user(cm->u.name, match->name, - strlen(match->name) + 1) || put_user(msize, &cm->match_size)) + if (copy_to_user(cm->u.name, match->name, strlen(match->name) + 1) || + put_user(match->revision, &cm->u.revision) || + put_user(msize, &cm->match_size)) return -EFAULT; if (match->compat_to_user) { @@ -1668,8 +1676,9 @@ static int compat_target_to_user(struct ebt_entry_target *t, BUG_ON(off >= t->target_size); - if (copy_to_user(cm->u.name, target->name, - strlen(target->name) + 1) || put_user(tsize, &cm->match_size)) + if (copy_to_user(cm->u.name, target->name, strlen(target->name) + 1) || + put_user(target->revision, &cm->u.revision) || + put_user(tsize, &cm->match_size)) return -EFAULT; if (target->compat_to_user) { @@ -1933,7 +1942,7 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt, struct ebt_entries_buf_state *state, const unsigned char *base) { - char name[EBT_FUNCTION_MAXNAMELEN]; + char name[EBT_EXTENSION_MAXNAMELEN]; struct xt_match *match; struct xt_target *wt; void *dst = NULL; @@ -1947,7 +1956,8 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt, switch (compat_mwt) { case EBT_COMPAT_MATCH: - match = xt_request_find_match(NFPROTO_BRIDGE, name, 0); + match = xt_request_find_match(NFPROTO_BRIDGE, name, + mwt->u.revision); if (IS_ERR(match)) return PTR_ERR(match); @@ -1966,7 +1976,8 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt, break; case EBT_COMPAT_WATCHER: /* fallthrough */ case EBT_COMPAT_TARGET: - wt = xt_request_find_target(NFPROTO_BRIDGE, name, 0); + wt = xt_request_find_target(NFPROTO_BRIDGE, name, + mwt->u.revision); if (IS_ERR(wt)) return PTR_ERR(wt); off = xt_compat_target_offset(wt); -- cgit v1.2.3 From a5040c2d8dd732252609cde84ee3cdd6f1b1f927 Mon Sep 17 00:00:00 2001 From: Souvik Banerjee Date: Fri, 30 Mar 2018 14:32:42 -0500 Subject: blktrace: fix comment in blktrace_api.h The `__u64 time` field of the blk_io_trace struct refers to the time in nanoseconds, not in microseconds. It is set in __blk_add_trace, which does the following: t->time = ktime_to_ns(ktime_get()); ktime_to_ns returns ktime_t in nanoseconds, not microseconds. Signed-off-by: Souvik Banerjee Signed-off-by: Jens Axboe --- include/uapi/linux/blktrace_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/blktrace_api.h b/include/uapi/linux/blktrace_api.h index 3c50e07ee833..690621b610e5 100644 --- a/include/uapi/linux/blktrace_api.h +++ b/include/uapi/linux/blktrace_api.h @@ -101,7 +101,7 @@ enum blktrace_notify { struct blk_io_trace { __u32 magic; /* MAGIC << 8 | version */ __u32 sequence; /* event number */ - __u64 time; /* in microseconds */ + __u64 time; /* in nanoseconds */ __u64 sector; /* disk offset */ __u32 bytes; /* transfer length */ __u32 action; /* what happened */ -- cgit v1.2.3 From 5e43f899b03a3492ce5fc44e8900becb04dae9c0 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov Date: Fri, 30 Mar 2018 15:08:00 -0700 Subject: bpf: Check attach type at prog load time == The problem == There are use-cases when a program of some type can be attached to multiple attach points and those attach points must have different permissions to access context or to call helpers. E.g. context structure may have fields for both IPv4 and IPv6 but it doesn't make sense to read from / write to IPv6 field when attach point is somewhere in IPv4 stack. Same applies to BPF-helpers: it may make sense to call some helper from some attach point, but not from other for same prog type. == The solution == Introduce `expected_attach_type` field in in `struct bpf_attr` for `BPF_PROG_LOAD` command. If scenario described in "The problem" section is the case for some prog type, the field will be checked twice: 1) At load time prog type is checked to see if attach type for it must be known to validate program permissions correctly. Prog will be rejected with EINVAL if it's the case and `expected_attach_type` is not specified or has invalid value. 2) At attach time `attach_type` is compared with `expected_attach_type`, if prog type requires to have one, and, if they differ, attach will be rejected with EINVAL. The `expected_attach_type` is now available as part of `struct bpf_prog` in both `bpf_verifier_ops->is_valid_access()` and `bpf_verifier_ops->get_func_proto()` () and can be used to check context accesses and calls to helpers correspondingly. Initially the idea was discussed by Alexei Starovoitov and Daniel Borkmann here: https://marc.info/?l=linux-netdev&m=152107378717201&w=2 Signed-off-by: Andrey Ignatov Signed-off-by: Alexei Starovoitov Signed-off-by: Daniel Borkmann --- include/linux/bpf.h | 5 ++++- include/linux/filter.h | 1 + include/uapi/linux/bpf.h | 5 +++++ kernel/bpf/cgroup.c | 3 ++- kernel/bpf/syscall.c | 31 ++++++++++++++++++++++++++++++- kernel/bpf/verifier.c | 6 +++--- kernel/trace/bpf_trace.c | 27 ++++++++++++++++++--------- net/core/filter.c | 39 +++++++++++++++++++++++++-------------- 8 files changed, 88 insertions(+), 29 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 819229c80eca..95a7abd0ee92 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -208,12 +208,15 @@ struct bpf_prog_ops { struct bpf_verifier_ops { /* return eBPF function prototype for verification */ - const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id); + const struct bpf_func_proto * + (*get_func_proto)(enum bpf_func_id func_id, + const struct bpf_prog *prog); /* return true if 'size' wide access at offset 'off' within bpf_context * with 'type' (read or write) is allowed */ bool (*is_valid_access)(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info); int (*gen_prologue)(struct bpf_insn *insn, bool direct_write, const struct bpf_prog *prog); diff --git a/include/linux/filter.h b/include/linux/filter.h index 897ff3d95968..13c044e4832d 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -469,6 +469,7 @@ struct bpf_prog { is_func:1, /* program is a bpf function */ kprobe_override:1; /* Do we override a kprobe? */ enum bpf_prog_type type; /* Type of BPF program */ + enum bpf_attach_type expected_attach_type; /* For some prog types */ u32 len; /* Number of filter blocks */ u32 jited_len; /* Size of jited insns in bytes */ u8 tag[BPF_TAG_SIZE]; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1878201c2d77..102718624d1e 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -296,6 +296,11 @@ union bpf_attr { __u32 prog_flags; char prog_name[BPF_OBJ_NAME_LEN]; __u32 prog_ifindex; /* ifindex of netdev to prep for */ + /* For some prog types expected attach type must be known at + * load time to verify attach type specific parts of prog + * (context accesses, allowed helpers, etc). + */ + __u32 expected_attach_type; }; struct { /* anonymous struct used by BPF_OBJ_* commands */ diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index c1c0b60d3f2f..8730b24ed540 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -545,7 +545,7 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, EXPORT_SYMBOL(__cgroup_bpf_check_dev_permission); static const struct bpf_func_proto * -cgroup_dev_func_proto(enum bpf_func_id func_id) +cgroup_dev_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_map_lookup_elem: @@ -566,6 +566,7 @@ cgroup_dev_func_proto(enum bpf_func_id func_id) static bool cgroup_dev_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { const int size_default = sizeof(__u32); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 95ca2523fa6e..9d3b572d4dec 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1171,8 +1171,27 @@ struct bpf_prog *bpf_prog_get_type_dev(u32 ufd, enum bpf_prog_type type, } EXPORT_SYMBOL_GPL(bpf_prog_get_type_dev); +static int +bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type, + enum bpf_attach_type expected_attach_type) +{ + /* There are currently no prog types that require specifying + * attach_type at load time. + */ + return 0; +} + +static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, + enum bpf_attach_type attach_type) +{ + /* There are currently no prog types that require specifying + * attach_type at load time. + */ + return 0; +} + /* last field in 'union bpf_attr' used by this command */ -#define BPF_PROG_LOAD_LAST_FIELD prog_ifindex +#define BPF_PROG_LOAD_LAST_FIELD expected_attach_type static int bpf_prog_load(union bpf_attr *attr) { @@ -1209,11 +1228,16 @@ static int bpf_prog_load(union bpf_attr *attr) !capable(CAP_SYS_ADMIN)) return -EPERM; + if (bpf_prog_load_check_attach_type(type, attr->expected_attach_type)) + return -EINVAL; + /* plain bpf_prog allocation */ prog = bpf_prog_alloc(bpf_prog_size(attr->insn_cnt), GFP_USER); if (!prog) return -ENOMEM; + prog->expected_attach_type = attr->expected_attach_type; + prog->aux->offload_requested = !!attr->prog_ifindex; err = security_bpf_prog_alloc(prog->aux); @@ -1474,6 +1498,11 @@ static int bpf_prog_attach(const union bpf_attr *attr) if (IS_ERR(prog)) return PTR_ERR(prog); + if (bpf_prog_attach_check_attach_type(prog, attr->attach_type)) { + bpf_prog_put(prog); + return -EINVAL; + } + cgrp = cgroup_get_from_fd(attr->target_fd); if (IS_ERR(cgrp)) { bpf_prog_put(prog); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 8acd2207e412..10024323031d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1323,7 +1323,7 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, }; if (env->ops->is_valid_access && - env->ops->is_valid_access(off, size, t, &info)) { + env->ops->is_valid_access(off, size, t, env->prog, &info)) { /* A non zero info.ctx_field_size indicates that this field is a * candidate for later verifier transformation to load the whole * field and then apply a mask when accessed with a narrower @@ -2349,7 +2349,7 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn } if (env->ops->get_func_proto) - fn = env->ops->get_func_proto(func_id); + fn = env->ops->get_func_proto(func_id, env->prog); if (!fn) { verbose(env, "unknown func %s#%d\n", func_id_name(func_id), func_id); @@ -5572,7 +5572,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env) insn = new_prog->insnsi + i + delta; } patch_call_imm: - fn = env->ops->get_func_proto(insn->imm); + fn = env->ops->get_func_proto(insn->imm, env->prog); /* all functions that have prototype and verifier allowed * programs to call them, must be real in-kernel functions */ diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 463e72d18c4c..d88e96d4e12c 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -524,7 +524,8 @@ static const struct bpf_func_proto bpf_probe_read_str_proto = { .arg3_type = ARG_ANYTHING, }; -static const struct bpf_func_proto *tracing_func_proto(enum bpf_func_id func_id) +static const struct bpf_func_proto * +tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_map_lookup_elem: @@ -568,7 +569,8 @@ static const struct bpf_func_proto *tracing_func_proto(enum bpf_func_id func_id) } } -static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id) +static const struct bpf_func_proto * +kprobe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_perf_event_output: @@ -582,12 +584,13 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func return &bpf_override_return_proto; #endif default: - return tracing_func_proto(func_id); + return tracing_func_proto(func_id, prog); } } /* bpf+kprobe programs can access fields of 'struct pt_regs' */ static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { if (off < 0 || off >= sizeof(struct pt_regs)) @@ -661,7 +664,8 @@ static const struct bpf_func_proto bpf_get_stackid_proto_tp = { .arg3_type = ARG_ANYTHING, }; -static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id) +static const struct bpf_func_proto * +tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_perf_event_output: @@ -669,11 +673,12 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id) case BPF_FUNC_get_stackid: return &bpf_get_stackid_proto_tp; default: - return tracing_func_proto(func_id); + return tracing_func_proto(func_id, prog); } } static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { if (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE) @@ -721,7 +726,8 @@ static const struct bpf_func_proto bpf_perf_prog_read_value_proto = { .arg3_type = ARG_CONST_SIZE, }; -static const struct bpf_func_proto *pe_prog_func_proto(enum bpf_func_id func_id) +static const struct bpf_func_proto * +pe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_perf_event_output: @@ -731,7 +737,7 @@ static const struct bpf_func_proto *pe_prog_func_proto(enum bpf_func_id func_id) case BPF_FUNC_perf_prog_read_value: return &bpf_perf_prog_read_value_proto; default: - return tracing_func_proto(func_id); + return tracing_func_proto(func_id, prog); } } @@ -781,7 +787,8 @@ static const struct bpf_func_proto bpf_get_stackid_proto_raw_tp = { .arg3_type = ARG_ANYTHING, }; -static const struct bpf_func_proto *raw_tp_prog_func_proto(enum bpf_func_id func_id) +static const struct bpf_func_proto * +raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_perf_event_output: @@ -789,12 +796,13 @@ static const struct bpf_func_proto *raw_tp_prog_func_proto(enum bpf_func_id func case BPF_FUNC_get_stackid: return &bpf_get_stackid_proto_raw_tp; default: - return tracing_func_proto(func_id); + return tracing_func_proto(func_id, prog); } } static bool raw_tp_prog_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { /* largest tracepoint in the kernel has 12 args */ @@ -816,6 +824,7 @@ const struct bpf_prog_ops raw_tracepoint_prog_ops = { }; static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { const int size_u64 = sizeof(u64); diff --git a/net/core/filter.c b/net/core/filter.c index e989bf313195..7790fd128614 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3685,7 +3685,7 @@ bpf_base_func_proto(enum bpf_func_id func_id) } static const struct bpf_func_proto * -sock_filter_func_proto(enum bpf_func_id func_id) +sock_filter_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { /* inet and inet6 sockets are created in a process @@ -3699,7 +3699,7 @@ sock_filter_func_proto(enum bpf_func_id func_id) } static const struct bpf_func_proto * -sk_filter_func_proto(enum bpf_func_id func_id) +sk_filter_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_skb_load_bytes: @@ -3714,7 +3714,7 @@ sk_filter_func_proto(enum bpf_func_id func_id) } static const struct bpf_func_proto * -tc_cls_act_func_proto(enum bpf_func_id func_id) +tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_skb_store_bytes: @@ -3781,7 +3781,7 @@ tc_cls_act_func_proto(enum bpf_func_id func_id) } static const struct bpf_func_proto * -xdp_func_proto(enum bpf_func_id func_id) +xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_perf_event_output: @@ -3804,7 +3804,7 @@ xdp_func_proto(enum bpf_func_id func_id) } static const struct bpf_func_proto * -lwt_inout_func_proto(enum bpf_func_id func_id) +lwt_inout_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_skb_load_bytes: @@ -3831,7 +3831,7 @@ lwt_inout_func_proto(enum bpf_func_id func_id) } static const struct bpf_func_proto * - sock_ops_func_proto(enum bpf_func_id func_id) +sock_ops_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_setsockopt: @@ -3847,7 +3847,8 @@ static const struct bpf_func_proto * } } -static const struct bpf_func_proto *sk_msg_func_proto(enum bpf_func_id func_id) +static const struct bpf_func_proto * +sk_msg_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_msg_redirect_map: @@ -3863,7 +3864,8 @@ static const struct bpf_func_proto *sk_msg_func_proto(enum bpf_func_id func_id) } } -static const struct bpf_func_proto *sk_skb_func_proto(enum bpf_func_id func_id) +static const struct bpf_func_proto * +sk_skb_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_skb_store_bytes: @@ -3888,7 +3890,7 @@ static const struct bpf_func_proto *sk_skb_func_proto(enum bpf_func_id func_id) } static const struct bpf_func_proto * -lwt_xmit_func_proto(enum bpf_func_id func_id) +lwt_xmit_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_skb_get_tunnel_key: @@ -3918,11 +3920,12 @@ lwt_xmit_func_proto(enum bpf_func_id func_id) case BPF_FUNC_set_hash_invalid: return &bpf_set_hash_invalid_proto; default: - return lwt_inout_func_proto(func_id); + return lwt_inout_func_proto(func_id, prog); } } static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { const int size_default = sizeof(__u32); @@ -3966,6 +3969,7 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type static bool sk_filter_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { switch (off) { @@ -3986,11 +3990,12 @@ static bool sk_filter_is_valid_access(int off, int size, } } - return bpf_skb_is_valid_access(off, size, type, info); + return bpf_skb_is_valid_access(off, size, type, prog, info); } static bool lwt_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { switch (off) { @@ -4020,11 +4025,12 @@ static bool lwt_is_valid_access(int off, int size, break; } - return bpf_skb_is_valid_access(off, size, type, info); + return bpf_skb_is_valid_access(off, size, type, prog, info); } static bool sock_filter_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { if (type == BPF_WRITE) { @@ -4096,6 +4102,7 @@ static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write, static bool tc_cls_act_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { if (type == BPF_WRITE) { @@ -4125,7 +4132,7 @@ static bool tc_cls_act_is_valid_access(int off, int size, return false; } - return bpf_skb_is_valid_access(off, size, type, info); + return bpf_skb_is_valid_access(off, size, type, prog, info); } static bool __is_valid_xdp_access(int off, int size) @@ -4142,6 +4149,7 @@ static bool __is_valid_xdp_access(int off, int size) static bool xdp_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { if (type == BPF_WRITE) @@ -4174,6 +4182,7 @@ EXPORT_SYMBOL_GPL(bpf_warn_invalid_xdp_action); static bool sock_ops_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { const int size_default = sizeof(__u32); @@ -4220,6 +4229,7 @@ static int sk_skb_prologue(struct bpf_insn *insn_buf, bool direct_write, static bool sk_skb_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { switch (off) { @@ -4249,11 +4259,12 @@ static bool sk_skb_is_valid_access(int off, int size, break; } - return bpf_skb_is_valid_access(off, size, type, info); + return bpf_skb_is_valid_access(off, size, type, prog, info); } static bool sk_msg_is_valid_access(int off, int size, enum bpf_access_type type, + const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { if (type == BPF_WRITE) -- cgit v1.2.3 From 4fbac77d2d092b475dda9eea66da674369665427 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov Date: Fri, 30 Mar 2018 15:08:02 -0700 Subject: bpf: Hooks for sys_bind == The problem == There is a use-case when all processes inside a cgroup should use one single IP address on a host that has multiple IP configured. Those processes should use the IP for both ingress and egress, for TCP and UDP traffic. So TCP/UDP servers should be bound to that IP to accept incoming connections on it, and TCP/UDP clients should make outgoing connections from that IP. It should not require changing application code since it's often not possible. Currently it's solved by intercepting glibc wrappers around syscalls such as `bind(2)` and `connect(2)`. It's done by a shared library that is preloaded for every process in a cgroup so that whenever TCP/UDP server calls `bind(2)`, the library replaces IP in sockaddr before passing arguments to syscall. When application calls `connect(2)` the library transparently binds the local end of connection to that IP (`bind(2)` with `IP_BIND_ADDRESS_NO_PORT` to avoid performance penalty). Shared library approach is fragile though, e.g.: * some applications clear env vars (incl. `LD_PRELOAD`); * `/etc/ld.so.preload` doesn't help since some applications are linked with option `-z nodefaultlib`; * other applications don't use glibc and there is nothing to intercept. == The solution == The patch provides much more reliable in-kernel solution for the 1st part of the problem: binding TCP/UDP servers on desired IP. It does not depend on application environment and implementation details (whether glibc is used or not). It adds new eBPF program type `BPF_PROG_TYPE_CGROUP_SOCK_ADDR` and attach types `BPF_CGROUP_INET4_BIND` and `BPF_CGROUP_INET6_BIND` (similar to already existing `BPF_CGROUP_INET_SOCK_CREATE`). The new program type is intended to be used with sockets (`struct sock`) in a cgroup and provided by user `struct sockaddr`. Pointers to both of them are parts of the context passed to programs of newly added types. The new attach types provides hooks in `bind(2)` system call for both IPv4 and IPv6 so that one can write a program to override IP addresses and ports user program tries to bind to and apply such a program for whole cgroup. == Implementation notes == [1] Separate attach types for `AF_INET` and `AF_INET6` are added intentionally to prevent reading/writing to offsets that don't make sense for corresponding socket family. E.g. if user passes `sockaddr_in` it doesn't make sense to read from / write to `user_ip6[]` context fields. [2] The write access to `struct bpf_sock_addr_kern` is implemented using special field as an additional "register". There are just two registers in `sock_addr_convert_ctx_access`: `src` with value to write and `dst` with pointer to context that can't be changed not to break later instructions. But the fields, allowed to write to, are not available directly and to access them address of corresponding pointer has to be loaded first. To get additional register the 1st not used by `src` and `dst` one is taken, its content is saved to `bpf_sock_addr_kern.tmp_reg`, then the register is used to load address of pointer field, and finally the register's content is restored from the temporary field after writing `src` value. Signed-off-by: Andrey Ignatov Signed-off-by: Alexei Starovoitov Signed-off-by: Daniel Borkmann --- include/linux/bpf-cgroup.h | 21 ++++ include/linux/bpf_types.h | 1 + include/linux/filter.h | 10 ++ include/uapi/linux/bpf.h | 23 +++++ kernel/bpf/cgroup.c | 36 +++++++ kernel/bpf/syscall.c | 36 +++++-- kernel/bpf/verifier.c | 1 + net/core/filter.c | 232 +++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/af_inet.c | 7 ++ net/ipv6/af_inet6.c | 7 ++ 10 files changed, 366 insertions(+), 8 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index 8a4566691c8f..67dc4a6471ad 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -6,6 +6,7 @@ #include struct sock; +struct sockaddr; struct cgroup; struct sk_buff; struct bpf_sock_ops_kern; @@ -63,6 +64,10 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk, int __cgroup_bpf_run_filter_sk(struct sock *sk, enum bpf_attach_type type); +int __cgroup_bpf_run_filter_sock_addr(struct sock *sk, + struct sockaddr *uaddr, + enum bpf_attach_type type); + int __cgroup_bpf_run_filter_sock_ops(struct sock *sk, struct bpf_sock_ops_kern *sock_ops, enum bpf_attach_type type); @@ -103,6 +108,20 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, __ret; \ }) +#define BPF_CGROUP_RUN_SA_PROG(sk, uaddr, type) \ +({ \ + int __ret = 0; \ + if (cgroup_bpf_enabled) \ + __ret = __cgroup_bpf_run_filter_sock_addr(sk, uaddr, type); \ + __ret; \ +}) + +#define BPF_CGROUP_RUN_PROG_INET4_BIND(sk, uaddr) \ + BPF_CGROUP_RUN_SA_PROG(sk, uaddr, BPF_CGROUP_INET4_BIND) + +#define BPF_CGROUP_RUN_PROG_INET6_BIND(sk, uaddr) \ + BPF_CGROUP_RUN_SA_PROG(sk, uaddr, BPF_CGROUP_INET6_BIND) + #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) \ ({ \ int __ret = 0; \ @@ -135,6 +154,8 @@ static inline int cgroup_bpf_inherit(struct cgroup *cgrp) { return 0; } #define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk,skb) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_INET4_BIND(sk, uaddr) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_INET6_BIND(sk, uaddr) ({ 0; }) #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; }) #define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; }) diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 6d7243bfb0ff..2b28fcf6f6ae 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -8,6 +8,7 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_ACT, tc_cls_act) BPF_PROG_TYPE(BPF_PROG_TYPE_XDP, xdp) BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SKB, cg_skb) BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK, cg_sock) +BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, cg_sock_addr) BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_IN, lwt_inout) BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_OUT, lwt_inout) BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_XMIT, lwt_xmit) diff --git a/include/linux/filter.h b/include/linux/filter.h index 13c044e4832d..fc4e8f91b03d 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -1021,6 +1021,16 @@ static inline int bpf_tell_extensions(void) return SKF_AD_MAX; } +struct bpf_sock_addr_kern { + struct sock *sk; + struct sockaddr *uaddr; + /* Temporary "register" to make indirect stores to nested structures + * defined above. We need three registers to make such a store, but + * only two (src and dst) are available at convert_ctx_access time + */ + u64 tmp_reg; +}; + struct bpf_sock_ops_kern { struct sock *sk; u32 op; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 102718624d1e..ce3e69e3c793 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -136,6 +136,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_CGROUP_DEVICE, BPF_PROG_TYPE_SK_MSG, BPF_PROG_TYPE_RAW_TRACEPOINT, + BPF_PROG_TYPE_CGROUP_SOCK_ADDR, }; enum bpf_attach_type { @@ -147,6 +148,8 @@ enum bpf_attach_type { BPF_SK_SKB_STREAM_VERDICT, BPF_CGROUP_DEVICE, BPF_SK_MSG_VERDICT, + BPF_CGROUP_INET4_BIND, + BPF_CGROUP_INET6_BIND, __MAX_BPF_ATTACH_TYPE }; @@ -1010,6 +1013,26 @@ struct bpf_map_info { __u64 netns_ino; } __attribute__((aligned(8))); +/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed + * by user and intended to be used by socket (e.g. to bind to, depends on + * attach attach type). + */ +struct bpf_sock_addr { + __u32 user_family; /* Allows 4-byte read, but no write. */ + __u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write. + * Stored in network byte order. + */ + __u32 user_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write. + * Stored in network byte order. + */ + __u32 user_port; /* Allows 4-byte read and write. + * Stored in network byte order + */ + __u32 family; /* Allows 4-byte read, but no write */ + __u32 type; /* Allows 4-byte read, but no write */ + __u32 protocol; /* Allows 4-byte read, but no write */ +}; + /* User bpf_sock_ops struct to access socket values and specify request ops * and their replies. * Some of this fields are in network (bigendian) byte order and may need diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 8730b24ed540..43171a0bb02b 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -494,6 +494,42 @@ int __cgroup_bpf_run_filter_sk(struct sock *sk, } EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk); +/** + * __cgroup_bpf_run_filter_sock_addr() - Run a program on a sock and + * provided by user sockaddr + * @sk: sock struct that will use sockaddr + * @uaddr: sockaddr struct provided by user + * @type: The type of program to be exectuted + * + * socket is expected to be of type INET or INET6. + * + * This function will return %-EPERM if an attached program is found and + * returned value != 1 during execution. In all other cases, 0 is returned. + */ +int __cgroup_bpf_run_filter_sock_addr(struct sock *sk, + struct sockaddr *uaddr, + enum bpf_attach_type type) +{ + struct bpf_sock_addr_kern ctx = { + .sk = sk, + .uaddr = uaddr, + }; + struct cgroup *cgrp; + int ret; + + /* Check socket family since not all sockets represent network + * endpoint (e.g. AF_UNIX). + */ + if (sk->sk_family != AF_INET && sk->sk_family != AF_INET6) + return 0; + + cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); + ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx, BPF_PROG_RUN); + + return ret == 1 ? 0 : -EPERM; +} +EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_addr); + /** * __cgroup_bpf_run_filter_sock_ops() - Run a program on a sock * @sk: socket to get cgroup from diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 9d3b572d4dec..2cad66a4cacb 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1175,19 +1175,29 @@ static int bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type, enum bpf_attach_type expected_attach_type) { - /* There are currently no prog types that require specifying - * attach_type at load time. - */ - return 0; + switch (prog_type) { + case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: + switch (expected_attach_type) { + case BPF_CGROUP_INET4_BIND: + case BPF_CGROUP_INET6_BIND: + return 0; + default: + return -EINVAL; + } + default: + return 0; + } } static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, enum bpf_attach_type attach_type) { - /* There are currently no prog types that require specifying - * attach_type at load time. - */ - return 0; + switch (prog->type) { + case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: + return attach_type == prog->expected_attach_type ? 0 : -EINVAL; + default: + return 0; + } } /* last field in 'union bpf_attr' used by this command */ @@ -1479,6 +1489,10 @@ static int bpf_prog_attach(const union bpf_attr *attr) case BPF_CGROUP_INET_SOCK_CREATE: ptype = BPF_PROG_TYPE_CGROUP_SOCK; break; + case BPF_CGROUP_INET4_BIND: + case BPF_CGROUP_INET6_BIND: + ptype = BPF_PROG_TYPE_CGROUP_SOCK_ADDR; + break; case BPF_CGROUP_SOCK_OPS: ptype = BPF_PROG_TYPE_SOCK_OPS; break; @@ -1541,6 +1555,10 @@ static int bpf_prog_detach(const union bpf_attr *attr) case BPF_CGROUP_INET_SOCK_CREATE: ptype = BPF_PROG_TYPE_CGROUP_SOCK; break; + case BPF_CGROUP_INET4_BIND: + case BPF_CGROUP_INET6_BIND: + ptype = BPF_PROG_TYPE_CGROUP_SOCK_ADDR; + break; case BPF_CGROUP_SOCK_OPS: ptype = BPF_PROG_TYPE_SOCK_OPS; break; @@ -1590,6 +1608,8 @@ static int bpf_prog_query(const union bpf_attr *attr, case BPF_CGROUP_INET_INGRESS: case BPF_CGROUP_INET_EGRESS: case BPF_CGROUP_INET_SOCK_CREATE: + case BPF_CGROUP_INET4_BIND: + case BPF_CGROUP_INET6_BIND: case BPF_CGROUP_SOCK_OPS: case BPF_CGROUP_DEVICE: break; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 10024323031d..5dd1dcb902bf 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3887,6 +3887,7 @@ static int check_return_code(struct bpf_verifier_env *env) switch (env->prog->type) { case BPF_PROG_TYPE_CGROUP_SKB: case BPF_PROG_TYPE_CGROUP_SOCK: + case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: case BPF_PROG_TYPE_SOCK_OPS: case BPF_PROG_TYPE_CGROUP_DEVICE: break; diff --git a/net/core/filter.c b/net/core/filter.c index 7790fd128614..c08e5b121558 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3698,6 +3698,20 @@ sock_filter_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) } } +static const struct bpf_func_proto * +sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) +{ + switch (func_id) { + /* inet and inet6 sockets are created in a process + * context so there is always a valid uid/gid + */ + case BPF_FUNC_get_current_uid_gid: + return &bpf_get_current_uid_gid_proto; + default: + return bpf_base_func_proto(func_id); + } +} + static const struct bpf_func_proto * sk_filter_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { @@ -4180,6 +4194,69 @@ void bpf_warn_invalid_xdp_action(u32 act) } EXPORT_SYMBOL_GPL(bpf_warn_invalid_xdp_action); +static bool sock_addr_is_valid_access(int off, int size, + enum bpf_access_type type, + const struct bpf_prog *prog, + struct bpf_insn_access_aux *info) +{ + const int size_default = sizeof(__u32); + + if (off < 0 || off >= sizeof(struct bpf_sock_addr)) + return false; + if (off % size != 0) + return false; + + /* Disallow access to IPv6 fields from IPv4 contex and vise + * versa. + */ + switch (off) { + case bpf_ctx_range(struct bpf_sock_addr, user_ip4): + switch (prog->expected_attach_type) { + case BPF_CGROUP_INET4_BIND: + break; + default: + return false; + } + break; + case bpf_ctx_range_till(struct bpf_sock_addr, user_ip6[0], user_ip6[3]): + switch (prog->expected_attach_type) { + case BPF_CGROUP_INET6_BIND: + break; + default: + return false; + } + break; + } + + switch (off) { + case bpf_ctx_range(struct bpf_sock_addr, user_ip4): + case bpf_ctx_range_till(struct bpf_sock_addr, user_ip6[0], user_ip6[3]): + /* Only narrow read access allowed for now. */ + if (type == BPF_READ) { + bpf_ctx_record_field_size(info, size_default); + if (!bpf_ctx_narrow_access_ok(off, size, size_default)) + return false; + } else { + if (size != size_default) + return false; + } + break; + case bpf_ctx_range(struct bpf_sock_addr, user_port): + if (size != size_default) + return false; + break; + default: + if (type == BPF_READ) { + if (size != size_default) + return false; + } else { + return false; + } + } + + return true; +} + static bool sock_ops_is_valid_access(int off, int size, enum bpf_access_type type, const struct bpf_prog *prog, @@ -4724,6 +4801,152 @@ static u32 xdp_convert_ctx_access(enum bpf_access_type type, return insn - insn_buf; } +/* SOCK_ADDR_LOAD_NESTED_FIELD() loads Nested Field S.F.NF where S is type of + * context Structure, F is Field in context structure that contains a pointer + * to Nested Structure of type NS that has the field NF. + * + * SIZE encodes the load size (BPF_B, BPF_H, etc). It's up to caller to make + * sure that SIZE is not greater than actual size of S.F.NF. + * + * If offset OFF is provided, the load happens from that offset relative to + * offset of NF. + */ +#define SOCK_ADDR_LOAD_NESTED_FIELD_SIZE_OFF(S, NS, F, NF, SIZE, OFF) \ + do { \ + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(S, F), si->dst_reg, \ + si->src_reg, offsetof(S, F)); \ + *insn++ = BPF_LDX_MEM( \ + SIZE, si->dst_reg, si->dst_reg, \ + bpf_target_off(NS, NF, FIELD_SIZEOF(NS, NF), \ + target_size) \ + + OFF); \ + } while (0) + +#define SOCK_ADDR_LOAD_NESTED_FIELD(S, NS, F, NF) \ + SOCK_ADDR_LOAD_NESTED_FIELD_SIZE_OFF(S, NS, F, NF, \ + BPF_FIELD_SIZEOF(NS, NF), 0) + +/* SOCK_ADDR_STORE_NESTED_FIELD_OFF() has semantic similar to + * SOCK_ADDR_LOAD_NESTED_FIELD_SIZE_OFF() but for store operation. + * + * It doesn't support SIZE argument though since narrow stores are not + * supported for now. + * + * In addition it uses Temporary Field TF (member of struct S) as the 3rd + * "register" since two registers available in convert_ctx_access are not + * enough: we can't override neither SRC, since it contains value to store, nor + * DST since it contains pointer to context that may be used by later + * instructions. But we need a temporary place to save pointer to nested + * structure whose field we want to store to. + */ +#define SOCK_ADDR_STORE_NESTED_FIELD_OFF(S, NS, F, NF, OFF, TF) \ + do { \ + int tmp_reg = BPF_REG_9; \ + if (si->src_reg == tmp_reg || si->dst_reg == tmp_reg) \ + --tmp_reg; \ + if (si->src_reg == tmp_reg || si->dst_reg == tmp_reg) \ + --tmp_reg; \ + *insn++ = BPF_STX_MEM(BPF_DW, si->dst_reg, tmp_reg, \ + offsetof(S, TF)); \ + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(S, F), tmp_reg, \ + si->dst_reg, offsetof(S, F)); \ + *insn++ = BPF_STX_MEM( \ + BPF_FIELD_SIZEOF(NS, NF), tmp_reg, si->src_reg, \ + bpf_target_off(NS, NF, FIELD_SIZEOF(NS, NF), \ + target_size) \ + + OFF); \ + *insn++ = BPF_LDX_MEM(BPF_DW, tmp_reg, si->dst_reg, \ + offsetof(S, TF)); \ + } while (0) + +#define SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD_SIZE_OFF(S, NS, F, NF, SIZE, OFF, \ + TF) \ + do { \ + if (type == BPF_WRITE) { \ + SOCK_ADDR_STORE_NESTED_FIELD_OFF(S, NS, F, NF, OFF, \ + TF); \ + } else { \ + SOCK_ADDR_LOAD_NESTED_FIELD_SIZE_OFF( \ + S, NS, F, NF, SIZE, OFF); \ + } \ + } while (0) + +#define SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD(S, NS, F, NF, TF) \ + SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD_SIZE_OFF( \ + S, NS, F, NF, BPF_FIELD_SIZEOF(NS, NF), 0, TF) + +static u32 sock_addr_convert_ctx_access(enum bpf_access_type type, + const struct bpf_insn *si, + struct bpf_insn *insn_buf, + struct bpf_prog *prog, u32 *target_size) +{ + struct bpf_insn *insn = insn_buf; + int off; + + switch (si->off) { + case offsetof(struct bpf_sock_addr, user_family): + SOCK_ADDR_LOAD_NESTED_FIELD(struct bpf_sock_addr_kern, + struct sockaddr, uaddr, sa_family); + break; + + case offsetof(struct bpf_sock_addr, user_ip4): + SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD_SIZE_OFF( + struct bpf_sock_addr_kern, struct sockaddr_in, uaddr, + sin_addr, BPF_SIZE(si->code), 0, tmp_reg); + break; + + case bpf_ctx_range_till(struct bpf_sock_addr, user_ip6[0], user_ip6[3]): + off = si->off; + off -= offsetof(struct bpf_sock_addr, user_ip6[0]); + SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD_SIZE_OFF( + struct bpf_sock_addr_kern, struct sockaddr_in6, uaddr, + sin6_addr.s6_addr32[0], BPF_SIZE(si->code), off, + tmp_reg); + break; + + case offsetof(struct bpf_sock_addr, user_port): + /* To get port we need to know sa_family first and then treat + * sockaddr as either sockaddr_in or sockaddr_in6. + * Though we can simplify since port field has same offset and + * size in both structures. + * Here we check this invariant and use just one of the + * structures if it's true. + */ + BUILD_BUG_ON(offsetof(struct sockaddr_in, sin_port) != + offsetof(struct sockaddr_in6, sin6_port)); + BUILD_BUG_ON(FIELD_SIZEOF(struct sockaddr_in, sin_port) != + FIELD_SIZEOF(struct sockaddr_in6, sin6_port)); + SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD(struct bpf_sock_addr_kern, + struct sockaddr_in6, uaddr, + sin6_port, tmp_reg); + break; + + case offsetof(struct bpf_sock_addr, family): + SOCK_ADDR_LOAD_NESTED_FIELD(struct bpf_sock_addr_kern, + struct sock, sk, sk_family); + break; + + case offsetof(struct bpf_sock_addr, type): + SOCK_ADDR_LOAD_NESTED_FIELD_SIZE_OFF( + struct bpf_sock_addr_kern, struct sock, sk, + __sk_flags_offset, BPF_W, 0); + *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, SK_FL_TYPE_MASK); + *insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, SK_FL_TYPE_SHIFT); + break; + + case offsetof(struct bpf_sock_addr, protocol): + SOCK_ADDR_LOAD_NESTED_FIELD_SIZE_OFF( + struct bpf_sock_addr_kern, struct sock, sk, + __sk_flags_offset, BPF_W, 0); + *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, SK_FL_PROTO_MASK); + *insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, + SK_FL_PROTO_SHIFT); + break; + } + + return insn - insn_buf; +} + static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, const struct bpf_insn *si, struct bpf_insn *insn_buf, @@ -5181,6 +5404,15 @@ const struct bpf_verifier_ops cg_sock_verifier_ops = { const struct bpf_prog_ops cg_sock_prog_ops = { }; +const struct bpf_verifier_ops cg_sock_addr_verifier_ops = { + .get_func_proto = sock_addr_func_proto, + .is_valid_access = sock_addr_is_valid_access, + .convert_ctx_access = sock_addr_convert_ctx_access, +}; + +const struct bpf_prog_ops cg_sock_addr_prog_ops = { +}; + const struct bpf_verifier_ops sock_ops_verifier_ops = { .get_func_proto = sock_ops_func_proto, .is_valid_access = sock_ops_is_valid_access, diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index e8c7fad8c329..2dec266507dc 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -450,6 +450,13 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (addr_len < sizeof(struct sockaddr_in)) goto out; + /* BPF prog is run before any checks are done so that if the prog + * changes context in a wrong way it will be caught. + */ + err = BPF_CGROUP_RUN_PROG_INET4_BIND(sk, uaddr); + if (err) + goto out; + if (addr->sin_family != AF_INET) { /* Compatibility games : accept AF_UNSPEC (mapped to AF_INET) * only if s_addr is INADDR_ANY. diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index dbbe04018813..fa24e3f06ac6 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -295,6 +295,13 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; + /* BPF prog is run before any checks are done so that if the prog + * changes context in a wrong way it will be caught. + */ + err = BPF_CGROUP_RUN_PROG_INET6_BIND(sk, uaddr); + if (err) + return err; + if (addr->sin6_family != AF_INET6) return -EAFNOSUPPORT; -- cgit v1.2.3 From d74bad4e74ee373787a9ae24197c17b7cdc428d5 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov Date: Fri, 30 Mar 2018 15:08:05 -0700 Subject: bpf: Hooks for sys_connect == The problem == See description of the problem in the initial patch of this patch set. == The solution == The patch provides much more reliable in-kernel solution for the 2nd part of the problem: making outgoing connecttion from desired IP. It adds new attach types `BPF_CGROUP_INET4_CONNECT` and `BPF_CGROUP_INET6_CONNECT` for program type `BPF_PROG_TYPE_CGROUP_SOCK_ADDR` that can be used to override both source and destination of a connection at connect(2) time. Local end of connection can be bound to desired IP using newly introduced BPF-helper `bpf_bind()`. It allows to bind to only IP though, and doesn't support binding to port, i.e. leverages `IP_BIND_ADDRESS_NO_PORT` socket option. There are two reasons for this: * looking for a free port is expensive and can affect performance significantly; * there is no use-case for port. As for remote end (`struct sockaddr *` passed by user), both parts of it can be overridden, remote IP and remote port. It's useful if an application inside cgroup wants to connect to another application inside same cgroup or to itself, but knows nothing about IP assigned to the cgroup. Support is added for IPv4 and IPv6, for TCP and UDP. IPv4 and IPv6 have separate attach types for same reason as sys_bind hooks, i.e. to prevent reading from / writing to e.g. user_ip6 fields when user passes sockaddr_in since it'd be out-of-bound. == Implementation notes == The patch introduces new field in `struct proto`: `pre_connect` that is a pointer to a function with same signature as `connect` but is called before it. The reason is in some cases BPF hooks should be called way before control is passed to `sk->sk_prot->connect`. Specifically `inet_dgram_connect` autobinds socket before calling `sk->sk_prot->connect` and there is no way to call `bpf_bind()` from hooks from e.g. `ip4_datagram_connect` or `ip6_datagram_connect` since it'd cause double-bind. On the other hand `proto.pre_connect` provides a flexible way to add BPF hooks for connect only for necessary `proto` and call them at desired time before `connect`. Since `bpf_bind()` is allowed to bind only to IP and autobind in `inet_dgram_connect` binds only port there is no chance of double-bind. bpf_bind() sets `force_bind_address_no_port` to bind to only IP despite of value of `bind_address_no_port` socket field. bpf_bind() sets `with_lock` to `false` when calling to __inet_bind() and __inet6_bind() since all call-sites, where bpf_bind() is called, already hold socket lock. Signed-off-by: Andrey Ignatov Signed-off-by: Alexei Starovoitov Signed-off-by: Daniel Borkmann --- include/linux/bpf-cgroup.h | 31 +++++++++++++++++++++++++ include/net/addrconf.h | 7 ++++++ include/net/sock.h | 3 +++ include/net/udp.h | 1 + include/uapi/linux/bpf.h | 12 +++++++++- kernel/bpf/syscall.c | 8 +++++++ net/core/filter.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/af_inet.c | 13 +++++++++++ net/ipv4/tcp_ipv4.c | 16 +++++++++++++ net/ipv4/udp.c | 14 ++++++++++++ net/ipv6/af_inet6.c | 5 ++++ net/ipv6/tcp_ipv6.c | 16 +++++++++++++ net/ipv6/udp.c | 20 ++++++++++++++++ 13 files changed, 202 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index 67dc4a6471ad..c6ab295e6dcb 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -116,12 +116,38 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, __ret; \ }) +#define BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, type) \ +({ \ + int __ret = 0; \ + if (cgroup_bpf_enabled) { \ + lock_sock(sk); \ + __ret = __cgroup_bpf_run_filter_sock_addr(sk, uaddr, type); \ + release_sock(sk); \ + } \ + __ret; \ +}) + #define BPF_CGROUP_RUN_PROG_INET4_BIND(sk, uaddr) \ BPF_CGROUP_RUN_SA_PROG(sk, uaddr, BPF_CGROUP_INET4_BIND) #define BPF_CGROUP_RUN_PROG_INET6_BIND(sk, uaddr) \ BPF_CGROUP_RUN_SA_PROG(sk, uaddr, BPF_CGROUP_INET6_BIND) +#define BPF_CGROUP_PRE_CONNECT_ENABLED(sk) (cgroup_bpf_enabled && \ + sk->sk_prot->pre_connect) + +#define BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr) \ + BPF_CGROUP_RUN_SA_PROG(sk, uaddr, BPF_CGROUP_INET4_CONNECT) + +#define BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr) \ + BPF_CGROUP_RUN_SA_PROG(sk, uaddr, BPF_CGROUP_INET6_CONNECT) + +#define BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr) \ + BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, BPF_CGROUP_INET4_CONNECT) + +#define BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK(sk, uaddr) \ + BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, BPF_CGROUP_INET6_CONNECT) + #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) \ ({ \ int __ret = 0; \ @@ -151,11 +177,16 @@ struct cgroup_bpf {}; static inline void cgroup_bpf_put(struct cgroup *cgrp) {} static inline int cgroup_bpf_inherit(struct cgroup *cgrp) { return 0; } +#define BPF_CGROUP_PRE_CONNECT_ENABLED(sk) (0) #define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk,skb) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET4_BIND(sk, uaddr) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET6_BIND(sk, uaddr) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK(sk, uaddr) ({ 0; }) #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; }) #define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; }) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 132e5b95167a..378d601258be 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -231,6 +231,13 @@ struct ipv6_stub { }; extern const struct ipv6_stub *ipv6_stub __read_mostly; +/* A stub used by bpf helpers. Similarly ugly as ipv6_stub */ +struct ipv6_bpf_stub { + int (*inet6_bind)(struct sock *sk, struct sockaddr *uaddr, int addr_len, + bool force_bind_address_no_port, bool with_lock); +}; +extern const struct ipv6_bpf_stub *ipv6_bpf_stub __read_mostly; + /* * identify MLD packets for MLD filter exceptions */ diff --git a/include/net/sock.h b/include/net/sock.h index b8ff435fa96e..49bd2c1796b0 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1026,6 +1026,9 @@ static inline void sk_prot_clear_nulls(struct sock *sk, int size) struct proto { void (*close)(struct sock *sk, long timeout); + int (*pre_connect)(struct sock *sk, + struct sockaddr *uaddr, + int addr_len); int (*connect)(struct sock *sk, struct sockaddr *uaddr, int addr_len); diff --git a/include/net/udp.h b/include/net/udp.h index 850a8e581cce..0676b272f6ac 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -273,6 +273,7 @@ void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst); int udp_rcv(struct sk_buff *skb); int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); int udp_init_sock(struct sock *sk); +int udp_pre_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); int __udp_disconnect(struct sock *sk, int flags); int udp_disconnect(struct sock *sk, int flags); __poll_t udp_poll(struct file *file, struct socket *sock, poll_table *wait); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index ce3e69e3c793..77afaf1ba556 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -150,6 +150,8 @@ enum bpf_attach_type { BPF_SK_MSG_VERDICT, BPF_CGROUP_INET4_BIND, BPF_CGROUP_INET6_BIND, + BPF_CGROUP_INET4_CONNECT, + BPF_CGROUP_INET6_CONNECT, __MAX_BPF_ATTACH_TYPE }; @@ -744,6 +746,13 @@ union bpf_attr { * @flags: reserved for future use * Return: SK_PASS * + * int bpf_bind(ctx, addr, addr_len) + * Bind socket to address. Only binding to IP is supported, no port can be + * set in addr. + * @ctx: pointer to context of type bpf_sock_addr + * @addr: pointer to struct sockaddr to bind socket to + * @addr_len: length of sockaddr structure + * Return: 0 on success or negative error code */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -809,7 +818,8 @@ union bpf_attr { FN(msg_redirect_map), \ FN(msg_apply_bytes), \ FN(msg_cork_bytes), \ - FN(msg_pull_data), + FN(msg_pull_data), \ + FN(bind), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 2cad66a4cacb..cf1b29bc0ab8 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1180,6 +1180,8 @@ bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type, switch (expected_attach_type) { case BPF_CGROUP_INET4_BIND: case BPF_CGROUP_INET6_BIND: + case BPF_CGROUP_INET4_CONNECT: + case BPF_CGROUP_INET6_CONNECT: return 0; default: return -EINVAL; @@ -1491,6 +1493,8 @@ static int bpf_prog_attach(const union bpf_attr *attr) break; case BPF_CGROUP_INET4_BIND: case BPF_CGROUP_INET6_BIND: + case BPF_CGROUP_INET4_CONNECT: + case BPF_CGROUP_INET6_CONNECT: ptype = BPF_PROG_TYPE_CGROUP_SOCK_ADDR; break; case BPF_CGROUP_SOCK_OPS: @@ -1557,6 +1561,8 @@ static int bpf_prog_detach(const union bpf_attr *attr) break; case BPF_CGROUP_INET4_BIND: case BPF_CGROUP_INET6_BIND: + case BPF_CGROUP_INET4_CONNECT: + case BPF_CGROUP_INET6_CONNECT: ptype = BPF_PROG_TYPE_CGROUP_SOCK_ADDR; break; case BPF_CGROUP_SOCK_OPS: @@ -1610,6 +1616,8 @@ static int bpf_prog_query(const union bpf_attr *attr, case BPF_CGROUP_INET_SOCK_CREATE: case BPF_CGROUP_INET4_BIND: case BPF_CGROUP_INET6_BIND: + case BPF_CGROUP_INET4_CONNECT: + case BPF_CGROUP_INET6_CONNECT: case BPF_CGROUP_SOCK_OPS: case BPF_CGROUP_DEVICE: break; diff --git a/net/core/filter.c b/net/core/filter.c index c08e5b121558..bdb9cadd4d27 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -3656,6 +3657,52 @@ static const struct bpf_func_proto bpf_sock_ops_cb_flags_set_proto = { .arg2_type = ARG_ANYTHING, }; +const struct ipv6_bpf_stub *ipv6_bpf_stub __read_mostly; +EXPORT_SYMBOL_GPL(ipv6_bpf_stub); + +BPF_CALL_3(bpf_bind, struct bpf_sock_addr_kern *, ctx, struct sockaddr *, addr, + int, addr_len) +{ +#ifdef CONFIG_INET + struct sock *sk = ctx->sk; + int err; + + /* Binding to port can be expensive so it's prohibited in the helper. + * Only binding to IP is supported. + */ + err = -EINVAL; + if (addr->sa_family == AF_INET) { + if (addr_len < sizeof(struct sockaddr_in)) + return err; + if (((struct sockaddr_in *)addr)->sin_port != htons(0)) + return err; + return __inet_bind(sk, addr, addr_len, true, false); +#if IS_ENABLED(CONFIG_IPV6) + } else if (addr->sa_family == AF_INET6) { + if (addr_len < SIN6_LEN_RFC2133) + return err; + if (((struct sockaddr_in6 *)addr)->sin6_port != htons(0)) + return err; + /* ipv6_bpf_stub cannot be NULL, since it's called from + * bpf_cgroup_inet6_connect hook and ipv6 is already loaded + */ + return ipv6_bpf_stub->inet6_bind(sk, addr, addr_len, true, false); +#endif /* CONFIG_IPV6 */ + } +#endif /* CONFIG_INET */ + + return -EAFNOSUPPORT; +} + +static const struct bpf_func_proto bpf_bind_proto = { + .func = bpf_bind, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_MEM, + .arg3_type = ARG_CONST_SIZE, +}; + static const struct bpf_func_proto * bpf_base_func_proto(enum bpf_func_id func_id) { @@ -3707,6 +3754,14 @@ sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) */ case BPF_FUNC_get_current_uid_gid: return &bpf_get_current_uid_gid_proto; + case BPF_FUNC_bind: + switch (prog->expected_attach_type) { + case BPF_CGROUP_INET4_CONNECT: + case BPF_CGROUP_INET6_CONNECT: + return &bpf_bind_proto; + default: + return NULL; + } default: return bpf_base_func_proto(func_id); } @@ -4213,6 +4268,7 @@ static bool sock_addr_is_valid_access(int off, int size, case bpf_ctx_range(struct bpf_sock_addr, user_ip4): switch (prog->expected_attach_type) { case BPF_CGROUP_INET4_BIND: + case BPF_CGROUP_INET4_CONNECT: break; default: return false; @@ -4221,6 +4277,7 @@ static bool sock_addr_is_valid_access(int off, int size, case bpf_ctx_range_till(struct bpf_sock_addr, user_ip6[0], user_ip6[3]): switch (prog->expected_attach_type) { case BPF_CGROUP_INET6_BIND: + case BPF_CGROUP_INET6_CONNECT: break; default: return false; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index e203a39d6988..488fe26ac8e5 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -547,12 +547,19 @@ int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { struct sock *sk = sock->sk; + int err; if (addr_len < sizeof(uaddr->sa_family)) return -EINVAL; if (uaddr->sa_family == AF_UNSPEC) return sk->sk_prot->disconnect(sk, flags); + if (BPF_CGROUP_PRE_CONNECT_ENABLED(sk)) { + err = sk->sk_prot->pre_connect(sk, uaddr, addr_len); + if (err) + return err; + } + if (!inet_sk(sk)->inet_num && inet_autobind(sk)) return -EAGAIN; return sk->sk_prot->connect(sk, uaddr, addr_len); @@ -633,6 +640,12 @@ int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, if (sk->sk_state != TCP_CLOSE) goto out; + if (BPF_CGROUP_PRE_CONNECT_ENABLED(sk)) { + err = sk->sk_prot->pre_connect(sk, uaddr, addr_len); + if (err) + goto out; + } + err = sk->sk_prot->connect(sk, uaddr, addr_len); if (err < 0) goto out; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 2c6aec2643e8..3c11d992d784 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -140,6 +140,21 @@ int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp) } EXPORT_SYMBOL_GPL(tcp_twsk_unique); +static int tcp_v4_pre_connect(struct sock *sk, struct sockaddr *uaddr, + int addr_len) +{ + /* This check is replicated from tcp_v4_connect() and intended to + * prevent BPF program called below from accessing bytes that are out + * of the bound specified by user in addr_len. + */ + if (addr_len < sizeof(struct sockaddr_in)) + return -EINVAL; + + sock_owned_by_me(sk); + + return BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr); +} + /* This will initiate an outgoing connection. */ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { @@ -2409,6 +2424,7 @@ struct proto tcp_prot = { .name = "TCP", .owner = THIS_MODULE, .close = tcp_close, + .pre_connect = tcp_v4_pre_connect, .connect = tcp_v4_connect, .disconnect = tcp_disconnect, .accept = inet_csk_accept, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 908fc02fb4f8..9c6c77fec963 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1658,6 +1658,19 @@ csum_copy_err: goto try_again; } +int udp_pre_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) +{ + /* This check is replicated from __ip4_datagram_connect() and + * intended to prevent BPF program called below from accessing bytes + * that are out of the bound specified by user in addr_len. + */ + if (addr_len < sizeof(struct sockaddr_in)) + return -EINVAL; + + return BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr); +} +EXPORT_SYMBOL(udp_pre_connect); + int __udp_disconnect(struct sock *sk, int flags) { struct inet_sock *inet = inet_sk(sk); @@ -2530,6 +2543,7 @@ struct proto udp_prot = { .name = "UDP", .owner = THIS_MODULE, .close = udp_lib_close, + .pre_connect = udp_pre_connect, .connect = ip4_datagram_connect, .disconnect = udp_disconnect, .ioctl = udp_ioctl, diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 13110bee5c14..566cec0e0a44 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -887,6 +887,10 @@ static const struct ipv6_stub ipv6_stub_impl = { .nd_tbl = &nd_tbl, }; +static const struct ipv6_bpf_stub ipv6_bpf_stub_impl = { + .inet6_bind = __inet6_bind, +}; + static int __init inet6_init(void) { struct list_head *r; @@ -1043,6 +1047,7 @@ static int __init inet6_init(void) /* ensure that ipv6 stubs are visible only after ipv6 is ready */ wmb(); ipv6_stub = &ipv6_stub_impl; + ipv6_bpf_stub = &ipv6_bpf_stub_impl; out: return err; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5425d7b100ee..6469b741cf5a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -117,6 +117,21 @@ static u32 tcp_v6_init_ts_off(const struct net *net, const struct sk_buff *skb) ipv6_hdr(skb)->saddr.s6_addr32); } +static int tcp_v6_pre_connect(struct sock *sk, struct sockaddr *uaddr, + int addr_len) +{ + /* This check is replicated from tcp_v6_connect() and intended to + * prevent BPF program called below from accessing bytes that are out + * of the bound specified by user in addr_len. + */ + if (addr_len < SIN6_LEN_RFC2133) + return -EINVAL; + + sock_owned_by_me(sk); + + return BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr); +} + static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { @@ -1925,6 +1940,7 @@ struct proto tcpv6_prot = { .name = "TCPv6", .owner = THIS_MODULE, .close = tcp_close, + .pre_connect = tcp_v6_pre_connect, .connect = tcp_v6_connect, .disconnect = tcp_disconnect, .accept = inet_csk_accept, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index ad30f5e31969..6861ed479469 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -957,6 +957,25 @@ static void udp_v6_flush_pending_frames(struct sock *sk) } } +static int udpv6_pre_connect(struct sock *sk, struct sockaddr *uaddr, + int addr_len) +{ + /* The following checks are replicated from __ip6_datagram_connect() + * and intended to prevent BPF program called below from accessing + * bytes that are out of the bound specified by user in addr_len. + */ + if (uaddr->sa_family == AF_INET) { + if (__ipv6_only_sock(sk)) + return -EAFNOSUPPORT; + return udp_pre_connect(sk, uaddr, addr_len); + } + + if (addr_len < SIN6_LEN_RFC2133) + return -EINVAL; + + return BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK(sk, uaddr); +} + /** * udp6_hwcsum_outgoing - handle outgoing HW checksumming * @sk: socket we are sending on @@ -1512,6 +1531,7 @@ struct proto udpv6_prot = { .name = "UDPv6", .owner = THIS_MODULE, .close = udp_lib_close, + .pre_connect = udpv6_pre_connect, .connect = ip6_datagram_connect, .disconnect = udp_disconnect, .ioctl = udp_ioctl, -- cgit v1.2.3 From aac3fc320d9404f2665a8b1249dc3170d5fa3caf Mon Sep 17 00:00:00 2001 From: Andrey Ignatov Date: Fri, 30 Mar 2018 15:08:07 -0700 Subject: bpf: Post-hooks for sys_bind "Post-hooks" are hooks that are called right before returning from sys_bind. At this time IP and port are already allocated and no further changes to `struct sock` can happen before returning from sys_bind but BPF program has a chance to inspect the socket and change sys_bind result. Specifically it can e.g. inspect what port was allocated and if it doesn't satisfy some policy, BPF program can force sys_bind to fail and return EPERM to user. Another example of usage is recording the IP:port pair to some map to use it in later calls to sys_connect. E.g. if some TCP server inside cgroup was bound to some IP:port_n, it can be recorded to a map. And later when some TCP client inside same cgroup is trying to connect to 127.0.0.1:port_n, BPF hook for sys_connect can override the destination and connect application to IP:port_n instead of 127.0.0.1:port_n. That helps forcing all applications inside a cgroup to use desired IP and not break those applications if they e.g. use localhost to communicate between each other. == Implementation details == Post-hooks are implemented as two new attach types `BPF_CGROUP_INET4_POST_BIND` and `BPF_CGROUP_INET6_POST_BIND` for existing prog type `BPF_PROG_TYPE_CGROUP_SOCK`. Separate attach types for IPv4 and IPv6 are introduced to avoid access to IPv6 field in `struct sock` from `inet_bind()` and to IPv4 field from `inet6_bind()` since those fields might not make sense in such cases. Signed-off-by: Andrey Ignatov Signed-off-by: Alexei Starovoitov Signed-off-by: Daniel Borkmann --- include/linux/bpf-cgroup.h | 16 +++++-- include/uapi/linux/bpf.h | 11 +++++ kernel/bpf/syscall.c | 43 +++++++++++++++++ net/core/filter.c | 116 +++++++++++++++++++++++++++++++++++++++------ net/ipv4/af_inet.c | 18 ++++--- net/ipv6/af_inet6.c | 21 +++++--- 6 files changed, 195 insertions(+), 30 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index c6ab295e6dcb..30d15e64b993 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -98,16 +98,24 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, __ret; \ }) -#define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) \ +#define BPF_CGROUP_RUN_SK_PROG(sk, type) \ ({ \ int __ret = 0; \ if (cgroup_bpf_enabled) { \ - __ret = __cgroup_bpf_run_filter_sk(sk, \ - BPF_CGROUP_INET_SOCK_CREATE); \ + __ret = __cgroup_bpf_run_filter_sk(sk, type); \ } \ __ret; \ }) +#define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) \ + BPF_CGROUP_RUN_SK_PROG(sk, BPF_CGROUP_INET_SOCK_CREATE) + +#define BPF_CGROUP_RUN_PROG_INET4_POST_BIND(sk) \ + BPF_CGROUP_RUN_SK_PROG(sk, BPF_CGROUP_INET4_POST_BIND) + +#define BPF_CGROUP_RUN_PROG_INET6_POST_BIND(sk) \ + BPF_CGROUP_RUN_SK_PROG(sk, BPF_CGROUP_INET6_POST_BIND) + #define BPF_CGROUP_RUN_SA_PROG(sk, uaddr, type) \ ({ \ int __ret = 0; \ @@ -183,6 +191,8 @@ static inline int cgroup_bpf_inherit(struct cgroup *cgrp) { return 0; } #define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET4_BIND(sk, uaddr) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET6_BIND(sk, uaddr) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_INET4_POST_BIND(sk) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_INET6_POST_BIND(sk) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr) ({ 0; }) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 77afaf1ba556..c5ec89732a8d 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -152,6 +152,8 @@ enum bpf_attach_type { BPF_CGROUP_INET6_BIND, BPF_CGROUP_INET4_CONNECT, BPF_CGROUP_INET6_CONNECT, + BPF_CGROUP_INET4_POST_BIND, + BPF_CGROUP_INET6_POST_BIND, __MAX_BPF_ATTACH_TYPE }; @@ -948,6 +950,15 @@ struct bpf_sock { __u32 protocol; __u32 mark; __u32 priority; + __u32 src_ip4; /* Allows 1,2,4-byte read. + * Stored in network byte order. + */ + __u32 src_ip6[4]; /* Allows 1,2,4-byte read. + * Stored in network byte order. + */ + __u32 src_port; /* Allows 4-byte read. + * Stored in host byte order + */ }; #define XDP_PACKET_HEADROOM 256 diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index cf1b29bc0ab8..0244973ee544 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1171,11 +1171,46 @@ struct bpf_prog *bpf_prog_get_type_dev(u32 ufd, enum bpf_prog_type type, } EXPORT_SYMBOL_GPL(bpf_prog_get_type_dev); +/* Initially all BPF programs could be loaded w/o specifying + * expected_attach_type. Later for some of them specifying expected_attach_type + * at load time became required so that program could be validated properly. + * Programs of types that are allowed to be loaded both w/ and w/o (for + * backward compatibility) expected_attach_type, should have the default attach + * type assigned to expected_attach_type for the latter case, so that it can be + * validated later at attach time. + * + * bpf_prog_load_fixup_attach_type() sets expected_attach_type in @attr if + * prog type requires it but has some attach types that have to be backward + * compatible. + */ +static void bpf_prog_load_fixup_attach_type(union bpf_attr *attr) +{ + switch (attr->prog_type) { + case BPF_PROG_TYPE_CGROUP_SOCK: + /* Unfortunately BPF_ATTACH_TYPE_UNSPEC enumeration doesn't + * exist so checking for non-zero is the way to go here. + */ + if (!attr->expected_attach_type) + attr->expected_attach_type = + BPF_CGROUP_INET_SOCK_CREATE; + break; + } +} + static int bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type, enum bpf_attach_type expected_attach_type) { switch (prog_type) { + case BPF_PROG_TYPE_CGROUP_SOCK: + switch (expected_attach_type) { + case BPF_CGROUP_INET_SOCK_CREATE: + case BPF_CGROUP_INET4_POST_BIND: + case BPF_CGROUP_INET6_POST_BIND: + return 0; + default: + return -EINVAL; + } case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: switch (expected_attach_type) { case BPF_CGROUP_INET4_BIND: @@ -1195,6 +1230,7 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, enum bpf_attach_type attach_type) { switch (prog->type) { + case BPF_PROG_TYPE_CGROUP_SOCK: case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: return attach_type == prog->expected_attach_type ? 0 : -EINVAL; default: @@ -1240,6 +1276,7 @@ static int bpf_prog_load(union bpf_attr *attr) !capable(CAP_SYS_ADMIN)) return -EPERM; + bpf_prog_load_fixup_attach_type(attr); if (bpf_prog_load_check_attach_type(type, attr->expected_attach_type)) return -EINVAL; @@ -1489,6 +1526,8 @@ static int bpf_prog_attach(const union bpf_attr *attr) ptype = BPF_PROG_TYPE_CGROUP_SKB; break; case BPF_CGROUP_INET_SOCK_CREATE: + case BPF_CGROUP_INET4_POST_BIND: + case BPF_CGROUP_INET6_POST_BIND: ptype = BPF_PROG_TYPE_CGROUP_SOCK; break; case BPF_CGROUP_INET4_BIND: @@ -1557,6 +1596,8 @@ static int bpf_prog_detach(const union bpf_attr *attr) ptype = BPF_PROG_TYPE_CGROUP_SKB; break; case BPF_CGROUP_INET_SOCK_CREATE: + case BPF_CGROUP_INET4_POST_BIND: + case BPF_CGROUP_INET6_POST_BIND: ptype = BPF_PROG_TYPE_CGROUP_SOCK; break; case BPF_CGROUP_INET4_BIND: @@ -1616,6 +1657,8 @@ static int bpf_prog_query(const union bpf_attr *attr, case BPF_CGROUP_INET_SOCK_CREATE: case BPF_CGROUP_INET4_BIND: case BPF_CGROUP_INET6_BIND: + case BPF_CGROUP_INET4_POST_BIND: + case BPF_CGROUP_INET6_POST_BIND: case BPF_CGROUP_INET4_CONNECT: case BPF_CGROUP_INET6_CONNECT: case BPF_CGROUP_SOCK_OPS: diff --git a/net/core/filter.c b/net/core/filter.c index bdb9cadd4d27..d31aff93270d 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -4097,30 +4097,80 @@ static bool lwt_is_valid_access(int off, int size, return bpf_skb_is_valid_access(off, size, type, prog, info); } -static bool sock_filter_is_valid_access(int off, int size, - enum bpf_access_type type, - const struct bpf_prog *prog, - struct bpf_insn_access_aux *info) + +/* Attach type specific accesses */ +static bool __sock_filter_check_attach_type(int off, + enum bpf_access_type access_type, + enum bpf_attach_type attach_type) { - if (type == BPF_WRITE) { - switch (off) { - case offsetof(struct bpf_sock, bound_dev_if): - case offsetof(struct bpf_sock, mark): - case offsetof(struct bpf_sock, priority): - break; + switch (off) { + case offsetof(struct bpf_sock, bound_dev_if): + case offsetof(struct bpf_sock, mark): + case offsetof(struct bpf_sock, priority): + switch (attach_type) { + case BPF_CGROUP_INET_SOCK_CREATE: + goto full_access; + default: + return false; + } + case bpf_ctx_range(struct bpf_sock, src_ip4): + switch (attach_type) { + case BPF_CGROUP_INET4_POST_BIND: + goto read_only; + default: + return false; + } + case bpf_ctx_range_till(struct bpf_sock, src_ip6[0], src_ip6[3]): + switch (attach_type) { + case BPF_CGROUP_INET6_POST_BIND: + goto read_only; + default: + return false; + } + case bpf_ctx_range(struct bpf_sock, src_port): + switch (attach_type) { + case BPF_CGROUP_INET4_POST_BIND: + case BPF_CGROUP_INET6_POST_BIND: + goto read_only; default: return false; } } +read_only: + return access_type == BPF_READ; +full_access: + return true; +} + +static bool __sock_filter_check_size(int off, int size, + struct bpf_insn_access_aux *info) +{ + const int size_default = sizeof(__u32); - if (off < 0 || off + size > sizeof(struct bpf_sock)) + switch (off) { + case bpf_ctx_range(struct bpf_sock, src_ip4): + case bpf_ctx_range_till(struct bpf_sock, src_ip6[0], src_ip6[3]): + bpf_ctx_record_field_size(info, size_default); + return bpf_ctx_narrow_access_ok(off, size, size_default); + } + + return size == size_default; +} + +static bool sock_filter_is_valid_access(int off, int size, + enum bpf_access_type type, + const struct bpf_prog *prog, + struct bpf_insn_access_aux *info) +{ + if (off < 0 || off >= sizeof(struct bpf_sock)) return false; - /* The verifier guarantees that size > 0. */ if (off % size != 0) return false; - if (size != sizeof(__u32)) + if (!__sock_filter_check_attach_type(off, type, + prog->expected_attach_type)) + return false; + if (!__sock_filter_check_size(off, size, info)) return false; - return true; } @@ -4728,6 +4778,7 @@ static u32 sock_filter_convert_ctx_access(enum bpf_access_type type, struct bpf_prog *prog, u32 *target_size) { struct bpf_insn *insn = insn_buf; + int off; switch (si->off) { case offsetof(struct bpf_sock, bound_dev_if): @@ -4783,6 +4834,43 @@ static u32 sock_filter_convert_ctx_access(enum bpf_access_type type, *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, SK_FL_PROTO_MASK); *insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, SK_FL_PROTO_SHIFT); break; + + case offsetof(struct bpf_sock, src_ip4): + *insn++ = BPF_LDX_MEM( + BPF_SIZE(si->code), si->dst_reg, si->src_reg, + bpf_target_off(struct sock_common, skc_rcv_saddr, + FIELD_SIZEOF(struct sock_common, + skc_rcv_saddr), + target_size)); + break; + + case bpf_ctx_range_till(struct bpf_sock, src_ip6[0], src_ip6[3]): +#if IS_ENABLED(CONFIG_IPV6) + off = si->off; + off -= offsetof(struct bpf_sock, src_ip6[0]); + *insn++ = BPF_LDX_MEM( + BPF_SIZE(si->code), si->dst_reg, si->src_reg, + bpf_target_off( + struct sock_common, + skc_v6_rcv_saddr.s6_addr32[0], + FIELD_SIZEOF(struct sock_common, + skc_v6_rcv_saddr.s6_addr32[0]), + target_size) + off); +#else + (void)off; + *insn++ = BPF_MOV32_IMM(si->dst_reg, 0); +#endif + break; + + case offsetof(struct bpf_sock, src_port): + *insn++ = BPF_LDX_MEM( + BPF_FIELD_SIZEOF(struct sock_common, skc_num), + si->dst_reg, si->src_reg, + bpf_target_off(struct sock_common, skc_num, + FIELD_SIZEOF(struct sock_common, + skc_num), + target_size)); + break; } return insn - insn_buf; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 488fe26ac8e5..142d4c35b493 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -519,12 +519,18 @@ int __inet_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len, inet->inet_saddr = 0; /* Use device */ /* Make sure we are allowed to bind here. */ - if ((snum || !(inet->bind_address_no_port || - force_bind_address_no_port)) && - sk->sk_prot->get_port(sk, snum)) { - inet->inet_saddr = inet->inet_rcv_saddr = 0; - err = -EADDRINUSE; - goto out_release_sock; + if (snum || !(inet->bind_address_no_port || + force_bind_address_no_port)) { + if (sk->sk_prot->get_port(sk, snum)) { + inet->inet_saddr = inet->inet_rcv_saddr = 0; + err = -EADDRINUSE; + goto out_release_sock; + } + err = BPF_CGROUP_RUN_PROG_INET4_POST_BIND(sk); + if (err) { + inet->inet_saddr = inet->inet_rcv_saddr = 0; + goto out_release_sock; + } } if (inet->inet_rcv_saddr) diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 566cec0e0a44..41f50472679d 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -412,13 +412,20 @@ int __inet6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len, sk->sk_ipv6only = 1; /* Make sure we are allowed to bind here. */ - if ((snum || !(inet->bind_address_no_port || - force_bind_address_no_port)) && - sk->sk_prot->get_port(sk, snum)) { - sk->sk_ipv6only = saved_ipv6only; - inet_reset_saddr(sk); - err = -EADDRINUSE; - goto out; + if (snum || !(inet->bind_address_no_port || + force_bind_address_no_port)) { + if (sk->sk_prot->get_port(sk, snum)) { + sk->sk_ipv6only = saved_ipv6only; + inet_reset_saddr(sk); + err = -EADDRINUSE; + goto out; + } + err = BPF_CGROUP_RUN_PROG_INET6_POST_BIND(sk); + if (err) { + sk->sk_ipv6only = saved_ipv6only; + inet_reset_saddr(sk); + goto out; + } } if (addr_type != IPV6_ADDR_ANY) -- cgit v1.2.3 From 7a74d39cc2927302bc236397c1fdb1fe5be209ce Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Thu, 29 Mar 2018 23:20:44 +0200 Subject: tipc: tipc: rename address types in user api The three address type structs in the user API have names that in reality reflect the specific, non-Linux environment where they were originally created. We now give them more intuitive names, in accordance with how TIPC is described in the current documentation. struct tipc_portid -> struct tipc_socket_addr struct tipc_name -> struct tipc_service_addr struct tipc_name_seq -> struct tipc_service_range To avoid confusion, we also update some commmets and macro names to match the new terminology. For compatibility, we add macros that map all old names to the new ones. Signed-off-by: Jon Maloy Signed-off-by: David S. Miller --- include/uapi/linux/tipc.h | 57 +++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 24 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/tipc.h b/include/uapi/linux/tipc.h index 4ac9f1f02b06..156224ac3d74 100644 --- a/include/uapi/linux/tipc.h +++ b/include/uapi/linux/tipc.h @@ -45,33 +45,33 @@ * TIPC addressing primitives */ -struct tipc_portid { +struct tipc_socket_addr { __u32 ref; __u32 node; }; -struct tipc_name { +struct tipc_service_addr { __u32 type; __u32 instance; }; -struct tipc_name_seq { +struct tipc_service_range { __u32 type; __u32 lower; __u32 upper; }; /* - * Application-accessible port name types + * Application-accessible service types */ -#define TIPC_CFG_SRV 0 /* configuration service name type */ -#define TIPC_TOP_SRV 1 /* topology service name type */ -#define TIPC_LINK_STATE 2 /* link state name type */ -#define TIPC_RESERVED_TYPES 64 /* lowest user-publishable name type */ +#define TIPC_NODE_STATE 0 /* node state service type */ +#define TIPC_TOP_SRV 1 /* topology server service type */ +#define TIPC_LINK_STATE 2 /* link state service type */ +#define TIPC_RESERVED_TYPES 64 /* lowest user-allowed service type */ /* - * Publication scopes when binding port names and port name sequences + * Publication scopes when binding service / service range */ enum tipc_scope { TIPC_CLUSTER_SCOPE = 2, /* 0 can also be used */ @@ -108,28 +108,28 @@ enum tipc_scope { * TIPC topology subscription service definitions */ -#define TIPC_SUB_PORTS 0x01 /* filter for port availability */ -#define TIPC_SUB_SERVICE 0x02 /* filter for service availability */ -#define TIPC_SUB_CANCEL 0x04 /* cancel a subscription */ +#define TIPC_SUB_PORTS 0x01 /* filter: evt at each match */ +#define TIPC_SUB_SERVICE 0x02 /* filter: evt at first up/last down */ +#define TIPC_SUB_CANCEL 0x04 /* filter: cancel a subscription */ #define TIPC_WAIT_FOREVER (~0) /* timeout for permanent subscription */ struct tipc_subscr { - struct tipc_name_seq seq; /* name sequence of interest */ + struct tipc_service_range seq; /* range of interest */ __u32 timeout; /* subscription duration (in ms) */ __u32 filter; /* bitmask of filter options */ char usr_handle[8]; /* available for subscriber use */ }; #define TIPC_PUBLISHED 1 /* publication event */ -#define TIPC_WITHDRAWN 2 /* withdraw event */ +#define TIPC_WITHDRAWN 2 /* withdrawal event */ #define TIPC_SUBSCR_TIMEOUT 3 /* subscription timeout event */ struct tipc_event { __u32 event; /* event type */ - __u32 found_lower; /* matching name seq instances */ - __u32 found_upper; /* " " " " */ - struct tipc_portid port; /* associated port */ + __u32 found_lower; /* matching range */ + __u32 found_upper; /* " " */ + struct tipc_socket_addr port; /* associated socket */ struct tipc_subscr s; /* associated subscription */ }; @@ -149,20 +149,20 @@ struct tipc_event { #define SOL_TIPC 271 #endif -#define TIPC_ADDR_NAMESEQ 1 -#define TIPC_ADDR_MCAST 1 -#define TIPC_ADDR_NAME 2 -#define TIPC_ADDR_ID 3 +#define TIPC_ADDR_MCAST 1 +#define TIPC_SERVICE_RANGE 1 +#define TIPC_SERVICE_ADDR 2 +#define TIPC_SOCKET_ADDR 3 struct sockaddr_tipc { unsigned short family; unsigned char addrtype; signed char scope; union { - struct tipc_portid id; - struct tipc_name_seq nameseq; + struct tipc_socket_addr id; + struct tipc_service_range nameseq; struct { - struct tipc_name name; + struct tipc_service_addr name; __u32 domain; } name; } addr; @@ -230,8 +230,13 @@ struct tipc_sioc_ln_req { /* The macros and functions below are deprecated: */ +#define TIPC_CFG_SRV 0 #define TIPC_ZONE_SCOPE 1 +#define TIPC_ADDR_NAMESEQ 1 +#define TIPC_ADDR_NAME 2 +#define TIPC_ADDR_ID 3 + #define TIPC_NODE_BITS 12 #define TIPC_CLUSTER_BITS 12 #define TIPC_ZONE_BITS 8 @@ -250,6 +255,10 @@ struct tipc_sioc_ln_req { #define TIPC_ZONE_CLUSTER_MASK (TIPC_ZONE_MASK | TIPC_CLUSTER_MASK) +#define tipc_portid tipc_socket_addr +#define tipc_name tipc_service_addr +#define tipc_name_seq tipc_service_range + static inline __u32 tipc_addr(unsigned int zone, unsigned int cluster, unsigned int node) -- cgit v1.2.3 From 7494cfa6d36d1556f17baa012dd93833620783db Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Thu, 29 Mar 2018 23:20:45 +0200 Subject: tipc: avoid possible string overflow gcc points out that the combined length of the fixed-length inputs to l->name is larger than the destination buffer size: net/tipc/link.c: In function 'tipc_link_create': net/tipc/link.c:465:26: error: '%s' directive writing up to 32 bytes into a region of size between 26 and 58 [-Werror=format-overflow=] sprintf(l->name, "%s:%s-%s:unknown", self_str, if_name, peer_str); net/tipc/link.c:465:2: note: 'sprintf' output 11 or more bytes (assuming 75) into a destination of size 60 sprintf(l->name, "%s:%s-%s:unknown", self_str, if_name, peer_str); A detailed analysis reveals that the theoretical maximum length of a link name is: max self_str + 1 + max if_name + 1 + max peer_str + 1 + max if_name = 16 + 1 + 15 + 1 + 16 + 1 + 15 = 65 Since we also need space for a trailing zero we now set MAX_LINK_NAME to 68. Just to be on the safe side we also replace the sprintf() call with snprintf(). Fixes: 25b0b9c4e835 ("tipc: handle collisions of 32-bit node address hash values") Reported-by: Arnd Bergmann Signed-off-by: Jon Maloy Signed-off-by: David S. Miller --- include/uapi/linux/tipc.h | 2 +- net/tipc/link.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/tipc.h b/include/uapi/linux/tipc.h index 156224ac3d74..bf6d28677cfe 100644 --- a/include/uapi/linux/tipc.h +++ b/include/uapi/linux/tipc.h @@ -216,7 +216,7 @@ struct tipc_group_req { #define TIPC_MAX_MEDIA_NAME 16 #define TIPC_MAX_IF_NAME 16 #define TIPC_MAX_BEARER_NAME 32 -#define TIPC_MAX_LINK_NAME 60 +#define TIPC_MAX_LINK_NAME 68 #define SIOCGETLINKNAME SIOCPROTOPRIVATE diff --git a/net/tipc/link.c b/net/tipc/link.c index 8f2a9496439b..695acb783969 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -462,7 +462,8 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id, sprintf(peer_str, "%x", peer); } /* Peer i/f name will be completed by reset/activate message */ - sprintf(l->name, "%s:%s-%s:unknown", self_str, if_name, peer_str); + snprintf(l->name, sizeof(l->name), "%s:%s-%s:unknown", + self_str, if_name, peer_str); strcpy(l->if_name, if_name); l->addr = peer; -- cgit v1.2.3 From 1eb5fa849f2bf9186a618e85bea23f02e527540a Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 28 Feb 2018 15:59:59 -0500 Subject: dm: allow targets to return output from messages they are sent Could be useful for a target to return stats or other information. If a target does DMEMIT() anything to @result from its .message method then it must return 1 to the caller. Signed-off-By: Mike Snitzer --- drivers/md/dm-cache-target.c | 3 ++- drivers/md/dm-crypt.c | 3 ++- drivers/md/dm-era-target.c | 3 ++- drivers/md/dm-ioctl.c | 2 +- drivers/md/dm-log-writes.c | 3 ++- drivers/md/dm-mpath.c | 3 ++- drivers/md/dm-raid.c | 3 ++- drivers/md/dm-switch.c | 3 ++- drivers/md/dm-thin.c | 3 ++- include/linux/device-mapper.h | 3 ++- include/uapi/linux/dm-ioctl.h | 4 ++-- 11 files changed, 21 insertions(+), 12 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 47407e43b96a..da208638fba4 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -3387,7 +3387,8 @@ static int process_invalidate_cblocks_message(struct cache *cache, unsigned coun * * The key migration_threshold is supported by the cache target core. */ -static int cache_message(struct dm_target *ti, unsigned argc, char **argv) +static int cache_message(struct dm_target *ti, unsigned argc, char **argv, + char *result, unsigned maxlen) { struct cache *cache = ti->private; diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 8168f737590e..dd9b5332b39c 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -2942,7 +2942,8 @@ static void crypt_resume(struct dm_target *ti) * key set * key wipe */ -static int crypt_message(struct dm_target *ti, unsigned argc, char **argv) +static int crypt_message(struct dm_target *ti, unsigned argc, char **argv, + char *result, unsigned maxlen) { struct crypt_config *cc = ti->private; int key_size, ret = -EINVAL; diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 73a5c198113a..8e48920a3ffa 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -1635,7 +1635,8 @@ err: DMEMIT("Error"); } -static int era_message(struct dm_target *ti, unsigned argc, char **argv) +static int era_message(struct dm_target *ti, unsigned argc, char **argv, + char *result, unsigned maxlen) { struct era *era = ti->private; diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index a89fd8f44453..5acf77de5945 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1595,7 +1595,7 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para DMWARN("Target message sector outside device."); r = -EINVAL; } else if (ti->type->message) - r = ti->type->message(ti, argc, argv); + r = ti->type->message(ti, argc, argv, result, maxlen); else { DMWARN("Target type does not support messages"); r = -EINVAL; diff --git a/drivers/md/dm-log-writes.c b/drivers/md/dm-log-writes.c index 3362d866793b..e4c015dfef43 100644 --- a/drivers/md/dm-log-writes.c +++ b/drivers/md/dm-log-writes.c @@ -887,7 +887,8 @@ static int log_writes_iterate_devices(struct dm_target *ti, * Messages supported: * mark - specify the marked data. */ -static int log_writes_message(struct dm_target *ti, unsigned argc, char **argv) +static int log_writes_message(struct dm_target *ti, unsigned argc, char **argv, + char *result, unsigned maxlen) { int r = -EINVAL; struct log_writes_c *lc = ti->private; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index a6b7baf31cdd..1e60ec44942e 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1811,7 +1811,8 @@ static void multipath_status(struct dm_target *ti, status_type_t type, spin_unlock_irqrestore(&m->lock, flags); } -static int multipath_message(struct dm_target *ti, unsigned argc, char **argv) +static int multipath_message(struct dm_target *ti, unsigned argc, char **argv, + char *result, unsigned maxlen) { int r = -EINVAL; struct dm_dev *dev; diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index c1d1034ff7b7..b7a9c710ebec 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -3663,7 +3663,8 @@ static void raid_status(struct dm_target *ti, status_type_t type, } } -static int raid_message(struct dm_target *ti, unsigned int argc, char **argv) +static int raid_message(struct dm_target *ti, unsigned int argc, char **argv, + char *result, unsigned maxlen) { struct raid_set *rs = ti->private; struct mddev *mddev = &rs->md; diff --git a/drivers/md/dm-switch.c b/drivers/md/dm-switch.c index 8d0ba879777e..8f9208c2d2e3 100644 --- a/drivers/md/dm-switch.c +++ b/drivers/md/dm-switch.c @@ -466,7 +466,8 @@ static int process_set_region_mappings(struct switch_ctx *sctx, * * Only set_region_mappings is supported. */ -static int switch_message(struct dm_target *ti, unsigned argc, char **argv) +static int switch_message(struct dm_target *ti, unsigned argc, char **argv, + char *result, unsigned maxlen) { static DEFINE_MUTEX(message_mutex); diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 629c555890c1..b11107497d2e 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -3705,7 +3705,8 @@ static int process_release_metadata_snap_mesg(unsigned argc, char **argv, struct * reserve_metadata_snap * release_metadata_snap */ -static int pool_message(struct dm_target *ti, unsigned argc, char **argv) +static int pool_message(struct dm_target *ti, unsigned argc, char **argv, + char *result, unsigned maxlen) { int r = -EINVAL; struct pool_c *pt = ti->private; diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index da83f64952e7..1e2426c18eb4 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -87,7 +87,8 @@ typedef void (*dm_resume_fn) (struct dm_target *ti); typedef void (*dm_status_fn) (struct dm_target *ti, status_type_t status_type, unsigned status_flags, char *result, unsigned maxlen); -typedef int (*dm_message_fn) (struct dm_target *ti, unsigned argc, char **argv); +typedef int (*dm_message_fn) (struct dm_target *ti, unsigned argc, char **argv, + char *result, unsigned maxlen); typedef int (*dm_prepare_ioctl_fn) (struct dm_target *ti, struct block_device **bdev, fmode_t *mode); diff --git a/include/uapi/linux/dm-ioctl.h b/include/uapi/linux/dm-ioctl.h index 14c44ec8b622..5108da02cd32 100644 --- a/include/uapi/linux/dm-ioctl.h +++ b/include/uapi/linux/dm-ioctl.h @@ -270,9 +270,9 @@ enum { #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) #define DM_VERSION_MAJOR 4 -#define DM_VERSION_MINOR 37 +#define DM_VERSION_MINOR 38 #define DM_VERSION_PATCHLEVEL 0 -#define DM_VERSION_EXTRA "-ioctl (2017-09-20)" +#define DM_VERSION_EXTRA "-ioctl (2018-02-28)" /* Status bits */ #define DM_READONLY_FLAG (1 << 0) /* In/Out */ -- cgit v1.2.3 From 971888c46993f871f20d02d1fe43486a924fad11 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Tue, 3 Apr 2018 15:05:12 -0400 Subject: dm: hold DM table for duration of ioctl rather than use blkdev_get Commit 519049afead ("dm: use blkdev_get rather than bdgrab when issuing pass-through ioctl") inadvertantly introduced a regression relative to users of device cgroups that issue ioctls (e.g. libvirt). Using blkdev_get() in DM's passthrough ioctl support implicitly introduced a cgroup permissions check that would fail unless care were taken to add all devices in the IO stack to the device cgroup. E.g. rather than just adding the top-level DM multipath device to the cgroup all the underlying devices would need to be allowed. Fix this, to no longer require allowing all underlying devices, by simply holding the live DM table (which includes the table's original blkdev_get() reference on the blockdevice that the ioctl will be issued to) for the duration of the ioctl. Also, bump the DM ioctl version so a user can know that their device cgroup allow workaround is no longer needed. Reported-by: Michal Privoznik Suggested-by: Mikulas Patocka Fixes: 519049afead ("dm: use blkdev_get rather than bdgrab when issuing pass-through ioctl") Cc: stable@vger.kernel.org # 4.16 Signed-off-by: Mike Snitzer --- drivers/md/dm.c | 97 ++++++++++++++++++++----------------------- include/uapi/linux/dm-ioctl.h | 4 +- 2 files changed, 46 insertions(+), 55 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 3b3cbd1a1659..d4438188d088 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -458,67 +458,56 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) return dm_get_geometry(md, geo); } -static char *_dm_claim_ptr = "I belong to device-mapper"; - -static int dm_get_bdev_for_ioctl(struct mapped_device *md, - struct block_device **bdev, - fmode_t *mode) +static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx, + struct block_device **bdev, fmode_t *mode) + __acquires(md->io_barrier) { struct dm_target *tgt; struct dm_table *map; - int srcu_idx, r, r2; + int r; retry: r = -ENOTTY; - map = dm_get_live_table(md, &srcu_idx); + map = dm_get_live_table(md, srcu_idx); if (!map || !dm_table_get_size(map)) - goto out; + return r; /* We only support devices that have a single target */ if (dm_table_get_num_targets(map) != 1) - goto out; + return r; tgt = dm_table_get_target(map, 0); if (!tgt->type->prepare_ioctl) - goto out; + return r; - if (dm_suspended_md(md)) { - r = -EAGAIN; - goto out; - } + if (dm_suspended_md(md)) + return -EAGAIN; r = tgt->type->prepare_ioctl(tgt, bdev, mode); - if (r < 0) - goto out; - - bdgrab(*bdev); - r2 = blkdev_get(*bdev, *mode, _dm_claim_ptr); - if (r2 < 0) { - r = r2; - goto out; - } - - dm_put_live_table(md, srcu_idx); - return r; - -out: - dm_put_live_table(md, srcu_idx); if (r == -ENOTCONN && !fatal_signal_pending(current)) { + dm_put_live_table(md, *srcu_idx); msleep(10); goto retry; } + return r; } +static void dm_unprepare_ioctl(struct mapped_device *md, int srcu_idx) + __releases(md->io_barrier) +{ + dm_put_live_table(md, srcu_idx); +} + static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { struct mapped_device *md = bdev->bd_disk->private_data; - int r; + int r, srcu_idx; - r = dm_get_bdev_for_ioctl(md, &bdev, &mode); + r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode); if (r < 0) - return r; + goto out; if (r > 0) { /* @@ -536,7 +525,7 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode, r = __blkdev_driver_ioctl(bdev, mode, cmd, arg); out: - blkdev_put(bdev, mode); + dm_unprepare_ioctl(md, srcu_idx); return r; } @@ -710,6 +699,8 @@ static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU) rcu_read_unlock(); } +static char *_dm_claim_ptr = "I belong to device-mapper"; + /* * Open a table device so we can use it as a map destination. */ @@ -3044,19 +3035,19 @@ static int dm_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type, struct mapped_device *md = bdev->bd_disk->private_data; const struct pr_ops *ops; fmode_t mode; - int r; + int r, srcu_idx; - r = dm_get_bdev_for_ioctl(md, &bdev, &mode); + r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode); if (r < 0) - return r; + goto out; ops = bdev->bd_disk->fops->pr_ops; if (ops && ops->pr_reserve) r = ops->pr_reserve(bdev, key, type, flags); else r = -EOPNOTSUPP; - - blkdev_put(bdev, mode); +out: + dm_unprepare_ioctl(md, srcu_idx); return r; } @@ -3065,19 +3056,19 @@ static int dm_pr_release(struct block_device *bdev, u64 key, enum pr_type type) struct mapped_device *md = bdev->bd_disk->private_data; const struct pr_ops *ops; fmode_t mode; - int r; + int r, srcu_idx; - r = dm_get_bdev_for_ioctl(md, &bdev, &mode); + r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode); if (r < 0) - return r; + goto out; ops = bdev->bd_disk->fops->pr_ops; if (ops && ops->pr_release) r = ops->pr_release(bdev, key, type); else r = -EOPNOTSUPP; - - blkdev_put(bdev, mode); +out: + dm_unprepare_ioctl(md, srcu_idx); return r; } @@ -3087,19 +3078,19 @@ static int dm_pr_preempt(struct block_device *bdev, u64 old_key, u64 new_key, struct mapped_device *md = bdev->bd_disk->private_data; const struct pr_ops *ops; fmode_t mode; - int r; + int r, srcu_idx; - r = dm_get_bdev_for_ioctl(md, &bdev, &mode); + r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode); if (r < 0) - return r; + goto out; ops = bdev->bd_disk->fops->pr_ops; if (ops && ops->pr_preempt) r = ops->pr_preempt(bdev, old_key, new_key, type, abort); else r = -EOPNOTSUPP; - - blkdev_put(bdev, mode); +out: + dm_unprepare_ioctl(md, srcu_idx); return r; } @@ -3108,19 +3099,19 @@ static int dm_pr_clear(struct block_device *bdev, u64 key) struct mapped_device *md = bdev->bd_disk->private_data; const struct pr_ops *ops; fmode_t mode; - int r; + int r, srcu_idx; - r = dm_get_bdev_for_ioctl(md, &bdev, &mode); + r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode); if (r < 0) - return r; + goto out; ops = bdev->bd_disk->fops->pr_ops; if (ops && ops->pr_clear) r = ops->pr_clear(bdev, key); else r = -EOPNOTSUPP; - - blkdev_put(bdev, mode); +out: + dm_unprepare_ioctl(md, srcu_idx); return r; } diff --git a/include/uapi/linux/dm-ioctl.h b/include/uapi/linux/dm-ioctl.h index 5108da02cd32..d1e49514977b 100644 --- a/include/uapi/linux/dm-ioctl.h +++ b/include/uapi/linux/dm-ioctl.h @@ -270,9 +270,9 @@ enum { #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) #define DM_VERSION_MAJOR 4 -#define DM_VERSION_MINOR 38 +#define DM_VERSION_MINOR 39 #define DM_VERSION_PATCHLEVEL 0 -#define DM_VERSION_EXTRA "-ioctl (2018-02-28)" +#define DM_VERSION_EXTRA "-ioctl (2018-04-03)" /* Status bits */ #define DM_READONLY_FLAG (1 << 0) /* In/Out */ -- cgit v1.2.3 From 6c64fe7f2adcee21d7c4247f1ec021fd18428fc4 Mon Sep 17 00:00:00 2001 From: Jonathan Helman Date: Mon, 19 Mar 2018 15:14:14 -0700 Subject: virtio_balloon: export hugetlb page allocation counts Export the number of successful and failed hugetlb page allocations via the virtio balloon driver. These 2 counts come directly from the vm_events HTLB_BUDDY_PGALLOC and HTLB_BUDDY_PGALLOC_FAIL. Signed-off-by: Jonathan Helman Signed-off-by: Michael S. Tsirkin Reviewed-by: Jason Wang --- drivers/virtio/virtio_balloon.c | 6 ++++++ include/uapi/linux/virtio_balloon.h | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index dfe5684000be..6b237e3f4983 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -272,6 +272,12 @@ static unsigned int update_balloon_stats(struct virtio_balloon *vb) pages_to_bytes(events[PSWPOUT])); update_stat(vb, idx++, VIRTIO_BALLOON_S_MAJFLT, events[PGMAJFAULT]); update_stat(vb, idx++, VIRTIO_BALLOON_S_MINFLT, events[PGFAULT]); +#ifdef CONFIG_HUGETLB_PAGE + update_stat(vb, idx++, VIRTIO_BALLOON_S_HTLB_PGALLOC, + events[HTLB_BUDDY_PGALLOC]); + update_stat(vb, idx++, VIRTIO_BALLOON_S_HTLB_PGFAIL, + events[HTLB_BUDDY_PGALLOC_FAIL]); +#endif #endif update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMFREE, pages_to_bytes(i.freeram)); diff --git a/include/uapi/linux/virtio_balloon.h b/include/uapi/linux/virtio_balloon.h index 4e8b8304b793..40297a3181ed 100644 --- a/include/uapi/linux/virtio_balloon.h +++ b/include/uapi/linux/virtio_balloon.h @@ -53,7 +53,9 @@ struct virtio_balloon_config { #define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ #define VIRTIO_BALLOON_S_AVAIL 6 /* Available memory as in /proc */ #define VIRTIO_BALLOON_S_CACHES 7 /* Disk caches */ -#define VIRTIO_BALLOON_S_NR 8 +#define VIRTIO_BALLOON_S_HTLB_PGALLOC 8 /* Hugetlb page allocations */ +#define VIRTIO_BALLOON_S_HTLB_PGFAIL 9 /* Hugetlb page allocation failures */ +#define VIRTIO_BALLOON_S_NR 10 /* * Memory statistics structure. -- cgit v1.2.3 From c21a6970ae727839a2f300cd8dd957de0d0238c3 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Tue, 10 Apr 2018 16:35:23 -0700 Subject: ipc/shm: introduce shmctl(SHM_STAT_ANY) Patch series "sysvipc: introduce STAT_ANY commands", v2. The following patches adds the discussed (see [1]) new command for shm as well as for sems and msq as they are subject to the same discrepancies for ipc object permission checks between the syscall and via procfs. These new commands are justified in that (1) we are stuck with this semantics as changing syscall and procfs can break userland; and (2) some users can benefit from performance (for large amounts of shm segments, for example) from not having to parse the procfs interface. Once merged, I will submit the necesary manpage updates. But I'm thinking something like: : diff --git a/man2/shmctl.2 b/man2/shmctl.2 : index 7bb503999941..bb00bbe21a57 100644 : --- a/man2/shmctl.2 : +++ b/man2/shmctl.2 : @@ -41,6 +41,7 @@ : .\" 2005-04-25, mtk -- noted aberrant Linux behavior w.r.t. new : .\" attaches to a segment that has already been marked for deletion. : .\" 2005-08-02, mtk: Added IPC_INFO, SHM_INFO, SHM_STAT descriptions. : +.\" 2018-02-13, dbueso: Added SHM_STAT_ANY description. : .\" : .TH SHMCTL 2 2017-09-15 "Linux" "Linux Programmer's Manual" : .SH NAME : @@ -242,6 +243,18 @@ However, the : argument is not a segment identifier, but instead an index into : the kernel's internal array that maintains information about : all shared memory segments on the system. : +.TP : +.BR SHM_STAT_ANY " (Linux-specific)" : +Return a : +.I shmid_ds : +structure as for : +.BR SHM_STAT . : +However, the : +.I shm_perm.mode : +is not checked for read access for : +.IR shmid , : +resembing the behaviour of : +/proc/sysvipc/shm. : .PP : The caller can prevent or allow swapping of a shared : memory segment with the following \fIcmd\fP values: : @@ -287,7 +300,7 @@ operation returns the index of the highest used entry in the : kernel's internal array recording information about all : shared memory segments. : (This information can be used with repeated : -.B SHM_STAT : +.B SHM_STAT/SHM_STAT_ANY : operations to obtain information about all shared memory segments : on the system.) : A successful : @@ -328,7 +341,7 @@ isn't accessible. : \fIshmid\fP is not a valid identifier, or \fIcmd\fP : is not a valid command. : Or: for a : -.B SHM_STAT : +.B SHM_STAT/SHM_STAT_ANY : operation, the index value specified in : .I shmid : referred to an array slot that is currently unused. This patch (of 3): There is a permission discrepancy when consulting shm ipc object metadata between /proc/sysvipc/shm (0444) and the SHM_STAT shmctl command. The later does permission checks for the object vs S_IRUGO. As such there can be cases where EACCESS is returned via syscall but the info is displayed anyways in the procfs files. While this might have security implications via info leaking (albeit no writing to the shm metadata), this behavior goes way back and showing all the objects regardless of the permissions was most likely an overlook - so we are stuck with it. Furthermore, modifying either the syscall or the procfs file can cause userspace programs to break (ie ipcs). Some applications require getting the procfs info (without root privileges) and can be rather slow in comparison with a syscall -- up to 500x in some reported cases. This patch introduces a new SHM_STAT_ANY command such that the shm ipc object permissions are ignored, and only audited instead. In addition, I've left the lsm security hook checks in place, as if some policy can block the call, then the user has no other choice than just parsing the procfs file. [1] https://lkml.org/lkml/2017/12/19/220 Link: http://lkml.kernel.org/r/20180215162458.10059-2-dave@stgolabs.net Signed-off-by: Davidlohr Bueso Acked-by: Michal Hocko Cc: Michael Kerrisk Cc: Manfred Spraul Cc: Eric W. Biederman Cc: Kees Cook Cc: Robert Kettler Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/uapi/linux/shm.h | 5 +++-- ipc/shm.c | 23 ++++++++++++++++++----- security/selinux/hooks.c | 1 + security/smack/smack_lsm.c | 1 + 4 files changed, 23 insertions(+), 7 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/shm.h b/include/uapi/linux/shm.h index 4de12a39b075..dde1344f047c 100644 --- a/include/uapi/linux/shm.h +++ b/include/uapi/linux/shm.h @@ -83,8 +83,9 @@ struct shmid_ds { #define SHM_UNLOCK 12 /* ipcs ctl commands */ -#define SHM_STAT 13 -#define SHM_INFO 14 +#define SHM_STAT 13 +#define SHM_INFO 14 +#define SHM_STAT_ANY 15 /* Obsolete, used only for backwards compatibility */ struct shminfo { diff --git a/ipc/shm.c b/ipc/shm.c index acefe44fefef..1a28b6a96449 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -947,14 +947,14 @@ static int shmctl_stat(struct ipc_namespace *ns, int shmid, memset(tbuf, 0, sizeof(*tbuf)); rcu_read_lock(); - if (cmd == SHM_STAT) { + if (cmd == SHM_STAT || cmd == SHM_STAT_ANY) { shp = shm_obtain_object(ns, shmid); if (IS_ERR(shp)) { err = PTR_ERR(shp); goto out_unlock; } id = shp->shm_perm.id; - } else { + } else { /* IPC_STAT */ shp = shm_obtain_object_check(ns, shmid); if (IS_ERR(shp)) { err = PTR_ERR(shp); @@ -962,9 +962,20 @@ static int shmctl_stat(struct ipc_namespace *ns, int shmid, } } - err = -EACCES; - if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) - goto out_unlock; + /* + * Semantically SHM_STAT_ANY ought to be identical to + * that functionality provided by the /proc/sysvipc/ + * interface. As such, only audit these calls and + * do not do traditional S_IRUGO permission checks on + * the ipc object. + */ + if (cmd == SHM_STAT_ANY) + audit_ipc_obj(&shp->shm_perm); + else { + err = -EACCES; + if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) + goto out_unlock; + } err = security_shm_shmctl(&shp->shm_perm, cmd); if (err) @@ -1104,6 +1115,7 @@ long ksys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf) return err; } case SHM_STAT: + case SHM_STAT_ANY: case IPC_STAT: { err = shmctl_stat(ns, shmid, cmd, &sem64); if (err < 0) @@ -1282,6 +1294,7 @@ long compat_ksys_shmctl(int shmid, int cmd, void __user *uptr) return err; } case IPC_STAT: + case SHM_STAT_ANY: case SHM_STAT: err = shmctl_stat(ns, shmid, cmd, &sem64); if (err < 0) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 1eeb70e439d7..1287013f747d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6157,6 +6157,7 @@ static int selinux_shm_shmctl(struct kern_ipc_perm *shp, int cmd) SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); case IPC_STAT: case SHM_STAT: + case SHM_STAT_ANY: perms = SHM__GETATTR | SHM__ASSOCIATE; break; case IPC_SET: diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 73549007bf9e..0daab3019023 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3046,6 +3046,7 @@ static int smack_shm_shmctl(struct kern_ipc_perm *isp, int cmd) switch (cmd) { case IPC_STAT: case SHM_STAT: + case SHM_STAT_ANY: may = MAY_READ; break; case IPC_SET: -- cgit v1.2.3 From a280d6dc77eb6002f269d58cd47c7c7e69b617b6 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Tue, 10 Apr 2018 16:35:26 -0700 Subject: ipc/sem: introduce semctl(SEM_STAT_ANY) There is a permission discrepancy when consulting shm ipc object metadata between /proc/sysvipc/sem (0444) and the SEM_STAT semctl command. The later does permission checks for the object vs S_IRUGO. As such there can be cases where EACCESS is returned via syscall but the info is displayed anyways in the procfs files. While this might have security implications via info leaking (albeit no writing to the sma metadata), this behavior goes way back and showing all the objects regardless of the permissions was most likely an overlook - so we are stuck with it. Furthermore, modifying either the syscall or the procfs file can cause userspace programs to break (ie ipcs). Some applications require getting the procfs info (without root privileges) and can be rather slow in comparison with a syscall -- up to 500x in some reported cases for shm. This patch introduces a new SEM_STAT_ANY command such that the sem ipc object permissions are ignored, and only audited instead. In addition, I've left the lsm security hook checks in place, as if some policy can block the call, then the user has no other choice than just parsing the procfs file. Link: http://lkml.kernel.org/r/20180215162458.10059-3-dave@stgolabs.net Signed-off-by: Davidlohr Bueso Reported-by: Robert Kettler Cc: Eric W. Biederman Cc: Kees Cook Cc: Manfred Spraul Cc: Michael Kerrisk Cc: Michal Hocko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/uapi/linux/sem.h | 1 + ipc/sem.c | 17 ++++++++++++----- security/selinux/hooks.c | 1 + security/smack/smack_lsm.c | 1 + 4 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/sem.h b/include/uapi/linux/sem.h index 9c3e745b0656..39a1876f039e 100644 --- a/include/uapi/linux/sem.h +++ b/include/uapi/linux/sem.h @@ -19,6 +19,7 @@ /* ipcs ctl cmds */ #define SEM_STAT 18 #define SEM_INFO 19 +#define SEM_STAT_ANY 20 /* Obsolete, used only for backwards compatibility and libc5 compiles */ struct semid_ds { diff --git a/ipc/sem.c b/ipc/sem.c index 2994da8ccc7f..06be75d9217a 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1220,14 +1220,14 @@ static int semctl_stat(struct ipc_namespace *ns, int semid, memset(semid64, 0, sizeof(*semid64)); rcu_read_lock(); - if (cmd == SEM_STAT) { + if (cmd == SEM_STAT || cmd == SEM_STAT_ANY) { sma = sem_obtain_object(ns, semid); if (IS_ERR(sma)) { err = PTR_ERR(sma); goto out_unlock; } id = sma->sem_perm.id; - } else { + } else { /* IPC_STAT */ sma = sem_obtain_object_check(ns, semid); if (IS_ERR(sma)) { err = PTR_ERR(sma); @@ -1235,9 +1235,14 @@ static int semctl_stat(struct ipc_namespace *ns, int semid, } } - err = -EACCES; - if (ipcperms(ns, &sma->sem_perm, S_IRUGO)) - goto out_unlock; + /* see comment for SHM_STAT_ANY */ + if (cmd == SEM_STAT_ANY) + audit_ipc_obj(&sma->sem_perm); + else { + err = -EACCES; + if (ipcperms(ns, &sma->sem_perm, S_IRUGO)) + goto out_unlock; + } err = security_sem_semctl(&sma->sem_perm, cmd); if (err) @@ -1626,6 +1631,7 @@ long ksys_semctl(int semid, int semnum, int cmd, unsigned long arg) return semctl_info(ns, semid, cmd, p); case IPC_STAT: case SEM_STAT: + case SEM_STAT_ANY: err = semctl_stat(ns, semid, cmd, &semid64); if (err < 0) return err; @@ -1732,6 +1738,7 @@ long compat_ksys_semctl(int semid, int semnum, int cmd, int arg) return semctl_info(ns, semid, cmd, p); case IPC_STAT: case SEM_STAT: + case SEM_STAT_ANY: err = semctl_stat(ns, semid, cmd, &semid64); if (err < 0) return err; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 1287013f747d..927904d0f115 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6273,6 +6273,7 @@ static int selinux_sem_semctl(struct kern_ipc_perm *sma, int cmd) break; case IPC_STAT: case SEM_STAT: + case SEM_STAT_ANY: perms = SEM__GETATTR | SEM__ASSOCIATE; break; default: diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 0daab3019023..cb36498a5076 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3140,6 +3140,7 @@ static int smack_sem_semctl(struct kern_ipc_perm *isp, int cmd) case GETALL: case IPC_STAT: case SEM_STAT: + case SEM_STAT_ANY: may = MAY_READ; break; case SETVAL: -- cgit v1.2.3 From 23c8cec8cf679b10997a512abb1e86f0cedc42ba Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Tue, 10 Apr 2018 16:35:30 -0700 Subject: ipc/msg: introduce msgctl(MSG_STAT_ANY) There is a permission discrepancy when consulting msq ipc object metadata between /proc/sysvipc/msg (0444) and the MSG_STAT shmctl command. The later does permission checks for the object vs S_IRUGO. As such there can be cases where EACCESS is returned via syscall but the info is displayed anyways in the procfs files. While this might have security implications via info leaking (albeit no writing to the msq metadata), this behavior goes way back and showing all the objects regardless of the permissions was most likely an overlook - so we are stuck with it. Furthermore, modifying either the syscall or the procfs file can cause userspace programs to break (ie ipcs). Some applications require getting the procfs info (without root privileges) and can be rather slow in comparison with a syscall -- up to 500x in some reported cases for shm. This patch introduces a new MSG_STAT_ANY command such that the msq ipc object permissions are ignored, and only audited instead. In addition, I've left the lsm security hook checks in place, as if some policy can block the call, then the user has no other choice than just parsing the procfs file. Link: http://lkml.kernel.org/r/20180215162458.10059-4-dave@stgolabs.net Signed-off-by: Davidlohr Bueso Reported-by: Robert Kettler Cc: Eric W. Biederman Cc: Kees Cook Cc: Manfred Spraul Cc: Michael Kerrisk Cc: Michal Hocko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/uapi/linux/msg.h | 1 + ipc/msg.c | 17 ++++++++++++----- security/selinux/hooks.c | 1 + security/smack/smack_lsm.c | 1 + 4 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/msg.h b/include/uapi/linux/msg.h index 5d5ab81dc9be..e4a0d9a9a9e8 100644 --- a/include/uapi/linux/msg.h +++ b/include/uapi/linux/msg.h @@ -7,6 +7,7 @@ /* ipcs ctl commands */ #define MSG_STAT 11 #define MSG_INFO 12 +#define MSG_STAT_ANY 13 /* msgrcv options */ #define MSG_NOERROR 010000 /* no error if message is too big */ diff --git a/ipc/msg.c b/ipc/msg.c index 114a21189613..56fd1c73eedc 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -497,14 +497,14 @@ static int msgctl_stat(struct ipc_namespace *ns, int msqid, memset(p, 0, sizeof(*p)); rcu_read_lock(); - if (cmd == MSG_STAT) { + if (cmd == MSG_STAT || cmd == MSG_STAT_ANY) { msq = msq_obtain_object(ns, msqid); if (IS_ERR(msq)) { err = PTR_ERR(msq); goto out_unlock; } id = msq->q_perm.id; - } else { + } else { /* IPC_STAT */ msq = msq_obtain_object_check(ns, msqid); if (IS_ERR(msq)) { err = PTR_ERR(msq); @@ -512,9 +512,14 @@ static int msgctl_stat(struct ipc_namespace *ns, int msqid, } } - err = -EACCES; - if (ipcperms(ns, &msq->q_perm, S_IRUGO)) - goto out_unlock; + /* see comment for SHM_STAT_ANY */ + if (cmd == MSG_STAT_ANY) + audit_ipc_obj(&msq->q_perm); + else { + err = -EACCES; + if (ipcperms(ns, &msq->q_perm, S_IRUGO)) + goto out_unlock; + } err = security_msg_queue_msgctl(&msq->q_perm, cmd); if (err) @@ -572,6 +577,7 @@ long ksys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) return err; } case MSG_STAT: /* msqid is an index rather than a msg queue id */ + case MSG_STAT_ANY: case IPC_STAT: err = msgctl_stat(ns, msqid, cmd, &msqid64); if (err < 0) @@ -690,6 +696,7 @@ long compat_ksys_msgctl(int msqid, int cmd, void __user *uptr) } case IPC_STAT: case MSG_STAT: + case MSG_STAT_ANY: err = msgctl_stat(ns, msqid, cmd, &msqid64); if (err < 0) return err; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 927904d0f115..4cafe6a19167 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6006,6 +6006,7 @@ static int selinux_msg_queue_msgctl(struct kern_ipc_perm *msq, int cmd) SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); case IPC_STAT: case MSG_STAT: + case MSG_STAT_ANY: perms = MSGQ__GETATTR | MSGQ__ASSOCIATE; break; case IPC_SET: diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index cb36498a5076..0b414836bebd 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3230,6 +3230,7 @@ static int smack_msg_queue_msgctl(struct kern_ipc_perm *isp, int cmd) switch (cmd) { case IPC_STAT: case MSG_STAT: + case MSG_STAT_ANY: may = MAY_READ; break; case IPC_SET: -- cgit v1.2.3 From 2a6cc8a6c0cb44baf7df2f64e5090aaf726002c3 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 10 Apr 2018 16:36:15 -0700 Subject: linux/const.h: prefix include guard of uapi/linux/const.h with _UAPI Patch series "linux/const.h: cleanups of macros such as UL(), _BITUL(), BIT() etc", v3. ARM, ARM64, UniCore32 define UL() as a shorthand of _AC(..., UL). More architectures may introduce it in the future. UL() is arch-agnostic, and useful. So let's move it to include/linux/const.h Currently, must be included to use UL(). It pulls in more bloats just for defining some bit macros. I posted V2 one year ago. The previous posts are: https://patchwork.kernel.org/patch/9498273/ https://patchwork.kernel.org/patch/9498275/ https://patchwork.kernel.org/patch/9498269/ https://patchwork.kernel.org/patch/9498271/ At that time, what blocked this series was a comment from David Howells: You need to be very careful doing this. Some userspace stuff depends on the guard macro names on the kernel header files. (https://patchwork.kernel.org/patch/9498275/) Looking at the code closer, I noticed this is not a problem. See the following line. https://github.com/torvalds/linux/blob/v4.16-rc2/scripts/headers_install.sh#L40 scripts/headers_install.sh rips off _UAPI prefix from guard macro names. I ran "make headers_install" and confirmed the result is what I expect. So, we can prefix the include guard of include/uapi/linux/const.h, and add a new include/linux/const.h. This patch (of 4): I am going to add include/linux/const.h for the kernel space. Add _UAPI to the include guard of include/uapi/linux/const.h to prepare for that. Please notice the guard name of the exported one will be kept as-is. So, this commit has no impact to the userspace even if some userspace stuff depends on the guard macro names. scripts/headers_install.sh processes exported headers by SED, and rips off "_UAPI" from guard macro names. #ifndef _UAPI_LINUX_CONST_H #define _UAPI_LINUX_CONST_H will be turned into #ifndef _LINUX_CONST_H #define _LINUX_CONST_H Link: http://lkml.kernel.org/r/1519301715-31798-2-git-send-email-yamada.masahiro@socionext.com Signed-off-by: Masahiro Yamada Cc: David Howells Cc: Will Deacon Cc: Guan Xuetao Cc: Geert Uytterhoeven Cc: Catalin Marinas Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/uapi/linux/const.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/const.h b/include/uapi/linux/const.h index 92537757590a..c5a60ebabf5e 100644 --- a/include/uapi/linux/const.h +++ b/include/uapi/linux/const.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* const.h: Macros for dealing with constants. */ -#ifndef _LINUX_CONST_H -#define _LINUX_CONST_H +#ifndef _UAPI_LINUX_CONST_H +#define _UAPI_LINUX_CONST_H /* Some constant macros are used in both assembler and * C code. Therefore we cannot annotate them always with @@ -25,4 +25,4 @@ #define _BITUL(x) (_AC(1,UL) << (x)) #define _BITULL(x) (_AC(1,ULL) << (x)) -#endif /* !(_LINUX_CONST_H) */ +#endif /* _UAPI_LINUX_CONST_H */ -- cgit v1.2.3 From 2dd8a62c647691161a2346546834262597739872 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 10 Apr 2018 16:36:19 -0700 Subject: linux/const.h: move UL() macro to include/linux/const.h ARM, ARM64 and UniCore32 duplicate the definition of UL(): #define UL(x) _AC(x, UL) This is not actually arch-specific, so it will be useful to move it to a common header. Currently, we only have the uapi variant for linux/const.h, so I am creating include/linux/const.h. I also added _UL(), _ULL() and ULL() because _AC() is mostly used in the form either _AC(..., UL) or _AC(..., ULL). I expect they will be replaced in follow-up cleanups. The underscore-prefixed ones should be used for exported headers. Link: http://lkml.kernel.org/r/1519301715-31798-4-git-send-email-yamada.masahiro@socionext.com Signed-off-by: Masahiro Yamada Acked-by: Guan Xuetao Acked-by: Catalin Marinas Acked-by: Russell King Cc: David Howells Cc: Geert Uytterhoeven Cc: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/include/asm/memory.h | 6 ------ arch/arm64/include/asm/memory.h | 6 ------ arch/unicore32/include/asm/memory.h | 6 ------ include/linux/const.h | 9 +++++++++ include/uapi/linux/const.h | 3 +++ 5 files changed, 12 insertions(+), 18 deletions(-) create mode 100644 include/linux/const.h (limited to 'include/uapi/linux') diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 496667703693..ed8fd0d19a3e 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -22,12 +22,6 @@ #include #endif -/* - * Allow for constants defined here to be used from assembly code - * by prepending the UL suffix only with actual C code compilation. - */ -#define UL(x) _AC(x, UL) - /* PAGE_OFFSET - the virtual address of the start of the kernel image */ #define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET) diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index 50fa96a49792..49d99214f43c 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -28,12 +28,6 @@ #include #include -/* - * Allow for constants defined here to be used from assembly code - * by prepending the UL suffix only with actual C code compilation. - */ -#define UL(x) _AC(x, UL) - /* * Size of the PCI I/O space. This must remain a power of two so that * IO_SPACE_LIMIT acts as a mask for the low bits of I/O addresses. diff --git a/arch/unicore32/include/asm/memory.h b/arch/unicore32/include/asm/memory.h index 3bb0a29fd2d7..66bb9f6525c0 100644 --- a/arch/unicore32/include/asm/memory.h +++ b/arch/unicore32/include/asm/memory.h @@ -19,12 +19,6 @@ #include #include -/* - * Allow for constants defined here to be used from assembly code - * by prepending the UL suffix only with actual C code compilation. - */ -#define UL(x) _AC(x, UL) - /* * PAGE_OFFSET - the virtual address of the start of the kernel image * TASK_SIZE - the maximum size of a user space task. diff --git a/include/linux/const.h b/include/linux/const.h new file mode 100644 index 000000000000..7b55a55f5911 --- /dev/null +++ b/include/linux/const.h @@ -0,0 +1,9 @@ +#ifndef _LINUX_CONST_H +#define _LINUX_CONST_H + +#include + +#define UL(x) (_UL(x)) +#define ULL(x) (_ULL(x)) + +#endif /* _LINUX_CONST_H */ diff --git a/include/uapi/linux/const.h b/include/uapi/linux/const.h index c5a60ebabf5e..09bc0e0e97e3 100644 --- a/include/uapi/linux/const.h +++ b/include/uapi/linux/const.h @@ -22,6 +22,9 @@ #define _AT(T,X) ((T)(X)) #endif +#define _UL(x) (_AC(x, UL)) +#define _ULL(x) (_AC(x, ULL)) + #define _BITUL(x) (_AC(1,UL) << (x)) #define _BITULL(x) (_AC(1,ULL) << (x)) -- cgit v1.2.3 From 21e7bc600e3b662020c05fd0749bcf85f16336f7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 10 Apr 2018 16:36:24 -0700 Subject: linux/const.h: refactor _BITUL and _BITULL a bit Minor cleanups available by _UL and _ULL. Link: http://lkml.kernel.org/r/1519301715-31798-5-git-send-email-yamada.masahiro@socionext.com Signed-off-by: Masahiro Yamada Cc: Catalin Marinas Cc: David Howells Cc: Geert Uytterhoeven Cc: Guan Xuetao Cc: Russell King Cc: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/uapi/linux/const.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/const.h b/include/uapi/linux/const.h index 09bc0e0e97e3..5ed721ad5b19 100644 --- a/include/uapi/linux/const.h +++ b/include/uapi/linux/const.h @@ -25,7 +25,7 @@ #define _UL(x) (_AC(x, UL)) #define _ULL(x) (_AC(x, ULL)) -#define _BITUL(x) (_AC(1,UL) << (x)) -#define _BITULL(x) (_AC(1,ULL) << (x)) +#define _BITUL(x) (_UL(1) << (x)) +#define _BITULL(x) (_ULL(1) << (x)) #endif /* _UAPI_LINUX_CONST_H */ -- cgit v1.2.3