Commit edec4282 authored by Arend van Spriel's avatar Arend van Spriel Committed by Kalle Valo

wifi: brcmfmac: allow per-vendor event handling

The firmware interface also defines events generated by
firmware on the device. As the get/set primitives the
events are likely to diverge between the vendors so this
commit adds support for per-vendor handling. The number
of events may differ so we let the vendor-specific code
allocate the struct brcmf_fweh_info which contains array
of event handlers. The existing event enumeration will be
used by the higher layers and thus are common definitions.
The vendor-specific code can provide a mapping table for
converting the common definition to the vendor-specific
firmware event definition and vice-versa.
Signed-off-by: default avatarArend van Spriel <arend.vanspriel@broadcom.com>
Signed-off-by: default avatarKalle Valo <kvalo@kernel.org>
Link: https://msgid.link/20240106103835.269149-4-arend.vanspriel@broadcom.com
parent b822015a
......@@ -11,12 +11,29 @@
#include "vops.h"
#define BRCMF_BCA_E_LAST 212
static void brcmf_bca_feat_attach(struct brcmf_if *ifp)
{
/* SAE support not confirmed so disabling for now */
ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_SAE);
}
static int brcmf_bca_alloc_fweh_info(struct brcmf_pub *drvr)
{
struct brcmf_fweh_info *fweh;
fweh = kzalloc(struct_size(fweh, evt_handler, BRCMF_BCA_E_LAST),
GFP_KERNEL);
if (!fweh)
return -ENOMEM;
fweh->num_event_codes = BRCMF_BCA_E_LAST;
drvr->fweh = fweh;
return 0;
}
const struct brcmf_fwvid_ops brcmf_bca_ops = {
.feat_attach = brcmf_bca_feat_attach,
.alloc_fweh_info = brcmf_bca_alloc_fweh_info,
};
......@@ -266,7 +266,7 @@ static int brcmf_c_process_cal_blob(struct brcmf_if *ifp)
int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
{
struct brcmf_pub *drvr = ifp->drvr;
s8 eventmask[BRCMF_EVENTING_MASK_LEN];
struct brcmf_fweh_info *fweh = drvr->fweh;
u8 buf[BRCMF_DCMD_SMLEN];
struct brcmf_bus *bus;
struct brcmf_rev_info_le revinfo;
......@@ -413,15 +413,21 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
brcmf_c_set_joinpref_default(ifp);
/* Setup event_msgs, enable E_IF */
err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask,
BRCMF_EVENTING_MASK_LEN);
err = brcmf_fil_iovar_data_get(ifp, "event_msgs", fweh->event_mask,
fweh->event_mask_len);
if (err) {
bphy_err(drvr, "Get event_msgs error (%d)\n", err);
goto done;
}
setbit(eventmask, BRCMF_E_IF);
err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask,
BRCMF_EVENTING_MASK_LEN);
/*
* BRCMF_E_IF can safely be used to set the appropriate bit
* in the event_mask as the firmware event code is guaranteed
* to match the value of BRCMF_E_IF because it is old cruft
* that all vendors have.
*/
setbit(fweh->event_mask, BRCMF_E_IF);
err = brcmf_fil_iovar_data_set(ifp, "event_msgs", fweh->event_mask,
fweh->event_mask_len);
if (err) {
bphy_err(drvr, "Set event_msgs error (%d)\n", err);
goto done;
......
......@@ -1348,13 +1348,17 @@ int brcmf_attach(struct device *dev)
goto fail;
}
/* attach firmware event handler */
ret = brcmf_fweh_attach(drvr);
if (ret != 0) {
bphy_err(drvr, "brcmf_fweh_attach failed\n");
goto fail;
}
/* Attach to events important for core code */
brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
brcmf_psm_watchdog_notify);
/* attach firmware event handler */
brcmf_fweh_attach(drvr);
ret = brcmf_bus_started(drvr, drvr->ops);
if (ret != 0) {
bphy_err(drvr, "dongle is not responding: err=%d\n", ret);
......
......@@ -122,7 +122,7 @@ struct brcmf_pub {
struct mutex proto_block;
unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
struct brcmf_fweh_info fweh;
struct brcmf_fweh_info *fweh;
struct brcmf_ampdu_rx_reorder
*reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
......
......@@ -11,6 +11,8 @@
#include "vops.h"
#define BRCMF_CYW_E_LAST 197
static int brcmf_cyw_set_sae_pwd(struct brcmf_if *ifp,
struct cfg80211_crypto_settings *crypto)
{
......@@ -37,6 +39,21 @@ static int brcmf_cyw_set_sae_pwd(struct brcmf_if *ifp,
return err;
}
static int brcmf_cyw_alloc_fweh_info(struct brcmf_pub *drvr)
{
struct brcmf_fweh_info *fweh;
fweh = kzalloc(struct_size(fweh, evt_handler, BRCMF_CYW_E_LAST),
GFP_KERNEL);
if (!fweh)
return -ENOMEM;
fweh->num_event_codes = BRCMF_CYW_E_LAST;
drvr->fweh = fweh;
return 0;
}
const struct brcmf_fwvid_ops brcmf_cyw_ops = {
.set_sae_password = brcmf_cyw_set_sae_pwd,
.alloc_fweh_info = brcmf_cyw_alloc_fweh_info,
};
......@@ -14,7 +14,8 @@
#include "fweh.h"
#include "fwil.h"
#include "proto.h"
#include "bus.h"
#include "fwvid.h"
/**
* struct brcmf_fweh_queue_item - event item on event queue.
*
......@@ -28,7 +29,7 @@
*/
struct brcmf_fweh_queue_item {
struct list_head q;
enum brcmf_fweh_event_code code;
u32 code;
u8 ifidx;
u8 ifaddr[ETH_ALEN];
struct brcmf_event_msg_be emsg;
......@@ -94,7 +95,7 @@ static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh,
static int brcmf_fweh_call_event_handler(struct brcmf_pub *drvr,
struct brcmf_if *ifp,
enum brcmf_fweh_event_code code,
u32 fwcode,
struct brcmf_event_msg *emsg,
void *data)
{
......@@ -102,13 +103,13 @@ static int brcmf_fweh_call_event_handler(struct brcmf_pub *drvr,
int err = -EINVAL;
if (ifp) {
fweh = &ifp->drvr->fweh;
fweh = ifp->drvr->fweh;
/* handle the event if valid interface and handler */
if (fweh->evt_handler[code])
err = fweh->evt_handler[code](ifp, emsg, data);
if (fweh->evt_handler[fwcode])
err = fweh->evt_handler[fwcode](ifp, emsg, data);
else
bphy_err(drvr, "unhandled event %d ignored\n", code);
bphy_err(drvr, "unhandled fwevt %d ignored\n", fwcode);
} else {
bphy_err(drvr, "no interface object\n");
}
......@@ -142,7 +143,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
(ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT ||
((ifevent->role == BRCMF_E_IF_ROLE_STA) &&
(drvr->fweh.p2pdev_setup_ongoing))));
(drvr->fweh->p2pdev_setup_ongoing))));
if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
brcmf_dbg(EVENT, "event can be ignored\n");
return;
......@@ -163,7 +164,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
return;
if (!is_p2pdev)
brcmf_proto_add_if(drvr, ifp);
if (!drvr->fweh.evt_handler[BRCMF_E_IF])
if (!drvr->fweh->evt_handler[BRCMF_E_IF])
if (brcmf_net_attach(ifp, false) < 0)
return;
}
......@@ -183,6 +184,45 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
}
}
static void brcmf_fweh_map_event_code(struct brcmf_fweh_info *fweh,
enum brcmf_fweh_event_code code,
u32 *fw_code)
{
int i;
if (WARN_ON(!fw_code))
return;
*fw_code = code;
if (fweh->event_map) {
for (i = 0; i < fweh->event_map->n_items; i++) {
if (fweh->event_map->items[i].code == code) {
*fw_code = fweh->event_map->items[i].fwevt_code;
break;
}
}
}
}
static void brcmf_fweh_map_fwevt_code(struct brcmf_fweh_info *fweh, u32 fw_code,
enum brcmf_fweh_event_code *code)
{
int i;
if (WARN_ON(!code))
return;
*code = fw_code;
if (fweh->event_map) {
for (i = 0; i < fweh->event_map->n_items; i++) {
if (fweh->event_map->items[i].fwevt_code == fw_code) {
*code = fweh->event_map->items[i].code;
break;
}
}
}
}
/**
* brcmf_fweh_dequeue_event() - get event from the queue.
*
......@@ -221,15 +261,19 @@ static void brcmf_fweh_event_worker(struct work_struct *work)
struct brcmf_event_msg emsg;
fweh = container_of(work, struct brcmf_fweh_info, event_work);
drvr = container_of(fweh, struct brcmf_pub, fweh);
drvr = fweh->drvr;
while ((event = brcmf_fweh_dequeue_event(fweh))) {
brcmf_dbg(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM\n",
brcmf_fweh_event_name(event->code), event->code,
enum brcmf_fweh_event_code code;
brcmf_fweh_map_fwevt_code(fweh, event->code, &code);
brcmf_dbg(EVENT, "event %s (%u:%u) ifidx %u bsscfg %u addr %pM\n",
brcmf_fweh_event_name(code), code, event->code,
event->emsg.ifidx, event->emsg.bsscfgidx,
event->emsg.addr);
if (event->emsg.bsscfgidx >= BRCMF_MAX_IFS) {
bphy_err(drvr, "invalid bsscfg index: %u\n", event->emsg.bsscfgidx);
bphy_err(drvr, "invalid bsscfg index: %u\n",
event->emsg.bsscfgidx);
goto event_free;
}
......@@ -237,7 +281,7 @@ static void brcmf_fweh_event_worker(struct work_struct *work)
emsg_be = &event->emsg;
emsg.version = be16_to_cpu(emsg_be->version);
emsg.flags = be16_to_cpu(emsg_be->flags);
emsg.event_code = event->code;
emsg.event_code = code;
emsg.status = be32_to_cpu(emsg_be->status);
emsg.reason = be32_to_cpu(emsg_be->reason);
emsg.auth_type = be32_to_cpu(emsg_be->auth_type);
......@@ -283,7 +327,7 @@ static void brcmf_fweh_event_worker(struct work_struct *work)
*/
void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
{
ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing;
ifp->drvr->fweh->p2pdev_setup_ongoing = ongoing;
}
/**
......@@ -291,12 +335,27 @@ void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
*
* @drvr: driver information object.
*/
void brcmf_fweh_attach(struct brcmf_pub *drvr)
int brcmf_fweh_attach(struct brcmf_pub *drvr)
{
struct brcmf_fweh_info *fweh = &drvr->fweh;
struct brcmf_fweh_info *fweh;
int err;
err = brcmf_fwvid_alloc_fweh_info(drvr);
if (err < 0)
return err;
fweh = drvr->fweh;
fweh->drvr = drvr;
fweh->event_mask_len = DIV_ROUND_UP(fweh->num_event_codes, 8);
fweh->event_mask = kzalloc(fweh->event_mask_len, GFP_KERNEL);
if (!fweh->event_mask)
return -ENOMEM;
INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker);
spin_lock_init(&fweh->evt_q_lock);
INIT_LIST_HEAD(&fweh->event_q);
return 0;
}
/**
......@@ -306,14 +365,19 @@ void brcmf_fweh_attach(struct brcmf_pub *drvr)
*/
void brcmf_fweh_detach(struct brcmf_pub *drvr)
{
struct brcmf_fweh_info *fweh = &drvr->fweh;
struct brcmf_fweh_info *fweh = drvr->fweh;
if (!fweh)
return;
/* cancel the worker if initialized */
if (fweh->event_work.func) {
cancel_work_sync(&fweh->event_work);
WARN_ON(!list_empty(&fweh->event_q));
memset(fweh->evt_handler, 0, sizeof(fweh->evt_handler));
}
drvr->fweh = NULL;
kfree(fweh->event_mask);
kfree(fweh);
}
/**
......@@ -326,11 +390,17 @@ void brcmf_fweh_detach(struct brcmf_pub *drvr)
int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
brcmf_fweh_handler_t handler)
{
if (drvr->fweh.evt_handler[code]) {
struct brcmf_fweh_info *fweh = drvr->fweh;
u32 evt_handler_idx;
brcmf_fweh_map_event_code(fweh, code, &evt_handler_idx);
if (fweh->evt_handler[evt_handler_idx]) {
bphy_err(drvr, "event code %d already registered\n", code);
return -ENOSPC;
}
drvr->fweh.evt_handler[code] = handler;
fweh->evt_handler[evt_handler_idx] = handler;
brcmf_dbg(TRACE, "event handler registered for %s\n",
brcmf_fweh_event_name(code));
return 0;
......@@ -345,9 +415,12 @@ int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
void brcmf_fweh_unregister(struct brcmf_pub *drvr,
enum brcmf_fweh_event_code code)
{
u32 evt_handler_idx;
brcmf_dbg(TRACE, "event handler cleared for %s\n",
brcmf_fweh_event_name(code));
drvr->fweh.evt_handler[code] = NULL;
brcmf_fweh_map_event_code(drvr->fweh, code, &evt_handler_idx);
drvr->fweh->evt_handler[evt_handler_idx] = NULL;
}
/**
......@@ -357,27 +430,28 @@ void brcmf_fweh_unregister(struct brcmf_pub *drvr,
*/
int brcmf_fweh_activate_events(struct brcmf_if *ifp)
{
struct brcmf_pub *drvr = ifp->drvr;
struct brcmf_fweh_info *fweh = ifp->drvr->fweh;
enum brcmf_fweh_event_code code;
int i, err;
s8 eventmask[BRCMF_EVENTING_MASK_LEN];
memset(eventmask, 0, sizeof(eventmask));
for (i = 0; i < BRCMF_E_LAST; i++) {
if (ifp->drvr->fweh.evt_handler[i]) {
memset(fweh->event_mask, 0, fweh->event_mask_len);
for (i = 0; i < fweh->num_event_codes; i++) {
if (fweh->evt_handler[i]) {
brcmf_fweh_map_fwevt_code(fweh, i, &code);
brcmf_dbg(EVENT, "enable event %s\n",
brcmf_fweh_event_name(i));
setbit(eventmask, i);
brcmf_fweh_event_name(code));
setbit(fweh->event_mask, i);
}
}
/* want to handle IF event as well */
brcmf_dbg(EVENT, "enable event IF\n");
setbit(eventmask, BRCMF_E_IF);
setbit(fweh->event_mask, BRCMF_E_IF);
err = brcmf_fil_iovar_data_set(ifp, "event_msgs",
eventmask, BRCMF_EVENTING_MASK_LEN);
err = brcmf_fil_iovar_data_set(ifp, "event_msgs", fweh->event_mask,
fweh->event_mask_len);
if (err)
bphy_err(drvr, "Set event_msgs error (%d)\n", err);
bphy_err(fweh->drvr, "Set event_msgs error (%d)\n", err);
return err;
}
......@@ -397,21 +471,21 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr,
struct brcmf_event *event_packet,
u32 packet_len, gfp_t gfp)
{
enum brcmf_fweh_event_code code;
struct brcmf_fweh_info *fweh = &drvr->fweh;
u32 fwevt_idx;
struct brcmf_fweh_info *fweh = drvr->fweh;
struct brcmf_fweh_queue_item *event;
void *data;
u32 datalen;
/* get event info */
code = get_unaligned_be32(&event_packet->msg.event_type);
fwevt_idx = get_unaligned_be32(&event_packet->msg.event_type);
datalen = get_unaligned_be32(&event_packet->msg.datalen);
data = &event_packet[1];
if (code >= BRCMF_E_LAST)
if (fwevt_idx >= fweh->num_event_codes)
return;
if (code != BRCMF_E_IF && !fweh->evt_handler[code])
if (fwevt_idx != BRCMF_E_IF && !fweh->evt_handler[fwevt_idx])
return;
if (datalen > BRCMF_DCMD_MAXLEN ||
......@@ -422,13 +496,13 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr,
if (!event)
return;
event->datalen = datalen;
event->code = code;
event->code = fwevt_idx;
event->ifidx = event_packet->msg.ifidx;
/* use memcpy to get aligned event message */
memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
memcpy(event->data, data, datalen);
event->datalen = datalen;
memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
brcmf_fweh_queue_event(fweh, event);
......
......@@ -17,6 +17,10 @@ struct brcmf_pub;
struct brcmf_if;
struct brcmf_cfg80211_info;
#define BRCMF_ABSTRACT_EVENT_BIT BIT(31)
#define BRCMF_ABSTRACT_ENUM_DEF(_id, _val) \
BRCMF_ENUM_DEF(_id, (BRCMF_ABSTRACT_EVENT_BIT | (_val)))
/* list of firmware events */
#define BRCMF_FWEH_EVENT_ENUM_DEFLIST \
BRCMF_ENUM_DEF(SET_SSID, 0) \
......@@ -98,16 +102,9 @@ struct brcmf_cfg80211_info;
/* firmware event codes sent by the dongle */
enum brcmf_fweh_event_code {
BRCMF_FWEH_EVENT_ENUM_DEFLIST
/* this determines event mask length which must match
* minimum length check in device firmware so it is
* hard-coded here.
*/
BRCMF_E_LAST = 139
};
#undef BRCMF_ENUM_DEF
#define BRCMF_EVENTING_MASK_LEN DIV_ROUND_UP(BRCMF_E_LAST, 8)
/* flags field values in struct brcmf_event_msg */
#define BRCMF_EVENT_MSG_LINK 0x01
#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02
......@@ -287,6 +284,33 @@ typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp,
const struct brcmf_event_msg *evtmsg,
void *data);
/**
* struct brcmf_fweh_event_map_item - fweh event and firmware event pair.
*
* @code: fweh event code as used by higher layers.
* @fwevt_code: firmware event code as used by firmware.
*
* This mapping is needed when a functionally identical event has a
* different numerical definition between vendors. When such mapping
* is needed the higher layer event code should not collide with the
* firmware event.
*/
struct brcmf_fweh_event_map_item {
enum brcmf_fweh_event_code code;
u32 fwevt_code;
};
/**
* struct brcmf_fweh_event_map - mapping between firmware event and fweh event.
*
* @n_items: number of mapping items.
* @items: array of fweh event and firmware event pairs.
*/
struct brcmf_fweh_event_map {
u32 n_items;
const struct brcmf_fweh_event_map_item items[] __counted_by(n_items);
};
/**
* struct brcmf_fweh_info - firmware event handling information.
*
......@@ -294,21 +318,33 @@ typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp,
* @event_work: event worker.
* @evt_q_lock: lock for event queue protection.
* @event_q: event queue.
* @evt_handler: registered event handlers.
* @event_mask_len: length of @event_mask used to enable firmware events.
* @event_mask: byte array used in 'event_msgs' iovar command.
* @event_map: mapping between fweh event and firmware event which
* may be provided by vendor-specific module for events that need
* mapping.
* @num_event_codes: number of firmware events supported by firmware which
* does a minimum length check for the @event_mask. This value is to
* be provided by vendor-specific module determining @event_mask_len
* and consequently the allocation size for @event_mask.
* @evt_handler: event handler registry indexed by firmware event code.
*/
struct brcmf_fweh_info {
struct brcmf_pub *drvr;
bool p2pdev_setup_ongoing;
struct work_struct event_work;
spinlock_t evt_q_lock;
struct list_head event_q;
int (*evt_handler[BRCMF_E_LAST])(struct brcmf_if *ifp,
const struct brcmf_event_msg *evtmsg,
void *data);
uint event_mask_len;
u8 *event_mask;
struct brcmf_fweh_event_map *event_map;
uint num_event_codes;
brcmf_fweh_handler_t evt_handler[] __counted_by(num_event_codes);
};
const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code);
void brcmf_fweh_attach(struct brcmf_pub *drvr);
int brcmf_fweh_attach(struct brcmf_pub *drvr);
void brcmf_fweh_detach(struct brcmf_pub *drvr);
int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
int (*handler)(struct brcmf_if *ifp,
......
......@@ -89,7 +89,8 @@ int brcmf_fwvid_register_vendor(enum brcmf_fwvendor fwvid, struct module *vmod,
if (fwvid >= BRCMF_FWVENDOR_NUM)
return -ERANGE;
if (WARN_ON(!vmod) || WARN_ON(!vops))
if (WARN_ON(!vmod) || WARN_ON(!vops) ||
WARN_ON(!vops->alloc_fweh_info))
return -EINVAL;
if (WARN_ON(fwvid_list[fwvid].vmod))
......
......@@ -14,6 +14,7 @@ struct brcmf_if;
struct brcmf_fwvid_ops {
void (*feat_attach)(struct brcmf_if *ifp);
int (*set_sae_password)(struct brcmf_if *ifp, struct cfg80211_crypto_settings *crypto);
int (*alloc_fweh_info)(struct brcmf_pub *drvr);
};
/* exported functions */
......@@ -47,4 +48,12 @@ static inline int brcmf_fwvid_set_sae_password(struct brcmf_if *ifp,
return vops->set_sae_password(ifp, crypto);
}
static inline int brcmf_fwvid_alloc_fweh_info(struct brcmf_pub *drvr)
{
if (!drvr->vops)
return -EIO;
return drvr->vops->alloc_fweh_info(drvr);
}
#endif /* FWVID_H_ */
......@@ -11,6 +11,8 @@
#include "vops.h"
#define BRCMF_WCC_E_LAST 213
static int brcmf_wcc_set_sae_pwd(struct brcmf_if *ifp,
struct cfg80211_crypto_settings *crypto)
{
......@@ -18,6 +20,21 @@ static int brcmf_wcc_set_sae_pwd(struct brcmf_if *ifp,
BRCMF_WSEC_PASSPHRASE);
}
static int brcmf_wcc_alloc_fweh_info(struct brcmf_pub *drvr)
{
struct brcmf_fweh_info *fweh;
fweh = kzalloc(struct_size(fweh, evt_handler, BRCMF_WCC_E_LAST),
GFP_KERNEL);
if (!fweh)
return -ENOMEM;
fweh->num_event_codes = BRCMF_WCC_E_LAST;
drvr->fweh = fweh;
return 0;
}
const struct brcmf_fwvid_ops brcmf_wcc_ops = {
.set_sae_password = brcmf_wcc_set_sae_pwd,
.alloc_fweh_info = brcmf_wcc_alloc_fweh_info,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment