Index: projects/capsicum-test/contrib/bsnmp/snmpd/trap.c =================================================================== --- projects/capsicum-test/contrib/bsnmp/snmpd/trap.c (revision 345770) +++ projects/capsicum-test/contrib/bsnmp/snmpd/trap.c (revision 345771) @@ -1,911 +1,910 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR 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. * * $Begemot: bsnmp/snmpd/trap.c,v 1.9 2005/10/04 11:21:39 brandt_h Exp $ * * TrapSinkTable */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snmpmod.h" #include "snmpd.h" #define SNMPTREE_TYPES #include "tree.h" #include "oid.h" struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list); /* List of target addresses */ static struct target_addresslist target_addresslist = SLIST_HEAD_INITIALIZER(target_addresslist); /* List of target parameters */ static struct target_paramlist target_paramlist = SLIST_HEAD_INITIALIZER(target_paramlist); /* List of notification targets */ static struct target_notifylist target_notifylist = SLIST_HEAD_INITIALIZER(target_notifylist); static const struct asn_oid oid_begemotTrapSinkTable = OIDX_begemotTrapSinkTable; static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime; static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID; struct trapsink_dep { struct snmp_dependency dep; u_int set; u_int status; u_char comm[SNMP_COMMUNITY_MAXLEN + 1]; u_int version; u_int rb; u_int rb_status; u_int rb_version; u_char rb_comm[SNMP_COMMUNITY_MAXLEN + 1]; }; enum { TDEP_STATUS = 0x0001, TDEP_COMM = 0x0002, TDEP_VERSION = 0x0004, TDEP_CREATE = 0x0001, TDEP_MODIFY = 0x0002, TDEP_DESTROY = 0x0004, }; static int trapsink_create(struct trapsink_dep *tdep) { struct trapsink *t; struct sockaddr_in sa; if ((t = malloc(sizeof(*t))) == NULL) return (SNMP_ERR_RES_UNAVAIL); t->index = tdep->dep.idx; t->status = TRAPSINK_NOT_READY; t->comm[0] = '\0'; t->version = TRAPSINK_V2; if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { syslog(LOG_ERR, "socket(UDP): %m"); free(t); return (SNMP_ERR_RES_UNAVAIL); } (void)shutdown(t->socket, SHUT_RD); memset(&sa, 0, sizeof(sa)); sa.sin_len = sizeof(sa); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) | (t->index.subs[1] << 16) | (t->index.subs[2] << 8) | (t->index.subs[3] << 0)); sa.sin_port = htons(t->index.subs[4]); if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) { syslog(LOG_ERR, "connect(%s,%u): %m", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port)); (void)close(t->socket); free(t); return (SNMP_ERR_GENERR); } if (tdep->set & TDEP_VERSION) t->version = tdep->version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->comm); if (t->comm[0] != '\0') t->status = TRAPSINK_NOT_IN_SERVICE; /* look whether we should activate */ if (tdep->status == 4) { if (t->status == TRAPSINK_NOT_READY) { if (t->socket != -1) (void)close(t->socket); free(t); return (SNMP_ERR_INCONS_VALUE); } t->status = TRAPSINK_ACTIVE; } INSERT_OBJECT_OID(t, &trapsink_list); tdep->rb |= TDEP_CREATE; return (SNMP_ERR_NOERROR); } static void trapsink_free(struct trapsink *t) { TAILQ_REMOVE(&trapsink_list, t, link); if (t->socket != -1) (void)close(t->socket); free(t); } static int trapsink_modify(struct trapsink *t, struct trapsink_dep *tdep) { tdep->rb_status = t->status; tdep->rb_version = t->version; strcpy(tdep->rb_comm, t->comm); if (tdep->set & TDEP_STATUS) { /* if we are active and should move to not_in_service do * this first */ if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) { t->status = TRAPSINK_NOT_IN_SERVICE; tdep->rb |= TDEP_MODIFY; } } if (tdep->set & TDEP_VERSION) t->version = tdep->version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->comm); if (tdep->set & TDEP_STATUS) { /* if we were inactive and should go active - do this now */ if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) { if (t->comm[0] == '\0') { t->status = tdep->rb_status; t->version = tdep->rb_version; strcpy(t->comm, tdep->rb_comm); return (SNMP_ERR_INCONS_VALUE); } t->status = TRAPSINK_ACTIVE; tdep->rb |= TDEP_MODIFY; } } return (SNMP_ERR_NOERROR); } static int trapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep) { if (tdep->set & TDEP_STATUS) t->status = tdep->rb_status; if (tdep->set & TDEP_VERSION) t->version = tdep->rb_version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->rb_comm); return (SNMP_ERR_NOERROR); } static int trapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t, struct trapsink_dep *tdep) { t->status = TRAPSINK_DESTROY; tdep->rb_status = t->status; tdep->rb |= TDEP_DESTROY; return (SNMP_ERR_NOERROR); } static int trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep) { t->status = tdep->rb_status; return (SNMP_ERR_NOERROR); } static int trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep, enum snmp_depop op) { struct trapsink_dep *tdep = (struct trapsink_dep *)dep; struct trapsink *t; t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0); switch (op) { case SNMP_DEPOP_COMMIT: if (tdep->set & TDEP_STATUS) { switch (tdep->status) { case 1: case 2: if (t == NULL) return (SNMP_ERR_INCONS_VALUE); return (trapsink_modify(t, tdep)); case 4: case 5: if (t != NULL) return (SNMP_ERR_INCONS_VALUE); return (trapsink_create(tdep)); case 6: if (t == NULL) return (SNMP_ERR_NOERROR); return (trapsink_destroy(ctx, t, tdep)); } } else if (tdep->set != 0) return (trapsink_modify(t, tdep)); return (SNMP_ERR_NOERROR); case SNMP_DEPOP_ROLLBACK: if (tdep->rb & TDEP_CREATE) { trapsink_free(t); return (SNMP_ERR_NOERROR); } if (tdep->rb & TDEP_MODIFY) return (trapsink_unmodify(t, tdep)); if(tdep->rb & TDEP_DESTROY) return (trapsink_undestroy(t, tdep)); return (SNMP_ERR_NOERROR); case SNMP_DEPOP_FINISH: if ((tdep->rb & TDEP_DESTROY) && t != NULL && ctx->code == SNMP_RET_OK) trapsink_free(t); return (SNMP_ERR_NOERROR); } abort(); } int op_trapsink(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { struct trapsink *t; u_char ipa[4]; int32_t port; struct asn_oid idx; struct trapsink_dep *tdep; u_char *p; t = NULL; /* gcc */ switch (op) { case SNMP_OP_GETNEXT: if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &t->index); break; case SNMP_OP_GET: if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, ipa, &port) || port == 0 || port > 65535) return (SNMP_ERR_NO_CREATION); t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub); asn_slice_oid(&idx, &value->var, sub, value->var.len); tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx, &oid_begemotTrapSinkTable, &idx, sizeof(*tdep), trapsink_dep); if (tdep == NULL) return (SNMP_ERR_RES_UNAVAIL); switch (value->var.subs[sub - 1]) { case LEAF_begemotTrapSinkStatus: if (tdep->set & TDEP_STATUS) return (SNMP_ERR_INCONS_VALUE); switch (value->v.integer) { case 1: case 2: if (t == NULL) return (SNMP_ERR_INCONS_VALUE); break; case 4: case 5: if (t != NULL) return (SNMP_ERR_INCONS_VALUE); break; case 6: break; default: return (SNMP_ERR_WRONG_VALUE); } tdep->status = value->v.integer; tdep->set |= TDEP_STATUS; return (SNMP_ERR_NOERROR); case LEAF_begemotTrapSinkComm: if (tdep->set & TDEP_COMM) return (SNMP_ERR_INCONS_VALUE); if (value->v.octetstring.len == 0 || value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN) return (SNMP_ERR_WRONG_VALUE); for (p = value->v.octetstring.octets; p < value->v.octetstring.octets + value->v.octetstring.len; p++) { if (!isascii(*p) || !isprint(*p)) return (SNMP_ERR_WRONG_VALUE); } tdep->set |= TDEP_COMM; strncpy(tdep->comm, value->v.octetstring.octets, value->v.octetstring.len); tdep->comm[value->v.octetstring.len] = '\0'; return (SNMP_ERR_NOERROR); case LEAF_begemotTrapSinkVersion: if (tdep->set & TDEP_VERSION) return (SNMP_ERR_INCONS_VALUE); if (value->v.integer != TRAPSINK_V1 && value->v.integer != TRAPSINK_V2) return (SNMP_ERR_WRONG_VALUE); tdep->version = value->v.integer; tdep->set |= TDEP_VERSION; return (SNMP_ERR_NOERROR); } if (t == NULL) return (SNMP_ERR_INCONS_NAME); else return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } switch (value->var.subs[sub - 1]) { case LEAF_begemotTrapSinkStatus: value->v.integer = t->status; break; case LEAF_begemotTrapSinkComm: return (string_get(value, t->comm, -1)); case LEAF_begemotTrapSinkVersion: value->v.integer = t->version; break; } return (SNMP_ERR_NOERROR); } static void snmp_create_v1_trap(struct snmp_pdu *pdu, char *com, const struct asn_oid *trap_oid) { memset(pdu, 0, sizeof(*pdu)); strlcpy(pdu->community, com, sizeof(pdu->community)); pdu->version = SNMP_V1; pdu->type = SNMP_PDU_TRAP; pdu->enterprise = systemg.object_id; memcpy(pdu->agent_addr, snmpd.trap1addr, 4); pdu->generic_trap = trap_oid->subs[trap_oid->len - 1] - 1; pdu->specific_trap = 0; pdu->time_stamp = get_ticks() - start_tick; pdu->nbindings = 0; } static void snmp_create_v2_trap(struct snmp_pdu *pdu, char *com, const struct asn_oid *trap_oid) { memset(pdu, 0, sizeof(*pdu)); strlcpy(pdu->community, com, sizeof(pdu->community)); pdu->version = SNMP_V2c; pdu->type = SNMP_PDU_TRAP2; pdu->request_id = reqid_next(trap_reqid); pdu->error_index = 0; pdu->error_status = SNMP_ERR_NOERROR; pdu->bindings[0].var = oid_sysUpTime; pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0; pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS; pdu->bindings[0].v.uint32 = get_ticks() - start_tick; pdu->bindings[1].var = oid_snmpTrapOID; pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0; pdu->bindings[1].syntax = SNMP_SYNTAX_OID; pdu->bindings[1].v.oid = *trap_oid; pdu->nbindings = 2; } static void snmp_create_v3_trap(struct snmp_pdu *pdu, struct target_param *target, const struct asn_oid *trap_oid) { struct usm_user *usmuser; memset(pdu, 0, sizeof(*pdu)); pdu->version = SNMP_V3; pdu->type = SNMP_PDU_TRAP2; pdu->request_id = reqid_next(trap_reqid); pdu->error_index = 0; pdu->error_status = SNMP_ERR_NOERROR; pdu->bindings[0].var = oid_sysUpTime; pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0; pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS; pdu->bindings[0].v.uint32 = get_ticks() - start_tick; pdu->bindings[1].var = oid_snmpTrapOID; pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0; pdu->bindings[1].syntax = SNMP_SYNTAX_OID; pdu->bindings[1].v.oid = *trap_oid; pdu->nbindings = 2; update_snmpd_engine_time(); memcpy(pdu->engine.engine_id, snmpd_engine.engine_id, snmpd_engine.engine_len); pdu->engine.engine_len = snmpd_engine.engine_len; pdu->engine.engine_boots = snmpd_engine.engine_boots; pdu->engine.engine_time = snmpd_engine.engine_time; pdu->engine.max_msg_size = snmpd_engine.max_msg_size; strlcpy(pdu->user.sec_name, target->secname, sizeof(pdu->user.sec_name)); pdu->security_model = target->sec_model; pdu->context_engine_len = snmpd_engine.engine_len; memcpy(pdu->context_engine, snmpd_engine.engine_id, snmpd_engine.engine_len); if (target->sec_model == SNMP_SECMODEL_USM && target->sec_level != SNMP_noAuthNoPriv) { usmuser = usm_find_user(pdu->engine.engine_id, pdu->engine.engine_len, pdu->user.sec_name); if (usmuser != NULL) { pdu->user.auth_proto = usmuser->suser.auth_proto; pdu->user.priv_proto = usmuser->suser.priv_proto; memcpy(pdu->user.auth_key, usmuser->suser.auth_key, sizeof(pdu->user.auth_key)); memcpy(pdu->user.priv_key, usmuser->suser.priv_key, sizeof(pdu->user.priv_key)); } snmp_pdu_init_secparams(pdu); } } void snmp_send_trap(const struct asn_oid *trap_oid, ...) { struct snmp_pdu pdu; struct trapsink *t; const struct snmp_value *v; struct target_notify *n; struct target_address *ta; struct target_param *tp; va_list ap; u_char *sndbuf; char *tag; size_t sndlen; ssize_t len; int32_t ip; TAILQ_FOREACH(t, &trapsink_list, link) { if (t->status != TRAPSINK_ACTIVE) continue; if (t->version == TRAPSINK_V1) snmp_create_v1_trap(&pdu, t->comm, trap_oid); else snmp_create_v2_trap(&pdu, t->comm, trap_oid); va_start(ap, trap_oid); while ((v = va_arg(ap, const struct snmp_value *)) != NULL) pdu.bindings[pdu.nbindings++] = *v; va_end(ap); if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) { syslog(LOG_DEBUG, "send trap to %s failed: no access", t->comm); continue; } if ((sndbuf = buf_alloc(1)) == NULL) { syslog(LOG_ERR, "trap send buffer: %m"); return; } snmp_output(&pdu, sndbuf, &sndlen, "TRAP"); if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1) syslog(LOG_ERR, "send: %m"); else if ((size_t)len != sndlen) syslog(LOG_ERR, "send: short write %zu/%zu", sndlen, (size_t)len); free(sndbuf); } SLIST_FOREACH(n, &target_notifylist, tn) { if (n->status != RowStatus_active || n->taglist[0] == '\0') continue; SLIST_FOREACH(ta, &target_addresslist, ta) if ((tag = strstr(ta->taglist, n->taglist)) != NULL && (tag[strlen(n->taglist)] == ' ' || tag[strlen(n->taglist)] == '\0' || tag[strlen(n->taglist)] == '\t' || tag[strlen(n->taglist)] == '\r' || tag[strlen(n->taglist)] == '\n') && ta->status == RowStatus_active) break; if (ta == NULL) continue; SLIST_FOREACH(tp, &target_paramlist, tp) if (strcmp(tp->name, ta->paramname) == 0 && tp->status == 1) break; if (tp == NULL) continue; switch (tp->mpmodel) { case SNMP_MPM_SNMP_V1: snmp_create_v1_trap(&pdu, tp->secname, trap_oid); break; case SNMP_MPM_SNMP_V2c: snmp_create_v2_trap(&pdu, tp->secname, trap_oid); break; case SNMP_MPM_SNMP_V3: snmp_create_v3_trap(&pdu, tp, trap_oid); break; default: continue; } va_start(ap, trap_oid); while ((v = va_arg(ap, const struct snmp_value *)) != NULL) pdu.bindings[pdu.nbindings++] = *v; va_end(ap); if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) { syslog(LOG_DEBUG, "send trap to %s failed: no access", t->comm); continue; } if ((sndbuf = buf_alloc(1)) == NULL) { syslog(LOG_ERR, "trap send buffer: %m"); return; } snmp_output(&pdu, sndbuf, &sndlen, "TRAP"); if ((len = send(ta->socket, sndbuf, sndlen, 0)) == -1) syslog(LOG_ERR, "send: %m"); else if ((size_t)len != sndlen) syslog(LOG_ERR, "send: short write %zu/%zu", sndlen, (size_t)len); free(sndbuf); } } /* * RFC 3413 SNMP Management Target MIB */ struct snmpd_target_stats * bsnmpd_get_target_stats(void) { return (&snmpd_target_stats); } struct target_address * target_first_address(void) { return (SLIST_FIRST(&target_addresslist)); } struct target_address * target_next_address(struct target_address *addrs) { if (addrs == NULL) return (NULL); return (SLIST_NEXT(addrs, ta)); } struct target_address * target_new_address(char *aname) { int cmp; struct target_address *addrs, *temp, *prev; SLIST_FOREACH(addrs, &target_addresslist, ta) if (strcmp(aname, addrs->name) == 0) return (NULL); if ((addrs = (struct target_address *)malloc(sizeof(*addrs))) == NULL) return (NULL); memset(addrs, 0, sizeof(*addrs)); strlcpy(addrs->name, aname, sizeof(addrs->name)); addrs->timeout = 150; addrs->retry = 3; /* XXX */ if ((prev = SLIST_FIRST(&target_addresslist)) == NULL || strcmp(aname, prev->name) < 0) { SLIST_INSERT_HEAD(&target_addresslist, addrs, ta); return (addrs); } SLIST_FOREACH(temp, &target_addresslist, ta) { if ((cmp = strcmp(aname, temp->name)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, addrs, ta); else if (cmp > 0) SLIST_INSERT_AFTER(temp, addrs, ta); else { syslog(LOG_ERR, "Target address %s exists", addrs->name); free(addrs); return (NULL); } return (addrs); } int target_activate_address(struct target_address *addrs) { struct sockaddr_in sa; if ((addrs->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { syslog(LOG_ERR, "socket(UDP): %m"); return (SNMP_ERR_RES_UNAVAIL); } (void)shutdown(addrs->socket, SHUT_RD); memset(&sa, 0, sizeof(sa)); sa.sin_len = sizeof(sa); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl((addrs->address[0] << 24) | (addrs->address[1] << 16) | (addrs->address[2] << 8) | (addrs->address[3] << 0)); - sa.sin_port = htons(addrs->address[4]) << 8 | - htons(addrs->address[5]) << 0; + sa.sin_port = htons(addrs->address[4] << 8 | addrs->address[5]); if (connect(addrs->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) { syslog(LOG_ERR, "connect(%s,%u): %m", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port)); (void)close(addrs->socket); return (SNMP_ERR_GENERR); } addrs->status = RowStatus_active; return (SNMP_ERR_NOERROR); } int target_delete_address(struct target_address *addrs) { SLIST_REMOVE(&target_addresslist, addrs, target_address, ta); if (addrs->status == RowStatus_active) close(addrs->socket); free(addrs); return (0); } struct target_param * target_first_param(void) { return (SLIST_FIRST(&target_paramlist)); } struct target_param * target_next_param(struct target_param *param) { if (param == NULL) return (NULL); return (SLIST_NEXT(param, tp)); } struct target_param * target_new_param(char *pname) { int cmp; struct target_param *param, *temp, *prev; SLIST_FOREACH(param, &target_paramlist, tp) if (strcmp(pname, param->name) == 0) return (NULL); if ((param = (struct target_param *)malloc(sizeof(*param))) == NULL) return (NULL); memset(param, 0, sizeof(*param)); strlcpy(param->name, pname, sizeof(param->name)); if ((prev = SLIST_FIRST(&target_paramlist)) == NULL || strcmp(pname, prev->name) < 0) { SLIST_INSERT_HEAD(&target_paramlist, param, tp); return (param); } SLIST_FOREACH(temp, &target_paramlist, tp) { if ((cmp = strcmp(pname, temp->name)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, param, tp); else if (cmp > 0) SLIST_INSERT_AFTER(temp, param, tp); else { syslog(LOG_ERR, "Target parameter %s exists", param->name); free(param); return (NULL); } return (param); } int target_delete_param(struct target_param *param) { SLIST_REMOVE(&target_paramlist, param, target_param, tp); free(param); return (0); } struct target_notify * target_first_notify(void) { return (SLIST_FIRST(&target_notifylist)); } struct target_notify * target_next_notify(struct target_notify *notify) { if (notify == NULL) return (NULL); return (SLIST_NEXT(notify, tn)); } struct target_notify * target_new_notify(char *nname) { int cmp; struct target_notify *notify, *temp, *prev; SLIST_FOREACH(notify, &target_notifylist, tn) if (strcmp(nname, notify->name) == 0) return (NULL); if ((notify = (struct target_notify *)malloc(sizeof(*notify))) == NULL) return (NULL); memset(notify, 0, sizeof(*notify)); strlcpy(notify->name, nname, sizeof(notify->name)); if ((prev = SLIST_FIRST(&target_notifylist)) == NULL || strcmp(nname, prev->name) < 0) { SLIST_INSERT_HEAD(&target_notifylist, notify, tn); return (notify); } SLIST_FOREACH(temp, &target_notifylist, tn) { if ((cmp = strcmp(nname, temp->name)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, notify, tn); else if (cmp > 0) SLIST_INSERT_AFTER(temp, notify, tn); else { syslog(LOG_ERR, "Notification target %s exists", notify->name); free(notify); return (NULL); } return (notify); } int target_delete_notify(struct target_notify *notify) { SLIST_REMOVE(&target_notifylist, notify, target_notify, tn); free(notify); return (0); } void target_flush_all(void) { struct target_address *addrs; struct target_param *param; struct target_notify *notify; while ((addrs = SLIST_FIRST(&target_addresslist)) != NULL) { SLIST_REMOVE_HEAD(&target_addresslist, ta); if (addrs->status == RowStatus_active) close(addrs->socket); free(addrs); } SLIST_INIT(&target_addresslist); while ((param = SLIST_FIRST(&target_paramlist)) != NULL) { SLIST_REMOVE_HEAD(&target_paramlist, tp); free(param); } SLIST_INIT(&target_paramlist); while ((notify = SLIST_FIRST(&target_notifylist)) != NULL) { SLIST_REMOVE_HEAD(&target_notifylist, tn); free(notify); } SLIST_INIT(&target_notifylist); } Index: projects/capsicum-test/lib/libbe/be.c =================================================================== --- projects/capsicum-test/lib/libbe/be.c (revision 345770) +++ projects/capsicum-test/lib/libbe/be.c (revision 345771) @@ -1,1017 +1,1017 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Kyle J. Kneitinger * 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. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "be.h" #include "be_impl.h" struct be_destroy_data { libbe_handle_t *lbh; char *snapname; }; #if SOON static int be_create_child_noent(libbe_handle_t *lbh, const char *active, const char *child_path); static int be_create_child_cloned(libbe_handle_t *lbh, const char *active); #endif /* * Iterator function for locating the rootfs amongst the children of the * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *. */ static int be_locate_rootfs(libbe_handle_t *lbh) { struct statfs sfs; struct extmnttab entry; zfs_handle_t *zfs; /* * Check first if root is ZFS; if not, we'll bail on rootfs capture. * Unfortunately needed because zfs_path_to_zhandle will emit to * stderr if / isn't actually a ZFS filesystem, which we'd like * to avoid. */ if (statfs("/", &sfs) == 0) { statfs2mnttab(&sfs, &entry); if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) return (1); } else return (1); zfs = zfs_path_to_zhandle(lbh->lzh, "/", ZFS_TYPE_FILESYSTEM); if (zfs == NULL) return (1); strlcpy(lbh->rootfs, zfs_get_name(zfs), sizeof(lbh->rootfs)); zfs_close(zfs); return (0); } /* * Initializes the libbe context to operate in the root boot environment * dataset, for example, zroot/ROOT. */ libbe_handle_t * libbe_init(const char *root) { char altroot[MAXPATHLEN]; libbe_handle_t *lbh; char *poolname, *pos; int pnamelen; lbh = NULL; poolname = pos = NULL; if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) goto err; if ((lbh->lzh = libzfs_init()) == NULL) goto err; /* * Grab rootfs, we'll work backwards from there if an optional BE root * has not been passed in. */ if (be_locate_rootfs(lbh) != 0) { if (root == NULL) goto err; *lbh->rootfs = '\0'; } if (root == NULL) { /* Strip off the final slash from rootfs to get the be root */ strlcpy(lbh->root, lbh->rootfs, sizeof(lbh->root)); pos = strrchr(lbh->root, '/'); if (pos == NULL) goto err; *pos = '\0'; } else strlcpy(lbh->root, root, sizeof(lbh->root)); if ((pos = strchr(lbh->root, '/')) == NULL) goto err; pnamelen = pos - lbh->root; poolname = malloc(pnamelen + 1); if (poolname == NULL) goto err; strlcpy(poolname, lbh->root, pnamelen + 1); if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) goto err; free(poolname); poolname = NULL; if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, sizeof(lbh->bootfs), NULL, true) != 0) goto err; if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_ALTROOT, altroot, sizeof(altroot), NULL, true) == 0 && strcmp(altroot, "-") != 0) lbh->altroot_len = strlen(altroot); return (lbh); err: if (lbh != NULL) { if (lbh->active_phandle != NULL) zpool_close(lbh->active_phandle); if (lbh->lzh != NULL) libzfs_fini(lbh->lzh); free(lbh); } free(poolname); return (NULL); } /* * Free memory allocated by libbe_init() */ void libbe_close(libbe_handle_t *lbh) { if (lbh->active_phandle != NULL) zpool_close(lbh->active_phandle); libzfs_fini(lbh->lzh); free(lbh); } /* * Proxy through to libzfs for the moment. */ void be_nicenum(uint64_t num, char *buf, size_t buflen) { zfs_nicenum(num, buf, buflen); } static int be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) { char path[BE_MAXPATHLEN]; struct be_destroy_data *bdd; zfs_handle_t *snap; int err; bdd = (struct be_destroy_data *)data; if (bdd->snapname == NULL) { err = zfs_iter_children(zfs_hdl, be_destroy_cb, data); if (err != 0) return (err); return (zfs_destroy(zfs_hdl, false)); } /* If we're dealing with snapshots instead, delete that one alone */ err = zfs_iter_filesystems(zfs_hdl, be_destroy_cb, data); if (err != 0) return (err); /* * This part is intentionally glossing over any potential errors, * because there's a lot less potential for errors when we're cleaning * up snapshots rather than a full deep BE. The primary error case * here being if the snapshot doesn't exist in the first place, which * the caller will likely deem insignificant as long as it doesn't * exist after the call. Thus, such a missing snapshot shouldn't jam * up the destruction. */ snprintf(path, sizeof(path), "%s@%s", zfs_get_name(zfs_hdl), bdd->snapname); if (!zfs_dataset_exists(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) return (0); snap = zfs_open(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT); if (snap != NULL) zfs_destroy(snap, false); return (0); } /* * Destroy the boot environment or snapshot specified by the name * parameter. Options are or'd together with the possible values: * BE_DESTROY_FORCE : forces operation on mounted datasets * BE_DESTROY_ORIGIN: destroy the origin snapshot as well */ int be_destroy(libbe_handle_t *lbh, const char *name, int options) { struct be_destroy_data bdd; char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN]; zfs_handle_t *fs; char *snapdelim; int err, force, mounted; size_t rootlen; bdd.lbh = lbh; bdd.snapname = NULL; force = options & BE_DESTROY_FORCE; *origin = '\0'; be_root_concat(lbh, name, path); if ((snapdelim = strchr(path, '@')) == NULL) { if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) return (set_error(lbh, BE_ERR_NOENT)); if (strcmp(path, lbh->rootfs) == 0 || strcmp(path, lbh->bootfs) == 0) return (set_error(lbh, BE_ERR_DESTROYACT)); fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM); if (fs == NULL) return (set_error(lbh, BE_ERR_ZFSOPEN)); if ((options & BE_DESTROY_ORIGIN) != 0 && zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin), NULL, NULL, 0, 1) != 0) return (set_error(lbh, BE_ERR_NOORIGIN)); + + /* Don't destroy a mounted dataset unless force is specified */ + if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { + if (force) { + zfs_unmount(fs, NULL, 0); + } else { + free(bdd.snapname); + return (set_error(lbh, BE_ERR_DESTROYMNT)); + } + } } else { if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) return (set_error(lbh, BE_ERR_NOENT)); bdd.snapname = strdup(snapdelim + 1); if (bdd.snapname == NULL) return (set_error(lbh, BE_ERR_NOMEM)); *snapdelim = '\0'; fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET); if (fs == NULL) { free(bdd.snapname); return (set_error(lbh, BE_ERR_ZFSOPEN)); - } - } - - /* Check if mounted, unmount if force is specified */ - if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { - if (force) { - zfs_unmount(fs, NULL, 0); - } else { - free(bdd.snapname); - return (set_error(lbh, BE_ERR_DESTROYMNT)); } } err = be_destroy_cb(fs, &bdd); zfs_close(fs); free(bdd.snapname); if (err != 0) { /* Children are still present or the mount is referenced */ if (err == EBUSY) return (set_error(lbh, BE_ERR_DESTROYMNT)); return (set_error(lbh, BE_ERR_UNKNOWN)); } if ((options & BE_DESTROY_ORIGIN) == 0) return (0); /* The origin can't possibly be shorter than the BE root */ rootlen = strlen(lbh->root); if (*origin == '\0' || strlen(origin) <= rootlen + 1) return (set_error(lbh, BE_ERR_INVORIGIN)); /* * We'll be chopping off the BE root and running this back through * be_destroy, so that we properly handle the origin snapshot whether * it be that of a deep BE or not. */ if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/') return (0); return (be_destroy(lbh, origin + rootlen + 1, options & ~BE_DESTROY_ORIGIN)); } int be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, bool recursive, char *result) { char buf[BE_MAXPATHLEN]; time_t rawtime; int len, err; be_root_concat(lbh, source, buf); if ((err = be_exists(lbh, buf)) != 0) return (set_error(lbh, err)); if (snap_name != NULL) { if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf)) return (set_error(lbh, BE_ERR_INVALIDNAME)); if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf)) return (set_error(lbh, BE_ERR_INVALIDNAME)); if (result != NULL) snprintf(result, BE_MAXPATHLEN, "%s@%s", source, snap_name); } else { time(&rawtime); len = strlen(buf); strftime(buf + len, sizeof(buf) - len, "@%F-%T", localtime(&rawtime)); if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1, sizeof(buf)) >= sizeof(buf)) return (set_error(lbh, BE_ERR_INVALIDNAME)); } if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { switch (err) { case EZFS_INVALIDNAME: return (set_error(lbh, BE_ERR_INVALIDNAME)); default: /* * The other errors that zfs_ioc_snapshot might return * shouldn't happen if we've set things up properly, so * we'll gloss over them and call it UNKNOWN as it will * require further triage. */ if (errno == ENOTSUP) return (set_error(lbh, BE_ERR_NOPOOL)); return (set_error(lbh, BE_ERR_UNKNOWN)); } } return (BE_ERR_SUCCESS); } /* * Create the boot environment specified by the name parameter */ int be_create(libbe_handle_t *lbh, const char *name) { int err; err = be_create_from_existing(lbh, name, be_active_path(lbh)); return (set_error(lbh, err)); } static int be_deep_clone_prop(int prop, void *cb) { int err; struct libbe_dccb *dccb; zprop_source_t src; char pval[BE_MAXPATHLEN]; char source[BE_MAXPATHLEN]; char *val; dccb = cb; /* Skip some properties we don't want to touch */ if (prop == ZFS_PROP_CANMOUNT) return (ZPROP_CONT); /* Don't copy readonly properties */ if (zfs_prop_readonly(prop)) return (ZPROP_CONT); if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, sizeof(pval), &src, (char *)&source, sizeof(source), false))) /* Just continue if we fail to read a property */ return (ZPROP_CONT); /* Only copy locally defined properties */ if (src != ZPROP_SRC_LOCAL) return (ZPROP_CONT); /* Augment mountpoint with altroot, if needed */ val = pval; if (prop == ZFS_PROP_MOUNTPOINT) val = be_mountpoint_augmented(dccb->lbh, val); nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val); return (ZPROP_CONT); } static int be_deep_clone(zfs_handle_t *ds, void *data) { int err; char be_path[BE_MAXPATHLEN]; char snap_path[BE_MAXPATHLEN]; const char *dspath; char *dsname; zfs_handle_t *snap_hdl; nvlist_t *props; struct libbe_deep_clone *isdc, sdc; struct libbe_dccb dccb; isdc = (struct libbe_deep_clone *)data; dspath = zfs_get_name(ds); if ((dsname = strrchr(dspath, '/')) == NULL) return (BE_ERR_UNKNOWN); dsname++; if (isdc->bename == NULL) snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname); else snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename); snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname); if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) return (set_error(isdc->lbh, BE_ERR_EXISTS)); if ((snap_hdl = zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) return (set_error(isdc->lbh, BE_ERR_ZFSOPEN)); nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); nvlist_add_string(props, "canmount", "noauto"); dccb.lbh = isdc->lbh; dccb.zhp = ds; dccb.props = props; if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) return (-1); if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) err = BE_ERR_ZFSCLONE; nvlist_free(props); zfs_close(snap_hdl); /* Failed to clone */ if (err != BE_ERR_SUCCESS) return (set_error(isdc->lbh, err)); sdc.lbh = isdc->lbh; sdc.bename = NULL; sdc.snapname = isdc->snapname; sdc.be_root = (char *)&be_path; err = zfs_iter_filesystems(ds, be_deep_clone, &sdc); return (err); } /* * Create the boot environment from pre-existing snapshot */ int be_create_from_existing_snap(libbe_handle_t *lbh, const char *name, const char *snap) { int err; char be_path[BE_MAXPATHLEN]; char snap_path[BE_MAXPATHLEN]; const char *bename; char *parentname, *snapname; zfs_handle_t *parent_hdl; struct libbe_deep_clone sdc; if ((err = be_validate_name(lbh, name)) != 0) return (set_error(lbh, err)); if ((err = be_root_concat(lbh, snap, snap_path)) != 0) return (set_error(lbh, err)); if ((err = be_validate_snap(lbh, snap_path)) != 0) return (set_error(lbh, err)); if ((err = be_root_concat(lbh, name, be_path)) != 0) return (set_error(lbh, err)); if ((bename = strrchr(name, '/')) == NULL) bename = name; else bename++; if ((parentname = strdup(snap_path)) == NULL) return (set_error(lbh, BE_ERR_UNKNOWN)); snapname = strchr(parentname, '@'); if (snapname == NULL) { free(parentname); return (set_error(lbh, BE_ERR_UNKNOWN)); } *snapname = '\0'; snapname++; sdc.lbh = lbh; sdc.bename = bename; sdc.snapname = snapname; sdc.be_root = lbh->root; parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); err = be_deep_clone(parent_hdl, &sdc); free(parentname); return (set_error(lbh, err)); } /* * Create a boot environment from an existing boot environment */ int be_create_from_existing(libbe_handle_t *lbh, const char *name, const char *old) { int err; char buf[BE_MAXPATHLEN]; if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf)) != 0) return (set_error(lbh, err)); err = be_create_from_existing_snap(lbh, name, (char *)buf); return (set_error(lbh, err)); } /* * Verifies that a snapshot has a valid name, exists, and has a mountpoint of * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon * failure. Does not set the internal library error state. */ int be_validate_snap(libbe_handle_t *lbh, const char *snap_name) { if (strlen(snap_name) >= BE_MAXPATHLEN) return (BE_ERR_PATHLEN); if (!zfs_dataset_exists(lbh->lzh, snap_name, ZFS_TYPE_SNAPSHOT)) return (BE_ERR_NOENT); return (BE_ERR_SUCCESS); } /* * Idempotently appends the name argument to the root boot environment path * and copies the resulting string into the result buffer (which is assumed * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, * or BE_ERR_INVALIDNAME if the name is a path that does not begin with * zfs_be_root. Does not set internal library error state. */ int be_root_concat(libbe_handle_t *lbh, const char *name, char *result) { size_t name_len, root_len; name_len = strlen(name); root_len = strlen(lbh->root); /* Act idempotently; return be name if it is already a full path */ if (strrchr(name, '/') != NULL) { if (strstr(name, lbh->root) != name) return (BE_ERR_INVALIDNAME); if (name_len >= BE_MAXPATHLEN) return (BE_ERR_PATHLEN); strlcpy(result, name, BE_MAXPATHLEN); return (BE_ERR_SUCCESS); } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, name); return (BE_ERR_SUCCESS); } return (BE_ERR_PATHLEN); } /* * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME * or BE_ERR_PATHLEN. * Does not set internal library error state. */ int be_validate_name(libbe_handle_t *lbh, const char *name) { for (int i = 0; *name; i++) { char c = *(name++); if (isalnum(c) || (c == '-') || (c == '_') || (c == '.')) continue; return (BE_ERR_INVALIDNAME); } /* * Impose the additional restriction that the entire dataset name must * not exceed the maximum length of a dataset, i.e. MAXNAMELEN. */ if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN) return (BE_ERR_PATHLEN); return (BE_ERR_SUCCESS); } /* * usage */ int be_rename(libbe_handle_t *lbh, const char *old, const char *new) { char full_old[BE_MAXPATHLEN]; char full_new[BE_MAXPATHLEN]; zfs_handle_t *zfs_hdl; int err; /* * be_validate_name is documented not to set error state, so we should * do so here. */ if ((err = be_validate_name(lbh, new)) != 0) return (set_error(lbh, err)); if ((err = be_root_concat(lbh, old, full_old)) != 0) return (set_error(lbh, err)); if ((err = be_root_concat(lbh, new, full_new)) != 0) return (set_error(lbh, err)); if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) return (set_error(lbh, BE_ERR_NOENT)); if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) return (set_error(lbh, BE_ERR_EXISTS)); if ((zfs_hdl = zfs_open(lbh->lzh, full_old, ZFS_TYPE_FILESYSTEM)) == NULL) return (set_error(lbh, BE_ERR_ZFSOPEN)); /* recurse, nounmount, forceunmount */ struct renameflags flags = { .nounmount = 1, }; err = zfs_rename(zfs_hdl, NULL, full_new, flags); zfs_close(zfs_hdl); if (err != 0) return (set_error(lbh, BE_ERR_UNKNOWN)); return (0); } int be_export(libbe_handle_t *lbh, const char *bootenv, int fd) { char snap_name[BE_MAXPATHLEN]; char buf[BE_MAXPATHLEN]; zfs_handle_t *zfs; int err; if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) /* Use the error set by be_snapshot */ return (err); be_root_concat(lbh, snap_name, buf); if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) return (set_error(lbh, BE_ERR_ZFSOPEN)); err = zfs_send_one(zfs, NULL, fd, 0); zfs_close(zfs); return (err); } int be_import(libbe_handle_t *lbh, const char *bootenv, int fd) { char buf[BE_MAXPATHLEN]; nvlist_t *props; zfs_handle_t *zfs; recvflags_t flags = { .nomount = 1 }; int err; be_root_concat(lbh, bootenv, buf); if ((err = zfs_receive(lbh->lzh, buf, NULL, &flags, fd, NULL)) != 0) { switch (err) { case EINVAL: return (set_error(lbh, BE_ERR_NOORIGIN)); case ENOENT: return (set_error(lbh, BE_ERR_NOENT)); case EIO: return (set_error(lbh, BE_ERR_IO)); default: return (set_error(lbh, BE_ERR_UNKNOWN)); } } if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_FILESYSTEM)) == NULL) return (set_error(lbh, BE_ERR_ZFSOPEN)); nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); nvlist_add_string(props, "canmount", "noauto"); nvlist_add_string(props, "mountpoint", "/"); err = zfs_prop_set_list(zfs, props); nvlist_free(props); zfs_close(zfs); if (err != 0) return (set_error(lbh, BE_ERR_UNKNOWN)); return (0); } #if SOON static int be_create_child_noent(libbe_handle_t *lbh, const char *active, const char *child_path) { nvlist_t *props; zfs_handle_t *zfs; int err; nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); nvlist_add_string(props, "canmount", "noauto"); nvlist_add_string(props, "mountpoint", child_path); /* Create */ if ((err = zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) != 0) { switch (err) { case EZFS_EXISTS: return (set_error(lbh, BE_ERR_EXISTS)); case EZFS_NOENT: return (set_error(lbh, BE_ERR_NOENT)); case EZFS_BADTYPE: case EZFS_BADVERSION: return (set_error(lbh, BE_ERR_NOPOOL)); case EZFS_BADPROP: default: /* We set something up wrong, probably... */ return (set_error(lbh, BE_ERR_UNKNOWN)); } } nvlist_free(props); if ((zfs = zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) return (set_error(lbh, BE_ERR_ZFSOPEN)); /* Set props */ if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) { zfs_close(zfs); /* * Similar to other cases, this shouldn't fail unless we've * done something wrong. This is a new dataset that shouldn't * have been mounted anywhere between creation and now. */ if (err == EZFS_NOMEM) return (set_error(lbh, BE_ERR_NOMEM)); return (set_error(lbh, BE_ERR_UNKNOWN)); } zfs_close(zfs); return (BE_ERR_SUCCESS); } static int be_create_child_cloned(libbe_handle_t *lbh, const char *active) { char buf[BE_MAXPATHLEN], tmp[BE_MAXPATHLEN];; zfs_handle_t *zfs; int err; /* XXX TODO ? */ /* * Establish if the existing path is a zfs dataset or just * the subdirectory of one */ strlcpy(tmp, "tmp/be_snap.XXXXX", sizeof(tmp)); if (mktemp(tmp) == NULL) return (set_error(lbh, BE_ERR_UNKNOWN)); be_root_concat(lbh, tmp, buf); printf("Here %s?\n", buf); if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) { switch (err) { case EZFS_INVALIDNAME: return (set_error(lbh, BE_ERR_INVALIDNAME)); default: /* * The other errors that zfs_ioc_snapshot might return * shouldn't happen if we've set things up properly, so * we'll gloss over them and call it UNKNOWN as it will * require further triage. */ if (errno == ENOTSUP) return (set_error(lbh, BE_ERR_NOPOOL)); return (set_error(lbh, BE_ERR_UNKNOWN)); } } /* Clone */ if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) return (BE_ERR_ZFSOPEN); if ((err = zfs_clone(zfs, active, NULL)) != 0) /* XXX TODO correct error */ return (set_error(lbh, BE_ERR_UNKNOWN)); /* set props */ zfs_close(zfs); return (BE_ERR_SUCCESS); } int be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) { struct stat sb; char active[BE_MAXPATHLEN], buf[BE_MAXPATHLEN]; nvlist_t *props; const char *s; /* Require absolute paths */ if (*child_path != '/') return (set_error(lbh, BE_ERR_BADPATH)); strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN); strcpy(buf, active); /* Create non-mountable parent dataset(s) */ s = child_path; for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { size_t len = p - s; strncat(buf, s, len); nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); nvlist_add_string(props, "canmount", "off"); nvlist_add_string(props, "mountpoint", "none"); zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); nvlist_free(props); } /* Path does not exist as a descendent of / yet */ if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN) return (set_error(lbh, BE_ERR_PATHLEN)); if (stat(child_path, &sb) != 0) { /* Verify that error is ENOENT */ if (errno != ENOENT) return (set_error(lbh, BE_ERR_UNKNOWN)); return (be_create_child_noent(lbh, active, child_path)); } else if (cp_if_exists) /* Path is already a descendent of / and should be copied */ return (be_create_child_cloned(lbh, active)); return (set_error(lbh, BE_ERR_EXISTS)); } #endif /* SOON */ static int be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid, const char *zfsdev) { nvlist_t **child; uint64_t vdev_guid; int c, children; if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child, &children) == 0) { for (c = 0; c < children; ++c) if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0) return (1); return (0); } if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) != 0) { return (1); } if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) { perror("ZFS_IOC_NEXTBOOT failed"); return (1); } return (0); } /* * Deactivate old BE dataset; currently just sets canmount=noauto */ static int be_deactivate(libbe_handle_t *lbh, const char *ds) { zfs_handle_t *zfs; if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL) return (1); if (zfs_prop_set(zfs, "canmount", "noauto") != 0) return (1); zfs_close(zfs); return (0); } int be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary) { char be_path[BE_MAXPATHLEN]; char buf[BE_MAXPATHLEN]; nvlist_t *config, *dsprops, *vdevs; char *origin; uint64_t pool_guid; zfs_handle_t *zhp; int err; be_root_concat(lbh, bootenv, be_path); /* Note: be_exists fails if mountpoint is not / */ if ((err = be_exists(lbh, be_path)) != 0) return (set_error(lbh, err)); if (temporary) { config = zpool_get_config(lbh->active_phandle, NULL); if (config == NULL) /* config should be fetchable... */ return (set_error(lbh, BE_ERR_UNKNOWN)); if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &pool_guid) != 0) /* Similarly, it shouldn't be possible */ return (set_error(lbh, BE_ERR_UNKNOWN)); /* Expected format according to zfsbootcfg(8) man */ snprintf(buf, sizeof(buf), "zfs:%s:", be_path); /* We have no config tree */ if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &vdevs) != 0) return (set_error(lbh, BE_ERR_NOPOOL)); return (be_set_nextboot(lbh, vdevs, pool_guid, buf)); } else { if (be_deactivate(lbh, lbh->bootfs) != 0) return (-1); /* Obtain bootenv zpool */ err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); if (err) return (-1); zhp = zfs_open(lbh->lzh, be_path, ZFS_TYPE_FILESYSTEM); if (zhp == NULL) return (-1); if (be_prop_list_alloc(&dsprops) != 0) return (-1); if (be_get_dataset_props(lbh, be_path, dsprops) != 0) { nvlist_free(dsprops); return (-1); } if (nvlist_lookup_string(dsprops, "origin", &origin) == 0) err = zfs_promote(zhp); nvlist_free(dsprops); zfs_close(zhp); if (err) return (-1); } return (BE_ERR_SUCCESS); } Index: projects/capsicum-test/sbin/bectl/tests/bectl_test.sh =================================================================== --- projects/capsicum-test/sbin/bectl/tests/bectl_test.sh (revision 345770) +++ projects/capsicum-test/sbin/bectl/tests/bectl_test.sh (revision 345771) @@ -1,359 +1,368 @@ # # SPDX-License-Identifier: BSD-2-Clause-FreeBSD # # Copyright (c) 2018 Kyle Evans # # 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. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. # # $FreeBSD$ ZPOOL_NAME_FILE=zpool_name get_zpool_name() { cat $ZPOOL_NAME_FILE } make_zpool_name() { mktemp -u bectl_test_XXXXXX > $ZPOOL_NAME_FILE get_zpool_name } # Establishes a bectl_create zpool that can be used for some light testing; contains # a 'default' BE and not much else. bectl_create_setup() { zpool=$1 disk=$2 mnt=$3 # Sanity check to make sure `make_zpool_name` succeeded atf_check test -n "$zpool" kldload -n -q zfs || atf_skip "ZFS module not loaded on the current system" atf_check mkdir -p ${mnt} atf_check truncate -s 1G ${disk} atf_check zpool create -o altroot=${mnt} ${zpool} ${disk} atf_check zfs create -o mountpoint=none ${zpool}/ROOT atf_check zfs create -o mountpoint=/ -o canmount=noauto \ ${zpool}/ROOT/default } bectl_create_deep_setup() { zpool=$1 disk=$2 mnt=$3 # Sanity check to make sure `make_zpool_name` succeeded atf_check test -n "$zpool" bectl_create_setup ${zpool} ${disk} ${mnt} atf_check mkdir -p ${root} atf_check -o ignore bectl -r ${zpool}/ROOT mount default ${root} atf_check mkdir -p ${root}/usr atf_check zfs create -o mountpoint=/usr -o canmount=noauto \ ${zpool}/ROOT/default/usr atf_check -o ignore bectl -r ${zpool}/ROOT umount default } bectl_cleanup() { zpool=$1 if [ -z "$zpool" ]; then echo "Skipping cleanup; zpool not set up" elif zpool get health ${zpool} >/dev/null 2>&1; then zpool destroy -f ${zpool} fi } atf_test_case bectl_create cleanup bectl_create_head() { atf_set "descr" "Check the various forms of bectl create" atf_set "require.user" root } bectl_create_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt bectl_create_setup ${zpool} ${disk} ${mount} # Test standard creation, creation of a snapshot, and creation from a # snapshot. atf_check bectl -r ${zpool}/ROOT create -e default default2 atf_check bectl -r ${zpool}/ROOT create default2@test_snap atf_check bectl -r ${zpool}/ROOT create -e default2@test_snap default3 } bectl_create_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_destroy cleanup bectl_destroy_head() { atf_set "descr" "Check bectl destroy" atf_set "require.user" root } bectl_destroy_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt + root=${mount}/root bectl_create_setup ${zpool} ${disk} ${mount} atf_check bectl -r ${zpool}/ROOT create -e default default2 atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2 atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2 atf_check -e not-empty -s not-exit:0 zfs get mountpoint ${zpool}/ROOT/default2 + + # Test origin snapshot deletion when the snapshot to be destroyed + # belongs to a mounted dataset, see PR 236043. + atf_check mkdir -p ${root} + atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root} + atf_check bectl -r ${zpool}/ROOT create -e default default3 + atf_check bectl -r ${zpool}/ROOT destroy -o default3 + atf_check bectl -r ${zpool}/ROOT unmount default } bectl_destroy_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_export_import cleanup bectl_export_import_head() { atf_set "descr" "Check bectl export and import" atf_set "require.user" root } bectl_export_import_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt bectl_create_setup ${zpool} ${disk} ${mount} atf_check -o save:exported bectl -r ${zpool}/ROOT export default atf_check -x "bectl -r ${zpool}/ROOT import default2 < exported" atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2 atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2 atf_check -e not-empty -s not-exit:0 zfs get mountpoint \ ${zpool}/ROOT/default2 } bectl_export_import_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_list cleanup bectl_list_head() { atf_set "descr" "Check bectl list" atf_set "require.user" root } bectl_list_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt bectl_create_setup ${zpool} ${disk} ${mount} # Test the list functionality, including that BEs come and go away # as they're created and destroyed. Creation and destruction tests # use the 'zfs' utility to verify that they're actually created, so # these are just light tests that 'list' is picking them up. atf_check -o save:list.out bectl -r ${zpool}/ROOT list atf_check -o not-empty grep 'default' list.out atf_check bectl -r ${zpool}/ROOT create -e default default2 atf_check -o save:list.out bectl -r ${zpool}/ROOT list atf_check -o not-empty grep 'default2' list.out atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2 atf_check -o save:list.out bectl -r ${zpool}/ROOT list atf_check -s not-exit:0 grep 'default2' list.out # XXX TODO: Formatting checks } bectl_list_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_mount cleanup bectl_mount_head() { atf_set "descr" "Check bectl mount/unmount" atf_set "require.user" root } bectl_mount_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt root=${mount}/root bectl_create_deep_setup ${zpool} ${disk} ${mount} atf_check mkdir -p ${root} # Test unmount first... atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root} atf_check -o not-empty -x "mount | grep '^${zpool}/ROOT/default'" atf_check bectl -r ${zpool}/ROOT unmount default atf_check -s not-exit:0 -x "mount | grep '^${zpool}/ROOT/default'" # Then umount! atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root} atf_check -o not-empty -x "mount | grep '^${zpool}/ROOT/default'" atf_check bectl -r ${zpool}/ROOT umount default atf_check -s not-exit:0 -x "mount | grep '^${zpool}/ROOT/default'" } bectl_mount_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_rename cleanup bectl_rename_head() { atf_set "descr" "Check bectl rename" atf_set "require.user" root } bectl_rename_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt bectl_create_setup ${zpool} ${disk} ${mount} atf_check bectl -r ${zpool}/ROOT rename default default2 atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2 atf_check -e not-empty -s not-exit:0 zfs get mountpoint \ ${zpool}/ROOT/default } bectl_rename_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_jail cleanup bectl_jail_head() { atf_set "descr" "Check bectl rename" atf_set "require.user" root } bectl_jail_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt root=${mount}/root if [ ! -f /rescue/rescue ]; then atf_skip "This test requires a rescue binary" fi bectl_create_deep_setup ${zpool} ${disk} ${mount} # Prepare our minimal BE... plop a rescue binary into it atf_check mkdir -p ${root} atf_check -o ignore bectl -r ${zpool}/ROOT mount default ${root} atf_check mkdir -p ${root}/rescue atf_check cp /rescue/rescue ${root}/rescue/rescue atf_check bectl -r ${zpool}/ROOT umount default # Prepare a second boot environment atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default target # When a jail name is not explicit, it should match the jail id. atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o jid=233637 default atf_check -o inline:"233637\n" -s exit:0 -x "jls -j 233637 name" atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default # Basic command-mode tests, with and without jail cleanup atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \ jail default /rescue/rescue ls -1 atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \ jail -Uo path=${root} default /rescue/rescue ls -1 atf_check [ -f ${root}/rescue/rescue ] atf_check bectl -r ${zpool}/ROOT ujail default # Batch mode tests atf_check bectl -r ${zpool}/ROOT jail -bo path=${root} default atf_check -o not-empty -x "jls | grep -F \"${root}\"" atf_check bectl -r ${zpool}/ROOT ujail default atf_check -s not-exit:0 -x "jls | grep -F \"${root}\"" # 'unjail' naming atf_check bectl -r ${zpool}/ROOT jail -b default atf_check bectl -r ${zpool}/ROOT unjail default atf_check -s not-exit:0 -x "jls | grep -F \"${root}\"" # 'unjail' by BE name. Force bectl to lookup jail id by the BE name. atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b default atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o name=bectl_test target atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail target atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default # cannot unjail an unjailed BE (by either command name) atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT ujail default atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT unjail default # set+unset atf_check bectl -r ${zpool}/ROOT jail -b -o path=${root} -u path default # Ensure that it didn't mount at ${root} atf_check -s not-exit:0 -x "mount | grep -F '${root}'" atf_check bectl -r ${zpool}/ROOT ujail default } # If a test has failed, it's possible that the boot environment hasn't # been 'unjail'ed. We want to remove the jail before 'bectl_cleanup' # attempts to destroy the zpool. bectl_jail_cleanup() { for bootenv in "default" "target"; do # mountpoint of the boot environment mountpoint="$(bectl -r bectl_test/ROOT list -H | grep ${bootenv} | awk '{print $3}')" # see if any jail paths match the boot environment mountpoint jailid="$(jls | grep ${mountpoint} | awk '{print $1}')" if [ -z "$jailid" ]; then continue; fi jail -r ${jailid} done; bectl_cleanup $(get_zpool_name) } atf_init_test_cases() { atf_add_test_case bectl_create atf_add_test_case bectl_destroy atf_add_test_case bectl_export_import atf_add_test_case bectl_list atf_add_test_case bectl_mount atf_add_test_case bectl_rename atf_add_test_case bectl_jail } Index: projects/capsicum-test/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c =================================================================== --- projects/capsicum-test/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c (revision 345770) +++ projects/capsicum-test/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c (revision 345771) @@ -1,689 +1,698 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 Oleksandr Tymoshenko * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmcbr_if.h" #include "sdhci_if.h" #include "opt_mmccam.h" #include "bcm2835_dma.h" #include #include "bcm2835_vcbus.h" #define BCM2835_DEFAULT_SDHCI_FREQ 50 #define BCM_SDHCI_BUFFER_SIZE 512 #define NUM_DMA_SEGS 2 #ifdef DEBUG -#define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ - printf(fmt,##args); } while (0) +static int bcm2835_sdhci_debug = 0; + +TUNABLE_INT("hw.bcm2835.sdhci.debug", &bcm2835_sdhci_debug); +SYSCTL_INT(_hw_sdhci, OID_AUTO, bcm2835_sdhci_debug, CTLFLAG_RWTUN, + &bcm2835_sdhci_debug, 0, "bcm2835 SDHCI debug level"); + +#define dprintf(fmt, args...) \ + do { \ + if (bcm2835_sdhci_debug) \ + printf("%s: " fmt, __func__, ##args); \ + } while (0) #else #define dprintf(fmt, args...) #endif static int bcm2835_sdhci_hs = 1; static int bcm2835_sdhci_pio_mode = 0; static struct ofw_compat_data compat_data[] = { {"broadcom,bcm2835-sdhci", 1}, {"brcm,bcm2835-sdhci", 1}, {"brcm,bcm2835-mmc", 1}, {NULL, 0} }; TUNABLE_INT("hw.bcm2835.sdhci.hs", &bcm2835_sdhci_hs); TUNABLE_INT("hw.bcm2835.sdhci.pio_mode", &bcm2835_sdhci_pio_mode); struct bcm_sdhci_softc { device_t sc_dev; struct resource * sc_mem_res; struct resource * sc_irq_res; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_intrhand; struct mmc_request * sc_req; struct sdhci_slot sc_slot; int sc_dma_ch; bus_dma_tag_t sc_dma_tag; bus_dmamap_t sc_dma_map; vm_paddr_t sc_sdhci_buffer_phys; uint32_t cmd_and_mode; bus_addr_t dmamap_seg_addrs[NUM_DMA_SEGS]; bus_size_t dmamap_seg_sizes[NUM_DMA_SEGS]; int dmamap_seg_count; int dmamap_seg_index; int dmamap_status; }; static int bcm_sdhci_probe(device_t); static int bcm_sdhci_attach(device_t); static int bcm_sdhci_detach(device_t); static void bcm_sdhci_intr(void *); static int bcm_sdhci_get_ro(device_t, device_t); static void bcm_sdhci_dma_intr(int ch, void *arg); static void bcm_sdhci_dmacb(void *arg, bus_dma_segment_t *segs, int nseg, int err) { struct bcm_sdhci_softc *sc = arg; int i; sc->dmamap_status = err; sc->dmamap_seg_count = nseg; /* Note nseg is guaranteed to be zero if err is non-zero. */ for (i = 0; i < nseg; i++) { sc->dmamap_seg_addrs[i] = segs[i].ds_addr; sc->dmamap_seg_sizes[i] = segs[i].ds_len; } } static int bcm_sdhci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Broadcom 2708 SDHCI controller"); return (BUS_PROBE_DEFAULT); } static int bcm_sdhci_attach(device_t dev) { struct bcm_sdhci_softc *sc = device_get_softc(dev); int rid, err; phandle_t node; pcell_t cell; u_int default_freq; sc->sc_dev = dev; sc->sc_req = NULL; err = bcm2835_mbox_set_power_state(BCM2835_MBOX_POWER_ID_EMMC, TRUE); if (err != 0) { if (bootverbose) device_printf(dev, "Unable to enable the power\n"); return (err); } default_freq = 0; err = bcm2835_mbox_get_clock_rate(BCM2835_MBOX_CLOCK_ID_EMMC, &default_freq); if (err == 0) { /* Convert to MHz */ default_freq /= 1000000; } if (default_freq == 0) { node = ofw_bus_get_node(sc->sc_dev); if ((OF_getencprop(node, "clock-frequency", &cell, sizeof(cell))) > 0) default_freq = cell / 1000000; } if (default_freq == 0) default_freq = BCM2835_DEFAULT_SDHCI_FREQ; if (bootverbose) device_printf(dev, "SDHCI frequency: %dMHz\n", default_freq); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); err = ENXIO; goto fail; } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { device_printf(dev, "cannot allocate interrupt\n"); err = ENXIO; goto fail; } if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, bcm_sdhci_intr, sc, &sc->sc_intrhand)) { device_printf(dev, "cannot setup interrupt handler\n"); err = ENXIO; goto fail; } if (!bcm2835_sdhci_pio_mode) sc->sc_slot.opt = SDHCI_PLATFORM_TRANSFER; sc->sc_slot.caps = SDHCI_CAN_VDD_330 | SDHCI_CAN_VDD_180; if (bcm2835_sdhci_hs) sc->sc_slot.caps |= SDHCI_CAN_DO_HISPD; sc->sc_slot.caps |= (default_freq << SDHCI_CLOCK_BASE_SHIFT); sc->sc_slot.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DONT_SET_HISPD_BIT | SDHCI_QUIRK_MISSING_CAPS; sdhci_init_slot(dev, &sc->sc_slot, 0); sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_ANY); if (sc->sc_dma_ch == BCM_DMA_CH_INVALID) goto fail; bcm_dma_setup_intr(sc->sc_dma_ch, bcm_sdhci_dma_intr, sc); /* Allocate bus_dma resources. */ err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BCM_SDHCI_BUFFER_SIZE, NUM_DMA_SEGS, BCM_SDHCI_BUFFER_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_dma_tag); if (err) { device_printf(dev, "failed allocate DMA tag"); goto fail; } err = bus_dmamap_create(sc->sc_dma_tag, 0, &sc->sc_dma_map); if (err) { device_printf(dev, "bus_dmamap_create failed\n"); goto fail; } /* FIXME: Fix along with other BUS_SPACE_PHYSADDR instances */ sc->sc_sdhci_buffer_phys = rman_get_start(sc->sc_mem_res) + SDHCI_BUFFER; bus_generic_probe(dev); bus_generic_attach(dev); sdhci_start_slot(&sc->sc_slot); return (0); fail: if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (err); } static int bcm_sdhci_detach(device_t dev) { return (EBUSY); } static void bcm_sdhci_intr(void *arg) { struct bcm_sdhci_softc *sc = arg; sdhci_generic_intr(&sc->sc_slot); } static int bcm_sdhci_get_ro(device_t bus, device_t child) { return (0); } static inline uint32_t RD4(struct bcm_sdhci_softc *sc, bus_size_t off) { uint32_t val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off); return val; } static inline void WR4(struct bcm_sdhci_softc *sc, bus_size_t off, uint32_t val) { bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val); /* * The Arasan HC has a bug where it may lose the content of * consecutive writes to registers that are within two SD-card * clock cycles of each other (a clock domain crossing problem). */ if (sc->sc_slot.clock > 0) DELAY(((2 * 1000000) / sc->sc_slot.clock) + 1); } static uint8_t bcm_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val = RD4(sc, off & ~3); return ((val >> (off & 3)*8) & 0xff); } static uint16_t bcm_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val = RD4(sc, off & ~3); /* * Standard 32-bit handling of command and transfer mode. */ if (off == SDHCI_TRANSFER_MODE) { return (sc->cmd_and_mode >> 16); } else if (off == SDHCI_COMMAND_FLAGS) { return (sc->cmd_and_mode & 0x0000ffff); } return ((val >> (off & 3)*8) & 0xffff); } static uint32_t bcm_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhci_softc *sc = device_get_softc(dev); return RD4(sc, off); } static void bcm_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct bcm_sdhci_softc *sc = device_get_softc(dev); bus_space_read_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count); } static void bcm_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val32 = RD4(sc, off & ~3); val32 &= ~(0xff << (off & 3)*8); val32 |= (val << (off & 3)*8); WR4(sc, off & ~3, val32); } static void bcm_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val32; if (off == SDHCI_COMMAND_FLAGS) val32 = sc->cmd_and_mode; else val32 = RD4(sc, off & ~3); val32 &= ~(0xffff << (off & 3)*8); val32 |= (val << (off & 3)*8); if (off == SDHCI_TRANSFER_MODE) sc->cmd_and_mode = val32; else { WR4(sc, off & ~3, val32); if (off == SDHCI_COMMAND_FLAGS) sc->cmd_and_mode = val32; } } static void bcm_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); WR4(sc, off, val); } static void bcm_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct bcm_sdhci_softc *sc = device_get_softc(dev); bus_space_write_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count); } static void bcm_sdhci_start_dma_seg(struct bcm_sdhci_softc *sc) { struct sdhci_slot *slot; vm_paddr_t pdst, psrc; int err, idx, len, sync_op; slot = &sc->sc_slot; idx = sc->dmamap_seg_index++; len = sc->dmamap_seg_sizes[idx]; slot->offset += len; if (slot->curcmd->data->flags & MMC_DATA_READ) { bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC, BCM_DMA_SAME_ADDR, BCM_DMA_32BIT); bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_NONE, BCM_DMA_INC_ADDR, (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT); psrc = sc->sc_sdhci_buffer_phys; pdst = sc->dmamap_seg_addrs[idx]; sync_op = BUS_DMASYNC_PREREAD; } else { bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE, BCM_DMA_INC_ADDR, (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT); bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC, BCM_DMA_SAME_ADDR, BCM_DMA_32BIT); psrc = sc->dmamap_seg_addrs[idx]; pdst = sc->sc_sdhci_buffer_phys; sync_op = BUS_DMASYNC_PREWRITE; } /* * When starting a new DMA operation do the busdma sync operation, and * disable SDCHI data interrrupts because we'll be driven by DMA * interrupts (or SDHCI error interrupts) until the IO is done. */ if (idx == 0) { bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op); slot->intmask &= ~(SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END); bcm_sdhci_write_4(sc->sc_dev, &sc->sc_slot, SDHCI_SIGNAL_ENABLE, slot->intmask); } /* * Start the DMA transfer. Only programming errors (like failing to * allocate a channel) cause a non-zero return from bcm_dma_start(). */ err = bcm_dma_start(sc->sc_dma_ch, psrc, pdst, len); KASSERT((err == 0), ("bcm2835_sdhci: failed DMA start")); } static void bcm_sdhci_dma_intr(int ch, void *arg) { struct bcm_sdhci_softc *sc = (struct bcm_sdhci_softc *)arg; struct sdhci_slot *slot = &sc->sc_slot; uint32_t reg, mask; int left, sync_op; mtx_lock(&slot->mtx); /* * If there are more segments for the current dma, start the next one. * Otherwise unload the dma map and decide what to do next based on the * status of the sdhci controller and whether there's more data left. */ if (sc->dmamap_seg_index < sc->dmamap_seg_count) { bcm_sdhci_start_dma_seg(sc); mtx_unlock(&slot->mtx); return; } if (slot->curcmd->data->flags & MMC_DATA_READ) { sync_op = BUS_DMASYNC_POSTREAD; mask = SDHCI_INT_DATA_AVAIL; } else { sync_op = BUS_DMASYNC_POSTWRITE; mask = SDHCI_INT_SPACE_AVAIL; } bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op); bus_dmamap_unload(sc->sc_dma_tag, sc->sc_dma_map); sc->dmamap_seg_count = 0; sc->dmamap_seg_index = 0; left = min(BCM_SDHCI_BUFFER_SIZE, slot->curcmd->data->len - slot->offset); /* DATA END? */ reg = bcm_sdhci_read_4(slot->bus, slot, SDHCI_INT_STATUS); if (reg & SDHCI_INT_DATA_END) { /* ACK for all outstanding interrupts */ bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS, reg); /* enable INT */ slot->intmask |= SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END; bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE, slot->intmask); /* finish this data */ sdhci_finish_data(slot); } else { /* already available? */ if (reg & mask) { /* ACK for DATA_AVAIL or SPACE_AVAIL */ bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS, mask); /* continue next DMA transfer */ if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, (uint8_t *)slot->curcmd->data->data + slot->offset, left, bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) { slot->curcmd->error = MMC_ERR_NO_MEMORY; sdhci_finish_data(slot); } else { bcm_sdhci_start_dma_seg(sc); } } else { /* wait for next data by INT */ /* enable INT */ slot->intmask |= SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END; bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE, slot->intmask); } } mtx_unlock(&slot->mtx); } static void bcm_sdhci_read_dma(device_t dev, struct sdhci_slot *slot) { struct bcm_sdhci_softc *sc = device_get_softc(slot->bus); size_t left; if (sc->dmamap_seg_count != 0) { device_printf(sc->sc_dev, "DMA in use\n"); return; } left = min(BCM_SDHCI_BUFFER_SIZE, slot->curcmd->data->len - slot->offset); KASSERT((left & 3) == 0, ("%s: len = %zu, not word-aligned", __func__, left)); if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, (uint8_t *)slot->curcmd->data->data + slot->offset, left, bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) { slot->curcmd->error = MMC_ERR_NO_MEMORY; return; } /* DMA start */ bcm_sdhci_start_dma_seg(sc); } static void bcm_sdhci_write_dma(device_t dev, struct sdhci_slot *slot) { struct bcm_sdhci_softc *sc = device_get_softc(slot->bus); size_t left; if (sc->dmamap_seg_count != 0) { device_printf(sc->sc_dev, "DMA in use\n"); return; } left = min(BCM_SDHCI_BUFFER_SIZE, slot->curcmd->data->len - slot->offset); KASSERT((left & 3) == 0, ("%s: len = %zu, not word-aligned", __func__, left)); if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, (uint8_t *)slot->curcmd->data->data + slot->offset, left, bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) { slot->curcmd->error = MMC_ERR_NO_MEMORY; return; } /* DMA start */ bcm_sdhci_start_dma_seg(sc); } static int bcm_sdhci_will_handle_transfer(device_t dev, struct sdhci_slot *slot) { size_t left; /* * Do not use DMA for transfers less than block size or with a length * that is not a multiple of four. */ left = min(BCM_DMA_BLOCK_SIZE, slot->curcmd->data->len - slot->offset); if (left < BCM_DMA_BLOCK_SIZE) return (0); if (left & 0x03) return (0); return (1); } static void bcm_sdhci_start_transfer(device_t dev, struct sdhci_slot *slot, uint32_t *intmask) { /* DMA transfer FIFO 1KB */ if (slot->curcmd->data->flags & MMC_DATA_READ) bcm_sdhci_read_dma(dev, slot); else bcm_sdhci_write_dma(dev, slot); } static void bcm_sdhci_finish_transfer(device_t dev, struct sdhci_slot *slot) { sdhci_finish_data(slot); } static device_method_t bcm_sdhci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_sdhci_probe), DEVMETHOD(device_attach, bcm_sdhci_attach), DEVMETHOD(device_detach, bcm_sdhci_detach), /* Bus interface */ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), /* MMC bridge interface */ DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios), DEVMETHOD(mmcbr_request, sdhci_generic_request), DEVMETHOD(mmcbr_get_ro, bcm_sdhci_get_ro), DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), /* Platform transfer methods */ DEVMETHOD(sdhci_platform_will_handle, bcm_sdhci_will_handle_transfer), DEVMETHOD(sdhci_platform_start_transfer, bcm_sdhci_start_transfer), DEVMETHOD(sdhci_platform_finish_transfer, bcm_sdhci_finish_transfer), /* SDHCI registers accessors */ DEVMETHOD(sdhci_read_1, bcm_sdhci_read_1), DEVMETHOD(sdhci_read_2, bcm_sdhci_read_2), DEVMETHOD(sdhci_read_4, bcm_sdhci_read_4), DEVMETHOD(sdhci_read_multi_4, bcm_sdhci_read_multi_4), DEVMETHOD(sdhci_write_1, bcm_sdhci_write_1), DEVMETHOD(sdhci_write_2, bcm_sdhci_write_2), DEVMETHOD(sdhci_write_4, bcm_sdhci_write_4), DEVMETHOD(sdhci_write_multi_4, bcm_sdhci_write_multi_4), DEVMETHOD_END }; static devclass_t bcm_sdhci_devclass; static driver_t bcm_sdhci_driver = { "sdhci_bcm", bcm_sdhci_methods, sizeof(struct bcm_sdhci_softc), }; DRIVER_MODULE(sdhci_bcm, simplebus, bcm_sdhci_driver, bcm_sdhci_devclass, NULL, NULL); SDHCI_DEPEND(sdhci_bcm); #ifndef MMCCAM MMC_DECLARE_BRIDGE(sdhci_bcm); #endif Index: projects/capsicum-test/sys/dev/md/md.c =================================================================== --- projects/capsicum-test/sys/dev/md/md.c (revision 345770) +++ projects/capsicum-test/sys/dev/md/md.c (revision 345771) @@ -1,2219 +1,2235 @@ /*- * SPDX-License-Identifier: (Beerware AND BSD-3-Clause) * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ /*- * The following functions are based in the vn(4) driver: mdstart_swap(), * mdstart_vnode(), mdcreate_swap(), mdcreate_vnode() and mddestroy(), * and as such under the following copyright: * * Copyright (c) 1988 University of Utah. * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS 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. * * from: Utah Hdr: vn.c 1.13 94/04/02 * * from: @(#)vn.c 8.6 (Berkeley) 4/1/94 * From: src/sys/dev/vn/vn.c,v 1.122 2000/12/16 16:06:03 */ #include "opt_rootdevname.h" #include "opt_geom.h" #include "opt_md.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MD_MODVER 1 #define MD_SHUTDOWN 0x10000 /* Tell worker thread to terminate. */ #define MD_EXITING 0x20000 /* Worker thread is exiting. */ +#define MD_PROVIDERGONE 0x40000 /* Safe to free the softc */ #ifndef MD_NSECT #define MD_NSECT (10000 * 2) #endif struct md_req { unsigned md_unit; /* unit number */ enum md_types md_type; /* type of disk */ off_t md_mediasize; /* size of disk in bytes */ unsigned md_sectorsize; /* sectorsize */ unsigned md_options; /* options */ int md_fwheads; /* firmware heads */ int md_fwsectors; /* firmware sectors */ char *md_file; /* pathname of file to mount */ enum uio_seg md_file_seg; /* location of md_file */ char *md_label; /* label of the device (userspace) */ int *md_units; /* pointer to units array (kernel) */ size_t md_units_nitems; /* items in md_units array */ }; #ifdef COMPAT_FREEBSD32 struct md_ioctl32 { unsigned md_version; unsigned md_unit; enum md_types md_type; uint32_t md_file; off_t md_mediasize; unsigned md_sectorsize; unsigned md_options; uint64_t md_base; int md_fwheads; int md_fwsectors; uint32_t md_label; int md_pad[MDNPAD]; } __attribute__((__packed__)); CTASSERT((sizeof(struct md_ioctl32)) == 436); #define MDIOCATTACH_32 _IOC_NEWTYPE(MDIOCATTACH, struct md_ioctl32) #define MDIOCDETACH_32 _IOC_NEWTYPE(MDIOCDETACH, struct md_ioctl32) #define MDIOCQUERY_32 _IOC_NEWTYPE(MDIOCQUERY, struct md_ioctl32) #define MDIOCLIST_32 _IOC_NEWTYPE(MDIOCLIST, struct md_ioctl32) #define MDIOCRESIZE_32 _IOC_NEWTYPE(MDIOCRESIZE, struct md_ioctl32) #endif /* COMPAT_FREEBSD32 */ static MALLOC_DEFINE(M_MD, "md_disk", "Memory Disk"); static MALLOC_DEFINE(M_MDSECT, "md_sectors", "Memory Disk Sectors"); static int md_debug; SYSCTL_INT(_debug, OID_AUTO, mddebug, CTLFLAG_RW, &md_debug, 0, "Enable md(4) debug messages"); static int md_malloc_wait; SYSCTL_INT(_vm, OID_AUTO, md_malloc_wait, CTLFLAG_RW, &md_malloc_wait, 0, "Allow malloc to wait for memory allocations"); #if defined(MD_ROOT) && !defined(MD_ROOT_FSTYPE) #define MD_ROOT_FSTYPE "ufs" #endif #if defined(MD_ROOT) /* * Preloaded image gets put here. */ #if defined(MD_ROOT_SIZE) /* * We put the mfs_root symbol into the oldmfs section of the kernel object file. * Applications that patch the object with the image can determine * the size looking at the oldmfs section size within the kernel. */ u_char mfs_root[MD_ROOT_SIZE*1024] __attribute__ ((section ("oldmfs"))); const int mfs_root_size = sizeof(mfs_root); #elif defined(MD_ROOT_MEM) /* MD region already mapped in the memory */ u_char *mfs_root; int mfs_root_size; #else extern volatile u_char __weak_symbol mfs_root; extern volatile u_char __weak_symbol mfs_root_end; __GLOBL(mfs_root); __GLOBL(mfs_root_end); #define mfs_root_size ((uintptr_t)(&mfs_root_end - &mfs_root)) #endif #endif static g_init_t g_md_init; static g_fini_t g_md_fini; static g_start_t g_md_start; static g_access_t g_md_access; static void g_md_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp __unused, struct g_provider *pp); +static g_provgone_t g_md_providergone; static struct cdev *status_dev = NULL; static struct sx md_sx; static struct unrhdr *md_uh; static d_ioctl_t mdctlioctl; static struct cdevsw mdctl_cdevsw = { .d_version = D_VERSION, .d_ioctl = mdctlioctl, .d_name = MD_NAME, }; struct g_class g_md_class = { .name = "MD", .version = G_VERSION, .init = g_md_init, .fini = g_md_fini, .start = g_md_start, .access = g_md_access, .dumpconf = g_md_dumpconf, + .providergone = g_md_providergone, }; DECLARE_GEOM_CLASS(g_md_class, g_md); static LIST_HEAD(, md_s) md_softc_list = LIST_HEAD_INITIALIZER(md_softc_list); #define NINDIR (PAGE_SIZE / sizeof(uintptr_t)) #define NMASK (NINDIR-1) static int nshift; static uma_zone_t md_pbuf_zone; struct indir { uintptr_t *array; u_int total; u_int used; u_int shift; }; struct md_s { int unit; LIST_ENTRY(md_s) list; struct bio_queue_head bio_queue; struct mtx queue_mtx; struct mtx stat_mtx; struct cdev *dev; enum md_types type; off_t mediasize; unsigned sectorsize; unsigned opencount; unsigned fwheads; unsigned fwsectors; char ident[32]; unsigned flags; char name[20]; struct proc *procp; struct g_geom *gp; struct g_provider *pp; int (*start)(struct md_s *sc, struct bio *bp); struct devstat *devstat; /* MD_MALLOC related fields */ struct indir *indir; uma_zone_t uma; /* MD_PRELOAD related fields */ u_char *pl_ptr; size_t pl_len; /* MD_VNODE related fields */ struct vnode *vnode; char file[PATH_MAX]; char label[PATH_MAX]; struct ucred *cred; /* MD_SWAP related fields */ vm_object_t object; }; static struct indir * new_indir(u_int shift) { struct indir *ip; ip = malloc(sizeof *ip, M_MD, (md_malloc_wait ? M_WAITOK : M_NOWAIT) | M_ZERO); if (ip == NULL) return (NULL); ip->array = malloc(sizeof(uintptr_t) * NINDIR, M_MDSECT, (md_malloc_wait ? M_WAITOK : M_NOWAIT) | M_ZERO); if (ip->array == NULL) { free(ip, M_MD); return (NULL); } ip->total = NINDIR; ip->shift = shift; return (ip); } static void del_indir(struct indir *ip) { free(ip->array, M_MDSECT); free(ip, M_MD); } static void destroy_indir(struct md_s *sc, struct indir *ip) { int i; for (i = 0; i < NINDIR; i++) { if (!ip->array[i]) continue; if (ip->shift) destroy_indir(sc, (struct indir*)(ip->array[i])); else if (ip->array[i] > 255) uma_zfree(sc->uma, (void *)(ip->array[i])); } del_indir(ip); } /* * This function does the math and allocates the top level "indir" structure * for a device of "size" sectors. */ static struct indir * dimension(off_t size) { off_t rcnt; struct indir *ip; int layer; rcnt = size; layer = 0; while (rcnt > NINDIR) { rcnt /= NINDIR; layer++; } /* * XXX: the top layer is probably not fully populated, so we allocate * too much space for ip->array in here. */ ip = malloc(sizeof *ip, M_MD, M_WAITOK | M_ZERO); ip->array = malloc(sizeof(uintptr_t) * NINDIR, M_MDSECT, M_WAITOK | M_ZERO); ip->total = NINDIR; ip->shift = layer * nshift; return (ip); } /* * Read a given sector */ static uintptr_t s_read(struct indir *ip, off_t offset) { struct indir *cip; int idx; uintptr_t up; if (md_debug > 1) printf("s_read(%jd)\n", (intmax_t)offset); up = 0; for (cip = ip; cip != NULL;) { if (cip->shift) { idx = (offset >> cip->shift) & NMASK; up = cip->array[idx]; cip = (struct indir *)up; continue; } idx = offset & NMASK; return (cip->array[idx]); } return (0); } /* * Write a given sector, prune the tree if the value is 0 */ static int s_write(struct indir *ip, off_t offset, uintptr_t ptr) { struct indir *cip, *lip[10]; int idx, li; uintptr_t up; if (md_debug > 1) printf("s_write(%jd, %p)\n", (intmax_t)offset, (void *)ptr); up = 0; li = 0; cip = ip; for (;;) { lip[li++] = cip; if (cip->shift) { idx = (offset >> cip->shift) & NMASK; up = cip->array[idx]; if (up != 0) { cip = (struct indir *)up; continue; } /* Allocate branch */ cip->array[idx] = (uintptr_t)new_indir(cip->shift - nshift); if (cip->array[idx] == 0) return (ENOSPC); cip->used++; up = cip->array[idx]; cip = (struct indir *)up; continue; } /* leafnode */ idx = offset & NMASK; up = cip->array[idx]; if (up != 0) cip->used--; cip->array[idx] = ptr; if (ptr != 0) cip->used++; break; } if (cip->used != 0 || li == 1) return (0); li--; while (cip->used == 0 && cip != ip) { li--; idx = (offset >> lip[li]->shift) & NMASK; up = lip[li]->array[idx]; KASSERT(up == (uintptr_t)cip, ("md screwed up")); del_indir(cip); lip[li]->array[idx] = 0; lip[li]->used--; cip = lip[li]; } return (0); } static int g_md_access(struct g_provider *pp, int r, int w, int e) { struct md_s *sc; sc = pp->geom->softc; if (sc == NULL) { if (r <= 0 && w <= 0 && e <= 0) return (0); return (ENXIO); } r += pp->acr; w += pp->acw; e += pp->ace; if ((sc->flags & MD_READONLY) != 0 && w > 0) return (EROFS); if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) { sc->opencount = 1; } else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) { sc->opencount = 0; } return (0); } static void g_md_start(struct bio *bp) { struct md_s *sc; sc = bp->bio_to->geom->softc; if ((bp->bio_cmd == BIO_READ) || (bp->bio_cmd == BIO_WRITE)) { mtx_lock(&sc->stat_mtx); devstat_start_transaction_bio(sc->devstat, bp); mtx_unlock(&sc->stat_mtx); } mtx_lock(&sc->queue_mtx); bioq_disksort(&sc->bio_queue, bp); - mtx_unlock(&sc->queue_mtx); wakeup(sc); + mtx_unlock(&sc->queue_mtx); } #define MD_MALLOC_MOVE_ZERO 1 #define MD_MALLOC_MOVE_FILL 2 #define MD_MALLOC_MOVE_READ 3 #define MD_MALLOC_MOVE_WRITE 4 #define MD_MALLOC_MOVE_CMP 5 static int md_malloc_move_ma(vm_page_t **mp, int *ma_offs, unsigned sectorsize, void *ptr, u_char fill, int op) { struct sf_buf *sf; vm_page_t m, *mp1; char *p, first; off_t *uc; unsigned n; int error, i, ma_offs1, sz, first_read; m = NULL; error = 0; sf = NULL; /* if (op == MD_MALLOC_MOVE_CMP) { gcc */ first = 0; first_read = 0; uc = ptr; mp1 = *mp; ma_offs1 = *ma_offs; /* } */ sched_pin(); for (n = sectorsize; n != 0; n -= sz) { sz = imin(PAGE_SIZE - *ma_offs, n); if (m != **mp) { if (sf != NULL) sf_buf_free(sf); m = **mp; sf = sf_buf_alloc(m, SFB_CPUPRIVATE | (md_malloc_wait ? 0 : SFB_NOWAIT)); if (sf == NULL) { error = ENOMEM; break; } } p = (char *)sf_buf_kva(sf) + *ma_offs; switch (op) { case MD_MALLOC_MOVE_ZERO: bzero(p, sz); break; case MD_MALLOC_MOVE_FILL: memset(p, fill, sz); break; case MD_MALLOC_MOVE_READ: bcopy(ptr, p, sz); cpu_flush_dcache(p, sz); break; case MD_MALLOC_MOVE_WRITE: bcopy(p, ptr, sz); break; case MD_MALLOC_MOVE_CMP: for (i = 0; i < sz; i++, p++) { if (!first_read) { *uc = (u_char)*p; first = *p; first_read = 1; } else if (*p != first) { error = EDOOFUS; break; } } break; default: KASSERT(0, ("md_malloc_move_ma unknown op %d\n", op)); break; } if (error != 0) break; *ma_offs += sz; *ma_offs %= PAGE_SIZE; if (*ma_offs == 0) (*mp)++; ptr = (char *)ptr + sz; } if (sf != NULL) sf_buf_free(sf); sched_unpin(); if (op == MD_MALLOC_MOVE_CMP && error != 0) { *mp = mp1; *ma_offs = ma_offs1; } return (error); } static int md_malloc_move_vlist(bus_dma_segment_t **pvlist, int *pma_offs, unsigned len, void *ptr, u_char fill, int op) { bus_dma_segment_t *vlist; uint8_t *p, *end, first; off_t *uc; int ma_offs, seg_len; vlist = *pvlist; ma_offs = *pma_offs; uc = ptr; for (; len != 0; len -= seg_len) { seg_len = imin(vlist->ds_len - ma_offs, len); p = (uint8_t *)(uintptr_t)vlist->ds_addr + ma_offs; switch (op) { case MD_MALLOC_MOVE_ZERO: bzero(p, seg_len); break; case MD_MALLOC_MOVE_FILL: memset(p, fill, seg_len); break; case MD_MALLOC_MOVE_READ: bcopy(ptr, p, seg_len); cpu_flush_dcache(p, seg_len); break; case MD_MALLOC_MOVE_WRITE: bcopy(p, ptr, seg_len); break; case MD_MALLOC_MOVE_CMP: end = p + seg_len; first = *uc = *p; /* Confirm all following bytes match the first */ while (++p < end) { if (*p != first) return (EDOOFUS); } break; default: KASSERT(0, ("md_malloc_move_vlist unknown op %d\n", op)); break; } ma_offs += seg_len; if (ma_offs == vlist->ds_len) { ma_offs = 0; vlist++; } ptr = (uint8_t *)ptr + seg_len; } *pvlist = vlist; *pma_offs = ma_offs; return (0); } static int mdstart_malloc(struct md_s *sc, struct bio *bp) { u_char *dst; vm_page_t *m; bus_dma_segment_t *vlist; int i, error, error1, ma_offs, notmapped; off_t secno, nsec, uc; uintptr_t sp, osp; switch (bp->bio_cmd) { case BIO_READ: case BIO_WRITE: case BIO_DELETE: break; default: return (EOPNOTSUPP); } notmapped = (bp->bio_flags & BIO_UNMAPPED) != 0; vlist = (bp->bio_flags & BIO_VLIST) != 0 ? (bus_dma_segment_t *)bp->bio_data : NULL; if (notmapped) { m = bp->bio_ma; ma_offs = bp->bio_ma_offset; dst = NULL; KASSERT(vlist == NULL, ("vlists cannot be unmapped")); } else if (vlist != NULL) { ma_offs = bp->bio_ma_offset; dst = NULL; } else { dst = bp->bio_data; } nsec = bp->bio_length / sc->sectorsize; secno = bp->bio_offset / sc->sectorsize; error = 0; while (nsec--) { osp = s_read(sc->indir, secno); if (bp->bio_cmd == BIO_DELETE) { if (osp != 0) error = s_write(sc->indir, secno, 0); } else if (bp->bio_cmd == BIO_READ) { if (osp == 0) { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, NULL, 0, MD_MALLOC_MOVE_ZERO); } else if (vlist != NULL) { error = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, NULL, 0, MD_MALLOC_MOVE_ZERO); } else bzero(dst, sc->sectorsize); } else if (osp <= 255) { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, NULL, osp, MD_MALLOC_MOVE_FILL); } else if (vlist != NULL) { error = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, NULL, osp, MD_MALLOC_MOVE_FILL); } else memset(dst, osp, sc->sectorsize); } else { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_READ); } else if (vlist != NULL) { error = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_READ); } else { bcopy((void *)osp, dst, sc->sectorsize); cpu_flush_dcache(dst, sc->sectorsize); } } osp = 0; } else if (bp->bio_cmd == BIO_WRITE) { if (sc->flags & MD_COMPRESS) { if (notmapped) { error1 = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, &uc, 0, MD_MALLOC_MOVE_CMP); i = error1 == 0 ? sc->sectorsize : 0; } else if (vlist != NULL) { error1 = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, &uc, 0, MD_MALLOC_MOVE_CMP); i = error1 == 0 ? sc->sectorsize : 0; } else { uc = dst[0]; for (i = 1; i < sc->sectorsize; i++) { if (dst[i] != uc) break; } } } else { i = 0; uc = 0; } if (i == sc->sectorsize) { if (osp != uc) error = s_write(sc->indir, secno, uc); } else { if (osp <= 255) { sp = (uintptr_t)uma_zalloc(sc->uma, md_malloc_wait ? M_WAITOK : M_NOWAIT); if (sp == 0) { error = ENOSPC; break; } if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, (void *)sp, 0, MD_MALLOC_MOVE_WRITE); } else if (vlist != NULL) { error = md_malloc_move_vlist( &vlist, &ma_offs, sc->sectorsize, (void *)sp, 0, MD_MALLOC_MOVE_WRITE); } else { bcopy(dst, (void *)sp, sc->sectorsize); } error = s_write(sc->indir, secno, sp); } else { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_WRITE); } else if (vlist != NULL) { error = md_malloc_move_vlist( &vlist, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_WRITE); } else { bcopy(dst, (void *)osp, sc->sectorsize); } osp = 0; } } } else { error = EOPNOTSUPP; } if (osp > 255) uma_zfree(sc->uma, (void*)osp); if (error != 0) break; secno++; if (!notmapped && vlist == NULL) dst += sc->sectorsize; } bp->bio_resid = 0; return (error); } static void mdcopyto_vlist(void *src, bus_dma_segment_t *vlist, off_t offset, off_t len) { off_t seg_len; while (offset >= vlist->ds_len) { offset -= vlist->ds_len; vlist++; } while (len != 0) { seg_len = omin(len, vlist->ds_len - offset); bcopy(src, (void *)(uintptr_t)(vlist->ds_addr + offset), seg_len); offset = 0; src = (uint8_t *)src + seg_len; len -= seg_len; vlist++; } } static void mdcopyfrom_vlist(bus_dma_segment_t *vlist, off_t offset, void *dst, off_t len) { off_t seg_len; while (offset >= vlist->ds_len) { offset -= vlist->ds_len; vlist++; } while (len != 0) { seg_len = omin(len, vlist->ds_len - offset); bcopy((void *)(uintptr_t)(vlist->ds_addr + offset), dst, seg_len); offset = 0; dst = (uint8_t *)dst + seg_len; len -= seg_len; vlist++; } } static int mdstart_preload(struct md_s *sc, struct bio *bp) { uint8_t *p; p = sc->pl_ptr + bp->bio_offset; switch (bp->bio_cmd) { case BIO_READ: if ((bp->bio_flags & BIO_VLIST) != 0) { mdcopyto_vlist(p, (bus_dma_segment_t *)bp->bio_data, bp->bio_ma_offset, bp->bio_length); } else { bcopy(p, bp->bio_data, bp->bio_length); } cpu_flush_dcache(bp->bio_data, bp->bio_length); break; case BIO_WRITE: if ((bp->bio_flags & BIO_VLIST) != 0) { mdcopyfrom_vlist((bus_dma_segment_t *)bp->bio_data, bp->bio_ma_offset, p, bp->bio_length); } else { bcopy(bp->bio_data, p, bp->bio_length); } break; } bp->bio_resid = 0; return (0); } static int mdstart_vnode(struct md_s *sc, struct bio *bp) { int error; struct uio auio; struct iovec aiov; struct iovec *piov; struct mount *mp; struct vnode *vp; struct buf *pb; bus_dma_segment_t *vlist; struct thread *td; off_t iolen, iostart, len, zerosize; int ma_offs, npages; switch (bp->bio_cmd) { case BIO_READ: auio.uio_rw = UIO_READ; break; case BIO_WRITE: case BIO_DELETE: auio.uio_rw = UIO_WRITE; break; case BIO_FLUSH: break; default: return (EOPNOTSUPP); } td = curthread; vp = sc->vnode; pb = NULL; piov = NULL; ma_offs = bp->bio_ma_offset; len = bp->bio_length; /* * VNODE I/O * * If an error occurs, we set BIO_ERROR but we do not set * B_INVAL because (for a write anyway), the buffer is * still valid. */ if (bp->bio_cmd == BIO_FLUSH) { (void) vn_start_write(vp, &mp, V_WAIT); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); error = VOP_FSYNC(vp, MNT_WAIT, td); VOP_UNLOCK(vp, 0); vn_finished_write(mp); return (error); } auio.uio_offset = (vm_ooffset_t)bp->bio_offset; auio.uio_resid = bp->bio_length; auio.uio_segflg = UIO_SYSSPACE; auio.uio_td = td; if (bp->bio_cmd == BIO_DELETE) { /* * Emulate BIO_DELETE by writing zeros. */ zerosize = ZERO_REGION_SIZE - (ZERO_REGION_SIZE % sc->sectorsize); auio.uio_iovcnt = howmany(bp->bio_length, zerosize); piov = malloc(sizeof(*piov) * auio.uio_iovcnt, M_MD, M_WAITOK); auio.uio_iov = piov; while (len > 0) { piov->iov_base = __DECONST(void *, zero_region); piov->iov_len = len; if (len > zerosize) piov->iov_len = zerosize; len -= piov->iov_len; piov++; } piov = auio.uio_iov; } else if ((bp->bio_flags & BIO_VLIST) != 0) { piov = malloc(sizeof(*piov) * bp->bio_ma_n, M_MD, M_WAITOK); auio.uio_iov = piov; vlist = (bus_dma_segment_t *)bp->bio_data; while (len > 0) { piov->iov_base = (void *)(uintptr_t)(vlist->ds_addr + ma_offs); piov->iov_len = vlist->ds_len - ma_offs; if (piov->iov_len > len) piov->iov_len = len; len -= piov->iov_len; ma_offs = 0; vlist++; piov++; } auio.uio_iovcnt = piov - auio.uio_iov; piov = auio.uio_iov; } else if ((bp->bio_flags & BIO_UNMAPPED) != 0) { pb = uma_zalloc(md_pbuf_zone, M_WAITOK); bp->bio_resid = len; unmapped_step: npages = atop(min(MAXPHYS, round_page(len + (ma_offs & PAGE_MASK)))); iolen = min(ptoa(npages) - (ma_offs & PAGE_MASK), len); KASSERT(iolen > 0, ("zero iolen")); pmap_qenter((vm_offset_t)pb->b_data, &bp->bio_ma[atop(ma_offs)], npages); aiov.iov_base = (void *)((vm_offset_t)pb->b_data + (ma_offs & PAGE_MASK)); aiov.iov_len = iolen; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_resid = iolen; } else { aiov.iov_base = bp->bio_data; aiov.iov_len = bp->bio_length; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; } iostart = auio.uio_offset; if (auio.uio_rw == UIO_READ) { vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); error = VOP_READ(vp, &auio, 0, sc->cred); VOP_UNLOCK(vp, 0); } else { (void) vn_start_write(vp, &mp, V_WAIT); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); error = VOP_WRITE(vp, &auio, sc->flags & MD_ASYNC ? 0 : IO_SYNC, sc->cred); VOP_UNLOCK(vp, 0); vn_finished_write(mp); if (error == 0) sc->flags &= ~MD_VERIFY; } /* When MD_CACHE is set, try to avoid double-caching the data. */ if (error == 0 && (sc->flags & MD_CACHE) == 0) VOP_ADVISE(vp, iostart, auio.uio_offset - 1, POSIX_FADV_DONTNEED); if (pb != NULL) { pmap_qremove((vm_offset_t)pb->b_data, npages); if (error == 0) { len -= iolen; bp->bio_resid -= iolen; ma_offs += iolen; if (len > 0) goto unmapped_step; } uma_zfree(md_pbuf_zone, pb); } free(piov, M_MD); if (pb == NULL) bp->bio_resid = auio.uio_resid; return (error); } static void md_swap_page_free(vm_page_t m) { vm_page_xunbusy(m); vm_page_lock(m); vm_page_free(m); vm_page_unlock(m); } static int mdstart_swap(struct md_s *sc, struct bio *bp) { vm_page_t m; u_char *p; vm_pindex_t i, lastp; bus_dma_segment_t *vlist; int rv, ma_offs, offs, len, lastend; switch (bp->bio_cmd) { case BIO_READ: case BIO_WRITE: case BIO_DELETE: break; default: return (EOPNOTSUPP); } p = bp->bio_data; ma_offs = (bp->bio_flags & (BIO_UNMAPPED|BIO_VLIST)) != 0 ? bp->bio_ma_offset : 0; vlist = (bp->bio_flags & BIO_VLIST) != 0 ? (bus_dma_segment_t *)bp->bio_data : NULL; /* * offs is the offset at which to start operating on the * next (ie, first) page. lastp is the last page on * which we're going to operate. lastend is the ending * position within that last page (ie, PAGE_SIZE if * we're operating on complete aligned pages). */ offs = bp->bio_offset % PAGE_SIZE; lastp = (bp->bio_offset + bp->bio_length - 1) / PAGE_SIZE; lastend = (bp->bio_offset + bp->bio_length - 1) % PAGE_SIZE + 1; rv = VM_PAGER_OK; VM_OBJECT_WLOCK(sc->object); vm_object_pip_add(sc->object, 1); for (i = bp->bio_offset / PAGE_SIZE; i <= lastp; i++) { len = ((i == lastp) ? lastend : PAGE_SIZE) - offs; m = vm_page_grab(sc->object, i, VM_ALLOC_SYSTEM); if (bp->bio_cmd == BIO_READ) { if (m->valid == VM_PAGE_BITS_ALL) rv = VM_PAGER_OK; else rv = vm_pager_get_pages(sc->object, &m, 1, NULL, NULL); if (rv == VM_PAGER_ERROR) { md_swap_page_free(m); break; } else if (rv == VM_PAGER_FAIL) { /* * Pager does not have the page. Zero * the allocated page, and mark it as * valid. Do not set dirty, the page * can be recreated if thrown out. */ pmap_zero_page(m); m->valid = VM_PAGE_BITS_ALL; } if ((bp->bio_flags & BIO_UNMAPPED) != 0) { pmap_copy_pages(&m, offs, bp->bio_ma, ma_offs, len); } else if ((bp->bio_flags & BIO_VLIST) != 0) { physcopyout_vlist(VM_PAGE_TO_PHYS(m) + offs, vlist, ma_offs, len); cpu_flush_dcache(p, len); } else { physcopyout(VM_PAGE_TO_PHYS(m) + offs, p, len); cpu_flush_dcache(p, len); } } else if (bp->bio_cmd == BIO_WRITE) { if (len == PAGE_SIZE || m->valid == VM_PAGE_BITS_ALL) rv = VM_PAGER_OK; else rv = vm_pager_get_pages(sc->object, &m, 1, NULL, NULL); if (rv == VM_PAGER_ERROR) { md_swap_page_free(m); break; } else if (rv == VM_PAGER_FAIL) pmap_zero_page(m); if ((bp->bio_flags & BIO_UNMAPPED) != 0) { pmap_copy_pages(bp->bio_ma, ma_offs, &m, offs, len); } else if ((bp->bio_flags & BIO_VLIST) != 0) { physcopyin_vlist(vlist, ma_offs, VM_PAGE_TO_PHYS(m) + offs, len); } else { physcopyin(p, VM_PAGE_TO_PHYS(m) + offs, len); } m->valid = VM_PAGE_BITS_ALL; if (m->dirty != VM_PAGE_BITS_ALL) { vm_page_dirty(m); vm_pager_page_unswapped(m); } } else if (bp->bio_cmd == BIO_DELETE) { if (len == PAGE_SIZE || m->valid == VM_PAGE_BITS_ALL) rv = VM_PAGER_OK; else rv = vm_pager_get_pages(sc->object, &m, 1, NULL, NULL); if (rv == VM_PAGER_ERROR) { md_swap_page_free(m); break; } else if (rv == VM_PAGER_FAIL) { md_swap_page_free(m); m = NULL; } else { /* Page is valid. */ if (len != PAGE_SIZE) { pmap_zero_page_area(m, offs, len); if (m->dirty != VM_PAGE_BITS_ALL) { vm_page_dirty(m); vm_pager_page_unswapped(m); } } else { vm_pager_page_unswapped(m); md_swap_page_free(m); m = NULL; } } } if (m != NULL) { vm_page_xunbusy(m); vm_page_lock(m); if (vm_page_active(m)) vm_page_reference(m); else vm_page_activate(m); vm_page_unlock(m); } /* Actions on further pages start at offset 0 */ p += PAGE_SIZE - offs; offs = 0; ma_offs += len; } vm_object_pip_wakeup(sc->object); VM_OBJECT_WUNLOCK(sc->object); return (rv != VM_PAGER_ERROR ? 0 : ENOSPC); } static int mdstart_null(struct md_s *sc, struct bio *bp) { switch (bp->bio_cmd) { case BIO_READ: bzero(bp->bio_data, bp->bio_length); cpu_flush_dcache(bp->bio_data, bp->bio_length); break; case BIO_WRITE: break; } bp->bio_resid = 0; return (0); } static void md_kthread(void *arg) { struct md_s *sc; struct bio *bp; int error; sc = arg; thread_lock(curthread); sched_prio(curthread, PRIBIO); thread_unlock(curthread); if (sc->type == MD_VNODE) curthread->td_pflags |= TDP_NORUNNINGBUF; for (;;) { mtx_lock(&sc->queue_mtx); if (sc->flags & MD_SHUTDOWN) { sc->flags |= MD_EXITING; mtx_unlock(&sc->queue_mtx); kproc_exit(0); } bp = bioq_takefirst(&sc->bio_queue); if (!bp) { msleep(sc, &sc->queue_mtx, PRIBIO | PDROP, "mdwait", 0); continue; } mtx_unlock(&sc->queue_mtx); if (bp->bio_cmd == BIO_GETATTR) { int isv = ((sc->flags & MD_VERIFY) != 0); if ((sc->fwsectors && sc->fwheads && (g_handleattr_int(bp, "GEOM::fwsectors", sc->fwsectors) || g_handleattr_int(bp, "GEOM::fwheads", sc->fwheads))) || g_handleattr_int(bp, "GEOM::candelete", 1)) error = -1; else if (sc->ident[0] != '\0' && g_handleattr_str(bp, "GEOM::ident", sc->ident)) error = -1; else if (g_handleattr_int(bp, "MNT::verified", isv)) error = -1; else error = EOPNOTSUPP; } else { error = sc->start(sc, bp); } if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) { /* * Devstat uses (bio_bcount, bio_resid) for * determining the length of the completed part of * the i/o. g_io_deliver() will translate from * bio_completed to that, but it also destroys the * bio so we must do our own translation. */ bp->bio_bcount = bp->bio_length; bp->bio_resid = (error == -1 ? bp->bio_bcount : 0); devstat_end_transaction_bio(sc->devstat, bp); } if (error != -1) { bp->bio_completed = bp->bio_length; g_io_deliver(bp, error); } } } static struct md_s * mdfind(int unit) { struct md_s *sc; LIST_FOREACH(sc, &md_softc_list, list) { if (sc->unit == unit) break; } return (sc); } static struct md_s * mdnew(int unit, int *errp, enum md_types type) { struct md_s *sc; int error; *errp = 0; if (unit == -1) unit = alloc_unr(md_uh); else unit = alloc_unr_specific(md_uh, unit); if (unit == -1) { *errp = EBUSY; return (NULL); } sc = (struct md_s *)malloc(sizeof *sc, M_MD, M_WAITOK | M_ZERO); sc->type = type; bioq_init(&sc->bio_queue); mtx_init(&sc->queue_mtx, "md bio queue", NULL, MTX_DEF); mtx_init(&sc->stat_mtx, "md stat", NULL, MTX_DEF); sc->unit = unit; sprintf(sc->name, "md%d", unit); LIST_INSERT_HEAD(&md_softc_list, sc, list); error = kproc_create(md_kthread, sc, &sc->procp, 0, 0,"%s", sc->name); if (error == 0) return (sc); LIST_REMOVE(sc, list); mtx_destroy(&sc->stat_mtx); mtx_destroy(&sc->queue_mtx); free_unr(md_uh, sc->unit); free(sc, M_MD); *errp = error; return (NULL); } static void mdinit(struct md_s *sc) { struct g_geom *gp; struct g_provider *pp; g_topology_lock(); gp = g_new_geomf(&g_md_class, "md%d", sc->unit); gp->softc = sc; pp = g_new_providerf(gp, "md%d", sc->unit); pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE; pp->mediasize = sc->mediasize; pp->sectorsize = sc->sectorsize; switch (sc->type) { case MD_MALLOC: case MD_VNODE: case MD_SWAP: pp->flags |= G_PF_ACCEPT_UNMAPPED; break; case MD_PRELOAD: case MD_NULL: break; } sc->gp = gp; sc->pp = pp; g_error_provider(pp, 0); g_topology_unlock(); sc->devstat = devstat_new_entry("md", sc->unit, sc->sectorsize, DEVSTAT_ALL_SUPPORTED, DEVSTAT_TYPE_DIRECT, DEVSTAT_PRIORITY_MAX); } static int mdcreate_malloc(struct md_s *sc, struct md_req *mdr) { uintptr_t sp; int error; off_t u; error = 0; if (mdr->md_options & ~(MD_AUTOUNIT | MD_COMPRESS | MD_RESERVE)) return (EINVAL); if (mdr->md_sectorsize != 0 && !powerof2(mdr->md_sectorsize)) return (EINVAL); /* Compression doesn't make sense if we have reserved space */ if (mdr->md_options & MD_RESERVE) mdr->md_options &= ~MD_COMPRESS; if (mdr->md_fwsectors != 0) sc->fwsectors = mdr->md_fwsectors; if (mdr->md_fwheads != 0) sc->fwheads = mdr->md_fwheads; sc->flags = mdr->md_options & (MD_COMPRESS | MD_FORCE); sc->indir = dimension(sc->mediasize / sc->sectorsize); sc->uma = uma_zcreate(sc->name, sc->sectorsize, NULL, NULL, NULL, NULL, 0x1ff, 0); if (mdr->md_options & MD_RESERVE) { off_t nsectors; nsectors = sc->mediasize / sc->sectorsize; for (u = 0; u < nsectors; u++) { sp = (uintptr_t)uma_zalloc(sc->uma, (md_malloc_wait ? M_WAITOK : M_NOWAIT) | M_ZERO); if (sp != 0) error = s_write(sc->indir, u, sp); else error = ENOMEM; if (error != 0) break; } } return (error); } static int mdsetcred(struct md_s *sc, struct ucred *cred) { char *tmpbuf; int error = 0; /* * Set credits in our softc */ if (sc->cred) crfree(sc->cred); sc->cred = crhold(cred); /* * Horrible kludge to establish credentials for NFS XXX. */ if (sc->vnode) { struct uio auio; struct iovec aiov; tmpbuf = malloc(sc->sectorsize, M_TEMP, M_WAITOK); bzero(&auio, sizeof(auio)); aiov.iov_base = tmpbuf; aiov.iov_len = sc->sectorsize; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_SYSSPACE; auio.uio_resid = aiov.iov_len; vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY); error = VOP_READ(sc->vnode, &auio, 0, sc->cred); VOP_UNLOCK(sc->vnode, 0); free(tmpbuf, M_TEMP); } return (error); } static int mdcreate_vnode(struct md_s *sc, struct md_req *mdr, struct thread *td) { struct vattr vattr; struct nameidata nd; char *fname; int error, flags; fname = mdr->md_file; if (mdr->md_file_seg == UIO_USERSPACE) { error = copyinstr(fname, sc->file, sizeof(sc->file), NULL); if (error != 0) return (error); } else if (mdr->md_file_seg == UIO_SYSSPACE) strlcpy(sc->file, fname, sizeof(sc->file)); else return (EDOOFUS); /* * If the user specified that this is a read only device, don't * set the FWRITE mask before trying to open the backing store. */ flags = FREAD | ((mdr->md_options & MD_READONLY) ? 0 : FWRITE) \ | ((mdr->md_options & MD_VERIFY) ? O_VERIFY : 0); NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, sc->file, td); error = vn_open(&nd, &flags, 0, NULL); if (error != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_vp->v_type != VREG) { error = EINVAL; goto bad; } error = VOP_GETATTR(nd.ni_vp, &vattr, td->td_ucred); if (error != 0) goto bad; if (VOP_ISLOCKED(nd.ni_vp) != LK_EXCLUSIVE) { vn_lock(nd.ni_vp, LK_UPGRADE | LK_RETRY); if (nd.ni_vp->v_iflag & VI_DOOMED) { /* Forced unmount. */ error = EBADF; goto bad; } } nd.ni_vp->v_vflag |= VV_MD; VOP_UNLOCK(nd.ni_vp, 0); if (mdr->md_fwsectors != 0) sc->fwsectors = mdr->md_fwsectors; if (mdr->md_fwheads != 0) sc->fwheads = mdr->md_fwheads; snprintf(sc->ident, sizeof(sc->ident), "MD-DEV%ju-INO%ju", (uintmax_t)vattr.va_fsid, (uintmax_t)vattr.va_fileid); sc->flags = mdr->md_options & (MD_ASYNC | MD_CACHE | MD_FORCE | MD_VERIFY); if (!(flags & FWRITE)) sc->flags |= MD_READONLY; sc->vnode = nd.ni_vp; error = mdsetcred(sc, td->td_ucred); if (error != 0) { sc->vnode = NULL; vn_lock(nd.ni_vp, LK_EXCLUSIVE | LK_RETRY); nd.ni_vp->v_vflag &= ~VV_MD; goto bad; } return (0); bad: VOP_UNLOCK(nd.ni_vp, 0); (void)vn_close(nd.ni_vp, flags, td->td_ucred, td); return (error); } +static void +g_md_providergone(struct g_provider *pp) +{ + struct md_s *sc = pp->geom->softc; + + mtx_lock(&sc->queue_mtx); + sc->flags |= MD_PROVIDERGONE; + wakeup(&sc->flags); + mtx_unlock(&sc->queue_mtx); +} + static int mddestroy(struct md_s *sc, struct thread *td) { if (sc->gp) { - sc->gp->softc = NULL; g_topology_lock(); g_wither_geom(sc->gp, ENXIO); g_topology_unlock(); - sc->gp = NULL; - sc->pp = NULL; + + mtx_lock(&sc->queue_mtx); + while (!(sc->flags & MD_PROVIDERGONE)) + msleep(&sc->flags, &sc->queue_mtx, PRIBIO, "mddestroy", 0); + mtx_unlock(&sc->queue_mtx); } if (sc->devstat) { devstat_remove_entry(sc->devstat); sc->devstat = NULL; } mtx_lock(&sc->queue_mtx); sc->flags |= MD_SHUTDOWN; wakeup(sc); while (!(sc->flags & MD_EXITING)) msleep(sc->procp, &sc->queue_mtx, PRIBIO, "mddestroy", hz / 10); mtx_unlock(&sc->queue_mtx); mtx_destroy(&sc->stat_mtx); mtx_destroy(&sc->queue_mtx); if (sc->vnode != NULL) { vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY); sc->vnode->v_vflag &= ~VV_MD; VOP_UNLOCK(sc->vnode, 0); (void)vn_close(sc->vnode, sc->flags & MD_READONLY ? FREAD : (FREAD|FWRITE), sc->cred, td); } if (sc->cred != NULL) crfree(sc->cred); if (sc->object != NULL) vm_object_deallocate(sc->object); if (sc->indir) destroy_indir(sc, sc->indir); if (sc->uma) uma_zdestroy(sc->uma); LIST_REMOVE(sc, list); free_unr(md_uh, sc->unit); free(sc, M_MD); return (0); } static int mdresize(struct md_s *sc, struct md_req *mdr) { int error, res; vm_pindex_t oldpages, newpages; switch (sc->type) { case MD_VNODE: case MD_NULL: break; case MD_SWAP: if (mdr->md_mediasize <= 0 || (mdr->md_mediasize % PAGE_SIZE) != 0) return (EDOM); oldpages = OFF_TO_IDX(round_page(sc->mediasize)); newpages = OFF_TO_IDX(round_page(mdr->md_mediasize)); if (newpages < oldpages) { VM_OBJECT_WLOCK(sc->object); vm_object_page_remove(sc->object, newpages, 0, 0); swap_pager_freespace(sc->object, newpages, oldpages - newpages); swap_release_by_cred(IDX_TO_OFF(oldpages - newpages), sc->cred); sc->object->charge = IDX_TO_OFF(newpages); sc->object->size = newpages; VM_OBJECT_WUNLOCK(sc->object); } else if (newpages > oldpages) { res = swap_reserve_by_cred(IDX_TO_OFF(newpages - oldpages), sc->cred); if (!res) return (ENOMEM); if ((mdr->md_options & MD_RESERVE) || (sc->flags & MD_RESERVE)) { error = swap_pager_reserve(sc->object, oldpages, newpages - oldpages); if (error < 0) { swap_release_by_cred( IDX_TO_OFF(newpages - oldpages), sc->cred); return (EDOM); } } VM_OBJECT_WLOCK(sc->object); sc->object->charge = IDX_TO_OFF(newpages); sc->object->size = newpages; VM_OBJECT_WUNLOCK(sc->object); } break; default: return (EOPNOTSUPP); } sc->mediasize = mdr->md_mediasize; g_topology_lock(); g_resize_provider(sc->pp, sc->mediasize); g_topology_unlock(); return (0); } static int mdcreate_swap(struct md_s *sc, struct md_req *mdr, struct thread *td) { vm_ooffset_t npage; int error; /* * Range check. Disallow negative sizes and sizes not being * multiple of page size. */ if (sc->mediasize <= 0 || (sc->mediasize % PAGE_SIZE) != 0) return (EDOM); /* * Allocate an OBJT_SWAP object. * * Note the truncation. */ if ((mdr->md_options & MD_VERIFY) != 0) return (EINVAL); npage = mdr->md_mediasize / PAGE_SIZE; if (mdr->md_fwsectors != 0) sc->fwsectors = mdr->md_fwsectors; if (mdr->md_fwheads != 0) sc->fwheads = mdr->md_fwheads; sc->object = vm_pager_allocate(OBJT_SWAP, NULL, PAGE_SIZE * npage, VM_PROT_DEFAULT, 0, td->td_ucred); if (sc->object == NULL) return (ENOMEM); sc->flags = mdr->md_options & (MD_FORCE | MD_RESERVE); if (mdr->md_options & MD_RESERVE) { if (swap_pager_reserve(sc->object, 0, npage) < 0) { error = EDOM; goto finish; } } error = mdsetcred(sc, td->td_ucred); finish: if (error != 0) { vm_object_deallocate(sc->object); sc->object = NULL; } return (error); } static int mdcreate_null(struct md_s *sc, struct md_req *mdr, struct thread *td) { /* * Range check. Disallow negative sizes and sizes not being * multiple of page size. */ if (sc->mediasize <= 0 || (sc->mediasize % PAGE_SIZE) != 0) return (EDOM); return (0); } static int kern_mdattach_locked(struct thread *td, struct md_req *mdr) { struct md_s *sc; unsigned sectsize; int error, i; sx_assert(&md_sx, SA_XLOCKED); switch (mdr->md_type) { case MD_MALLOC: case MD_PRELOAD: case MD_VNODE: case MD_SWAP: case MD_NULL: break; default: return (EINVAL); } if (mdr->md_sectorsize == 0) sectsize = DEV_BSIZE; else sectsize = mdr->md_sectorsize; if (sectsize > MAXPHYS || mdr->md_mediasize < sectsize) return (EINVAL); if (mdr->md_options & MD_AUTOUNIT) sc = mdnew(-1, &error, mdr->md_type); else { if (mdr->md_unit > INT_MAX) return (EINVAL); sc = mdnew(mdr->md_unit, &error, mdr->md_type); } if (sc == NULL) return (error); if (mdr->md_label != NULL) error = copyinstr(mdr->md_label, sc->label, sizeof(sc->label), NULL); if (error != 0) goto err_after_new; if (mdr->md_options & MD_AUTOUNIT) mdr->md_unit = sc->unit; sc->mediasize = mdr->md_mediasize; sc->sectorsize = sectsize; error = EDOOFUS; switch (sc->type) { case MD_MALLOC: sc->start = mdstart_malloc; error = mdcreate_malloc(sc, mdr); break; case MD_PRELOAD: /* * We disallow attaching preloaded memory disks via * ioctl. Preloaded memory disks are automatically * attached in g_md_init(). */ error = EOPNOTSUPP; break; case MD_VNODE: sc->start = mdstart_vnode; error = mdcreate_vnode(sc, mdr, td); break; case MD_SWAP: sc->start = mdstart_swap; error = mdcreate_swap(sc, mdr, td); break; case MD_NULL: sc->start = mdstart_null; error = mdcreate_null(sc, mdr, td); break; } err_after_new: if (error != 0) { mddestroy(sc, td); return (error); } /* Prune off any residual fractional sector */ i = sc->mediasize % sc->sectorsize; sc->mediasize -= i; mdinit(sc); return (0); } static int kern_mdattach(struct thread *td, struct md_req *mdr) { int error; sx_xlock(&md_sx); error = kern_mdattach_locked(td, mdr); sx_xunlock(&md_sx); return (error); } static int kern_mddetach_locked(struct thread *td, struct md_req *mdr) { struct md_s *sc; sx_assert(&md_sx, SA_XLOCKED); if (mdr->md_mediasize != 0 || (mdr->md_options & ~MD_FORCE) != 0) return (EINVAL); sc = mdfind(mdr->md_unit); if (sc == NULL) return (ENOENT); if (sc->opencount != 0 && !(sc->flags & MD_FORCE) && !(mdr->md_options & MD_FORCE)) return (EBUSY); return (mddestroy(sc, td)); } static int kern_mddetach(struct thread *td, struct md_req *mdr) { int error; sx_xlock(&md_sx); error = kern_mddetach_locked(td, mdr); sx_xunlock(&md_sx); return (error); } static int kern_mdresize_locked(struct md_req *mdr) { struct md_s *sc; sx_assert(&md_sx, SA_XLOCKED); if ((mdr->md_options & ~(MD_FORCE | MD_RESERVE)) != 0) return (EINVAL); sc = mdfind(mdr->md_unit); if (sc == NULL) return (ENOENT); if (mdr->md_mediasize < sc->sectorsize) return (EINVAL); if (mdr->md_mediasize < sc->mediasize && !(sc->flags & MD_FORCE) && !(mdr->md_options & MD_FORCE)) return (EBUSY); return (mdresize(sc, mdr)); } static int kern_mdresize(struct md_req *mdr) { int error; sx_xlock(&md_sx); error = kern_mdresize_locked(mdr); sx_xunlock(&md_sx); return (error); } static int kern_mdquery_locked(struct md_req *mdr) { struct md_s *sc; int error; sx_assert(&md_sx, SA_XLOCKED); sc = mdfind(mdr->md_unit); if (sc == NULL) return (ENOENT); mdr->md_type = sc->type; mdr->md_options = sc->flags; mdr->md_mediasize = sc->mediasize; mdr->md_sectorsize = sc->sectorsize; error = 0; if (mdr->md_label != NULL) { error = copyout(sc->label, mdr->md_label, strlen(sc->label) + 1); if (error != 0) return (error); } if (sc->type == MD_VNODE || (sc->type == MD_PRELOAD && mdr->md_file != NULL)) error = copyout(sc->file, mdr->md_file, strlen(sc->file) + 1); return (error); } static int kern_mdquery(struct md_req *mdr) { int error; sx_xlock(&md_sx); error = kern_mdquery_locked(mdr); sx_xunlock(&md_sx); return (error); } static int kern_mdlist_locked(struct md_req *mdr) { struct md_s *sc; int i; sx_assert(&md_sx, SA_XLOCKED); /* * Write the number of md devices to mdr->md_units[0]. * Write the unit number of the first (mdr->md_units_nitems - 2) * units to mdr->md_units[1::(mdr->md_units - 2)] and terminate the * list with -1. * * XXX: There is currently no mechanism to retrieve unit * numbers for more than (MDNPAD - 2) units. * * XXX: Due to the use of LIST_INSERT_HEAD in mdnew(), the * list of visible unit numbers not stable. */ i = 1; LIST_FOREACH(sc, &md_softc_list, list) { if (i < mdr->md_units_nitems - 1) mdr->md_units[i] = sc->unit; i++; } mdr->md_units[MIN(i, mdr->md_units_nitems - 1)] = -1; mdr->md_units[0] = i - 1; return (0); } static int kern_mdlist(struct md_req *mdr) { int error; sx_xlock(&md_sx); error = kern_mdlist_locked(mdr); sx_xunlock(&md_sx); return (error); } /* Copy members that are not userspace pointers. */ #define MD_IOCTL2REQ(mdio, mdr) do { \ (mdr)->md_unit = (mdio)->md_unit; \ (mdr)->md_type = (mdio)->md_type; \ (mdr)->md_mediasize = (mdio)->md_mediasize; \ (mdr)->md_sectorsize = (mdio)->md_sectorsize; \ (mdr)->md_options = (mdio)->md_options; \ (mdr)->md_fwheads = (mdio)->md_fwheads; \ (mdr)->md_fwsectors = (mdio)->md_fwsectors; \ (mdr)->md_units = &(mdio)->md_pad[0]; \ (mdr)->md_units_nitems = nitems((mdio)->md_pad); \ } while(0) /* Copy members that might have been updated */ #define MD_REQ2IOCTL(mdr, mdio) do { \ (mdio)->md_unit = (mdr)->md_unit; \ (mdio)->md_type = (mdr)->md_type; \ (mdio)->md_mediasize = (mdr)->md_mediasize; \ (mdio)->md_sectorsize = (mdr)->md_sectorsize; \ (mdio)->md_options = (mdr)->md_options; \ (mdio)->md_fwheads = (mdr)->md_fwheads; \ (mdio)->md_fwsectors = (mdr)->md_fwsectors; \ } while(0) static int mdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { struct md_req mdr; int error; if (md_debug) printf("mdctlioctl(%s %lx %p %x %p)\n", devtoname(dev), cmd, addr, flags, td); bzero(&mdr, sizeof(mdr)); switch (cmd) { case MDIOCATTACH: case MDIOCDETACH: case MDIOCRESIZE: case MDIOCQUERY: case MDIOCLIST: { struct md_ioctl *mdio = (struct md_ioctl *)addr; if (mdio->md_version != MDIOVERSION) return (EINVAL); MD_IOCTL2REQ(mdio, &mdr); mdr.md_file = mdio->md_file; mdr.md_file_seg = UIO_USERSPACE; /* If the file is adjacent to the md_ioctl it's in kernel. */ if ((void *)mdio->md_file == (void *)(mdio + 1)) mdr.md_file_seg = UIO_SYSSPACE; mdr.md_label = mdio->md_label; break; } #ifdef COMPAT_FREEBSD32 case MDIOCATTACH_32: case MDIOCDETACH_32: case MDIOCRESIZE_32: case MDIOCQUERY_32: case MDIOCLIST_32: { struct md_ioctl32 *mdio = (struct md_ioctl32 *)addr; if (mdio->md_version != MDIOVERSION) return (EINVAL); MD_IOCTL2REQ(mdio, &mdr); mdr.md_file = (void *)(uintptr_t)mdio->md_file; mdr.md_file_seg = UIO_USERSPACE; mdr.md_label = (void *)(uintptr_t)mdio->md_label; break; } #endif default: /* Fall through to handler switch. */ break; } error = 0; switch (cmd) { case MDIOCATTACH: #ifdef COMPAT_FREEBSD32 case MDIOCATTACH_32: #endif error = kern_mdattach(td, &mdr); break; case MDIOCDETACH: #ifdef COMPAT_FREEBSD32 case MDIOCDETACH_32: #endif error = kern_mddetach(td, &mdr); break; case MDIOCRESIZE: #ifdef COMPAT_FREEBSD32 case MDIOCRESIZE_32: #endif error = kern_mdresize(&mdr); break; case MDIOCQUERY: #ifdef COMPAT_FREEBSD32 case MDIOCQUERY_32: #endif error = kern_mdquery(&mdr); break; case MDIOCLIST: #ifdef COMPAT_FREEBSD32 case MDIOCLIST_32: #endif error = kern_mdlist(&mdr); break; default: error = ENOIOCTL; } switch (cmd) { case MDIOCATTACH: case MDIOCQUERY: { struct md_ioctl *mdio = (struct md_ioctl *)addr; MD_REQ2IOCTL(&mdr, mdio); break; } #ifdef COMPAT_FREEBSD32 case MDIOCATTACH_32: case MDIOCQUERY_32: { struct md_ioctl32 *mdio = (struct md_ioctl32 *)addr; MD_REQ2IOCTL(&mdr, mdio); break; } #endif default: /* Other commands to not alter mdr. */ break; } return (error); } static void md_preloaded(u_char *image, size_t length, const char *name) { struct md_s *sc; int error; sc = mdnew(-1, &error, MD_PRELOAD); if (sc == NULL) return; sc->mediasize = length; sc->sectorsize = DEV_BSIZE; sc->pl_ptr = image; sc->pl_len = length; sc->start = mdstart_preload; if (name != NULL) strlcpy(sc->file, name, sizeof(sc->file)); #ifdef MD_ROOT if (sc->unit == 0) { #ifndef ROOTDEVNAME rootdevnames[0] = MD_ROOT_FSTYPE ":/dev/md0"; #endif #ifdef MD_ROOT_READONLY sc->flags |= MD_READONLY; #endif } #endif mdinit(sc); if (name != NULL) { printf("%s%d: Preloaded image <%s> %zd bytes at %p\n", MD_NAME, sc->unit, name, length, image); } else { printf("%s%d: Embedded image %zd bytes at %p\n", MD_NAME, sc->unit, length, image); } } static void g_md_init(struct g_class *mp __unused) { caddr_t mod; u_char *ptr, *name, *type; unsigned len; int i; /* figure out log2(NINDIR) */ for (i = NINDIR, nshift = -1; i; nshift++) i >>= 1; mod = NULL; sx_init(&md_sx, "MD config lock"); g_topology_unlock(); md_uh = new_unrhdr(0, INT_MAX, NULL); #ifdef MD_ROOT if (mfs_root_size != 0) { sx_xlock(&md_sx); #ifdef MD_ROOT_MEM md_preloaded(mfs_root, mfs_root_size, NULL); #else md_preloaded(__DEVOLATILE(u_char *, &mfs_root), mfs_root_size, NULL); #endif sx_xunlock(&md_sx); } #endif /* XXX: are preload_* static or do they need Giant ? */ while ((mod = preload_search_next_name(mod)) != NULL) { name = (char *)preload_search_info(mod, MODINFO_NAME); if (name == NULL) continue; type = (char *)preload_search_info(mod, MODINFO_TYPE); if (type == NULL) continue; if (strcmp(type, "md_image") && strcmp(type, "mfs_root")) continue; ptr = preload_fetch_addr(mod); len = preload_fetch_size(mod); if (ptr != NULL && len != 0) { sx_xlock(&md_sx); md_preloaded(ptr, len, name); sx_xunlock(&md_sx); } } md_pbuf_zone = pbuf_zsecond_create("mdpbuf", nswbuf / 10); status_dev = make_dev(&mdctl_cdevsw, INT_MAX, UID_ROOT, GID_WHEEL, 0600, MDCTL_NAME); g_topology_lock(); } static void g_md_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp __unused, struct g_provider *pp) { struct md_s *mp; char *type; mp = gp->softc; if (mp == NULL) return; switch (mp->type) { case MD_MALLOC: type = "malloc"; break; case MD_PRELOAD: type = "preload"; break; case MD_VNODE: type = "vnode"; break; case MD_SWAP: type = "swap"; break; case MD_NULL: type = "null"; break; default: type = "unknown"; break; } if (pp != NULL) { if (indent == NULL) { sbuf_printf(sb, " u %d", mp->unit); sbuf_printf(sb, " s %ju", (uintmax_t) mp->sectorsize); sbuf_printf(sb, " f %ju", (uintmax_t) mp->fwheads); sbuf_printf(sb, " fs %ju", (uintmax_t) mp->fwsectors); sbuf_printf(sb, " l %ju", (uintmax_t) mp->mediasize); sbuf_printf(sb, " t %s", type); if ((mp->type == MD_VNODE && mp->vnode != NULL) || (mp->type == MD_PRELOAD && mp->file[0] != '\0')) sbuf_printf(sb, " file %s", mp->file); sbuf_printf(sb, " label %s", mp->label); } else { sbuf_printf(sb, "%s%d\n", indent, mp->unit); sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->sectorsize); sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->fwheads); sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->fwsectors); if (mp->ident[0] != '\0') { sbuf_printf(sb, "%s", indent); g_conf_printf_escaped(sb, "%s", mp->ident); sbuf_printf(sb, "\n"); } sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->mediasize); sbuf_printf(sb, "%s%s\n", indent, (mp->flags & MD_COMPRESS) == 0 ? "off": "on"); sbuf_printf(sb, "%s%s\n", indent, (mp->flags & MD_READONLY) == 0 ? "read-write": "read-only"); sbuf_printf(sb, "%s%s\n", indent, type); if ((mp->type == MD_VNODE && mp->vnode != NULL) || (mp->type == MD_PRELOAD && mp->file[0] != '\0')) { sbuf_printf(sb, "%s", indent); g_conf_printf_escaped(sb, "%s", mp->file); sbuf_printf(sb, "\n"); } if (mp->type == MD_VNODE) sbuf_printf(sb, "%s%s\n", indent, (mp->flags & MD_CACHE) == 0 ? "off": "on"); sbuf_printf(sb, "%s\n"); } } } static void g_md_fini(struct g_class *mp __unused) { sx_destroy(&md_sx); if (status_dev != NULL) destroy_dev(status_dev); uma_zdestroy(md_pbuf_zone); delete_unrhdr(md_uh); } Index: projects/capsicum-test/sys/dev/usb/wlan/if_run.c =================================================================== --- projects/capsicum-test/sys/dev/usb/wlan/if_run.c (revision 345770) +++ projects/capsicum-test/sys/dev/usb/wlan/if_run.c (revision 345771) @@ -1,6323 +1,6330 @@ /*- * Copyright (c) 2008,2010 Damien Bergamini * ported to FreeBSD by Akinori Furukoshi * USB Consulting, Hans Petter Selasky * Copyright (c) 2013-2014 Kevin Lo * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /*- * Ralink Technology RT2700U/RT2800U/RT3000U/RT3900E chipset driver. * http://www.ralinktech.com/ */ #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR run_debug #include #include #include #include #ifdef USB_DEBUG #define RUN_DEBUG #endif #ifdef RUN_DEBUG int run_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, run, CTLFLAG_RW, 0, "USB run"); SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RWTUN, &run_debug, 0, "run debug level"); enum { RUN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ RUN_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */ RUN_DEBUG_RECV = 0x00000004, /* basic recv operation */ RUN_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */ RUN_DEBUG_STATE = 0x00000010, /* 802.11 state transitions */ RUN_DEBUG_RATE = 0x00000020, /* rate adaptation */ RUN_DEBUG_USB = 0x00000040, /* usb requests */ RUN_DEBUG_FIRMWARE = 0x00000080, /* firmware(9) loading debug */ RUN_DEBUG_BEACON = 0x00000100, /* beacon handling */ RUN_DEBUG_INTR = 0x00000200, /* ISR */ RUN_DEBUG_TEMP = 0x00000400, /* temperature calibration */ RUN_DEBUG_ROM = 0x00000800, /* various ROM info */ RUN_DEBUG_KEY = 0x00001000, /* crypto keys management */ RUN_DEBUG_TXPWR = 0x00002000, /* dump Tx power values */ RUN_DEBUG_RSSI = 0x00004000, /* dump RSSI lookups */ RUN_DEBUG_RESET = 0x00008000, /* initialization progress */ RUN_DEBUG_CALIB = 0x00010000, /* calibration progress */ RUN_DEBUG_CMD = 0x00020000, /* command queue */ RUN_DEBUG_ANY = 0xffffffff }; #define RUN_DPRINTF(_sc, _m, ...) do { \ if (run_debug & (_m)) \ device_printf((_sc)->sc_dev, __VA_ARGS__); \ } while(0) #else #define RUN_DPRINTF(_sc, _m, ...) do { (void) _sc; } while (0) #endif #define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh) /* * Because of LOR in run_key_delete(), use atomic instead. * '& RUN_CMDQ_MASQ' is to loop cmdq[]. */ #define RUN_CMDQ_GET(c) (atomic_fetchadd_32((c), 1) & RUN_CMDQ_MASQ) static const STRUCT_USB_HOST_ID run_devs[] = { #define RUN_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } #define RUN_DEV_EJECT(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RUN_EJECT) } #define RUN_EJECT 1 RUN_DEV(ABOCOM, RT2770), RUN_DEV(ABOCOM, RT2870), RUN_DEV(ABOCOM, RT3070), RUN_DEV(ABOCOM, RT3071), RUN_DEV(ABOCOM, RT3072), RUN_DEV(ABOCOM2, RT2870_1), RUN_DEV(ACCTON, RT2770), RUN_DEV(ACCTON, RT2870_1), RUN_DEV(ACCTON, RT2870_2), RUN_DEV(ACCTON, RT2870_3), RUN_DEV(ACCTON, RT2870_4), RUN_DEV(ACCTON, RT2870_5), RUN_DEV(ACCTON, RT3070), RUN_DEV(ACCTON, RT3070_1), RUN_DEV(ACCTON, RT3070_2), RUN_DEV(ACCTON, RT3070_3), RUN_DEV(ACCTON, RT3070_4), RUN_DEV(ACCTON, RT3070_5), RUN_DEV(AIRTIES, RT3070), RUN_DEV(ALLWIN, RT2070), RUN_DEV(ALLWIN, RT2770), RUN_DEV(ALLWIN, RT2870), RUN_DEV(ALLWIN, RT3070), RUN_DEV(ALLWIN, RT3071), RUN_DEV(ALLWIN, RT3072), RUN_DEV(ALLWIN, RT3572), RUN_DEV(AMIGO, RT2870_1), RUN_DEV(AMIGO, RT2870_2), RUN_DEV(AMIT, CGWLUSB2GNR), RUN_DEV(AMIT, RT2870_1), RUN_DEV(AMIT2, RT2870), RUN_DEV(ASUS, RT2870_1), RUN_DEV(ASUS, RT2870_2), RUN_DEV(ASUS, RT2870_3), RUN_DEV(ASUS, RT2870_4), RUN_DEV(ASUS, RT2870_5), RUN_DEV(ASUS, USBN13), RUN_DEV(ASUS, RT3070_1), RUN_DEV(ASUS, USBN66), RUN_DEV(ASUS, USB_N53), RUN_DEV(ASUS2, USBN11), RUN_DEV(AZUREWAVE, RT2870_1), RUN_DEV(AZUREWAVE, RT2870_2), RUN_DEV(AZUREWAVE, RT3070_1), RUN_DEV(AZUREWAVE, RT3070_2), RUN_DEV(AZUREWAVE, RT3070_3), RUN_DEV(BELKIN, F9L1103), RUN_DEV(BELKIN, F5D8053V3), RUN_DEV(BELKIN, F5D8055), RUN_DEV(BELKIN, F5D8055V2), RUN_DEV(BELKIN, F6D4050V1), RUN_DEV(BELKIN, F6D4050V2), RUN_DEV(BELKIN, RT2870_1), RUN_DEV(BELKIN, RT2870_2), RUN_DEV(CISCOLINKSYS, AE1000), RUN_DEV(CISCOLINKSYS2, RT3070), RUN_DEV(CISCOLINKSYS3, RT3070), RUN_DEV(CONCEPTRONIC2, RT2870_1), RUN_DEV(CONCEPTRONIC2, RT2870_2), RUN_DEV(CONCEPTRONIC2, RT2870_3), RUN_DEV(CONCEPTRONIC2, RT2870_4), RUN_DEV(CONCEPTRONIC2, RT2870_5), RUN_DEV(CONCEPTRONIC2, RT2870_6), RUN_DEV(CONCEPTRONIC2, RT2870_7), RUN_DEV(CONCEPTRONIC2, RT2870_8), RUN_DEV(CONCEPTRONIC2, RT3070_1), RUN_DEV(CONCEPTRONIC2, RT3070_2), RUN_DEV(CONCEPTRONIC2, VIGORN61), RUN_DEV(COREGA, CGWLUSB300GNM), RUN_DEV(COREGA, RT2870_1), RUN_DEV(COREGA, RT2870_2), RUN_DEV(COREGA, RT2870_3), RUN_DEV(COREGA, RT3070), RUN_DEV(CYBERTAN, RT2870), RUN_DEV(DLINK, RT2870), RUN_DEV(DLINK, RT3072), RUN_DEV(DLINK, DWA125A3), RUN_DEV(DLINK, DWA127), RUN_DEV(DLINK, DWA140B3), RUN_DEV(DLINK, DWA160B2), RUN_DEV(DLINK, DWA140D1), RUN_DEV(DLINK, DWA162), RUN_DEV(DLINK2, DWA130), RUN_DEV(DLINK2, RT2870_1), RUN_DEV(DLINK2, RT2870_2), RUN_DEV(DLINK2, RT3070_1), RUN_DEV(DLINK2, RT3070_2), RUN_DEV(DLINK2, RT3070_3), RUN_DEV(DLINK2, RT3070_4), RUN_DEV(DLINK2, RT3070_5), RUN_DEV(DLINK2, RT3072), RUN_DEV(DLINK2, RT3072_1), RUN_DEV(EDIMAX, EW7717), RUN_DEV(EDIMAX, EW7718), RUN_DEV(EDIMAX, EW7733UND), RUN_DEV(EDIMAX, RT2870_1), RUN_DEV(ENCORE, RT3070_1), RUN_DEV(ENCORE, RT3070_2), RUN_DEV(ENCORE, RT3070_3), RUN_DEV(GIGABYTE, GNWB31N), RUN_DEV(GIGABYTE, GNWB32L), RUN_DEV(GIGABYTE, RT2870_1), RUN_DEV(GIGASET, RT3070_1), RUN_DEV(GIGASET, RT3070_2), RUN_DEV(GUILLEMOT, HWNU300), RUN_DEV(HAWKING, HWUN2), RUN_DEV(HAWKING, RT2870_1), RUN_DEV(HAWKING, RT2870_2), RUN_DEV(HAWKING, RT3070), RUN_DEV(IODATA, RT3072_1), RUN_DEV(IODATA, RT3072_2), RUN_DEV(IODATA, RT3072_3), RUN_DEV(IODATA, RT3072_4), RUN_DEV(LINKSYS4, RT3070), RUN_DEV(LINKSYS4, WUSB100), RUN_DEV(LINKSYS4, WUSB54GCV3), RUN_DEV(LINKSYS4, WUSB600N), RUN_DEV(LINKSYS4, WUSB600NV2), RUN_DEV(LOGITEC, RT2870_1), RUN_DEV(LOGITEC, RT2870_2), RUN_DEV(LOGITEC, RT2870_3), RUN_DEV(LOGITEC, LANW300NU2), RUN_DEV(LOGITEC, LANW150NU2), RUN_DEV(LOGITEC, LANW300NU2S), RUN_DEV(MELCO, WLIUCG300HP), RUN_DEV(MELCO, RT2870_2), RUN_DEV(MELCO, WLIUCAG300N), RUN_DEV(MELCO, WLIUCG300N), RUN_DEV(MELCO, WLIUCG301N), RUN_DEV(MELCO, WLIUCGN), RUN_DEV(MELCO, WLIUCGNM), RUN_DEV(MELCO, WLIUCG300HPV1), RUN_DEV(MELCO, WLIUCGNM2), RUN_DEV(MOTOROLA4, RT2770), RUN_DEV(MOTOROLA4, RT3070), RUN_DEV(MSI, RT3070_1), RUN_DEV(MSI, RT3070_2), RUN_DEV(MSI, RT3070_3), RUN_DEV(MSI, RT3070_4), RUN_DEV(MSI, RT3070_5), RUN_DEV(MSI, RT3070_6), RUN_DEV(MSI, RT3070_7), RUN_DEV(MSI, RT3070_8), RUN_DEV(MSI, RT3070_9), RUN_DEV(MSI, RT3070_10), RUN_DEV(MSI, RT3070_11), RUN_DEV(NETGEAR, WNDA4100), RUN_DEV(OVISLINK, RT3072), RUN_DEV(PARA, RT3070), RUN_DEV(PEGATRON, RT2870), RUN_DEV(PEGATRON, RT3070), RUN_DEV(PEGATRON, RT3070_2), RUN_DEV(PEGATRON, RT3070_3), RUN_DEV(PHILIPS, RT2870), RUN_DEV(PLANEX2, GWUS300MINIS), RUN_DEV(PLANEX2, GWUSMICRON), RUN_DEV(PLANEX2, RT2870), RUN_DEV(PLANEX2, RT3070), RUN_DEV(QCOM, RT2870), RUN_DEV(QUANTA, RT3070), RUN_DEV(RALINK, RT2070), RUN_DEV(RALINK, RT2770), RUN_DEV(RALINK, RT2870), RUN_DEV(RALINK, RT3070), RUN_DEV(RALINK, RT3071), RUN_DEV(RALINK, RT3072), RUN_DEV(RALINK, RT3370), RUN_DEV(RALINK, RT3572), RUN_DEV(RALINK, RT3573), RUN_DEV(RALINK, RT5370), RUN_DEV(RALINK, RT5372), RUN_DEV(RALINK, RT5572), RUN_DEV(RALINK, RT8070), RUN_DEV(SAMSUNG, WIS09ABGN), RUN_DEV(SAMSUNG2, RT2870_1), RUN_DEV(SENAO, RT2870_1), RUN_DEV(SENAO, RT2870_2), RUN_DEV(SENAO, RT2870_3), RUN_DEV(SENAO, RT2870_4), RUN_DEV(SENAO, RT3070), RUN_DEV(SENAO, RT3071), RUN_DEV(SENAO, RT3072_1), RUN_DEV(SENAO, RT3072_2), RUN_DEV(SENAO, RT3072_3), RUN_DEV(SENAO, RT3072_4), RUN_DEV(SENAO, RT3072_5), RUN_DEV(SITECOMEU, RT2770), RUN_DEV(SITECOMEU, RT2870_1), RUN_DEV(SITECOMEU, RT2870_2), RUN_DEV(SITECOMEU, RT2870_3), RUN_DEV(SITECOMEU, RT2870_4), RUN_DEV(SITECOMEU, RT3070), RUN_DEV(SITECOMEU, RT3070_2), RUN_DEV(SITECOMEU, RT3070_3), RUN_DEV(SITECOMEU, RT3070_4), RUN_DEV(SITECOMEU, RT3071), RUN_DEV(SITECOMEU, RT3072_1), RUN_DEV(SITECOMEU, RT3072_2), RUN_DEV(SITECOMEU, RT3072_3), RUN_DEV(SITECOMEU, RT3072_4), RUN_DEV(SITECOMEU, RT3072_5), RUN_DEV(SITECOMEU, RT3072_6), RUN_DEV(SITECOMEU, WL608), RUN_DEV(SPARKLAN, RT2870_1), RUN_DEV(SPARKLAN, RT3070), RUN_DEV(SWEEX2, LW153), RUN_DEV(SWEEX2, LW303), RUN_DEV(SWEEX2, LW313), RUN_DEV(TOSHIBA, RT3070), RUN_DEV(UMEDIA, RT2870_1), RUN_DEV(ZCOM, RT2870_1), RUN_DEV(ZCOM, RT2870_2), RUN_DEV(ZINWELL, RT2870_1), RUN_DEV(ZINWELL, RT2870_2), RUN_DEV(ZINWELL, RT3070), RUN_DEV(ZINWELL, RT3072_1), RUN_DEV(ZINWELL, RT3072_2), RUN_DEV(ZYXEL, RT2870_1), RUN_DEV(ZYXEL, RT2870_2), RUN_DEV(ZYXEL, RT3070), RUN_DEV_EJECT(ZYXEL, NWD2705), RUN_DEV_EJECT(RALINK, RT_STOR), #undef RUN_DEV_EJECT #undef RUN_DEV }; static device_probe_t run_match; static device_attach_t run_attach; static device_detach_t run_detach; static usb_callback_t run_bulk_rx_callback; static usb_callback_t run_bulk_tx_callback0; static usb_callback_t run_bulk_tx_callback1; static usb_callback_t run_bulk_tx_callback2; static usb_callback_t run_bulk_tx_callback3; static usb_callback_t run_bulk_tx_callback4; static usb_callback_t run_bulk_tx_callback5; static void run_autoinst(void *, struct usb_device *, struct usb_attach_arg *); static int run_driver_loaded(struct module *, int, void *); static void run_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index); static struct ieee80211vap *run_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void run_vap_delete(struct ieee80211vap *); static void run_cmdq_cb(void *, int); static void run_setup_tx_list(struct run_softc *, struct run_endpoint_queue *); static void run_unsetup_tx_list(struct run_softc *, struct run_endpoint_queue *); static int run_load_microcode(struct run_softc *); static int run_reset(struct run_softc *); static usb_error_t run_do_request(struct run_softc *, struct usb_device_request *, void *); static int run_read(struct run_softc *, uint16_t, uint32_t *); static int run_read_region_1(struct run_softc *, uint16_t, uint8_t *, int); static int run_write_2(struct run_softc *, uint16_t, uint16_t); static int run_write(struct run_softc *, uint16_t, uint32_t); static int run_write_region_1(struct run_softc *, uint16_t, const uint8_t *, int); static int run_set_region_4(struct run_softc *, uint16_t, uint32_t, int); static int run_efuse_read(struct run_softc *, uint16_t, uint16_t *, int); static int run_efuse_read_2(struct run_softc *, uint16_t, uint16_t *); static int run_eeprom_read_2(struct run_softc *, uint16_t, uint16_t *); static int run_rt2870_rf_write(struct run_softc *, uint32_t); static int run_rt3070_rf_read(struct run_softc *, uint8_t, uint8_t *); static int run_rt3070_rf_write(struct run_softc *, uint8_t, uint8_t); static int run_bbp_read(struct run_softc *, uint8_t, uint8_t *); static int run_bbp_write(struct run_softc *, uint8_t, uint8_t); static int run_mcu_cmd(struct run_softc *, uint8_t, uint16_t); static const char *run_get_rf(uint16_t); static void run_rt3593_get_txpower(struct run_softc *); static void run_get_txpower(struct run_softc *); static int run_read_eeprom(struct run_softc *); static struct ieee80211_node *run_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static int run_media_change(struct ifnet *); static int run_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int run_wme_update(struct ieee80211com *); static void run_key_set_cb(void *); static int run_key_set(struct ieee80211vap *, struct ieee80211_key *); static void run_key_delete_cb(void *); static int run_key_delete(struct ieee80211vap *, struct ieee80211_key *); static void run_ratectl_to(void *); static void run_ratectl_cb(void *, int); static void run_drain_fifo(void *); static void run_iter_func(void *, struct ieee80211_node *); static void run_newassoc_cb(void *); static void run_newassoc(struct ieee80211_node *, int); static void run_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, const struct ieee80211_rx_stats *, int, int); static void run_rx_frame(struct run_softc *, struct mbuf *, uint32_t); static void run_tx_free(struct run_endpoint_queue *pq, struct run_tx_data *, int); static void run_set_tx_desc(struct run_softc *, struct run_tx_data *); static int run_tx(struct run_softc *, struct mbuf *, struct ieee80211_node *); static int run_tx_mgt(struct run_softc *, struct mbuf *, struct ieee80211_node *); static int run_sendprot(struct run_softc *, const struct mbuf *, struct ieee80211_node *, int, int); static int run_tx_param(struct run_softc *, struct mbuf *, struct ieee80211_node *, const struct ieee80211_bpf_params *); static int run_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static int run_transmit(struct ieee80211com *, struct mbuf *); static void run_start(struct run_softc *); static void run_parent(struct ieee80211com *); static void run_iq_calib(struct run_softc *, u_int); static void run_set_agc(struct run_softc *, uint8_t); static void run_select_chan_group(struct run_softc *, int); static void run_set_rx_antenna(struct run_softc *, int); static void run_rt2870_set_chan(struct run_softc *, u_int); static void run_rt3070_set_chan(struct run_softc *, u_int); static void run_rt3572_set_chan(struct run_softc *, u_int); static void run_rt3593_set_chan(struct run_softc *, u_int); static void run_rt5390_set_chan(struct run_softc *, u_int); static void run_rt5592_set_chan(struct run_softc *, u_int); static int run_set_chan(struct run_softc *, struct ieee80211_channel *); static void run_set_channel(struct ieee80211com *); static void run_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel[]); static void run_scan_start(struct ieee80211com *); static void run_scan_end(struct ieee80211com *); static void run_update_beacon(struct ieee80211vap *, int); static void run_update_beacon_cb(void *); static void run_updateprot(struct ieee80211com *); static void run_updateprot_cb(void *); static void run_usb_timeout_cb(void *); static void run_reset_livelock(struct run_softc *); static void run_enable_tsf_sync(struct run_softc *); static void run_enable_tsf(struct run_softc *); static void run_disable_tsf(struct run_softc *); static void run_get_tsf(struct run_softc *, uint64_t *); static void run_enable_mrr(struct run_softc *); static void run_set_txpreamble(struct run_softc *); static void run_set_basicrates(struct run_softc *); static void run_set_leds(struct run_softc *, uint16_t); static void run_set_bssid(struct run_softc *, const uint8_t *); static void run_set_macaddr(struct run_softc *, const uint8_t *); static void run_updateslot(struct ieee80211com *); static void run_updateslot_cb(void *); static void run_update_mcast(struct ieee80211com *); static int8_t run_rssi2dbm(struct run_softc *, uint8_t, uint8_t); static void run_update_promisc_locked(struct run_softc *); static void run_update_promisc(struct ieee80211com *); static void run_rt5390_bbp_init(struct run_softc *); static int run_bbp_init(struct run_softc *); static int run_rt3070_rf_init(struct run_softc *); static void run_rt3593_rf_init(struct run_softc *); static void run_rt5390_rf_init(struct run_softc *); static int run_rt3070_filter_calib(struct run_softc *, uint8_t, uint8_t, uint8_t *); static void run_rt3070_rf_setup(struct run_softc *); static void run_rt3593_rf_setup(struct run_softc *); static void run_rt5390_rf_setup(struct run_softc *); static int run_txrx_enable(struct run_softc *); static void run_adjust_freq_offset(struct run_softc *); static void run_init_locked(struct run_softc *); static void run_stop(void *); static void run_delay(struct run_softc *, u_int); static eventhandler_tag run_etag; static const struct rt2860_rate { uint8_t rate; uint8_t mcs; enum ieee80211_phytype phy; uint8_t ctl_ridx; uint16_t sp_ack_dur; uint16_t lp_ack_dur; } rt2860_rates[] = { { 2, 0, IEEE80211_T_DS, 0, 314, 314 }, { 4, 1, IEEE80211_T_DS, 1, 258, 162 }, { 11, 2, IEEE80211_T_DS, 2, 223, 127 }, { 22, 3, IEEE80211_T_DS, 3, 213, 117 }, { 12, 0, IEEE80211_T_OFDM, 4, 60, 60 }, { 18, 1, IEEE80211_T_OFDM, 4, 52, 52 }, { 24, 2, IEEE80211_T_OFDM, 6, 48, 48 }, { 36, 3, IEEE80211_T_OFDM, 6, 44, 44 }, { 48, 4, IEEE80211_T_OFDM, 8, 44, 44 }, { 72, 5, IEEE80211_T_OFDM, 8, 40, 40 }, { 96, 6, IEEE80211_T_OFDM, 8, 40, 40 }, { 108, 7, IEEE80211_T_OFDM, 8, 40, 40 } }; static const struct { uint16_t reg; uint32_t val; } rt2870_def_mac[] = { RT2870_DEF_MAC }; static const struct { uint8_t reg; uint8_t val; } rt2860_def_bbp[] = { RT2860_DEF_BBP },rt5390_def_bbp[] = { RT5390_DEF_BBP },rt5592_def_bbp[] = { RT5592_DEF_BBP }; /* * Default values for BBP register R196 for RT5592. */ static const uint8_t rt5592_bbp_r196[] = { 0xe0, 0x1f, 0x38, 0x32, 0x08, 0x28, 0x19, 0x0a, 0xff, 0x00, 0x16, 0x10, 0x10, 0x0b, 0x36, 0x2c, 0x26, 0x24, 0x42, 0x36, 0x30, 0x2d, 0x4c, 0x46, 0x3d, 0x40, 0x3e, 0x42, 0x3d, 0x40, 0x3c, 0x34, 0x2c, 0x2f, 0x3c, 0x35, 0x2e, 0x2a, 0x49, 0x41, 0x36, 0x31, 0x30, 0x30, 0x0e, 0x0d, 0x28, 0x21, 0x1c, 0x16, 0x50, 0x4a, 0x43, 0x40, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x14, 0x32, 0x2c, 0x36, 0x4c, 0x43, 0x2c, 0x2e, 0x36, 0x30, 0x6e }; static const struct rfprog { uint8_t chan; uint32_t r1, r2, r3, r4; } rt2860_rf2850[] = { RT2860_RF2850 }; struct { uint8_t n, r, k; } rt3070_freqs[] = { RT3070_RF3052 }; static const struct rt5592_freqs { uint16_t n; uint8_t k, m, r; } rt5592_freqs_20mhz[] = { RT5592_RF5592_20MHZ },rt5592_freqs_40mhz[] = { RT5592_RF5592_40MHZ }; static const struct { uint8_t reg; uint8_t val; } rt3070_def_rf[] = { RT3070_DEF_RF },rt3572_def_rf[] = { RT3572_DEF_RF },rt3593_def_rf[] = { RT3593_DEF_RF },rt5390_def_rf[] = { RT5390_DEF_RF },rt5392_def_rf[] = { RT5392_DEF_RF },rt5592_def_rf[] = { RT5592_DEF_RF },rt5592_2ghz_def_rf[] = { RT5592_2GHZ_DEF_RF },rt5592_5ghz_def_rf[] = { RT5592_5GHZ_DEF_RF }; static const struct { u_int firstchan; u_int lastchan; uint8_t reg; uint8_t val; } rt5592_chan_5ghz[] = { RT5592_CHAN_5GHZ }; static const struct usb_config run_config[RUN_N_XFER] = { [RUN_BULK_TX_BE] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .ep_index = 0, .direction = UE_DIR_OUT, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = run_bulk_tx_callback0, .timeout = 5000, /* ms */ }, [RUN_BULK_TX_BK] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .ep_index = 1, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = run_bulk_tx_callback1, .timeout = 5000, /* ms */ }, [RUN_BULK_TX_VI] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .ep_index = 2, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = run_bulk_tx_callback2, .timeout = 5000, /* ms */ }, [RUN_BULK_TX_VO] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .ep_index = 3, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = run_bulk_tx_callback3, .timeout = 5000, /* ms */ }, [RUN_BULK_TX_HCCA] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .ep_index = 4, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, .callback = run_bulk_tx_callback4, .timeout = 5000, /* ms */ }, [RUN_BULK_TX_PRIO] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .ep_index = 5, .bufsize = RUN_MAX_TXSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, .callback = run_bulk_tx_callback5, .timeout = 5000, /* ms */ }, [RUN_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = RUN_MAX_RXSZ, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = run_bulk_rx_callback, } }; static void run_autoinst(void *arg, struct usb_device *udev, struct usb_attach_arg *uaa) { struct usb_interface *iface; struct usb_interface_descriptor *id; if (uaa->dev_state != UAA_DEV_READY) return; iface = usbd_get_iface(udev, 0); if (iface == NULL) return; id = iface->idesc; if (id == NULL || id->bInterfaceClass != UICLASS_MASS) return; if (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa)) return; if (usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT) == 0) uaa->dev_state = UAA_DEV_EJECTING; } static int run_driver_loaded(struct module *mod, int what, void *arg) { switch (what) { case MOD_LOAD: run_etag = EVENTHANDLER_REGISTER(usb_dev_configured, run_autoinst, NULL, EVENTHANDLER_PRI_ANY); break; case MOD_UNLOAD: EVENTHANDLER_DEREGISTER(usb_dev_configured, run_etag); break; default: return (EOPNOTSUPP); } return (0); } static int run_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != 0) return (ENXIO); if (uaa->info.bIfaceIndex != RT2860_IFACE_INDEX) return (ENXIO); return (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa)); } static int run_attach(device_t self) { struct run_softc *sc = device_get_softc(self); struct usb_attach_arg *uaa = device_get_ivars(self); struct ieee80211com *ic = &sc->sc_ic; uint32_t ver; uint8_t iface_index; int ntries, error; device_set_usb_desc(self); sc->sc_udev = uaa->device; sc->sc_dev = self; if (USB_GET_DRIVER_INFO(uaa) != RUN_EJECT) sc->sc_flags |= RUN_FLAG_FWLOAD_NEEDED; mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF); mbufq_init(&sc->sc_snd, ifqmaxlen); iface_index = RT2860_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, run_config, RUN_N_XFER, sc, &sc->sc_mtx); if (error) { device_printf(self, "could not allocate USB transfers, " "err=%s\n", usbd_errstr(error)); goto detach; } RUN_LOCK(sc); /* wait for the chip to settle */ for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_ASIC_VER_ID, &ver) != 0) { RUN_UNLOCK(sc); goto detach; } if (ver != 0 && ver != 0xffffffff) break; run_delay(sc, 10); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for NIC to initialize\n"); RUN_UNLOCK(sc); goto detach; } sc->mac_ver = ver >> 16; sc->mac_rev = ver & 0xffff; /* retrieve RF rev. no and various other things from EEPROM */ run_read_eeprom(sc); device_printf(sc->sc_dev, "MAC/BBP RT%04X (rev 0x%04X), RF %s (MIMO %dT%dR), address %s\n", sc->mac_ver, sc->mac_rev, run_get_rf(sc->rf_rev), sc->ntxchains, sc->nrxchains, ether_sprintf(ic->ic_macaddr)); RUN_UNLOCK(sc); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(self); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA | /* station mode supported */ IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_WDS | /* 4-address traffic works */ IEEE80211_C_MBSS | IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_WME | /* WME */ IEEE80211_C_WPA; /* WPA1|WPA2(RSN) */ ic->ic_cryptocaps = IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_AES_CCM | IEEE80211_CRYPTO_TKIPMIC | IEEE80211_CRYPTO_TKIP; ic->ic_flags |= IEEE80211_F_DATAPAD; ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; run_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, ic->ic_channels); ieee80211_ifattach(ic); ic->ic_scan_start = run_scan_start; ic->ic_scan_end = run_scan_end; ic->ic_set_channel = run_set_channel; ic->ic_getradiocaps = run_getradiocaps; ic->ic_node_alloc = run_node_alloc; ic->ic_newassoc = run_newassoc; ic->ic_updateslot = run_updateslot; ic->ic_update_mcast = run_update_mcast; ic->ic_wme.wme_update = run_wme_update; ic->ic_raw_xmit = run_raw_xmit; ic->ic_update_promisc = run_update_promisc; ic->ic_vap_create = run_vap_create; ic->ic_vap_delete = run_vap_delete; ic->ic_transmit = run_transmit; ic->ic_parent = run_parent; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), RUN_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), RUN_RX_RADIOTAP_PRESENT); TASK_INIT(&sc->cmdq_task, 0, run_cmdq_cb, sc); TASK_INIT(&sc->ratectl_task, 0, run_ratectl_cb, sc); usb_callout_init_mtx(&sc->ratectl_ch, &sc->sc_mtx, 0); if (bootverbose) ieee80211_announce(ic); return (0); detach: run_detach(self); return (ENXIO); } static void run_drain_mbufq(struct run_softc *sc) { struct mbuf *m; struct ieee80211_node *ni; RUN_LOCK_ASSERT(sc, MA_OWNED); while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; ieee80211_free_node(ni); m_freem(m); } } static int run_detach(device_t self) { struct run_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; int i; RUN_LOCK(sc); sc->sc_detached = 1; RUN_UNLOCK(sc); /* stop all USB transfers */ usbd_transfer_unsetup(sc->sc_xfer, RUN_N_XFER); RUN_LOCK(sc); sc->ratectl_run = RUN_RATECTL_OFF; sc->cmdq_run = sc->cmdq_key_set = RUN_CMDQ_ABORT; /* free TX list, if any */ for (i = 0; i != RUN_EP_QUEUES; i++) run_unsetup_tx_list(sc, &sc->sc_epq[i]); /* Free TX queue */ run_drain_mbufq(sc); RUN_UNLOCK(sc); if (sc->sc_ic.ic_softc == sc) { /* drain tasks */ usb_callout_drain(&sc->ratectl_ch); ieee80211_draintask(ic, &sc->cmdq_task); ieee80211_draintask(ic, &sc->ratectl_task); ieee80211_ifdetach(ic); } mtx_destroy(&sc->sc_mtx); return (0); } static struct ieee80211vap * run_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct run_softc *sc = ic->ic_softc; struct run_vap *rvp; struct ieee80211vap *vap; int i; if (sc->rvp_cnt >= RUN_VAP_MAX) { device_printf(sc->sc_dev, "number of VAPs maxed out\n"); return (NULL); } switch (opmode) { case IEEE80211_M_STA: /* enable s/w bmiss handling for sta mode */ flags |= IEEE80211_CLONE_NOBEACONS; /* fall though */ case IEEE80211_M_IBSS: case IEEE80211_M_MONITOR: case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: /* other than WDS vaps, only one at a time */ if (!TAILQ_EMPTY(&ic->ic_vaps)) return (NULL); break; case IEEE80211_M_WDS: TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next){ if(vap->iv_opmode != IEEE80211_M_HOSTAP) continue; /* WDS vap's always share the local mac address. */ flags &= ~IEEE80211_CLONE_BSSID; break; } if (vap == NULL) { device_printf(sc->sc_dev, "wds only supported in ap mode\n"); return (NULL); } break; default: device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); return (NULL); } rvp = malloc(sizeof(struct run_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &rvp->vap; if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid) != 0) { /* out of memory */ free(rvp, M_80211_VAP); return (NULL); } vap->iv_update_beacon = run_update_beacon; vap->iv_max_aid = RT2870_WCID_MAX; /* * To delete the right key from h/w, we need wcid. * Luckily, there is unused space in ieee80211_key{}, wk_pad, * and matching wcid will be written into there. So, cast * some spells to remove 'const' from ieee80211_key{} */ vap->iv_key_delete = (void *)run_key_delete; vap->iv_key_set = (void *)run_key_set; /* override state transition machine */ rvp->newstate = vap->iv_newstate; vap->iv_newstate = run_newstate; if (opmode == IEEE80211_M_IBSS) { rvp->recv_mgmt = vap->iv_recv_mgmt; vap->iv_recv_mgmt = run_recv_mgmt; } ieee80211_ratectl_init(vap); ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); /* complete setup */ ieee80211_vap_attach(vap, run_media_change, ieee80211_media_status, mac); /* make sure id is always unique */ for (i = 0; i < RUN_VAP_MAX; i++) { if((sc->rvp_bmap & 1 << i) == 0){ sc->rvp_bmap |= 1 << i; rvp->rvp_id = i; break; } } if (sc->rvp_cnt++ == 0) ic->ic_opmode = opmode; if (opmode == IEEE80211_M_HOSTAP) sc->cmdq_run = RUN_CMDQ_GO; RUN_DPRINTF(sc, RUN_DEBUG_STATE, "rvp_id=%d bmap=%x rvp_cnt=%d\n", rvp->rvp_id, sc->rvp_bmap, sc->rvp_cnt); return (vap); } static void run_vap_delete(struct ieee80211vap *vap) { struct run_vap *rvp = RUN_VAP(vap); struct ieee80211com *ic; struct run_softc *sc; uint8_t rvp_id; if (vap == NULL) return; ic = vap->iv_ic; sc = ic->ic_softc; RUN_LOCK(sc); m_freem(rvp->beacon_mbuf); rvp->beacon_mbuf = NULL; rvp_id = rvp->rvp_id; sc->ratectl_run &= ~(1 << rvp_id); sc->rvp_bmap &= ~(1 << rvp_id); run_set_region_4(sc, RT2860_SKEY(rvp_id, 0), 0, 128); run_set_region_4(sc, RT2860_BCN_BASE(rvp_id), 0, 512); --sc->rvp_cnt; RUN_DPRINTF(sc, RUN_DEBUG_STATE, "vap=%p rvp_id=%d bmap=%x rvp_cnt=%d\n", vap, rvp_id, sc->rvp_bmap, sc->rvp_cnt); RUN_UNLOCK(sc); ieee80211_ratectl_deinit(vap); ieee80211_vap_detach(vap); free(rvp, M_80211_VAP); } /* * There are numbers of functions need to be called in context thread. * Rather than creating taskqueue event for each of those functions, * here is all-for-one taskqueue callback function. This function * guarantees deferred functions are executed in the same order they * were enqueued. * '& RUN_CMDQ_MASQ' is to loop cmdq[]. */ static void run_cmdq_cb(void *arg, int pending) { struct run_softc *sc = arg; uint8_t i; /* call cmdq[].func locked */ RUN_LOCK(sc); for (i = sc->cmdq_exec; sc->cmdq[i].func && pending; i = sc->cmdq_exec, pending--) { RUN_DPRINTF(sc, RUN_DEBUG_CMD, "cmdq_exec=%d pending=%d\n", i, pending); if (sc->cmdq_run == RUN_CMDQ_GO) { /* * If arg0 is NULL, callback func needs more * than one arg. So, pass ptr to cmdq struct. */ if (sc->cmdq[i].arg0) sc->cmdq[i].func(sc->cmdq[i].arg0); else sc->cmdq[i].func(&sc->cmdq[i]); } sc->cmdq[i].arg0 = NULL; sc->cmdq[i].func = NULL; sc->cmdq_exec++; sc->cmdq_exec &= RUN_CMDQ_MASQ; } RUN_UNLOCK(sc); } static void run_setup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq) { struct run_tx_data *data; memset(pq, 0, sizeof(*pq)); STAILQ_INIT(&pq->tx_qh); STAILQ_INIT(&pq->tx_fh); for (data = &pq->tx_data[0]; data < &pq->tx_data[RUN_TX_RING_COUNT]; data++) { data->sc = sc; STAILQ_INSERT_TAIL(&pq->tx_fh, data, next); } pq->tx_nfree = RUN_TX_RING_COUNT; } static void run_unsetup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq) { struct run_tx_data *data; /* make sure any subsequent use of the queues will fail */ pq->tx_nfree = 0; STAILQ_INIT(&pq->tx_fh); STAILQ_INIT(&pq->tx_qh); /* free up all node references and mbufs */ for (data = &pq->tx_data[0]; data < &pq->tx_data[RUN_TX_RING_COUNT]; data++) { if (data->m != NULL) { m_freem(data->m); data->m = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } } static int run_load_microcode(struct run_softc *sc) { usb_device_request_t req; const struct firmware *fw; const u_char *base; uint32_t tmp; int ntries, error; const uint64_t *temp; uint64_t bytes; RUN_UNLOCK(sc); fw = firmware_get("runfw"); RUN_LOCK(sc); if (fw == NULL) { device_printf(sc->sc_dev, "failed loadfirmware of file %s\n", "runfw"); return ENOENT; } if (fw->datasize != 8192) { device_printf(sc->sc_dev, "invalid firmware size (should be 8KB)\n"); error = EINVAL; goto fail; } /* * RT3071/RT3072 use a different firmware * run-rt2870 (8KB) contains both, * first half (4KB) is for rt2870, * last half is for rt3071. */ base = fw->data; if ((sc->mac_ver) != 0x2860 && (sc->mac_ver) != 0x2872 && (sc->mac_ver) != 0x3070) { base += 4096; } /* cheap sanity check */ temp = fw->data; bytes = *temp; if (bytes != be64toh(0xffffff0210280210ULL)) { device_printf(sc->sc_dev, "firmware checksum failed\n"); error = EINVAL; goto fail; } /* write microcode image */ if (sc->sc_flags & RUN_FLAG_FWLOAD_NEEDED) { run_write_region_1(sc, RT2870_FW_BASE, base, 4096); run_write(sc, RT2860_H2M_MAILBOX_CID, 0xffffffff); run_write(sc, RT2860_H2M_MAILBOX_STATUS, 0xffffffff); } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2870_RESET; USETW(req.wValue, 8); USETW(req.wIndex, 0); USETW(req.wLength, 0); if ((error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)) != 0) { device_printf(sc->sc_dev, "firmware reset failed\n"); goto fail; } run_delay(sc, 10); run_write(sc, RT2860_H2M_BBPAGENT, 0); run_write(sc, RT2860_H2M_MAILBOX, 0); run_write(sc, RT2860_H2M_INTSRC, 0); if ((error = run_mcu_cmd(sc, RT2860_MCU_CMD_RFRESET, 0)) != 0) goto fail; /* wait until microcontroller is ready */ for (ntries = 0; ntries < 1000; ntries++) { if ((error = run_read(sc, RT2860_SYS_CTRL, &tmp)) != 0) goto fail; if (tmp & RT2860_MCU_READY) break; run_delay(sc, 10); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for MCU to initialize\n"); error = ETIMEDOUT; goto fail; } device_printf(sc->sc_dev, "firmware %s ver. %u.%u loaded\n", (base == fw->data) ? "RT2870" : "RT3071", *(base + 4092), *(base + 4093)); fail: firmware_put(fw, FIRMWARE_UNLOAD); return (error); } static int run_reset(struct run_softc *sc) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2870_RESET; USETW(req.wValue, 1); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)); } static usb_error_t run_do_request(struct run_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 10; RUN_LOCK_ASSERT(sc, MA_OWNED); while (ntries--) { err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 250 /* ms */); if (err == 0) break; RUN_DPRINTF(sc, RUN_DEBUG_USB, "Control request failed, %s (retrying)\n", usbd_errstr(err)); run_delay(sc, 10); } return (err); } static int run_read(struct run_softc *sc, uint16_t reg, uint32_t *val) { uint32_t tmp; int error; error = run_read_region_1(sc, reg, (uint8_t *)&tmp, sizeof tmp); if (error == 0) *val = le32toh(tmp); else *val = 0xffffffff; return (error); } static int run_read_region_1(struct run_softc *sc, uint16_t reg, uint8_t *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2870_READ_REGION_1; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); return (run_do_request(sc, &req, buf)); } static int run_write_2(struct run_softc *sc, uint16_t reg, uint16_t val) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2870_WRITE_2; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); return (run_do_request(sc, &req, NULL)); } static int run_write(struct run_softc *sc, uint16_t reg, uint32_t val) { int error; if ((error = run_write_2(sc, reg, val & 0xffff)) == 0) error = run_write_2(sc, reg + 2, val >> 16); return (error); } static int run_write_region_1(struct run_softc *sc, uint16_t reg, const uint8_t *buf, int len) { #if 1 int i, error = 0; /* * NB: the WRITE_REGION_1 command is not stable on RT2860. * We thus issue multiple WRITE_2 commands instead. */ KASSERT((len & 1) == 0, ("run_write_region_1: Data too long.\n")); for (i = 0; i < len && error == 0; i += 2) error = run_write_2(sc, reg + i, buf[i] | buf[i + 1] << 8); return (error); #else usb_device_request_t req; int error = 0; /* * NOTE: It appears the WRITE_REGION_1 command cannot be * passed a huge amount of data, which will crash the * firmware. Limit amount of data passed to 64-bytes at a * time. */ while (len > 0) { int delta = 64; if (delta > len) delta = len; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2870_WRITE_REGION_1; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, delta); error = run_do_request(sc, &req, __DECONST(uint8_t *, buf)); if (error != 0) break; reg += delta; buf += delta; len -= delta; } return (error); #endif } static int run_set_region_4(struct run_softc *sc, uint16_t reg, uint32_t val, int len) { int i, error = 0; KASSERT((len & 3) == 0, ("run_set_region_4: Invalid data length.\n")); for (i = 0; i < len && error == 0; i += 4) error = run_write(sc, reg + i, val); return (error); } static int run_efuse_read(struct run_softc *sc, uint16_t addr, uint16_t *val, int count) { uint32_t tmp; uint16_t reg; int error, ntries; if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0) return (error); if (count == 2) addr *= 2; /*- * Read one 16-byte block into registers EFUSE_DATA[0-3]: * DATA0: F E D C * DATA1: B A 9 8 * DATA2: 7 6 5 4 * DATA3: 3 2 1 0 */ tmp &= ~(RT3070_EFSROM_MODE_MASK | RT3070_EFSROM_AIN_MASK); tmp |= (addr & ~0xf) << RT3070_EFSROM_AIN_SHIFT | RT3070_EFSROM_KICK; run_write(sc, RT3070_EFUSE_CTRL, tmp); for (ntries = 0; ntries < 100; ntries++) { if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0) return (error); if (!(tmp & RT3070_EFSROM_KICK)) break; run_delay(sc, 2); } if (ntries == 100) return (ETIMEDOUT); if ((tmp & RT3070_EFUSE_AOUT_MASK) == RT3070_EFUSE_AOUT_MASK) { *val = 0xffff; /* address not found */ return (0); } /* determine to which 32-bit register our 16-bit word belongs */ reg = RT3070_EFUSE_DATA3 - (addr & 0xc); if ((error = run_read(sc, reg, &tmp)) != 0) return (error); tmp >>= (8 * (addr & 0x3)); *val = (addr & 1) ? tmp >> 16 : tmp & 0xffff; return (0); } /* Read 16-bit from eFUSE ROM for RT3xxx. */ static int run_efuse_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val) { return (run_efuse_read(sc, addr, val, 2)); } static int run_eeprom_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val) { usb_device_request_t req; uint16_t tmp; int error; addr *= 2; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2870_EEPROM_READ; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, sizeof(tmp)); error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &tmp); if (error == 0) *val = le16toh(tmp); else *val = 0xffff; return (error); } static __inline int run_srom_read(struct run_softc *sc, uint16_t addr, uint16_t *val) { /* either eFUSE ROM or EEPROM */ return sc->sc_srom_read(sc, addr, val); } static int run_rt2870_rf_write(struct run_softc *sc, uint32_t val) { uint32_t tmp; int error, ntries; for (ntries = 0; ntries < 10; ntries++) { if ((error = run_read(sc, RT2860_RF_CSR_CFG0, &tmp)) != 0) return (error); if (!(tmp & RT2860_RF_REG_CTRL)) break; } if (ntries == 10) return (ETIMEDOUT); return (run_write(sc, RT2860_RF_CSR_CFG0, val)); } static int run_rt3070_rf_read(struct run_softc *sc, uint8_t reg, uint8_t *val) { uint32_t tmp; int error, ntries; for (ntries = 0; ntries < 100; ntries++) { if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT3070_RF_KICK)) break; } if (ntries == 100) return (ETIMEDOUT); tmp = RT3070_RF_KICK | reg << 8; if ((error = run_write(sc, RT3070_RF_CSR_CFG, tmp)) != 0) return (error); for (ntries = 0; ntries < 100; ntries++) { if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT3070_RF_KICK)) break; } if (ntries == 100) return (ETIMEDOUT); *val = tmp & 0xff; return (0); } static int run_rt3070_rf_write(struct run_softc *sc, uint8_t reg, uint8_t val) { uint32_t tmp; int error, ntries; for (ntries = 0; ntries < 10; ntries++) { if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT3070_RF_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); tmp = RT3070_RF_WRITE | RT3070_RF_KICK | reg << 8 | val; return (run_write(sc, RT3070_RF_CSR_CFG, tmp)); } static int run_bbp_read(struct run_softc *sc, uint8_t reg, uint8_t *val) { uint32_t tmp; int ntries, error; for (ntries = 0; ntries < 10; ntries++) { if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT2860_BBP_CSR_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); tmp = RT2860_BBP_CSR_READ | RT2860_BBP_CSR_KICK | reg << 8; if ((error = run_write(sc, RT2860_BBP_CSR_CFG, tmp)) != 0) return (error); for (ntries = 0; ntries < 10; ntries++) { if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT2860_BBP_CSR_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); *val = tmp & 0xff; return (0); } static int run_bbp_write(struct run_softc *sc, uint8_t reg, uint8_t val) { uint32_t tmp; int ntries, error; for (ntries = 0; ntries < 10; ntries++) { if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) return (error); if (!(tmp & RT2860_BBP_CSR_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); tmp = RT2860_BBP_CSR_KICK | reg << 8 | val; return (run_write(sc, RT2860_BBP_CSR_CFG, tmp)); } /* * Send a command to the 8051 microcontroller unit. */ static int run_mcu_cmd(struct run_softc *sc, uint8_t cmd, uint16_t arg) { uint32_t tmp; int error, ntries; for (ntries = 0; ntries < 100; ntries++) { if ((error = run_read(sc, RT2860_H2M_MAILBOX, &tmp)) != 0) return error; if (!(tmp & RT2860_H2M_BUSY)) break; } if (ntries == 100) return ETIMEDOUT; tmp = RT2860_H2M_BUSY | RT2860_TOKEN_NO_INTR << 16 | arg; if ((error = run_write(sc, RT2860_H2M_MAILBOX, tmp)) == 0) error = run_write(sc, RT2860_HOST_CMD, cmd); return (error); } /* * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word. * Used to adjust per-rate Tx power registers. */ static __inline uint32_t b4inc(uint32_t b32, int8_t delta) { int8_t i, b4; for (i = 0; i < 8; i++) { b4 = b32 & 0xf; b4 += delta; if (b4 < 0) b4 = 0; else if (b4 > 0xf) b4 = 0xf; b32 = b32 >> 4 | b4 << 28; } return (b32); } static const char * run_get_rf(uint16_t rev) { switch (rev) { case RT2860_RF_2820: return "RT2820"; case RT2860_RF_2850: return "RT2850"; case RT2860_RF_2720: return "RT2720"; case RT2860_RF_2750: return "RT2750"; case RT3070_RF_3020: return "RT3020"; case RT3070_RF_2020: return "RT2020"; case RT3070_RF_3021: return "RT3021"; case RT3070_RF_3022: return "RT3022"; case RT3070_RF_3052: return "RT3052"; case RT3593_RF_3053: return "RT3053"; case RT5592_RF_5592: return "RT5592"; case RT5390_RF_5370: return "RT5370"; case RT5390_RF_5372: return "RT5372"; } return ("unknown"); } static void run_rt3593_get_txpower(struct run_softc *sc) { uint16_t addr, val; int i; /* Read power settings for 2GHz channels. */ for (i = 0; i < 14; i += 2) { addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE1 : RT2860_EEPROM_PWR2GHZ_BASE1; run_srom_read(sc, addr + i / 2, &val); sc->txpow1[i + 0] = (int8_t)(val & 0xff); sc->txpow1[i + 1] = (int8_t)(val >> 8); addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE2 : RT2860_EEPROM_PWR2GHZ_BASE2; run_srom_read(sc, addr + i / 2, &val); sc->txpow2[i + 0] = (int8_t)(val & 0xff); sc->txpow2[i + 1] = (int8_t)(val >> 8); if (sc->ntxchains == 3) { run_srom_read(sc, RT3593_EEPROM_PWR2GHZ_BASE3 + i / 2, &val); sc->txpow3[i + 0] = (int8_t)(val & 0xff); sc->txpow3[i + 1] = (int8_t)(val >> 8); } } /* Fix broken Tx power entries. */ for (i = 0; i < 14; i++) { if (sc->txpow1[i] > 31) sc->txpow1[i] = 5; if (sc->txpow2[i] > 31) sc->txpow2[i] = 5; if (sc->ntxchains == 3) { if (sc->txpow3[i] > 31) sc->txpow3[i] = 5; } } /* Read power settings for 5GHz channels. */ for (i = 0; i < 40; i += 2) { run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE1 + i / 2, &val); sc->txpow1[i + 14] = (int8_t)(val & 0xff); sc->txpow1[i + 15] = (int8_t)(val >> 8); run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE2 + i / 2, &val); sc->txpow2[i + 14] = (int8_t)(val & 0xff); sc->txpow2[i + 15] = (int8_t)(val >> 8); if (sc->ntxchains == 3) { run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE3 + i / 2, &val); sc->txpow3[i + 14] = (int8_t)(val & 0xff); sc->txpow3[i + 15] = (int8_t)(val >> 8); } } } static void run_get_txpower(struct run_softc *sc) { uint16_t val; int i; /* Read power settings for 2GHz channels. */ for (i = 0; i < 14; i += 2) { run_srom_read(sc, RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2, &val); sc->txpow1[i + 0] = (int8_t)(val & 0xff); sc->txpow1[i + 1] = (int8_t)(val >> 8); if (sc->mac_ver != 0x5390) { run_srom_read(sc, RT2860_EEPROM_PWR2GHZ_BASE2 + i / 2, &val); sc->txpow2[i + 0] = (int8_t)(val & 0xff); sc->txpow2[i + 1] = (int8_t)(val >> 8); } } /* Fix broken Tx power entries. */ for (i = 0; i < 14; i++) { if (sc->mac_ver >= 0x5390) { if (sc->txpow1[i] < 0 || sc->txpow1[i] > 39) sc->txpow1[i] = 5; } else { if (sc->txpow1[i] < 0 || sc->txpow1[i] > 31) sc->txpow1[i] = 5; } if (sc->mac_ver > 0x5390) { if (sc->txpow2[i] < 0 || sc->txpow2[i] > 39) sc->txpow2[i] = 5; } else if (sc->mac_ver < 0x5390) { if (sc->txpow2[i] < 0 || sc->txpow2[i] > 31) sc->txpow2[i] = 5; } RUN_DPRINTF(sc, RUN_DEBUG_TXPWR, "chan %d: power1=%d, power2=%d\n", rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i]); } /* Read power settings for 5GHz channels. */ for (i = 0; i < 40; i += 2) { run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2, &val); sc->txpow1[i + 14] = (int8_t)(val & 0xff); sc->txpow1[i + 15] = (int8_t)(val >> 8); run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2, &val); sc->txpow2[i + 14] = (int8_t)(val & 0xff); sc->txpow2[i + 15] = (int8_t)(val >> 8); } /* Fix broken Tx power entries. */ for (i = 0; i < 40; i++ ) { if (sc->mac_ver != 0x5592) { if (sc->txpow1[14 + i] < -7 || sc->txpow1[14 + i] > 15) sc->txpow1[14 + i] = 5; if (sc->txpow2[14 + i] < -7 || sc->txpow2[14 + i] > 15) sc->txpow2[14 + i] = 5; } RUN_DPRINTF(sc, RUN_DEBUG_TXPWR, "chan %d: power1=%d, power2=%d\n", rt2860_rf2850[14 + i].chan, sc->txpow1[14 + i], sc->txpow2[14 + i]); } } static int run_read_eeprom(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; int8_t delta_2ghz, delta_5ghz; uint32_t tmp; uint16_t val; int ridx, ant, i; /* check whether the ROM is eFUSE ROM or EEPROM */ sc->sc_srom_read = run_eeprom_read_2; if (sc->mac_ver >= 0x3070) { run_read(sc, RT3070_EFUSE_CTRL, &tmp); RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EFUSE_CTRL=0x%08x\n", tmp); if ((tmp & RT3070_SEL_EFUSE) || sc->mac_ver == 0x3593) sc->sc_srom_read = run_efuse_read_2; } /* read ROM version */ run_srom_read(sc, RT2860_EEPROM_VERSION, &val); RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM rev=%d, FAE=%d\n", val >> 8, val & 0xff); /* read MAC address */ run_srom_read(sc, RT2860_EEPROM_MAC01, &val); ic->ic_macaddr[0] = val & 0xff; ic->ic_macaddr[1] = val >> 8; run_srom_read(sc, RT2860_EEPROM_MAC23, &val); ic->ic_macaddr[2] = val & 0xff; ic->ic_macaddr[3] = val >> 8; run_srom_read(sc, RT2860_EEPROM_MAC45, &val); ic->ic_macaddr[4] = val & 0xff; ic->ic_macaddr[5] = val >> 8; if (sc->mac_ver < 0x3593) { /* read vender BBP settings */ for (i = 0; i < 10; i++) { run_srom_read(sc, RT2860_EEPROM_BBP_BASE + i, &val); sc->bbp[i].val = val & 0xff; sc->bbp[i].reg = val >> 8; RUN_DPRINTF(sc, RUN_DEBUG_ROM, "BBP%d=0x%02x\n", sc->bbp[i].reg, sc->bbp[i].val); } if (sc->mac_ver >= 0x3071) { /* read vendor RF settings */ for (i = 0; i < 10; i++) { run_srom_read(sc, RT3071_EEPROM_RF_BASE + i, &val); sc->rf[i].val = val & 0xff; sc->rf[i].reg = val >> 8; RUN_DPRINTF(sc, RUN_DEBUG_ROM, "RF%d=0x%02x\n", sc->rf[i].reg, sc->rf[i].val); } } } /* read RF frequency offset from EEPROM */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS : RT3593_EEPROM_FREQ, &val); sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0; RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM freq offset %d\n", sc->freq & 0xff); run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS : RT3593_EEPROM_FREQ_LEDS, &val); if (val >> 8 != 0xff) { /* read LEDs operating mode */ sc->leds = val >> 8; run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED1 : RT3593_EEPROM_LED1, &sc->led[0]); run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED2 : RT3593_EEPROM_LED2, &sc->led[1]); run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED3 : RT3593_EEPROM_LED3, &sc->led[2]); } else { /* broken EEPROM, use default settings */ sc->leds = 0x01; sc->led[0] = 0x5555; sc->led[1] = 0x2221; sc->led[2] = 0x5627; /* differs from RT2860 */ } RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n", sc->leds, sc->led[0], sc->led[1], sc->led[2]); /* read RF information */ if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392) run_srom_read(sc, 0x00, &val); else run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val); if (val == 0xffff) { device_printf(sc->sc_dev, "invalid EEPROM antenna info, using default\n"); if (sc->mac_ver == 0x3572) { /* default to RF3052 2T2R */ sc->rf_rev = RT3070_RF_3052; sc->ntxchains = 2; sc->nrxchains = 2; } else if (sc->mac_ver >= 0x3070) { /* default to RF3020 1T1R */ sc->rf_rev = RT3070_RF_3020; sc->ntxchains = 1; sc->nrxchains = 1; } else { /* default to RF2820 1T2R */ sc->rf_rev = RT2860_RF_2820; sc->ntxchains = 1; sc->nrxchains = 2; } } else { if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392) { sc->rf_rev = val; run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val); } else sc->rf_rev = (val >> 8) & 0xf; sc->ntxchains = (val >> 4) & 0xf; sc->nrxchains = val & 0xf; } RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM RF rev=0x%04x chains=%dT%dR\n", sc->rf_rev, sc->ntxchains, sc->nrxchains); /* check if RF supports automatic Tx access gain control */ run_srom_read(sc, RT2860_EEPROM_CONFIG, &val); RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM CFG 0x%04x\n", val); /* check if driver should patch the DAC issue */ if ((val >> 8) != 0xff) sc->patch_dac = (val >> 15) & 1; if ((val & 0xff) != 0xff) { sc->ext_5ghz_lna = (val >> 3) & 1; sc->ext_2ghz_lna = (val >> 2) & 1; /* check if RF supports automatic Tx access gain control */ sc->calib_2ghz = sc->calib_5ghz = (val >> 1) & 1; /* check if we have a hardware radio switch */ sc->rfswitch = val & 1; } /* Read Tx power settings. */ if (sc->mac_ver == 0x3593) run_rt3593_get_txpower(sc); else run_get_txpower(sc); /* read Tx power compensation for each Tx rate */ run_srom_read(sc, RT2860_EEPROM_DELTAPWR, &val); delta_2ghz = delta_5ghz = 0; if ((val & 0xff) != 0xff && (val & 0x80)) { delta_2ghz = val & 0xf; if (!(val & 0x40)) /* negative number */ delta_2ghz = -delta_2ghz; } val >>= 8; if ((val & 0xff) != 0xff && (val & 0x80)) { delta_5ghz = val & 0xf; if (!(val & 0x40)) /* negative number */ delta_5ghz = -delta_5ghz; } RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_TXPWR, "power compensation=%d (2GHz), %d (5GHz)\n", delta_2ghz, delta_5ghz); for (ridx = 0; ridx < 5; ridx++) { uint32_t reg; run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2, &val); reg = val; run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2 + 1, &val); reg |= (uint32_t)val << 16; sc->txpow20mhz[ridx] = reg; sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz); sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz); RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_TXPWR, "ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, " "40MHz/5GHz=0x%08x\n", ridx, sc->txpow20mhz[ridx], sc->txpow40mhz_2ghz[ridx], sc->txpow40mhz_5ghz[ridx]); } /* Read RSSI offsets and LNA gains from EEPROM. */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_2GHZ : RT3593_EEPROM_RSSI1_2GHZ, &val); sc->rssi_2ghz[0] = val & 0xff; /* Ant A */ sc->rssi_2ghz[1] = val >> 8; /* Ant B */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_2GHZ : RT3593_EEPROM_RSSI2_2GHZ, &val); if (sc->mac_ver >= 0x3070) { if (sc->mac_ver == 0x3593) { sc->txmixgain_2ghz = 0; sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ } else { /* * On RT3070 chips (limited to 2 Rx chains), this ROM * field contains the Tx mixer gain for the 2GHz band. */ if ((val & 0xff) != 0xff) sc->txmixgain_2ghz = val & 0x7; } RUN_DPRINTF(sc, RUN_DEBUG_ROM, "tx mixer gain=%u (2GHz)\n", sc->txmixgain_2ghz); } else sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ if (sc->mac_ver == 0x3593) run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val); sc->lna[2] = val >> 8; /* channel group 2 */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_5GHZ : RT3593_EEPROM_RSSI1_5GHZ, &val); sc->rssi_5ghz[0] = val & 0xff; /* Ant A */ sc->rssi_5ghz[1] = val >> 8; /* Ant B */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_5GHZ : RT3593_EEPROM_RSSI2_5GHZ, &val); if (sc->mac_ver == 0x3572) { /* * On RT3572 chips (limited to 2 Rx chains), this ROM * field contains the Tx mixer gain for the 5GHz band. */ if ((val & 0xff) != 0xff) sc->txmixgain_5ghz = val & 0x7; RUN_DPRINTF(sc, RUN_DEBUG_ROM, "tx mixer gain=%u (5GHz)\n", sc->txmixgain_5ghz); } else sc->rssi_5ghz[2] = val & 0xff; /* Ant C */ if (sc->mac_ver == 0x3593) { sc->txmixgain_5ghz = 0; run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val); } sc->lna[3] = val >> 8; /* channel group 3 */ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LNA : RT3593_EEPROM_LNA, &val); sc->lna[0] = val & 0xff; /* channel group 0 */ sc->lna[1] = val >> 8; /* channel group 1 */ /* fix broken 5GHz LNA entries */ if (sc->lna[2] == 0 || sc->lna[2] == 0xff) { RUN_DPRINTF(sc, RUN_DEBUG_ROM, "invalid LNA for channel group %d\n", 2); sc->lna[2] = sc->lna[1]; } if (sc->lna[3] == 0 || sc->lna[3] == 0xff) { RUN_DPRINTF(sc, RUN_DEBUG_ROM, "invalid LNA for channel group %d\n", 3); sc->lna[3] = sc->lna[1]; } /* fix broken RSSI offset entries */ for (ant = 0; ant < 3; ant++) { if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) { RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_RSSI, "invalid RSSI%d offset: %d (2GHz)\n", ant + 1, sc->rssi_2ghz[ant]); sc->rssi_2ghz[ant] = 0; } if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) { RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_RSSI, "invalid RSSI%d offset: %d (5GHz)\n", ant + 1, sc->rssi_5ghz[ant]); sc->rssi_5ghz[ant] = 0; } } return (0); } static struct ieee80211_node * run_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { return malloc(sizeof (struct run_node), M_80211_NODE, M_NOWAIT | M_ZERO); } static int run_media_change(struct ifnet *ifp) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_txparam *tp; struct run_softc *sc = ic->ic_softc; uint8_t rate, ridx; int error; RUN_LOCK(sc); error = ieee80211_media_change(ifp); if (error != ENETRESET) { RUN_UNLOCK(sc); return (error); } tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { struct ieee80211_node *ni; struct run_node *rn; rate = ic->ic_sup_rates[ic->ic_curmode]. rs_rates[tp->ucastrate] & IEEE80211_RATE_VAL; for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == rate) break; ni = ieee80211_ref_node(vap->iv_bss); rn = RUN_NODE(ni); rn->fix_ridx = ridx; RUN_DPRINTF(sc, RUN_DEBUG_RATE, "rate=%d, fix_ridx=%d\n", rate, rn->fix_ridx); ieee80211_free_node(ni); } #if 0 if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & RUN_RUNNING)){ run_init_locked(sc); } #endif RUN_UNLOCK(sc); return (0); } static int run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { const struct ieee80211_txparam *tp; struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; struct run_vap *rvp = RUN_VAP(vap); enum ieee80211_state ostate; uint32_t sta[3]; uint8_t ratectl; uint8_t restart_ratectl = 0; uint8_t bid = 1 << rvp->rvp_id; ostate = vap->iv_state; RUN_DPRINTF(sc, RUN_DEBUG_STATE, "%s -> %s\n", ieee80211_state_name[ostate], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); RUN_LOCK(sc); ratectl = sc->ratectl_run; /* remember current state */ sc->ratectl_run = RUN_RATECTL_OFF; usb_callout_stop(&sc->ratectl_ch); if (ostate == IEEE80211_S_RUN) { /* turn link LED off */ run_set_leds(sc, RT2860_LED_RADIO); } switch (nstate) { case IEEE80211_S_INIT: restart_ratectl = 1; if (ostate != IEEE80211_S_RUN) break; ratectl &= ~bid; sc->runbmap &= ~bid; /* abort TSF synchronization if there is no vap running */ if (--sc->running == 0) run_disable_tsf(sc); break; case IEEE80211_S_RUN: if (!(sc->runbmap & bid)) { if(sc->running++) restart_ratectl = 1; sc->runbmap |= bid; } m_freem(rvp->beacon_mbuf); rvp->beacon_mbuf = NULL; switch (vap->iv_opmode) { case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: sc->ap_running |= bid; ic->ic_opmode = vap->iv_opmode; run_update_beacon_cb(vap); break; case IEEE80211_M_IBSS: sc->adhoc_running |= bid; if (!sc->ap_running) ic->ic_opmode = vap->iv_opmode; run_update_beacon_cb(vap); break; case IEEE80211_M_STA: sc->sta_running |= bid; if (!sc->ap_running && !sc->adhoc_running) ic->ic_opmode = vap->iv_opmode; /* read statistic counters (clear on read) */ run_read_region_1(sc, RT2860_TX_STA_CNT0, (uint8_t *)sta, sizeof sta); break; default: ic->ic_opmode = vap->iv_opmode; break; } if (vap->iv_opmode != IEEE80211_M_MONITOR) { struct ieee80211_node *ni; if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) { RUN_UNLOCK(sc); IEEE80211_LOCK(ic); return (-1); } run_updateslot(ic); run_enable_mrr(sc); run_set_txpreamble(sc); run_set_basicrates(sc); ni = ieee80211_ref_node(vap->iv_bss); IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); run_set_bssid(sc, sc->sc_bssid); ieee80211_free_node(ni); run_enable_tsf_sync(sc); /* enable automatic rate adaptation */ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) ratectl |= bid; } else run_enable_tsf(sc); /* turn link LED on */ run_set_leds(sc, RT2860_LED_RADIO | (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ? RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ)); break; default: RUN_DPRINTF(sc, RUN_DEBUG_STATE, "undefined state\n"); break; } /* restart amrr for running VAPs */ if ((sc->ratectl_run = ratectl) && restart_ratectl) usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc); RUN_UNLOCK(sc); IEEE80211_LOCK(ic); return(rvp->newstate(vap, nstate, arg)); } static int run_wme_update(struct ieee80211com *ic) { struct chanAccParams chp; struct run_softc *sc = ic->ic_softc; const struct wmeParams *ac; int aci, error = 0; ieee80211_wme_ic_getparams(ic, &chp); ac = chp.cap_wmeParams; /* update MAC TX configuration registers */ RUN_LOCK(sc); for (aci = 0; aci < WME_NUM_AC; aci++) { error = run_write(sc, RT2860_EDCA_AC_CFG(aci), ac[aci].wmep_logcwmax << 16 | ac[aci].wmep_logcwmin << 12 | ac[aci].wmep_aifsn << 8 | ac[aci].wmep_txopLimit); if (error) goto err; } /* update SCH/DMA registers too */ error = run_write(sc, RT2860_WMM_AIFSN_CFG, ac[WME_AC_VO].wmep_aifsn << 12 | ac[WME_AC_VI].wmep_aifsn << 8 | ac[WME_AC_BK].wmep_aifsn << 4 | ac[WME_AC_BE].wmep_aifsn); if (error) goto err; error = run_write(sc, RT2860_WMM_CWMIN_CFG, ac[WME_AC_VO].wmep_logcwmin << 12 | ac[WME_AC_VI].wmep_logcwmin << 8 | ac[WME_AC_BK].wmep_logcwmin << 4 | ac[WME_AC_BE].wmep_logcwmin); if (error) goto err; error = run_write(sc, RT2860_WMM_CWMAX_CFG, ac[WME_AC_VO].wmep_logcwmax << 12 | ac[WME_AC_VI].wmep_logcwmax << 8 | ac[WME_AC_BK].wmep_logcwmax << 4 | ac[WME_AC_BE].wmep_logcwmax); if (error) goto err; error = run_write(sc, RT2860_WMM_TXOP0_CFG, ac[WME_AC_BK].wmep_txopLimit << 16 | ac[WME_AC_BE].wmep_txopLimit); if (error) goto err; error = run_write(sc, RT2860_WMM_TXOP1_CFG, ac[WME_AC_VO].wmep_txopLimit << 16 | ac[WME_AC_VI].wmep_txopLimit); err: RUN_UNLOCK(sc); if (error) RUN_DPRINTF(sc, RUN_DEBUG_USB, "WME update failed\n"); return (error); } static void run_key_set_cb(void *arg) { struct run_cmdq *cmdq = arg; struct ieee80211vap *vap = cmdq->arg1; struct ieee80211_key *k = cmdq->k; struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; struct ieee80211_node *ni; u_int cipher = k->wk_cipher->ic_cipher; uint32_t attr; uint16_t base, associd; uint8_t mode, wcid, iv[8]; RUN_LOCK_ASSERT(sc, MA_OWNED); if (vap->iv_opmode == IEEE80211_M_HOSTAP) ni = ieee80211_find_vap_node(&ic->ic_sta, vap, cmdq->mac); else ni = vap->iv_bss; associd = (ni != NULL) ? ni->ni_associd : 0; /* map net80211 cipher to RT2860 security mode */ switch (cipher) { case IEEE80211_CIPHER_WEP: if(k->wk_keylen < 8) mode = RT2860_MODE_WEP40; else mode = RT2860_MODE_WEP104; break; case IEEE80211_CIPHER_TKIP: mode = RT2860_MODE_TKIP; break; case IEEE80211_CIPHER_AES_CCM: mode = RT2860_MODE_AES_CCMP; break; default: RUN_DPRINTF(sc, RUN_DEBUG_KEY, "undefined case\n"); return; } RUN_DPRINTF(sc, RUN_DEBUG_KEY, "associd=%x, keyix=%d, mode=%x, type=%s, tx=%s, rx=%s\n", associd, k->wk_keyix, mode, (k->wk_flags & IEEE80211_KEY_GROUP) ? "group" : "pairwise", (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off", (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off"); if (k->wk_flags & IEEE80211_KEY_GROUP) { wcid = 0; /* NB: update WCID0 for group keys */ base = RT2860_SKEY(RUN_VAP(vap)->rvp_id, k->wk_keyix); } else { wcid = (vap->iv_opmode == IEEE80211_M_STA) ? 1 : RUN_AID2WCID(associd); base = RT2860_PKEY(wcid); } if (cipher == IEEE80211_CIPHER_TKIP) { if(run_write_region_1(sc, base, k->wk_key, 16)) return; if(run_write_region_1(sc, base + 16, &k->wk_key[16], 8)) /* wk_txmic */ return; if(run_write_region_1(sc, base + 24, &k->wk_key[24], 8)) /* wk_rxmic */ return; } else { /* roundup len to 16-bit: XXX fix write_region_1() instead */ if(run_write_region_1(sc, base, k->wk_key, (k->wk_keylen + 1) & ~1)) return; } if (!(k->wk_flags & IEEE80211_KEY_GROUP) || (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))) { /* set initial packet number in IV+EIV */ if (cipher == IEEE80211_CIPHER_WEP) { memset(iv, 0, sizeof iv); iv[3] = vap->iv_def_txkey << 6; } else { if (cipher == IEEE80211_CIPHER_TKIP) { iv[0] = k->wk_keytsc >> 8; iv[1] = (iv[0] | 0x20) & 0x7f; iv[2] = k->wk_keytsc; } else /* CCMP */ { iv[0] = k->wk_keytsc; iv[1] = k->wk_keytsc >> 8; iv[2] = 0; } iv[3] = k->wk_keyix << 6 | IEEE80211_WEP_EXTIV; iv[4] = k->wk_keytsc >> 16; iv[5] = k->wk_keytsc >> 24; iv[6] = k->wk_keytsc >> 32; iv[7] = k->wk_keytsc >> 40; } if (run_write_region_1(sc, RT2860_IVEIV(wcid), iv, 8)) return; } if (k->wk_flags & IEEE80211_KEY_GROUP) { /* install group key */ if (run_read(sc, RT2860_SKEY_MODE_0_7, &attr)) return; attr &= ~(0xf << (k->wk_keyix * 4)); attr |= mode << (k->wk_keyix * 4); if (run_write(sc, RT2860_SKEY_MODE_0_7, attr)) return; } else { /* install pairwise key */ if (run_read(sc, RT2860_WCID_ATTR(wcid), &attr)) return; attr = (attr & ~0xf) | (mode << 1) | RT2860_RX_PKEY_EN; if (run_write(sc, RT2860_WCID_ATTR(wcid), attr)) return; } /* TODO create a pass-thru key entry? */ /* need wcid to delete the right key later */ k->wk_pad = wcid; } /* * Don't have to be deferred, but in order to keep order of * execution, i.e. with run_key_delete(), defer this and let * run_cmdq_cb() maintain the order. * * return 0 on error */ static int run_key_set(struct ieee80211vap *vap, struct ieee80211_key *k) { struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; uint32_t i; i = RUN_CMDQ_GET(&sc->cmdq_store); RUN_DPRINTF(sc, RUN_DEBUG_KEY, "cmdq_store=%d\n", i); sc->cmdq[i].func = run_key_set_cb; sc->cmdq[i].arg0 = NULL; sc->cmdq[i].arg1 = vap; sc->cmdq[i].k = k; IEEE80211_ADDR_COPY(sc->cmdq[i].mac, k->wk_macaddr); ieee80211_runtask(ic, &sc->cmdq_task); /* * To make sure key will be set when hostapd * calls iv_key_set() before if_init(). */ if (vap->iv_opmode == IEEE80211_M_HOSTAP) { RUN_LOCK(sc); sc->cmdq_key_set = RUN_CMDQ_GO; RUN_UNLOCK(sc); } return (1); } /* * If wlan is destroyed without being brought down i.e. without * wlan down or wpa_cli terminate, this function is called after * vap is gone. Don't refer it. */ static void run_key_delete_cb(void *arg) { struct run_cmdq *cmdq = arg; struct run_softc *sc = cmdq->arg1; struct ieee80211_key *k = &cmdq->key; uint32_t attr; uint8_t wcid; RUN_LOCK_ASSERT(sc, MA_OWNED); if (k->wk_flags & IEEE80211_KEY_GROUP) { /* remove group key */ RUN_DPRINTF(sc, RUN_DEBUG_KEY, "removing group key\n"); run_read(sc, RT2860_SKEY_MODE_0_7, &attr); attr &= ~(0xf << (k->wk_keyix * 4)); run_write(sc, RT2860_SKEY_MODE_0_7, attr); } else { /* remove pairwise key */ RUN_DPRINTF(sc, RUN_DEBUG_KEY, "removing key for wcid %x\n", k->wk_pad); /* matching wcid was written to wk_pad in run_key_set() */ wcid = k->wk_pad; run_read(sc, RT2860_WCID_ATTR(wcid), &attr); attr &= ~0xf; run_write(sc, RT2860_WCID_ATTR(wcid), attr); run_set_region_4(sc, RT2860_WCID_ENTRY(wcid), 0, 8); } k->wk_pad = 0; } /* * return 0 on error */ static int run_key_delete(struct ieee80211vap *vap, struct ieee80211_key *k) { struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; struct ieee80211_key *k0; uint32_t i; /* * When called back, key might be gone. So, make a copy * of some values need to delete keys before deferring. * But, because of LOR with node lock, cannot use lock here. * So, use atomic instead. */ i = RUN_CMDQ_GET(&sc->cmdq_store); RUN_DPRINTF(sc, RUN_DEBUG_KEY, "cmdq_store=%d\n", i); sc->cmdq[i].func = run_key_delete_cb; sc->cmdq[i].arg0 = NULL; sc->cmdq[i].arg1 = sc; k0 = &sc->cmdq[i].key; k0->wk_flags = k->wk_flags; k0->wk_keyix = k->wk_keyix; /* matching wcid was written to wk_pad in run_key_set() */ k0->wk_pad = k->wk_pad; ieee80211_runtask(ic, &sc->cmdq_task); return (1); /* return fake success */ } static void run_ratectl_to(void *arg) { struct run_softc *sc = arg; /* do it in a process context, so it can go sleep */ ieee80211_runtask(&sc->sc_ic, &sc->ratectl_task); /* next timeout will be rescheduled in the callback task */ } /* ARGSUSED */ static void run_ratectl_cb(void *arg, int pending) { struct run_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (vap == NULL) return; if (sc->rvp_cnt > 1 || vap->iv_opmode != IEEE80211_M_STA) { /* * run_reset_livelock() doesn't do anything with AMRR, * but Ralink wants us to call it every 1 sec. So, we * piggyback here rather than creating another callout. * Livelock may occur only in HOSTAP or IBSS mode * (when h/w is sending beacons). */ RUN_LOCK(sc); run_reset_livelock(sc); /* just in case, there are some stats to drain */ run_drain_fifo(sc); RUN_UNLOCK(sc); } ieee80211_iterate_nodes(&ic->ic_sta, run_iter_func, sc); RUN_LOCK(sc); if(sc->ratectl_run != RUN_RATECTL_OFF) usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc); RUN_UNLOCK(sc); } static void run_drain_fifo(void *arg) { struct run_softc *sc = arg; uint32_t stat; uint16_t (*wstat)[3]; uint8_t wcid, mcs, pid; int8_t retry; RUN_LOCK_ASSERT(sc, MA_OWNED); for (;;) { /* drain Tx status FIFO (maxsize = 16) */ run_read(sc, RT2860_TX_STAT_FIFO, &stat); RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx stat 0x%08x\n", stat); if (!(stat & RT2860_TXQ_VLD)) break; wcid = (stat >> RT2860_TXQ_WCID_SHIFT) & 0xff; /* if no ACK was requested, no feedback is available */ if (!(stat & RT2860_TXQ_ACKREQ) || wcid > RT2870_WCID_MAX || wcid == 0) continue; /* * Even though each stat is Tx-complete-status like format, * the device can poll stats. Because there is no guarantee * that the referring node is still around when read the stats. * So that, if we use ieee80211_ratectl_tx_update(), we will * have hard time not to refer already freed node. * * To eliminate such page faults, we poll stats in softc. * Then, update the rates later with ieee80211_ratectl_tx_update(). */ wstat = &(sc->wcid_stats[wcid]); (*wstat)[RUN_TXCNT]++; if (stat & RT2860_TXQ_OK) (*wstat)[RUN_SUCCESS]++; else counter_u64_add(sc->sc_ic.ic_oerrors, 1); /* * Check if there were retries, ie if the Tx success rate is * different from the requested rate. Note that it works only * because we do not allow rate fallback from OFDM to CCK. */ mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f; pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf; if ((retry = pid -1 - mcs) > 0) { (*wstat)[RUN_TXCNT] += retry; (*wstat)[RUN_RETRY] += retry; } } RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "count=%d\n", sc->fifo_cnt); sc->fifo_cnt = 0; } static void run_iter_func(void *arg, struct ieee80211_node *ni) { struct run_softc *sc = arg; struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs; struct ieee80211vap *vap = ni->ni_vap; struct run_node *rn = RUN_NODE(ni); union run_stats sta[2]; uint16_t (*wstat)[3]; int error; RUN_LOCK(sc); /* Check for special case */ if (sc->rvp_cnt <= 1 && vap->iv_opmode == IEEE80211_M_STA && ni != vap->iv_bss) goto fail; txs->flags = IEEE80211_RATECTL_TX_STATS_NODE | IEEE80211_RATECTL_TX_STATS_RETRIES; txs->ni = ni; if (sc->rvp_cnt <= 1 && (vap->iv_opmode == IEEE80211_M_IBSS || vap->iv_opmode == IEEE80211_M_STA)) { /* read statistic counters (clear on read) and update AMRR state */ error = run_read_region_1(sc, RT2860_TX_STA_CNT0, (uint8_t *)sta, sizeof sta); if (error != 0) goto fail; /* count failed TX as errors */ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, le16toh(sta[0].error.fail)); txs->nretries = le16toh(sta[1].tx.retry); txs->nsuccess = le16toh(sta[1].tx.success); /* nretries??? */ txs->nframes = txs->nretries + txs->nsuccess + le16toh(sta[0].error.fail); RUN_DPRINTF(sc, RUN_DEBUG_RATE, "retrycnt=%d success=%d failcnt=%d\n", txs->nretries, txs->nsuccess, le16toh(sta[0].error.fail)); } else { wstat = &(sc->wcid_stats[RUN_AID2WCID(ni->ni_associd)]); if (wstat == &(sc->wcid_stats[0]) || wstat > &(sc->wcid_stats[RT2870_WCID_MAX])) goto fail; txs->nretries = (*wstat)[RUN_RETRY]; txs->nsuccess = (*wstat)[RUN_SUCCESS]; txs->nframes = (*wstat)[RUN_TXCNT]; RUN_DPRINTF(sc, RUN_DEBUG_RATE, "retrycnt=%d txcnt=%d success=%d\n", txs->nretries, txs->nframes, txs->nsuccess); memset(wstat, 0, sizeof(*wstat)); } ieee80211_ratectl_tx_update(vap, txs); rn->amrr_ridx = ieee80211_ratectl_rate(ni, NULL, 0); fail: RUN_UNLOCK(sc); RUN_DPRINTF(sc, RUN_DEBUG_RATE, "ridx=%d\n", rn->amrr_ridx); } static void run_newassoc_cb(void *arg) { struct run_cmdq *cmdq = arg; struct ieee80211_node *ni = cmdq->arg1; struct run_softc *sc = ni->ni_vap->iv_ic->ic_softc; uint8_t wcid = cmdq->wcid; RUN_LOCK_ASSERT(sc, MA_OWNED); run_write_region_1(sc, RT2860_WCID_ENTRY(wcid), ni->ni_macaddr, IEEE80211_ADDR_LEN); memset(&(sc->wcid_stats[wcid]), 0, sizeof(sc->wcid_stats[wcid])); } static void run_newassoc(struct ieee80211_node *ni, int isnew) { struct run_node *rn = RUN_NODE(ni); struct ieee80211_rateset *rs = &ni->ni_rates; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; uint8_t rate; uint8_t ridx; uint8_t wcid; int i, j; wcid = (vap->iv_opmode == IEEE80211_M_STA) ? 1 : RUN_AID2WCID(ni->ni_associd); if (wcid > RT2870_WCID_MAX) { device_printf(sc->sc_dev, "wcid=%d out of range\n", wcid); return; } /* only interested in true associations */ if (isnew && ni->ni_associd != 0) { /* * This function could is called though timeout function. * Need to defer. */ uint32_t cnt = RUN_CMDQ_GET(&sc->cmdq_store); RUN_DPRINTF(sc, RUN_DEBUG_STATE, "cmdq_store=%d\n", cnt); sc->cmdq[cnt].func = run_newassoc_cb; sc->cmdq[cnt].arg0 = NULL; sc->cmdq[cnt].arg1 = ni; sc->cmdq[cnt].wcid = wcid; ieee80211_runtask(ic, &sc->cmdq_task); } RUN_DPRINTF(sc, RUN_DEBUG_STATE, "new assoc isnew=%d associd=%x addr=%s\n", isnew, ni->ni_associd, ether_sprintf(ni->ni_macaddr)); for (i = 0; i < rs->rs_nrates; i++) { rate = rs->rs_rates[i] & IEEE80211_RATE_VAL; /* convert 802.11 rate to hardware rate index */ for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == rate) break; rn->ridx[i] = ridx; /* determine rate of control response frames */ for (j = i; j >= 0; j--) { if ((rs->rs_rates[j] & IEEE80211_RATE_BASIC) && rt2860_rates[rn->ridx[i]].phy == rt2860_rates[rn->ridx[j]].phy) break; } if (j >= 0) { rn->ctl_ridx[i] = rn->ridx[j]; } else { /* no basic rate found, use mandatory one */ rn->ctl_ridx[i] = rt2860_rates[ridx].ctl_ridx; } RUN_DPRINTF(sc, RUN_DEBUG_STATE | RUN_DEBUG_RATE, "rate=0x%02x ridx=%d ctl_ridx=%d\n", rs->rs_rates[i], rn->ridx[i], rn->ctl_ridx[i]); } rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate; for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == rate) break; rn->mgt_ridx = ridx; RUN_DPRINTF(sc, RUN_DEBUG_STATE | RUN_DEBUG_RATE, "rate=%d, mgmt_ridx=%d\n", rate, rn->mgt_ridx); RUN_LOCK(sc); if(sc->ratectl_run != RUN_RATECTL_OFF) usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc); RUN_UNLOCK(sc); } /* * Return the Rx chain with the highest RSSI for a given frame. */ static __inline uint8_t run_maxrssi_chain(struct run_softc *sc, const struct rt2860_rxwi *rxwi) { uint8_t rxchain = 0; if (sc->nrxchains > 1) { if (rxwi->rssi[1] > rxwi->rssi[rxchain]) rxchain = 1; if (sc->nrxchains > 2) if (rxwi->rssi[2] > rxwi->rssi[rxchain]) rxchain = 2; } return (rxchain); } static void run_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) { struct ieee80211vap *vap = ni->ni_vap; struct run_softc *sc = vap->iv_ic->ic_softc; struct run_vap *rvp = RUN_VAP(vap); uint64_t ni_tstamp, rx_tstamp; rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); if (vap->iv_state == IEEE80211_S_RUN && (subtype == IEEE80211_FC0_SUBTYPE_BEACON || subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { ni_tstamp = le64toh(ni->ni_tstamp.tsf); RUN_LOCK(sc); run_get_tsf(sc, &rx_tstamp); RUN_UNLOCK(sc); rx_tstamp = le64toh(rx_tstamp); if (ni_tstamp >= rx_tstamp) { RUN_DPRINTF(sc, RUN_DEBUG_RECV | RUN_DEBUG_BEACON, "ibss merge, tsf %ju tstamp %ju\n", (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp); (void) ieee80211_ibss_merge(ni); } } } static void run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct rt2870_rxd *rxd; struct rt2860_rxwi *rxwi; uint32_t flags; uint16_t len, rxwisize; uint8_t ant, rssi; int8_t nf; rxwisize = sizeof(struct rt2860_rxwi); if (sc->mac_ver == 0x5592) rxwisize += sizeof(uint64_t); else if (sc->mac_ver == 0x3593) rxwisize += sizeof(uint32_t); if (__predict_false(dmalen < rxwisize + sizeof(struct ieee80211_frame_ack))) { RUN_DPRINTF(sc, RUN_DEBUG_RECV, "payload is too short: dma length %u < %zu\n", dmalen, rxwisize + sizeof(struct ieee80211_frame_ack)); goto fail; } rxwi = mtod(m, struct rt2860_rxwi *); len = le16toh(rxwi->len) & 0xfff; if (__predict_false(len > dmalen - rxwisize)) { RUN_DPRINTF(sc, RUN_DEBUG_RECV, "bad RXWI length %u > %u\n", len, dmalen); goto fail; } /* Rx descriptor is located at the end */ rxd = (struct rt2870_rxd *)(mtod(m, caddr_t) + dmalen); flags = le32toh(rxd->flags); if (__predict_false(flags & (RT2860_RX_CRCERR | RT2860_RX_ICVERR))) { RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s error.\n", (flags & RT2860_RX_CRCERR)?"CRC":"ICV"); goto fail; } if (flags & RT2860_RX_L2PAD) { - /* - * XXX OpenBSD removes padding between header - * and payload here... - */ RUN_DPRINTF(sc, RUN_DEBUG_RECV, "received RT2860_RX_L2PAD frame\n"); len += 2; } m->m_data += rxwisize; m->m_pkthdr.len = m->m_len = len; wh = mtod(m, struct ieee80211_frame *); - /* XXX wrong for monitor mode */ - if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) != 0 && + (flags & RT2860_RX_DEC) != 0) { wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; m->m_flags |= M_WEP; } if (len >= sizeof(struct ieee80211_frame_min)) { ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); } else ni = NULL; if (__predict_false(flags & RT2860_RX_MICERR)) { /* report MIC failures to net80211 for TKIP */ if (ni != NULL) ieee80211_notify_michael_failure(ni->ni_vap, wh, rxwi->keyidx); RUN_DPRINTF(sc, RUN_DEBUG_RECV, "MIC error. Someone is lying.\n"); goto fail; } ant = run_maxrssi_chain(sc, rxwi); rssi = rxwi->rssi[ant]; nf = run_rssi2dbm(sc, rssi, ant); if (__predict_false(ieee80211_radiotap_active(ic))) { struct run_rx_radiotap_header *tap = &sc->sc_rxtap; uint16_t phy; tap->wr_flags = 0; + if (flags & RT2860_RX_L2PAD) + tap->wr_flags |= IEEE80211_RADIOTAP_F_DATAPAD; tap->wr_antsignal = rssi; tap->wr_antenna = ant; tap->wr_dbm_antsignal = run_rssi2dbm(sc, rssi, ant); tap->wr_rate = 2; /* in case it can't be found below */ RUN_LOCK(sc); run_get_tsf(sc, &tap->wr_tsf); RUN_UNLOCK(sc); phy = le16toh(rxwi->phy); switch (phy & RT2860_PHY_MODE) { case RT2860_PHY_CCK: switch ((phy & RT2860_PHY_MCS) & ~RT2860_PHY_SHPRE) { case 0: tap->wr_rate = 2; break; case 1: tap->wr_rate = 4; break; case 2: tap->wr_rate = 11; break; case 3: tap->wr_rate = 22; break; } if (phy & RT2860_PHY_SHPRE) tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; break; case RT2860_PHY_OFDM: switch (phy & RT2860_PHY_MCS) { case 0: tap->wr_rate = 12; break; case 1: tap->wr_rate = 18; break; case 2: tap->wr_rate = 24; break; case 3: tap->wr_rate = 36; break; case 4: tap->wr_rate = 48; break; case 5: tap->wr_rate = 72; break; case 6: tap->wr_rate = 96; break; case 7: tap->wr_rate = 108; break; } break; } } if (ni != NULL) { (void)ieee80211_input(ni, m, rssi, nf); ieee80211_free_node(ni); } else { (void)ieee80211_input_all(ic, m, rssi, nf); } return; fail: m_freem(m); counter_u64_add(ic->ic_ierrors, 1); } static void run_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct run_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct mbuf *m = NULL; struct mbuf *m0; uint32_t dmalen, mbuf_len; uint16_t rxwisize; int xferlen; rxwisize = sizeof(struct rt2860_rxwi); if (sc->mac_ver == 0x5592) rxwisize += sizeof(uint64_t); else if (sc->mac_ver == 0x3593) rxwisize += sizeof(uint32_t); usbd_xfer_status(xfer, &xferlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: RUN_DPRINTF(sc, RUN_DEBUG_RECV, "rx done, actlen=%d\n", xferlen); if (xferlen < (int)(sizeof(uint32_t) + rxwisize + sizeof(struct rt2870_rxd))) { RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB, "xfer too short %d\n", xferlen); goto tr_setup; } m = sc->rx_m; sc->rx_m = NULL; /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: if (sc->rx_m == NULL) { sc->rx_m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE /* xfer can be bigger than MCLBYTES */); } if (sc->rx_m == NULL) { RUN_DPRINTF(sc, RUN_DEBUG_RECV | RUN_DEBUG_RECV_DESC, "could not allocate mbuf - idle with stall\n"); counter_u64_add(ic->ic_ierrors, 1); usbd_xfer_set_stall(xfer); usbd_xfer_set_frames(xfer, 0); } else { /* * Directly loading a mbuf cluster into DMA to * save some data copying. This works because * there is only one cluster. */ usbd_xfer_set_frame_data(xfer, 0, mtod(sc->rx_m, caddr_t), RUN_MAX_RXSZ); usbd_xfer_set_frames(xfer, 1); } usbd_transfer_submit(xfer); break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); if (error == USB_ERR_TIMEOUT) device_printf(sc->sc_dev, "device timeout\n"); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } if (sc->rx_m != NULL) { m_freem(sc->rx_m); sc->rx_m = NULL; } break; } if (m == NULL) return; /* inputting all the frames must be last */ RUN_UNLOCK(sc); m->m_pkthdr.len = m->m_len = xferlen; /* HW can aggregate multiple 802.11 frames in a single USB xfer */ for(;;) { dmalen = le32toh(*mtod(m, uint32_t *)) & 0xffff; if ((dmalen >= (uint32_t)-8) || (dmalen == 0) || ((dmalen & 3) != 0)) { RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB, "bad DMA length %u\n", dmalen); break; } if ((dmalen + 8) > (uint32_t)xferlen) { RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB, "bad DMA length %u > %d\n", dmalen + 8, xferlen); break; } /* If it is the last one or a single frame, we won't copy. */ if ((xferlen -= dmalen + 8) <= 8) { /* trim 32-bit DMA-len header */ m->m_data += 4; m->m_pkthdr.len = m->m_len -= 4; run_rx_frame(sc, m, dmalen); m = NULL; /* don't free source buffer */ break; } mbuf_len = dmalen + sizeof(struct rt2870_rxd); if (__predict_false(mbuf_len > MCLBYTES)) { RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB, "payload is too big: mbuf_len %u\n", mbuf_len); counter_u64_add(ic->ic_ierrors, 1); break; } /* copy aggregated frames to another mbuf */ m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m0 == NULL)) { RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC, "could not allocate mbuf\n"); counter_u64_add(ic->ic_ierrors, 1); break; } m_copydata(m, 4 /* skip 32-bit DMA-len header */, mbuf_len, mtod(m0, caddr_t)); m0->m_pkthdr.len = m0->m_len = mbuf_len; run_rx_frame(sc, m0, dmalen); /* update data ptr */ m->m_data += mbuf_len + 4; m->m_pkthdr.len = m->m_len -= mbuf_len + 4; } /* make sure we free the source buffer, if any */ m_freem(m); RUN_LOCK(sc); } static void run_tx_free(struct run_endpoint_queue *pq, struct run_tx_data *data, int txerr) { ieee80211_tx_complete(data->ni, data->m, txerr); data->m = NULL; data->ni = NULL; STAILQ_INSERT_TAIL(&pq->tx_fh, data, next); pq->tx_nfree++; } static void run_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index) { struct run_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct run_tx_data *data; struct ieee80211vap *vap = NULL; struct usb_page_cache *pc; struct run_endpoint_queue *pq = &sc->sc_epq[index]; struct mbuf *m; usb_frlength_t size; int actlen; int sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB, "transfer complete: %d bytes @ index %d\n", actlen, index); data = usbd_xfer_get_priv(xfer); run_tx_free(pq, data, 0); usbd_xfer_set_priv(xfer, NULL); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&pq->tx_qh); if (data == NULL) break; STAILQ_REMOVE_HEAD(&pq->tx_qh, next); m = data->m; size = (sc->mac_ver == 0x5592) ? sizeof(data->desc) + sizeof(uint32_t) : sizeof(data->desc); if ((m->m_pkthdr.len + size + 3 + 8) > RUN_MAX_TXSZ) { RUN_DPRINTF(sc, RUN_DEBUG_XMIT_DESC | RUN_DEBUG_USB, "data overflow, %u bytes\n", m->m_pkthdr.len); run_tx_free(pq, data, 1); goto tr_setup; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &data->desc, size); usbd_m_copy_in(pc, size, m, 0, m->m_pkthdr.len); size += m->m_pkthdr.len; /* * Align end on a 4-byte boundary, pad 8 bytes (CRC + * 4-byte padding), and be sure to zero those trailing * bytes: */ usbd_frame_zero(pc, size, ((-size) & 3) + 8); size += ((-size) & 3) + 8; vap = data->ni->ni_vap; if (ieee80211_radiotap_active_vap(vap)) { + const struct ieee80211_frame *wh; struct run_tx_radiotap_header *tap = &sc->sc_txtap; struct rt2860_txwi *txwi = (struct rt2860_txwi *)(&data->desc + sizeof(struct rt2870_txd)); + int has_l2pad; + + wh = mtod(m, struct ieee80211_frame *); + has_l2pad = IEEE80211_HAS_ADDR4(wh) != + IEEE80211_QOS_HAS_SEQ(wh); + tap->wt_flags = 0; tap->wt_rate = rt2860_rates[data->ridx].rate; tap->wt_hwqueue = index; if (le16toh(txwi->phy) & RT2860_PHY_SHPRE) tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + if (has_l2pad) + tap->wt_flags |= IEEE80211_RADIOTAP_F_DATAPAD; ieee80211_radiotap_tx(vap, m); } RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB, "sending frame len=%u/%u @ index %d\n", m->m_pkthdr.len, size, index); usbd_xfer_set_frame_len(xfer, 0, size); usbd_xfer_set_priv(xfer, data); usbd_transfer_submit(xfer); run_start(sc); break; default: RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB, "USB transfer error, %s\n", usbd_errstr(error)); data = usbd_xfer_get_priv(xfer); if (data != NULL) { if(data->ni != NULL) vap = data->ni->ni_vap; run_tx_free(pq, data, error); usbd_xfer_set_priv(xfer, NULL); } if (vap == NULL) vap = TAILQ_FIRST(&ic->ic_vaps); if (error != USB_ERR_CANCELLED) { if (error == USB_ERR_TIMEOUT) { device_printf(sc->sc_dev, "device timeout\n"); uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store); RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB, "cmdq_store=%d\n", i); sc->cmdq[i].func = run_usb_timeout_cb; sc->cmdq[i].arg0 = vap; ieee80211_runtask(ic, &sc->cmdq_task); } /* * Try to clear stall first, also if other * errors occur, hence clearing stall * introduces a 50 ms delay: */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void run_bulk_tx_callback0(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 0); } static void run_bulk_tx_callback1(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 1); } static void run_bulk_tx_callback2(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 2); } static void run_bulk_tx_callback3(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 3); } static void run_bulk_tx_callback4(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 4); } static void run_bulk_tx_callback5(struct usb_xfer *xfer, usb_error_t error) { run_bulk_tx_callbackN(xfer, error, 5); } static void run_set_tx_desc(struct run_softc *sc, struct run_tx_data *data) { struct mbuf *m = data->m; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = data->ni->ni_vap; struct ieee80211_frame *wh; struct rt2870_txd *txd; struct rt2860_txwi *txwi; uint16_t xferlen, txwisize; uint16_t mcs; uint8_t ridx = data->ridx; uint8_t pad; /* get MCS code from rate index */ mcs = rt2860_rates[ridx].mcs; txwisize = (sc->mac_ver == 0x5592) ? sizeof(*txwi) + sizeof(uint32_t) : sizeof(*txwi); xferlen = txwisize + m->m_pkthdr.len; /* roundup to 32-bit alignment */ xferlen = (xferlen + 3) & ~3; txd = (struct rt2870_txd *)&data->desc; txd->len = htole16(xferlen); wh = mtod(m, struct ieee80211_frame *); /* * Ether both are true or both are false, the header * are nicely aligned to 32-bit. So, no L2 padding. */ if(IEEE80211_HAS_ADDR4(wh) == IEEE80211_QOS_HAS_SEQ(wh)) pad = 0; else pad = 2; /* setup TX Wireless Information */ txwi = (struct rt2860_txwi *)(txd + 1); txwi->len = htole16(m->m_pkthdr.len - pad); if (rt2860_rates[ridx].phy == IEEE80211_T_DS) { mcs |= RT2860_PHY_CCK; if (ridx != RT2860_RIDX_CCK1 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) mcs |= RT2860_PHY_SHPRE; } else mcs |= RT2860_PHY_OFDM; txwi->phy = htole16(mcs); /* check if RTS/CTS or CTS-to-self protection is required */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold || ((ic->ic_flags & IEEE80211_F_USEPROT) && rt2860_rates[ridx].phy == IEEE80211_T_OFDM))) txwi->txop |= RT2860_TX_TXOP_HT; else txwi->txop |= RT2860_TX_TXOP_BACKOFF; if (vap->iv_opmode != IEEE80211_M_STA && !IEEE80211_QOS_HAS_SEQ(wh)) txwi->xflags |= RT2860_TX_NSEQ; } /* This function must be called locked */ static int run_tx(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp = ni->ni_txparms; struct run_node *rn = RUN_NODE(ni); struct run_tx_data *data; struct rt2870_txd *txd; struct rt2860_txwi *txwi; uint16_t qos; uint16_t dur; uint16_t qid; uint8_t type; uint8_t tid; uint8_t ridx; uint8_t ctl_ridx; uint8_t qflags; uint8_t xflags = 0; int hasqos; RUN_LOCK_ASSERT(sc, MA_OWNED); wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; /* * There are 7 bulk endpoints: 1 for RX * and 6 for TX (4 EDCAs + HCCA + Prio). * Update 03-14-2009: some devices like the Planex GW-US300MiniS * seem to have only 4 TX bulk endpoints (Fukaumi Naoki). */ if ((hasqos = IEEE80211_QOS_HAS_SEQ(wh))) { uint8_t *frm; frm = ieee80211_getqos(wh); qos = le16toh(*(const uint16_t *)frm); tid = qos & IEEE80211_QOS_TID; qid = TID_TO_WME_AC(tid); } else { qos = 0; tid = 0; qid = WME_AC_BE; } qflags = (qid < 4) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_HCCA; RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "qos %d\tqid %d\ttid %d\tqflags %x\n", qos, qid, tid, qflags); /* pickup a rate index */ if (IEEE80211_IS_MULTICAST(wh->i_addr1) || type != IEEE80211_FC0_TYPE_DATA || m->m_flags & M_EAPOL) { ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; ctl_ridx = rt2860_rates[ridx].ctl_ridx; } else { if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) ridx = rn->fix_ridx; else ridx = rn->amrr_ridx; ctl_ridx = rt2860_rates[ridx].ctl_ridx; } if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && (!hasqos || (qos & IEEE80211_QOS_ACKPOLICY) != IEEE80211_QOS_ACKPOLICY_NOACK)) { xflags |= RT2860_TX_ACK; if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) dur = rt2860_rates[ctl_ridx].sp_ack_dur; else dur = rt2860_rates[ctl_ridx].lp_ack_dur; USETW(wh->i_dur, dur); } /* reserve slots for mgmt packets, just in case */ if (sc->sc_epq[qid].tx_nfree < 3) { RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx ring %d is full\n", qid); return (-1); } data = STAILQ_FIRST(&sc->sc_epq[qid].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[qid].tx_fh, next); sc->sc_epq[qid].tx_nfree--; txd = (struct rt2870_txd *)&data->desc; txd->flags = qflags; txwi = (struct rt2860_txwi *)(txd + 1); txwi->xflags = xflags; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) txwi->wcid = 0; else txwi->wcid = (vap->iv_opmode == IEEE80211_M_STA) ? 1 : RUN_AID2WCID(ni->ni_associd); /* clear leftover garbage bits */ txwi->flags = 0; txwi->txop = 0; data->m = m; data->ni = ni; data->ridx = ridx; run_set_tx_desc(sc, data); /* * The chip keeps track of 2 kind of Tx stats, * * TX_STAT_FIFO, for per WCID stats, and * * TX_STA_CNT0 for all-TX-in-one stats. * * To use FIFO stats, we need to store MCS into the driver-private * PacketID field. So that, we can tell whose stats when we read them. * We add 1 to the MCS because setting the PacketID field to 0 means * that we don't want feedback in TX_STAT_FIFO. * And, that's what we want for STA mode, since TX_STA_CNT0 does the job. * * FIFO stats doesn't count Tx with WCID 0xff, so we do this in run_tx(). */ if (sc->rvp_cnt > 1 || vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_MBSS) { uint16_t pid = (rt2860_rates[ridx].mcs + 1) & 0xf; txwi->len |= htole16(pid << RT2860_TX_PID_SHIFT); /* * Unlike PCI based devices, we don't get any interrupt from * USB devices, so we simulate FIFO-is-full interrupt here. * Ralink recommends to drain FIFO stats every 100 ms, but 16 slots * quickly get fulled. To prevent overflow, increment a counter on * every FIFO stat request, so we know how many slots are left. * We do this only in HOSTAP or multiple vap mode since FIFO stats * are used only in those modes. * We just drain stats. AMRR gets updated every 1 sec by * run_ratectl_cb() via callout. * Call it early. Otherwise overflow. */ if (sc->fifo_cnt++ == 10) { /* * With multiple vaps or if_bridge, if_start() is called * with a non-sleepable lock, tcpinp. So, need to defer. */ uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store); RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "cmdq_store=%d\n", i); sc->cmdq[i].func = run_drain_fifo; sc->cmdq[i].arg0 = sc; ieee80211_runtask(ic, &sc->cmdq_task); } } STAILQ_INSERT_TAIL(&sc->sc_epq[qid].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[qid]); RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending data frame len=%d rate=%d qid=%d\n", m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) + sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate, qid); return (0); } static int run_tx_mgt(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; struct run_node *rn = RUN_NODE(ni); struct run_tx_data *data; struct ieee80211_frame *wh; struct rt2870_txd *txd; struct rt2860_txwi *txwi; uint16_t dur; uint8_t ridx = rn->mgt_ridx; uint8_t xflags = 0; uint8_t wflags = 0; RUN_LOCK_ASSERT(sc, MA_OWNED); wh = mtod(m, struct ieee80211_frame *); /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) wflags |= RT2860_TX_TS; else if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { xflags |= RT2860_TX_ACK; dur = ieee80211_ack_duration(ic->ic_rt, rt2860_rates[ridx].rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); } if (sc->sc_epq[0].tx_nfree == 0) /* let caller free mbuf */ return (EIO); data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); sc->sc_epq[0].tx_nfree--; txd = (struct rt2870_txd *)&data->desc; txd->flags = RT2860_TX_QSEL_EDCA; txwi = (struct rt2860_txwi *)(txd + 1); txwi->wcid = 0xff; txwi->flags = wflags; txwi->xflags = xflags; txwi->txop = 0; /* clear leftover garbage bits */ data->m = m; data->ni = ni; data->ridx = ridx; run_set_tx_desc(sc, data); RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending mgt frame len=%d rate=%d\n", m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) + sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate); STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[0]); return (0); } static int run_sendprot(struct run_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) { struct ieee80211com *ic = ni->ni_ic; struct run_tx_data *data; struct rt2870_txd *txd; struct rt2860_txwi *txwi; struct mbuf *mprot; int ridx; int protrate; uint8_t wflags = 0; uint8_t xflags = 0; RUN_LOCK_ASSERT(sc, MA_OWNED); /* check that there are free slots before allocating the mbuf */ if (sc->sc_epq[0].tx_nfree == 0) /* let caller free mbuf */ return (ENOBUFS); mprot = ieee80211_alloc_prot(ni, m, rate, prot); if (mprot == NULL) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "could not allocate mbuf\n"); return (ENOBUFS); } protrate = ieee80211_ctl_rate(ic->ic_rt, rate); wflags = RT2860_TX_FRAG; xflags = 0; if (prot == IEEE80211_PROT_RTSCTS) xflags |= RT2860_TX_ACK; data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); sc->sc_epq[0].tx_nfree--; txd = (struct rt2870_txd *)&data->desc; txd->flags = RT2860_TX_QSEL_EDCA; txwi = (struct rt2860_txwi *)(txd + 1); txwi->wcid = 0xff; txwi->flags = wflags; txwi->xflags = xflags; txwi->txop = 0; /* clear leftover garbage bits */ data->m = mprot; data->ni = ieee80211_ref_node(ni); for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == protrate) break; data->ridx = ridx; run_set_tx_desc(sc, data); RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending prot len=%u rate=%u\n", m->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[0]); return (0); } static int run_tx_param(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct run_tx_data *data; struct rt2870_txd *txd; struct rt2860_txwi *txwi; uint8_t ridx; uint8_t rate; uint8_t opflags = 0; uint8_t xflags = 0; int error; RUN_LOCK_ASSERT(sc, MA_OWNED); KASSERT(params != NULL, ("no raw xmit params")); rate = params->ibp_rate0; if (!ieee80211_isratevalid(ic->ic_rt, rate)) { /* let caller free mbuf */ return (EINVAL); } if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) xflags |= RT2860_TX_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { error = run_sendprot(sc, m, ni, params->ibp_flags & IEEE80211_BPF_RTS ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); if (error) { /* let caller free mbuf */ return error; } opflags |= /*XXX RT2573_TX_LONG_RETRY |*/ RT2860_TX_TXOP_SIFS; } if (sc->sc_epq[0].tx_nfree == 0) { /* let caller free mbuf */ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending raw frame, but tx ring is full\n"); return (EIO); } data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); sc->sc_epq[0].tx_nfree--; txd = (struct rt2870_txd *)&data->desc; txd->flags = RT2860_TX_QSEL_EDCA; txwi = (struct rt2860_txwi *)(txd + 1); txwi->wcid = 0xff; txwi->xflags = xflags; txwi->txop = opflags; txwi->flags = 0; /* clear leftover garbage bits */ data->m = m; data->ni = ni; for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == rate) break; data->ridx = ridx; run_set_tx_desc(sc, data); RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending raw frame len=%u rate=%u\n", m->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[0]); return (0); } static int run_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct run_softc *sc = ni->ni_ic->ic_softc; int error = 0; RUN_LOCK(sc); /* prevent management frames from being sent if we're not ready */ if (!(sc->sc_flags & RUN_RUNNING)) { error = ENETDOWN; goto done; } if (params == NULL) { /* tx mgt packet */ if ((error = run_tx_mgt(sc, m, ni)) != 0) { RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "mgt tx failed\n"); goto done; } } else { /* tx raw packet with param */ if ((error = run_tx_param(sc, m, ni, params)) != 0) { RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx with param failed\n"); goto done; } } done: RUN_UNLOCK(sc); if (error != 0) { if(m != NULL) m_freem(m); } return (error); } static int run_transmit(struct ieee80211com *ic, struct mbuf *m) { struct run_softc *sc = ic->ic_softc; int error; RUN_LOCK(sc); if ((sc->sc_flags & RUN_RUNNING) == 0) { RUN_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { RUN_UNLOCK(sc); return (error); } run_start(sc); RUN_UNLOCK(sc); return (0); } static void run_start(struct run_softc *sc) { struct ieee80211_node *ni; struct mbuf *m; RUN_LOCK_ASSERT(sc, MA_OWNED); if ((sc->sc_flags & RUN_RUNNING) == 0) return; while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; if (run_tx(sc, m, ni) != 0) { mbufq_prepend(&sc->sc_snd, m); break; } } } static void run_parent(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; int startall = 0; RUN_LOCK(sc); if (sc->sc_detached) { RUN_UNLOCK(sc); return; } if (ic->ic_nrunning > 0) { if (!(sc->sc_flags & RUN_RUNNING)) { startall = 1; run_init_locked(sc); } else run_update_promisc_locked(sc); } else if ((sc->sc_flags & RUN_RUNNING) && sc->rvp_cnt <= 1) run_stop(sc); RUN_UNLOCK(sc); if (startall) ieee80211_start_all(ic); } static void run_iq_calib(struct run_softc *sc, u_int chan) { uint16_t val; /* Tx0 IQ gain. */ run_bbp_write(sc, 158, 0x2c); if (chan <= 14) run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_2GHZ, &val, 1); else if (chan <= 64) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5GHZ, &val, 1); } else if (chan <= 138) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5GHZ, &val, 1); } else if (chan <= 165) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5GHZ, &val, 1); } else val = 0; run_bbp_write(sc, 159, val); /* Tx0 IQ phase. */ run_bbp_write(sc, 158, 0x2d); if (chan <= 14) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_2GHZ, &val, 1); } else if (chan <= 64) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5GHZ, &val, 1); } else if (chan <= 138) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5GHZ, &val, 1); } else if (chan <= 165) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5GHZ, &val, 1); } else val = 0; run_bbp_write(sc, 159, val); /* Tx1 IQ gain. */ run_bbp_write(sc, 158, 0x4a); if (chan <= 14) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_2GHZ, &val, 1); } else if (chan <= 64) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5GHZ, &val, 1); } else if (chan <= 138) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5GHZ, &val, 1); } else if (chan <= 165) { run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5GHZ, &val, 1); } else val = 0; run_bbp_write(sc, 159, val); /* Tx1 IQ phase. */ run_bbp_write(sc, 158, 0x4b); if (chan <= 14) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_2GHZ, &val, 1); } else if (chan <= 64) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5GHZ, &val, 1); } else if (chan <= 138) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5GHZ, &val, 1); } else if (chan <= 165) { run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5GHZ, &val, 1); } else val = 0; run_bbp_write(sc, 159, val); /* RF IQ compensation control. */ run_bbp_write(sc, 158, 0x04); run_efuse_read(sc, RT5390_EEPROM_RF_IQ_COMPENSATION_CTL, &val, 1); run_bbp_write(sc, 159, val); /* RF IQ imbalance compensation control. */ run_bbp_write(sc, 158, 0x03); run_efuse_read(sc, RT5390_EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CTL, &val, 1); run_bbp_write(sc, 159, val); } static void run_set_agc(struct run_softc *sc, uint8_t agc) { uint8_t bbp; if (sc->mac_ver == 0x3572) { run_bbp_read(sc, 27, &bbp); bbp &= ~(0x3 << 5); run_bbp_write(sc, 27, bbp | 0 << 5); /* select Rx0 */ run_bbp_write(sc, 66, agc); run_bbp_write(sc, 27, bbp | 1 << 5); /* select Rx1 */ run_bbp_write(sc, 66, agc); } else run_bbp_write(sc, 66, agc); } static void run_select_chan_group(struct run_softc *sc, int group) { uint32_t tmp; uint8_t agc; run_bbp_write(sc, 62, 0x37 - sc->lna[group]); run_bbp_write(sc, 63, 0x37 - sc->lna[group]); run_bbp_write(sc, 64, 0x37 - sc->lna[group]); if (sc->mac_ver < 0x3572) run_bbp_write(sc, 86, 0x00); if (sc->mac_ver == 0x3593) { run_bbp_write(sc, 77, 0x98); run_bbp_write(sc, 83, (group == 0) ? 0x8a : 0x9a); } if (group == 0) { if (sc->ext_2ghz_lna) { if (sc->mac_ver >= 0x5390) run_bbp_write(sc, 75, 0x52); else { run_bbp_write(sc, 82, 0x62); run_bbp_write(sc, 75, 0x46); } } else { if (sc->mac_ver == 0x5592) { run_bbp_write(sc, 79, 0x1c); run_bbp_write(sc, 80, 0x0e); run_bbp_write(sc, 81, 0x3a); run_bbp_write(sc, 82, 0x62); run_bbp_write(sc, 195, 0x80); run_bbp_write(sc, 196, 0xe0); run_bbp_write(sc, 195, 0x81); run_bbp_write(sc, 196, 0x1f); run_bbp_write(sc, 195, 0x82); run_bbp_write(sc, 196, 0x38); run_bbp_write(sc, 195, 0x83); run_bbp_write(sc, 196, 0x32); run_bbp_write(sc, 195, 0x85); run_bbp_write(sc, 196, 0x28); run_bbp_write(sc, 195, 0x86); run_bbp_write(sc, 196, 0x19); } else if (sc->mac_ver >= 0x5390) run_bbp_write(sc, 75, 0x50); else { run_bbp_write(sc, 82, (sc->mac_ver == 0x3593) ? 0x62 : 0x84); run_bbp_write(sc, 75, 0x50); } } } else { if (sc->mac_ver == 0x5592) { run_bbp_write(sc, 79, 0x18); run_bbp_write(sc, 80, 0x08); run_bbp_write(sc, 81, 0x38); run_bbp_write(sc, 82, 0x92); run_bbp_write(sc, 195, 0x80); run_bbp_write(sc, 196, 0xf0); run_bbp_write(sc, 195, 0x81); run_bbp_write(sc, 196, 0x1e); run_bbp_write(sc, 195, 0x82); run_bbp_write(sc, 196, 0x28); run_bbp_write(sc, 195, 0x83); run_bbp_write(sc, 196, 0x20); run_bbp_write(sc, 195, 0x85); run_bbp_write(sc, 196, 0x7f); run_bbp_write(sc, 195, 0x86); run_bbp_write(sc, 196, 0x7f); } else if (sc->mac_ver == 0x3572) run_bbp_write(sc, 82, 0x94); else run_bbp_write(sc, 82, (sc->mac_ver == 0x3593) ? 0x82 : 0xf2); if (sc->ext_5ghz_lna) run_bbp_write(sc, 75, 0x46); else run_bbp_write(sc, 75, 0x50); } run_read(sc, RT2860_TX_BAND_CFG, &tmp); tmp &= ~(RT2860_5G_BAND_SEL_N | RT2860_5G_BAND_SEL_P); tmp |= (group == 0) ? RT2860_5G_BAND_SEL_N : RT2860_5G_BAND_SEL_P; run_write(sc, RT2860_TX_BAND_CFG, tmp); /* enable appropriate Power Amplifiers and Low Noise Amplifiers */ tmp = RT2860_RFTR_EN | RT2860_TRSW_EN | RT2860_LNA_PE0_EN; if (sc->mac_ver == 0x3593) tmp |= 1 << 29 | 1 << 28; if (sc->nrxchains > 1) tmp |= RT2860_LNA_PE1_EN; if (group == 0) { /* 2GHz */ tmp |= RT2860_PA_PE_G0_EN; if (sc->ntxchains > 1) tmp |= RT2860_PA_PE_G1_EN; if (sc->mac_ver == 0x3593) { if (sc->ntxchains > 2) tmp |= 1 << 25; } } else { /* 5GHz */ tmp |= RT2860_PA_PE_A0_EN; if (sc->ntxchains > 1) tmp |= RT2860_PA_PE_A1_EN; } if (sc->mac_ver == 0x3572) { run_rt3070_rf_write(sc, 8, 0x00); run_write(sc, RT2860_TX_PIN_CFG, tmp); run_rt3070_rf_write(sc, 8, 0x80); } else run_write(sc, RT2860_TX_PIN_CFG, tmp); if (sc->mac_ver == 0x5592) { run_bbp_write(sc, 195, 0x8d); run_bbp_write(sc, 196, 0x1a); } if (sc->mac_ver == 0x3593) { run_read(sc, RT2860_GPIO_CTRL, &tmp); tmp &= ~0x01010000; if (group == 0) tmp |= 0x00010000; tmp = (tmp & ~0x00009090) | 0x00000090; run_write(sc, RT2860_GPIO_CTRL, tmp); } /* set initial AGC value */ if (group == 0) { /* 2GHz band */ if (sc->mac_ver >= 0x3070) agc = 0x1c + sc->lna[0] * 2; else agc = 0x2e + sc->lna[0]; } else { /* 5GHz band */ if (sc->mac_ver == 0x5592) agc = 0x24 + sc->lna[group] * 2; else if (sc->mac_ver == 0x3572 || sc->mac_ver == 0x3593) agc = 0x22 + (sc->lna[group] * 5) / 3; else agc = 0x32 + (sc->lna[group] * 5) / 3; } run_set_agc(sc, agc); } static void run_rt2870_set_chan(struct run_softc *sc, u_int chan) { const struct rfprog *rfprog = rt2860_rf2850; uint32_t r2, r3, r4; int8_t txpow1, txpow2; int i; /* find the settings for this channel (we know it exists) */ for (i = 0; rfprog[i].chan != chan; i++); r2 = rfprog[i].r2; if (sc->ntxchains == 1) r2 |= 1 << 14; /* 1T: disable Tx chain 2 */ if (sc->nrxchains == 1) r2 |= 1 << 17 | 1 << 6; /* 1R: disable Rx chains 2 & 3 */ else if (sc->nrxchains == 2) r2 |= 1 << 6; /* 2R: disable Rx chain 3 */ /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; /* Initialize RF R3 and R4. */ r3 = rfprog[i].r3 & 0xffffc1ff; r4 = (rfprog[i].r4 & ~(0x001f87c0)) | (sc->freq << 15); if (chan > 14) { if (txpow1 >= 0) { txpow1 = (txpow1 > 0xf) ? (0xf) : (txpow1); r3 |= (txpow1 << 10) | (1 << 9); } else { txpow1 += 7; /* txpow1 is not possible larger than 15. */ r3 |= (txpow1 << 10); } if (txpow2 >= 0) { txpow2 = (txpow2 > 0xf) ? (0xf) : (txpow2); r4 |= (txpow2 << 7) | (1 << 6); } else { txpow2 += 7; r4 |= (txpow2 << 7); } } else { /* Set Tx0 power. */ r3 |= (txpow1 << 9); /* Set frequency offset and Tx1 power. */ r4 |= (txpow2 << 6); } run_rt2870_rf_write(sc, rfprog[i].r1); run_rt2870_rf_write(sc, r2); run_rt2870_rf_write(sc, r3 & ~(1 << 2)); run_rt2870_rf_write(sc, r4); run_delay(sc, 10); run_rt2870_rf_write(sc, rfprog[i].r1); run_rt2870_rf_write(sc, r2); run_rt2870_rf_write(sc, r3 | (1 << 2)); run_rt2870_rf_write(sc, r4); run_delay(sc, 10); run_rt2870_rf_write(sc, rfprog[i].r1); run_rt2870_rf_write(sc, r2); run_rt2870_rf_write(sc, r3 & ~(1 << 2)); run_rt2870_rf_write(sc, r4); } static void run_rt3070_set_chan(struct run_softc *sc, u_int chan) { int8_t txpow1, txpow2; uint8_t rf; int i; /* find the settings for this channel (we know it exists) */ for (i = 0; rt2860_rf2850[i].chan != chan; i++); /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n); /* RT3370/RT3390: RF R3 [7:4] is not reserved bits. */ run_rt3070_rf_read(sc, 3, &rf); rf = (rf & ~0x0f) | rt3070_freqs[i].k; run_rt3070_rf_write(sc, 3, rf); run_rt3070_rf_read(sc, 6, &rf); rf = (rf & ~0x03) | rt3070_freqs[i].r; run_rt3070_rf_write(sc, 6, rf); /* set Tx0 power */ run_rt3070_rf_read(sc, 12, &rf); rf = (rf & ~0x1f) | txpow1; run_rt3070_rf_write(sc, 12, rf); /* set Tx1 power */ run_rt3070_rf_read(sc, 13, &rf); rf = (rf & ~0x1f) | txpow2; run_rt3070_rf_write(sc, 13, rf); run_rt3070_rf_read(sc, 1, &rf); rf &= ~0xfc; if (sc->ntxchains == 1) rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */ else if (sc->ntxchains == 2) rf |= 1 << 7; /* 2T: disable Tx chain 3 */ if (sc->nrxchains == 1) rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ else if (sc->nrxchains == 2) rf |= 1 << 6; /* 2R: disable Rx chain 3 */ run_rt3070_rf_write(sc, 1, rf); /* set RF offset */ run_rt3070_rf_read(sc, 23, &rf); rf = (rf & ~0x7f) | sc->freq; run_rt3070_rf_write(sc, 23, rf); /* program RF filter */ run_rt3070_rf_read(sc, 24, &rf); /* Tx */ rf = (rf & ~0x3f) | sc->rf24_20mhz; run_rt3070_rf_write(sc, 24, rf); run_rt3070_rf_read(sc, 31, &rf); /* Rx */ rf = (rf & ~0x3f) | sc->rf24_20mhz; run_rt3070_rf_write(sc, 31, rf); /* enable RF tuning */ run_rt3070_rf_read(sc, 7, &rf); run_rt3070_rf_write(sc, 7, rf | 0x01); } static void run_rt3572_set_chan(struct run_softc *sc, u_int chan) { int8_t txpow1, txpow2; uint32_t tmp; uint8_t rf; int i; /* find the settings for this channel (we know it exists) */ for (i = 0; rt2860_rf2850[i].chan != chan; i++); /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; if (chan <= 14) { run_bbp_write(sc, 25, sc->bbp25); run_bbp_write(sc, 26, sc->bbp26); } else { /* enable IQ phase correction */ run_bbp_write(sc, 25, 0x09); run_bbp_write(sc, 26, 0xff); } run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n); run_rt3070_rf_write(sc, 3, rt3070_freqs[i].k); run_rt3070_rf_read(sc, 6, &rf); rf = (rf & ~0x0f) | rt3070_freqs[i].r; rf |= (chan <= 14) ? 0x08 : 0x04; run_rt3070_rf_write(sc, 6, rf); /* set PLL mode */ run_rt3070_rf_read(sc, 5, &rf); rf &= ~(0x08 | 0x04); rf |= (chan <= 14) ? 0x04 : 0x08; run_rt3070_rf_write(sc, 5, rf); /* set Tx power for chain 0 */ if (chan <= 14) rf = 0x60 | txpow1; else rf = 0xe0 | (txpow1 & 0xc) << 1 | (txpow1 & 0x3); run_rt3070_rf_write(sc, 12, rf); /* set Tx power for chain 1 */ if (chan <= 14) rf = 0x60 | txpow2; else rf = 0xe0 | (txpow2 & 0xc) << 1 | (txpow2 & 0x3); run_rt3070_rf_write(sc, 13, rf); /* set Tx/Rx streams */ run_rt3070_rf_read(sc, 1, &rf); rf &= ~0xfc; if (sc->ntxchains == 1) rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */ else if (sc->ntxchains == 2) rf |= 1 << 7; /* 2T: disable Tx chain 3 */ if (sc->nrxchains == 1) rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ else if (sc->nrxchains == 2) rf |= 1 << 6; /* 2R: disable Rx chain 3 */ run_rt3070_rf_write(sc, 1, rf); /* set RF offset */ run_rt3070_rf_read(sc, 23, &rf); rf = (rf & ~0x7f) | sc->freq; run_rt3070_rf_write(sc, 23, rf); /* program RF filter */ rf = sc->rf24_20mhz; run_rt3070_rf_write(sc, 24, rf); /* Tx */ run_rt3070_rf_write(sc, 31, rf); /* Rx */ /* enable RF tuning */ run_rt3070_rf_read(sc, 7, &rf); rf = (chan <= 14) ? 0xd8 : ((rf & ~0xc8) | 0x14); run_rt3070_rf_write(sc, 7, rf); /* TSSI */ rf = (chan <= 14) ? 0xc3 : 0xc0; run_rt3070_rf_write(sc, 9, rf); /* set loop filter 1 */ run_rt3070_rf_write(sc, 10, 0xf1); /* set loop filter 2 */ run_rt3070_rf_write(sc, 11, (chan <= 14) ? 0xb9 : 0x00); /* set tx_mx2_ic */ run_rt3070_rf_write(sc, 15, (chan <= 14) ? 0x53 : 0x43); /* set tx_mx1_ic */ if (chan <= 14) rf = 0x48 | sc->txmixgain_2ghz; else rf = 0x78 | sc->txmixgain_5ghz; run_rt3070_rf_write(sc, 16, rf); /* set tx_lo1 */ run_rt3070_rf_write(sc, 17, 0x23); /* set tx_lo2 */ if (chan <= 14) rf = 0x93; else if (chan <= 64) rf = 0xb7; else if (chan <= 128) rf = 0x74; else rf = 0x72; run_rt3070_rf_write(sc, 19, rf); /* set rx_lo1 */ if (chan <= 14) rf = 0xb3; else if (chan <= 64) rf = 0xf6; else if (chan <= 128) rf = 0xf4; else rf = 0xf3; run_rt3070_rf_write(sc, 20, rf); /* set pfd_delay */ if (chan <= 14) rf = 0x15; else if (chan <= 64) rf = 0x3d; else rf = 0x01; run_rt3070_rf_write(sc, 25, rf); /* set rx_lo2 */ run_rt3070_rf_write(sc, 26, (chan <= 14) ? 0x85 : 0x87); /* set ldo_rf_vc */ run_rt3070_rf_write(sc, 27, (chan <= 14) ? 0x00 : 0x01); /* set drv_cc */ run_rt3070_rf_write(sc, 29, (chan <= 14) ? 0x9b : 0x9f); run_read(sc, RT2860_GPIO_CTRL, &tmp); tmp &= ~0x8080; if (chan <= 14) tmp |= 0x80; run_write(sc, RT2860_GPIO_CTRL, tmp); /* enable RF tuning */ run_rt3070_rf_read(sc, 7, &rf); run_rt3070_rf_write(sc, 7, rf | 0x01); run_delay(sc, 2); } static void run_rt3593_set_chan(struct run_softc *sc, u_int chan) { int8_t txpow1, txpow2, txpow3; uint8_t h20mhz, rf; int i; /* find the settings for this channel (we know it exists) */ for (i = 0; rt2860_rf2850[i].chan != chan; i++); /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; txpow3 = (sc->ntxchains == 3) ? sc->txpow3[i] : 0; if (chan <= 14) { run_bbp_write(sc, 25, sc->bbp25); run_bbp_write(sc, 26, sc->bbp26); } else { /* Enable IQ phase correction. */ run_bbp_write(sc, 25, 0x09); run_bbp_write(sc, 26, 0xff); } run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n); run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f); run_rt3070_rf_read(sc, 11, &rf); rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03); run_rt3070_rf_write(sc, 11, rf); /* Set pll_idoh. */ run_rt3070_rf_read(sc, 11, &rf); rf &= ~0x4c; rf |= (chan <= 14) ? 0x44 : 0x48; run_rt3070_rf_write(sc, 11, rf); if (chan <= 14) rf = txpow1 & 0x1f; else rf = 0x40 | ((txpow1 & 0x18) << 1) | (txpow1 & 0x07); run_rt3070_rf_write(sc, 53, rf); if (chan <= 14) rf = txpow2 & 0x1f; else rf = 0x40 | ((txpow2 & 0x18) << 1) | (txpow2 & 0x07); run_rt3070_rf_write(sc, 55, rf); if (chan <= 14) rf = txpow3 & 0x1f; else rf = 0x40 | ((txpow3 & 0x18) << 1) | (txpow3 & 0x07); run_rt3070_rf_write(sc, 54, rf); rf = RT3070_RF_BLOCK | RT3070_PLL_PD; if (sc->ntxchains == 3) rf |= RT3070_TX0_PD | RT3070_TX1_PD | RT3070_TX2_PD; else rf |= RT3070_TX0_PD | RT3070_TX1_PD; rf |= RT3070_RX0_PD | RT3070_RX1_PD | RT3070_RX2_PD; run_rt3070_rf_write(sc, 1, rf); run_adjust_freq_offset(sc); run_rt3070_rf_write(sc, 31, (chan <= 14) ? 0xa0 : 0x80); h20mhz = (sc->rf24_20mhz & 0x20) >> 5; run_rt3070_rf_read(sc, 30, &rf); rf = (rf & ~0x06) | (h20mhz << 1) | (h20mhz << 2); run_rt3070_rf_write(sc, 30, rf); run_rt3070_rf_read(sc, 36, &rf); if (chan <= 14) rf |= 0x80; else rf &= ~0x80; run_rt3070_rf_write(sc, 36, rf); /* Set vcolo_bs. */ run_rt3070_rf_write(sc, 34, (chan <= 14) ? 0x3c : 0x20); /* Set pfd_delay. */ run_rt3070_rf_write(sc, 12, (chan <= 14) ? 0x1a : 0x12); /* Set vco bias current control. */ run_rt3070_rf_read(sc, 6, &rf); rf &= ~0xc0; if (chan <= 14) rf |= 0x40; else if (chan <= 128) rf |= 0x80; else rf |= 0x40; run_rt3070_rf_write(sc, 6, rf); run_rt3070_rf_read(sc, 30, &rf); rf = (rf & ~0x18) | 0x10; run_rt3070_rf_write(sc, 30, rf); run_rt3070_rf_write(sc, 10, (chan <= 14) ? 0xd3 : 0xd8); run_rt3070_rf_write(sc, 13, (chan <= 14) ? 0x12 : 0x23); run_rt3070_rf_read(sc, 51, &rf); rf = (rf & ~0x03) | 0x01; run_rt3070_rf_write(sc, 51, rf); /* Set tx_mx1_cc. */ run_rt3070_rf_read(sc, 51, &rf); rf &= ~0x1c; rf |= (chan <= 14) ? 0x14 : 0x10; run_rt3070_rf_write(sc, 51, rf); /* Set tx_mx1_ic. */ run_rt3070_rf_read(sc, 51, &rf); rf &= ~0xe0; rf |= (chan <= 14) ? 0x60 : 0x40; run_rt3070_rf_write(sc, 51, rf); /* Set tx_lo1_ic. */ run_rt3070_rf_read(sc, 49, &rf); rf &= ~0x1c; rf |= (chan <= 14) ? 0x0c : 0x08; run_rt3070_rf_write(sc, 49, rf); /* Set tx_lo1_en. */ run_rt3070_rf_read(sc, 50, &rf); run_rt3070_rf_write(sc, 50, rf & ~0x20); /* Set drv_cc. */ run_rt3070_rf_read(sc, 57, &rf); rf &= ~0xfc; rf |= (chan <= 14) ? 0x6c : 0x3c; run_rt3070_rf_write(sc, 57, rf); /* Set rx_mix1_ic, rxa_lnactr, lna_vc, lna_inbias_en and lna_en. */ run_rt3070_rf_write(sc, 44, (chan <= 14) ? 0x93 : 0x9b); /* Set drv_gnd_a, tx_vga_cc_a and tx_mx2_gain. */ run_rt3070_rf_write(sc, 52, (chan <= 14) ? 0x45 : 0x05); /* Enable VCO calibration. */ run_rt3070_rf_read(sc, 3, &rf); rf &= ~RT5390_VCOCAL; rf |= (chan <= 14) ? RT5390_VCOCAL : 0xbe; run_rt3070_rf_write(sc, 3, rf); if (chan <= 14) rf = 0x23; else if (chan <= 64) rf = 0x36; else if (chan <= 128) rf = 0x32; else rf = 0x30; run_rt3070_rf_write(sc, 39, rf); if (chan <= 14) rf = 0xbb; else if (chan <= 64) rf = 0xeb; else if (chan <= 128) rf = 0xb3; else rf = 0x9b; run_rt3070_rf_write(sc, 45, rf); /* Set FEQ/AEQ control. */ run_bbp_write(sc, 105, 0x34); } static void run_rt5390_set_chan(struct run_softc *sc, u_int chan) { int8_t txpow1, txpow2; uint8_t rf; int i; /* find the settings for this channel (we know it exists) */ for (i = 0; rt2860_rf2850[i].chan != chan; i++); /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n); run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f); run_rt3070_rf_read(sc, 11, &rf); rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03); run_rt3070_rf_write(sc, 11, rf); run_rt3070_rf_read(sc, 49, &rf); rf = (rf & ~0x3f) | (txpow1 & 0x3f); /* The valid range of the RF R49 is 0x00 to 0x27. */ if ((rf & 0x3f) > 0x27) rf = (rf & ~0x3f) | 0x27; run_rt3070_rf_write(sc, 49, rf); if (sc->mac_ver == 0x5392) { run_rt3070_rf_read(sc, 50, &rf); rf = (rf & ~0x3f) | (txpow2 & 0x3f); /* The valid range of the RF R50 is 0x00 to 0x27. */ if ((rf & 0x3f) > 0x27) rf = (rf & ~0x3f) | 0x27; run_rt3070_rf_write(sc, 50, rf); } run_rt3070_rf_read(sc, 1, &rf); rf |= RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD; if (sc->mac_ver == 0x5392) rf |= RT3070_RX1_PD | RT3070_TX1_PD; run_rt3070_rf_write(sc, 1, rf); if (sc->mac_ver != 0x5392) { run_rt3070_rf_read(sc, 2, &rf); rf |= 0x80; run_rt3070_rf_write(sc, 2, rf); run_delay(sc, 10); rf &= 0x7f; run_rt3070_rf_write(sc, 2, rf); } run_adjust_freq_offset(sc); if (sc->mac_ver == 0x5392) { /* Fix for RT5392C. */ if (sc->mac_rev >= 0x0223) { if (chan <= 4) rf = 0x0f; else if (chan >= 5 && chan <= 7) rf = 0x0e; else rf = 0x0d; run_rt3070_rf_write(sc, 23, rf); if (chan <= 4) rf = 0x0c; else if (chan == 5) rf = 0x0b; else if (chan >= 6 && chan <= 7) rf = 0x0a; else if (chan >= 8 && chan <= 10) rf = 0x09; else rf = 0x08; run_rt3070_rf_write(sc, 59, rf); } else { if (chan <= 11) rf = 0x0f; else rf = 0x0b; run_rt3070_rf_write(sc, 59, rf); } } else { /* Fix for RT5390F. */ if (sc->mac_rev >= 0x0502) { if (chan <= 11) rf = 0x43; else rf = 0x23; run_rt3070_rf_write(sc, 55, rf); if (chan <= 11) rf = 0x0f; else if (chan == 12) rf = 0x0d; else rf = 0x0b; run_rt3070_rf_write(sc, 59, rf); } else { run_rt3070_rf_write(sc, 55, 0x44); run_rt3070_rf_write(sc, 59, 0x8f); } } /* Enable VCO calibration. */ run_rt3070_rf_read(sc, 3, &rf); rf |= RT5390_VCOCAL; run_rt3070_rf_write(sc, 3, rf); } static void run_rt5592_set_chan(struct run_softc *sc, u_int chan) { const struct rt5592_freqs *freqs; uint32_t tmp; uint8_t reg, rf, txpow_bound; int8_t txpow1, txpow2; int i; run_read(sc, RT5592_DEBUG_INDEX, &tmp); freqs = (tmp & RT5592_SEL_XTAL) ? rt5592_freqs_40mhz : rt5592_freqs_20mhz; /* find the settings for this channel (we know it exists) */ for (i = 0; rt2860_rf2850[i].chan != chan; i++, freqs++); /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; txpow2 = sc->txpow2[i]; run_read(sc, RT3070_LDO_CFG0, &tmp); tmp &= ~0x1c000000; if (chan > 14) tmp |= 0x14000000; run_write(sc, RT3070_LDO_CFG0, tmp); /* N setting. */ run_rt3070_rf_write(sc, 8, freqs->n & 0xff); run_rt3070_rf_read(sc, 9, &rf); rf &= ~(1 << 4); rf |= ((freqs->n & 0x0100) >> 8) << 4; run_rt3070_rf_write(sc, 9, rf); /* K setting. */ run_rt3070_rf_read(sc, 9, &rf); rf &= ~0x0f; rf |= (freqs->k & 0x0f); run_rt3070_rf_write(sc, 9, rf); /* Mode setting. */ run_rt3070_rf_read(sc, 11, &rf); rf &= ~0x0c; rf |= ((freqs->m - 0x8) & 0x3) << 2; run_rt3070_rf_write(sc, 11, rf); run_rt3070_rf_read(sc, 9, &rf); rf &= ~(1 << 7); rf |= (((freqs->m - 0x8) & 0x4) >> 2) << 7; run_rt3070_rf_write(sc, 9, rf); /* R setting. */ run_rt3070_rf_read(sc, 11, &rf); rf &= ~0x03; rf |= (freqs->r - 0x1); run_rt3070_rf_write(sc, 11, rf); if (chan <= 14) { /* Initialize RF registers for 2GHZ. */ for (i = 0; i < nitems(rt5592_2ghz_def_rf); i++) { run_rt3070_rf_write(sc, rt5592_2ghz_def_rf[i].reg, rt5592_2ghz_def_rf[i].val); } rf = (chan <= 10) ? 0x07 : 0x06; run_rt3070_rf_write(sc, 23, rf); run_rt3070_rf_write(sc, 59, rf); run_rt3070_rf_write(sc, 55, 0x43); /* * RF R49/R50 Tx power ALC code. * G-band bit<7:6>=1:0, bit<5:0> range from 0x0 ~ 0x27. */ reg = 2; txpow_bound = 0x27; } else { /* Initialize RF registers for 5GHZ. */ for (i = 0; i < nitems(rt5592_5ghz_def_rf); i++) { run_rt3070_rf_write(sc, rt5592_5ghz_def_rf[i].reg, rt5592_5ghz_def_rf[i].val); } for (i = 0; i < nitems(rt5592_chan_5ghz); i++) { if (chan >= rt5592_chan_5ghz[i].firstchan && chan <= rt5592_chan_5ghz[i].lastchan) { run_rt3070_rf_write(sc, rt5592_chan_5ghz[i].reg, rt5592_chan_5ghz[i].val); } } /* * RF R49/R50 Tx power ALC code. * A-band bit<7:6>=1:1, bit<5:0> range from 0x0 ~ 0x2b. */ reg = 3; txpow_bound = 0x2b; } /* RF R49 ch0 Tx power ALC code. */ run_rt3070_rf_read(sc, 49, &rf); rf &= ~0xc0; rf |= (reg << 6); rf = (rf & ~0x3f) | (txpow1 & 0x3f); if ((rf & 0x3f) > txpow_bound) rf = (rf & ~0x3f) | txpow_bound; run_rt3070_rf_write(sc, 49, rf); /* RF R50 ch1 Tx power ALC code. */ run_rt3070_rf_read(sc, 50, &rf); rf &= ~(1 << 7 | 1 << 6); rf |= (reg << 6); rf = (rf & ~0x3f) | (txpow2 & 0x3f); if ((rf & 0x3f) > txpow_bound) rf = (rf & ~0x3f) | txpow_bound; run_rt3070_rf_write(sc, 50, rf); /* Enable RF_BLOCK, PLL_PD, RX0_PD, and TX0_PD. */ run_rt3070_rf_read(sc, 1, &rf); rf |= (RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD); if (sc->ntxchains > 1) rf |= RT3070_TX1_PD; if (sc->nrxchains > 1) rf |= RT3070_RX1_PD; run_rt3070_rf_write(sc, 1, rf); run_rt3070_rf_write(sc, 6, 0xe4); run_rt3070_rf_write(sc, 30, 0x10); run_rt3070_rf_write(sc, 31, 0x80); run_rt3070_rf_write(sc, 32, 0x80); run_adjust_freq_offset(sc); /* Enable VCO calibration. */ run_rt3070_rf_read(sc, 3, &rf); rf |= RT5390_VCOCAL; run_rt3070_rf_write(sc, 3, rf); } static void run_set_rx_antenna(struct run_softc *sc, int aux) { uint32_t tmp; uint8_t bbp152; if (aux) { if (sc->rf_rev == RT5390_RF_5370) { run_bbp_read(sc, 152, &bbp152); run_bbp_write(sc, 152, bbp152 & ~0x80); } else { run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 0); run_read(sc, RT2860_GPIO_CTRL, &tmp); run_write(sc, RT2860_GPIO_CTRL, (tmp & ~0x0808) | 0x08); } } else { if (sc->rf_rev == RT5390_RF_5370) { run_bbp_read(sc, 152, &bbp152); run_bbp_write(sc, 152, bbp152 | 0x80); } else { run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 1); run_read(sc, RT2860_GPIO_CTRL, &tmp); run_write(sc, RT2860_GPIO_CTRL, tmp & ~0x0808); } } } static int run_set_chan(struct run_softc *sc, struct ieee80211_channel *c) { struct ieee80211com *ic = &sc->sc_ic; u_int chan, group; chan = ieee80211_chan2ieee(ic, c); if (chan == 0 || chan == IEEE80211_CHAN_ANY) return (EINVAL); if (sc->mac_ver == 0x5592) run_rt5592_set_chan(sc, chan); else if (sc->mac_ver >= 0x5390) run_rt5390_set_chan(sc, chan); else if (sc->mac_ver == 0x3593) run_rt3593_set_chan(sc, chan); else if (sc->mac_ver == 0x3572) run_rt3572_set_chan(sc, chan); else if (sc->mac_ver >= 0x3070) run_rt3070_set_chan(sc, chan); else run_rt2870_set_chan(sc, chan); /* determine channel group */ if (chan <= 14) group = 0; else if (chan <= 64) group = 1; else if (chan <= 128) group = 2; else group = 3; /* XXX necessary only when group has changed! */ run_select_chan_group(sc, group); run_delay(sc, 10); /* Perform IQ calibration. */ if (sc->mac_ver >= 0x5392) run_iq_calib(sc, chan); return (0); } static void run_set_channel(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; RUN_LOCK(sc); run_set_chan(sc, ic->ic_curchan); RUN_UNLOCK(sc); return; } static void run_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { struct run_softc *sc = ic->ic_softc; uint8_t bands[IEEE80211_MODE_BYTES]; memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0); if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850 || sc->rf_rev == RT3070_RF_3052 || sc->rf_rev == RT3593_RF_3053 || sc->rf_rev == RT5592_RF_5592) { setbit(bands, IEEE80211_MODE_11A); ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, run_chan_5ghz, nitems(run_chan_5ghz), bands, 0); } } static void run_scan_start(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; RUN_LOCK(sc); /* abort TSF synchronization */ run_disable_tsf(sc); run_set_bssid(sc, ieee80211broadcastaddr); RUN_UNLOCK(sc); return; } static void run_scan_end(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; RUN_LOCK(sc); run_enable_tsf_sync(sc); run_set_bssid(sc, sc->sc_bssid); RUN_UNLOCK(sc); return; } /* * Could be called from ieee80211_node_timeout() * (non-sleepable thread) */ static void run_update_beacon(struct ieee80211vap *vap, int item) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; struct ieee80211_node *ni = vap->iv_bss; struct run_softc *sc = ic->ic_softc; struct run_vap *rvp = RUN_VAP(vap); int mcast = 0; uint32_t i; switch (item) { case IEEE80211_BEACON_ERP: run_updateslot(ic); break; case IEEE80211_BEACON_HTINFO: run_updateprot(ic); break; case IEEE80211_BEACON_TIM: mcast = 1; /*TODO*/ break; default: break; } setbit(bo->bo_flags, item); if (rvp->beacon_mbuf == NULL) { rvp->beacon_mbuf = ieee80211_beacon_alloc(ni); if (rvp->beacon_mbuf == NULL) return; } ieee80211_beacon_update(ni, rvp->beacon_mbuf, mcast); i = RUN_CMDQ_GET(&sc->cmdq_store); RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i); sc->cmdq[i].func = run_update_beacon_cb; sc->cmdq[i].arg0 = vap; ieee80211_runtask(ic, &sc->cmdq_task); return; } static void run_update_beacon_cb(void *arg) { struct ieee80211vap *vap = arg; struct ieee80211_node *ni = vap->iv_bss; struct run_vap *rvp = RUN_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct run_softc *sc = ic->ic_softc; struct rt2860_txwi txwi; struct mbuf *m; uint16_t txwisize; uint8_t ridx; if (ni->ni_chan == IEEE80211_CHAN_ANYC) return; if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) return; /* * No need to call ieee80211_beacon_update(), run_update_beacon() * is taking care of appropriate calls. */ if (rvp->beacon_mbuf == NULL) { rvp->beacon_mbuf = ieee80211_beacon_alloc(ni); if (rvp->beacon_mbuf == NULL) return; } m = rvp->beacon_mbuf; memset(&txwi, 0, sizeof(txwi)); txwi.wcid = 0xff; txwi.len = htole16(m->m_pkthdr.len); /* send beacons at the lowest available rate */ ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; txwi.phy = htole16(rt2860_rates[ridx].mcs); if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) txwi.phy |= htole16(RT2860_PHY_OFDM); txwi.txop = RT2860_TX_TXOP_HT; txwi.flags = RT2860_TX_TS; txwi.xflags = RT2860_TX_NSEQ; txwisize = (sc->mac_ver == 0x5592) ? sizeof(txwi) + sizeof(uint32_t) : sizeof(txwi); run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id), (uint8_t *)&txwi, txwisize); run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id) + txwisize, mtod(m, uint8_t *), (m->m_pkthdr.len + 1) & ~1); } static void run_updateprot(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; uint32_t i; i = RUN_CMDQ_GET(&sc->cmdq_store); RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i); sc->cmdq[i].func = run_updateprot_cb; sc->cmdq[i].arg0 = ic; ieee80211_runtask(ic, &sc->cmdq_task); } static void run_updateprot_cb(void *arg) { struct ieee80211com *ic = arg; struct run_softc *sc = ic->ic_softc; uint32_t tmp; tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL; /* setup protection frame rate (MCS code) */ tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ? rt2860_rates[RT2860_RIDX_OFDM6].mcs | RT2860_PHY_OFDM : rt2860_rates[RT2860_RIDX_CCK11].mcs; /* CCK frames don't require protection */ run_write(sc, RT2860_CCK_PROT_CFG, tmp); if (ic->ic_flags & IEEE80211_F_USEPROT) { if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) tmp |= RT2860_PROT_CTRL_RTS_CTS; else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) tmp |= RT2860_PROT_CTRL_CTS; } run_write(sc, RT2860_OFDM_PROT_CFG, tmp); } static void run_usb_timeout_cb(void *arg) { struct ieee80211vap *vap = arg; struct run_softc *sc = vap->iv_ic->ic_softc; RUN_LOCK_ASSERT(sc, MA_OWNED); if(vap->iv_state == IEEE80211_S_RUN && vap->iv_opmode != IEEE80211_M_STA) run_reset_livelock(sc); else if (vap->iv_state == IEEE80211_S_SCAN) { RUN_DPRINTF(sc, RUN_DEBUG_USB | RUN_DEBUG_STATE, "timeout caused by scan\n"); /* cancel bgscan */ ieee80211_cancel_scan(vap); } else RUN_DPRINTF(sc, RUN_DEBUG_USB | RUN_DEBUG_STATE, "timeout by unknown cause\n"); } static void run_reset_livelock(struct run_softc *sc) { uint32_t tmp; RUN_LOCK_ASSERT(sc, MA_OWNED); /* * In IBSS or HostAP modes (when the hardware sends beacons), the MAC * can run into a livelock and start sending CTS-to-self frames like * crazy if protection is enabled. Reset MAC/BBP for a while */ run_read(sc, RT2860_DEBUG, &tmp); RUN_DPRINTF(sc, RUN_DEBUG_RESET, "debug reg %08x\n", tmp); if ((tmp & (1 << 29)) && (tmp & (1 << 7 | 1 << 5))) { RUN_DPRINTF(sc, RUN_DEBUG_RESET, "CTS-to-self livelock detected\n"); run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_SRST); run_delay(sc, 1); run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); } } static void run_update_promisc_locked(struct run_softc *sc) { uint32_t tmp; run_read(sc, RT2860_RX_FILTR_CFG, &tmp); tmp |= RT2860_DROP_UC_NOME; if (sc->sc_ic.ic_promisc > 0) tmp &= ~RT2860_DROP_UC_NOME; run_write(sc, RT2860_RX_FILTR_CFG, tmp); RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s promiscuous mode\n", (sc->sc_ic.ic_promisc > 0) ? "entering" : "leaving"); } static void run_update_promisc(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; if ((sc->sc_flags & RUN_RUNNING) == 0) return; RUN_LOCK(sc); run_update_promisc_locked(sc); RUN_UNLOCK(sc); } static void run_enable_tsf_sync(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t tmp; RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "rvp_id=%d ic_opmode=%d\n", RUN_VAP(vap)->rvp_id, ic->ic_opmode); run_read(sc, RT2860_BCN_TIME_CFG, &tmp); tmp &= ~0x1fffff; tmp |= vap->iv_bss->ni_intval * 16; tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN; if (ic->ic_opmode == IEEE80211_M_STA) { /* * Local TSF is always updated with remote TSF on beacon * reception. */ tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT; } else if (ic->ic_opmode == IEEE80211_M_IBSS) { tmp |= RT2860_BCN_TX_EN; /* * Local TSF is updated with remote TSF on beacon reception * only if the remote TSF is greater than local TSF. */ tmp |= 2 << RT2860_TSF_SYNC_MODE_SHIFT; } else if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) { tmp |= RT2860_BCN_TX_EN; /* SYNC with nobody */ tmp |= 3 << RT2860_TSF_SYNC_MODE_SHIFT; } else { RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "Enabling TSF failed. undefined opmode\n"); return; } run_write(sc, RT2860_BCN_TIME_CFG, tmp); } static void run_enable_tsf(struct run_softc *sc) { uint32_t tmp; if (run_read(sc, RT2860_BCN_TIME_CFG, &tmp) == 0) { tmp &= ~(RT2860_BCN_TX_EN | RT2860_TBTT_TIMER_EN); tmp |= RT2860_TSF_TIMER_EN; run_write(sc, RT2860_BCN_TIME_CFG, tmp); } } static void run_disable_tsf(struct run_softc *sc) { uint32_t tmp; if (run_read(sc, RT2860_BCN_TIME_CFG, &tmp) == 0) { tmp &= ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN); run_write(sc, RT2860_BCN_TIME_CFG, tmp); } } static void run_get_tsf(struct run_softc *sc, uint64_t *buf) { run_read_region_1(sc, RT2860_TSF_TIMER_DW0, (uint8_t *)buf, sizeof(*buf)); } static void run_enable_mrr(struct run_softc *sc) { #define CCK(mcs) (mcs) #define OFDM(mcs) (1 << 3 | (mcs)) run_write(sc, RT2860_LG_FBK_CFG0, OFDM(6) << 28 | /* 54->48 */ OFDM(5) << 24 | /* 48->36 */ OFDM(4) << 20 | /* 36->24 */ OFDM(3) << 16 | /* 24->18 */ OFDM(2) << 12 | /* 18->12 */ OFDM(1) << 8 | /* 12-> 9 */ OFDM(0) << 4 | /* 9-> 6 */ OFDM(0)); /* 6-> 6 */ run_write(sc, RT2860_LG_FBK_CFG1, CCK(2) << 12 | /* 11->5.5 */ CCK(1) << 8 | /* 5.5-> 2 */ CCK(0) << 4 | /* 2-> 1 */ CCK(0)); /* 1-> 1 */ #undef OFDM #undef CCK } static void run_set_txpreamble(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t tmp; run_read(sc, RT2860_AUTO_RSP_CFG, &tmp); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) tmp |= RT2860_CCK_SHORT_EN; else tmp &= ~RT2860_CCK_SHORT_EN; run_write(sc, RT2860_AUTO_RSP_CFG, tmp); } static void run_set_basicrates(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; /* set basic rates mask */ if (ic->ic_curmode == IEEE80211_MODE_11B) run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x003); else if (ic->ic_curmode == IEEE80211_MODE_11A) run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x150); else /* 11g */ run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x15f); } static void run_set_leds(struct run_softc *sc, uint16_t which) { (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LEDS, which | (sc->leds & 0x7f)); } static void run_set_bssid(struct run_softc *sc, const uint8_t *bssid) { run_write(sc, RT2860_MAC_BSSID_DW0, bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); run_write(sc, RT2860_MAC_BSSID_DW1, bssid[4] | bssid[5] << 8); } static void run_set_macaddr(struct run_softc *sc, const uint8_t *addr) { run_write(sc, RT2860_MAC_ADDR_DW0, addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); run_write(sc, RT2860_MAC_ADDR_DW1, addr[4] | addr[5] << 8 | 0xff << 16); } static void run_updateslot(struct ieee80211com *ic) { struct run_softc *sc = ic->ic_softc; uint32_t i; i = RUN_CMDQ_GET(&sc->cmdq_store); RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i); sc->cmdq[i].func = run_updateslot_cb; sc->cmdq[i].arg0 = ic; ieee80211_runtask(ic, &sc->cmdq_task); return; } /* ARGSUSED */ static void run_updateslot_cb(void *arg) { struct ieee80211com *ic = arg; struct run_softc *sc = ic->ic_softc; uint32_t tmp; run_read(sc, RT2860_BKOFF_SLOT_CFG, &tmp); tmp &= ~0xff; tmp |= IEEE80211_GET_SLOTTIME(ic); run_write(sc, RT2860_BKOFF_SLOT_CFG, tmp); } static void run_update_mcast(struct ieee80211com *ic) { } static int8_t run_rssi2dbm(struct run_softc *sc, uint8_t rssi, uint8_t rxchain) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_channel *c = ic->ic_curchan; int delta; if (IEEE80211_IS_CHAN_5GHZ(c)) { u_int chan = ieee80211_chan2ieee(ic, c); delta = sc->rssi_5ghz[rxchain]; /* determine channel group */ if (chan <= 64) delta -= sc->lna[1]; else if (chan <= 128) delta -= sc->lna[2]; else delta -= sc->lna[3]; } else delta = sc->rssi_2ghz[rxchain] - sc->lna[0]; return (-12 - delta - rssi); } static void run_rt5390_bbp_init(struct run_softc *sc) { u_int i; uint8_t bbp; /* Apply maximum likelihood detection for 2 stream case. */ run_bbp_read(sc, 105, &bbp); if (sc->nrxchains > 1) run_bbp_write(sc, 105, bbp | RT5390_MLD); /* Avoid data lost and CRC error. */ run_bbp_read(sc, 4, &bbp); run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); if (sc->mac_ver == 0x5592) { for (i = 0; i < nitems(rt5592_def_bbp); i++) { run_bbp_write(sc, rt5592_def_bbp[i].reg, rt5592_def_bbp[i].val); } for (i = 0; i < nitems(rt5592_bbp_r196); i++) { run_bbp_write(sc, 195, i + 0x80); run_bbp_write(sc, 196, rt5592_bbp_r196[i]); } } else { for (i = 0; i < nitems(rt5390_def_bbp); i++) { run_bbp_write(sc, rt5390_def_bbp[i].reg, rt5390_def_bbp[i].val); } } if (sc->mac_ver == 0x5392) { run_bbp_write(sc, 88, 0x90); run_bbp_write(sc, 95, 0x9a); run_bbp_write(sc, 98, 0x12); run_bbp_write(sc, 106, 0x12); run_bbp_write(sc, 134, 0xd0); run_bbp_write(sc, 135, 0xf6); run_bbp_write(sc, 148, 0x84); } run_bbp_read(sc, 152, &bbp); run_bbp_write(sc, 152, bbp | 0x80); /* Fix BBP254 for RT5592C. */ if (sc->mac_ver == 0x5592 && sc->mac_rev >= 0x0221) { run_bbp_read(sc, 254, &bbp); run_bbp_write(sc, 254, bbp | 0x80); } /* Disable hardware antenna diversity. */ if (sc->mac_ver == 0x5390) run_bbp_write(sc, 154, 0); /* Initialize Rx CCK/OFDM frequency offset report. */ run_bbp_write(sc, 142, 1); run_bbp_write(sc, 143, 57); } static int run_bbp_init(struct run_softc *sc) { int i, error, ntries; uint8_t bbp0; /* wait for BBP to wake up */ for (ntries = 0; ntries < 20; ntries++) { if ((error = run_bbp_read(sc, 0, &bbp0)) != 0) return error; if (bbp0 != 0 && bbp0 != 0xff) break; } if (ntries == 20) return (ETIMEDOUT); /* initialize BBP registers to default values */ if (sc->mac_ver >= 0x5390) run_rt5390_bbp_init(sc); else { for (i = 0; i < nitems(rt2860_def_bbp); i++) { run_bbp_write(sc, rt2860_def_bbp[i].reg, rt2860_def_bbp[i].val); } } if (sc->mac_ver == 0x3593) { run_bbp_write(sc, 79, 0x13); run_bbp_write(sc, 80, 0x05); run_bbp_write(sc, 81, 0x33); run_bbp_write(sc, 86, 0x46); run_bbp_write(sc, 137, 0x0f); } /* fix BBP84 for RT2860E */ if (sc->mac_ver == 0x2860 && sc->mac_rev != 0x0101) run_bbp_write(sc, 84, 0x19); if (sc->mac_ver >= 0x3070 && (sc->mac_ver != 0x3593 && sc->mac_ver != 0x5592)) { run_bbp_write(sc, 79, 0x13); run_bbp_write(sc, 80, 0x05); run_bbp_write(sc, 81, 0x33); } else if (sc->mac_ver == 0x2860 && sc->mac_rev == 0x0100) { run_bbp_write(sc, 69, 0x16); run_bbp_write(sc, 73, 0x12); } return (0); } static int run_rt3070_rf_init(struct run_softc *sc) { uint32_t tmp; uint8_t bbp4, mingain, rf, target; u_int i; run_rt3070_rf_read(sc, 30, &rf); /* toggle RF R30 bit 7 */ run_rt3070_rf_write(sc, 30, rf | 0x80); run_delay(sc, 10); run_rt3070_rf_write(sc, 30, rf & ~0x80); /* initialize RF registers to default value */ if (sc->mac_ver == 0x3572) { for (i = 0; i < nitems(rt3572_def_rf); i++) { run_rt3070_rf_write(sc, rt3572_def_rf[i].reg, rt3572_def_rf[i].val); } } else { for (i = 0; i < nitems(rt3070_def_rf); i++) { run_rt3070_rf_write(sc, rt3070_def_rf[i].reg, rt3070_def_rf[i].val); } } if (sc->mac_ver == 0x3070 && sc->mac_rev < 0x0201) { /* * Change voltage from 1.2V to 1.35V for RT3070. * The DAC issue (RT3070_LDO_CFG0) has been fixed * in RT3070(F). */ run_read(sc, RT3070_LDO_CFG0, &tmp); tmp = (tmp & ~0x0f000000) | 0x0d000000; run_write(sc, RT3070_LDO_CFG0, tmp); } else if (sc->mac_ver == 0x3071) { run_rt3070_rf_read(sc, 6, &rf); run_rt3070_rf_write(sc, 6, rf | 0x40); run_rt3070_rf_write(sc, 31, 0x14); run_read(sc, RT3070_LDO_CFG0, &tmp); tmp &= ~0x1f000000; if (sc->mac_rev < 0x0211) tmp |= 0x0d000000; /* 1.3V */ else tmp |= 0x01000000; /* 1.2V */ run_write(sc, RT3070_LDO_CFG0, tmp); /* patch LNA_PE_G1 */ run_read(sc, RT3070_GPIO_SWITCH, &tmp); run_write(sc, RT3070_GPIO_SWITCH, tmp & ~0x20); } else if (sc->mac_ver == 0x3572) { run_rt3070_rf_read(sc, 6, &rf); run_rt3070_rf_write(sc, 6, rf | 0x40); /* increase voltage from 1.2V to 1.35V */ run_read(sc, RT3070_LDO_CFG0, &tmp); tmp = (tmp & ~0x1f000000) | 0x0d000000; run_write(sc, RT3070_LDO_CFG0, tmp); if (sc->mac_rev < 0x0211 || !sc->patch_dac) { run_delay(sc, 1); /* wait for 1msec */ /* decrease voltage back to 1.2V */ tmp = (tmp & ~0x1f000000) | 0x01000000; run_write(sc, RT3070_LDO_CFG0, tmp); } } /* select 20MHz bandwidth */ run_rt3070_rf_read(sc, 31, &rf); run_rt3070_rf_write(sc, 31, rf & ~0x20); /* calibrate filter for 20MHz bandwidth */ sc->rf24_20mhz = 0x1f; /* default value */ target = (sc->mac_ver < 0x3071) ? 0x16 : 0x13; run_rt3070_filter_calib(sc, 0x07, target, &sc->rf24_20mhz); /* select 40MHz bandwidth */ run_bbp_read(sc, 4, &bbp4); run_bbp_write(sc, 4, (bbp4 & ~0x18) | 0x10); run_rt3070_rf_read(sc, 31, &rf); run_rt3070_rf_write(sc, 31, rf | 0x20); /* calibrate filter for 40MHz bandwidth */ sc->rf24_40mhz = 0x2f; /* default value */ target = (sc->mac_ver < 0x3071) ? 0x19 : 0x15; run_rt3070_filter_calib(sc, 0x27, target, &sc->rf24_40mhz); /* go back to 20MHz bandwidth */ run_bbp_read(sc, 4, &bbp4); run_bbp_write(sc, 4, bbp4 & ~0x18); if (sc->mac_ver == 0x3572) { /* save default BBP registers 25 and 26 values */ run_bbp_read(sc, 25, &sc->bbp25); run_bbp_read(sc, 26, &sc->bbp26); } else if (sc->mac_rev < 0x0201 || sc->mac_rev < 0x0211) run_rt3070_rf_write(sc, 27, 0x03); run_read(sc, RT3070_OPT_14, &tmp); run_write(sc, RT3070_OPT_14, tmp | 1); if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) { run_rt3070_rf_read(sc, 17, &rf); rf &= ~RT3070_TX_LO1; if ((sc->mac_ver == 0x3070 || (sc->mac_ver == 0x3071 && sc->mac_rev >= 0x0211)) && !sc->ext_2ghz_lna) rf |= 0x20; /* fix for long range Rx issue */ mingain = (sc->mac_ver == 0x3070) ? 1 : 2; if (sc->txmixgain_2ghz >= mingain) rf = (rf & ~0x7) | sc->txmixgain_2ghz; run_rt3070_rf_write(sc, 17, rf); } if (sc->mac_ver == 0x3071) { run_rt3070_rf_read(sc, 1, &rf); rf &= ~(RT3070_RX0_PD | RT3070_TX0_PD); rf |= RT3070_RF_BLOCK | RT3070_RX1_PD | RT3070_TX1_PD; run_rt3070_rf_write(sc, 1, rf); run_rt3070_rf_read(sc, 15, &rf); run_rt3070_rf_write(sc, 15, rf & ~RT3070_TX_LO2); run_rt3070_rf_read(sc, 20, &rf); run_rt3070_rf_write(sc, 20, rf & ~RT3070_RX_LO1); run_rt3070_rf_read(sc, 21, &rf); run_rt3070_rf_write(sc, 21, rf & ~RT3070_RX_LO2); } if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) { /* fix Tx to Rx IQ glitch by raising RF voltage */ run_rt3070_rf_read(sc, 27, &rf); rf &= ~0x77; if (sc->mac_rev < 0x0211) rf |= 0x03; run_rt3070_rf_write(sc, 27, rf); } return (0); } static void run_rt3593_rf_init(struct run_softc *sc) { uint32_t tmp; uint8_t rf; u_int i; /* Disable the GPIO bits 4 and 7 for LNA PE control. */ run_read(sc, RT3070_GPIO_SWITCH, &tmp); tmp &= ~(1 << 4 | 1 << 7); run_write(sc, RT3070_GPIO_SWITCH, tmp); /* Initialize RF registers to default value. */ for (i = 0; i < nitems(rt3593_def_rf); i++) { run_rt3070_rf_write(sc, rt3593_def_rf[i].reg, rt3593_def_rf[i].val); } /* Toggle RF R2 to initiate calibration. */ run_rt3070_rf_write(sc, 2, RT5390_RESCAL); /* Initialize RF frequency offset. */ run_adjust_freq_offset(sc); run_rt3070_rf_read(sc, 18, &rf); run_rt3070_rf_write(sc, 18, rf | RT3593_AUTOTUNE_BYPASS); /* * Increase voltage from 1.2V to 1.35V, wait for 1 msec to * decrease voltage back to 1.2V. */ run_read(sc, RT3070_LDO_CFG0, &tmp); tmp = (tmp & ~0x1f000000) | 0x0d000000; run_write(sc, RT3070_LDO_CFG0, tmp); run_delay(sc, 1); tmp = (tmp & ~0x1f000000) | 0x01000000; run_write(sc, RT3070_LDO_CFG0, tmp); sc->rf24_20mhz = 0x1f; sc->rf24_40mhz = 0x2f; /* Save default BBP registers 25 and 26 values. */ run_bbp_read(sc, 25, &sc->bbp25); run_bbp_read(sc, 26, &sc->bbp26); run_read(sc, RT3070_OPT_14, &tmp); run_write(sc, RT3070_OPT_14, tmp | 1); } static void run_rt5390_rf_init(struct run_softc *sc) { uint32_t tmp; uint8_t rf; u_int i; /* Toggle RF R2 to initiate calibration. */ if (sc->mac_ver == 0x5390) { run_rt3070_rf_read(sc, 2, &rf); run_rt3070_rf_write(sc, 2, rf | RT5390_RESCAL); run_delay(sc, 10); run_rt3070_rf_write(sc, 2, rf & ~RT5390_RESCAL); } else { run_rt3070_rf_write(sc, 2, RT5390_RESCAL); run_delay(sc, 10); } /* Initialize RF registers to default value. */ if (sc->mac_ver == 0x5592) { for (i = 0; i < nitems(rt5592_def_rf); i++) { run_rt3070_rf_write(sc, rt5592_def_rf[i].reg, rt5592_def_rf[i].val); } /* Initialize RF frequency offset. */ run_adjust_freq_offset(sc); } else if (sc->mac_ver == 0x5392) { for (i = 0; i < nitems(rt5392_def_rf); i++) { run_rt3070_rf_write(sc, rt5392_def_rf[i].reg, rt5392_def_rf[i].val); } if (sc->mac_rev >= 0x0223) { run_rt3070_rf_write(sc, 23, 0x0f); run_rt3070_rf_write(sc, 24, 0x3e); run_rt3070_rf_write(sc, 51, 0x32); run_rt3070_rf_write(sc, 53, 0x22); run_rt3070_rf_write(sc, 56, 0xc1); run_rt3070_rf_write(sc, 59, 0x0f); } } else { for (i = 0; i < nitems(rt5390_def_rf); i++) { run_rt3070_rf_write(sc, rt5390_def_rf[i].reg, rt5390_def_rf[i].val); } if (sc->mac_rev >= 0x0502) { run_rt3070_rf_write(sc, 6, 0xe0); run_rt3070_rf_write(sc, 25, 0x80); run_rt3070_rf_write(sc, 46, 0x73); run_rt3070_rf_write(sc, 53, 0x00); run_rt3070_rf_write(sc, 56, 0x42); run_rt3070_rf_write(sc, 61, 0xd1); } } sc->rf24_20mhz = 0x1f; /* default value */ sc->rf24_40mhz = (sc->mac_ver == 0x5592) ? 0 : 0x2f; if (sc->mac_rev < 0x0211) run_rt3070_rf_write(sc, 27, 0x3); run_read(sc, RT3070_OPT_14, &tmp); run_write(sc, RT3070_OPT_14, tmp | 1); } static int run_rt3070_filter_calib(struct run_softc *sc, uint8_t init, uint8_t target, uint8_t *val) { uint8_t rf22, rf24; uint8_t bbp55_pb, bbp55_sb, delta; int ntries; /* program filter */ run_rt3070_rf_read(sc, 24, &rf24); rf24 = (rf24 & 0xc0) | init; /* initial filter value */ run_rt3070_rf_write(sc, 24, rf24); /* enable baseband loopback mode */ run_rt3070_rf_read(sc, 22, &rf22); run_rt3070_rf_write(sc, 22, rf22 | 0x01); /* set power and frequency of passband test tone */ run_bbp_write(sc, 24, 0x00); for (ntries = 0; ntries < 100; ntries++) { /* transmit test tone */ run_bbp_write(sc, 25, 0x90); run_delay(sc, 10); /* read received power */ run_bbp_read(sc, 55, &bbp55_pb); if (bbp55_pb != 0) break; } if (ntries == 100) return (ETIMEDOUT); /* set power and frequency of stopband test tone */ run_bbp_write(sc, 24, 0x06); for (ntries = 0; ntries < 100; ntries++) { /* transmit test tone */ run_bbp_write(sc, 25, 0x90); run_delay(sc, 10); /* read received power */ run_bbp_read(sc, 55, &bbp55_sb); delta = bbp55_pb - bbp55_sb; if (delta > target) break; /* reprogram filter */ rf24++; run_rt3070_rf_write(sc, 24, rf24); } if (ntries < 100) { if (rf24 != init) rf24--; /* backtrack */ *val = rf24; run_rt3070_rf_write(sc, 24, rf24); } /* restore initial state */ run_bbp_write(sc, 24, 0x00); /* disable baseband loopback mode */ run_rt3070_rf_read(sc, 22, &rf22); run_rt3070_rf_write(sc, 22, rf22 & ~0x01); return (0); } static void run_rt3070_rf_setup(struct run_softc *sc) { uint8_t bbp, rf; int i; if (sc->mac_ver == 0x3572) { /* enable DC filter */ if (sc->mac_rev >= 0x0201) run_bbp_write(sc, 103, 0xc0); run_bbp_read(sc, 138, &bbp); if (sc->ntxchains == 1) bbp |= 0x20; /* turn off DAC1 */ if (sc->nrxchains == 1) bbp &= ~0x02; /* turn off ADC1 */ run_bbp_write(sc, 138, bbp); if (sc->mac_rev >= 0x0211) { /* improve power consumption */ run_bbp_read(sc, 31, &bbp); run_bbp_write(sc, 31, bbp & ~0x03); } run_rt3070_rf_read(sc, 16, &rf); rf = (rf & ~0x07) | sc->txmixgain_2ghz; run_rt3070_rf_write(sc, 16, rf); } else if (sc->mac_ver == 0x3071) { if (sc->mac_rev >= 0x0211) { /* enable DC filter */ run_bbp_write(sc, 103, 0xc0); /* improve power consumption */ run_bbp_read(sc, 31, &bbp); run_bbp_write(sc, 31, bbp & ~0x03); } run_bbp_read(sc, 138, &bbp); if (sc->ntxchains == 1) bbp |= 0x20; /* turn off DAC1 */ if (sc->nrxchains == 1) bbp &= ~0x02; /* turn off ADC1 */ run_bbp_write(sc, 138, bbp); run_write(sc, RT2860_TX_SW_CFG1, 0); if (sc->mac_rev < 0x0211) { run_write(sc, RT2860_TX_SW_CFG2, sc->patch_dac ? 0x2c : 0x0f); } else run_write(sc, RT2860_TX_SW_CFG2, 0); } else if (sc->mac_ver == 0x3070) { if (sc->mac_rev >= 0x0201) { /* enable DC filter */ run_bbp_write(sc, 103, 0xc0); /* improve power consumption */ run_bbp_read(sc, 31, &bbp); run_bbp_write(sc, 31, bbp & ~0x03); } if (sc->mac_rev < 0x0201) { run_write(sc, RT2860_TX_SW_CFG1, 0); run_write(sc, RT2860_TX_SW_CFG2, 0x2c); } else run_write(sc, RT2860_TX_SW_CFG2, 0); } /* initialize RF registers from ROM for >=RT3071*/ if (sc->mac_ver >= 0x3071) { for (i = 0; i < 10; i++) { if (sc->rf[i].reg == 0 || sc->rf[i].reg == 0xff) continue; run_rt3070_rf_write(sc, sc->rf[i].reg, sc->rf[i].val); } } } static void run_rt3593_rf_setup(struct run_softc *sc) { uint8_t bbp, rf; if (sc->mac_rev >= 0x0211) { /* Enable DC filter. */ run_bbp_write(sc, 103, 0xc0); } run_write(sc, RT2860_TX_SW_CFG1, 0); if (sc->mac_rev < 0x0211) { run_write(sc, RT2860_TX_SW_CFG2, sc->patch_dac ? 0x2c : 0x0f); } else run_write(sc, RT2860_TX_SW_CFG2, 0); run_rt3070_rf_read(sc, 50, &rf); run_rt3070_rf_write(sc, 50, rf & ~RT3593_TX_LO2); run_rt3070_rf_read(sc, 51, &rf); rf = (rf & ~(RT3593_TX_LO1 | 0x0c)) | ((sc->txmixgain_2ghz & 0x07) << 2); run_rt3070_rf_write(sc, 51, rf); run_rt3070_rf_read(sc, 38, &rf); run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1); run_rt3070_rf_read(sc, 39, &rf); run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2); run_rt3070_rf_read(sc, 1, &rf); run_rt3070_rf_write(sc, 1, rf & ~(RT3070_RF_BLOCK | RT3070_PLL_PD)); run_rt3070_rf_read(sc, 30, &rf); rf = (rf & ~0x18) | 0x10; run_rt3070_rf_write(sc, 30, rf); /* Apply maximum likelihood detection for 2 stream case. */ run_bbp_read(sc, 105, &bbp); if (sc->nrxchains > 1) run_bbp_write(sc, 105, bbp | RT5390_MLD); /* Avoid data lost and CRC error. */ run_bbp_read(sc, 4, &bbp); run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); run_bbp_write(sc, 92, 0x02); run_bbp_write(sc, 82, 0x82); run_bbp_write(sc, 106, 0x05); run_bbp_write(sc, 104, 0x92); run_bbp_write(sc, 88, 0x90); run_bbp_write(sc, 148, 0xc8); run_bbp_write(sc, 47, 0x48); run_bbp_write(sc, 120, 0x50); run_bbp_write(sc, 163, 0x9d); /* SNR mapping. */ run_bbp_write(sc, 142, 0x06); run_bbp_write(sc, 143, 0xa0); run_bbp_write(sc, 142, 0x07); run_bbp_write(sc, 143, 0xa1); run_bbp_write(sc, 142, 0x08); run_bbp_write(sc, 143, 0xa2); run_bbp_write(sc, 31, 0x08); run_bbp_write(sc, 68, 0x0b); run_bbp_write(sc, 105, 0x04); } static void run_rt5390_rf_setup(struct run_softc *sc) { uint8_t bbp, rf; if (sc->mac_rev >= 0x0211) { /* Enable DC filter. */ run_bbp_write(sc, 103, 0xc0); if (sc->mac_ver != 0x5592) { /* Improve power consumption. */ run_bbp_read(sc, 31, &bbp); run_bbp_write(sc, 31, bbp & ~0x03); } } run_bbp_read(sc, 138, &bbp); if (sc->ntxchains == 1) bbp |= 0x20; /* turn off DAC1 */ if (sc->nrxchains == 1) bbp &= ~0x02; /* turn off ADC1 */ run_bbp_write(sc, 138, bbp); run_rt3070_rf_read(sc, 38, &rf); run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1); run_rt3070_rf_read(sc, 39, &rf); run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2); /* Avoid data lost and CRC error. */ run_bbp_read(sc, 4, &bbp); run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); run_rt3070_rf_read(sc, 30, &rf); rf = (rf & ~0x18) | 0x10; run_rt3070_rf_write(sc, 30, rf); if (sc->mac_ver != 0x5592) { run_write(sc, RT2860_TX_SW_CFG1, 0); if (sc->mac_rev < 0x0211) { run_write(sc, RT2860_TX_SW_CFG2, sc->patch_dac ? 0x2c : 0x0f); } else run_write(sc, RT2860_TX_SW_CFG2, 0); } } static int run_txrx_enable(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t tmp; int error, ntries; run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN); for (ntries = 0; ntries < 200; ntries++) { if ((error = run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp)) != 0) return (error); if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) break; run_delay(sc, 50); } if (ntries == 200) return (ETIMEDOUT); run_delay(sc, 50); tmp |= RT2860_RX_DMA_EN | RT2860_TX_DMA_EN | RT2860_TX_WB_DDONE; run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); /* enable Rx bulk aggregation (set timeout and limit) */ tmp = RT2860_USB_TX_EN | RT2860_USB_RX_EN | RT2860_USB_RX_AGG_EN | RT2860_USB_RX_AGG_TO(128) | RT2860_USB_RX_AGG_LMT(2); run_write(sc, RT2860_USB_DMA_CFG, tmp); /* set Rx filter */ tmp = RT2860_DROP_CRC_ERR | RT2860_DROP_PHY_ERR; if (ic->ic_opmode != IEEE80211_M_MONITOR) { tmp |= RT2860_DROP_UC_NOME | RT2860_DROP_DUPL | RT2860_DROP_CTS | RT2860_DROP_BA | RT2860_DROP_ACK | RT2860_DROP_VER_ERR | RT2860_DROP_CTRL_RSV | RT2860_DROP_CFACK | RT2860_DROP_CFEND; if (ic->ic_opmode == IEEE80211_M_STA) tmp |= RT2860_DROP_RTS | RT2860_DROP_PSPOLL; } run_write(sc, RT2860_RX_FILTR_CFG, tmp); run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); return (0); } static void run_adjust_freq_offset(struct run_softc *sc) { uint8_t rf, tmp; run_rt3070_rf_read(sc, 17, &rf); tmp = rf; rf = (rf & ~0x7f) | (sc->freq & 0x7f); rf = MIN(rf, 0x5f); if (tmp != rf) run_mcu_cmd(sc, 0x74, (tmp << 8 ) | rf); } static void run_init_locked(struct run_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t tmp; uint8_t bbp1, bbp3; int i; int ridx; int ntries; if (ic->ic_nrunning > 1) return; run_stop(sc); if (run_load_microcode(sc) != 0) { device_printf(sc->sc_dev, "could not load 8051 microcode\n"); goto fail; } for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_ASIC_VER_ID, &tmp) != 0) goto fail; if (tmp != 0 && tmp != 0xffffffff) break; run_delay(sc, 10); } if (ntries == 100) goto fail; for (i = 0; i != RUN_EP_QUEUES; i++) run_setup_tx_list(sc, &sc->sc_epq[i]); run_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) goto fail; if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) break; run_delay(sc, 10); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); goto fail; } tmp &= 0xff0; tmp |= RT2860_TX_WB_DDONE; run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); /* turn off PME_OEN to solve high-current issue */ run_read(sc, RT2860_SYS_CTRL, &tmp); run_write(sc, RT2860_SYS_CTRL, tmp & ~RT2860_PME_OEN); run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST); run_write(sc, RT2860_USB_DMA_CFG, 0); if (run_reset(sc) != 0) { device_printf(sc->sc_dev, "could not reset chipset\n"); goto fail; } run_write(sc, RT2860_MAC_SYS_CTRL, 0); /* init Tx power for all Tx rates (from EEPROM) */ for (ridx = 0; ridx < 5; ridx++) { if (sc->txpow20mhz[ridx] == 0xffffffff) continue; run_write(sc, RT2860_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]); } for (i = 0; i < nitems(rt2870_def_mac); i++) run_write(sc, rt2870_def_mac[i].reg, rt2870_def_mac[i].val); run_write(sc, RT2860_WMM_AIFSN_CFG, 0x00002273); run_write(sc, RT2860_WMM_CWMIN_CFG, 0x00002344); run_write(sc, RT2860_WMM_CWMAX_CFG, 0x000034aa); if (sc->mac_ver >= 0x5390) { run_write(sc, RT2860_TX_SW_CFG0, 4 << RT2860_DLY_PAPE_EN_SHIFT | 4); if (sc->mac_ver >= 0x5392) { run_write(sc, RT2860_MAX_LEN_CFG, 0x00002fff); if (sc->mac_ver == 0x5592) { run_write(sc, RT2860_HT_FBK_CFG1, 0xedcba980); run_write(sc, RT2860_TXOP_HLDR_ET, 0x00000082); } else { run_write(sc, RT2860_HT_FBK_CFG1, 0xedcb4980); run_write(sc, RT2860_LG_FBK_CFG0, 0xedcba322); } } } else if (sc->mac_ver == 0x3593) { run_write(sc, RT2860_TX_SW_CFG0, 4 << RT2860_DLY_PAPE_EN_SHIFT | 2); } else if (sc->mac_ver >= 0x3070) { /* set delay of PA_PE assertion to 1us (unit of 0.25us) */ run_write(sc, RT2860_TX_SW_CFG0, 4 << RT2860_DLY_PAPE_EN_SHIFT); } /* wait while MAC is busy */ for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_MAC_STATUS_REG, &tmp) != 0) goto fail; if (!(tmp & (RT2860_RX_STATUS_BUSY | RT2860_TX_STATUS_BUSY))) break; run_delay(sc, 10); } if (ntries == 100) goto fail; /* clear Host to MCU mailbox */ run_write(sc, RT2860_H2M_BBPAGENT, 0); run_write(sc, RT2860_H2M_MAILBOX, 0); run_delay(sc, 10); if (run_bbp_init(sc) != 0) { device_printf(sc->sc_dev, "could not initialize BBP\n"); goto fail; } /* abort TSF synchronization */ run_disable_tsf(sc); /* clear RX WCID search table */ run_set_region_4(sc, RT2860_WCID_ENTRY(0), 0, 512); /* clear WCID attribute table */ run_set_region_4(sc, RT2860_WCID_ATTR(0), 0, 8 * 32); /* hostapd sets a key before init. So, don't clear it. */ if (sc->cmdq_key_set != RUN_CMDQ_GO) { /* clear shared key table */ run_set_region_4(sc, RT2860_SKEY(0, 0), 0, 8 * 32); /* clear shared key mode */ run_set_region_4(sc, RT2860_SKEY_MODE_0_7, 0, 4); } run_read(sc, RT2860_US_CYC_CNT, &tmp); tmp = (tmp & ~0xff) | 0x1e; run_write(sc, RT2860_US_CYC_CNT, tmp); if (sc->mac_rev != 0x0101) run_write(sc, RT2860_TXOP_CTRL_CFG, 0x0000583f); run_write(sc, RT2860_WMM_TXOP0_CFG, 0); run_write(sc, RT2860_WMM_TXOP1_CFG, 48 << 16 | 96); /* write vendor-specific BBP values (from EEPROM) */ if (sc->mac_ver < 0x3593) { for (i = 0; i < 10; i++) { if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff) continue; run_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val); } } /* select Main antenna for 1T1R devices */ if (sc->rf_rev == RT3070_RF_3020 || sc->rf_rev == RT5390_RF_5370) run_set_rx_antenna(sc, 0); /* send LEDs operating mode to microcontroller */ (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED1, sc->led[0]); (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED2, sc->led[1]); (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED3, sc->led[2]); if (sc->mac_ver >= 0x5390) run_rt5390_rf_init(sc); else if (sc->mac_ver == 0x3593) run_rt3593_rf_init(sc); else if (sc->mac_ver >= 0x3070) run_rt3070_rf_init(sc); /* disable non-existing Rx chains */ run_bbp_read(sc, 3, &bbp3); bbp3 &= ~(1 << 3 | 1 << 4); if (sc->nrxchains == 2) bbp3 |= 1 << 3; else if (sc->nrxchains == 3) bbp3 |= 1 << 4; run_bbp_write(sc, 3, bbp3); /* disable non-existing Tx chains */ run_bbp_read(sc, 1, &bbp1); if (sc->ntxchains == 1) bbp1 &= ~(1 << 3 | 1 << 4); run_bbp_write(sc, 1, bbp1); if (sc->mac_ver >= 0x5390) run_rt5390_rf_setup(sc); else if (sc->mac_ver == 0x3593) run_rt3593_rf_setup(sc); else if (sc->mac_ver >= 0x3070) run_rt3070_rf_setup(sc); /* select default channel */ run_set_chan(sc, ic->ic_curchan); /* setup initial protection mode */ run_updateprot_cb(ic); /* turn radio LED on */ run_set_leds(sc, RT2860_LED_RADIO); sc->sc_flags |= RUN_RUNNING; sc->cmdq_run = RUN_CMDQ_GO; for (i = 0; i != RUN_N_XFER; i++) usbd_xfer_set_stall(sc->sc_xfer[i]); usbd_transfer_start(sc->sc_xfer[RUN_BULK_RX]); if (run_txrx_enable(sc) != 0) goto fail; return; fail: run_stop(sc); } static void run_stop(void *arg) { struct run_softc *sc = (struct run_softc *)arg; uint32_t tmp; int i; int ntries; RUN_LOCK_ASSERT(sc, MA_OWNED); if (sc->sc_flags & RUN_RUNNING) run_set_leds(sc, 0); /* turn all LEDs off */ sc->sc_flags &= ~RUN_RUNNING; sc->ratectl_run = RUN_RATECTL_OFF; sc->cmdq_run = sc->cmdq_key_set; RUN_UNLOCK(sc); for(i = 0; i < RUN_N_XFER; i++) usbd_transfer_drain(sc->sc_xfer[i]); RUN_LOCK(sc); run_drain_mbufq(sc); if (sc->rx_m != NULL) { m_free(sc->rx_m); sc->rx_m = NULL; } /* Disable Tx/Rx DMA. */ if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) return; tmp &= ~(RT2860_RX_DMA_EN | RT2860_TX_DMA_EN); run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) return; if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) break; run_delay(sc, 10); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); return; } /* disable Tx/Rx */ run_read(sc, RT2860_MAC_SYS_CTRL, &tmp); tmp &= ~(RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); run_write(sc, RT2860_MAC_SYS_CTRL, tmp); /* wait for pending Tx to complete */ for (ntries = 0; ntries < 100; ntries++) { if (run_read(sc, RT2860_TXRXQ_PCNT, &tmp) != 0) { RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET, "Cannot read Tx queue count\n"); break; } if ((tmp & RT2860_TX2Q_PCNT_MASK) == 0) { RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET, "All Tx cleared\n"); break; } run_delay(sc, 10); } if (ntries >= 100) RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET, "There are still pending Tx\n"); run_delay(sc, 10); run_write(sc, RT2860_USB_DMA_CFG, 0); run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST); run_write(sc, RT2860_MAC_SYS_CTRL, 0); for (i = 0; i != RUN_EP_QUEUES; i++) run_unsetup_tx_list(sc, &sc->sc_epq[i]); } static void run_delay(struct run_softc *sc, u_int ms) { usb_pause_mtx(mtx_owned(&sc->sc_mtx) ? &sc->sc_mtx : NULL, USB_MS_TO_TICKS(ms)); } static device_method_t run_methods[] = { /* Device interface */ DEVMETHOD(device_probe, run_match), DEVMETHOD(device_attach, run_attach), DEVMETHOD(device_detach, run_detach), DEVMETHOD_END }; static driver_t run_driver = { .name = "run", .methods = run_methods, .size = sizeof(struct run_softc) }; static devclass_t run_devclass; DRIVER_MODULE(run, uhub, run_driver, run_devclass, run_driver_loaded, NULL); MODULE_DEPEND(run, wlan, 1, 1, 1); MODULE_DEPEND(run, usb, 1, 1, 1); MODULE_DEPEND(run, firmware, 1, 1, 1); MODULE_VERSION(run, 1); USB_PNP_HOST_INFO(run_devs); Index: projects/capsicum-test/sys/dev/usb/wlan/if_uath.c =================================================================== --- projects/capsicum-test/sys/dev/usb/wlan/if_uath.c (revision 345770) +++ projects/capsicum-test/sys/dev/usb/wlan/if_uath.c (revision 345771) @@ -1,2875 +1,2875 @@ /*- * SPDX-License-Identifier: (BSD-2-Clause-FreeBSD AND BSD-1-Clause) * * Copyright (c) 2006 Sam Leffler, Errno Consulting * Copyright (c) 2008-2009 Weongyo Jeong * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ /* * This driver is distantly derived from a driver of the same name * by Damien Bergamini. The original copyright is included below: * * Copyright (c) 2006 * Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /*- * Driver for Atheros AR5523 USB parts. * * The driver requires firmware to be loaded into the device. This * is done on device discovery from a user application (uathload) * that is launched by devd when a device with suitable product ID * is recognized. Once firmware has been loaded the device will * reset the USB port and re-attach with the original product ID+1 * and this driver will be attached. The firmware is licensed for * general use (royalty free) and may be incorporated in products. * Note that the firmware normally packaged with the NDIS drivers * for these devices does not work in this way and so does not work * with this driver. */ #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #include #include #include #include #include #include #include "usbdevs.h" #include #include static SYSCTL_NODE(_hw_usb, OID_AUTO, uath, CTLFLAG_RW, 0, "USB Atheros"); static int uath_countrycode = CTRY_DEFAULT; /* country code */ SYSCTL_INT(_hw_usb_uath, OID_AUTO, countrycode, CTLFLAG_RWTUN, &uath_countrycode, 0, "country code"); static int uath_regdomain = 0; /* regulatory domain */ SYSCTL_INT(_hw_usb_uath, OID_AUTO, regdomain, CTLFLAG_RD, &uath_regdomain, 0, "regulatory domain"); #ifdef UATH_DEBUG int uath_debug = 0; SYSCTL_INT(_hw_usb_uath, OID_AUTO, debug, CTLFLAG_RWTUN, &uath_debug, 0, "uath debug level"); enum { UATH_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ UATH_DEBUG_XMIT_DUMP = 0x00000002, /* xmit dump */ UATH_DEBUG_RECV = 0x00000004, /* basic recv operation */ UATH_DEBUG_TX_PROC = 0x00000008, /* tx ISR proc */ UATH_DEBUG_RX_PROC = 0x00000010, /* rx ISR proc */ UATH_DEBUG_RECV_ALL = 0x00000020, /* trace all frames (beacons) */ UATH_DEBUG_INIT = 0x00000040, /* initialization of dev */ UATH_DEBUG_DEVCAP = 0x00000080, /* dev caps */ UATH_DEBUG_CMDS = 0x00000100, /* commands */ UATH_DEBUG_CMDS_DUMP = 0x00000200, /* command buffer dump */ UATH_DEBUG_RESET = 0x00000400, /* reset processing */ UATH_DEBUG_STATE = 0x00000800, /* 802.11 state transitions */ UATH_DEBUG_MULTICAST = 0x00001000, /* multicast */ UATH_DEBUG_WME = 0x00002000, /* WME */ UATH_DEBUG_CHANNEL = 0x00004000, /* channel */ UATH_DEBUG_RATES = 0x00008000, /* rates */ UATH_DEBUG_CRYPTO = 0x00010000, /* crypto */ UATH_DEBUG_LED = 0x00020000, /* LED */ UATH_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) do { \ if (sc->sc_debug & (m)) \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(sc, m, fmt, ...) do { \ (void) sc; \ } while (0) #endif /* recognized device vendors/products */ static const STRUCT_USB_HOST_ID uath_devs[] = { #define UATH_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } UATH_DEV(ACCTON, SMCWUSBTG2), UATH_DEV(ATHEROS, AR5523), UATH_DEV(ATHEROS2, AR5523_1), UATH_DEV(ATHEROS2, AR5523_2), UATH_DEV(ATHEROS2, AR5523_3), UATH_DEV(CONCEPTRONIC, AR5523_1), UATH_DEV(CONCEPTRONIC, AR5523_2), UATH_DEV(DLINK, DWLAG122), UATH_DEV(DLINK, DWLAG132), UATH_DEV(DLINK, DWLG132), UATH_DEV(DLINK2, DWA120), UATH_DEV(GIGASET, AR5523), UATH_DEV(GIGASET, SMCWUSBTG), UATH_DEV(GLOBALSUN, AR5523_1), UATH_DEV(GLOBALSUN, AR5523_2), UATH_DEV(NETGEAR, WG111U), UATH_DEV(NETGEAR3, WG111T), UATH_DEV(NETGEAR3, WPN111), UATH_DEV(NETGEAR3, WPN111_2), UATH_DEV(UMEDIA, TEW444UBEU), UATH_DEV(UMEDIA, AR5523_2), UATH_DEV(WISTRONNEWEB, AR5523_1), UATH_DEV(WISTRONNEWEB, AR5523_2), UATH_DEV(ZCOM, AR5523) #undef UATH_DEV }; static usb_callback_t uath_intr_rx_callback; static usb_callback_t uath_intr_tx_callback; static usb_callback_t uath_bulk_rx_callback; static usb_callback_t uath_bulk_tx_callback; static const struct usb_config uath_usbconfig[UATH_N_XFERS] = { [UATH_INTR_RX] = { .type = UE_BULK, .endpoint = 0x1, .direction = UE_DIR_IN, .bufsize = UATH_MAX_CMDSZ, .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = uath_intr_rx_callback }, [UATH_INTR_TX] = { .type = UE_BULK, .endpoint = 0x1, .direction = UE_DIR_OUT, .bufsize = UATH_MAX_CMDSZ * UATH_CMD_LIST_COUNT, .flags = { .force_short_xfer = 1, .pipe_bof = 1, }, .callback = uath_intr_tx_callback, .timeout = UATH_CMD_TIMEOUT }, [UATH_BULK_RX] = { .type = UE_BULK, .endpoint = 0x2, .direction = UE_DIR_IN, .bufsize = MCLBYTES, .flags = { .ext_buffer = 1, .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = uath_bulk_rx_callback }, [UATH_BULK_TX] = { .type = UE_BULK, .endpoint = 0x2, .direction = UE_DIR_OUT, .bufsize = UATH_MAX_TXBUFSZ * UATH_TX_DATA_LIST_COUNT, .flags = { .force_short_xfer = 1, .pipe_bof = 1 }, .callback = uath_bulk_tx_callback, .timeout = UATH_DATA_TIMEOUT } }; static struct ieee80211vap *uath_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void uath_vap_delete(struct ieee80211vap *); static int uath_alloc_cmd_list(struct uath_softc *, struct uath_cmd []); static void uath_free_cmd_list(struct uath_softc *, struct uath_cmd []); static int uath_host_available(struct uath_softc *); static int uath_get_capability(struct uath_softc *, uint32_t, uint32_t *); static int uath_get_devcap(struct uath_softc *); static struct uath_cmd * uath_get_cmdbuf(struct uath_softc *); static int uath_cmd_read(struct uath_softc *, uint32_t, const void *, int, void *, int, int); static int uath_cmd_write(struct uath_softc *, uint32_t, const void *, int, int); static void uath_stat(void *); #ifdef UATH_DEBUG static void uath_dump_cmd(const uint8_t *, int, char); static const char * uath_codename(int); #endif static int uath_get_devstatus(struct uath_softc *, uint8_t macaddr[IEEE80211_ADDR_LEN]); static int uath_get_status(struct uath_softc *, uint32_t, void *, int); static int uath_alloc_rx_data_list(struct uath_softc *); static int uath_alloc_tx_data_list(struct uath_softc *); static void uath_free_rx_data_list(struct uath_softc *); static void uath_free_tx_data_list(struct uath_softc *); static int uath_init(struct uath_softc *); static void uath_stop(struct uath_softc *); static void uath_parent(struct ieee80211com *); static int uath_transmit(struct ieee80211com *, struct mbuf *); static void uath_start(struct uath_softc *); static int uath_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void uath_scan_start(struct ieee80211com *); static void uath_scan_end(struct ieee80211com *); static void uath_set_channel(struct ieee80211com *); static void uath_update_mcast(struct ieee80211com *); static void uath_update_promisc(struct ieee80211com *); static int uath_config(struct uath_softc *, uint32_t, uint32_t); static int uath_config_multi(struct uath_softc *, uint32_t, const void *, int); static int uath_switch_channel(struct uath_softc *, struct ieee80211_channel *); static int uath_set_rxfilter(struct uath_softc *, uint32_t, uint32_t); static void uath_watchdog(void *); static void uath_abort_xfers(struct uath_softc *); static int uath_dataflush(struct uath_softc *); static int uath_cmdflush(struct uath_softc *); static int uath_flush(struct uath_softc *); static int uath_set_ledstate(struct uath_softc *, int); static int uath_set_chan(struct uath_softc *, struct ieee80211_channel *); static int uath_reset_tx_queues(struct uath_softc *); static int uath_wme_init(struct uath_softc *); static struct uath_data * uath_getbuf(struct uath_softc *); static int uath_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int uath_set_key(struct uath_softc *, const struct ieee80211_key *, int); static int uath_set_keys(struct uath_softc *, struct ieee80211vap *); static void uath_sysctl_node(struct uath_softc *); static int uath_match(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != UATH_CONFIG_INDEX) return (ENXIO); if (uaa->info.bIfaceIndex != UATH_IFACE_INDEX) return (ENXIO); return (usbd_lookup_id_by_uaa(uath_devs, sizeof(uath_devs), uaa)); } static int uath_attach(device_t dev) { struct uath_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); struct ieee80211com *ic = &sc->sc_ic; uint8_t bands[IEEE80211_MODE_BYTES]; uint8_t iface_index = UATH_IFACE_INDEX; /* XXX */ usb_error_t error; sc->sc_dev = dev; sc->sc_udev = uaa->device; #ifdef UATH_DEBUG sc->sc_debug = uath_debug; #endif device_set_usb_desc(dev); /* * Only post-firmware devices here. */ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init(&sc->stat_ch, 0); callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0); mbufq_init(&sc->sc_snd, ifqmaxlen); error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, uath_usbconfig, UATH_N_XFERS, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB transfers, " "err=%s\n", usbd_errstr(error)); goto fail; } sc->sc_cmd_dma_buf = usbd_xfer_get_frame_buffer(sc->sc_xfer[UATH_INTR_TX], 0); sc->sc_tx_dma_buf = usbd_xfer_get_frame_buffer(sc->sc_xfer[UATH_BULK_TX], 0); /* * Setup buffers for firmware commands. */ error = uath_alloc_cmd_list(sc, sc->sc_cmd); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Tx command list\n"); goto fail1; } /* * We're now ready to send+receive firmware commands. */ UATH_LOCK(sc); error = uath_host_available(sc); if (error != 0) { device_printf(sc->sc_dev, "could not initialize adapter\n"); goto fail2; } error = uath_get_devcap(sc); if (error != 0) { device_printf(sc->sc_dev, "could not get device capabilities\n"); goto fail2; } UATH_UNLOCK(sc); /* Create device sysctl node. */ uath_sysctl_node(sc); UATH_LOCK(sc); error = uath_get_devstatus(sc, ic->ic_macaddr); if (error != 0) { device_printf(sc->sc_dev, "could not get device status\n"); goto fail2; } /* * Allocate xfers for Rx/Tx data pipes. */ error = uath_alloc_rx_data_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Rx data list\n"); goto fail2; } error = uath_alloc_tx_data_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Tx data list\n"); goto fail2; } UATH_UNLOCK(sc); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(dev); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA | /* station mode */ IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_WPA | /* 802.11i */ IEEE80211_C_BGSCAN | /* capable of bg scanning */ IEEE80211_C_TXFRAG; /* handle tx frags */ /* put a regulatory domain to reveal informations. */ uath_regdomain = sc->sc_devcap.regDomain; memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); if ((sc->sc_devcap.analog5GhzRevision & 0xf0) == 0x30) setbit(bands, IEEE80211_MODE_11A); /* XXX turbo */ ieee80211_init_channels(ic, NULL, bands); ieee80211_ifattach(ic); ic->ic_raw_xmit = uath_raw_xmit; ic->ic_scan_start = uath_scan_start; ic->ic_scan_end = uath_scan_end; ic->ic_set_channel = uath_set_channel; ic->ic_vap_create = uath_vap_create; ic->ic_vap_delete = uath_vap_delete; ic->ic_update_mcast = uath_update_mcast; ic->ic_update_promisc = uath_update_promisc; ic->ic_transmit = uath_transmit; ic->ic_parent = uath_parent; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), UATH_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), UATH_RX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); return (0); fail2: UATH_UNLOCK(sc); uath_free_cmd_list(sc, sc->sc_cmd); fail1: usbd_transfer_unsetup(sc->sc_xfer, UATH_N_XFERS); fail: return (error); } static int uath_detach(device_t dev) { struct uath_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; unsigned int x; /* * Prevent further allocations from RX/TX/CMD * data lists and ioctls */ UATH_LOCK(sc); sc->sc_flags |= UATH_FLAG_INVALID; STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); STAILQ_INIT(&sc->sc_cmd_active); STAILQ_INIT(&sc->sc_cmd_pending); STAILQ_INIT(&sc->sc_cmd_waiting); STAILQ_INIT(&sc->sc_cmd_inactive); uath_stop(sc); UATH_UNLOCK(sc); callout_drain(&sc->stat_ch); callout_drain(&sc->watchdog_ch); /* drain USB transfers */ for (x = 0; x != UATH_N_XFERS; x++) usbd_transfer_drain(sc->sc_xfer[x]); /* free data buffers */ UATH_LOCK(sc); uath_free_rx_data_list(sc); uath_free_tx_data_list(sc); uath_free_cmd_list(sc, sc->sc_cmd); UATH_UNLOCK(sc); /* free USB transfers and some data buffers */ usbd_transfer_unsetup(sc->sc_xfer, UATH_N_XFERS); ieee80211_ifdetach(ic); mbufq_drain(&sc->sc_snd); mtx_destroy(&sc->sc_mtx); return (0); } static void uath_free_cmd_list(struct uath_softc *sc, struct uath_cmd cmds[]) { int i; for (i = 0; i != UATH_CMD_LIST_COUNT; i++) cmds[i].buf = NULL; } static int uath_alloc_cmd_list(struct uath_softc *sc, struct uath_cmd cmds[]) { int i; STAILQ_INIT(&sc->sc_cmd_active); STAILQ_INIT(&sc->sc_cmd_pending); STAILQ_INIT(&sc->sc_cmd_waiting); STAILQ_INIT(&sc->sc_cmd_inactive); for (i = 0; i != UATH_CMD_LIST_COUNT; i++) { struct uath_cmd *cmd = &cmds[i]; cmd->sc = sc; /* backpointer for callbacks */ cmd->msgid = i; cmd->buf = ((uint8_t *)sc->sc_cmd_dma_buf) + (i * UATH_MAX_CMDSZ); STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, cmd, next); UATH_STAT_INC(sc, st_cmd_inactive); } return (0); } static int uath_host_available(struct uath_softc *sc) { struct uath_cmd_host_available setup; UATH_ASSERT_LOCKED(sc); /* inform target the host is available */ setup.sw_ver_major = htobe32(ATH_SW_VER_MAJOR); setup.sw_ver_minor = htobe32(ATH_SW_VER_MINOR); setup.sw_ver_patch = htobe32(ATH_SW_VER_PATCH); setup.sw_ver_build = htobe32(ATH_SW_VER_BUILD); return uath_cmd_read(sc, WDCMSG_HOST_AVAILABLE, &setup, sizeof setup, NULL, 0, 0); } #ifdef UATH_DEBUG static void uath_dump_cmd(const uint8_t *buf, int len, char prefix) { const char *sep = ""; int i; for (i = 0; i < len; i++) { if ((i % 16) == 0) { printf("%s%c ", sep, prefix); sep = "\n"; } else if ((i % 4) == 0) printf(" "); printf("%02x", buf[i]); } printf("\n"); } static const char * uath_codename(int code) { static const char *names[] = { "0x00", "HOST_AVAILABLE", "BIND", "TARGET_RESET", "TARGET_GET_CAPABILITY", "TARGET_SET_CONFIG", "TARGET_GET_STATUS", "TARGET_GET_STATS", "TARGET_START", "TARGET_STOP", "TARGET_ENABLE", "TARGET_DISABLE", "CREATE_CONNECTION", "UPDATE_CONNECT_ATTR", "DELETE_CONNECT", "SEND", "FLUSH", "STATS_UPDATE", "BMISS", "DEVICE_AVAIL", "SEND_COMPLETE", "DATA_AVAIL", "SET_PWR_MODE", "BMISS_ACK", "SET_LED_STEADY", "SET_LED_BLINK", "SETUP_BEACON_DESC", "BEACON_INIT", "RESET_KEY_CACHE", "RESET_KEY_CACHE_ENTRY", "SET_KEY_CACHE_ENTRY", "SET_DECOMP_MASK", "SET_REGULATORY_DOMAIN", "SET_LED_STATE", "WRITE_ASSOCID", "SET_STA_BEACON_TIMERS", "GET_TSF", "RESET_TSF", "SET_ADHOC_MODE", "SET_BASIC_RATE", "MIB_CONTROL", "GET_CHANNEL_DATA", "GET_CUR_RSSI", "SET_ANTENNA_SWITCH", "0x2c", "0x2d", "0x2e", "USE_SHORT_SLOT_TIME", "SET_POWER_MODE", "SETUP_PSPOLL_DESC", "SET_RX_MULTICAST_FILTER", "RX_FILTER", "PER_CALIBRATION", "RESET", "DISABLE", "PHY_DISABLE", "SET_TX_POWER_LIMIT", "SET_TX_QUEUE_PARAMS", "SETUP_TX_QUEUE", "RELEASE_TX_QUEUE", }; static char buf[8]; if (code < nitems(names)) return names[code]; if (code == WDCMSG_SET_DEFAULT_KEY) return "SET_DEFAULT_KEY"; snprintf(buf, sizeof(buf), "0x%02x", code); return buf; } #endif /* * Low-level function to send read or write commands to the firmware. */ static int uath_cmdsend(struct uath_softc *sc, uint32_t code, const void *idata, int ilen, void *odata, int olen, int flags) { struct uath_cmd_hdr *hdr; struct uath_cmd *cmd; int error; UATH_ASSERT_LOCKED(sc); /* grab a xfer */ cmd = uath_get_cmdbuf(sc); if (cmd == NULL) { device_printf(sc->sc_dev, "%s: empty inactive queue\n", __func__); return (ENOBUFS); } cmd->flags = flags; /* always bulk-out a multiple of 4 bytes */ cmd->buflen = roundup2(sizeof(struct uath_cmd_hdr) + ilen, 4); hdr = (struct uath_cmd_hdr *)cmd->buf; memset(hdr, 0, sizeof(struct uath_cmd_hdr)); hdr->len = htobe32(cmd->buflen); hdr->code = htobe32(code); hdr->msgid = cmd->msgid; /* don't care about endianness */ hdr->magic = htobe32((cmd->flags & UATH_CMD_FLAG_MAGIC) ? 1 << 24 : 0); memcpy((uint8_t *)(hdr + 1), idata, ilen); #ifdef UATH_DEBUG if (sc->sc_debug & UATH_DEBUG_CMDS) { printf("%s: send %s [flags 0x%x] olen %d\n", __func__, uath_codename(code), cmd->flags, olen); if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP) uath_dump_cmd(cmd->buf, cmd->buflen, '+'); } #endif cmd->odata = odata; KASSERT(odata == NULL || olen < UATH_MAX_CMDSZ - sizeof(*hdr) + sizeof(uint32_t), ("odata %p olen %u", odata, olen)); cmd->olen = olen; STAILQ_INSERT_TAIL(&sc->sc_cmd_pending, cmd, next); UATH_STAT_INC(sc, st_cmd_pending); usbd_transfer_start(sc->sc_xfer[UATH_INTR_TX]); if (cmd->flags & UATH_CMD_FLAG_READ) { usbd_transfer_start(sc->sc_xfer[UATH_INTR_RX]); /* wait at most two seconds for command reply */ error = mtx_sleep(cmd, &sc->sc_mtx, 0, "uathcmd", 2 * hz); cmd->odata = NULL; /* in case reply comes too late */ if (error != 0) { device_printf(sc->sc_dev, "timeout waiting for reply " "to cmd 0x%x (%u)\n", code, code); } else if (cmd->olen != olen) { device_printf(sc->sc_dev, "unexpected reply data count " "to cmd 0x%x (%u), got %u, expected %u\n", code, code, cmd->olen, olen); error = EINVAL; } return (error); } return (0); } static int uath_cmd_read(struct uath_softc *sc, uint32_t code, const void *idata, int ilen, void *odata, int olen, int flags) { flags |= UATH_CMD_FLAG_READ; return uath_cmdsend(sc, code, idata, ilen, odata, olen, flags); } static int uath_cmd_write(struct uath_softc *sc, uint32_t code, const void *data, int len, int flags) { flags &= ~UATH_CMD_FLAG_READ; return uath_cmdsend(sc, code, data, len, NULL, 0, flags); } static struct uath_cmd * uath_get_cmdbuf(struct uath_softc *sc) { struct uath_cmd *uc; UATH_ASSERT_LOCKED(sc); uc = STAILQ_FIRST(&sc->sc_cmd_inactive); if (uc != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_cmd_inactive, next); UATH_STAT_DEC(sc, st_cmd_inactive); } else uc = NULL; if (uc == NULL) DPRINTF(sc, UATH_DEBUG_XMIT, "%s: %s\n", __func__, "out of command xmit buffers"); return (uc); } /* * This function is called periodically (every second) when associated to * query device statistics. */ static void uath_stat(void *arg) { struct uath_softc *sc = arg; int error; UATH_LOCK(sc); /* * Send request for statistics asynchronously. The timer will be * restarted when we'll get the stats notification. */ error = uath_cmd_write(sc, WDCMSG_TARGET_GET_STATS, NULL, 0, UATH_CMD_FLAG_ASYNC); if (error != 0) { device_printf(sc->sc_dev, "could not query stats, error %d\n", error); } UATH_UNLOCK(sc); } static int uath_get_capability(struct uath_softc *sc, uint32_t cap, uint32_t *val) { int error; cap = htobe32(cap); error = uath_cmd_read(sc, WDCMSG_TARGET_GET_CAPABILITY, &cap, sizeof cap, val, sizeof(uint32_t), UATH_CMD_FLAG_MAGIC); if (error != 0) { device_printf(sc->sc_dev, "could not read capability %u\n", be32toh(cap)); return (error); } *val = be32toh(*val); return (error); } static int uath_get_devcap(struct uath_softc *sc) { #define GETCAP(x, v) do { \ error = uath_get_capability(sc, x, &v); \ if (error != 0) \ return (error); \ DPRINTF(sc, UATH_DEBUG_DEVCAP, \ "%s: %s=0x%08x\n", __func__, #x, v); \ } while (0) struct uath_devcap *cap = &sc->sc_devcap; int error; /* collect device capabilities */ GETCAP(CAP_TARGET_VERSION, cap->targetVersion); GETCAP(CAP_TARGET_REVISION, cap->targetRevision); GETCAP(CAP_MAC_VERSION, cap->macVersion); GETCAP(CAP_MAC_REVISION, cap->macRevision); GETCAP(CAP_PHY_REVISION, cap->phyRevision); GETCAP(CAP_ANALOG_5GHz_REVISION, cap->analog5GhzRevision); GETCAP(CAP_ANALOG_2GHz_REVISION, cap->analog2GhzRevision); GETCAP(CAP_REG_DOMAIN, cap->regDomain); GETCAP(CAP_REG_CAP_BITS, cap->regCapBits); #if 0 /* NB: not supported in rev 1.5 */ GETCAP(CAP_COUNTRY_CODE, cap->countryCode); #endif GETCAP(CAP_WIRELESS_MODES, cap->wirelessModes); GETCAP(CAP_CHAN_SPREAD_SUPPORT, cap->chanSpreadSupport); GETCAP(CAP_COMPRESS_SUPPORT, cap->compressSupport); GETCAP(CAP_BURST_SUPPORT, cap->burstSupport); GETCAP(CAP_FAST_FRAMES_SUPPORT, cap->fastFramesSupport); GETCAP(CAP_CHAP_TUNING_SUPPORT, cap->chapTuningSupport); GETCAP(CAP_TURBOG_SUPPORT, cap->turboGSupport); GETCAP(CAP_TURBO_PRIME_SUPPORT, cap->turboPrimeSupport); GETCAP(CAP_DEVICE_TYPE, cap->deviceType); GETCAP(CAP_WME_SUPPORT, cap->wmeSupport); GETCAP(CAP_TOTAL_QUEUES, cap->numTxQueues); GETCAP(CAP_CONNECTION_ID_MAX, cap->connectionIdMax); GETCAP(CAP_LOW_5GHZ_CHAN, cap->low5GhzChan); GETCAP(CAP_HIGH_5GHZ_CHAN, cap->high5GhzChan); GETCAP(CAP_LOW_2GHZ_CHAN, cap->low2GhzChan); GETCAP(CAP_HIGH_2GHZ_CHAN, cap->high2GhzChan); GETCAP(CAP_TWICE_ANTENNAGAIN_5G, cap->twiceAntennaGain5G); GETCAP(CAP_TWICE_ANTENNAGAIN_2G, cap->twiceAntennaGain2G); GETCAP(CAP_CIPHER_AES_CCM, cap->supportCipherAES_CCM); GETCAP(CAP_CIPHER_TKIP, cap->supportCipherTKIP); GETCAP(CAP_MIC_TKIP, cap->supportMicTKIP); cap->supportCipherWEP = 1; /* NB: always available */ return (0); } static int uath_get_devstatus(struct uath_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) { int error; /* retrieve MAC address */ error = uath_get_status(sc, ST_MAC_ADDR, macaddr, IEEE80211_ADDR_LEN); if (error != 0) { device_printf(sc->sc_dev, "could not read MAC address\n"); return (error); } error = uath_get_status(sc, ST_SERIAL_NUMBER, &sc->sc_serial[0], sizeof(sc->sc_serial)); if (error != 0) { device_printf(sc->sc_dev, "could not read device serial number\n"); return (error); } return (0); } static int uath_get_status(struct uath_softc *sc, uint32_t which, void *odata, int olen) { int error; which = htobe32(which); error = uath_cmd_read(sc, WDCMSG_TARGET_GET_STATUS, &which, sizeof(which), odata, olen, UATH_CMD_FLAG_MAGIC); if (error != 0) device_printf(sc->sc_dev, "could not read EEPROM offset 0x%02x\n", be32toh(which)); return (error); } static void uath_free_data_list(struct uath_softc *sc, struct uath_data data[], int ndata, int fillmbuf) { int i; for (i = 0; i < ndata; i++) { struct uath_data *dp = &data[i]; if (fillmbuf == 1) { if (dp->m != NULL) { m_freem(dp->m); dp->m = NULL; dp->buf = NULL; } } else { dp->buf = NULL; } if (dp->ni != NULL) { ieee80211_free_node(dp->ni); dp->ni = NULL; } } } static int uath_alloc_data_list(struct uath_softc *sc, struct uath_data data[], int ndata, int maxsz, void *dma_buf) { int i, error; for (i = 0; i < ndata; i++) { struct uath_data *dp = &data[i]; dp->sc = sc; if (dma_buf == NULL) { /* XXX check maxsz */ dp->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (dp->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } dp->buf = mtod(dp->m, uint8_t *); } else { dp->m = NULL; dp->buf = ((uint8_t *)dma_buf) + (i * maxsz); } dp->ni = NULL; } return (0); fail: uath_free_data_list(sc, data, ndata, 1 /* free mbufs */); return (error); } static int uath_alloc_rx_data_list(struct uath_softc *sc) { int error, i; /* XXX is it enough to store the RX packet with MCLBYTES bytes? */ error = uath_alloc_data_list(sc, sc->sc_rx, UATH_RX_DATA_LIST_COUNT, MCLBYTES, NULL /* setup mbufs */); if (error != 0) return (error); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) { STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); UATH_STAT_INC(sc, st_rx_inactive); } return (0); } static int uath_alloc_tx_data_list(struct uath_softc *sc) { int error, i; error = uath_alloc_data_list(sc, sc->sc_tx, UATH_TX_DATA_LIST_COUNT, UATH_MAX_TXBUFSZ, sc->sc_tx_dma_buf); if (error != 0) return (error); STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); for (i = 0; i < UATH_TX_DATA_LIST_COUNT; i++) { STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next); UATH_STAT_INC(sc, st_tx_inactive); } return (0); } static void uath_free_rx_data_list(struct uath_softc *sc) { uath_free_data_list(sc, sc->sc_rx, UATH_RX_DATA_LIST_COUNT, 1 /* free mbufs */); } static void uath_free_tx_data_list(struct uath_softc *sc) { uath_free_data_list(sc, sc->sc_tx, UATH_TX_DATA_LIST_COUNT, 0 /* no mbufs */); } static struct ieee80211vap * uath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct uath_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return (NULL); uvp = malloc(sizeof(struct uath_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { /* out of memory */ free(uvp, M_80211_VAP); return (NULL); } /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = uath_newstate; /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return (vap); } static void uath_vap_delete(struct ieee80211vap *vap) { struct uath_vap *uvp = UATH_VAP(vap); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static int uath_init(struct uath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t val; int error; UATH_ASSERT_LOCKED(sc); if (sc->sc_flags & UATH_FLAG_INITDONE) uath_stop(sc); /* reset variables */ sc->sc_intrx_nextnum = sc->sc_msgid = 0; val = htobe32(0); uath_cmd_write(sc, WDCMSG_BIND, &val, sizeof val, 0); /* set MAC address */ uath_config_multi(sc, CFG_MAC_ADDR, vap ? vap->iv_myaddr : ic->ic_macaddr, IEEE80211_ADDR_LEN); /* XXX honor net80211 state */ uath_config(sc, CFG_RATE_CONTROL_ENABLE, 0x00000001); uath_config(sc, CFG_DIVERSITY_CTL, 0x00000001); uath_config(sc, CFG_ABOLT, 0x0000003f); uath_config(sc, CFG_WME_ENABLED, 0x00000001); uath_config(sc, CFG_SERVICE_TYPE, 1); uath_config(sc, CFG_TP_SCALE, 0x00000000); uath_config(sc, CFG_TPC_HALF_DBM5, 0x0000003c); uath_config(sc, CFG_TPC_HALF_DBM2, 0x0000003c); uath_config(sc, CFG_OVERRD_TX_POWER, 0x00000000); uath_config(sc, CFG_GMODE_PROTECTION, 0x00000000); uath_config(sc, CFG_GMODE_PROTECT_RATE_INDEX, 0x00000003); uath_config(sc, CFG_PROTECTION_TYPE, 0x00000000); uath_config(sc, CFG_MODE_CTS, 0x00000002); error = uath_cmd_read(sc, WDCMSG_TARGET_START, NULL, 0, &val, sizeof(val), UATH_CMD_FLAG_MAGIC); if (error) { device_printf(sc->sc_dev, "could not start target, error %d\n", error); goto fail; } DPRINTF(sc, UATH_DEBUG_INIT, "%s returns handle: 0x%x\n", uath_codename(WDCMSG_TARGET_START), be32toh(val)); /* set default channel */ error = uath_switch_channel(sc, ic->ic_curchan); if (error) { device_printf(sc->sc_dev, "could not switch channel, error %d\n", error); goto fail; } val = htobe32(TARGET_DEVICE_AWAKE); uath_cmd_write(sc, WDCMSG_SET_PWR_MODE, &val, sizeof val, 0); /* XXX? check */ uath_cmd_write(sc, WDCMSG_RESET_KEY_CACHE, NULL, 0, 0); usbd_transfer_start(sc->sc_xfer[UATH_BULK_RX]); /* enable Rx */ uath_set_rxfilter(sc, 0x0, UATH_FILTER_OP_INIT); uath_set_rxfilter(sc, UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST | UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON, UATH_FILTER_OP_SET); sc->sc_flags |= UATH_FLAG_INITDONE; callout_reset(&sc->watchdog_ch, hz, uath_watchdog, sc); return (0); fail: uath_stop(sc); return (error); } static void uath_stop(struct uath_softc *sc) { UATH_ASSERT_LOCKED(sc); sc->sc_flags &= ~UATH_FLAG_INITDONE; callout_stop(&sc->stat_ch); callout_stop(&sc->watchdog_ch); sc->sc_tx_timer = 0; /* abort pending transmits */ uath_abort_xfers(sc); /* flush data & control requests into the target */ (void)uath_flush(sc); /* set a LED status to the disconnected. */ uath_set_ledstate(sc, 0); /* stop the target */ uath_cmd_write(sc, WDCMSG_TARGET_STOP, NULL, 0, 0); } static int uath_config(struct uath_softc *sc, uint32_t reg, uint32_t val) { struct uath_write_mac write; int error; write.reg = htobe32(reg); write.len = htobe32(0); /* 0 = single write */ *(uint32_t *)write.data = htobe32(val); error = uath_cmd_write(sc, WDCMSG_TARGET_SET_CONFIG, &write, 3 * sizeof (uint32_t), 0); if (error != 0) { device_printf(sc->sc_dev, "could not write register 0x%02x\n", reg); } return (error); } static int uath_config_multi(struct uath_softc *sc, uint32_t reg, const void *data, int len) { struct uath_write_mac write; int error; write.reg = htobe32(reg); write.len = htobe32(len); bcopy(data, write.data, len); /* properly handle the case where len is zero (reset) */ error = uath_cmd_write(sc, WDCMSG_TARGET_SET_CONFIG, &write, (len == 0) ? sizeof (uint32_t) : 2 * sizeof (uint32_t) + len, 0); if (error != 0) { device_printf(sc->sc_dev, "could not write %d bytes to register 0x%02x\n", len, reg); } return (error); } static int uath_switch_channel(struct uath_softc *sc, struct ieee80211_channel *c) { int error; UATH_ASSERT_LOCKED(sc); /* set radio frequency */ error = uath_set_chan(sc, c); if (error) { device_printf(sc->sc_dev, "could not set channel, error %d\n", error); goto failed; } /* reset Tx rings */ error = uath_reset_tx_queues(sc); if (error) { device_printf(sc->sc_dev, "could not reset Tx queues, error %d\n", error); goto failed; } /* set Tx rings WME properties */ error = uath_wme_init(sc); if (error) { device_printf(sc->sc_dev, "could not init Tx queues, error %d\n", error); goto failed; } error = uath_set_ledstate(sc, 0); if (error) { device_printf(sc->sc_dev, "could not set led state, error %d\n", error); goto failed; } error = uath_flush(sc); if (error) { device_printf(sc->sc_dev, "could not flush pipes, error %d\n", error); goto failed; } failed: return (error); } static int uath_set_rxfilter(struct uath_softc *sc, uint32_t bits, uint32_t op) { struct uath_cmd_rx_filter rxfilter; rxfilter.bits = htobe32(bits); rxfilter.op = htobe32(op); DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "setting Rx filter=0x%x flags=0x%x\n", bits, op); return uath_cmd_write(sc, WDCMSG_RX_FILTER, &rxfilter, sizeof rxfilter, 0); } static void uath_watchdog(void *arg) { struct uath_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "device timeout\n"); - /*uath_init(sc); XXX needs a process context! */ counter_u64_add(ic->ic_oerrors, 1); + ieee80211_restart_all(ic); return; } callout_reset(&sc->watchdog_ch, hz, uath_watchdog, sc); } } static void uath_abort_xfers(struct uath_softc *sc) { int i; UATH_ASSERT_LOCKED(sc); /* abort any pending transfers */ for (i = 0; i < UATH_N_XFERS; i++) usbd_transfer_stop(sc->sc_xfer[i]); } static int uath_flush(struct uath_softc *sc) { int error; error = uath_dataflush(sc); if (error != 0) goto failed; error = uath_cmdflush(sc); if (error != 0) goto failed; failed: return (error); } static int uath_cmdflush(struct uath_softc *sc) { return uath_cmd_write(sc, WDCMSG_FLUSH, NULL, 0, 0); } static int uath_dataflush(struct uath_softc *sc) { struct uath_data *data; struct uath_chunk *chunk; struct uath_tx_desc *desc; UATH_ASSERT_LOCKED(sc); data = uath_getbuf(sc); if (data == NULL) return (ENOBUFS); data->buflen = sizeof(struct uath_chunk) + sizeof(struct uath_tx_desc); data->m = NULL; data->ni = NULL; chunk = (struct uath_chunk *)data->buf; desc = (struct uath_tx_desc *)(chunk + 1); /* one chunk only */ chunk->seqnum = 0; chunk->flags = UATH_CFLAGS_FINAL; chunk->length = htobe16(sizeof (struct uath_tx_desc)); memset(desc, 0, sizeof(struct uath_tx_desc)); desc->msglen = htobe32(sizeof(struct uath_tx_desc)); desc->msgid = (sc->sc_msgid++) + 1; /* don't care about endianness */ desc->type = htobe32(WDCMSG_FLUSH); desc->txqid = htobe32(0); desc->connid = htobe32(0); desc->flags = htobe32(0); #ifdef UATH_DEBUG if (sc->sc_debug & UATH_DEBUG_CMDS) { DPRINTF(sc, UATH_DEBUG_RESET, "send flush ix %d\n", desc->msgid); if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP) uath_dump_cmd(data->buf, data->buflen, '+'); } #endif STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); UATH_STAT_INC(sc, st_tx_pending); sc->sc_tx_timer = 5; usbd_transfer_start(sc->sc_xfer[UATH_BULK_TX]); return (0); } static struct uath_data * _uath_getbuf(struct uath_softc *sc) { struct uath_data *bf; bf = STAILQ_FIRST(&sc->sc_tx_inactive); if (bf != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); UATH_STAT_DEC(sc, st_tx_inactive); } else bf = NULL; if (bf == NULL) DPRINTF(sc, UATH_DEBUG_XMIT, "%s: %s\n", __func__, "out of xmit buffers"); return (bf); } static struct uath_data * uath_getbuf(struct uath_softc *sc) { struct uath_data *bf; UATH_ASSERT_LOCKED(sc); bf = _uath_getbuf(sc); if (bf == NULL) DPRINTF(sc, UATH_DEBUG_XMIT, "%s: stop queue\n", __func__); return (bf); } static int uath_set_ledstate(struct uath_softc *sc, int connected) { DPRINTF(sc, UATH_DEBUG_LED, "set led state %sconnected\n", connected ? "" : "!"); connected = htobe32(connected); return uath_cmd_write(sc, WDCMSG_SET_LED_STATE, &connected, sizeof connected, 0); } static int uath_set_chan(struct uath_softc *sc, struct ieee80211_channel *c) { #ifdef UATH_DEBUG struct ieee80211com *ic = &sc->sc_ic; #endif struct uath_cmd_reset reset; memset(&reset, 0, sizeof(reset)); if (IEEE80211_IS_CHAN_2GHZ(c)) reset.flags |= htobe32(UATH_CHAN_2GHZ); if (IEEE80211_IS_CHAN_5GHZ(c)) reset.flags |= htobe32(UATH_CHAN_5GHZ); /* NB: 11g =>'s 11b so don't specify both OFDM and CCK */ if (IEEE80211_IS_CHAN_OFDM(c)) reset.flags |= htobe32(UATH_CHAN_OFDM); else if (IEEE80211_IS_CHAN_CCK(c)) reset.flags |= htobe32(UATH_CHAN_CCK); /* turbo can be used in either 2GHz or 5GHz */ if (c->ic_flags & IEEE80211_CHAN_TURBO) reset.flags |= htobe32(UATH_CHAN_TURBO); reset.freq = htobe32(c->ic_freq); reset.maxrdpower = htobe32(50); /* XXX */ reset.channelchange = htobe32(1); reset.keeprccontent = htobe32(0); DPRINTF(sc, UATH_DEBUG_CHANNEL, "set channel %d, flags 0x%x freq %u\n", ieee80211_chan2ieee(ic, c), be32toh(reset.flags), be32toh(reset.freq)); return uath_cmd_write(sc, WDCMSG_RESET, &reset, sizeof reset, 0); } static int uath_reset_tx_queues(struct uath_softc *sc) { int ac, error; DPRINTF(sc, UATH_DEBUG_RESET, "%s: reset Tx queues\n", __func__); for (ac = 0; ac < 4; ac++) { const uint32_t qid = htobe32(ac); error = uath_cmd_write(sc, WDCMSG_RELEASE_TX_QUEUE, &qid, sizeof qid, 0); if (error != 0) break; } return (error); } static int uath_wme_init(struct uath_softc *sc) { /* XXX get from net80211 */ static const struct uath_wme_settings uath_wme_11g[4] = { { 7, 4, 10, 0, 0 }, /* Background */ { 3, 4, 10, 0, 0 }, /* Best-Effort */ { 3, 3, 4, 26, 0 }, /* Video */ { 2, 2, 3, 47, 0 } /* Voice */ }; struct uath_cmd_txq_setup qinfo; int ac, error; DPRINTF(sc, UATH_DEBUG_WME, "%s: setup Tx queues\n", __func__); for (ac = 0; ac < 4; ac++) { qinfo.qid = htobe32(ac); qinfo.len = htobe32(sizeof(qinfo.attr)); qinfo.attr.priority = htobe32(ac); /* XXX */ qinfo.attr.aifs = htobe32(uath_wme_11g[ac].aifsn); qinfo.attr.logcwmin = htobe32(uath_wme_11g[ac].logcwmin); qinfo.attr.logcwmax = htobe32(uath_wme_11g[ac].logcwmax); qinfo.attr.bursttime = htobe32(IEEE80211_TXOP_TO_US( uath_wme_11g[ac].txop)); qinfo.attr.mode = htobe32(uath_wme_11g[ac].acm);/*XXX? */ qinfo.attr.qflags = htobe32(1); /* XXX? */ error = uath_cmd_write(sc, WDCMSG_SETUP_TX_QUEUE, &qinfo, sizeof qinfo, 0); if (error != 0) break; } return (error); } static void uath_parent(struct ieee80211com *ic) { struct uath_softc *sc = ic->ic_softc; int startall = 0; UATH_LOCK(sc); if (sc->sc_flags & UATH_FLAG_INVALID) { UATH_UNLOCK(sc); return; } if (ic->ic_nrunning > 0) { if (!(sc->sc_flags & UATH_FLAG_INITDONE)) { uath_init(sc); startall = 1; } } else if (sc->sc_flags & UATH_FLAG_INITDONE) uath_stop(sc); UATH_UNLOCK(sc); if (startall) ieee80211_start_all(ic); } static int uath_tx_start(struct uath_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, struct uath_data *data) { struct ieee80211vap *vap = ni->ni_vap; struct uath_chunk *chunk; struct uath_tx_desc *desc; const struct ieee80211_frame *wh; struct ieee80211_key *k; int framelen, msglen; UATH_ASSERT_LOCKED(sc); data->ni = ni; data->m = m0; chunk = (struct uath_chunk *)data->buf; desc = (struct uath_tx_desc *)(chunk + 1); if (ieee80211_radiotap_active_vap(vap)) { struct uath_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; if (m0->m_flags & M_FRAG) tap->wt_flags |= IEEE80211_RADIOTAP_F_FRAG; ieee80211_radiotap_tx(vap, m0); } wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return (ENOBUFS); } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(desc + 1)); framelen = m0->m_pkthdr.len + IEEE80211_CRC_LEN; msglen = framelen + sizeof (struct uath_tx_desc); data->buflen = msglen + sizeof (struct uath_chunk); /* one chunk only for now */ chunk->seqnum = sc->sc_seqnum++; chunk->flags = (m0->m_flags & M_FRAG) ? 0 : UATH_CFLAGS_FINAL; if (m0->m_flags & M_LASTFRAG) chunk->flags |= UATH_CFLAGS_FINAL; chunk->flags = UATH_CFLAGS_FINAL; chunk->length = htobe16(msglen); /* fill Tx descriptor */ desc->msglen = htobe32(msglen); /* NB: to get UATH_TX_NOTIFY reply, `msgid' must be larger than 0 */ desc->msgid = (sc->sc_msgid++) + 1; /* don't care about endianness */ desc->type = htobe32(WDCMSG_SEND); switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { case IEEE80211_FC0_TYPE_CTL: case IEEE80211_FC0_TYPE_MGT: /* NB: force all management frames to highest queue */ if (ni->ni_flags & IEEE80211_NODE_QOS) { /* NB: force all management frames to highest queue */ desc->txqid = htobe32(WME_AC_VO | UATH_TXQID_MINRATE); } else desc->txqid = htobe32(WME_AC_BE | UATH_TXQID_MINRATE); break; case IEEE80211_FC0_TYPE_DATA: /* XXX multicast frames should honor mcastrate */ desc->txqid = htobe32(M_WME_GETAC(m0)); break; default: device_printf(sc->sc_dev, "bogus frame type 0x%x (%s)\n", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__); m_freem(m0); return (EIO); } if (vap->iv_state == IEEE80211_S_AUTH || vap->iv_state == IEEE80211_S_ASSOC || vap->iv_state == IEEE80211_S_RUN) desc->connid = htobe32(UATH_ID_BSS); else desc->connid = htobe32(UATH_ID_INVALID); desc->flags = htobe32(0 /* no UATH_TX_NOTIFY */); desc->buflen = htobe32(m0->m_pkthdr.len); #ifdef UATH_DEBUG DPRINTF(sc, UATH_DEBUG_XMIT, "send frame ix %u framelen %d msglen %d connid 0x%x txqid 0x%x\n", desc->msgid, framelen, msglen, be32toh(desc->connid), be32toh(desc->txqid)); if (sc->sc_debug & UATH_DEBUG_XMIT_DUMP) uath_dump_cmd(data->buf, data->buflen, '+'); #endif STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); UATH_STAT_INC(sc, st_tx_pending); usbd_transfer_start(sc->sc_xfer[UATH_BULK_TX]); return (0); } /* * Cleanup driver resources when we run out of buffers while processing * fragments; return the tx buffers allocated and drop node references. */ static void uath_txfrag_cleanup(struct uath_softc *sc, uath_datahead *frags, struct ieee80211_node *ni) { struct uath_data *bf, *next; UATH_ASSERT_LOCKED(sc); STAILQ_FOREACH_SAFE(bf, frags, next, next) { /* NB: bf assumed clean */ STAILQ_REMOVE_HEAD(frags, next); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); UATH_STAT_INC(sc, st_tx_inactive); ieee80211_node_decref(ni); } } /* * Setup xmit of a fragmented frame. Allocate a buffer for each frag and bump * the node reference count to reflect the held reference to be setup by * uath_tx_start. */ static int uath_txfrag_setup(struct uath_softc *sc, uath_datahead *frags, struct mbuf *m0, struct ieee80211_node *ni) { struct mbuf *m; struct uath_data *bf; UATH_ASSERT_LOCKED(sc); for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) { bf = uath_getbuf(sc); if (bf == NULL) { /* out of buffers, cleanup */ uath_txfrag_cleanup(sc, frags, ni); break; } ieee80211_node_incref(ni); STAILQ_INSERT_TAIL(frags, bf, next); } return !STAILQ_EMPTY(frags); } static int uath_transmit(struct ieee80211com *ic, struct mbuf *m) { struct uath_softc *sc = ic->ic_softc; int error; UATH_LOCK(sc); if ((sc->sc_flags & UATH_FLAG_INITDONE) == 0) { UATH_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { UATH_UNLOCK(sc); return (error); } uath_start(sc); UATH_UNLOCK(sc); return (0); } static void uath_start(struct uath_softc *sc) { struct uath_data *bf; struct ieee80211_node *ni; struct mbuf *m, *next; uath_datahead frags; UATH_ASSERT_LOCKED(sc); if ((sc->sc_flags & UATH_FLAG_INITDONE) == 0 || (sc->sc_flags & UATH_FLAG_INVALID)) return; while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { bf = uath_getbuf(sc); if (bf == NULL) { mbufq_prepend(&sc->sc_snd, m); break; } ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; /* * Check for fragmentation. If this frame has been broken up * verify we have enough buffers to send all the fragments * so all go out or none... */ STAILQ_INIT(&frags); if ((m->m_flags & M_FRAG) && !uath_txfrag_setup(sc, &frags, m, ni)) { DPRINTF(sc, UATH_DEBUG_XMIT, "%s: out of txfrag buffers\n", __func__); ieee80211_free_mbuf(m); goto bad; } sc->sc_seqnum = 0; nextfrag: /* * Pass the frame to the h/w for transmission. * Fragmented frames have each frag chained together * with m_nextpkt. We know there are sufficient uath_data's * to send all the frags because of work done by * uath_txfrag_setup. */ next = m->m_nextpkt; if (uath_tx_start(sc, m, ni, bf) != 0) { bad: if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); reclaim: STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); UATH_STAT_INC(sc, st_tx_inactive); uath_txfrag_cleanup(sc, &frags, ni); ieee80211_free_node(ni); continue; } if (next != NULL) { /* * Beware of state changing between frags. XXX check sta power-save state? */ if (ni->ni_vap->iv_state != IEEE80211_S_RUN) { DPRINTF(sc, UATH_DEBUG_XMIT, "%s: flush fragmented packet, state %s\n", __func__, ieee80211_state_name[ni->ni_vap->iv_state]); ieee80211_free_mbuf(next); goto reclaim; } m = next; bf = STAILQ_FIRST(&frags); KASSERT(bf != NULL, ("no buf for txfrag")); STAILQ_REMOVE_HEAD(&frags, next); goto nextfrag; } sc->sc_tx_timer = 5; } } static int uath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct uath_data *bf; struct uath_softc *sc = ic->ic_softc; UATH_LOCK(sc); /* prevent management frames from being sent if we're not ready */ if ((sc->sc_flags & UATH_FLAG_INVALID) || !(sc->sc_flags & UATH_FLAG_INITDONE)) { m_freem(m); UATH_UNLOCK(sc); return (ENETDOWN); } /* grab a TX buffer */ bf = uath_getbuf(sc); if (bf == NULL) { m_freem(m); UATH_UNLOCK(sc); return (ENOBUFS); } sc->sc_seqnum = 0; if (uath_tx_start(sc, m, ni, bf) != 0) { STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); UATH_STAT_INC(sc, st_tx_inactive); UATH_UNLOCK(sc); return (EIO); } UATH_UNLOCK(sc); sc->sc_tx_timer = 5; return (0); } static void uath_scan_start(struct ieee80211com *ic) { /* do nothing */ } static void uath_scan_end(struct ieee80211com *ic) { /* do nothing */ } static void uath_set_channel(struct ieee80211com *ic) { struct uath_softc *sc = ic->ic_softc; UATH_LOCK(sc); if ((sc->sc_flags & UATH_FLAG_INVALID) || (sc->sc_flags & UATH_FLAG_INITDONE) == 0) { UATH_UNLOCK(sc); return; } (void)uath_switch_channel(sc, ic->ic_curchan); UATH_UNLOCK(sc); } static int uath_set_rxmulti_filter(struct uath_softc *sc) { /* XXX broken */ return (0); } static void uath_update_mcast(struct ieee80211com *ic) { struct uath_softc *sc = ic->ic_softc; UATH_LOCK(sc); if ((sc->sc_flags & UATH_FLAG_INVALID) || (sc->sc_flags & UATH_FLAG_INITDONE) == 0) { UATH_UNLOCK(sc); return; } /* * this is for avoiding the race condition when we're try to * connect to the AP with WPA. */ if (sc->sc_flags & UATH_FLAG_INITDONE) (void)uath_set_rxmulti_filter(sc); UATH_UNLOCK(sc); } static void uath_update_promisc(struct ieee80211com *ic) { struct uath_softc *sc = ic->ic_softc; UATH_LOCK(sc); if ((sc->sc_flags & UATH_FLAG_INVALID) || (sc->sc_flags & UATH_FLAG_INITDONE) == 0) { UATH_UNLOCK(sc); return; } if (sc->sc_flags & UATH_FLAG_INITDONE) { uath_set_rxfilter(sc, UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST | UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON | UATH_FILTER_RX_PROM, UATH_FILTER_OP_SET); } UATH_UNLOCK(sc); } static int uath_create_connection(struct uath_softc *sc, uint32_t connid) { const struct ieee80211_rateset *rs; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni; struct uath_cmd_create_connection create; ni = ieee80211_ref_node(vap->iv_bss); memset(&create, 0, sizeof(create)); create.connid = htobe32(connid); create.bssid = htobe32(0); /* XXX packed or not? */ create.size = htobe32(sizeof(struct uath_cmd_rateset)); rs = &ni->ni_rates; create.connattr.rateset.length = rs->rs_nrates; bcopy(rs->rs_rates, &create.connattr.rateset.set[0], rs->rs_nrates); /* XXX turbo */ if (IEEE80211_IS_CHAN_A(ni->ni_chan)) create.connattr.wlanmode = htobe32(WLAN_MODE_11a); else if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) create.connattr.wlanmode = htobe32(WLAN_MODE_11g); else create.connattr.wlanmode = htobe32(WLAN_MODE_11b); ieee80211_free_node(ni); return uath_cmd_write(sc, WDCMSG_CREATE_CONNECTION, &create, sizeof create, 0); } static int uath_set_rates(struct uath_softc *sc, const struct ieee80211_rateset *rs) { struct uath_cmd_rates rates; memset(&rates, 0, sizeof(rates)); rates.connid = htobe32(UATH_ID_BSS); /* XXX */ rates.size = htobe32(sizeof(struct uath_cmd_rateset)); /* XXX bounds check rs->rs_nrates */ rates.rateset.length = rs->rs_nrates; bcopy(rs->rs_rates, &rates.rateset.set[0], rs->rs_nrates); DPRINTF(sc, UATH_DEBUG_RATES, "setting supported rates nrates=%d\n", rs->rs_nrates); return uath_cmd_write(sc, WDCMSG_SET_BASIC_RATE, &rates, sizeof rates, 0); } static int uath_write_associd(struct uath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni; struct uath_cmd_set_associd associd; ni = ieee80211_ref_node(vap->iv_bss); memset(&associd, 0, sizeof(associd)); associd.defaultrateix = htobe32(1); /* XXX */ associd.associd = htobe32(ni->ni_associd); associd.timoffset = htobe32(0x3b); /* XXX */ IEEE80211_ADDR_COPY(associd.bssid, ni->ni_bssid); ieee80211_free_node(ni); return uath_cmd_write(sc, WDCMSG_WRITE_ASSOCID, &associd, sizeof associd, 0); } static int uath_set_ledsteady(struct uath_softc *sc, int lednum, int ledmode) { struct uath_cmd_ledsteady led; led.lednum = htobe32(lednum); led.ledmode = htobe32(ledmode); DPRINTF(sc, UATH_DEBUG_LED, "set %s led %s (steady)\n", (lednum == UATH_LED_LINK) ? "link" : "activity", ledmode ? "on" : "off"); return uath_cmd_write(sc, WDCMSG_SET_LED_STEADY, &led, sizeof led, 0); } static int uath_set_ledblink(struct uath_softc *sc, int lednum, int ledmode, int blinkrate, int slowmode) { struct uath_cmd_ledblink led; led.lednum = htobe32(lednum); led.ledmode = htobe32(ledmode); led.blinkrate = htobe32(blinkrate); led.slowmode = htobe32(slowmode); DPRINTF(sc, UATH_DEBUG_LED, "set %s led %s (blink)\n", (lednum == UATH_LED_LINK) ? "link" : "activity", ledmode ? "on" : "off"); return uath_cmd_write(sc, WDCMSG_SET_LED_BLINK, &led, sizeof led, 0); } static int uath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { enum ieee80211_state ostate = vap->iv_state; int error; struct ieee80211_node *ni; struct ieee80211com *ic = vap->iv_ic; struct uath_softc *sc = ic->ic_softc; struct uath_vap *uvp = UATH_VAP(vap); DPRINTF(sc, UATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); UATH_LOCK(sc); callout_stop(&sc->stat_ch); callout_stop(&sc->watchdog_ch); ni = ieee80211_ref_node(vap->iv_bss); switch (nstate) { case IEEE80211_S_INIT: if (ostate == IEEE80211_S_RUN) { /* turn link and activity LEDs off */ uath_set_ledstate(sc, 0); } break; case IEEE80211_S_SCAN: break; case IEEE80211_S_AUTH: /* XXX good place? set RTS threshold */ uath_config(sc, CFG_USER_RTS_THRESHOLD, vap->iv_rtsthreshold); /* XXX bad place */ error = uath_set_keys(sc, vap); if (error != 0) { device_printf(sc->sc_dev, "could not set crypto keys, error %d\n", error); break; } if (uath_switch_channel(sc, ni->ni_chan) != 0) { device_printf(sc->sc_dev, "could not switch channel\n"); break; } if (uath_create_connection(sc, UATH_ID_BSS) != 0) { device_printf(sc->sc_dev, "could not create connection\n"); break; } break; case IEEE80211_S_ASSOC: if (uath_set_rates(sc, &ni->ni_rates) != 0) { device_printf(sc->sc_dev, "could not set negotiated rate set\n"); break; } break; case IEEE80211_S_RUN: /* XXX monitor mode doesn't be tested */ if (ic->ic_opmode == IEEE80211_M_MONITOR) { uath_set_ledstate(sc, 1); break; } /* * Tx rate is controlled by firmware, report the maximum * negotiated rate in ifconfig output. */ ni->ni_txrate = ni->ni_rates.rs_rates[ni->ni_rates.rs_nrates-1]; if (uath_write_associd(sc) != 0) { device_printf(sc->sc_dev, "could not write association id\n"); break; } /* turn link LED on */ uath_set_ledsteady(sc, UATH_LED_LINK, UATH_LED_ON); /* make activity LED blink */ uath_set_ledblink(sc, UATH_LED_ACTIVITY, UATH_LED_ON, 1, 2); /* set state to associated */ uath_set_ledstate(sc, 1); /* start statistics timer */ callout_reset(&sc->stat_ch, hz, uath_stat, sc); break; default: break; } ieee80211_free_node(ni); UATH_UNLOCK(sc); IEEE80211_LOCK(ic); return (uvp->newstate(vap, nstate, arg)); } static int uath_set_key(struct uath_softc *sc, const struct ieee80211_key *wk, int index) { #if 0 struct uath_cmd_crypto crypto; int i; memset(&crypto, 0, sizeof(crypto)); crypto.keyidx = htobe32(index); crypto.magic1 = htobe32(1); crypto.size = htobe32(368); crypto.mask = htobe32(0xffff); crypto.flags = htobe32(0x80000068); if (index != UATH_DEFAULT_KEY) crypto.flags |= htobe32(index << 16); memset(crypto.magic2, 0xff, sizeof(crypto.magic2)); /* * Each byte of the key must be XOR'ed with 10101010 before being * transmitted to the firmware. */ for (i = 0; i < wk->wk_keylen; i++) crypto.key[i] = wk->wk_key[i] ^ 0xaa; DPRINTF(sc, UATH_DEBUG_CRYPTO, "setting crypto key index=%d len=%d\n", index, wk->wk_keylen); return uath_cmd_write(sc, WDCMSG_SET_KEY_CACHE_ENTRY, &crypto, sizeof crypto, 0); #else /* XXX support H/W cryto */ return (0); #endif } static int uath_set_keys(struct uath_softc *sc, struct ieee80211vap *vap) { int i, error; error = 0; for (i = 0; i < IEEE80211_WEP_NKID; i++) { const struct ieee80211_key *wk = &vap->iv_nw_keys[i]; if (wk->wk_flags & (IEEE80211_KEY_XMIT|IEEE80211_KEY_RECV)) { error = uath_set_key(sc, wk, i); if (error) return (error); } } if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) { error = uath_set_key(sc, &vap->iv_nw_keys[vap->iv_def_txkey], UATH_DEFAULT_KEY); } return (error); } #define UATH_SYSCTL_STAT_ADD32(c, h, n, p, d) \ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) static void uath_sysctl_node(struct uath_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *child; struct sysctl_oid *tree; struct uath_stat *stats; stats = &sc->sc_stat; ctx = device_get_sysctl_ctx(sc->sc_dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, NULL, "UATH statistics"); child = SYSCTL_CHILDREN(tree); UATH_SYSCTL_STAT_ADD32(ctx, child, "badchunkseqnum", &stats->st_badchunkseqnum, "Bad chunk sequence numbers"); UATH_SYSCTL_STAT_ADD32(ctx, child, "invalidlen", &stats->st_invalidlen, "Invalid length"); UATH_SYSCTL_STAT_ADD32(ctx, child, "multichunk", &stats->st_multichunk, "Multi chunks"); UATH_SYSCTL_STAT_ADD32(ctx, child, "toobigrxpkt", &stats->st_toobigrxpkt, "Too big rx packets"); UATH_SYSCTL_STAT_ADD32(ctx, child, "stopinprogress", &stats->st_stopinprogress, "Stop in progress"); UATH_SYSCTL_STAT_ADD32(ctx, child, "crcerrs", &stats->st_crcerr, "CRC errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "phyerr", &stats->st_phyerr, "PHY errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "decrypt_crcerr", &stats->st_decrypt_crcerr, "Decryption CRC errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "decrypt_micerr", &stats->st_decrypt_micerr, "Decryption Misc errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "decomperr", &stats->st_decomperr, "Decomp errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "keyerr", &stats->st_keyerr, "Key errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "err", &stats->st_err, "Unknown errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_active", &stats->st_cmd_active, "Active numbers in Command queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_inactive", &stats->st_cmd_inactive, "Inactive numbers in Command queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_pending", &stats->st_cmd_pending, "Pending numbers in Command queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_waiting", &stats->st_cmd_waiting, "Waiting numbers in Command queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "rx_active", &stats->st_rx_active, "Active numbers in RX queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "rx_inactive", &stats->st_rx_inactive, "Inactive numbers in RX queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_active", &stats->st_tx_active, "Active numbers in TX queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_inactive", &stats->st_tx_inactive, "Inactive numbers in TX queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_pending", &stats->st_tx_pending, "Pending numbers in TX queue"); } #undef UATH_SYSCTL_STAT_ADD32 CTASSERT(sizeof(u_int) >= sizeof(uint32_t)); static void uath_cmdeof(struct uath_softc *sc, struct uath_cmd *cmd) { struct uath_cmd_hdr *hdr; uint32_t dlen; hdr = (struct uath_cmd_hdr *)cmd->buf; /* NB: msgid is passed thru w/o byte swapping */ #ifdef UATH_DEBUG if (sc->sc_debug & UATH_DEBUG_CMDS) { uint32_t len = be32toh(hdr->len); printf("%s: %s [ix %u] len %u status %u\n", __func__, uath_codename(be32toh(hdr->code)), hdr->msgid, len, be32toh(hdr->magic)); if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP) uath_dump_cmd(cmd->buf, len > UATH_MAX_CMDSZ ? sizeof(*hdr) : len, '-'); } #endif hdr->code = be32toh(hdr->code); hdr->len = be32toh(hdr->len); hdr->magic = be32toh(hdr->magic); /* target status on return */ switch (hdr->code & 0xff) { /* reply to a read command */ default: DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL, "%s: code %d hdr len %u\n", __func__, hdr->code & 0xff, hdr->len); /* * The first response from the target after the * HOST_AVAILABLE has an invalid msgid so we must * treat it specially. */ if (hdr->msgid < UATH_CMD_LIST_COUNT) { uint32_t *rp = (uint32_t *)(hdr+1); u_int olen; if (sizeof(*hdr) > hdr->len || hdr->len >= UATH_MAX_CMDSZ) { device_printf(sc->sc_dev, "%s: invalid WDC msg length %u; " "msg ignored\n", __func__, hdr->len); return; } /* * Calculate return/receive payload size; the * first word, if present, always gives the * number of bytes--unless it's 0 in which * case a single 32-bit word should be present. */ dlen = hdr->len - sizeof(*hdr); if (dlen >= sizeof(uint32_t)) { olen = be32toh(rp[0]); dlen -= sizeof(uint32_t); if (olen == 0) { /* convention is 0 =>'s one word */ olen = sizeof(uint32_t); /* XXX KASSERT(olen == dlen ) */ } } else olen = 0; if (cmd->odata != NULL) { /* NB: cmd->olen validated in uath_cmd */ if (olen > (u_int)cmd->olen) { /* XXX complain? */ device_printf(sc->sc_dev, "%s: cmd 0x%x olen %u cmd olen %u\n", __func__, hdr->code, olen, cmd->olen); olen = cmd->olen; } if (olen > dlen) { /* XXX complain, shouldn't happen */ device_printf(sc->sc_dev, "%s: cmd 0x%x olen %u dlen %u\n", __func__, hdr->code, olen, dlen); olen = dlen; } /* XXX have submitter do this */ /* copy answer into caller's supplied buffer */ bcopy(&rp[1], cmd->odata, olen); cmd->olen = olen; } } wakeup_one(cmd); /* wake up caller */ break; case WDCMSG_TARGET_START: if (hdr->msgid >= UATH_CMD_LIST_COUNT) { /* XXX */ return; } dlen = hdr->len - sizeof(*hdr); if (dlen != sizeof(uint32_t)) { device_printf(sc->sc_dev, "%s: dlen (%u) != %zu!\n", __func__, dlen, sizeof(uint32_t)); return; } /* XXX have submitter do this */ /* copy answer into caller's supplied buffer */ bcopy(hdr+1, cmd->odata, sizeof(uint32_t)); cmd->olen = sizeof(uint32_t); wakeup_one(cmd); /* wake up caller */ break; case WDCMSG_SEND_COMPLETE: /* this notification is sent when UATH_TX_NOTIFY is set */ DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL, "%s: received Tx notification\n", __func__); break; case WDCMSG_TARGET_GET_STATS: DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL, "%s: received device statistics\n", __func__); callout_reset(&sc->stat_ch, hz, uath_stat, sc); break; } } static void uath_intr_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct uath_softc *sc = usbd_xfer_softc(xfer); struct uath_cmd *cmd; struct uath_cmd_hdr *hdr; struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); UATH_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: cmd = STAILQ_FIRST(&sc->sc_cmd_waiting); if (cmd == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_cmd_waiting, next); UATH_STAT_DEC(sc, st_cmd_waiting); STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, cmd, next); UATH_STAT_INC(sc, st_cmd_inactive); if (actlen < sizeof(struct uath_cmd_hdr)) { device_printf(sc->sc_dev, "%s: short xfer error (actlen %d)\n", __func__, actlen); goto setup; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, cmd->buf, actlen); hdr = (struct uath_cmd_hdr *)cmd->buf; hdr->len = be32toh(hdr->len); if (hdr->len > (uint32_t)actlen) { device_printf(sc->sc_dev, "%s: truncated xfer (len %u, actlen %d)\n", __func__, hdr->len, actlen); goto setup; } uath_cmdeof(sc, cmd); case USB_ST_SETUP: setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); break; default: if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto setup; } break; } } static void uath_intr_tx_callback(struct usb_xfer *xfer, usb_error_t error) { struct uath_softc *sc = usbd_xfer_softc(xfer); struct uath_cmd *cmd; UATH_ASSERT_LOCKED(sc); cmd = STAILQ_FIRST(&sc->sc_cmd_active); if (cmd != NULL && USB_GET_STATE(xfer) != USB_ST_SETUP) { STAILQ_REMOVE_HEAD(&sc->sc_cmd_active, next); UATH_STAT_DEC(sc, st_cmd_active); STAILQ_INSERT_TAIL((cmd->flags & UATH_CMD_FLAG_READ) ? &sc->sc_cmd_waiting : &sc->sc_cmd_inactive, cmd, next); if (cmd->flags & UATH_CMD_FLAG_READ) UATH_STAT_INC(sc, st_cmd_waiting); else UATH_STAT_INC(sc, st_cmd_inactive); } switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: setup: cmd = STAILQ_FIRST(&sc->sc_cmd_pending); if (cmd == NULL) { DPRINTF(sc, UATH_DEBUG_XMIT, "%s: empty pending queue\n", __func__); return; } STAILQ_REMOVE_HEAD(&sc->sc_cmd_pending, next); UATH_STAT_DEC(sc, st_cmd_pending); STAILQ_INSERT_TAIL((cmd->flags & UATH_CMD_FLAG_ASYNC) ? &sc->sc_cmd_inactive : &sc->sc_cmd_active, cmd, next); if (cmd->flags & UATH_CMD_FLAG_ASYNC) UATH_STAT_INC(sc, st_cmd_inactive); else UATH_STAT_INC(sc, st_cmd_active); usbd_xfer_set_frame_data(xfer, 0, cmd->buf, cmd->buflen); usbd_transfer_submit(xfer); break; default: if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto setup; } break; } } static void uath_update_rxstat(struct uath_softc *sc, uint32_t status) { switch (status) { case UATH_STATUS_STOP_IN_PROGRESS: UATH_STAT_INC(sc, st_stopinprogress); break; case UATH_STATUS_CRC_ERR: UATH_STAT_INC(sc, st_crcerr); break; case UATH_STATUS_PHY_ERR: UATH_STAT_INC(sc, st_phyerr); break; case UATH_STATUS_DECRYPT_CRC_ERR: UATH_STAT_INC(sc, st_decrypt_crcerr); break; case UATH_STATUS_DECRYPT_MIC_ERR: UATH_STAT_INC(sc, st_decrypt_micerr); break; case UATH_STATUS_DECOMP_ERR: UATH_STAT_INC(sc, st_decomperr); break; case UATH_STATUS_KEY_ERR: UATH_STAT_INC(sc, st_keyerr); break; case UATH_STATUS_ERR: UATH_STAT_INC(sc, st_err); break; default: break; } } CTASSERT(UATH_MIN_RXBUFSZ >= sizeof(struct uath_chunk)); static struct mbuf * uath_data_rxeof(struct usb_xfer *xfer, struct uath_data *data, struct uath_rx_desc **pdesc) { struct uath_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct uath_chunk *chunk; struct uath_rx_desc *desc; struct mbuf *m = data->m, *mnew, *mp; uint16_t chunklen; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (actlen < (int)UATH_MIN_RXBUFSZ) { DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "%s: wrong xfer size (len=%d)\n", __func__, actlen); counter_u64_add(ic->ic_ierrors, 1); return (NULL); } chunk = (struct uath_chunk *)data->buf; chunklen = be16toh(chunk->length); if (chunk->seqnum == 0 && chunk->flags == 0 && chunklen == 0) { device_printf(sc->sc_dev, "%s: strange response\n", __func__); counter_u64_add(ic->ic_ierrors, 1); UATH_RESET_INTRX(sc); return (NULL); } if (chunklen > actlen) { device_printf(sc->sc_dev, "%s: invalid chunk length (len %u > actlen %d)\n", __func__, chunklen, actlen); counter_u64_add(ic->ic_ierrors, 1); /* XXX cleanup? */ UATH_RESET_INTRX(sc); return (NULL); } if (chunk->seqnum != sc->sc_intrx_nextnum) { DPRINTF(sc, UATH_DEBUG_XMIT, "invalid seqnum %d, expected %d\n", chunk->seqnum, sc->sc_intrx_nextnum); UATH_STAT_INC(sc, st_badchunkseqnum); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } /* check multi-chunk frames */ if ((chunk->seqnum == 0 && !(chunk->flags & UATH_CFLAGS_FINAL)) || (chunk->seqnum != 0 && (chunk->flags & UATH_CFLAGS_FINAL)) || chunk->flags & UATH_CFLAGS_RXMSG) UATH_STAT_INC(sc, st_multichunk); if (chunk->flags & UATH_CFLAGS_FINAL) { if (chunklen < sizeof(struct uath_rx_desc)) { device_printf(sc->sc_dev, "%s: invalid chunk length %d\n", __func__, chunklen); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } chunklen -= sizeof(struct uath_rx_desc); } if (chunklen > 0 && (!(chunk->flags & UATH_CFLAGS_FINAL) || !(chunk->seqnum == 0))) { /* we should use intermediate RX buffer */ if (chunk->seqnum == 0) UATH_RESET_INTRX(sc); if ((sc->sc_intrx_len + sizeof(struct uath_rx_desc) + chunklen) > UATH_MAX_INTRX_SIZE) { UATH_STAT_INC(sc, st_invalidlen); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } m->m_len = chunklen; m->m_data += sizeof(struct uath_chunk); if (sc->sc_intrx_head == NULL) { sc->sc_intrx_head = m; sc->sc_intrx_tail = m; } else { m->m_flags &= ~M_PKTHDR; sc->sc_intrx_tail->m_next = m; sc->sc_intrx_tail = m; } } sc->sc_intrx_len += chunklen; mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) { DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "%s: can't get new mbuf, drop frame\n", __func__); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } data->m = mnew; data->buf = mtod(mnew, uint8_t *); /* if the frame is not final continue the transfer */ if (!(chunk->flags & UATH_CFLAGS_FINAL)) { sc->sc_intrx_nextnum++; UATH_RESET_INTRX(sc); return (NULL); } /* * if the frame is not set UATH_CFLAGS_RXMSG, then rx descriptor is * located at the end, 32-bit aligned */ desc = (chunk->flags & UATH_CFLAGS_RXMSG) ? (struct uath_rx_desc *)(chunk + 1) : (struct uath_rx_desc *)(((uint8_t *)chunk) + sizeof(struct uath_chunk) + be16toh(chunk->length) - sizeof(struct uath_rx_desc)); if ((uint8_t *)chunk + actlen - sizeof(struct uath_rx_desc) < (uint8_t *)desc) { device_printf(sc->sc_dev, "%s: wrong Rx descriptor pointer " "(desc %p chunk %p actlen %d)\n", __func__, desc, chunk, actlen); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } *pdesc = desc; DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "%s: frame len %u code %u status %u rate %u antenna %u " "rssi %d channel %u phyerror %u connix %u decrypterror %u " "keycachemiss %u\n", __func__, be32toh(desc->framelen) , be32toh(desc->code), be32toh(desc->status), be32toh(desc->rate) , be32toh(desc->antenna), be32toh(desc->rssi), be32toh(desc->channel) , be32toh(desc->phyerror), be32toh(desc->connix) , be32toh(desc->decrypterror), be32toh(desc->keycachemiss)); if (be32toh(desc->len) > MCLBYTES) { DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "%s: bad descriptor (len=%d)\n", __func__, be32toh(desc->len)); counter_u64_add(ic->ic_ierrors, 1); UATH_STAT_INC(sc, st_toobigrxpkt); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } uath_update_rxstat(sc, be32toh(desc->status)); /* finalize mbuf */ if (sc->sc_intrx_head == NULL) { uint32_t framelen; if (be32toh(desc->framelen) < UATH_RX_DUMMYSIZE) { device_printf(sc->sc_dev, "%s: framelen too small (%u)\n", __func__, be32toh(desc->framelen)); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } framelen = be32toh(desc->framelen) - UATH_RX_DUMMYSIZE; if (framelen > actlen - sizeof(struct uath_chunk) || framelen < sizeof(struct ieee80211_frame_ack)) { device_printf(sc->sc_dev, "%s: wrong frame length (%u, actlen %d)!\n", __func__, framelen, actlen); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } m->m_pkthdr.len = m->m_len = framelen; m->m_data += sizeof(struct uath_chunk); } else { mp = sc->sc_intrx_head; mp->m_flags |= M_PKTHDR; mp->m_pkthdr.len = sc->sc_intrx_len; m = mp; } /* there are a lot more fields in the RX descriptor */ if ((sc->sc_flags & UATH_FLAG_INVALID) == 0 && ieee80211_radiotap_active(ic)) { struct uath_rx_radiotap_header *tap = &sc->sc_rxtap; uint32_t tsf_hi = be32toh(desc->tstamp_high); uint32_t tsf_lo = be32toh(desc->tstamp_low); /* XXX only get low order 24bits of tsf from h/w */ tap->wr_tsf = htole64(((uint64_t)tsf_hi << 32) | tsf_lo); tap->wr_flags = 0; if (be32toh(desc->status) == UATH_STATUS_CRC_ERR) tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; /* XXX map other status to BADFCS? */ /* XXX ath h/w rate code, need to map */ tap->wr_rate = be32toh(desc->rate); tap->wr_antenna = be32toh(desc->antenna); tap->wr_antsignal = -95 + be32toh(desc->rssi); tap->wr_antnoise = -95; } UATH_RESET_INTRX(sc); return (m); } static void uath_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct uath_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct mbuf *m = NULL; struct uath_data *data; struct uath_rx_desc *desc = NULL; int8_t nf; UATH_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_rx_active); if (data == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); UATH_STAT_DEC(sc, st_rx_active); m = uath_data_rxeof(xfer, data, &desc); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); UATH_STAT_INC(sc, st_rx_inactive); /* FALLTHROUGH */ case USB_ST_SETUP: setup: data = STAILQ_FIRST(&sc->sc_rx_inactive); if (data == NULL) return; STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); UATH_STAT_DEC(sc, st_rx_inactive); STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); UATH_STAT_INC(sc, st_rx_active); usbd_xfer_set_frame_data(xfer, 0, data->buf, MCLBYTES); usbd_transfer_submit(xfer); /* * To avoid LOR we should unlock our private mutex here to call * ieee80211_input() because here is at the end of a USB * callback and safe to unlock. */ if (sc->sc_flags & UATH_FLAG_INVALID) { if (m != NULL) m_freem(m); return; } UATH_UNLOCK(sc); if (m != NULL && desc != NULL) { wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); nf = -95; /* XXX */ if (ni != NULL) { (void) ieee80211_input(ni, m, (int)be32toh(desc->rssi), nf); /* node is no longer needed */ ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, (int)be32toh(desc->rssi), nf); m = NULL; desc = NULL; } UATH_LOCK(sc); uath_start(sc); break; default: /* needs it to the inactive queue due to a error. */ data = STAILQ_FIRST(&sc->sc_rx_active); if (data != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); UATH_STAT_DEC(sc, st_rx_active); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); UATH_STAT_INC(sc, st_rx_inactive); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto setup; } break; } } static void uath_data_txeof(struct usb_xfer *xfer, struct uath_data *data) { struct uath_softc *sc = usbd_xfer_softc(xfer); UATH_ASSERT_LOCKED(sc); if (data->m) { /* XXX status? */ ieee80211_tx_complete(data->ni, data->m, 0); data->m = NULL; data->ni = NULL; } sc->sc_tx_timer = 0; } static void uath_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) { struct uath_softc *sc = usbd_xfer_softc(xfer); struct uath_data *data; UATH_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next); UATH_STAT_DEC(sc, st_tx_active); uath_data_txeof(xfer, data); STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); UATH_STAT_INC(sc, st_tx_inactive); /* FALLTHROUGH */ case USB_ST_SETUP: setup: data = STAILQ_FIRST(&sc->sc_tx_pending); if (data == NULL) { DPRINTF(sc, UATH_DEBUG_XMIT, "%s: empty pending queue\n", __func__); return; } STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); UATH_STAT_DEC(sc, st_tx_pending); STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); UATH_STAT_INC(sc, st_tx_active); usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); usbd_transfer_submit(xfer); uath_start(sc); break; default: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto setup; if (data->ni != NULL) { if_inc_counter(data->ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); if ((sc->sc_flags & UATH_FLAG_INVALID) == 0) ieee80211_free_node(data->ni); data->ni = NULL; } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto setup; } break; } } static device_method_t uath_methods[] = { DEVMETHOD(device_probe, uath_match), DEVMETHOD(device_attach, uath_attach), DEVMETHOD(device_detach, uath_detach), DEVMETHOD_END }; static driver_t uath_driver = { .name = "uath", .methods = uath_methods, .size = sizeof(struct uath_softc) }; static devclass_t uath_devclass; DRIVER_MODULE(uath, uhub, uath_driver, uath_devclass, NULL, 0); MODULE_DEPEND(uath, wlan, 1, 1, 1); MODULE_DEPEND(uath, usb, 1, 1, 1); MODULE_VERSION(uath, 1); USB_PNP_HOST_INFO(uath_devs); Index: projects/capsicum-test/sys/dev/usb/wlan/if_urtw.c =================================================================== --- projects/capsicum-test/sys/dev/usb/wlan/if_urtw.c (revision 345770) +++ projects/capsicum-test/sys/dev/usb/wlan/if_urtw.c (revision 345771) @@ -1,4413 +1,4415 @@ /*- * Copyright (c) 2008 Weongyo Jeong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #include #include #include #include #include #include "usbdevs.h" #include #include /* copy some rate indices from if_rtwn_ridx.h */ #define URTW_RIDX_CCK5 2 #define URTW_RIDX_CCK11 3 #define URTW_RIDX_OFDM6 4 #define URTW_RIDX_OFDM24 8 static SYSCTL_NODE(_hw_usb, OID_AUTO, urtw, CTLFLAG_RW, 0, "USB Realtek 8187L"); #ifdef URTW_DEBUG int urtw_debug = 0; SYSCTL_INT(_hw_usb_urtw, OID_AUTO, debug, CTLFLAG_RWTUN, &urtw_debug, 0, "control debugging printfs"); enum { URTW_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ URTW_DEBUG_RECV = 0x00000002, /* basic recv operation */ URTW_DEBUG_RESET = 0x00000004, /* reset processing */ URTW_DEBUG_TX_PROC = 0x00000008, /* tx ISR proc */ URTW_DEBUG_RX_PROC = 0x00000010, /* rx ISR proc */ URTW_DEBUG_STATE = 0x00000020, /* 802.11 state transitions */ URTW_DEBUG_STAT = 0x00000040, /* statistic */ URTW_DEBUG_INIT = 0x00000080, /* initialization of dev */ URTW_DEBUG_TXSTATUS = 0x00000100, /* tx status */ URTW_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) do { \ if (sc->sc_debug & (m)) \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(sc, m, fmt, ...) do { \ (void) sc; \ } while (0) #endif static int urtw_preamble_mode = URTW_PREAMBLE_MODE_LONG; SYSCTL_INT(_hw_usb_urtw, OID_AUTO, preamble_mode, CTLFLAG_RWTUN, &urtw_preamble_mode, 0, "set the preable mode (long or short)"); /* recognized device vendors/products */ #define urtw_lookup(v, p) \ ((const struct urtw_type *)usb_lookup(urtw_devs, v, p)) #define URTW_DEV_B(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187B) } #define URTW_DEV_L(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187L) } #define URTW_REV_RTL8187B 0 #define URTW_REV_RTL8187L 1 static const STRUCT_USB_HOST_ID urtw_devs[] = { URTW_DEV_B(NETGEAR, WG111V3), URTW_DEV_B(REALTEK, RTL8187B_0), URTW_DEV_B(REALTEK, RTL8187B_1), URTW_DEV_B(REALTEK, RTL8187B_2), URTW_DEV_B(SITECOMEU, WL168V4), URTW_DEV_L(ASUS, P5B_WIFI), URTW_DEV_L(BELKIN, F5D7050E), URTW_DEV_L(LINKSYS4, WUSB54GCV2), URTW_DEV_L(NETGEAR, WG111V2), URTW_DEV_L(REALTEK, RTL8187), URTW_DEV_L(SITECOMEU, WL168V1), URTW_DEV_L(SURECOM, EP9001G2A), { USB_VPI(USB_VENDOR_OVISLINK, 0x8187, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_DICKSMITH, 0x9401, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_HP, 0xca02, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_LOGITEC, 0x010c, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_NETGEAR, 0x6100, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_SPHAIRON, 0x0150, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_QCOM, 0x6232, URTW_REV_RTL8187L) }, #undef URTW_DEV_L #undef URTW_DEV_B }; #define urtw_read8_m(sc, val, data) do { \ error = urtw_read8_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write8_m(sc, val, data) do { \ error = urtw_write8_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_read16_m(sc, val, data) do { \ error = urtw_read16_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write16_m(sc, val, data) do { \ error = urtw_write16_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_read32_m(sc, val, data) do { \ error = urtw_read32_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write32_m(sc, val, data) do { \ error = urtw_write32_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_8187_write_phy_ofdm(sc, val, data) do { \ error = urtw_8187_write_phy_ofdm_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_8187_write_phy_cck(sc, val, data) do { \ error = urtw_8187_write_phy_cck_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_8225_write(sc, val, data) do { \ error = urtw_8225_write_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) struct urtw_pair { uint32_t reg; uint32_t val; }; static uint8_t urtw_8225_agc[] = { 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; static uint8_t urtw_8225z2_agc[] = { 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, 0x4f, 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37, 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31 }; static uint32_t urtw_8225_channel[] = { 0x0000, /* dummy channel 0 */ 0x085c, /* 1 */ 0x08dc, /* 2 */ 0x095c, /* 3 */ 0x09dc, /* 4 */ 0x0a5c, /* 5 */ 0x0adc, /* 6 */ 0x0b5c, /* 7 */ 0x0bdc, /* 8 */ 0x0c5c, /* 9 */ 0x0cdc, /* 10 */ 0x0d5c, /* 11 */ 0x0ddc, /* 12 */ 0x0e5c, /* 13 */ 0x0f72, /* 14 */ }; static uint8_t urtw_8225_gain[] = { 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */ 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */ 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */ 0x33, 0x80, 0x79, 0xc5, /* -78dbm */ 0x43, 0x78, 0x76, 0xc5, /* -74dbm */ 0x53, 0x60, 0x73, 0xc5, /* -70dbm */ 0x63, 0x58, 0x70, 0xc5, /* -66dbm */ }; static struct urtw_pair urtw_8225_rf_part1[] = { { 0x00, 0x0067 }, { 0x01, 0x0fe0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, { 0x04, 0x0486 }, { 0x05, 0x0bc0 }, { 0x06, 0x0ae6 }, { 0x07, 0x082a }, { 0x08, 0x001f }, { 0x09, 0x0334 }, { 0x0a, 0x0fd4 }, { 0x0b, 0x0391 }, { 0x0c, 0x0050 }, { 0x0d, 0x06db }, { 0x0e, 0x0029 }, { 0x0f, 0x0914 }, }; static struct urtw_pair urtw_8225_rf_part2[] = { { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 }, { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 }, { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x09 }, { 0x0b, 0x80 }, { 0x0c, 0x01 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, { 0x10, 0x84 }, { 0x11, 0x06 }, { 0x12, 0x20 }, { 0x13, 0x20 }, { 0x14, 0x00 }, { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, { 0x18, 0xef }, { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x76 }, { 0x1c, 0x04 }, { 0x1e, 0x95 }, { 0x1f, 0x75 }, { 0x20, 0x1f }, { 0x21, 0x27 }, { 0x22, 0x16 }, { 0x24, 0x46 }, { 0x25, 0x20 }, { 0x26, 0x90 }, { 0x27, 0x88 } }; static struct urtw_pair urtw_8225_rf_part3[] = { { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 }, { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x10, 0x9b }, { 0x11, 0x88 }, { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, { 0x1a, 0xa0 }, { 0x1b, 0x08 }, { 0x40, 0x86 }, { 0x41, 0x8d }, { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x1f }, { 0x45, 0x1e }, { 0x46, 0x1a }, { 0x47, 0x15 }, { 0x48, 0x10 }, { 0x49, 0x0a }, { 0x4a, 0x05 }, { 0x4b, 0x02 }, { 0x4c, 0x05 } }; static uint16_t urtw_8225_rxgain[] = { 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb }; static uint8_t urtw_8225_threshold[] = { 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd, }; static uint8_t urtw_8225_tx_gain_cck_ofdm[] = { 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e }; static uint8_t urtw_8225_txpwr_cck[] = { 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 }; static uint8_t urtw_8225_txpwr_cck_ch14[] = { 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 }; static uint8_t urtw_8225_txpwr_ofdm[]={ 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 }; static uint8_t urtw_8225v2_gain_bg[]={ 0x23, 0x15, 0xa5, /* -82-1dbm */ 0x23, 0x15, 0xb5, /* -82-2dbm */ 0x23, 0x15, 0xc5, /* -82-3dbm */ 0x33, 0x15, 0xc5, /* -78dbm */ 0x43, 0x15, 0xc5, /* -74dbm */ 0x53, 0x15, 0xc5, /* -70dbm */ 0x63, 0x15, 0xc5, /* -66dbm */ }; static struct urtw_pair urtw_8225v2_rf_part1[] = { { 0x00, 0x02bf }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a }, { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb }, { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 } }; static struct urtw_pair urtw_8225v2b_rf_part0[] = { { 0x00, 0x00b7 }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a }, { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb }, { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 } }; static struct urtw_pair urtw_8225v2b_rf_part1[] = { {0x0f0, 0x32}, {0x0f1, 0x32}, {0x0f2, 0x00}, {0x0f3, 0x00}, {0x0f4, 0x32}, {0x0f5, 0x43}, {0x0f6, 0x00}, {0x0f7, 0x00}, {0x0f8, 0x46}, {0x0f9, 0xa4}, {0x0fa, 0x00}, {0x0fb, 0x00}, {0x0fc, 0x96}, {0x0fd, 0xa4}, {0x0fe, 0x00}, {0x0ff, 0x00}, {0x158, 0x4b}, {0x159, 0x00}, {0x15a, 0x4b}, {0x15b, 0x00}, {0x160, 0x4b}, {0x161, 0x09}, {0x162, 0x4b}, {0x163, 0x09}, {0x1ce, 0x0f}, {0x1cf, 0x00}, {0x1e0, 0xff}, {0x1e1, 0x0f}, {0x1e2, 0x00}, {0x1f0, 0x4e}, {0x1f1, 0x01}, {0x1f2, 0x02}, {0x1f3, 0x03}, {0x1f4, 0x04}, {0x1f5, 0x05}, {0x1f6, 0x06}, {0x1f7, 0x07}, {0x1f8, 0x08}, {0x24e, 0x00}, {0x20c, 0x04}, {0x221, 0x61}, {0x222, 0x68}, {0x223, 0x6f}, {0x224, 0x76}, {0x225, 0x7d}, {0x226, 0x84}, {0x227, 0x8d}, {0x24d, 0x08}, {0x250, 0x05}, {0x251, 0xf5}, {0x252, 0x04}, {0x253, 0xa0}, {0x254, 0x1f}, {0x255, 0x23}, {0x256, 0x45}, {0x257, 0x67}, {0x258, 0x08}, {0x259, 0x08}, {0x25a, 0x08}, {0x25b, 0x08}, {0x260, 0x08}, {0x261, 0x08}, {0x262, 0x08}, {0x263, 0x08}, {0x264, 0xcf}, {0x272, 0x56}, {0x273, 0x9a}, {0x034, 0xf0}, {0x035, 0x0f}, {0x05b, 0x40}, {0x084, 0x88}, {0x085, 0x24}, {0x088, 0x54}, {0x08b, 0xb8}, {0x08c, 0x07}, {0x08d, 0x00}, {0x094, 0x1b}, {0x095, 0x12}, {0x096, 0x00}, {0x097, 0x06}, {0x09d, 0x1a}, {0x09f, 0x10}, {0x0b4, 0x22}, {0x0be, 0x80}, {0x0db, 0x00}, {0x0ee, 0x00}, {0x091, 0x03}, {0x24c, 0x00}, {0x39f, 0x00}, {0x08c, 0x01}, {0x08d, 0x10}, {0x08e, 0x08}, {0x08f, 0x00} }; static struct urtw_pair urtw_8225v2_rf_part2[] = { { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 }, { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 }, { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x08 }, { 0x0b, 0x80 }, { 0x0c, 0x01 }, { 0x0d, 0x43 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, { 0x10, 0x84 }, { 0x11, 0x07 }, { 0x12, 0x20 }, { 0x13, 0x20 }, { 0x14, 0x00 }, { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, { 0x18, 0xef }, { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x15 }, { 0x1c, 0x04 }, { 0x1d, 0xc5 }, { 0x1e, 0x95 }, { 0x1f, 0x75 }, { 0x20, 0x1f }, { 0x21, 0x17 }, { 0x22, 0x16 }, { 0x23, 0x80 }, { 0x24, 0x46 }, { 0x25, 0x00 }, { 0x26, 0x90 }, { 0x27, 0x88 } }; static struct urtw_pair urtw_8225v2b_rf_part2[] = { { 0x00, 0x10 }, { 0x01, 0x0d }, { 0x02, 0x01 }, { 0x03, 0x00 }, { 0x04, 0x14 }, { 0x05, 0xfb }, { 0x06, 0xfb }, { 0x07, 0x60 }, { 0x08, 0x00 }, { 0x09, 0x60 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x0c, 0x00 }, { 0x0d, 0x5c }, { 0x0e, 0x00 }, { 0x0f, 0x00 }, { 0x10, 0x40 }, { 0x11, 0x00 }, { 0x12, 0x40 }, { 0x13, 0x00 }, { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0xa8 }, { 0x17, 0x26 }, { 0x18, 0x32 }, { 0x19, 0x33 }, { 0x1a, 0x07 }, { 0x1b, 0xa5 }, { 0x1c, 0x6f }, { 0x1d, 0x55 }, { 0x1e, 0xc8 }, { 0x1f, 0xb3 }, { 0x20, 0x0a }, { 0x21, 0xe1 }, { 0x22, 0x2C }, { 0x23, 0x8a }, { 0x24, 0x86 }, { 0x25, 0x83 }, { 0x26, 0x34 }, { 0x27, 0x0f }, { 0x28, 0x4f }, { 0x29, 0x24 }, { 0x2a, 0x6f }, { 0x2b, 0xc2 }, { 0x2c, 0x6b }, { 0x2d, 0x40 }, { 0x2e, 0x80 }, { 0x2f, 0x00 }, { 0x30, 0xc0 }, { 0x31, 0xc1 }, { 0x32, 0x58 }, { 0x33, 0xf1 }, { 0x34, 0x00 }, { 0x35, 0xe4 }, { 0x36, 0x90 }, { 0x37, 0x3e }, { 0x38, 0x6d }, { 0x39, 0x3c }, { 0x3a, 0xfb }, { 0x3b, 0x07 } }; static struct urtw_pair urtw_8225v2_rf_part3[] = { { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 }, { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x09, 0x11 }, { 0x0a, 0x17 }, { 0x0b, 0x11 }, { 0x10, 0x9b }, { 0x11, 0x88 }, { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, { 0x1a, 0xa0 }, { 0x1b, 0x08 }, { 0x1d, 0x00 }, { 0x40, 0x86 }, { 0x41, 0x9d }, { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x36 }, { 0x45, 0x35 }, { 0x46, 0x2e }, { 0x47, 0x25 }, { 0x48, 0x1c }, { 0x49, 0x12 }, { 0x4a, 0x09 }, { 0x4b, 0x04 }, { 0x4c, 0x05 } }; static uint16_t urtw_8225v2_rxgain[] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009, 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141, 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244, 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345, 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389, 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb }; static uint16_t urtw_8225v2b_rxgain[] = { 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb }; static uint8_t urtw_8225v2_tx_gain_cck_ofdm[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, }; static uint8_t urtw_8225v2_txpwr_cck[] = { 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 }; static uint8_t urtw_8225v2_txpwr_cck_ch14[] = { 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 }; static uint8_t urtw_8225v2b_txpwr_cck[] = { 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04, 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03, 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03, 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03 }; static uint8_t urtw_8225v2b_txpwr_cck_ch14[] = { 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00 }; static struct urtw_pair urtw_ratetable[] = { { 2, 0 }, { 4, 1 }, { 11, 2 }, { 12, 4 }, { 18, 5 }, { 22, 3 }, { 24, 6 }, { 36, 7 }, { 48, 8 }, { 72, 9 }, { 96, 10 }, { 108, 11 } }; #if 0 static const uint8_t urtw_8187b_reg_table[][3] = { { 0xf0, 0x32, 0 }, { 0xf1, 0x32, 0 }, { 0xf2, 0x00, 0 }, { 0xf3, 0x00, 0 }, { 0xf4, 0x32, 0 }, { 0xf5, 0x43, 0 }, { 0xf6, 0x00, 0 }, { 0xf7, 0x00, 0 }, { 0xf8, 0x46, 0 }, { 0xf9, 0xa4, 0 }, { 0xfa, 0x00, 0 }, { 0xfb, 0x00, 0 }, { 0xfc, 0x96, 0 }, { 0xfd, 0xa4, 0 }, { 0xfe, 0x00, 0 }, { 0xff, 0x00, 0 }, { 0x58, 0x4b, 1 }, { 0x59, 0x00, 1 }, { 0x5a, 0x4b, 1 }, { 0x5b, 0x00, 1 }, { 0x60, 0x4b, 1 }, { 0x61, 0x09, 1 }, { 0x62, 0x4b, 1 }, { 0x63, 0x09, 1 }, { 0xce, 0x0f, 1 }, { 0xcf, 0x00, 1 }, { 0xe0, 0xff, 1 }, { 0xe1, 0x0f, 1 }, { 0xe2, 0x00, 1 }, { 0xf0, 0x4e, 1 }, { 0xf1, 0x01, 1 }, { 0xf2, 0x02, 1 }, { 0xf3, 0x03, 1 }, { 0xf4, 0x04, 1 }, { 0xf5, 0x05, 1 }, { 0xf6, 0x06, 1 }, { 0xf7, 0x07, 1 }, { 0xf8, 0x08, 1 }, { 0x4e, 0x00, 2 }, { 0x0c, 0x04, 2 }, { 0x21, 0x61, 2 }, { 0x22, 0x68, 2 }, { 0x23, 0x6f, 2 }, { 0x24, 0x76, 2 }, { 0x25, 0x7d, 2 }, { 0x26, 0x84, 2 }, { 0x27, 0x8d, 2 }, { 0x4d, 0x08, 2 }, { 0x50, 0x05, 2 }, { 0x51, 0xf5, 2 }, { 0x52, 0x04, 2 }, { 0x53, 0xa0, 2 }, { 0x54, 0x1f, 2 }, { 0x55, 0x23, 2 }, { 0x56, 0x45, 2 }, { 0x57, 0x67, 2 }, { 0x58, 0x08, 2 }, { 0x59, 0x08, 2 }, { 0x5a, 0x08, 2 }, { 0x5b, 0x08, 2 }, { 0x60, 0x08, 2 }, { 0x61, 0x08, 2 }, { 0x62, 0x08, 2 }, { 0x63, 0x08, 2 }, { 0x64, 0xcf, 2 }, { 0x72, 0x56, 2 }, { 0x73, 0x9a, 2 }, { 0x34, 0xf0, 0 }, { 0x35, 0x0f, 0 }, { 0x5b, 0x40, 0 }, { 0x84, 0x88, 0 }, { 0x85, 0x24, 0 }, { 0x88, 0x54, 0 }, { 0x8b, 0xb8, 0 }, { 0x8c, 0x07, 0 }, { 0x8d, 0x00, 0 }, { 0x94, 0x1b, 0 }, { 0x95, 0x12, 0 }, { 0x96, 0x00, 0 }, { 0x97, 0x06, 0 }, { 0x9d, 0x1a, 0 }, { 0x9f, 0x10, 0 }, { 0xb4, 0x22, 0 }, { 0xbe, 0x80, 0 }, { 0xdb, 0x00, 0 }, { 0xee, 0x00, 0 }, { 0x91, 0x03, 0 }, { 0x4c, 0x00, 2 }, { 0x9f, 0x00, 3 }, { 0x8c, 0x01, 0 }, { 0x8d, 0x10, 0 }, { 0x8e, 0x08, 0 }, { 0x8f, 0x00, 0 } }; #endif static usb_callback_t urtw_bulk_rx_callback; static usb_callback_t urtw_bulk_tx_callback; static usb_callback_t urtw_bulk_tx_status_callback; static const struct usb_config urtw_8187b_usbconfig[URTW_8187B_N_XFERS] = { [URTW_8187B_BULK_RX] = { .type = UE_BULK, .endpoint = 0x83, .direction = UE_DIR_IN, .bufsize = MCLBYTES, .flags = { .ext_buffer = 1, .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = urtw_bulk_rx_callback }, [URTW_8187B_BULK_TX_STATUS] = { .type = UE_BULK, .endpoint = 0x89, .direction = UE_DIR_IN, .bufsize = sizeof(uint64_t), .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = urtw_bulk_tx_status_callback }, [URTW_8187B_BULK_TX_BE] = { .type = UE_BULK, .endpoint = URTW_8187B_TXPIPE_BE, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE * URTW_TX_DATA_LIST_COUNT, .flags = { .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, [URTW_8187B_BULK_TX_BK] = { .type = UE_BULK, .endpoint = URTW_8187B_TXPIPE_BK, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE, .flags = { .ext_buffer = 1, .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, [URTW_8187B_BULK_TX_VI] = { .type = UE_BULK, .endpoint = URTW_8187B_TXPIPE_VI, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE, .flags = { .ext_buffer = 1, .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, [URTW_8187B_BULK_TX_VO] = { .type = UE_BULK, .endpoint = URTW_8187B_TXPIPE_VO, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE, .flags = { .ext_buffer = 1, .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, [URTW_8187B_BULK_TX_EP12] = { .type = UE_BULK, .endpoint = 0xc, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE, .flags = { .ext_buffer = 1, .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT } }; static const struct usb_config urtw_8187l_usbconfig[URTW_8187L_N_XFERS] = { [URTW_8187L_BULK_RX] = { .type = UE_BULK, .endpoint = 0x81, .direction = UE_DIR_IN, .bufsize = MCLBYTES, .flags = { .ext_buffer = 1, .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = urtw_bulk_rx_callback }, [URTW_8187L_BULK_TX_LOW] = { .type = UE_BULK, .endpoint = 0x2, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE * URTW_TX_DATA_LIST_COUNT, .flags = { .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, [URTW_8187L_BULK_TX_NORMAL] = { .type = UE_BULK, .endpoint = 0x3, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE, .flags = { .ext_buffer = 1, .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, }; static struct ieee80211vap *urtw_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void urtw_vap_delete(struct ieee80211vap *); static void urtw_init(struct urtw_softc *); static void urtw_stop(struct urtw_softc *); static void urtw_parent(struct ieee80211com *); static int urtw_transmit(struct ieee80211com *, struct mbuf *); static void urtw_start(struct urtw_softc *); static int urtw_alloc_rx_data_list(struct urtw_softc *); static int urtw_alloc_tx_data_list(struct urtw_softc *); static int urtw_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void urtw_scan_start(struct ieee80211com *); static void urtw_scan_end(struct ieee80211com *); static void urtw_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel[]); static void urtw_set_channel(struct ieee80211com *); static void urtw_update_promisc(struct ieee80211com *); static void urtw_update_mcast(struct ieee80211com *); static int urtw_tx_start(struct urtw_softc *, struct ieee80211_node *, struct mbuf *, struct urtw_data *, int); static int urtw_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void urtw_led_ch(void *); static void urtw_ledtask(void *, int); static void urtw_watchdog(void *); static void urtw_set_multi(void *); static int urtw_isbmode(uint16_t); static uint16_t urtw_rtl2rate(uint32_t); static usb_error_t urtw_set_rate(struct urtw_softc *); static usb_error_t urtw_update_msr(struct urtw_softc *); static usb_error_t urtw_read8_c(struct urtw_softc *, int, uint8_t *); static usb_error_t urtw_read16_c(struct urtw_softc *, int, uint16_t *); static usb_error_t urtw_read32_c(struct urtw_softc *, int, uint32_t *); static usb_error_t urtw_write8_c(struct urtw_softc *, int, uint8_t); static usb_error_t urtw_write16_c(struct urtw_softc *, int, uint16_t); static usb_error_t urtw_write32_c(struct urtw_softc *, int, uint32_t); static usb_error_t urtw_eprom_cs(struct urtw_softc *, int); static usb_error_t urtw_eprom_ck(struct urtw_softc *); static usb_error_t urtw_eprom_sendbits(struct urtw_softc *, int16_t *, int); static usb_error_t urtw_eprom_read32(struct urtw_softc *, uint32_t, uint32_t *); static usb_error_t urtw_eprom_readbit(struct urtw_softc *, int16_t *); static usb_error_t urtw_eprom_writebit(struct urtw_softc *, int16_t); static usb_error_t urtw_get_macaddr(struct urtw_softc *); static usb_error_t urtw_get_txpwr(struct urtw_softc *); static usb_error_t urtw_get_rfchip(struct urtw_softc *); static usb_error_t urtw_led_init(struct urtw_softc *); static usb_error_t urtw_8185_rf_pins_enable(struct urtw_softc *); static usb_error_t urtw_8185_tx_antenna(struct urtw_softc *, uint8_t); static usb_error_t urtw_8187_write_phy(struct urtw_softc *, uint8_t, uint32_t); static usb_error_t urtw_8187_write_phy_ofdm_c(struct urtw_softc *, uint8_t, uint32_t); static usb_error_t urtw_8187_write_phy_cck_c(struct urtw_softc *, uint8_t, uint32_t); static usb_error_t urtw_8225_setgain(struct urtw_softc *, int16_t); static usb_error_t urtw_8225_usb_init(struct urtw_softc *); static usb_error_t urtw_8225_write_c(struct urtw_softc *, uint8_t, uint16_t); static usb_error_t urtw_8225_write_s16(struct urtw_softc *, uint8_t, int, uint16_t *); static usb_error_t urtw_8225_read(struct urtw_softc *, uint8_t, uint32_t *); static usb_error_t urtw_8225_rf_init(struct urtw_softc *); static usb_error_t urtw_8225_rf_set_chan(struct urtw_softc *, int); static usb_error_t urtw_8225_rf_set_sens(struct urtw_softc *, int); static usb_error_t urtw_8225_set_txpwrlvl(struct urtw_softc *, int); static usb_error_t urtw_8225_rf_stop(struct urtw_softc *); static usb_error_t urtw_8225v2_rf_init(struct urtw_softc *); static usb_error_t urtw_8225v2_rf_set_chan(struct urtw_softc *, int); static usb_error_t urtw_8225v2_set_txpwrlvl(struct urtw_softc *, int); static usb_error_t urtw_8225v2_setgain(struct urtw_softc *, int16_t); static usb_error_t urtw_8225_isv2(struct urtw_softc *, int *); static usb_error_t urtw_8225v2b_rf_init(struct urtw_softc *); static usb_error_t urtw_8225v2b_rf_set_chan(struct urtw_softc *, int); static usb_error_t urtw_read8e(struct urtw_softc *, int, uint8_t *); static usb_error_t urtw_write8e(struct urtw_softc *, int, uint8_t); static usb_error_t urtw_8180_set_anaparam(struct urtw_softc *, uint32_t); static usb_error_t urtw_8185_set_anaparam2(struct urtw_softc *, uint32_t); static usb_error_t urtw_intr_enable(struct urtw_softc *); static usb_error_t urtw_intr_disable(struct urtw_softc *); static usb_error_t urtw_reset(struct urtw_softc *); static usb_error_t urtw_led_on(struct urtw_softc *, int); static usb_error_t urtw_led_ctl(struct urtw_softc *, int); static usb_error_t urtw_led_blink(struct urtw_softc *); static usb_error_t urtw_led_mode0(struct urtw_softc *, int); static usb_error_t urtw_led_mode1(struct urtw_softc *, int); static usb_error_t urtw_led_mode2(struct urtw_softc *, int); static usb_error_t urtw_led_mode3(struct urtw_softc *, int); static usb_error_t urtw_rx_setconf(struct urtw_softc *); static usb_error_t urtw_rx_enable(struct urtw_softc *); static usb_error_t urtw_tx_enable(struct urtw_softc *sc); static void urtw_free_tx_data_list(struct urtw_softc *); static void urtw_free_rx_data_list(struct urtw_softc *); static void urtw_free_data_list(struct urtw_softc *, struct urtw_data data[], int, int); static usb_error_t urtw_adapter_start(struct urtw_softc *); static usb_error_t urtw_adapter_start_b(struct urtw_softc *); static usb_error_t urtw_set_mode(struct urtw_softc *, uint32_t); static usb_error_t urtw_8187b_cmd_reset(struct urtw_softc *); static usb_error_t urtw_do_request(struct urtw_softc *, struct usb_device_request *, void *); static usb_error_t urtw_8225v2b_set_txpwrlvl(struct urtw_softc *, int); static usb_error_t urtw_led_off(struct urtw_softc *, int); static void urtw_abort_xfers(struct urtw_softc *); static struct urtw_data * urtw_getbuf(struct urtw_softc *sc); static int urtw_compute_txtime(uint16_t, uint16_t, uint8_t, uint8_t); static void urtw_updateslot(struct ieee80211com *); static void urtw_updateslottask(void *, int); static void urtw_sysctl_node(struct urtw_softc *); static int urtw_match(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != URTW_CONFIG_INDEX) return (ENXIO); if (uaa->info.bIfaceIndex != URTW_IFACE_INDEX) return (ENXIO); return (usbd_lookup_id_by_uaa(urtw_devs, sizeof(urtw_devs), uaa)); } static int urtw_attach(device_t dev) { const struct usb_config *setup_start; int ret = ENXIO; struct urtw_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); struct ieee80211com *ic = &sc->sc_ic; uint8_t iface_index = URTW_IFACE_INDEX; /* XXX */ uint16_t n_setup; uint32_t data; usb_error_t error; device_set_usb_desc(dev); sc->sc_dev = dev; sc->sc_udev = uaa->device; if (USB_GET_DRIVER_INFO(uaa) == URTW_REV_RTL8187B) sc->sc_flags |= URTW_RTL8187B; #ifdef URTW_DEBUG sc->sc_debug = urtw_debug; #endif mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF); usb_callout_init_mtx(&sc->sc_led_ch, &sc->sc_mtx, 0); TASK_INIT(&sc->sc_led_task, 0, urtw_ledtask, sc); TASK_INIT(&sc->sc_updateslot_task, 0, urtw_updateslottask, sc); callout_init(&sc->sc_watchdog_ch, 0); mbufq_init(&sc->sc_snd, ifqmaxlen); if (sc->sc_flags & URTW_RTL8187B) { setup_start = urtw_8187b_usbconfig; n_setup = URTW_8187B_N_XFERS; } else { setup_start = urtw_8187l_usbconfig; n_setup = URTW_8187L_N_XFERS; } error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, setup_start, n_setup, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB transfers, " "err=%s\n", usbd_errstr(error)); ret = ENXIO; goto fail0; } if (sc->sc_flags & URTW_RTL8187B) { sc->sc_tx_dma_buf = usbd_xfer_get_frame_buffer(sc->sc_xfer[ URTW_8187B_BULK_TX_BE], 0); } else { sc->sc_tx_dma_buf = usbd_xfer_get_frame_buffer(sc->sc_xfer[ URTW_8187L_BULK_TX_LOW], 0); } URTW_LOCK(sc); urtw_read32_m(sc, URTW_RX, &data); sc->sc_epromtype = (data & URTW_RX_9356SEL) ? URTW_EEPROM_93C56 : URTW_EEPROM_93C46; error = urtw_get_rfchip(sc); if (error != 0) goto fail; error = urtw_get_macaddr(sc); if (error != 0) goto fail; error = urtw_get_txpwr(sc); if (error != 0) goto fail; error = urtw_led_init(sc); if (error != 0) goto fail; URTW_UNLOCK(sc); sc->sc_rts_retry = URTW_DEFAULT_RTS_RETRY; sc->sc_tx_retry = URTW_DEFAULT_TX_RETRY; sc->sc_currate = URTW_RIDX_CCK11; sc->sc_preamble_mode = urtw_preamble_mode; ic->ic_softc = sc; ic->ic_name = device_get_nameunit(dev); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA | /* station mode */ IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_BGSCAN | /* capable of bg scanning */ IEEE80211_C_WPA; /* 802.11i */ /* XXX TODO: setup regdomain if URTW_EPROM_CHANPLAN_BY_HW bit is set.*/ urtw_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, ic->ic_channels); ieee80211_ifattach(ic); ic->ic_raw_xmit = urtw_raw_xmit; ic->ic_scan_start = urtw_scan_start; ic->ic_scan_end = urtw_scan_end; ic->ic_getradiocaps = urtw_getradiocaps; ic->ic_set_channel = urtw_set_channel; ic->ic_updateslot = urtw_updateslot; ic->ic_vap_create = urtw_vap_create; ic->ic_vap_delete = urtw_vap_delete; ic->ic_update_promisc = urtw_update_promisc; ic->ic_update_mcast = urtw_update_mcast; ic->ic_parent = urtw_parent; ic->ic_transmit = urtw_transmit; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), URTW_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), URTW_RX_RADIOTAP_PRESENT); urtw_sysctl_node(sc); if (bootverbose) ieee80211_announce(ic); return (0); fail: URTW_UNLOCK(sc); usbd_transfer_unsetup(sc->sc_xfer, (sc->sc_flags & URTW_RTL8187B) ? URTW_8187B_N_XFERS : URTW_8187L_N_XFERS); fail0: return (ret); } static int urtw_detach(device_t dev) { struct urtw_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; unsigned int x; unsigned int n_xfers; /* Prevent further ioctls */ URTW_LOCK(sc); sc->sc_flags |= URTW_DETACHED; urtw_stop(sc); URTW_UNLOCK(sc); ieee80211_draintask(ic, &sc->sc_updateslot_task); ieee80211_draintask(ic, &sc->sc_led_task); usb_callout_drain(&sc->sc_led_ch); callout_drain(&sc->sc_watchdog_ch); n_xfers = (sc->sc_flags & URTW_RTL8187B) ? URTW_8187B_N_XFERS : URTW_8187L_N_XFERS; /* prevent further allocations from RX/TX data lists */ URTW_LOCK(sc); STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); URTW_UNLOCK(sc); /* drain USB transfers */ for (x = 0; x != n_xfers; x++) usbd_transfer_drain(sc->sc_xfer[x]); /* free data buffers */ URTW_LOCK(sc); urtw_free_tx_data_list(sc); urtw_free_rx_data_list(sc); URTW_UNLOCK(sc); /* free USB transfers and some data buffers */ usbd_transfer_unsetup(sc->sc_xfer, n_xfers); ieee80211_ifdetach(ic); mbufq_drain(&sc->sc_snd); mtx_destroy(&sc->sc_mtx); return (0); } static void urtw_free_tx_data_list(struct urtw_softc *sc) { urtw_free_data_list(sc, sc->sc_tx, URTW_TX_DATA_LIST_COUNT, 0); } static void urtw_free_rx_data_list(struct urtw_softc *sc) { urtw_free_data_list(sc, sc->sc_rx, URTW_RX_DATA_LIST_COUNT, 1); } static void urtw_free_data_list(struct urtw_softc *sc, struct urtw_data data[], int ndata, int fillmbuf) { int i; for (i = 0; i < ndata; i++) { struct urtw_data *dp = &data[i]; if (fillmbuf == 1) { if (dp->m != NULL) { m_freem(dp->m); dp->m = NULL; dp->buf = NULL; } } else { dp->buf = NULL; } if (dp->ni != NULL) { ieee80211_free_node(dp->ni); dp->ni = NULL; } } } static struct ieee80211vap * urtw_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct urtw_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return (NULL); uvp = malloc(sizeof(struct urtw_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { /* out of memory */ free(uvp, M_80211_VAP); return (NULL); } /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = urtw_newstate; /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return (vap); } static void urtw_vap_delete(struct ieee80211vap *vap) { struct urtw_vap *uvp = URTW_VAP(vap); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static void urtw_init(struct urtw_softc *sc) { usb_error_t error; int ret; URTW_ASSERT_LOCKED(sc); if (sc->sc_flags & URTW_RUNNING) urtw_stop(sc); error = (sc->sc_flags & URTW_RTL8187B) ? urtw_adapter_start_b(sc) : urtw_adapter_start(sc); if (error != 0) goto fail; /* reset softc variables */ sc->sc_txtimer = 0; if (!(sc->sc_flags & URTW_INIT_ONCE)) { ret = urtw_alloc_rx_data_list(sc); if (ret != 0) goto fail; ret = urtw_alloc_tx_data_list(sc); if (ret != 0) goto fail; sc->sc_flags |= URTW_INIT_ONCE; } error = urtw_rx_enable(sc); if (error != 0) goto fail; error = urtw_tx_enable(sc); if (error != 0) goto fail; if (sc->sc_flags & URTW_RTL8187B) usbd_transfer_start(sc->sc_xfer[URTW_8187B_BULK_TX_STATUS]); sc->sc_flags |= URTW_RUNNING; callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); fail: return; } static usb_error_t urtw_adapter_start_b(struct urtw_softc *sc) { uint8_t data8; usb_error_t error; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG3, &data8); urtw_write8_m(sc, URTW_CONFIG3, data8 | URTW_CONFIG3_ANAPARAM_WRITE | URTW_CONFIG3_GNT_SELECT); urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8187B_8225_ANAPARAM2_ON); urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_ON); urtw_write8_m(sc, URTW_ANAPARAM3, URTW_8187B_8225_ANAPARAM3_ON); urtw_write8_m(sc, 0x61, 0x10); urtw_read8_m(sc, 0x62, &data8); urtw_write8_m(sc, 0x62, data8 & ~(1 << 5)); urtw_write8_m(sc, 0x62, data8 | (1 << 5)); urtw_read8_m(sc, URTW_CONFIG3, &data8); data8 &= ~URTW_CONFIG3_ANAPARAM_WRITE; urtw_write8_m(sc, URTW_CONFIG3, data8); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; error = urtw_8187b_cmd_reset(sc); if (error) goto fail; error = sc->sc_rf_init(sc); if (error != 0) goto fail; urtw_write8_m(sc, URTW_CMD, URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE); /* fix RTL8187B RX stall */ error = urtw_intr_enable(sc); if (error) goto fail; error = urtw_write8e(sc, 0x41, 0xf4); if (error) goto fail; error = urtw_write8e(sc, 0x40, 0x00); if (error) goto fail; error = urtw_write8e(sc, 0x42, 0x00); if (error) goto fail; error = urtw_write8e(sc, 0x42, 0x01); if (error) goto fail; error = urtw_write8e(sc, 0x40, 0x0f); if (error) goto fail; error = urtw_write8e(sc, 0x42, 0x00); if (error) goto fail; error = urtw_write8e(sc, 0x42, 0x01); if (error) goto fail; urtw_read8_m(sc, 0xdb, &data8); urtw_write8_m(sc, 0xdb, data8 | (1 << 2)); urtw_write16_m(sc, 0x372, 0x59fa); urtw_write16_m(sc, 0x374, 0x59d2); urtw_write16_m(sc, 0x376, 0x59d2); urtw_write16_m(sc, 0x378, 0x19fa); urtw_write16_m(sc, 0x37a, 0x19fa); urtw_write16_m(sc, 0x37c, 0x00d0); urtw_write8_m(sc, 0x61, 0); urtw_write8_m(sc, 0x180, 0x0f); urtw_write8_m(sc, 0x183, 0x03); urtw_write8_m(sc, 0xda, 0x10); urtw_write8_m(sc, 0x24d, 0x08); urtw_write32_m(sc, URTW_HSSI_PARA, 0x0600321b); urtw_write16_m(sc, 0x1ec, 0x800); /* RX MAX SIZE */ fail: return (error); } static usb_error_t urtw_adapter_start(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; usb_error_t error; error = urtw_reset(sc); if (error) goto fail; urtw_write8_m(sc, URTW_ADDR_MAGIC1, 0); urtw_write8_m(sc, URTW_GPIO, 0); /* for led */ urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4); error = urtw_led_ctl(sc, URTW_LED_CTL_POWER_ON); if (error != 0) goto fail; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; /* applying MAC address again. */ urtw_write32_m(sc, URTW_MAC0, ((uint32_t *)ic->ic_macaddr)[0]); urtw_write16_m(sc, URTW_MAC4, ((uint32_t *)ic->ic_macaddr)[1] & 0xffff); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; error = urtw_update_msr(sc); if (error) goto fail; urtw_write32_m(sc, URTW_INT_TIMEOUT, 0); urtw_write8_m(sc, URTW_WPA_CONFIG, 0); urtw_write8_m(sc, URTW_RATE_FALLBACK, URTW_RATE_FALLBACK_ENABLE | 0x1); error = urtw_set_rate(sc); if (error != 0) goto fail; error = sc->sc_rf_init(sc); if (error != 0) goto fail; if (sc->sc_rf_set_sens != NULL) sc->sc_rf_set_sens(sc, sc->sc_sens); /* XXX correct? to call write16 */ urtw_write16_m(sc, URTW_PSR, 1); urtw_write16_m(sc, URTW_ADDR_MAGIC2, 0x10); urtw_write8_m(sc, URTW_TALLY_SEL, 0x80); urtw_write8_m(sc, URTW_ADDR_MAGIC3, 0x60); /* XXX correct? to call write16 */ urtw_write16_m(sc, URTW_PSR, 0); urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4); error = urtw_intr_enable(sc); if (error != 0) goto fail; fail: return (error); } static usb_error_t urtw_set_mode(struct urtw_softc *sc, uint32_t mode) { uint8_t data; usb_error_t error; urtw_read8_m(sc, URTW_EPROM_CMD, &data); data = (data & ~URTW_EPROM_CMD_MASK) | (mode << URTW_EPROM_CMD_SHIFT); data = data & ~(URTW_EPROM_CS | URTW_EPROM_CK); urtw_write8_m(sc, URTW_EPROM_CMD, data); fail: return (error); } static usb_error_t urtw_8187b_cmd_reset(struct urtw_softc *sc) { int i; uint8_t data8; usb_error_t error; /* XXX the code can be duplicate with urtw_reset(). */ urtw_read8_m(sc, URTW_CMD, &data8); data8 = (data8 & 0x2) | URTW_CMD_RST; urtw_write8_m(sc, URTW_CMD, data8); for (i = 0; i < 20; i++) { usb_pause_mtx(&sc->sc_mtx, 2); urtw_read8_m(sc, URTW_CMD, &data8); if (!(data8 & URTW_CMD_RST)) break; } if (i >= 20) { device_printf(sc->sc_dev, "reset timeout\n"); goto fail; } fail: return (error); } static usb_error_t urtw_do_request(struct urtw_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 10; URTW_ASSERT_LOCKED(sc); while (ntries--) { err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 250 /* ms */); if (err == 0) break; DPRINTF(sc, URTW_DEBUG_INIT, "Control request failed, %s (retrying)\n", usbd_errstr(err)); usb_pause_mtx(&sc->sc_mtx, hz / 100); } return (err); } static void urtw_stop(struct urtw_softc *sc) { uint8_t data8; usb_error_t error; URTW_ASSERT_LOCKED(sc); sc->sc_flags &= ~URTW_RUNNING; error = urtw_intr_disable(sc); if (error) goto fail; urtw_read8_m(sc, URTW_CMD, &data8); data8 &= ~(URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE); urtw_write8_m(sc, URTW_CMD, data8); error = sc->sc_rf_stop(sc); if (error != 0) goto fail; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG4, &data8); urtw_write8_m(sc, URTW_CONFIG4, data8 | URTW_CONFIG4_VCOOFF); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; fail: if (error) device_printf(sc->sc_dev, "failed to stop (%s)\n", usbd_errstr(error)); usb_callout_stop(&sc->sc_led_ch); callout_stop(&sc->sc_watchdog_ch); urtw_abort_xfers(sc); } static void urtw_abort_xfers(struct urtw_softc *sc) { int i, max; URTW_ASSERT_LOCKED(sc); max = (sc->sc_flags & URTW_RTL8187B) ? URTW_8187B_N_XFERS : URTW_8187L_N_XFERS; /* abort any pending transfers */ for (i = 0; i < max; i++) usbd_transfer_stop(sc->sc_xfer[i]); } static void urtw_parent(struct ieee80211com *ic) { struct urtw_softc *sc = ic->ic_softc; int startall = 0; URTW_LOCK(sc); if (sc->sc_flags & URTW_DETACHED) { URTW_UNLOCK(sc); return; } if (ic->ic_nrunning > 0) { if (sc->sc_flags & URTW_RUNNING) { if (ic->ic_promisc > 0 || ic->ic_allmulti > 0) urtw_set_multi(sc); } else { urtw_init(sc); startall = 1; } } else if (sc->sc_flags & URTW_RUNNING) urtw_stop(sc); URTW_UNLOCK(sc); if (startall) ieee80211_start_all(ic); } static int urtw_transmit(struct ieee80211com *ic, struct mbuf *m) { struct urtw_softc *sc = ic->ic_softc; int error; URTW_LOCK(sc); if ((sc->sc_flags & URTW_RUNNING) == 0) { URTW_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { URTW_UNLOCK(sc); return (error); } urtw_start(sc); URTW_UNLOCK(sc); return (0); } static void urtw_start(struct urtw_softc *sc) { struct urtw_data *bf; struct ieee80211_node *ni; struct mbuf *m; URTW_ASSERT_LOCKED(sc); if ((sc->sc_flags & URTW_RUNNING) == 0) return; while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { bf = urtw_getbuf(sc); if (bf == NULL) { mbufq_prepend(&sc->sc_snd, m); break; } ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_NORMAL) != 0) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); ieee80211_free_node(ni); break; } sc->sc_txtimer = 5; callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); } } static int urtw_alloc_data_list(struct urtw_softc *sc, struct urtw_data data[], int ndata, int maxsz, void *dma_buf) { int i, error; for (i = 0; i < ndata; i++) { struct urtw_data *dp = &data[i]; dp->sc = sc; if (dma_buf == NULL) { dp->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (dp->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } dp->buf = mtod(dp->m, uint8_t *); } else { dp->m = NULL; dp->buf = ((uint8_t *)dma_buf) + (i * maxsz); } dp->ni = NULL; } return (0); fail: urtw_free_data_list(sc, data, ndata, 1); return (error); } static int urtw_alloc_rx_data_list(struct urtw_softc *sc) { int error, i; error = urtw_alloc_data_list(sc, sc->sc_rx, URTW_RX_DATA_LIST_COUNT, MCLBYTES, NULL /* mbufs */); if (error != 0) return (error); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); for (i = 0; i < URTW_RX_DATA_LIST_COUNT; i++) STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); return (0); } static int urtw_alloc_tx_data_list(struct urtw_softc *sc) { int error, i; error = urtw_alloc_data_list(sc, sc->sc_tx, URTW_TX_DATA_LIST_COUNT, URTW_TX_MAXSIZE, sc->sc_tx_dma_buf /* no mbufs */); if (error != 0) return (error); STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); for (i = 0; i < URTW_TX_DATA_LIST_COUNT; i++) STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next); return (0); } static int urtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct urtw_softc *sc = ic->ic_softc; struct urtw_data *bf; /* prevent management frames from being sent if we're not ready */ if (!(sc->sc_flags & URTW_RUNNING)) { m_freem(m); return ENETDOWN; } URTW_LOCK(sc); bf = urtw_getbuf(sc); if (bf == NULL) { m_freem(m); URTW_UNLOCK(sc); return (ENOBUFS); /* XXX */ } if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_LOW) != 0) { STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); URTW_UNLOCK(sc); return (EIO); } URTW_UNLOCK(sc); sc->sc_txtimer = 5; return (0); } static void urtw_scan_start(struct ieee80211com *ic) { /* XXX do nothing? */ } static void urtw_scan_end(struct ieee80211com *ic) { /* XXX do nothing? */ } static void urtw_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { uint8_t bands[IEEE80211_MODE_BYTES]; memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0); } static void urtw_set_channel(struct ieee80211com *ic) { struct urtw_softc *sc = ic->ic_softc; uint32_t data, orig; usb_error_t error; /* * if the user set a channel explicitly using ifconfig(8) this function * can be called earlier than we're expected that in some cases the * initialization would be failed if setting a channel is called before * the init have done. */ if (!(sc->sc_flags & URTW_RUNNING)) return; if (sc->sc_curchan != NULL && sc->sc_curchan == ic->ic_curchan) return; URTW_LOCK(sc); /* * during changing th channel we need to temporarily be disable * TX. */ urtw_read32_m(sc, URTW_TX_CONF, &orig); data = orig & ~URTW_TX_LOOPBACK_MASK; urtw_write32_m(sc, URTW_TX_CONF, data | URTW_TX_LOOPBACK_MAC); error = sc->sc_rf_set_chan(sc, ieee80211_chan2ieee(ic, ic->ic_curchan)); if (error != 0) goto fail; usb_pause_mtx(&sc->sc_mtx, 10); urtw_write32_m(sc, URTW_TX_CONF, orig); urtw_write16_m(sc, URTW_ATIM_WND, 2); urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100); urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100); urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100); fail: URTW_UNLOCK(sc); sc->sc_curchan = ic->ic_curchan; if (error != 0) device_printf(sc->sc_dev, "could not change the channel\n"); } static void urtw_update_promisc(struct ieee80211com *ic) { struct urtw_softc *sc = ic->ic_softc; URTW_LOCK(sc); if (sc->sc_flags & URTW_RUNNING) urtw_rx_setconf(sc); URTW_UNLOCK(sc); } static void urtw_update_mcast(struct ieee80211com *ic) { /* XXX do nothing? */ } static int urtw_tx_start(struct urtw_softc *sc, struct ieee80211_node *ni, struct mbuf *m0, struct urtw_data *data, int prior) { struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); struct ieee80211_key *k; const struct ieee80211_txparam *tp = ni->ni_txparms; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct usb_xfer *rtl8187b_pipes[URTW_8187B_TXPIPE_MAX] = { sc->sc_xfer[URTW_8187B_BULK_TX_BE], sc->sc_xfer[URTW_8187B_BULK_TX_BK], sc->sc_xfer[URTW_8187B_BULK_TX_VI], sc->sc_xfer[URTW_8187B_BULK_TX_VO] }; struct usb_xfer *xfer; int dur = 0, rtsdur = 0, rtsenable = 0, ctsenable = 0, rate, type, pkttime = 0, txdur = 0, isshort = 0, xferlen, ismcast; uint16_t acktime, rtstime, ctstime; uint32_t flags; usb_error_t error; URTW_ASSERT_LOCKED(sc); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; /* * Software crypto. */ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { device_printf(sc->sc_dev, "ieee80211_crypto_encap returns NULL.\n"); /* XXX we don't expect the fragmented frames */ m_freem(m0); return (ENOBUFS); } /* in case packet header moved, reset pointer */ wh = mtod(m0, struct ieee80211_frame *); } if (ieee80211_radiotap_active_vap(vap)) { struct urtw_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; ieee80211_radiotap_tx(vap, m0); } if (type == IEEE80211_FC0_TYPE_MGT || type == IEEE80211_FC0_TYPE_CTL || (m0->m_flags & M_EAPOL) != 0) { rate = tp->mgmtrate; } else { /* for data frames */ if (ismcast) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = urtw_rtl2rate(sc->sc_currate); } sc->sc_stats.txrates[sc->sc_currate]++; if (ismcast) txdur = pkttime = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, 0); else { acktime = urtw_compute_txtime(14, 2,0, 0); if ((m0->m_pkthdr.len + 4) > vap->iv_rtsthreshold) { rtsenable = 1; ctsenable = 0; rtstime = urtw_compute_txtime(URTW_ACKCTS_LEN, 2, 0, 0); ctstime = urtw_compute_txtime(14, 2, 0, 0); pkttime = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, isshort); rtsdur = ctstime + pkttime + acktime + 3 * URTW_ASIFS_TIME; txdur = rtstime + rtsdur; } else { rtsenable = ctsenable = rtsdur = 0; pkttime = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, isshort); txdur = pkttime + URTW_ASIFS_TIME + acktime; } if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) dur = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, isshort) + 3 * URTW_ASIFS_TIME + 2 * acktime; else dur = URTW_ASIFS_TIME + acktime; } USETW(wh->i_dur, dur); xferlen = m0->m_pkthdr.len; xferlen += (sc->sc_flags & URTW_RTL8187B) ? (4 * 8) : (4 * 3); if ((0 == xferlen % 64) || (0 == xferlen % 512)) xferlen += 1; memset(data->buf, 0, URTW_TX_MAXSIZE); flags = m0->m_pkthdr.len & 0xfff; flags |= URTW_TX_FLAG_NO_ENC; if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) && (sc->sc_preamble_mode == URTW_PREAMBLE_MODE_SHORT) && (sc->sc_currate != 0)) flags |= URTW_TX_FLAG_SPLCP; if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) flags |= URTW_TX_FLAG_MOREFRAG; flags |= (sc->sc_currate & 0xf) << URTW_TX_FLAG_TXRATE_SHIFT; if (sc->sc_flags & URTW_RTL8187B) { struct urtw_8187b_txhdr *tx; tx = (struct urtw_8187b_txhdr *)data->buf; if (ctsenable) flags |= URTW_TX_FLAG_CTS; if (rtsenable) { flags |= URTW_TX_FLAG_RTS; flags |= URTW_RIDX_CCK5 << URTW_TX_FLAG_RTSRATE_SHIFT; tx->rtsdur = rtsdur; } tx->flag = htole32(flags); tx->txdur = txdur; if (type == IEEE80211_FC0_TYPE_MGT && (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) tx->retry = 1; else tx->retry = URTW_TX_MAXRETRY; m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1)); } else { struct urtw_8187l_txhdr *tx; tx = (struct urtw_8187l_txhdr *)data->buf; if (rtsenable) { flags |= URTW_TX_FLAG_RTS; tx->rtsdur = rtsdur; } flags |= URTW_RIDX_CCK5 << URTW_TX_FLAG_RTSRATE_SHIFT; tx->flag = htole32(flags); tx->retry = 3; /* CW minimum */ tx->retry |= 7 << 4; /* CW maximum */ tx->retry |= URTW_TX_MAXRETRY << 8; /* retry limitation */ m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1)); } data->buflen = xferlen; data->ni = ni; data->m = m0; if (sc->sc_flags & URTW_RTL8187B) { switch (type) { case IEEE80211_FC0_TYPE_CTL: case IEEE80211_FC0_TYPE_MGT: xfer = sc->sc_xfer[URTW_8187B_BULK_TX_EP12]; break; default: KASSERT(M_WME_GETAC(m0) < URTW_8187B_TXPIPE_MAX, ("unsupported WME pipe %d", M_WME_GETAC(m0))); xfer = rtl8187b_pipes[M_WME_GETAC(m0)]; break; } } else xfer = (prior == URTW_PRIORITY_LOW) ? sc->sc_xfer[URTW_8187L_BULK_TX_LOW] : sc->sc_xfer[URTW_8187L_BULK_TX_NORMAL]; STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); usbd_transfer_start(xfer); error = urtw_led_ctl(sc, URTW_LED_CTL_TX); if (error != 0) device_printf(sc->sc_dev, "could not control LED (%d)\n", error); return (0); } static int urtw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct urtw_softc *sc = ic->ic_softc; struct urtw_vap *uvp = URTW_VAP(vap); struct ieee80211_node *ni; usb_error_t error = 0; DPRINTF(sc, URTW_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); sc->sc_state = nstate; IEEE80211_UNLOCK(ic); URTW_LOCK(sc); usb_callout_stop(&sc->sc_led_ch); callout_stop(&sc->sc_watchdog_ch); switch (nstate) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: break; case IEEE80211_S_RUN: ni = ieee80211_ref_node(vap->iv_bss); /* setting bssid. */ urtw_write32_m(sc, URTW_BSSID, ((uint32_t *)ni->ni_bssid)[0]); urtw_write16_m(sc, URTW_BSSID + 4, ((uint16_t *)ni->ni_bssid)[2]); urtw_update_msr(sc); /* XXX maybe the below would be incorrect. */ urtw_write16_m(sc, URTW_ATIM_WND, 2); urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100); urtw_write16_m(sc, URTW_BEACON_INTERVAL, 0x64); urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100); error = urtw_led_ctl(sc, URTW_LED_CTL_LINK); if (error != 0) device_printf(sc->sc_dev, "could not control LED (%d)\n", error); ieee80211_free_node(ni); break; default: break; } fail: URTW_UNLOCK(sc); IEEE80211_LOCK(ic); return (uvp->newstate(vap, nstate, arg)); } static void urtw_watchdog(void *arg) { struct urtw_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; if (sc->sc_txtimer > 0) { if (--sc->sc_txtimer == 0) { device_printf(sc->sc_dev, "device timeout\n"); - counter_u64_add(sc->sc_ic.ic_oerrors, 1); + counter_u64_add(ic->ic_oerrors, 1); + ieee80211_restart_all(ic); return; } callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); } } static void urtw_set_multi(void *arg) { /* XXX don't know how to set a device. Lack of docs. */ } static usb_error_t urtw_set_rate(struct urtw_softc *sc) { int i, basic_rate, min_rr_rate, max_rr_rate; uint16_t data; usb_error_t error; basic_rate = URTW_RIDX_OFDM24; min_rr_rate = URTW_RIDX_OFDM6; max_rr_rate = URTW_RIDX_OFDM24; urtw_write8_m(sc, URTW_RESP_RATE, max_rr_rate << URTW_RESP_MAX_RATE_SHIFT | min_rr_rate << URTW_RESP_MIN_RATE_SHIFT); urtw_read16_m(sc, URTW_BRSR, &data); data &= ~URTW_BRSR_MBR_8185; for (i = 0; i <= basic_rate; i++) data |= (1 << i); urtw_write16_m(sc, URTW_BRSR, data); fail: return (error); } static uint16_t urtw_rtl2rate(uint32_t rate) { unsigned int i; for (i = 0; i < nitems(urtw_ratetable); i++) { if (rate == urtw_ratetable[i].val) return urtw_ratetable[i].reg; } return (0); } static usb_error_t urtw_update_msr(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint8_t data; usb_error_t error; urtw_read8_m(sc, URTW_MSR, &data); data &= ~URTW_MSR_LINK_MASK; if (sc->sc_state == IEEE80211_S_RUN) { switch (ic->ic_opmode) { case IEEE80211_M_STA: case IEEE80211_M_MONITOR: data |= URTW_MSR_LINK_STA; if (sc->sc_flags & URTW_RTL8187B) data |= URTW_MSR_LINK_ENEDCA; break; case IEEE80211_M_IBSS: data |= URTW_MSR_LINK_ADHOC; break; case IEEE80211_M_HOSTAP: data |= URTW_MSR_LINK_HOSTAP; break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported operation mode 0x%x\n", ic->ic_opmode); error = USB_ERR_INVAL; goto fail; } } else data |= URTW_MSR_LINK_NONE; urtw_write8_m(sc, URTW_MSR, data); fail: return (error); } static usb_error_t urtw_read8_c(struct urtw_softc *sc, int val, uint8_t *data) { struct usb_device_request req; usb_error_t error; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint8_t)); error = urtw_do_request(sc, &req, data); return (error); } static usb_error_t urtw_read16_c(struct urtw_softc *sc, int val, uint16_t *data) { struct usb_device_request req; usb_error_t error; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint16_t)); error = urtw_do_request(sc, &req, data); return (error); } static usb_error_t urtw_read32_c(struct urtw_softc *sc, int val, uint32_t *data) { struct usb_device_request req; usb_error_t error; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint32_t)); error = urtw_do_request(sc, &req, data); return (error); } static usb_error_t urtw_write8_c(struct urtw_softc *sc, int val, uint8_t data) { struct usb_device_request req; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint8_t)); return (urtw_do_request(sc, &req, &data)); } static usb_error_t urtw_write16_c(struct urtw_softc *sc, int val, uint16_t data) { struct usb_device_request req; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint16_t)); return (urtw_do_request(sc, &req, &data)); } static usb_error_t urtw_write32_c(struct urtw_softc *sc, int val, uint32_t data) { struct usb_device_request req; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint32_t)); return (urtw_do_request(sc, &req, &data)); } static usb_error_t urtw_get_macaddr(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t data; usb_error_t error; error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR, &data); if (error != 0) goto fail; ic->ic_macaddr[0] = data & 0xff; ic->ic_macaddr[1] = (data & 0xff00) >> 8; error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 1, &data); if (error != 0) goto fail; ic->ic_macaddr[2] = data & 0xff; ic->ic_macaddr[3] = (data & 0xff00) >> 8; error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 2, &data); if (error != 0) goto fail; ic->ic_macaddr[4] = data & 0xff; ic->ic_macaddr[5] = (data & 0xff00) >> 8; fail: return (error); } static usb_error_t urtw_eprom_read32(struct urtw_softc *sc, uint32_t addr, uint32_t *data) { #define URTW_READCMD_LEN 3 int addrlen, i; int16_t addrstr[8], data16, readcmd[] = { 1, 1, 0 }; usb_error_t error; /* NB: make sure the buffer is initialized */ *data = 0; /* enable EPROM programming */ urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_PROGRAM_MODE); DELAY(URTW_EPROM_DELAY); error = urtw_eprom_cs(sc, URTW_EPROM_ENABLE); if (error != 0) goto fail; error = urtw_eprom_ck(sc); if (error != 0) goto fail; error = urtw_eprom_sendbits(sc, readcmd, URTW_READCMD_LEN); if (error != 0) goto fail; if (sc->sc_epromtype == URTW_EEPROM_93C56) { addrlen = 8; addrstr[0] = addr & (1 << 7); addrstr[1] = addr & (1 << 6); addrstr[2] = addr & (1 << 5); addrstr[3] = addr & (1 << 4); addrstr[4] = addr & (1 << 3); addrstr[5] = addr & (1 << 2); addrstr[6] = addr & (1 << 1); addrstr[7] = addr & (1 << 0); } else { addrlen=6; addrstr[0] = addr & (1 << 5); addrstr[1] = addr & (1 << 4); addrstr[2] = addr & (1 << 3); addrstr[3] = addr & (1 << 2); addrstr[4] = addr & (1 << 1); addrstr[5] = addr & (1 << 0); } error = urtw_eprom_sendbits(sc, addrstr, addrlen); if (error != 0) goto fail; error = urtw_eprom_writebit(sc, 0); if (error != 0) goto fail; for (i = 0; i < 16; i++) { error = urtw_eprom_ck(sc); if (error != 0) goto fail; error = urtw_eprom_readbit(sc, &data16); if (error != 0) goto fail; (*data) |= (data16 << (15 - i)); } error = urtw_eprom_cs(sc, URTW_EPROM_DISABLE); if (error != 0) goto fail; error = urtw_eprom_ck(sc); if (error != 0) goto fail; /* now disable EPROM programming */ urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_NORMAL_MODE); fail: return (error); #undef URTW_READCMD_LEN } static usb_error_t urtw_eprom_cs(struct urtw_softc *sc, int able) { uint8_t data; usb_error_t error; urtw_read8_m(sc, URTW_EPROM_CMD, &data); if (able == URTW_EPROM_ENABLE) urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CS); else urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CS); DELAY(URTW_EPROM_DELAY); fail: return (error); } static usb_error_t urtw_eprom_ck(struct urtw_softc *sc) { uint8_t data; usb_error_t error; /* masking */ urtw_read8_m(sc, URTW_EPROM_CMD, &data); urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CK); DELAY(URTW_EPROM_DELAY); /* unmasking */ urtw_read8_m(sc, URTW_EPROM_CMD, &data); urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CK); DELAY(URTW_EPROM_DELAY); fail: return (error); } static usb_error_t urtw_eprom_readbit(struct urtw_softc *sc, int16_t *data) { uint8_t data8; usb_error_t error; urtw_read8_m(sc, URTW_EPROM_CMD, &data8); *data = (data8 & URTW_EPROM_READBIT) ? 1 : 0; DELAY(URTW_EPROM_DELAY); fail: return (error); } static usb_error_t urtw_eprom_writebit(struct urtw_softc *sc, int16_t bit) { uint8_t data; usb_error_t error; urtw_read8_m(sc, URTW_EPROM_CMD, &data); if (bit != 0) urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_WRITEBIT); else urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_WRITEBIT); DELAY(URTW_EPROM_DELAY); fail: return (error); } static usb_error_t urtw_eprom_sendbits(struct urtw_softc *sc, int16_t *buf, int buflen) { int i = 0; usb_error_t error = 0; for (i = 0; i < buflen; i++) { error = urtw_eprom_writebit(sc, buf[i]); if (error != 0) goto fail; error = urtw_eprom_ck(sc); if (error != 0) goto fail; } fail: return (error); } static usb_error_t urtw_get_txpwr(struct urtw_softc *sc) { int i, j; uint32_t data; usb_error_t error; error = urtw_eprom_read32(sc, URTW_EPROM_TXPW_BASE, &data); if (error != 0) goto fail; sc->sc_txpwr_cck_base = data & 0xf; sc->sc_txpwr_ofdm_base = (data >> 4) & 0xf; for (i = 1, j = 0; i < 6; i += 2, j++) { error = urtw_eprom_read32(sc, URTW_EPROM_TXPW0 + j, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[i] = data & 0xf; sc->sc_txpwr_cck[i + 1] = (data & 0xf00) >> 8; sc->sc_txpwr_ofdm[i] = (data & 0xf0) >> 4; sc->sc_txpwr_ofdm[i + 1] = (data & 0xf000) >> 12; } for (i = 1, j = 0; i < 4; i += 2, j++) { error = urtw_eprom_read32(sc, URTW_EPROM_TXPW1 + j, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[i + 6] = data & 0xf; sc->sc_txpwr_cck[i + 6 + 1] = (data & 0xf00) >> 8; sc->sc_txpwr_ofdm[i + 6] = (data & 0xf0) >> 4; sc->sc_txpwr_ofdm[i + 6 + 1] = (data & 0xf000) >> 12; } if (sc->sc_flags & URTW_RTL8187B) { error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[1 + 6 + 4] = data & 0xf; sc->sc_txpwr_ofdm[1 + 6 + 4] = (data & 0xf0) >> 4; error = urtw_eprom_read32(sc, 0x0a, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[2 + 6 + 4] = data & 0xf; sc->sc_txpwr_ofdm[2 + 6 + 4] = (data & 0xf0) >> 4; error = urtw_eprom_read32(sc, 0x1c, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[3 + 6 + 4] = data & 0xf; sc->sc_txpwr_cck[3 + 6 + 4 + 1] = (data & 0xf00) >> 8; sc->sc_txpwr_ofdm[3 + 6 + 4] = (data & 0xf0) >> 4; sc->sc_txpwr_ofdm[3 + 6 + 4 + 1] = (data & 0xf000) >> 12; } else { for (i = 1, j = 0; i < 4; i += 2, j++) { error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2 + j, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[i + 6 + 4] = data & 0xf; sc->sc_txpwr_cck[i + 6 + 4 + 1] = (data & 0xf00) >> 8; sc->sc_txpwr_ofdm[i + 6 + 4] = (data & 0xf0) >> 4; sc->sc_txpwr_ofdm[i + 6 + 4 + 1] = (data & 0xf000) >> 12; } } fail: return (error); } static usb_error_t urtw_get_rfchip(struct urtw_softc *sc) { int ret; uint8_t data8; uint32_t data; usb_error_t error; if (sc->sc_flags & URTW_RTL8187B) { urtw_read8_m(sc, 0xe1, &data8); switch (data8) { case 0: sc->sc_flags |= URTW_RTL8187B_REV_B; break; case 1: sc->sc_flags |= URTW_RTL8187B_REV_D; break; case 2: sc->sc_flags |= URTW_RTL8187B_REV_E; break; default: device_printf(sc->sc_dev, "unknown type: %#x\n", data8); sc->sc_flags |= URTW_RTL8187B_REV_B; break; } } else { urtw_read32_m(sc, URTW_TX_CONF, &data); switch (data & URTW_TX_HWMASK) { case URTW_TX_R8187vD_B: sc->sc_flags |= URTW_RTL8187B; break; case URTW_TX_R8187vD: break; default: device_printf(sc->sc_dev, "unknown RTL8187L type: %#x\n", data & URTW_TX_HWMASK); break; } } error = urtw_eprom_read32(sc, URTW_EPROM_RFCHIPID, &data); if (error != 0) goto fail; switch (data & 0xff) { case URTW_EPROM_RFCHIPID_RTL8225U: error = urtw_8225_isv2(sc, &ret); if (error != 0) goto fail; if (ret == 0) { sc->sc_rf_init = urtw_8225_rf_init; sc->sc_rf_set_sens = urtw_8225_rf_set_sens; sc->sc_rf_set_chan = urtw_8225_rf_set_chan; sc->sc_rf_stop = urtw_8225_rf_stop; } else { sc->sc_rf_init = urtw_8225v2_rf_init; sc->sc_rf_set_chan = urtw_8225v2_rf_set_chan; sc->sc_rf_stop = urtw_8225_rf_stop; } sc->sc_max_sens = URTW_8225_RF_MAX_SENS; sc->sc_sens = URTW_8225_RF_DEF_SENS; break; case URTW_EPROM_RFCHIPID_RTL8225Z2: sc->sc_rf_init = urtw_8225v2b_rf_init; sc->sc_rf_set_chan = urtw_8225v2b_rf_set_chan; sc->sc_max_sens = URTW_8225_RF_MAX_SENS; sc->sc_sens = URTW_8225_RF_DEF_SENS; sc->sc_rf_stop = urtw_8225_rf_stop; break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported RF chip %d\n", data & 0xff); error = USB_ERR_INVAL; goto fail; } device_printf(sc->sc_dev, "%s rf %s hwrev %s\n", (sc->sc_flags & URTW_RTL8187B) ? "rtl8187b" : "rtl8187l", ((data & 0xff) == URTW_EPROM_RFCHIPID_RTL8225U) ? "rtl8225u" : "rtl8225z2", (sc->sc_flags & URTW_RTL8187B) ? ((data8 == 0) ? "b" : (data8 == 1) ? "d" : "e") : "none"); fail: return (error); } static usb_error_t urtw_led_init(struct urtw_softc *sc) { uint32_t rev; usb_error_t error; urtw_read8_m(sc, URTW_PSR, &sc->sc_psr); error = urtw_eprom_read32(sc, URTW_EPROM_SWREV, &rev); if (error != 0) goto fail; switch (rev & URTW_EPROM_CID_MASK) { case URTW_EPROM_CID_ALPHA0: sc->sc_strategy = URTW_SW_LED_MODE1; break; case URTW_EPROM_CID_SERCOMM_PS: sc->sc_strategy = URTW_SW_LED_MODE3; break; case URTW_EPROM_CID_HW_LED: sc->sc_strategy = URTW_HW_LED; break; case URTW_EPROM_CID_RSVD0: case URTW_EPROM_CID_RSVD1: default: sc->sc_strategy = URTW_SW_LED_MODE0; break; } sc->sc_gpio_ledpin = URTW_LED_PIN_GPIO0; fail: return (error); } static usb_error_t urtw_8225_rf_init(struct urtw_softc *sc) { unsigned int i; uint16_t data; usb_error_t error; error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); if (error) goto fail; error = urtw_8225_usb_init(sc); if (error) goto fail; urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008); urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */ urtw_write16_m(sc, URTW_BRSR, 0xffff); urtw_write32_m(sc, URTW_RF_PARA, 0x100044); error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_write8_m(sc, URTW_CONFIG3, 0x44); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; error = urtw_8185_rf_pins_enable(sc); if (error) goto fail; usb_pause_mtx(&sc->sc_mtx, 1000); for (i = 0; i < nitems(urtw_8225_rf_part1); i++) { urtw_8225_write(sc, urtw_8225_rf_part1[i].reg, urtw_8225_rf_part1[i].val); usb_pause_mtx(&sc->sc_mtx, 1); } usb_pause_mtx(&sc->sc_mtx, 100); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); usb_pause_mtx(&sc->sc_mtx, 200); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); usb_pause_mtx(&sc->sc_mtx, 200); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC3); for (i = 0; i < 95; i++) { urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225_rxgain[i]); } urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC4); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC5); for (i = 0; i < 128; i++) { urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80); usb_pause_mtx(&sc->sc_mtx, 1); } for (i = 0; i < nitems(urtw_8225_rf_part2); i++) { urtw_8187_write_phy_ofdm(sc, urtw_8225_rf_part2[i].reg, urtw_8225_rf_part2[i].val); usb_pause_mtx(&sc->sc_mtx, 1); } error = urtw_8225_setgain(sc, 4); if (error) goto fail; for (i = 0; i < nitems(urtw_8225_rf_part3); i++) { urtw_8187_write_phy_cck(sc, urtw_8225_rf_part3[i].reg, urtw_8225_rf_part3[i].val); usb_pause_mtx(&sc->sc_mtx, 1); } urtw_write8_m(sc, URTW_TESTR, 0x0d); error = urtw_8225_set_txpwrlvl(sc, 1); if (error) goto fail; urtw_8187_write_phy_cck(sc, 0x10, 0x9b); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8187_write_phy_ofdm(sc, 0x26, 0x90); usb_pause_mtx(&sc->sc_mtx, 1); /* TX ant A, 0x0 for B */ error = urtw_8185_tx_antenna(sc, 0x3); if (error) goto fail; urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002); error = urtw_8225_rf_set_chan(sc, 1); fail: return (error); } static usb_error_t urtw_8185_rf_pins_enable(struct urtw_softc *sc) { usb_error_t error = 0; urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1ff7); fail: return (error); } static usb_error_t urtw_8185_tx_antenna(struct urtw_softc *sc, uint8_t ant) { usb_error_t error; urtw_write8_m(sc, URTW_TX_ANTENNA, ant); usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_error_t urtw_8187_write_phy_ofdm_c(struct urtw_softc *sc, uint8_t addr, uint32_t data) { data = data & 0xff; return urtw_8187_write_phy(sc, addr, data); } static usb_error_t urtw_8187_write_phy_cck_c(struct urtw_softc *sc, uint8_t addr, uint32_t data) { data = data & 0xff; return urtw_8187_write_phy(sc, addr, data | 0x10000); } static usb_error_t urtw_8187_write_phy(struct urtw_softc *sc, uint8_t addr, uint32_t data) { uint32_t phyw; usb_error_t error; phyw = ((data << 8) | (addr | 0x80)); urtw_write8_m(sc, URTW_PHY_MAGIC4, ((phyw & 0xff000000) >> 24)); urtw_write8_m(sc, URTW_PHY_MAGIC3, ((phyw & 0x00ff0000) >> 16)); urtw_write8_m(sc, URTW_PHY_MAGIC2, ((phyw & 0x0000ff00) >> 8)); urtw_write8_m(sc, URTW_PHY_MAGIC1, ((phyw & 0x000000ff))); usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_error_t urtw_8225_setgain(struct urtw_softc *sc, int16_t gain) { usb_error_t error; urtw_8187_write_phy_ofdm(sc, 0x0d, urtw_8225_gain[gain * 4]); urtw_8187_write_phy_ofdm(sc, 0x1b, urtw_8225_gain[gain * 4 + 2]); urtw_8187_write_phy_ofdm(sc, 0x1d, urtw_8225_gain[gain * 4 + 3]); urtw_8187_write_phy_ofdm(sc, 0x23, urtw_8225_gain[gain * 4 + 1]); fail: return (error); } static usb_error_t urtw_8225_usb_init(struct urtw_softc *sc) { uint8_t data; usb_error_t error; urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 0); urtw_write8_m(sc, URTW_GPIO, 0); error = urtw_read8e(sc, 0x53, &data); if (error) goto fail; error = urtw_write8e(sc, 0x53, data | (1 << 7)); if (error) goto fail; urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 4); urtw_write8_m(sc, URTW_GPIO, 0x20); urtw_write8_m(sc, URTW_GP_ENABLE, 0); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x80); urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x80); urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x80); usb_pause_mtx(&sc->sc_mtx, 500); fail: return (error); } static usb_error_t urtw_8225_write_c(struct urtw_softc *sc, uint8_t addr, uint16_t data) { uint16_t d80, d82, d84; usb_error_t error; urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &d80); d80 &= URTW_RF_PINS_MAGIC1; urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &d82); urtw_read16_m(sc, URTW_RF_PINS_SELECT, &d84); d84 &= URTW_RF_PINS_MAGIC2; urtw_write16_m(sc, URTW_RF_PINS_ENABLE, d82 | URTW_RF_PINS_MAGIC3); urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84 | URTW_RF_PINS_MAGIC3); DELAY(10); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80); DELAY(10); error = urtw_8225_write_s16(sc, addr, 0x8225, &data); if (error != 0) goto fail; urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); DELAY(10); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84); usb_pause_mtx(&sc->sc_mtx, 2); fail: return (error); } static usb_error_t urtw_8225_write_s16(struct urtw_softc *sc, uint8_t addr, int index, uint16_t *data) { uint8_t buf[2]; uint16_t data16; struct usb_device_request req; usb_error_t error = 0; data16 = *data; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, addr); USETW(req.wIndex, index); USETW(req.wLength, sizeof(uint16_t)); buf[0] = (data16 & 0x00ff); buf[1] = (data16 & 0xff00) >> 8; error = urtw_do_request(sc, &req, buf); return (error); } static usb_error_t urtw_8225_rf_set_chan(struct urtw_softc *sc, int chan) { usb_error_t error; error = urtw_8225_set_txpwrlvl(sc, chan); if (error) goto fail; urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); usb_pause_mtx(&sc->sc_mtx, 10); fail: return (error); } static usb_error_t urtw_8225_rf_set_sens(struct urtw_softc *sc, int sens) { usb_error_t error; if (sens < 0 || sens > 6) return -1; if (sens > 4) urtw_8225_write(sc, URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC1); else urtw_8225_write(sc, URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC2); sens = 6 - sens; error = urtw_8225_setgain(sc, sens); if (error) goto fail; urtw_8187_write_phy_cck(sc, 0x41, urtw_8225_threshold[sens]); fail: return (error); } static usb_error_t urtw_8225_set_txpwrlvl(struct urtw_softc *sc, int chan) { int i, idx, set; uint8_t *cck_pwltable; uint8_t cck_pwrlvl_max, ofdm_pwrlvl_min, ofdm_pwrlvl_max; uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; usb_error_t error; cck_pwrlvl_max = 11; ofdm_pwrlvl_max = 25; /* 12 -> 25 */ ofdm_pwrlvl_min = 10; /* CCK power setting */ cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl; idx = cck_pwrlvl % 6; set = cck_pwrlvl / 6; cck_pwltable = (chan == 14) ? urtw_8225_txpwr_cck_ch14 : urtw_8225_txpwr_cck; urtw_write8_m(sc, URTW_TX_GAIN_CCK, urtw_8225_tx_gain_cck_ofdm[set] >> 1); for (i = 0; i < 8; i++) { urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwltable[idx * 8 + i]); } usb_pause_mtx(&sc->sc_mtx, 1); /* OFDM power setting */ ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ? ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min; ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; idx = ofdm_pwrlvl % 6; set = ofdm_pwrlvl / 6; error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); if (error) goto fail; urtw_8187_write_phy_ofdm(sc, 2, 0x42); urtw_8187_write_phy_ofdm(sc, 6, 0); urtw_8187_write_phy_ofdm(sc, 8, 0); urtw_write8_m(sc, URTW_TX_GAIN_OFDM, urtw_8225_tx_gain_cck_ofdm[set] >> 1); urtw_8187_write_phy_ofdm(sc, 0x5, urtw_8225_txpwr_ofdm[idx]); urtw_8187_write_phy_ofdm(sc, 0x7, urtw_8225_txpwr_ofdm[idx]); usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_error_t urtw_8225_rf_stop(struct urtw_softc *sc) { uint8_t data; usb_error_t error; urtw_8225_write(sc, 0x4, 0x1f); error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG3, &data); urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); if (sc->sc_flags & URTW_RTL8187B) { urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8187B_8225_ANAPARAM2_OFF); urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_OFF); urtw_write32_m(sc, URTW_ANAPARAM3, URTW_8187B_8225_ANAPARAM3_OFF); } else { urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8225_ANAPARAM2_OFF); urtw_write32_m(sc, URTW_ANAPARAM, URTW_8225_ANAPARAM_OFF); } urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; fail: return (error); } static usb_error_t urtw_8225v2_rf_init(struct urtw_softc *sc) { unsigned int i; uint16_t data; uint32_t data32; usb_error_t error; error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); if (error) goto fail; error = urtw_8225_usb_init(sc); if (error) goto fail; urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008); urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */ urtw_write16_m(sc, URTW_BRSR, 0xffff); urtw_write32_m(sc, URTW_RF_PARA, 0x100044); error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_write8_m(sc, URTW_CONFIG3, 0x44); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; error = urtw_8185_rf_pins_enable(sc); if (error) goto fail; usb_pause_mtx(&sc->sc_mtx, 500); for (i = 0; i < nitems(urtw_8225v2_rf_part1); i++) { urtw_8225_write(sc, urtw_8225v2_rf_part1[i].reg, urtw_8225v2_rf_part1[i].val); } usb_pause_mtx(&sc->sc_mtx, 50); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC1); for (i = 0; i < 95; i++) { urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225v2_rxgain[i]); } urtw_8225_write(sc, URTW_8225_ADDR_3_MAGIC, URTW_8225_ADDR_3_DATA_MAGIC1); urtw_8225_write(sc, URTW_8225_ADDR_5_MAGIC, URTW_8225_ADDR_5_DATA_MAGIC1); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC2); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); usb_pause_mtx(&sc->sc_mtx, 100); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); usb_pause_mtx(&sc->sc_mtx, 100); error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32); if (error != 0) goto fail; if (data32 != URTW_8225_ADDR_6_DATA_MAGIC1) device_printf(sc->sc_dev, "expect 0xe6!! (0x%x)\n", data32); if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2)) { urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); usb_pause_mtx(&sc->sc_mtx, 100); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); usb_pause_mtx(&sc->sc_mtx, 50); error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32); if (error != 0) goto fail; if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2)) device_printf(sc->sc_dev, "RF calibration failed\n"); } usb_pause_mtx(&sc->sc_mtx, 100); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC6); for (i = 0; i < 128; i++) { urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]); urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80); } for (i = 0; i < nitems(urtw_8225v2_rf_part2); i++) { urtw_8187_write_phy_ofdm(sc, urtw_8225v2_rf_part2[i].reg, urtw_8225v2_rf_part2[i].val); } error = urtw_8225v2_setgain(sc, 4); if (error) goto fail; for (i = 0; i < nitems(urtw_8225v2_rf_part3); i++) { urtw_8187_write_phy_cck(sc, urtw_8225v2_rf_part3[i].reg, urtw_8225v2_rf_part3[i].val); } urtw_write8_m(sc, URTW_TESTR, 0x0d); error = urtw_8225v2_set_txpwrlvl(sc, 1); if (error) goto fail; urtw_8187_write_phy_cck(sc, 0x10, 0x9b); urtw_8187_write_phy_ofdm(sc, 0x26, 0x90); /* TX ant A, 0x0 for B */ error = urtw_8185_tx_antenna(sc, 0x3); if (error) goto fail; urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002); error = urtw_8225_rf_set_chan(sc, 1); fail: return (error); } static usb_error_t urtw_8225v2_rf_set_chan(struct urtw_softc *sc, int chan) { usb_error_t error; error = urtw_8225v2_set_txpwrlvl(sc, chan); if (error) goto fail; urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); usb_pause_mtx(&sc->sc_mtx, 10); fail: return (error); } static usb_error_t urtw_8225_read(struct urtw_softc *sc, uint8_t addr, uint32_t *data) { int i; int16_t bit; uint8_t rlen = 12, wlen = 6; uint16_t o1, o2, o3, tmp; uint32_t d2w = ((uint32_t)(addr & 0x1f)) << 27; uint32_t mask = 0x80000000, value = 0; usb_error_t error; urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &o1); urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &o2); urtw_read16_m(sc, URTW_RF_PINS_SELECT, &o3); urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2 | URTW_RF_PINS_MAGIC4); urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3 | URTW_RF_PINS_MAGIC4); o1 &= ~URTW_RF_PINS_MAGIC4; urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN); DELAY(5); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1); DELAY(5); for (i = 0; i < (wlen / 2); i++, mask = mask >> 1) { bit = ((d2w & mask) != 0) ? 1 : 0; urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_CLK); DELAY(2); mask = mask >> 1; if (i == 2) break; bit = ((d2w & mask) != 0) ? 1 : 0; urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1); DELAY(1); } urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW); DELAY(2); mask = 0x800; for (i = 0; i < rlen; i++, mask = mask >> 1) { urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_read16_m(sc, URTW_RF_PINS_INPUT, &tmp); value |= ((tmp & URTW_BB_HOST_BANG_CLK) ? mask : 0); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW); DELAY(2); } urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN | URTW_BB_HOST_BANG_RW); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2); urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_OUTPUT_MAGIC1); if (data != NULL) *data = value; fail: return (error); } static usb_error_t urtw_8225v2_set_txpwrlvl(struct urtw_softc *sc, int chan) { int i; uint8_t *cck_pwrtable; uint8_t cck_pwrlvl_max = 15, ofdm_pwrlvl_max = 25, ofdm_pwrlvl_min = 10; uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; usb_error_t error; /* CCK power setting */ cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl; cck_pwrlvl += sc->sc_txpwr_cck_base; cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl; cck_pwrtable = (chan == 14) ? urtw_8225v2_txpwr_cck_ch14 : urtw_8225v2_txpwr_cck; for (i = 0; i < 8; i++) urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]); urtw_write8_m(sc, URTW_TX_GAIN_CCK, urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl]); usb_pause_mtx(&sc->sc_mtx, 1); /* OFDM power setting */ ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ? ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min; ofdm_pwrlvl += sc->sc_txpwr_ofdm_base; ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); if (error) goto fail; urtw_8187_write_phy_ofdm(sc, 2, 0x42); urtw_8187_write_phy_ofdm(sc, 5, 0x0); urtw_8187_write_phy_ofdm(sc, 6, 0x40); urtw_8187_write_phy_ofdm(sc, 7, 0x0); urtw_8187_write_phy_ofdm(sc, 8, 0x40); urtw_write8_m(sc, URTW_TX_GAIN_OFDM, urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl]); usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_error_t urtw_8225v2_setgain(struct urtw_softc *sc, int16_t gain) { uint8_t *gainp; usb_error_t error; /* XXX for A? */ gainp = urtw_8225v2_gain_bg; urtw_8187_write_phy_ofdm(sc, 0x0d, gainp[gain * 3]); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8187_write_phy_ofdm(sc, 0x1b, gainp[gain * 3 + 1]); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8187_write_phy_ofdm(sc, 0x1d, gainp[gain * 3 + 2]); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8187_write_phy_ofdm(sc, 0x21, 0x17); usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_error_t urtw_8225_isv2(struct urtw_softc *sc, int *ret) { uint32_t data; usb_error_t error; *ret = 1; urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_MAGIC5); urtw_write16_m(sc, URTW_RF_PINS_SELECT, URTW_RF_PINS_MAGIC5); urtw_write16_m(sc, URTW_RF_PINS_ENABLE, URTW_RF_PINS_MAGIC5); usb_pause_mtx(&sc->sc_mtx, 500); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC1); error = urtw_8225_read(sc, URTW_8225_ADDR_8_MAGIC, &data); if (error != 0) goto fail; if (data != URTW_8225_ADDR_8_DATA_MAGIC1) *ret = 0; else { error = urtw_8225_read(sc, URTW_8225_ADDR_9_MAGIC, &data); if (error != 0) goto fail; if (data != URTW_8225_ADDR_9_DATA_MAGIC1) *ret = 0; } urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC2); fail: return (error); } static usb_error_t urtw_8225v2b_rf_init(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; unsigned int i; uint8_t data8; usb_error_t error; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; /* * initialize extra registers on 8187 */ urtw_write16_m(sc, URTW_BRSR_8187B, 0xfff); /* retry limit */ urtw_read8_m(sc, URTW_CW_CONF, &data8); data8 |= URTW_CW_CONF_PERPACKET_RETRY; urtw_write8_m(sc, URTW_CW_CONF, data8); /* TX AGC */ urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8); data8 |= URTW_TX_AGC_CTL_PERPACKET_GAIN; urtw_write8_m(sc, URTW_TX_AGC_CTL, data8); /* Auto Rate Fallback Control */ #define URTW_ARFR 0x1e0 urtw_write16_m(sc, URTW_ARFR, 0xfff); urtw_read8_m(sc, URTW_RATE_FALLBACK, &data8); urtw_write8_m(sc, URTW_RATE_FALLBACK, data8 | URTW_RATE_FALLBACK_ENABLE); urtw_read8_m(sc, URTW_MSR, &data8); urtw_write8_m(sc, URTW_MSR, data8 & 0xf3); urtw_read8_m(sc, URTW_MSR, &data8); urtw_write8_m(sc, URTW_MSR, data8 | URTW_MSR_LINK_ENEDCA); urtw_write8_m(sc, URTW_ACM_CONTROL, sc->sc_acmctl); urtw_write16_m(sc, URTW_ATIM_WND, 2); urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100); #define URTW_FEMR_FOR_8187B 0x1d4 urtw_write16_m(sc, URTW_FEMR_FOR_8187B, 0xffff); /* led type */ urtw_read8_m(sc, URTW_CONFIG1, &data8); data8 = (data8 & 0x3f) | 0x80; urtw_write8_m(sc, URTW_CONFIG1, data8); /* applying MAC address again. */ urtw_write32_m(sc, URTW_MAC0, ((uint32_t *)ic->ic_macaddr)[0]); urtw_write16_m(sc, URTW_MAC4, ((uint32_t *)ic->ic_macaddr)[1] & 0xffff); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; urtw_write8_m(sc, URTW_WPA_CONFIG, 0); /* * MAC configuration */ for (i = 0; i < nitems(urtw_8225v2b_rf_part1); i++) urtw_write8_m(sc, urtw_8225v2b_rf_part1[i].reg, urtw_8225v2b_rf_part1[i].val); urtw_write16_m(sc, URTW_TID_AC_MAP, 0xfa50); urtw_write16_m(sc, URTW_INT_MIG, 0x0000); urtw_write32_m(sc, 0x1f0, 0); urtw_write32_m(sc, 0x1f4, 0); urtw_write8_m(sc, 0x1f8, 0); urtw_write32_m(sc, URTW_RF_TIMING, 0x4001); #define URTW_RFSW_CTRL 0x272 urtw_write16_m(sc, URTW_RFSW_CTRL, 0x569a); /* * initialize PHY */ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG3, &data8); urtw_write8_m(sc, URTW_CONFIG3, data8 | URTW_CONFIG3_ANAPARAM_WRITE); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; /* setup RFE initial timing */ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x0480); urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x2488); urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1fff); usb_pause_mtx(&sc->sc_mtx, 1100); for (i = 0; i < nitems(urtw_8225v2b_rf_part0); i++) { urtw_8225_write(sc, urtw_8225v2b_rf_part0[i].reg, urtw_8225v2b_rf_part0[i].val); usb_pause_mtx(&sc->sc_mtx, 1); } urtw_8225_write(sc, 0x00, 0x01b7); for (i = 0; i < 95; i++) { urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225v2b_rxgain[i]); usb_pause_mtx(&sc->sc_mtx, 1); } urtw_8225_write(sc, URTW_8225_ADDR_3_MAGIC, 0x080); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8225_write(sc, URTW_8225_ADDR_5_MAGIC, 0x004); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x0b7); usb_pause_mtx(&sc->sc_mtx, 1); usb_pause_mtx(&sc->sc_mtx, 3000); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0xc4d); usb_pause_mtx(&sc->sc_mtx, 2000); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0x44d); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x2bf); usb_pause_mtx(&sc->sc_mtx, 1); urtw_write8_m(sc, URTW_TX_GAIN_CCK, 0x03); urtw_write8_m(sc, URTW_TX_GAIN_OFDM, 0x07); urtw_write8_m(sc, URTW_TX_ANTENNA, 0x03); urtw_8187_write_phy_ofdm(sc, 0x80, 0x12); for (i = 0; i < 128; i++) { uint32_t addr, data; data = (urtw_8225z2_agc[i] << 8) | 0x0000008f; addr = ((i + 0x80) << 8) | 0x0000008e; urtw_8187_write_phy_ofdm(sc, data & 0x7f, (data >> 8) & 0xff); urtw_8187_write_phy_ofdm(sc, addr & 0x7f, (addr >> 8) & 0xff); urtw_8187_write_phy_ofdm(sc, 0x0e, 0x00); } urtw_8187_write_phy_ofdm(sc, 0x80, 0x10); for (i = 0; i < nitems(urtw_8225v2b_rf_part2); i++) urtw_8187_write_phy_ofdm(sc, i, urtw_8225v2b_rf_part2[i].val); urtw_write32_m(sc, URTW_8187B_AC_VO, (7 << 12) | (3 << 8) | 0x1c); urtw_write32_m(sc, URTW_8187B_AC_VI, (7 << 12) | (3 << 8) | 0x1c); urtw_write32_m(sc, URTW_8187B_AC_BE, (7 << 12) | (3 << 8) | 0x1c); urtw_write32_m(sc, URTW_8187B_AC_BK, (7 << 12) | (3 << 8) | 0x1c); urtw_8187_write_phy_ofdm(sc, 0x97, 0x46); urtw_8187_write_phy_ofdm(sc, 0xa4, 0xb6); urtw_8187_write_phy_ofdm(sc, 0x85, 0xfc); urtw_8187_write_phy_cck(sc, 0xc1, 0x88); fail: return (error); } static usb_error_t urtw_8225v2b_rf_set_chan(struct urtw_softc *sc, int chan) { usb_error_t error; error = urtw_8225v2b_set_txpwrlvl(sc, chan); if (error) goto fail; urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); usb_pause_mtx(&sc->sc_mtx, 10); fail: return (error); } static usb_error_t urtw_8225v2b_set_txpwrlvl(struct urtw_softc *sc, int chan) { int i; uint8_t *cck_pwrtable; uint8_t cck_pwrlvl_max = 15; uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; usb_error_t error; /* CCK power setting */ cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? ((sc->sc_flags & URTW_RTL8187B_REV_B) ? cck_pwrlvl_max : 22) : (cck_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 0 : 7)); cck_pwrlvl += sc->sc_txpwr_cck_base; cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl; cck_pwrtable = (chan == 14) ? urtw_8225v2b_txpwr_cck_ch14 : urtw_8225v2b_txpwr_cck; if (sc->sc_flags & URTW_RTL8187B_REV_B) cck_pwrtable += (cck_pwrlvl <= 6) ? 0 : ((cck_pwrlvl <= 11) ? 8 : 16); else cck_pwrtable += (cck_pwrlvl <= 5) ? 0 : ((cck_pwrlvl <= 11) ? 8 : ((cck_pwrlvl <= 17) ? 16 : 24)); for (i = 0; i < 8; i++) urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]); urtw_write8_m(sc, URTW_TX_GAIN_CCK, urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl] << 1); usb_pause_mtx(&sc->sc_mtx, 1); /* OFDM power setting */ ofdm_pwrlvl = (ofdm_pwrlvl > 15) ? ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 17 : 25) : (ofdm_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 2 : 10)); ofdm_pwrlvl += sc->sc_txpwr_ofdm_base; ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; urtw_write8_m(sc, URTW_TX_GAIN_OFDM, urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl] << 1); if (sc->sc_flags & URTW_RTL8187B_REV_B) { if (ofdm_pwrlvl <= 11) { urtw_8187_write_phy_ofdm(sc, 0x87, 0x60); urtw_8187_write_phy_ofdm(sc, 0x89, 0x60); } else { urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c); urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c); } } else { if (ofdm_pwrlvl <= 11) { urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c); urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c); } else if (ofdm_pwrlvl <= 17) { urtw_8187_write_phy_ofdm(sc, 0x87, 0x54); urtw_8187_write_phy_ofdm(sc, 0x89, 0x54); } else { urtw_8187_write_phy_ofdm(sc, 0x87, 0x50); urtw_8187_write_phy_ofdm(sc, 0x89, 0x50); } } usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_error_t urtw_read8e(struct urtw_softc *sc, int val, uint8_t *data) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, val | 0xfe00); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(uint8_t)); error = urtw_do_request(sc, &req, data); return (error); } static usb_error_t urtw_write8e(struct urtw_softc *sc, int val, uint8_t data) { struct usb_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, val | 0xfe00); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(uint8_t)); return (urtw_do_request(sc, &req, &data)); } static usb_error_t urtw_8180_set_anaparam(struct urtw_softc *sc, uint32_t val) { uint8_t data; usb_error_t error; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG3, &data); urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); urtw_write32_m(sc, URTW_ANAPARAM, val); urtw_read8_m(sc, URTW_CONFIG3, &data); urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; fail: return (error); } static usb_error_t urtw_8185_set_anaparam2(struct urtw_softc *sc, uint32_t val) { uint8_t data; usb_error_t error; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG3, &data); urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); urtw_write32_m(sc, URTW_ANAPARAM2, val); urtw_read8_m(sc, URTW_CONFIG3, &data); urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; fail: return (error); } static usb_error_t urtw_intr_enable(struct urtw_softc *sc) { usb_error_t error; urtw_write16_m(sc, URTW_INTR_MASK, 0xffff); fail: return (error); } static usb_error_t urtw_intr_disable(struct urtw_softc *sc) { usb_error_t error; urtw_write16_m(sc, URTW_INTR_MASK, 0); fail: return (error); } static usb_error_t urtw_reset(struct urtw_softc *sc) { uint8_t data; usb_error_t error; error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); if (error) goto fail; error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); if (error) goto fail; error = urtw_intr_disable(sc); if (error) goto fail; usb_pause_mtx(&sc->sc_mtx, 100); error = urtw_write8e(sc, 0x18, 0x10); if (error != 0) goto fail; error = urtw_write8e(sc, 0x18, 0x11); if (error != 0) goto fail; error = urtw_write8e(sc, 0x18, 0x00); if (error != 0) goto fail; usb_pause_mtx(&sc->sc_mtx, 100); urtw_read8_m(sc, URTW_CMD, &data); data = (data & 0x2) | URTW_CMD_RST; urtw_write8_m(sc, URTW_CMD, data); usb_pause_mtx(&sc->sc_mtx, 100); urtw_read8_m(sc, URTW_CMD, &data); if (data & URTW_CMD_RST) { device_printf(sc->sc_dev, "reset timeout\n"); goto fail; } error = urtw_set_mode(sc, URTW_EPROM_CMD_LOAD); if (error) goto fail; usb_pause_mtx(&sc->sc_mtx, 100); error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); if (error) goto fail; error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); if (error) goto fail; fail: return (error); } static usb_error_t urtw_led_ctl(struct urtw_softc *sc, int mode) { usb_error_t error = 0; switch (sc->sc_strategy) { case URTW_SW_LED_MODE0: error = urtw_led_mode0(sc, mode); break; case URTW_SW_LED_MODE1: error = urtw_led_mode1(sc, mode); break; case URTW_SW_LED_MODE2: error = urtw_led_mode2(sc, mode); break; case URTW_SW_LED_MODE3: error = urtw_led_mode3(sc, mode); break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED mode %d\n", sc->sc_strategy); error = USB_ERR_INVAL; break; } return (error); } static usb_error_t urtw_led_mode0(struct urtw_softc *sc, int mode) { switch (mode) { case URTW_LED_CTL_POWER_ON: sc->sc_gpio_ledstate = URTW_LED_POWER_ON_BLINK; break; case URTW_LED_CTL_TX: if (sc->sc_gpio_ledinprogress == 1) return (0); sc->sc_gpio_ledstate = URTW_LED_BLINK_NORMAL; sc->sc_gpio_blinktime = 2; break; case URTW_LED_CTL_LINK: sc->sc_gpio_ledstate = URTW_LED_ON; break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED mode 0x%x", mode); return (USB_ERR_INVAL); } switch (sc->sc_gpio_ledstate) { case URTW_LED_ON: if (sc->sc_gpio_ledinprogress != 0) break; urtw_led_on(sc, URTW_LED_GPIO); break; case URTW_LED_BLINK_NORMAL: if (sc->sc_gpio_ledinprogress != 0) break; sc->sc_gpio_ledinprogress = 1; sc->sc_gpio_blinkstate = (sc->sc_gpio_ledon != 0) ? URTW_LED_OFF : URTW_LED_ON; usb_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc); break; case URTW_LED_POWER_ON_BLINK: urtw_led_on(sc, URTW_LED_GPIO); usb_pause_mtx(&sc->sc_mtx, 100); urtw_led_off(sc, URTW_LED_GPIO); break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unknown LED status 0x%x", sc->sc_gpio_ledstate); return (USB_ERR_INVAL); } return (0); } static usb_error_t urtw_led_mode1(struct urtw_softc *sc, int mode) { return (USB_ERR_INVAL); } static usb_error_t urtw_led_mode2(struct urtw_softc *sc, int mode) { return (USB_ERR_INVAL); } static usb_error_t urtw_led_mode3(struct urtw_softc *sc, int mode) { return (USB_ERR_INVAL); } static usb_error_t urtw_led_on(struct urtw_softc *sc, int type) { usb_error_t error; if (type == URTW_LED_GPIO) { switch (sc->sc_gpio_ledpin) { case URTW_LED_PIN_GPIO0: urtw_write8_m(sc, URTW_GPIO, 0x01); urtw_write8_m(sc, URTW_GP_ENABLE, 0x00); break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED PIN type 0x%x", sc->sc_gpio_ledpin); error = USB_ERR_INVAL; goto fail; } } else { DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED type 0x%x", type); error = USB_ERR_INVAL; goto fail; } sc->sc_gpio_ledon = 1; fail: return (error); } static usb_error_t urtw_led_off(struct urtw_softc *sc, int type) { usb_error_t error; if (type == URTW_LED_GPIO) { switch (sc->sc_gpio_ledpin) { case URTW_LED_PIN_GPIO0: urtw_write8_m(sc, URTW_GPIO, URTW_GPIO_DATA_MAGIC1); urtw_write8_m(sc, URTW_GP_ENABLE, URTW_GP_ENABLE_DATA_MAGIC1); break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED PIN type 0x%x", sc->sc_gpio_ledpin); error = USB_ERR_INVAL; goto fail; } } else { DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED type 0x%x", type); error = USB_ERR_INVAL; goto fail; } sc->sc_gpio_ledon = 0; fail: return (error); } static void urtw_led_ch(void *arg) { struct urtw_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; ieee80211_runtask(ic, &sc->sc_led_task); } static void urtw_ledtask(void *arg, int pending) { struct urtw_softc *sc = arg; if (sc->sc_strategy != URTW_SW_LED_MODE0) { DPRINTF(sc, URTW_DEBUG_STATE, "could not process a LED strategy 0x%x", sc->sc_strategy); return; } URTW_LOCK(sc); urtw_led_blink(sc); URTW_UNLOCK(sc); } static usb_error_t urtw_led_blink(struct urtw_softc *sc) { uint8_t ing = 0; usb_error_t error; if (sc->sc_gpio_blinkstate == URTW_LED_ON) error = urtw_led_on(sc, URTW_LED_GPIO); else error = urtw_led_off(sc, URTW_LED_GPIO); sc->sc_gpio_blinktime--; if (sc->sc_gpio_blinktime == 0) ing = 1; else { if (sc->sc_gpio_ledstate != URTW_LED_BLINK_NORMAL && sc->sc_gpio_ledstate != URTW_LED_BLINK_SLOWLY && sc->sc_gpio_ledstate != URTW_LED_BLINK_CM3) ing = 1; } if (ing == 1) { if (sc->sc_gpio_ledstate == URTW_LED_ON && sc->sc_gpio_ledon == 0) error = urtw_led_on(sc, URTW_LED_GPIO); else if (sc->sc_gpio_ledstate == URTW_LED_OFF && sc->sc_gpio_ledon == 1) error = urtw_led_off(sc, URTW_LED_GPIO); sc->sc_gpio_blinktime = 0; sc->sc_gpio_ledinprogress = 0; return (0); } sc->sc_gpio_blinkstate = (sc->sc_gpio_blinkstate != URTW_LED_ON) ? URTW_LED_ON : URTW_LED_OFF; switch (sc->sc_gpio_ledstate) { case URTW_LED_BLINK_NORMAL: usb_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc); break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unknown LED status 0x%x", sc->sc_gpio_ledstate); return (USB_ERR_INVAL); } return (0); } static usb_error_t urtw_rx_enable(struct urtw_softc *sc) { uint8_t data; usb_error_t error; usbd_transfer_start((sc->sc_flags & URTW_RTL8187B) ? sc->sc_xfer[URTW_8187B_BULK_RX] : sc->sc_xfer[URTW_8187L_BULK_RX]); error = urtw_rx_setconf(sc); if (error != 0) goto fail; if ((sc->sc_flags & URTW_RTL8187B) == 0) { urtw_read8_m(sc, URTW_CMD, &data); urtw_write8_m(sc, URTW_CMD, data | URTW_CMD_RX_ENABLE); } fail: return (error); } static usb_error_t urtw_tx_enable(struct urtw_softc *sc) { uint8_t data8; uint32_t data; usb_error_t error; if (sc->sc_flags & URTW_RTL8187B) { urtw_read32_m(sc, URTW_TX_CONF, &data); data &= ~URTW_TX_LOOPBACK_MASK; data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK); data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK); data &= ~URTW_TX_SWPLCPLEN; data |= URTW_TX_HW_SEQNUM | URTW_TX_DISREQQSIZE | (7 << 8) | /* short retry limit */ (7 << 0) | /* long retry limit */ (7 << 21); /* MAX TX DMA */ urtw_write32_m(sc, URTW_TX_CONF, data); urtw_read8_m(sc, URTW_MSR, &data8); data8 |= URTW_MSR_LINK_ENEDCA; urtw_write8_m(sc, URTW_MSR, data8); return (error); } urtw_read8_m(sc, URTW_CW_CONF, &data8); data8 &= ~(URTW_CW_CONF_PERPACKET_CW | URTW_CW_CONF_PERPACKET_RETRY); urtw_write8_m(sc, URTW_CW_CONF, data8); urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8); data8 &= ~URTW_TX_AGC_CTL_PERPACKET_GAIN; data8 &= ~URTW_TX_AGC_CTL_PERPACKET_ANTSEL; data8 &= ~URTW_TX_AGC_CTL_FEEDBACK_ANT; urtw_write8_m(sc, URTW_TX_AGC_CTL, data8); urtw_read32_m(sc, URTW_TX_CONF, &data); data &= ~URTW_TX_LOOPBACK_MASK; data |= URTW_TX_LOOPBACK_NONE; data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK); data |= sc->sc_tx_retry << URTW_TX_DPRETRY_SHIFT; data |= sc->sc_rts_retry << URTW_TX_RTSRETRY_SHIFT; data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK); data |= URTW_TX_MXDMA_2048 | URTW_TX_CWMIN | URTW_TX_DISCW; data &= ~URTW_TX_SWPLCPLEN; data |= URTW_TX_NOICV; urtw_write32_m(sc, URTW_TX_CONF, data); urtw_read8_m(sc, URTW_CMD, &data8); urtw_write8_m(sc, URTW_CMD, data8 | URTW_CMD_TX_ENABLE); fail: return (error); } static usb_error_t urtw_rx_setconf(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t data; usb_error_t error; urtw_read32_m(sc, URTW_RX, &data); data = data &~ URTW_RX_FILTER_MASK; if (sc->sc_flags & URTW_RTL8187B) { data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA | URTW_RX_FILTER_MCAST | URTW_RX_FILTER_BCAST | URTW_RX_FIFO_THRESHOLD_NONE | URTW_MAX_RX_DMA_2048 | URTW_RX_AUTORESETPHY | URTW_RCR_ONLYERLPKT; } else { data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA; data = data | URTW_RX_FILTER_BCAST | URTW_RX_FILTER_MCAST; if (ic->ic_opmode == IEEE80211_M_MONITOR) { data = data | URTW_RX_FILTER_ICVERR; data = data | URTW_RX_FILTER_PWR; } if (sc->sc_crcmon == 1 && ic->ic_opmode == IEEE80211_M_MONITOR) data = data | URTW_RX_FILTER_CRCERR; data = data &~ URTW_RX_FIFO_THRESHOLD_MASK; data = data | URTW_RX_FIFO_THRESHOLD_NONE | URTW_RX_AUTORESETPHY; data = data &~ URTW_MAX_RX_DMA_MASK; data = data | URTW_MAX_RX_DMA_2048 | URTW_RCR_ONLYERLPKT; } /* XXX allmulti should not be checked here... */ if (ic->ic_opmode == IEEE80211_M_MONITOR || ic->ic_promisc > 0 || ic->ic_allmulti > 0) { data = data | URTW_RX_FILTER_CTL; data = data | URTW_RX_FILTER_ALLMAC; } else { data = data | URTW_RX_FILTER_NICMAC; data = data | URTW_RX_CHECK_BSSID; } urtw_write32_m(sc, URTW_RX, data); fail: return (error); } static struct mbuf * urtw_rxeof(struct usb_xfer *xfer, struct urtw_data *data, int *rssi_p, int8_t *nf_p) { int actlen, flen, rssi; struct ieee80211_frame *wh; struct mbuf *m, *mnew; struct urtw_softc *sc = data->sc; struct ieee80211com *ic = &sc->sc_ic; uint8_t noise = 0, rate; uint64_t mactime; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (sc->sc_flags & URTW_RTL8187B) { struct urtw_8187b_rxhdr *rx; if (actlen < sizeof(*rx) + IEEE80211_ACK_LEN) goto fail; rx = (struct urtw_8187b_rxhdr *)(data->buf + (actlen - (sizeof(struct urtw_8187b_rxhdr)))); flen = le32toh(rx->flag) & 0xfff; if (flen > actlen - sizeof(*rx)) goto fail; rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf; /* XXX correct? */ rssi = rx->rssi & URTW_RX_RSSI_MASK; noise = rx->noise; if (ieee80211_radiotap_active(ic)) mactime = rx->mactime; } else { struct urtw_8187l_rxhdr *rx; if (actlen < sizeof(*rx) + IEEE80211_ACK_LEN) goto fail; rx = (struct urtw_8187l_rxhdr *)(data->buf + (actlen - (sizeof(struct urtw_8187l_rxhdr)))); flen = le32toh(rx->flag) & 0xfff; if (flen > actlen - sizeof(*rx)) goto fail; rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf; /* XXX correct? */ rssi = rx->rssi & URTW_RX_8187L_RSSI_MASK; noise = rx->noise; if (ieee80211_radiotap_active(ic)) mactime = rx->mactime; } if (flen < IEEE80211_ACK_LEN) goto fail; mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) goto fail; m = data->m; data->m = mnew; data->buf = mtod(mnew, uint8_t *); /* finalize mbuf */ m->m_pkthdr.len = m->m_len = flen - IEEE80211_CRC_LEN; if (ieee80211_radiotap_active(ic)) { struct urtw_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_tsf = mactime; tap->wr_flags = 0; tap->wr_dbm_antsignal = (int8_t)rssi; } wh = mtod(m, struct ieee80211_frame *); if (IEEE80211_IS_DATA(wh)) sc->sc_currate = (rate > 0) ? rate : sc->sc_currate; *rssi_p = rssi; *nf_p = noise; /* XXX correct? */ return (m); fail: counter_u64_add(ic->ic_ierrors, 1); return (NULL); } static void urtw_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtw_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct mbuf *m = NULL; struct urtw_data *data; int8_t nf = -95; int rssi = 1; URTW_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_rx_active); if (data == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); m = urtw_rxeof(xfer, data, &rssi, &nf); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); /* FALLTHROUGH */ case USB_ST_SETUP: setup: data = STAILQ_FIRST(&sc->sc_rx_inactive); if (data == NULL) { KASSERT(m == NULL, ("mbuf isn't NULL")); return; } STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); /* * To avoid LOR we should unlock our private mutex here to call * ieee80211_input() because here is at the end of a USB * callback and safe to unlock. */ URTW_UNLOCK(sc); if (m != NULL) { if (m->m_pkthdr.len >= sizeof(struct ieee80211_frame_min)) { ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); } else ni = NULL; if (ni != NULL) { (void) ieee80211_input(ni, m, rssi, nf); /* node is no longer needed */ ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi, nf); m = NULL; } URTW_LOCK(sc); break; default: /* needs it to the inactive queue due to a error. */ data = STAILQ_FIRST(&sc->sc_rx_active); if (data != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto setup; } break; } } #define URTW_STATUS_TYPE_TXCLOSE 1 #define URTW_STATUS_TYPE_BEACON_INTR 0 static void urtw_txstatus_eof(struct usb_xfer *xfer) { struct urtw_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; int actlen, type, pktretry, seq; uint64_t val; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (actlen != sizeof(uint64_t)) return; val = le64toh(sc->sc_txstatus); type = (val >> 30) & 0x3; if (type == URTW_STATUS_TYPE_TXCLOSE) { pktretry = val & 0xff; seq = (val >> 16) & 0xff; if (pktretry == URTW_TX_MAXRETRY) counter_u64_add(ic->ic_oerrors, 1); DPRINTF(sc, URTW_DEBUG_TXSTATUS, "pktretry %d seq %#x\n", pktretry, seq); } } static void urtw_bulk_tx_status_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtw_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; void *dma_buf = usbd_xfer_get_frame_buffer(xfer, 0); URTW_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: urtw_txstatus_eof(xfer); /* FALLTHROUGH */ case USB_ST_SETUP: setup: memcpy(dma_buf, &sc->sc_txstatus, sizeof(uint64_t)); usbd_xfer_set_frame_len(xfer, 0, sizeof(uint64_t)); usbd_transfer_submit(xfer); break; default: if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto setup; } break; } } static void urtw_txeof(struct usb_xfer *xfer, struct urtw_data *data) { struct urtw_softc *sc = usbd_xfer_softc(xfer); URTW_ASSERT_LOCKED(sc); if (data->m) { /* XXX status? */ ieee80211_tx_complete(data->ni, data->m, 0); data->m = NULL; data->ni = NULL; } sc->sc_txtimer = 0; } static void urtw_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtw_softc *sc = usbd_xfer_softc(xfer); struct urtw_data *data; URTW_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next); urtw_txeof(xfer, data); STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); /* FALLTHROUGH */ case USB_ST_SETUP: setup: data = STAILQ_FIRST(&sc->sc_tx_pending); if (data == NULL) { DPRINTF(sc, URTW_DEBUG_XMIT, "%s: empty pending queue\n", __func__); return; } STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); usbd_transfer_submit(xfer); urtw_start(sc); break; default: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto setup; if (data->ni != NULL) { if_inc_counter(data->ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(data->ni); data->ni = NULL; } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto setup; } break; } } static struct urtw_data * _urtw_getbuf(struct urtw_softc *sc) { struct urtw_data *bf; bf = STAILQ_FIRST(&sc->sc_tx_inactive); if (bf != NULL) STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); else bf = NULL; if (bf == NULL) DPRINTF(sc, URTW_DEBUG_XMIT, "%s: %s\n", __func__, "out of xmit buffers"); return (bf); } static struct urtw_data * urtw_getbuf(struct urtw_softc *sc) { struct urtw_data *bf; URTW_ASSERT_LOCKED(sc); bf = _urtw_getbuf(sc); if (bf == NULL) DPRINTF(sc, URTW_DEBUG_XMIT, "%s: stop queue\n", __func__); return (bf); } static int urtw_isbmode(uint16_t rate) { return ((rate <= 22 && rate != 12 && rate != 18) || rate == 44) ? (1) : (0); } static uint16_t urtw_rate2dbps(uint16_t rate) { switch(rate) { case 12: case 18: case 24: case 36: case 48: case 72: case 96: case 108: return (rate * 2); default: break; } return (24); } static int urtw_compute_txtime(uint16_t framelen, uint16_t rate, uint8_t ismgt, uint8_t isshort) { uint16_t ceiling, frametime, n_dbps; if (urtw_isbmode(rate)) { if (ismgt || !isshort || rate == 2) frametime = (uint16_t)(144 + 48 + (framelen * 8 / (rate / 2))); else frametime = (uint16_t)(72 + 24 + (framelen * 8 / (rate / 2))); if ((framelen * 8 % (rate / 2)) != 0) frametime++; } else { n_dbps = urtw_rate2dbps(rate); ceiling = (16 + 8 * framelen + 6) / n_dbps + (((16 + 8 * framelen + 6) % n_dbps) ? 1 : 0); frametime = (uint16_t)(16 + 4 + 4 * ceiling + 6); } return (frametime); } /* * Callback from the 802.11 layer to update the * slot time based on the current setting. */ static void urtw_updateslot(struct ieee80211com *ic) { struct urtw_softc *sc = ic->ic_softc; ieee80211_runtask(ic, &sc->sc_updateslot_task); } static void urtw_updateslottask(void *arg, int pending) { struct urtw_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; int error; URTW_LOCK(sc); if ((sc->sc_flags & URTW_RUNNING) == 0) { URTW_UNLOCK(sc); return; } if (sc->sc_flags & URTW_RTL8187B) { urtw_write8_m(sc, URTW_SIFS, 0x22); if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SHSLOT); else urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SLOT); urtw_write8_m(sc, URTW_8187B_EIFS, 0x5b); urtw_write8_m(sc, URTW_CARRIER_SCOUNT, 0x5b); } else { urtw_write8_m(sc, URTW_SIFS, 0x22); if (sc->sc_state == IEEE80211_S_ASSOC && ic->ic_flags & IEEE80211_F_SHSLOT) urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SHSLOT); else urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SLOT); if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { urtw_write8_m(sc, URTW_DIFS, 0x14); urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14); urtw_write8_m(sc, URTW_CW_VAL, 0x73); } else { urtw_write8_m(sc, URTW_DIFS, 0x24); urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24); urtw_write8_m(sc, URTW_CW_VAL, 0xa5); } } fail: URTW_UNLOCK(sc); } static void urtw_sysctl_node(struct urtw_softc *sc) { #define URTW_SYSCTL_STAT_ADD32(c, h, n, p, d) \ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) struct sysctl_ctx_list *ctx; struct sysctl_oid_list *child, *parent; struct sysctl_oid *tree; struct urtw_stats *stats = &sc->sc_stats; ctx = device_get_sysctl_ctx(sc->sc_dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, NULL, "URTW statistics"); parent = SYSCTL_CHILDREN(tree); /* Tx statistics. */ tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD, NULL, "Tx MAC statistics"); child = SYSCTL_CHILDREN(tree); URTW_SYSCTL_STAT_ADD32(ctx, child, "1m", &stats->txrates[0], "1 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "2m", &stats->txrates[1], "2 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "5.5m", &stats->txrates[2], "5.5 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "6m", &stats->txrates[4], "6 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "9m", &stats->txrates[5], "9 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "11m", &stats->txrates[3], "11 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "12m", &stats->txrates[6], "12 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "18m", &stats->txrates[7], "18 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "24m", &stats->txrates[8], "24 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "36m", &stats->txrates[9], "36 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "48m", &stats->txrates[10], "48 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "54m", &stats->txrates[11], "54 Mbit/s"); #undef URTW_SYSCTL_STAT_ADD32 } static device_method_t urtw_methods[] = { DEVMETHOD(device_probe, urtw_match), DEVMETHOD(device_attach, urtw_attach), DEVMETHOD(device_detach, urtw_detach), DEVMETHOD_END }; static driver_t urtw_driver = { .name = "urtw", .methods = urtw_methods, .size = sizeof(struct urtw_softc) }; static devclass_t urtw_devclass; DRIVER_MODULE(urtw, uhub, urtw_driver, urtw_devclass, NULL, 0); MODULE_DEPEND(urtw, wlan, 1, 1, 1); MODULE_DEPEND(urtw, usb, 1, 1, 1); MODULE_VERSION(urtw, 1); USB_PNP_HOST_INFO(urtw_devs); Index: projects/capsicum-test/tests/sys/audit/Makefile =================================================================== --- projects/capsicum-test/tests/sys/audit/Makefile (revision 345770) +++ projects/capsicum-test/tests/sys/audit/Makefile (revision 345771) @@ -1,58 +1,60 @@ # $FreeBSD$ TESTSDIR= ${TESTSBASE}/sys/audit ATF_TESTS_C= file-attribute-access ATF_TESTS_C+= file-attribute-modify ATF_TESTS_C+= file-create ATF_TESTS_C+= file-delete ATF_TESTS_C+= file-close ATF_TESTS_C+= file-write ATF_TESTS_C+= file-read ATF_TESTS_C+= open ATF_TESTS_C+= ioctl ATF_TESTS_C+= network ATF_TESTS_C+= inter-process ATF_TESTS_C+= administrative ATF_TESTS_C+= process-control ATF_TESTS_C+= miscellaneous SRCS.file-attribute-access+= file-attribute-access.c SRCS.file-attribute-access+= utils.c SRCS.file-attribute-modify+= file-attribute-modify.c SRCS.file-attribute-modify+= utils.c SRCS.file-create+= file-create.c SRCS.file-create+= utils.c SRCS.file-delete+= file-delete.c SRCS.file-delete+= utils.c SRCS.file-close+= file-close.c SRCS.file-close+= utils.c SRCS.file-write+= file-write.c SRCS.file-write+= utils.c SRCS.file-read+= file-read.c SRCS.file-read+= utils.c SRCS.open+= open.c SRCS.open+= utils.c SRCS.ioctl+= ioctl.c SRCS.ioctl+= utils.c SRCS.network+= network.c SRCS.network+= utils.c SRCS.inter-process+= inter-process.c SRCS.inter-process+= utils.c SRCS.administrative+= administrative.c SRCS.administrative+= utils.c SRCS.process-control+= process-control.c SRCS.process-control+= utils.c SRCS.miscellaneous+= miscellaneous.c SRCS.miscellaneous+= utils.c TEST_METADATA+= timeout="30" TEST_METADATA+= required_user="root" TEST_METADATA+= is_exclusive="true" TEST_METADATA+= required_files="/etc/rc.d/auditd" WARNS?= 6 LDFLAGS+= -lbsm -lutil +CFLAGS.process-control.c+= -I${SRCTOP}/tests + .include Index: projects/capsicum-test/tests/sys/audit/process-control.c =================================================================== --- projects/capsicum-test/tests/sys/audit/process-control.c (revision 345770) +++ projects/capsicum-test/tests/sys/audit/process-control.c (revision 345771) @@ -1,1674 +1,1664 @@ /*- * Copyright (c) 2018 Aniket Pandey * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * 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 * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" +#include "freebsd_test_suite/macros.h" + static pid_t pid; static int filedesc, status; static struct pollfd fds[1]; static char pcregex[80]; static const char *auclass = "pc"; ATF_TC_WITH_CLEANUP(fork_success); ATF_TC_HEAD(fork_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "fork(2) call"); } ATF_TC_BODY(fork_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "fork.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* Check if fork(2) succeded. If so, exit from the child process */ ATF_REQUIRE((pid = fork()) != -1); if (pid) check_audit(fds, pcregex, pipefd); else _exit(0); } ATF_TC_CLEANUP(fork_success, tc) { cleanup(); } /* * No fork(2) in failure mode since possibilities for failure are only when * user is not privileged or when the number of processes exceed KERN_MAXPROC. */ ATF_TC_WITH_CLEANUP(_exit_success); ATF_TC_HEAD(_exit_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "_exit(2) call"); } ATF_TC_BODY(_exit_success, tc) { FILE *pipefd = setup(fds, auclass); ATF_REQUIRE((pid = fork()) != -1); if (pid) { snprintf(pcregex, sizeof(pcregex), "exit.*%d.*success", pid); check_audit(fds, pcregex, pipefd); } else _exit(0); } ATF_TC_CLEANUP(_exit_success, tc) { cleanup(); } /* * _exit(2) never returns, hence the auditing by default is always successful */ ATF_TC_WITH_CLEANUP(rfork_success); ATF_TC_HEAD(rfork_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "rfork(2) call"); } ATF_TC_BODY(rfork_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "rfork.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE((pid = rfork(RFPROC)) != -1); if (pid) check_audit(fds, pcregex, pipefd); else _exit(0); } ATF_TC_CLEANUP(rfork_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(rfork_failure); ATF_TC_HEAD(rfork_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "rfork(2) call"); } ATF_TC_BODY(rfork_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "rfork.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Invalid argument */ ATF_REQUIRE_EQ(-1, rfork(-1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(rfork_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(wait4_success); ATF_TC_HEAD(wait4_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "wait4(2) call"); } ATF_TC_BODY(wait4_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "wait4.*%d.*return,success", pid); ATF_REQUIRE((pid = fork()) != -1); if (pid) { FILE *pipefd = setup(fds, auclass); /* wpid = -1 : Wait for any child process */ ATF_REQUIRE(wait4(-1, &status, 0, NULL) != -1); check_audit(fds, pcregex, pipefd); } else _exit(0); } ATF_TC_CLEANUP(wait4_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(wait4_failure); ATF_TC_HEAD(wait4_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "wait4(2) call"); } ATF_TC_BODY(wait4_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "wait4.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: No child process to wait for */ ATF_REQUIRE_EQ(-1, wait4(-1, NULL, 0, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(wait4_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(wait6_success); ATF_TC_HEAD(wait6_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "wait6(2) call"); } ATF_TC_BODY(wait6_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "wait6.*%d.*return,success", pid); ATF_REQUIRE((pid = fork()) != -1); if (pid) { FILE *pipefd = setup(fds, auclass); ATF_REQUIRE(wait6(P_ALL, 0, &status, WEXITED, NULL,NULL) != -1); check_audit(fds, pcregex, pipefd); } else _exit(0); } ATF_TC_CLEANUP(wait6_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(wait6_failure); ATF_TC_HEAD(wait6_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "wait6(2) call"); } ATF_TC_BODY(wait6_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "wait6.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Invalid argument */ ATF_REQUIRE_EQ(-1, wait6(0, 0, NULL, 0, NULL, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(wait6_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(kill_success); ATF_TC_HEAD(kill_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "kill(2) call"); } ATF_TC_BODY(kill_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "kill.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* Don't send any signal to anyone, live in peace! */ ATF_REQUIRE_EQ(0, kill(0, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(kill_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(kill_failure); ATF_TC_HEAD(kill_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "kill(2) call"); } ATF_TC_BODY(kill_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "kill.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* * Failure reason: Non existent process with PID '-2' * Note: '-1' is not used as it means sending no signal to * all non-system processes: A successful invocation */ ATF_REQUIRE_EQ(-1, kill(0, -2)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(kill_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(chdir_success); ATF_TC_HEAD(chdir_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "chdir(2) call"); } ATF_TC_BODY(chdir_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "chdir.*/.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, chdir("/")); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(chdir_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(chdir_failure); ATF_TC_HEAD(chdir_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "chdir(2) call"); } ATF_TC_BODY(chdir_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "chdir.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Bad address */ ATF_REQUIRE_EQ(-1, chdir(NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(chdir_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(fchdir_success); ATF_TC_HEAD(fchdir_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "fchdir(2) call"); } ATF_TC_BODY(fchdir_success, tc) { /* Build an absolute path to the test-case directory */ char dirpath[50]; ATF_REQUIRE(getcwd(dirpath, sizeof(dirpath)) != NULL); ATF_REQUIRE((filedesc = open(dirpath, O_RDONLY)) != -1); /* Audit record generated by fchdir(2) does not contain filedesc */ pid = getpid(); snprintf(pcregex, sizeof(pcregex), "fchdir.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, fchdir(filedesc)); check_audit(fds, pcregex, pipefd); close(filedesc); } ATF_TC_CLEANUP(fchdir_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(fchdir_failure); ATF_TC_HEAD(fchdir_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "fchdir(2) call"); } ATF_TC_BODY(fchdir_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "fchdir.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Bad directory address */ ATF_REQUIRE_EQ(-1, fchdir(-1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(fchdir_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(chroot_success); ATF_TC_HEAD(chroot_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "chroot(2) call"); } ATF_TC_BODY(chroot_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "chroot.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* We don't want to change the root directory, hence '/' */ ATF_REQUIRE_EQ(0, chroot("/")); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(chroot_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(chroot_failure); ATF_TC_HEAD(chroot_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "chroot(2) call"); } ATF_TC_BODY(chroot_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "chroot.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, chroot(NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(chroot_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(umask_success); ATF_TC_HEAD(umask_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "umask(2) call"); } ATF_TC_BODY(umask_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "umask.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); umask(0); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(umask_success, tc) { cleanup(); } /* * umask(2) system call never fails. Hence, no test case for failure mode */ ATF_TC_WITH_CLEANUP(setuid_success); ATF_TC_HEAD(setuid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setuid(2) call"); } ATF_TC_BODY(setuid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setuid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* Since we're privileged, we'll let ourselves be privileged! */ ATF_REQUIRE_EQ(0, setuid(0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setuid_success, tc) { cleanup(); } /* * setuid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(seteuid_success); ATF_TC_HEAD(seteuid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "seteuid(2) call"); } ATF_TC_BODY(seteuid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "seteuid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* This time, we'll let ourselves be 'effectively' privileged! */ ATF_REQUIRE_EQ(0, seteuid(0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(seteuid_success, tc) { cleanup(); } /* * seteuid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setgid_success); ATF_TC_HEAD(setgid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setgid(2) call"); } ATF_TC_BODY(setgid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setgid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setgid(0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setgid_success, tc) { cleanup(); } /* * setgid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setegid_success); ATF_TC_HEAD(setegid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setegid(2) call"); } ATF_TC_BODY(setegid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setegid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setegid(0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setegid_success, tc) { cleanup(); } /* * setegid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setregid_success); ATF_TC_HEAD(setregid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setregid(2) call"); } ATF_TC_BODY(setregid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setregid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* setregid(-1, -1) does not change any real or effective GIDs */ ATF_REQUIRE_EQ(0, setregid(-1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setregid_success, tc) { cleanup(); } /* * setregid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setreuid_success); ATF_TC_HEAD(setreuid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setreuid(2) call"); } ATF_TC_BODY(setreuid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setreuid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* setreuid(-1, -1) does not change any real or effective UIDs */ ATF_REQUIRE_EQ(0, setreuid(-1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setreuid_success, tc) { cleanup(); } /* * setregid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setresuid_success); ATF_TC_HEAD(setresuid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setresuid(2) call"); } ATF_TC_BODY(setresuid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setresuid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* setresuid(-1, -1, -1) does not change real, effective & saved UIDs */ ATF_REQUIRE_EQ(0, setresuid(-1, -1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setresuid_success, tc) { cleanup(); } /* * setresuid(2) fails only when the current user is not root. So no test case * for failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setresgid_success); ATF_TC_HEAD(setresgid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setresgid(2) call"); } ATF_TC_BODY(setresgid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setresgid.*%d.*ret.*success", pid); FILE *pipefd = setup(fds, auclass); /* setresgid(-1, -1, -1) does not change real, effective & saved GIDs */ ATF_REQUIRE_EQ(0, setresgid(-1, -1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setresgid_success, tc) { cleanup(); } /* * setresgid(2) fails only when the current user is not root. So no test case * for failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(getresuid_success); ATF_TC_HEAD(getresuid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "getresuid(2) call"); } ATF_TC_BODY(getresuid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "getresuid.*%d.*ret.*success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, getresuid(NULL, NULL, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(getresuid_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(getresuid_failure); ATF_TC_HEAD(getresuid_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "getresuid(2) call"); } ATF_TC_BODY(getresuid_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "getresuid.*%d.*ret.*failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Invalid address "-1" */ ATF_REQUIRE_EQ(-1, getresuid((uid_t *)-1, NULL, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(getresuid_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(getresgid_success); ATF_TC_HEAD(getresgid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "getresgid(2) call"); } ATF_TC_BODY(getresgid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "getresgid.*%d.*ret.*success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, getresgid(NULL, NULL, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(getresgid_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(getresgid_failure); ATF_TC_HEAD(getresgid_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "getresgid(2) call"); } ATF_TC_BODY(getresgid_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "getresgid.*%d.*ret.*failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Invalid address "-1" */ ATF_REQUIRE_EQ(-1, getresgid((gid_t *)-1, NULL, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(getresgid_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setpriority_success); ATF_TC_HEAD(setpriority_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setpriority(2) call"); } ATF_TC_BODY(setpriority_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setpriority.*%d.*success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setpriority(PRIO_PROCESS, 0, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setpriority_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setpriority_failure); ATF_TC_HEAD(setpriority_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setpriority(2) call"); } ATF_TC_BODY(setpriority_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setpriority.*%d.*failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, setpriority(-1, -1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setpriority_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setgroups_success); ATF_TC_HEAD(setgroups_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setgroups(2) call"); } ATF_TC_BODY(setgroups_success, tc) { gid_t gids[5]; pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setgroups.*%d.*ret.*success", pid); /* Retrieve the current group access list to be used with setgroups */ ATF_REQUIRE(getgroups(sizeof(gids)/sizeof(gids[0]), gids) != -1); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setgroups(sizeof(gids)/sizeof(gids[0]), gids)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setgroups_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setgroups_failure); ATF_TC_HEAD(setgroups_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setgroups(2) call"); } ATF_TC_BODY(setgroups_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setgroups.*%d.*ret.*failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, setgroups(-1, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setgroups_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setpgrp_success); ATF_TC_HEAD(setpgrp_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setpgrp(2) call"); } ATF_TC_BODY(setpgrp_success, tc) { /* Main procedure is carried out from within the child process */ ATF_REQUIRE((pid = fork()) != -1); if (pid) { ATF_REQUIRE(wait(&status) != -1); } else { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setpgrp.*%d.*success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setpgrp(0, 0)); check_audit(fds, pcregex, pipefd); } } ATF_TC_CLEANUP(setpgrp_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setpgrp_failure); ATF_TC_HEAD(setpgrp_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setpgrp(2) call"); } ATF_TC_BODY(setpgrp_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setpgrp.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, setpgrp(-1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setpgrp_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setsid_success); ATF_TC_HEAD(setsid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setsid(2) call"); } ATF_TC_BODY(setsid_success, tc) { /* Main procedure is carried out from within the child process */ ATF_REQUIRE((pid = fork()) != -1); if (pid) { ATF_REQUIRE(wait(&status) != -1); } else { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setsid.*%d.*success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE(setsid() != -1); check_audit(fds, pcregex, pipefd); } } ATF_TC_CLEANUP(setsid_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setsid_failure); ATF_TC_HEAD(setsid_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setsid(2) call"); } ATF_TC_BODY(setsid_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setsid.*%d.*return,failure", pid); /* * Here, we are intentionally ignoring the output of the setsid() * call because it may or may not be a process leader already. But it * ensures that the next invocation of setsid() will definitely fail. */ setsid(); FILE *pipefd = setup(fds, auclass); /* * Failure reason: [EPERM] Creating a new session is not permitted * as the PID of calling process matches the PGID of a process group * created by premature setsid() call. */ ATF_REQUIRE_EQ(-1, setsid()); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setsid_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setrlimit_success); ATF_TC_HEAD(setrlimit_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setrlimit(2) call"); } ATF_TC_BODY(setrlimit_success, tc) { struct rlimit rlp; pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setrlimit.*%d.*ret.*success", pid); /* Retrieve the system resource consumption limit to be used later on */ ATF_REQUIRE_EQ(0, getrlimit(RLIMIT_FSIZE, &rlp)); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setrlimit(RLIMIT_FSIZE, &rlp)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setrlimit_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setrlimit_failure); ATF_TC_HEAD(setrlimit_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setrlimit(2) call"); } ATF_TC_BODY(setrlimit_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setrlimit.*%d.*ret.*failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, setrlimit(RLIMIT_FSIZE, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setrlimit_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(mlock_success); ATF_TC_HEAD(mlock_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "mlock(2) call"); } ATF_TC_BODY(mlock_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "mlock.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, mlock(NULL, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(mlock_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(mlock_failure); ATF_TC_HEAD(mlock_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "mlock(2) call"); } ATF_TC_BODY(mlock_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "mlock.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, mlock((void *)(-1), -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(mlock_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(munlock_success); ATF_TC_HEAD(munlock_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "munlock(2) call"); } ATF_TC_BODY(munlock_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "munlock.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, munlock(NULL, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(munlock_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(munlock_failure); ATF_TC_HEAD(munlock_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "munlock(2) call"); } ATF_TC_BODY(munlock_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "munlock.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, munlock((void *)(-1), -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(munlock_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(minherit_success); ATF_TC_HEAD(minherit_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "minherit(2) call"); } ATF_TC_BODY(minherit_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "minherit.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, minherit(NULL, 0, INHERIT_ZERO)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(minherit_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(minherit_failure); ATF_TC_HEAD(minherit_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "minherit(2) call"); } ATF_TC_BODY(minherit_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "minherit.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, minherit((void *)(-1), -1, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(minherit_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setlogin_success); ATF_TC_HEAD(setlogin_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setlogin(2) call"); } ATF_TC_BODY(setlogin_success, tc) { char *name; pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setlogin.*%d.*return,success", pid); /* Retrieve the current user's login name to be used with setlogin(2) */ ATF_REQUIRE((name = getlogin()) != NULL); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setlogin(name)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setlogin_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setlogin_failure); ATF_TC_HEAD(setlogin_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setlogin(2) call"); } ATF_TC_BODY(setlogin_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setlogin.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, setlogin(NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setlogin_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(rtprio_success); ATF_TC_HEAD(rtprio_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "rtprio(2) call"); } ATF_TC_BODY(rtprio_success, tc) { struct rtprio rtp; pid = getpid(); snprintf(pcregex, sizeof(pcregex), "rtprio.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, rtprio(RTP_LOOKUP, 0, &rtp)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(rtprio_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(rtprio_failure); ATF_TC_HEAD(rtprio_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "rtprio(2) call"); } ATF_TC_BODY(rtprio_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "rtprio.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, rtprio(-1, -1, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(rtprio_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(profil_success); ATF_TC_HEAD(profil_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "profil(2) call"); } ATF_TC_BODY(profil_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "profil.*%d.*return,success", pid); char samples[20]; FILE *pipefd = setup(fds, auclass); /* Set scale argument as 0 to disable profiling of current process */ ATF_REQUIRE_EQ(0, profil(samples, sizeof(samples), 0, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(profil_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(profil_failure); ATF_TC_HEAD(profil_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "profil(2) call"); } ATF_TC_BODY(profil_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "profil.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, profil((char *)(SIZE_MAX), -1, -1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(profil_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(ptrace_success); ATF_TC_HEAD(ptrace_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "ptrace(2) call"); } ATF_TC_BODY(ptrace_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "ptrace.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, ptrace(PT_TRACE_ME, 0, NULL, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(ptrace_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(ptrace_failure); ATF_TC_HEAD(ptrace_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "ptrace(2) call"); } ATF_TC_BODY(ptrace_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "ptrace.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, ptrace(-1, 0, NULL, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(ptrace_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(ktrace_success); ATF_TC_HEAD(ktrace_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "ktrace(2) call"); } ATF_TC_BODY(ktrace_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "ktrace.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, ktrace(NULL, KTROP_CLEAR, KTRFAC_SYSCALL, pid)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(ktrace_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(ktrace_failure); ATF_TC_HEAD(ktrace_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "ktrace(2) call"); } ATF_TC_BODY(ktrace_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "ktrace.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, ktrace(NULL, -1, -1, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(ktrace_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(procctl_success); ATF_TC_HEAD(procctl_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "procctl(2) call"); } ATF_TC_BODY(procctl_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "procctl.*%d.*return,success", pid); struct procctl_reaper_status reapstat; FILE *pipefd = setup(fds, auclass); /* Retrieve information about the reaper of current process (pid) */ ATF_REQUIRE_EQ(0, procctl(P_PID, pid, PROC_REAP_STATUS, &reapstat)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(procctl_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(procctl_failure); ATF_TC_HEAD(procctl_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "procctl(2) call"); } ATF_TC_BODY(procctl_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "procctl.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, procctl(-1, -1, -1, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(procctl_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(cap_enter_success); ATF_TC_HEAD(cap_enter_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "cap_enter(2) call"); } ATF_TC_BODY(cap_enter_success, tc) { - int capinfo; - size_t len = sizeof(capinfo); - const char *capname = "kern.features.security_capability_mode"; - ATF_REQUIRE_EQ(0, sysctlbyname(capname, &capinfo, &len, NULL, 0)); + ATF_REQUIRE_FEATURE("security_capability_mode"); - /* Without CAPABILITY_MODE enabled, cap_enter() returns ENOSYS */ - if (!capinfo) - atf_tc_skip("Capsicum is not enabled in the system"); - FILE *pipefd = setup(fds, auclass); ATF_REQUIRE((pid = fork()) != -1); if (pid) { snprintf(pcregex, sizeof(pcregex), "cap_enter.*%d.*return,success", pid); ATF_REQUIRE(wait(&status) != -1); check_audit(fds, pcregex, pipefd); } else { ATF_REQUIRE_EQ(0, cap_enter()); _exit(0); } } ATF_TC_CLEANUP(cap_enter_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(cap_getmode_success); ATF_TC_HEAD(cap_getmode_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "cap_getmode(2) call"); } ATF_TC_BODY(cap_getmode_success, tc) { - int capinfo, modep; - size_t len = sizeof(capinfo); - const char *capname = "kern.features.security_capability_mode"; - ATF_REQUIRE_EQ(0, sysctlbyname(capname, &capinfo, &len, NULL, 0)); + int modep; - /* Without CAPABILITY_MODE enabled, cap_getmode() returns ENOSYS */ - if (!capinfo) - atf_tc_skip("Capsicum is not enabled in the system"); + ATF_REQUIRE_FEATURE("security_capability_mode"); pid = getpid(); snprintf(pcregex, sizeof(pcregex), "cap_getmode.*%d.*success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, cap_getmode(&modep)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(cap_getmode_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(cap_getmode_failure); ATF_TC_HEAD(cap_getmode_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "cap_getmode(2) call"); } ATF_TC_BODY(cap_getmode_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "cap_getmode.*%d.*failure", pid); FILE *pipefd = setup(fds, auclass); /* cap_getmode(2) can either fail with EFAULT or ENOSYS */ ATF_REQUIRE_EQ(-1, cap_getmode(NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(cap_getmode_failure, tc) { cleanup(); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, fork_success); ATF_TP_ADD_TC(tp, _exit_success); ATF_TP_ADD_TC(tp, rfork_success); ATF_TP_ADD_TC(tp, rfork_failure); ATF_TP_ADD_TC(tp, wait4_success); ATF_TP_ADD_TC(tp, wait4_failure); ATF_TP_ADD_TC(tp, wait6_success); ATF_TP_ADD_TC(tp, wait6_failure); ATF_TP_ADD_TC(tp, kill_success); ATF_TP_ADD_TC(tp, kill_failure); ATF_TP_ADD_TC(tp, chdir_success); ATF_TP_ADD_TC(tp, chdir_failure); ATF_TP_ADD_TC(tp, fchdir_success); ATF_TP_ADD_TC(tp, fchdir_failure); ATF_TP_ADD_TC(tp, chroot_success); ATF_TP_ADD_TC(tp, chroot_failure); ATF_TP_ADD_TC(tp, umask_success); ATF_TP_ADD_TC(tp, setuid_success); ATF_TP_ADD_TC(tp, seteuid_success); ATF_TP_ADD_TC(tp, setgid_success); ATF_TP_ADD_TC(tp, setegid_success); ATF_TP_ADD_TC(tp, setreuid_success); ATF_TP_ADD_TC(tp, setregid_success); ATF_TP_ADD_TC(tp, setresuid_success); ATF_TP_ADD_TC(tp, setresgid_success); ATF_TP_ADD_TC(tp, getresuid_success); ATF_TP_ADD_TC(tp, getresuid_failure); ATF_TP_ADD_TC(tp, getresgid_success); ATF_TP_ADD_TC(tp, getresgid_failure); ATF_TP_ADD_TC(tp, setpriority_success); ATF_TP_ADD_TC(tp, setpriority_failure); ATF_TP_ADD_TC(tp, setgroups_success); ATF_TP_ADD_TC(tp, setgroups_failure); ATF_TP_ADD_TC(tp, setpgrp_success); ATF_TP_ADD_TC(tp, setpgrp_failure); ATF_TP_ADD_TC(tp, setsid_success); ATF_TP_ADD_TC(tp, setsid_failure); ATF_TP_ADD_TC(tp, setrlimit_success); ATF_TP_ADD_TC(tp, setrlimit_failure); ATF_TP_ADD_TC(tp, mlock_success); ATF_TP_ADD_TC(tp, mlock_failure); ATF_TP_ADD_TC(tp, munlock_success); ATF_TP_ADD_TC(tp, munlock_failure); ATF_TP_ADD_TC(tp, minherit_success); ATF_TP_ADD_TC(tp, minherit_failure); ATF_TP_ADD_TC(tp, setlogin_success); ATF_TP_ADD_TC(tp, setlogin_failure); ATF_TP_ADD_TC(tp, rtprio_success); ATF_TP_ADD_TC(tp, rtprio_failure); ATF_TP_ADD_TC(tp, profil_success); ATF_TP_ADD_TC(tp, profil_failure); ATF_TP_ADD_TC(tp, ptrace_success); ATF_TP_ADD_TC(tp, ptrace_failure); ATF_TP_ADD_TC(tp, ktrace_success); ATF_TP_ADD_TC(tp, ktrace_failure); ATF_TP_ADD_TC(tp, procctl_success); ATF_TP_ADD_TC(tp, procctl_failure); ATF_TP_ADD_TC(tp, cap_enter_success); ATF_TP_ADD_TC(tp, cap_getmode_success); ATF_TP_ADD_TC(tp, cap_getmode_failure); return (atf_no_error()); } Index: projects/capsicum-test =================================================================== --- projects/capsicum-test (revision 345770) +++ projects/capsicum-test (revision 345771) Property changes on: projects/capsicum-test ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r345746-345770