-- (c) William Welch 2005 -- -- This software is provided 'as-is', without any express or implied -- warranty. In no event will the authors be held liable for any damages -- arising from the use of this software. -- -- Permission is granted to anyone to use this software for any purpose, -- including commercial applications, and to alter it and redistribute it -- freely, subject to the following restrictions: -- -- 1. The origin of this software must not be misrepresented; -- you must not claim that you wrote the original software. -- If you use this software in a product, an acknowledgment in -- the product documentation would be appreciated but is not required. -- -- 2. Altered source versions must be plainly marked as such, and must -- not be misrepresented as being the original software. -- -- 3. This notice may not be removed or altered from any source distribution. -- -- This license is commonly known as the zlib/libpng License. -- -- william welch 5 march 2005. prism2/3 support for the Airdrop (8-bit Compact flash slot) -- This driver would not be possible without the previous work of many people -- in the Linux community. Thank you all. Here is a list of the sources that -- I found myself going back to over and over: -- Intersil's datasheet for the ISL3873a, from their own website: -- http://www.intersil.com/data/fn/fn8015.pdf -- Linux/FreeBSD if_wi driver: -- http://fxr.watson.org/fxr/source/dev/wi/if_wi.c -- Linux "orinoco" driver: -- http://lxr.linux.no/source/drivers/net/wireless/orinoco.c -- Other Linux drivers: -- http://www.linux-wlan.org/ -- http://hostap.epitest.fi/ -- "COR" register const byte COR_HI = 0x03 const byte COR_LO = 0xe0 const byte FUNC_ENA = 0x01 const byte SOFT_RESET = 0x80 -- prism I/O registers const byte CMD = 0x00 const byte PARAM0 = 0x02 const byte PSTATUS = 0x08 const byte RESP0 = 0x0a const byte INFOFID = 0x10 const byte SELECT0 = 0x18 const byte OFFSET0 = 0x1c const byte RXFID = 0x20 const byte ALLOCFID = 0x22 const byte SWSUP0 = 0x28 const byte EVSTAT = 0x30 const byte EVACK = 0x34 const byte DATA0 = 0x36 const byte INITCMD = 0 const byte ENABLECMD = 1 const byte TXCMD = 0x0B const byte ACCESSCMD = 0x21 const byte ALLOCCMD = 0x0A const byte BAPBUSY = 0x80 const byte CMDBUSY = 0x80 const byte EVCMD = 0x10 const byte EVALLOC = 8 const byte EVTX = 6 var byte net_http_clk, net_telnet_clk var byte pkt_owner procedure initialize_cmd is var byte lo, hi -- initialize the prism chipset. -- do it twice for good measure. for 2 loop iowr8 ( PARAM0 , 0 ) iowr8 ( PARAM0 + 1 , 0 ) iowr8 ( CMD , INITCMD ) iowr8 ( CMD + 1 , 0 ) delay_100ms ( 5 ) iowr8 ( EVACK , 0xff ) iowr8 ( EVACK + 1 , 0xff ) end loop -- try a simple read/write test. iowr8 ( SWSUP0 , 0xAA ) iowr8 ( SWSUP0 + 1 , 0x55 ) lo = iord8 ( SWSUP0 ) hi = iord8 ( SWSUP0 + 1 ) putc = "P" putc = "R" putc = "I" putc = "S" putc = "M" putc = " " if (lo == 0xAA) & (hi == 0x55) then putc = "O" putc = "K" putc = ":" putc = " " puthex (lo) puthex (hi) putc = 13 putc = 10 else putc = "B" putc = "A" putc = "D" putc = ":" putc = " " puthex (lo) puthex (hi) putc = 13 putc = 10 end if end procedure procedure init_prism is var byte v, hi, lo v = attr_rd ( COR_HI , COR_LO ) v = v | SOFT_RESET attr_wr ( COR_HI , COR_LO , v ) delay_1ms v = v ^ SOFT_RESET attr_wr ( COR_HI , COR_LO , v ) delay_100ms ( 5 ) -- enable the prism I/O ports v = v | FUNC_ENA attr_wr ( COR_HI , COR_LO , v ) delay_100ms ( 5 ) initialize_cmd delay_100ms ( 5 ) net_http_clk = 0 net_telnet_clk = 0 pkt_owner = 0 end procedure function cmdbusy_wait return bit is var byte lo, hi for 200 loop lo = iord8 ( CMD ) hi = iord8 ( CMD + 1) if ( hi & CMDBUSY ) == 0 then return false end if delay_1ms end loop return true end function function evcmd_wait return bit is var byte lo, hi for 200 loop lo = iord8 ( EVSTAT ) hi = iord8 ( EVSTAT + 1) if ( lo & EVCMD ) != 0 then return false end if delay_1ms end loop return true end function function bap_wait return bit is var byte lo, hi for 200 loop lo = iord8 ( OFFSET0 ) hi = iord8 ( OFFSET0 + 1) if ( hi & BAPBUSY ) == 0 then return false end if delay_1ms end loop return true end function procedure getrid ( byte in rid_hi , byte in rid_lo ) is var byte n, lo, hi, v if cmdbusy_wait then -- timeout waiting for CMDBUSY to reset pr_err( "G", "R", "1") return end if iowr8 ( PARAM0, rid_lo ) iowr8 ( PARAM0 + 1, rid_hi ) iowr8 ( CMD, ACCESSCMD ) iowr8 ( CMD + 1, 0 ) if evcmd_wait then -- timeout waiting for CMD event pr_err( "G", "R", "2") return end if iowr8 ( EVACK, EVCMD ) iowr8 ( EVACK + 1, 0) if bap_wait then -- timeout waiting for BAP to become available pr_err( "G", "R", "4") return end if iowr8 ( SELECT0, rid_lo ) iowr8 ( SELECT0 + 1, rid_hi ) iowr8 ( OFFSET0, 0 ) iowr8 ( OFFSET0 + 1, 0 ) if bap_wait then -- timeout waiting for buffer to be set up pr_err( "G", "R", "5") return end if -- get the length of the RID. toss the rest of the header lo = iord8 ( DATA0) hi = iord8 ( DATA0 + 1) v = iord8 ( DATA0) v = iord8 ( DATA0 + 1) -- note this is a word count. n = lo - 1 if n > ( TBUFSZ >> 1 ) then n = ( TBUFSZ >> 1 ) end if -- store RID in TBUF LoadPointer_0 ( TBUF ) for n loop lo = iord8 ( DATA0) hi = iord8 ( DATA0 + 1) MemNext = lo MemNext = hi end loop end procedure -- allocate a buffer inside the prism card. -- returns the buffer's "handle" or "FID". -- there are two steps required: 1) issue the alloc request. 2) wait for the alloc event. function net_alloc ( byte out fid_hi , byte out fid_lo ) return bit is var byte i, lo, hi, done if cmdbusy_wait then pr_err( "A", "L", "1") return false end if iowr8 ( PARAM0, ( 2000 & 0xff ) ) iowr8 ( PARAM0 + 1, ( 2000 >> 8 ) ) iowr8 ( CMD, ALLOCCMD ) iowr8 ( CMD + 1, 0 ) if evcmd_wait then pr_err( "A", "L", "2") return false end if iowr8 ( EVACK, EVCMD ) iowr8 ( EVACK + 1, 0) i = 0 done = 0 while ( done == 0 ) & ( i < 200 ) loop lo = iord8 ( EVSTAT ) hi = iord8 ( EVSTAT + 1) if ( lo & EVALLOC ) != 0 then done = 1 end if delay_10ms i = i + 1 end loop if i == 200 then pr_err( "A", "L", "3") return false end if fid_lo = iord8 ( ALLOCFID ) fid_hi = iord8 ( ALLOCFID + 1 ) iowr8 ( EVACK, EVALLOC ) iowr8 ( EVACK + 1, 0) return true end function -- transfer "len" bytes from buffer "buf" into the previously allocated prism buffer "fid". -- offset defines the initial position in the buffer inside the prism chip. -- note: "len" should be an even number of bytes. procedure net_putbuf ( byte in fid_hi, byte in fid_lo, byte in offset, byte in buf, byte in len_hi , byte in len_lo ) is var byte v if bap_wait then pr_err( "P", "B", "1") return end if iowr8 ( SELECT0, fid_lo ) iowr8 ( SELECT0 + 1, fid_hi ) iowr8 ( OFFSET0, offset ) iowr8 ( OFFSET0 + 1, 0 ) if bap_wait then pr_err( "P", "B", "2") return end if -- handle large buffers I16_LDL ( P_N16 , len_hi , len_lo ) I16_SRF ( P_N16 ) LoadPointer_2 ( buf ) while I16_CEQ ( P_N16 , ZERO16 ) == false loop v = MemNext2 iowr8 ( DATA0 , v ) v = MemNext2 iowr8 ( DATA0 + 1, v ) I16_DEC ( P_N16 ) end loop end procedure -- transfer "len" bytes from prism buffer "fid", to the system RAM buffer "buf". -- offset defines the initial position in the buffer inside the prism chip. -- note: "len" should be an even number of bytes. procedure net_getbuf ( byte in fid_hi , byte in fid_lo , byte in offset , byte in buf , byte in len_hi , byte in len_lo ) is var byte n, v if bap_wait then pr_err( "G", "B", "1") return end if iowr8 ( SELECT0, fid_lo ) iowr8 ( SELECT0 + 1, fid_hi ) iowr8 ( OFFSET0, offset ) iowr8 ( OFFSET0 + 1, 0 ) if bap_wait then pr_err( "G", "B", "2") return end if -- handle large buffers I16_LDL ( P_N16 , len_hi , len_lo ) I16_SRF ( P_N16 ) LoadPointer_2 ( buf ) while I16_CEQ ( P_N16 , ZERO16 ) == false loop v = iord8 ( DATA0 ) MemNext2 = v v = iord8 ( DATA0 + 1 ) MemNext2 = v I16_DEC ( P_N16 ) end loop end procedure -- updates the contents/value of the "RID". Caller must create the header for the -- rid, and the len must be even. procedure put_rid ( byte in rid_hi, byte in rid_lo, byte in buf, byte in len ) is -- copy the "rid" to the prism buffer net_putbuf ( rid_hi , rid_lo, 0, buf, 0, len ) if cmdbusy_wait then pr_err( "P", "R", "1") return end if -- issue the ACCESS command to request the "RID". note the "write" flag iowr8 ( PARAM0, rid_lo ) iowr8 ( PARAM0 + 1, rid_hi ) iowr8 ( CMD, ACCESSCMD ) iowr8 ( CMD + 1, 1 ) if evcmd_wait then pr_err( "P", "R", "2") return end if iowr8 ( EVACK, EVCMD ) iowr8 ( EVACK + 1, 0) end procedure procedure net_get_mac_addr is getrid ( 0xfc, 0x01 ) MemCpy ( MYMAC , TBUF , 6 ) end procedure function get_port_status return byte is var byte v getrid ( 0xFD, 0x40 ) v = MemRd ( TBUF ) return v end function -- set time interval for TICK events. units are 1mS procedure set_tick ( byte in hi , byte in lo ) is -- build the PORTTYPE rid ( 0xFCE0 ) LoadPointer_0 ( TBUF ) MemNext = 2 MemNext = 0 MemNext = 0xE0 MemNext = 0xFC MemNext = lo MemNext = hi -- now send the rid to the prism card put_rid ( 0xFC , 0xE0, TBUF, 6 ) end procedure procedure setporttype ( byte in t ) is -- build the PORTTYPE rid ( 0xFC00 ) LoadPointer_0 ( TBUF ) MemNext = 2 MemNext = 0 MemNext = 0 MemNext = 0xFC MemNext = t MemNext = 0 -- now send the rid to the prism card put_rid ( 0xFC , 0, TBUF, 6 ) end procedure procedure set_ssid ( byte in ssid , byte in len ) is var byte i, n, v -- build the DESIREDSSID rid ( 0xFC02) LoadPointer_0 ( TBUF ) MemNext = 18 MemNext = 0 MemNext = 2 MemNext = 0xFC MemNext = len MemNext = 0 -- copy the ssid into the rid buffer MemCpyEx ( TBUF , 6, ssid , 0 , len ) -- now send the rid to the prism card n = len if ( n & 1 ) != 0 then n = n + 1 end if put_rid ( 0xFC , 2, TBUF, n + 6 ) end procedure procedure enable_cmd is if cmdbusy_wait then pr_err( "E", "N", "1") return end if iowr8 ( CMD, ENABLECMD ) iowr8 ( CMD + 1, 0 ) if evcmd_wait then pr_err( "E", "N", "2") return end if iowr8 ( EVACK, EVCMD ) iowr8 ( EVACK + 1, 0) end procedure function net_rxpoll return bit is var byte i, v, fid_hi, fid_lo, lo, hi, mtype, port pkt_owner = 0 lo = iord8 ( EVSTAT ) hi = iord8 ( EVSTAT + 1 ) -- "info" message from the prism card. no need to display this -- at all, unless you are curious. but be sure to "ACK" it. if ( lo & 0x80 ) != 0 then -- change the next line to enable "info" messages to be displayed. if 1 == 0 then var byte n, info_hi, info_lo info_lo = iord8 ( INFOFID ) info_hi = iord8 ( INFOFID + 1 ) net_getbuf ( info_hi, info_lo, 0, TBUF, 0 , 4 ) n = MemRd ( TBUF ) n = n + 1 n = n * 2 if n > TBUFSZ then n = TBUFSZ end if net_getbuf ( info_hi, info_lo, 0, TBUF, 0 , n ) putc = "I" putc = "N" putc = " " LoadPointer_0 ( TBUF ) i = 0 for n loop v = MemNext puthex ( v ) i = i + 1 end loop putc = 13 putc = 10 end if iowr8 ( EVACK , 0x80 ) iowr8 ( EVACK + 1, 0 ) end if lo = iord8 ( EVSTAT ) hi = iord8 ( EVSTAT + 1 ) if ( hi & 0x80 ) != 0 then iowr8 ( EVACK , 0 ) iowr8 ( EVACK + 1, 0x80 ) net_http_clk = net_http_clk + 1 net_telnet_clk = net_telnet_clk + 1 end if lo = iord8 ( EVSTAT ) hi = iord8 ( EVSTAT + 1 ) if ( lo & 1 ) == 0 then return false end if fid_lo = iord8 ( RXFID ) fid_hi = iord8 ( RXFID + 1 ) -- get the rx status word from the prism header net_getbuf ( fid_hi, fid_lo, 0, TBUF, 0 , 2 ) lo = MemRd ( TBUF ) hi = MemNext port = hi & 7 mtype = hi >> 5 if ( ( lo & 3 ) != 0 ) | ( port != 0 ) then pr_err( "R", "X", "1") puthex ( hi ) puthex ( lo ) putc = 13 putc = 10 return false end if -- get the length from the 802.3 hdr. note "network" byte order. net_getbuf ( fid_hi, fid_lo, 58, TBUF, 0 , 2 ) hi = MemRd ( TBUF ) lo = MemNext I16_LDL ( P_K16 , hi , lo ) if ( ( mtype != 1 ) & ( mtype != 2 ) ) then pr_err( "R", "X", "4") iowr8 ( EVACK , 1 ) iowr8 ( EVACK + 1 , 0) return false end if -- get the 802.3, 802.2, and snap headers net_getbuf ( fid_hi, fid_lo, 46, TBUF, 0 , 22 ) lo = MemRdEx ( TBUF , 14 ) hi = MemNext -- valid snap header ? if ( lo != 0xAA ) | ( hi != 0xAA ) then pr_err( "R", "X", "5") iowr8 ( EVACK , 1 ) iowr8 ( EVACK + 1 , 0) return false end if -- build ethernet header: dst, src, ethertype MemCpy ( PKT , TBUF , 12 ) MemCpyEx ( PKT , 12 , TBUF , 20 , 2 ) -- no snap header I16_SUB8 ( P_K16 , 8 ) -- round up, if odd length if I16_BTT ( P_K16 , 0 ) then I16_INC ( P_K16 ) end if -- get the payload data, store directly in PKT after ethernet header if I16_CEQ ( P_K16 , ZERO16 ) == false then I16_SVL ( P_K16 , hi , lo ) net_getbuf ( fid_hi , fid_lo , 68 , IP_OFF , hi , lo ) end if iowr8 ( EVACK , 1 ) iowr8 ( EVACK + 1 , 0) return true end function procedure net_txpkt ( byte in len_hi, byte in len_lo ) is var byte txfid_hi, txfid_lo, done, i, t, lo, hi if net_alloc ( txfid_hi , txfid_lo ) == false then pr_err( "T", "X", "0") return end if -- build the prism tx header MemSet ( TBUF , 0 , 14 ) -- set txok and txerror MemWrEx ( TBUF , 12 , 6 ) net_putbuf ( txfid_hi , txfid_lo , 0, TBUF , 0 , 14 ) -- build the 802.11 header (just zeros) MemSet ( TBUF , 0 , 32 ) net_putbuf ( txfid_hi , txfid_lo , 14, TBUF , 0 , 32 ) -- build the 802.3 header (derived from pkt's ethernet header) MemCpy ( TBUF , PKT , 12) I16_LDL ( P_K16 , len_hi , len_lo ) I16_SUB8 ( P_K16 , 6 ) I16_SVL ( P_K16 , len_hi , len_lo ) MemWrEx ( TBUF , 12 , len_hi ) MemNext = len_lo net_putbuf ( txfid_hi , txfid_lo , 46, TBUF , 0 , 14 ) -- 802.2 and snap header LoadPointer_0 ( TBUF ) MemNext = 0xAA MemNext = 0xAA MemNext = 3 MemNext = 0 MemNext = 0 MemNext = 0 MemCpyEx ( TBUF , 6 , PKT , 12 , 2 ) net_putbuf ( txfid_hi , txfid_lo , 60, TBUF , 0 , 8 ) -- payload I16_SUB8 ( P_K16 , 8 ) I16_SVL ( P_K16 , len_hi , len_lo ) net_putbuf ( txfid_hi , txfid_lo , 68, IP_OFF , len_hi , len_lo ) if cmdbusy_wait then pr_err( "T", "X", "1") return end if iowr8 ( PARAM0 , txfid_lo ) iowr8 ( PARAM0 + 1 , txfid_hi) iowr8 ( CMD, TXCMD ) iowr8 ( CMD + 1, 0 ) if evcmd_wait then pr_err( "T", "X", "2") return end if iowr8 ( EVACK, EVCMD ) iowr8 ( EVACK + 1, 0) i = 0 done = 0 while ( done == 0 ) & ( i < 200 ) loop lo = iord8 ( EVSTAT ) hi = iord8 ( EVSTAT + 1) if ( lo & EVTX ) != 0 then done = 1 end if delay_1ms i = i + 1 end loop if i == 200 then pr_err( "T", "X", "3") return end if -- check for error/exception event if ( lo & 4 ) != 0 then pr_err( "T", "X", "4") end if iowr8 ( EVACK, EVTX ) iowr8 ( EVACK + 1, 0) end procedure procedure net_init is var byte n, v, pstat init_prism net_get_mac_addr -- BSS, use Access point, not ad-hoc setporttype( 1 ) -- specify the SSID that we wish to join set_ssid ( MYSSID , MYSSID_LEN ) -- set Prism "tick event" to 1 second (1000 mS) intervals. -- this is used by the TCP timeout/retry routine. set_tick ( 1000 >> 8 , 1000 & 0xFF ) -- this should cause the prism card to connect/associate -- with the access point enable_cmd n = 0 putc = "I" putc = "P" putc = " " for 4 loop v = MemRdEx ( MYIP , n ) puthex( v ) n = n + 1 end loop putc = 13 putc = 10 n = 0 putc = "M" putc = "A" putc = "C" putc = " " for 6 loop v = MemRdEx ( MYMAC , n ) puthex( v ) n = n + 1 end loop putc = 13 putc = 10 pstat = 0 while pstat != 4 loop toggle_led delay_100ms ( 5 ) delay_100ms ( 5 ) v = get_port_status if v != pstat then putc = "S" putc = "T" putc = "A" putc = "T" putc = "=" putc = " " puthex( v ) putc = 13 putc = 10 pstat = v end if end loop end procedure