;;; -*- Mode:LISP; Package:NETWORK; Base:10; Readtable:CL -*-

#|

  Copyright LISP Machine, Inc. 1987
   See filename "Copyright.Text" for
  licensing and release information.

|#

(export '( *network-interfaces*
	   network-interface
	   add-address-info))

(defstream network-interface
	   ()
	   "NI-"
  ;The structure describing a particular network interface
  (tag nil)					; A tag to identify this particular network interface
  (interface nil)				; A keyword to identify the type of interface
  (keyword nil)					; A keyword to identify the type of network
  (opened nil)					; The interface has been initialized and is in global list
  (enabled nil)					; The interface is available for use
  (hardware-type nil)				; A hardware type code, as used by ARP
  (address-length nil)				; The length in bytes of the address of this interface
  (address nil)					; The hardware address of this interface on the network
  (broadcast-address nil)			; The hardware address used for broadcast.  NIL if can't
  (sent-header-length 0)			; Space reserved for the link level header in sent buffers
  (rcvd-header-length 0)			; Space taken by the link level header in received buffers
  (sent-trailer-length 0)			; Space reserved for the link level trailer in sent buffers
  (rcvd-trailer-length 0)			; Space taken by the link level trailer in received buffers
  (minimum-data-length 0)			; minimum packet length on this network
  (maximum-data-length 0)			; maximum packet length on this network
  (protocol-alist nil)				; Association list: (protocol-keyword . receive-function)
  (network-alist nil)				; Association list: (protocol-keyword . network-number)
  (address-alist nil)				; Association list: (protocol-keyword . (addresses))
  (address-translations nil)			; ARP table for this network interface
  (loopback nil)				; T if software loopback needed
  (point-to-point nil)				; T if point-to-point: must use address-translation-list
  (reset-function nil)				; function to reset interface
  (enable-function nil)				; function to enable interface
  (disable-function nil)			; function to disable interface
  (packet-ready-function nil)			; function that returns t if packet available
  (get-next-packet-function nil)		; function that gets next packet
  (send-function nil)				; function to send a packet onto the network
  (need-receive-buffer nil)			; t if non-blocking allocate failed
  (reserved-receive-buffer nil)			; the buffer allocated by scheduler for receive
  (allocation-failures 0)			; statistic: how many times int-pkts not available
  (statistics-block (make-statistics-block))	; Network statistics block maintained at clock level
  (packets-sent-discarded 0)			; statistic: sent packets discarded
  (packets-received-discarded 0)		; statistic: received packets discarded
  (bytes-sent-discarded 0)			; statistic: sent bytes discarded
  (bytes-received-discarded 0)			; statistic: received bytes discarded
  (protocols-not-understood nil)		; list of unhandled protocols received from network
  (active-gauges nil)				; list of gauges in control panel
  (inactive-gauges nil)				; list of gauges not currently in control panel
  (gauge-name nil)				; the name of this interface for its gauges
  )

(defsubst ni-packets-sent (ni)
  (aref (ni-statistics-block ni) STAT-PS STAT-CURR))
(defsubst ni-packets-received (ni)
  (aref (ni-statistics-block ni) STAT-PR STAT-CURR))
(defsubst ni-bytes-sent (ni)
  (aref (ni-statistics-block ni) STAT-BS STAT-CURR))
(defsubst ni-bytes-received (ni)
  (aref (ni-statistics-block ni) STAT-BR STAT-CURR))

(defvar *network-interfaces* nil
  "The global list of network interfaces known to this machine")

(defvar *network-interface-fifo* (make-fifo)
  "A copy of *network-interfaces* rotated by the network receiver process")

(defvar network-driver-initialization-list nil "The initialization list for network drivers")

(defop (network-interface :open) nil
  (unless (ni-opened self)
    (dolist (ni *network-interfaces*)
      (when (equal (ni-tag self) (ni-tag ni))
	(send ni :close)
	(return t)))
    (without-interrupts
      (push self *network-interfaces*)
      (push-fifo self *network-interface-fifo*))
    (when (ni-reset-function self)
      (funcall (ni-reset-function self) self))
    (add-network-statistics-block (ni-statistics-block self))
    (setf (ni-enabled self) nil)
    (setf (ni-opened self) t)))

