/* * Copyright (c) 2023 joshua stein * * 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 #include #include #include "wi-fi.h" struct scsi_inquiry { unsigned char deviceType; unsigned char deviceQualifier; unsigned char version; unsigned char responseFormat; unsigned char additionalLength; unsigned char vendor; short reserved; unsigned char vendorID[8]; unsigned char productID[16]; unsigned char revision[4]; unsigned char vendor2[20]; unsigned char reserved2[42]; unsigned char vendor3[158]; }; enum { SCSI_READ, SCSI_WRITE }; unsigned char scsi_data[2048]; struct SCSIInstr tib[2]; short scsi_io(unsigned short scsi_id, unsigned char *cdb, unsigned long cdb_len, short io_type, unsigned long len); short scsi_find_wifi(void) { struct scsi_inquiry inq; short scsi_stat, scsi_msg; unsigned char cdb[16] = { 0 }; short i, scsi_id; cdb[0] = 0x12; /* inquiry */ cdb[4] = 5; /* length */ tib[0].scOpcode = scNoInc; tib[0].scParam1 = (unsigned long)&inq; tib[0].scParam2 = 5; tib[1].scOpcode = scStop; tib[1].scParam1 = 0; tib[1].scParam2 = 0; SCSIReset(); for (scsi_id = 0; scsi_id <= 7; scsi_id++) { for (i = 0; i <= 1; i++) { if (SCSIGet() != noErr) goto scan_failed; if (SCSISelect(scsi_id) != noErr) break; if (SCSICmd((Ptr)&cdb, 6) != noErr) { SCSIComplete(&scsi_stat, &scsi_msg, 300); goto scan_failed; } memset(&inq, 0, sizeof(inq)); if (SCSIRead((Ptr)&tib) != noErr) { SCSIComplete(&scsi_stat, &scsi_msg, 300); goto scan_failed; } if (i == 0) { cdb[4] += inq.additionalLength; tib[0].scParam2 += inq.additionalLength; } if (SCSIComplete(&scsi_stat, &scsi_msg, 300) != noErr) goto scan_failed; if (i == 0) /* re-inquire with full length */ continue; inq.vendorID[sizeof(inq.vendorID) - 1] = '\0'; inq.productID[sizeof(inq.productID) - 1] = '\0'; DEBUG_LOG(("scsi[%d]: v \"%s\", p \"%s\"", scsi_id, inq.vendorID, inq.productID)); if (memcmp(inq.vendorID, "Dayna", 5) != 0 || memcmp(inq.productID, "SCSI/Link", 9) != 0) break; if (scsi_wifi_info(scsi_id, NULL)) return scsi_id; DEBUG_LOG(("scsi[%d]: matched vendor/product but no info", scsi_id)); } } scan_failed: return WIFI_SCSI_ID_NONE; } bool scsi_wifi_scan(short scsi_id) { unsigned char cdb[6]; memset(wifi_scan_networks, 0, sizeof(wifi_scan_networks)); memset(cdb, 0, sizeof(cdb)); cdb[0] = BLUESCSI_NETWORK_WIFI_CMD; cdb[1] = BLUESCSI_NETWORK_WIFI_CMD_SCAN; cdb[4] = 0x01; wifi_scan_started = Time; return (scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, 1) != -1); } bool scsi_wifi_scan_finished(short scsi_id) { unsigned char cdb[6]; memset(cdb, 0, sizeof(cdb)); cdb[0] = BLUESCSI_NETWORK_WIFI_CMD; cdb[1] = BLUESCSI_NETWORK_WIFI_CMD_COMPLETE; cdb[4] = 0x01; if (scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, 1) == -1) return true; if (scsi_data[0] == 1) return true; return false; } short scsi_wifi_scan_results(short scsi_id, struct wifi_network_entry *resp, short count) { unsigned char cdb[6]; size_t size, net_count; memset(cdb, 0, sizeof(cdb)); cdb[0] = BLUESCSI_NETWORK_WIFI_CMD; cdb[1] = BLUESCSI_NETWORK_WIFI_CMD_SCAN_RESULTS; cdb[3] = (sizeof(scsi_data) >> 8) & 0xff; cdb[4] = sizeof(scsi_data) & 0xff; if (scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, sizeof(scsi_data)) == -1) return 0; size = (scsi_data[0] << 8) | scsi_data[1]; net_count = size / sizeof(struct wifi_network_entry); if (net_count > count) net_count = count; memset(resp, 0, sizeof(struct wifi_network_entry) * count); memcpy(resp, scsi_data + 2, sizeof(struct wifi_network_entry) * count); return net_count; } bool scsi_wifi_info(short scsi_id, struct wifi_network_entry *resp) { static struct wifi_network_entry wifi_info; unsigned char cdb[6]; short size; memset(cdb, 0, sizeof(cdb)); cdb[0] = BLUESCSI_NETWORK_WIFI_CMD; cdb[1] = BLUESCSI_NETWORK_WIFI_CMD_INFO; size = sizeof(struct wifi_network_entry) + 2; cdb[3] = (size >> 8) & 0xff; cdb[4] = size & 0xff; if (scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, size) == -1) return false; if (resp != NULL) { memset(resp, 0, sizeof(struct wifi_network_entry)); /* skip returned size */ memcpy(resp, scsi_data + 2, sizeof(struct wifi_network_entry)); } return true; } bool scsi_wifi_join(short scsi_id, struct wifi_join_request *wjr) { unsigned char cdb[6]; unsigned short size; unsigned char n; memset(cdb, 0, sizeof(cdb)); cdb[0] = BLUESCSI_NETWORK_WIFI_CMD; cdb[1] = BLUESCSI_NETWORK_WIFI_CMD_JOIN; size = sizeof(struct wifi_join_request); cdb[3] = (size >> 8) & 0xff; cdb[4] = size & 0xff; memcpy(scsi_data, wjr, sizeof(struct wifi_join_request)); return (scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_WRITE, size) != -1); } short scsi_io(unsigned short scsi_id, unsigned char *cdb, unsigned long cdb_len, short io_type, unsigned long len) { short scsi_stat, scsi_msg; short ret, ioret; if (SCSIGet() != noErr) { warn("SCSIGet failed"); return -1; } if (SCSISelect(scsi_id) != noErr) { warn("SCSISelect failed"); return -1; } if (SCSICmd((Ptr)cdb, cdb_len) != noErr) { SCSIComplete(&scsi_stat, &scsi_msg, 300); warn("SCSICmd failed"); return -1; } if (len) { switch (io_type) { case SCSI_READ: memset(scsi_data, 0, sizeof(scsi_data)); tib[0].scOpcode = scNoInc; tib[0].scParam1 = (long)&scsi_data; tib[0].scParam2 = len; tib[1].scOpcode = scStop; tib[1].scParam1 = 0; tib[1].scParam2 = 0; ioret = SCSIRead((Ptr)&tib); break; case SCSI_WRITE: tib[0].scOpcode = scNoInc; tib[0].scParam1 = (long)&scsi_data; tib[0].scParam2 = len; tib[1].scOpcode = scStop; tib[1].scParam1 = 0; tib[1].scParam2 = 0; ioret = SCSIWrite((Ptr)&tib); break; } } else { tib[0].scOpcode = scStop; tib[0].scParam1 = 0; tib[0].scParam2 = 0; ioret = noErr; } /* complete and free the bus before responding to the read/write */ if (SCSIComplete(&scsi_stat, &scsi_msg, 300 /* 1/60 ticks */) != noErr) { DEBUG_LOG(("SCSIComplete failed")); return -1; } if (ioret != noErr && ioret != scPhaseErr) DEBUG_LOG(("SCSIRead/Write failed: %d", ioret)); if (scsi_stat != noErr) DEBUG_LOG(("SCSIComplete bad status: %d", scsi_stat)); return len; }