jcs
/wifi_da
/amendments
/1
*: Re-import with some System 6 fixes
System 6 doesn't seem to support popupMenuProc, so kind of implement it
ourselves.
Since we're running in a DA we can't access screenBits.bounds, so create
CenterDialog to work around it by creating a new Grafport and checking its
size which will be the same as the screen. I got this tip from a 1988
Usenet post by Rich Siegel.
jcs made amendment 1 about 1 year ago
--- about.c Tue Aug 22 23:19:40 2023
+++ about.c Tue Aug 22 23:19:40 2023
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023 joshua stein <jcs@jcs.org>
+ *
+ * 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 "wi-fi.h"
+
+void
+about(void)
+{
+ DialogPtr dp;
+ short hit;
+ Handle h;
+
+ dp = GetNewDialog(OwnedResourceID(ABOUT_DIALOG_ID), 0L, (WindowPtr)-1L);
+
+ ParamText("\pWi-Fi Desk Accessory", "\pCopyright 2023, joshua stein",
+ "\pWorld Wide Web: http://jcs.org/",
+ "\pI miss you, Carl");
+
+ CenterDialog(dp, false);
+ ShowWindow(dp);
+
+ for (;;) {
+ ModalDialog(0, &hit);
+ if (hit == ok)
+ break;
+ }
+
+ DisposDialog(dp);
+}
--- main.c Tue Aug 22 23:31:42 2023
+++ main.c Tue Aug 22 23:31:42 2023
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2023 joshua stein <jcs@jcs.org>
+ *
+ * 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 <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include "wi-fi.h"
+
+/* device control entry */
+DCtlPtr dce;
+
+short da_state = STATE_CLOSED;
+short wifi_scsi_id = WIFI_SCSI_ID_FINDING;
+struct wifi_network_entry wifi_cur_info = { 0 };
+struct wifi_network_entry wifi_scan_networks[10] = { 0 };
+MenuHandle menu, apple_menu;
+short menuID;
+WindowPtr win = 0L;
+Cursor watch;
+
+short main(cntrlParam *p, DCtlPtr d, short n);
+void da_open(void);
+void da_control(short code, short *parm);
+void da_close(void);
+bool handle_menu(short i);
+void handle_event(EventRecord *e);
+void handle_timer(void);
+
+short
+main(cntrlParam *p, DCtlPtr d, short n)
+{
+ if (d->dCtlStorage == 0) {
+ /* open */
+ if (n == 0) {
+ SysBeep(3);
+ CloseDriver(d->dCtlRefNum);
+ }
+ return 0;
+ }
+
+ dce = d;
+ dce->dCtlFlags &= ~dCtlEnable;
+
+ switch (n) {
+ case 0:
+ da_open();
+ break;
+ case 2:
+ da_control(p->csCode, p->csParam);
+ break;
+ case 4:
+ da_close();
+ break;
+ }
+
+ dce->dCtlFlags |= dCtlEnable;
+
+ return 0;
+}
+
+/*
+ * This is called each time the DA is selected from the Apple menu,
+ * even if we're already open. However, the Device Manager resets
+ * the "dCtlFlags", "dCtlMenu", "dCtlDelay", and "dCtlEMask" fields
+ * of the device control entry from the corresponding fields of the
+ * device header each time the desk accessory is opened.
+ */
+void
+da_open(void)
+{
+ CursHandle h;
+ bool dispose;
+
+ dce->dCtlFlags |= dNeedLock | dNeedGoodBye;
+
+ if (win)
+ SelectWindow(win);
+
+ if (da_state != STATE_CLOSED) {
+ dce->dCtlMenu = menuID;
+ return;
+ }
+
+ da_state = STATE_INIT;
+
+ SetResLoad(false);
+ h = GetCursor(watchCursor);
+ dispose = (GetHandleSize((Handle)h) == 0);
+ SetResLoad(true);
+ BlockMove(*(h=GetCursor(watchCursor)), &watch, sizeof(Cursor));
+ if (dispose)
+ ReleaseResource((Handle)h);
+
+ dce->dCtlMenu = menuID = OwnedResourceID(MAIN_MENU_ID);
+ menu = GetMenu(menuID);
+ (**menu).menuID = menuID;
+ InsertMenu(menu = GetMenu(menuID), 0);
+
+ DrawMenuBar();
+
+ dce->dCtlWindow = create_window(dce->dCtlRefNum);
+
+ HiliteMenu(0);
+}
+
+void
+da_control(short code, short *param)
+{
+ switch (code) {
+ case accCursor:
+ handle_timer();
+ break;
+ case accMenu:
+ handle_menu(param[1]);
+ break;
+ case accEvent:
+ handle_event(*((EventRecord **)param));
+ break;
+ case goodbye:
+ break;
+ }
+}
+
+void
+da_close(void)
+{
+ DeleteMenu(dce->dCtlMenu);
+ DrawMenuBar();
+ dce->dCtlMenu = 0;
+ DisposeMenu(menu);
+ destroy_window();
+ dce->dCtlWindow = 0;
+ da_state = STATE_CLOSED;
+}
+
+bool
+handle_menu(short i)
+{
+ bool ret = true;
+
+ switch (i) {
+ case MAIN_MENU_ABOUT_ID:
+ about();
+ break;
+ case MAIN_MENU_QUIT_ID:
+ CloseDriver(dce->dCtlRefNum);
+ break;
+ default:
+ ret = false;
+ }
+
+ HiliteMenu(0);
+ return ret;
+}
+
+void
+handle_event(EventRecord *e)
+{
+ short key, mk;
+
+ switch (e->what) {
+ case updateEvt:
+ update_window(false);
+ break;
+ case mouseDown:
+ window_mousedown(e->where);
+ update_window(false);
+ break;
+ case activateEvt:
+ activate_window((bool)(e->modifiers & activeFlag));
+ break;
+ case keyDown:
+ case autoKey:
+ key = e->message & charCodeMask;
+ if ((e->modifiers & cmdKey) != 0) {
+ mk = MenuKey(key);
+ if (mk != 0 && handle_menu(mk))
+ break;
+ }
+ break;
+ }
+}
+
+void
+handle_timer(void)
+{
+ static long last_update = 0;
+
+ if (!win)
+ return;
+
+ switch (da_state) {
+ case STATE_SCANNING:
+ /* check every second */
+ if ((Ticks - last_update) < (60 * 1))
+ break;
+ last_update = Ticks;
+ if (!scsi_wifi_scan_finished(wifi_scsi_id))
+ break;
+ da_state = STATE_IDLE;
+ update_wifi_ssid_list();
+ break;
+ case STATE_IDLE:
+ /* refresh RSSI every 5 seconds */
+ if ((Ticks - last_update) < (60 * 5))
+ break;
+ last_update = Ticks;
+ update_wifi_cur_info();
+ break;
+ default:
+ return;
+ }
+}
+
+long
+OwnedResourceID(long n)
+{
+ return (0xc000 + ((~(dce->dCtlRefNum)) << 5) + n);
+}
+
+void
+warn(char *format, ...)
+{
+ static char warn_str[255];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(warn_str, format, ap);
+ va_end(ap);
+
+ CtoPstr(warn_str);
+ ParamText(warn_str, "", "", "");
+ Alert(OwnedResourceID(ERROR_DIALOG_ID), 0L);
+}
+
+void
+CenterDialog(DialogPtr dp, bool titlebar)
+{
+ GrafPtr tport;
+ Rect bounds;
+ short width, height;
+
+ /* screenBits is not available from a DA */
+ tport = (GrafPtr)NewPtr(sizeof(GrafPort));
+ OpenPort(tport);
+
+ width = ((DialogPeek)dp)->window.port.portRect.right;
+ height = ((DialogPeek)dp)->window.port.portRect.bottom;
+
+ bounds.left = ((tport->portRect.right - tport->portRect.left) / 2) -
+ (width / 2);
+ bounds.top = ((tport->portRect.bottom - tport->portRect.top) / 2) -
+ (height / 2) + (titlebar ? GetMBarHeight() : 0);
+ bounds.right = bounds.left + width;
+ bounds.bottom = bounds.top + height;
+
+ ClosePort(tport);
+
+ MoveWindow(dp, bounds.left, bounds.top, false);
+}
+
+
+/* C Extensions */
+
+/*
+ * Appends src to string dst of size dsize (unlike strncat, dsize is the
+ * full size of dst, not space left). At most dsize-1 characters
+ * will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
+ * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
+ * If retval >= dsize, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t dsize)
+{
+ const char *odst = dst;
+ const char *osrc = src;
+ size_t n = dsize;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end. */
+ while (n-- != 0 && *dst != '\0')
+ dst++;
+ dlen = dst - odst;
+ n = dsize - dlen;
+
+ if (n-- == 0)
+ return(dlen + strlen(src));
+ while (*src != '\0') {
+ if (n != 0) {
+ *dst++ = *src;
+ n--;
+ }
+ src++;
+ }
+ *dst = '\0';
+
+ return(dlen + (src - osrc)); /* count does not include NUL */
+}
+
+/*
+ * Copy string src to buffer dst of size dsize. At most dsize-1
+ * chars will be copied. Always NUL terminates (unless dsize == 0).
+ * Returns strlen(src); if retval >= dsize, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t dsize)
+{
+ const char *osrc = src;
+ size_t nleft = dsize;
+
+ /* Copy as many bytes as will fit. */
+ if (nleft != 0) {
+ while (--nleft != 0) {
+ if ((*dst++ = *src++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src. */
+ if (nleft == 0) {
+ if (dsize != 0)
+ *dst = '\0'; /* NUL-terminate dst */
+ while (*src++)
+ ;
+ }
+
+ return(src - osrc - 1); /* count does not include NUL */
+}
--- scsi.c Fri Jan 1 06:29:17 1904
+++ scsi.c Fri Jan 1 06:29:17 1904
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2023 joshua stein <jcs@jcs.org>
+ *
+ * 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 <SCSI.h>
+#include <string.h>
+#include <stdio.h>
+#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[2000];
+struct SCSIInstr tib[2];
+short stat, msg, err;
+
+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;
+ 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)
+ return -1;
+
+ if (SCSISelect(scsi_id) != noErr)
+ break;
+
+ if (SCSICmd((Ptr)&cdb, 6) != noErr) {
+ SCSIComplete(&stat, &msg, 300);
+ return -1;
+ }
+
+ if (SCSIRead((Ptr)&tib) != noErr) {
+ SCSIComplete(&stat, &msg, 300);
+ return -1;
+ }
+
+ if (i == 0) {
+ cdb[4] += inq.additionalLength;
+ tib[0].scParam2 += inq.additionalLength;
+ }
+
+ if (SCSIComplete(&stat, &msg, 300) != noErr)
+ return -1;
+
+ if (memcmp(inq.vendorID, "Dayna", 5) == 0 &&
+ memcmp(inq.productID, "SCSI/Link", 9) == 0)
+ return scsi_id;
+ }
+ }
+
+ return -1;
+}
+
+short
+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[2] = BLUESCSI_NETWORK_WIFI_CMD_SCAN;
+ cdb[4] = 0x01;
+
+ scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, 1);
+
+ return 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[2] = BLUESCSI_NETWORK_WIFI_CMD_COMPLETE;
+ cdb[4] = 0x01;
+
+ scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, 1);
+
+ 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[2] = BLUESCSI_NETWORK_WIFI_CMD_SCAN_RESULTS;
+ cdb[3] = (sizeof(scsi_data) >> 8) & 0xff;
+ cdb[4] = sizeof(scsi_data) & 0xff;
+
+ scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, sizeof(scsi_data));
+
+ 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;
+}
+
+short
+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[2] = BLUESCSI_NETWORK_WIFI_CMD_INFO;
+
+ size = sizeof(struct wifi_network_entry) + 2;
+ cdb[3] = (size >> 8) & 0xff;
+ cdb[4] = size & 0xff;
+
+ scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, size);
+
+ memset(resp, 0, sizeof(struct wifi_network_entry));
+ /* skip returned size */
+ memcpy(resp, scsi_data + 2, sizeof(struct wifi_network_entry));
+
+ return 1;
+}
+
+short
+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[2] = 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));
+
+ scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_WRITE, size);
+
+ return 1;
+}
+
+short
+scsi_io(unsigned short scsi_id, unsigned char *cdb, unsigned long cdb_len,
+ short io_type, unsigned long len)
+{
+ short ret, ioret, stat;
+
+ 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(&stat, &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 ((ret = SCSIComplete(&stat, &msg, 300 /* 1/60 ticks */)) != noErr) {
+ warn("SCSIComplete failed");
+ return -1;
+ }
+
+ if (ioret != noErr && ioret != scPhaseErr)
+ warn("SCSIRead/Write failed");
+
+ if (stat != noErr)
+ warn("SCSIComplete bad status");
+
+ return len;
+}
--- wi-fi.h Tue Aug 22 23:09:56 2023
+++ wi-fi.h Tue Aug 22 23:09:56 2023
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2023 joshua stein <jcs@jcs.org>
+ *
+ * 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.
+ */
+
+#ifndef __WIFI_H__
+#define __WIFI_H__
+
+#include <stdlib.h>
+
+#define MAIN_MENU_ID 1
+#define MAIN_MENU_ABOUT_ID 1
+#define MAIN_MENU_QUIT_ID 3
+
+#define SSID_MENU_ID 2
+
+#define ERROR_DIALOG_ID 0
+
+#define PASSWORD_DIALOG_ID 1
+#define PASSWORD_DIALOG_PASSWORD_FIELD_ID 3
+
+#define ABOUT_DIALOG_ID 2
+
+#define BLUESCSI_NETWORK_WIFI_CMD 0x1c
+#define BLUESCSI_NETWORK_WIFI_CMD_SCAN 0x01
+#define BLUESCSI_NETWORK_WIFI_CMD_COMPLETE 0x02
+#define BLUESCSI_NETWORK_WIFI_CMD_SCAN_RESULTS 0x03
+#define BLUESCSI_NETWORK_WIFI_CMD_INFO 0x04
+#define BLUESCSI_NETWORK_WIFI_CMD_JOIN 0x05
+
+#define hiword(x) (((short *)&(x))[0])
+#define loword(x) (((short *)&(x))[1])
+
+#define nitems(what) (sizeof((what)) / sizeof((what)[0]))
+#define member_size(type, member) sizeof(((type *)0)->member)
+
+typedef Boolean bool;
+typedef signed long ssize_t;
+
+typedef short SICN[16];
+typedef SICN **SICNHandle;
+
+enum state {
+ STATE_CLOSED,
+ STATE_INIT,
+ STATE_FINDING,
+ STATE_SCANNING,
+ STATE_IDLE
+};
+extern short da_state;
+
+enum signal_icon_id {
+ SIGNAL_NO_DEVICE = 0,
+ SIGNAL_FINDING_DEVICE = 1,
+ SIGNAL_NONE = 2,
+ SIGNAL_1 = 3,
+ SIGNAL_2 = 4,
+ SIGNAL_3 = 5,
+ SIGNAL_4 = 6
+};
+
+struct wifi_network_entry {
+ char ssid[64];
+ unsigned char bssid[6];
+ char rssi;
+ unsigned char channel;
+ char flags;
+ char _padding;
+};
+
+struct wifi_join_request {
+ char ssid[64];
+ char key[64];
+ unsigned char channel;
+ unsigned char _padding;
+};
+
+extern WindowPtr win;
+
+extern short wifi_scsi_id;
+enum {
+ WIFI_SCSI_ID_FINDING = -1,
+ WIFI_SCSI_ID_NONE = -2
+};
+extern struct wifi_network_entry wifi_cur_info;
+extern struct wifi_network_entry wifi_scan_networks[10];
+extern Cursor watch;
+
+void about(void);
+
+size_t strlcat(char *dst, const char *src, size_t dsize);
+size_t strlcpy(char *dst, const char *src, size_t dsize);
+char * strndup(const char *str, size_t maxlen);
+
+WindowPtr create_window(short ctlrefnum);
+void update_window(bool force);
+void update_wifi_cur_info(void);
+void update_wifi_ssid_list(void);
+void destroy_window(void);
+void activate_window(bool activate);
+long OwnedResourceID(long n);
+void warn(char *format, ...);
+void CenterDialog(DialogPtr dp, bool titlebar);
+void window_mousedown(Point p);
+
+short scsi_find_wifi(void);
+short scsi_wifi_scan(short lun);
+bool scsi_wifi_scan_finished(short lun);
+short scsi_wifi_info(short lun, struct wifi_network_entry *resp);
+short scsi_wifi_scan_results(short scsi_id,
+ struct wifi_network_entry *resp, short count);
+short scsi_wifi_join(short scsi_id, struct wifi_join_request *wjr);
+
+#endif
--- Wi-Fi DA.π.r Tue Aug 22 23:33:58 2023
+++ Wi-Fi DA.π.r Tue Aug 22 23:33:58 2023
@@ -0,0 +1,123 @@
+data 'ALRT' (-16000) {
+ $"0046 0092 00AB 0175 C180 5555" /* .F.í.´.u¡ÄUU */
+};
+
+data 'DITL' (-16000) {
+ $"0001 0000 0000 0046 0050 005A 008C 0402" /* .......F.P.Z.å.. */
+ $"4F4B 0000 0000 0009 000C 003F 00D8 8808" /* OK.....∆...?.ÿà. */
+ $"5E30 5E31 5E32 5E33" /* ^0^1^2^3 */
+};
+
+data 'DITL' (-15999) {
+ $"0004 0000 0000 0050 0136 0064 017F 0407" /* .......P.6.d.... */
+ $"436F 6E6E 6563 745B 0000 0000 0050 00E6" /* Connect[.....P.. */
+ $"0064 012F 0406 4361 6E63 656C 0000 0000" /* .d./..Cancel.... */
+ $"0032 005A 0042 0118 1000 0000 0000 0032" /* .2.Z.B.........2 */
+ $"000A 0042 0055 8809 5061 7373 776F 7264" /* ...B.Uà∆Password */
+ $"3A72 0000 0000 000A 000A 0029 0182 882B" /* :r.........).Çà+ */
+ $"5468 6520 5769 2D46 6920 6E65 7477 6F72" /* The Wi-Fi networ */
+ $"6B20 225E 3022 2072 6571 7569 7265 7320" /* k "^0" requires */
+ $"6120 7061 7373 776F 7264 2E00" /* a password.. */
+};
+
+data 'DITL' (-15998) {
+ $"0001 0000 0000 0078 0064 008C 00A0 0402" /* .......x.d.å.†.. */
+ $"4F4B 0000 0000 000A 000A 0071 00F0 880D" /* OK.........q..ଠ*/
+ $"5E30 0D5E 310D 0D5E 320D 0D5E 3300" /* ^0¬^1¬¬^2¬¬^3. */
+};
+
+data 'MENU' (-15998) {
+ $"C182 0000 0000 0000 0000 FFFF FFFF 0553" /* ¡Ç.............S */
+ $"5349 4473 0B53 6361 6E6E 696E 672E 2E2E" /* SIDs.Scanning... */
+ $"0000 0000 00" /* ..... */
+};
+
+data 'MENU' (-15999) {
+ $"C181 0000 0000 0000 0000 FFFF FFFF 0557" /* ¡Å.............W */
+ $"692D 4669 0E41 626F 7574 2057 692D 4669" /* i-Fi.About Wi-Fi */
+ $"2E2E 2E00 0000 0000" /* ........ */
+};
+
+data 'DLOG' (-15999) {
+ $"0066 0036 00D6 01C4 0010 0000 0100 0000" /* .f.6.÷.ƒ........ */
+ $"0000 C181 0E57 692D 4669 2050 6173 7377" /* ..¡Å.Wi-Fi Passw */
+ $"6F72 641C 680A" /* ord.h. */
+};
+
+data 'DLOG' (-16000) {
+ $"002A 00A8 0064 0162 0001 0100 0000 0000" /* .*.®.d.b........ */
+ $"0000 C182 0000 280A" /* ..¡Ç..(. */
+};
+
+data 'DLOG' (-15998) {
+ $"0064 00A2 00FC 019D 0001 0000 0000 0000" /* .d.¢...ù........ */
+ $"0000 C182 0000 280A" /* ..¡Ç..(. */
+};
+
+data 'SICN' (-16000) {
+ $"0000 6006 700E 3FFC 1FF8 3E7C 67E6 4FF2" /* ..`.p.?...>|g.O. */
+ $"1BD8 17E8 0FF0 1C38 399C 718E 6006 0000" /* .ÿ.....89úqé`... */
+ $"0000 0FC0 1860 3010 67C8 482C 438C 444C" /* ...¿.`0.g»H,CåDL */
+ $"410C 2018 1878 0FFC 07FE 001F 000F 0006" /* A. ..x.......... */
+ $"0000 0000 0000 0550 0AA8 1004 22A2 4550" /* .......P.®.."¢EP */
+ $"0808 1140 02A0 0400 0080 0100 0000 0000" /* ...@.†...Ä...... */
+ $"0000 0000 0000 0550 0AA8 1004 22A2 4550" /* .......P.®.."¢EP */
+ $"0808 1140 02A0 0400 0180 0180 0000 0000" /* ...@.†...Ä.Ä.... */
+ $"0000 0000 0000 0550 0AA8 1004 22A2 4550" /* .......P.®.."¢EP */
+ $"0808 13C0 07E0 0420 0180 0180 0000 0000" /* ...¿... .Ä.Ä.... */
+ $"0000 0000 0000 0550 0AA8 1004 27E2 4FF0" /* .......P.®..'.O. */
+ $"1818 13C8 07E0 0420 0180 0180 0000 0000" /* ...»... .Ä.Ä.... */
+ $"0000 0000 0000 0FF0 1FF8 300C 67E6 4FF2" /* ..........0.g.O. */
+ $"1818 13C8 07E0 0420 0180 0180 0000 0000" /* ...»... .Ä.Ä.... */
+};
+
+data 'ICN#' (-16000) {
+ $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */
+ $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */
+ $"0000 0000 003F F800 007F FC00 01E0 0F00" /* .....?.......... */
+ $"0380 0380 070F E1C0 063F F8C0 0078 3C00" /* .Ä.Ä...¿.?.¿.x<. */
+ $"00E0 0E00 00C7 C600 000F E000 001C 7000" /* .....«∆.......p. */
+ $"0018 3000 0001 0000 0003 8000 0001 0000" /* ..0.......Ä..... */
+ $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */
+ $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */
+ $"0000 0000 0000 0000 FFFF FFFE FFFF FFFF" /* ................ */
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */
+ $"7FFF FFFF 0000 0000 0000 0000 0000 0000" /* ................ */
+};
+
+data 'BNDL' (-16000) {
+ $"7769 6669 0000 0001 4652 4546 0000 0000" /* wifi....FREF.... */
+ $"0080 4943 4E23 0000 0000 C180" /* .ÄICN#....¡Ä */
+};
+
+data 'vers' (1) {
+ $"0100 8000 0000 0331 2E30 2631 2E30 20A9" /* ..Ä....1.0&1.0 © */
+ $"2032 3032 332C 206A 6F73 6875 6120 7374" /* 2023, joshua st */
+ $"6569 6E20 3C6A 6373 406A 6373 2E6F 7267" /* ein <jcs@jcs.org */
+ $"3E" /* > */
+};
+
+data 'ics#' (-16000) {
+ $"0000 0000 0000 0FF0 1FF8 300C 67E6 4FF2" /* ..........0.g.O. */
+ $"1818 13C8 07E0 0420 0180 0180 0000 0000" /* ...»... .Ä.Ä.... */
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */
+};
+
+data 'wifi' (0, "Owner resource") {
+ $"00" /* . */
+};
+
+data 'FREF' (-16000) {
+ $"6466 696C 0000 00" /* dfil... */
+};
+
+data 'FREF' (128) {
+ $"6466 696C 0000 00" /* dfil... */
+};
+
--- window.c Tue Aug 22 23:18:00 2023
+++ window.c Tue Aug 22 23:18:00 2023
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2023 joshua stein <jcs@jcs.org>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include "wi-fi.h"
+
+#define NO_DEVICE_TEXT "\pNo Device Found"
+#define NO_NETWORK_TEXT "(No Network)"
+
+SICNHandle signal_icons;
+ControlHandle ssid_list;
+MenuHandle ssid_menu;
+Rect ssid_menu_rect;
+
+WindowPtr
+create_window(short ctlrefnum)
+{
+ GrafPtr savePort;
+ Rect bounds = { 50, 320, 75, 500 }; /* tlbr */
+
+ GetPort(&savePort);
+
+ win = NewWindow(0L, &bounds, "\pWi-Fi", true, rDocProc, 0L, true, 0L);
+ ((WindowPeek)win)->windowKind = ctlrefnum;
+
+ signal_icons = (SICNHandle)GetResource('SICN', OwnedResourceID(0));
+ if (signal_icons == NULL)
+ warn("Can't find SICN");
+
+ ssid_menu = GetMenu(OwnedResourceID(SSID_MENU_ID));
+ SetItem(ssid_menu, 1, "\pFinding Wi-Fi...");
+
+ SetRect(&ssid_menu_rect, 30, 3, 170, 21); /* ltrb */
+
+#if 0 /* system 6 does not seem to support popupMenuProc */
+ ssid_list = NewControl(win, &ssid_menu_rect, 0, true, 0,
+ OwnedResourceID(SSID_MENU_ID), 0, popupMenuProc /* + popupFixedWidth*/, 0L);
+#endif
+
+ update_window(true);
+
+ if (wifi_scsi_id == WIFI_SCSI_ID_FINDING)
+ wifi_scsi_id = scsi_find_wifi();
+
+ if (wifi_scsi_id == WIFI_SCSI_ID_NONE) {
+ da_state = STATE_IDLE;
+ SetItem(ssid_menu, 1, NO_DEVICE_TEXT);
+ update_window(true);
+ } else {
+ scsi_wifi_info(wifi_scsi_id, &wifi_cur_info);
+ da_state = STATE_SCANNING;
+ scsi_wifi_scan(wifi_scsi_id);
+ }
+
+ SetPort(savePort);
+
+ return win;
+}
+
+void
+update_window(bool force)
+{
+ Str255 menuText;
+ GrafPtr savePort;
+ Point p;
+ short i, icon;
+ RgnHandle tmpRgn;
+ Rect r, destRect;
+ BitMap bm;
+
+ GetPort(&savePort);
+ SetPort(win);
+
+ if (!force)
+ BeginUpdate(win);
+
+ EraseRect(&win->portRect);
+ DrawControls(win);
+
+ /* system 6 does not seem to support popupMenuProc, fake it */
+ FrameRect(&ssid_menu_rect);
+ MoveTo(ssid_menu_rect.left + 1, ssid_menu_rect.bottom);
+ LineTo(ssid_menu_rect.right, ssid_menu_rect.bottom);
+ MoveTo(ssid_menu_rect.right, ssid_menu_rect.top + 1);
+ LineTo(ssid_menu_rect.right, ssid_menu_rect.bottom);
+ MoveTo(ssid_menu_rect.left + 15, 16);
+ TextFont(systemFont);
+ GetItem(ssid_menu, 1, &menuText);
+ DrawString(menuText);
+
+ HLock(signal_icons);
+
+ SetRect(&destRect, 6, 4, 22, 20); /* ltrb */
+
+ if (wifi_scsi_id == WIFI_SCSI_ID_NONE)
+ icon = SIGNAL_NO_DEVICE;
+ else if (wifi_scsi_id == WIFI_SCSI_ID_FINDING)
+ icon = SIGNAL_FINDING_DEVICE;
+ else if (wifi_cur_info.ssid[0] == '\0')
+ icon = SIGNAL_NONE;
+ else if (wifi_cur_info.rssi >= -60)
+ icon = SIGNAL_4;
+ else if (wifi_cur_info.rssi >= -70)
+ icon = SIGNAL_3;
+ else if (wifi_cur_info.rssi >= -75)
+ icon = SIGNAL_2;
+ else
+ icon = SIGNAL_1;
+
+ bm.baseAddr = (Ptr)(*signal_icons)[icon];
+ bm.rowBytes = 2;
+ SetRect(&bm.bounds, 0, 0, 16, 16);
+ CopyBits(&bm, &win->portBits, &bm.bounds, &destRect, srcOr, NULL);
+
+ HUnlock(signal_icons);
+
+ if (!force)
+ EndUpdate(win);
+
+ ValidRect(&win->portRect);
+ SetPort(savePort);
+}
+
+void
+update_wifi_cur_info(void)
+{
+ struct wifi_network_entry old_info;
+
+ if (wifi_scsi_id == WIFI_SCSI_ID_NONE)
+ return;
+
+ memcpy(&old_info, &wifi_cur_info, sizeof(old_info));
+
+ scsi_wifi_info(wifi_scsi_id, &wifi_cur_info);
+
+ if (memcmp(&old_info, &wifi_cur_info, sizeof(old_info)) != 0)
+ update_window(true);
+}
+
+void
+update_wifi_ssid_list(void)
+{
+ struct wifi_network_entry tmpnets[10] = { 0 };
+ char ssid[64] = { 0 };
+ short count, n, m, mitem;
+
+ if (wifi_scsi_id == WIFI_SCSI_ID_NONE)
+ return;
+
+ count = scsi_wifi_scan_results(wifi_scsi_id, wifi_scan_networks,
+ nitems(wifi_scan_networks));
+ if (count == 0) {
+ memcpy(&wifi_scan_networks, &wifi_cur_info,
+ sizeof(wifi_cur_info));
+ count = 1;
+ } else {
+ memcpy(&tmpnets[0], &wifi_cur_info, sizeof(wifi_cur_info));
+
+ for (n = 0, m = 1; n < count; n++) {
+ if (strcmp(wifi_scan_networks[n].ssid,
+ wifi_cur_info.ssid) == 0)
+ continue;
+
+ memcpy(&tmpnets[m++], &wifi_scan_networks[n],
+ sizeof(tmpnets[0]));
+ }
+
+ memcpy(&wifi_scan_networks, &tmpnets,
+ sizeof(wifi_scan_networks));
+ }
+
+ if (wifi_scan_networks[0].ssid[0] == '\0')
+ strlcpy(wifi_scan_networks[0].ssid, NO_NETWORK_TEXT,
+ sizeof(wifi_scan_networks[0].ssid));
+
+ /* leave the first item and we'll set its text later */
+ while (CountMItems(ssid_menu) > 1)
+ DelMenuItem(ssid_menu, 2);
+
+ for (n = 0, mitem = 1; n < count; n++) {
+ /*
+ * InsMenuItem supports meta chars in item text which can
+ * have side effects, so insert a blank item and then set its
+ * name with SetItem which does not suport meta chars.
+ */
+ strlcpy(ssid, wifi_scan_networks[n].ssid, sizeof(ssid));
+ CtoPstr(ssid);
+
+ if (n > 0)
+ InsMenuItem(ssid_menu, ssid, mitem);
+
+ SetItem(ssid_menu, mitem, ssid);
+
+ CheckItem(ssid_menu, mitem,
+ strcmp(wifi_scan_networks[n].ssid, wifi_cur_info.ssid) == 0);
+ mitem++;
+ }
+
+ update_window(true);
+}
+
+void
+activate_window(bool activate)
+{
+ Rect r;
+ GrafPtr savePort;
+
+ GetPort(&savePort);
+ SetPort(win);
+ r = win->portRect;
+ r.top = r.bottom - 16;
+ r.left = r.left - 16;
+ InvalRect(&r);
+ SetPort(savePort);
+}
+
+void
+destroy_window(void)
+{
+ ReleaseResource(signal_icons);
+
+ if (!win)
+ return;
+
+ DisposeWindow(win);
+ win = 0;
+}
+
+void
+window_mousedown(Point p)
+{
+ Str255 pass;
+ char ssid[64];
+ Rect r, irect;
+ GrafPtr savePort;
+ short mitems, n, selitem, hit, itype;
+ long new_net;
+ struct wifi_network_entry *net;
+ struct wifi_join_request wjr;
+ DialogPtr dg;
+ EventRecord e;
+ ControlHandle ihandle;
+
+ GetPort(&savePort);
+ SetPort(win);
+
+ r = ssid_menu_rect;
+ LocalToGlobal(&r);
+
+ /* XXX: why does PtInRect not work here? */
+ if (!(p.h >= r.left && p.h <= r.left + r.right &&
+ p.v >= r.top && p.v <= r.top + r.bottom)) {
+ SetPort(savePort);
+ return;
+ }
+
+ if (wifi_scsi_id == WIFI_SCSI_ID_NONE)
+ return;
+
+ InsertMenu(ssid_menu, -1);
+
+ mitems = CountMItems(ssid_menu);
+ selitem = 1;
+ for (n = 0; n < mitems; n++) {
+ if (strcmp(wifi_scan_networks[n].ssid, wifi_cur_info.ssid) == 0) {
+ selitem = n + 1;
+ break;
+ }
+ }
+
+ new_net = PopUpMenuSelect(ssid_menu, r.top + 1, r.left + 1, selitem);
+
+ DeleteMenu((*ssid_menu)->menuID);
+
+ if (hiword(new_net) != 0 && loword(new_net) != selitem) {
+ net = &wifi_scan_networks[loword(new_net) - 1];
+ strlcpy(ssid, net->ssid, sizeof(ssid));
+
+ CtoPstr(ssid);
+ ParamText(ssid, "", "", "");
+ PtoCstr(ssid);
+
+ dg = GetNewDialog(OwnedResourceID(PASSWORD_DIALOG_ID), 0L,
+ (WindowPtr)-1L);
+ CenterDialog(dg, false);
+ ShowWindow(dg);
+ for (;;) {
+ ModalDialog(0, &hit);
+ if (hit == ok || hit == cancel)
+ break;
+ }
+
+ if (hit == ok) {
+ GetDItem(dg, PASSWORD_DIALOG_PASSWORD_FIELD_ID, &itype,
+ &ihandle, &irect);
+ GetIText(ihandle, &pass);
+ PtoCstr(pass);
+
+ memset(&wjr, 0, sizeof(wjr));
+ strlcpy(wjr.ssid, ssid, sizeof(wjr.ssid));
+ strlcpy(wjr.key, (char *)pass, sizeof(wjr.key));
+
+ DisposDialog(dg);
+
+ scsi_wifi_join(wifi_scsi_id, &wjr);
+ } else
+ DisposDialog(dg);
+ }
+
+ SetPort(savePort);
+}