(defop (network-interface :close) nil
  (when (ni-opened self)
    (when (ni-enabled self)
      (send self :disable))
    (delete-network-statistics-block (ni-statistics-block self))
    (setf (ni-opened self) nil)
    (send self :kill-gauges)
    (without-interrupts
      (setq *network-interfaces* (delete self *network-interfaces*))
      (remove-from-fifo self *network-interface-fifo*)))
  t)

(defop (network-interface :reset) nil
  (when (ni-opened self)
    (when (ni-reset-function self)
      (funcall (ni-reset-function self) self))))

(defop (network-interface :enable) (&rest arguments)
  (when (ni-opened self)
    (cond ((ni-enabled self))
	  ((ni-enable-function self)
	   (and (apply (ni-enable-function self) self arguments)
		(setf (ni-enabled self) t)))
	  (t
	   (setf (ni-enabled self) t)))))

(defop (network-interface :disable) (&rest arguments)
  (when (ni-enabled self)
    (setf (ni-enabled self) nil)
    (setf (ni-need-receive-buffer self) nil)
    (when (ni-reserved-receive-buffer self)
      (free-packet (ni-reserved-receive-buffer self))
      (setf (ni-reserved-receive-buffer self) nil))
    (when (ni-disable-function self)
      (apply (ni-disable-function self) self arguments))))

(defop (network-interface :print-self) (stream &optional ignore ignore)
  (system:printing-random-object (self stream :type :no-pointer)
    (format stream "~S (~:[closed~;open~] ~:[dis~;en~]abled) ~A ~A address ~X"
	    (ni-tag self)
	    (ni-opened self)
	    (ni-enabled self)
	    (ni-interface self)
	    (ni-keyword self)
	    (ni-address self))))

(defop (network-interface :set-gauges) (gauge-list)
  "Set the active network gauges for this network interface"
  (set-gauges (ni-statistics-block self)
	      (locf (ni-active-gauges self))
	      (locf (ni-inactive-gauges self))
	      (ni-gauge-name self)
	      gauge-list))

(defop (network-interface :make-gauges) (&optional (gauge-list '(:apr :aps :abr :abs)))
  "Add selected gauges for this network interface"
  (add-gauges (ni-statistics-block self)
	      (locf (ni-active-gauges self))
	      (locf (ni-inactive-gauges self))
	      (ni-gauge-name self)
	      gauge-list))

(defop (network-interface :kill-gauges) (&optional (gauge-list '(:ipr :apr :ips :aps :ibr :abr :ibs :abs)))
  "Delete selected gauges from a network interface.  Default is to delete all gauges"
  (delete-gauges (locf (ni-active-gauges self))
		 (locf (ni-inactive-gauges self))
		 gauge-list))

;;; the structure of one entry in the address-translations list
(defstruct (address-translations
	     (:type :list)
	     (:conc-name "AT-"))
  protocol-address				;The Network Protocol address (e.g. Chaos or Internet)
  hardware-address				;The Hardware Address (e.g. Ethernet)
  protocol					;The Keyword for the Network Protocol
  on-behalf-of)					;A list of addresses that this info is valid for

(defun add-address-info (address protocol interface hardware-address &optional on-behalf-of &aux adr-info)
  (when (assoc protocol (ni-address-alist interface) :test #'eq)
    (setq adr-info (without-interrupts
		     (dolist (at (ni-address-translations interface))
		       (when (and (= address (at-protocol-address at))
				  (eq protocol (at-protocol at)))
			 (return at)))))
    (unless adr-info
      (setq adr-info (make-address-translations))
      (push adr-info (ni-address-translations interface)))
    (setf (at-protocol-address adr-info) address)
    (setf (at-hardware-address adr-info) hardware-address)
    (setf (at-protocol adr-info) protocol)
    (when on-behalf-of
      (pushnew on-behalf-of (at-on-behalf-of adr-info)))))
