;;; -*- Mode:LISP; Package:SERIAL-IP; Readtable:CL; Base:10 -*- #| Copyright GigaMos Systems, Inc. 1988 See filename "Copyright.Text" for licensing and release information. This is the implementation of the "Serial IP" interface driver and I/O process for the new network system. The high-level serial I/O handling and remote site code is in another file, SERIAL-SITES. The actual packet protocol and low-level serial I/O is in the file SERIAL-PROTOCOL. |# (export '(*serial-interface* *serial-io-process* setup-serial enable-serial-interface disable-serial-interface reenable-serial-interface)) (in-package "SERIAL-IP") (use-package '("SERIAL-PROTO" "NETWORK" "LISP")) ;;;Interface globals (defvar *serial-interface* nil "The (only, global) Serial network interface") (defconstant *serial-max-packet-size* (floor *serial-proto-max-packet-size* 2)) (defvar *serial-free-list* (make-fifo) "List of serial-packets free for use") (defvar *serial-send-list* (make-fifo) "List of serial-packets waiting to be received") (defvar *serial-recv-list* (make-fifo) "List of serial-packets waiting to be sent") ;;;Process globals (defvar *serial-io-process* nil "The (one, only) Serial I/O Handler process") (defvar *serial-process-name* "Serial-IP I/O Process") (defvar *serial-process-num* 0) ;How many serial processes run ;;;Define interface (defstream serial-interface (network-interface) "SERIAL-INTERFACE-" (site nil) ;Current serial-site (message ;Latest message (make-string 100. :fill-pointer 0.)) (process nil) ;Current I/O process (startup-time nil) ;Process startup time (connected-p nil) ;Are we connected? ) (defop (serial-interface :peek-special-fields) (ni) (list (tv:scroll-parse-item `(:function serial-ip:describe-current-site (,ni) NIL ("~A"))) (tv:scroll-parse-item `(:function serial-ip:serial-process-message (,ni) NIL ("~A"))) (tv:scroll-parse-item `(:function serial-ip:copy-serial-interface-message (,ni) NIL ("~A")))) ) ;;;Describe selected site (defun describe-current-site(&optional (interface *serial-interface*) stream (max-length 100.) &aux site) (unless stream (setq stream (make-string max-length :fill-pointer 0.))) (format stream "Current site: ") (cond ((null (setq site (serial-interface-site interface))) (format stream "not selected")) ((or (not (arrayp stream)) (not(array-has-fill-pointer-p stream))) (format stream "~s" site)) (t (prog(len) (setq len (fill-pointer stream)) (format stream "~s" site) (when (> (fill-pointer stream) max-length) (setf (fill-pointer stream) len) (print-serial-site-1 site stream)) (when (> (fill-pointer stream) max-length) (setf (fill-pointer stream) len) (format stream "~a" (serial-site-tag site)))))) stream) ;;;Warning/status messages (defvar *serial-message-max-length* 120.) (defun blank-serial-message(&optional (interface *serial-interface*)) (if (stringp (serial-interface-message interface)) (setf (fill-pointer (serial-interface-message interface)) 0.) (setf (serial-interface-message interface) (make-string *serial-message-max-length* :fill-pointer 0.)))) (defun serial-message(interface prefix format-string &rest format-args) (blank-serial-message interface) (let((str (serial-interface-message interface)) (max *serial-message-max-length*)) (format str "~1(~a:~) ~a" prefix (apply #'format nil format-string format-args)) (and max (> (string-length str) max) (setf (fill-pointer str) max)) str)) (defun serial-warn(interface format-string &rest format-args) (apply #'serial-message interface "Warning" format-string format-args)) (defun serial-status(interface format-string &rest format-args) (apply #'serial-message interface "Status" format-string format-args)) (defun copy-serial-interface-message(interface) (subseq (or (serial-interface-message interface) "") 0 nil)) ;;Address/gateway handling for protocols (defun null-internet-alist() (list (ncons :internet))) (defun serial-add-address-info(interface site) ;;Interface's address is just a label: (setf (serial-interface-address interface) (serial-site-tag site)) (let*((remote-address (serial-site-remote-address site)) (site-local-address (serial-site-local-address site)) (host-remote-address (serial-site-host-remote-address site)) (gateway-to (serial-site-gateway-to site))) (multiple-value-bind (remote-network mask) (ip:ip-network-number-and-mask remote-address) (cond ((null remote-address) (serial-warn interface "No remote Internet address for site")) (t ;;Associate interface with remote Internet network and address: (setf (serial-interface-address-alist interface) `((:internet ,host-remote-address))) (setf (serial-interface-network-alist interface) `((:internet ,remote-network))) ;;Associate remote site with interface (add-address-info site-local-address :internet interface site) (add-address-info remote-address :internet interface site) ;;Tell Network Software that remote network directly accessable (push (cons remote-network mask) ip:*default-network-numbers*) ;;Create gateway: (ip:add-gateway gateway-to remote-address interface)))))) (defun serial-remove-address-info(interface &aux site) (when (and (setq site (serial-interface-site interface)) (eq *serial-io-process* (serial-interface-process interface))) (setq ip:*default-network-numbers* (delete (ip:ip-network-number-from-address (serial-site-remote-address site)) ip:*default-network-numbers* :key #'car)) (ip:remove-gateway (serial-site-gateway-to site)))) ; (setf (serial-interface-address interface) nil) ; (setf (serial-interface-address-alist interface) (null-internet-alist)) ; (setf (serial-interface-network-alist interface) (null-internet-alist))) ; (setf (serial-interface-address-translations interface) nil)) ;;Disable interface (defun serial-disable(interface &aux packets) (serial-status interface "Shutting down") (serial-remove-address-info interface) (kill-serial-process interface) (setq packets (append (fifo-as-list *serial-recv-list*) (fifo-as-list *serial-send-list*))) (setq *serial-recv-list* (make-fifo)) (setq *serial-send-list* (make-fifo)) (dolist(spkt packets) (push-fifo spkt *serial-free-list*))) ;;Enable/start interface (defun serial-start(interface site) "Assign SITE to INTERFACE with address info" (kill-serial-process *serial-interface*) (setf (serial-interface-site interface) site) (serial-add-address-info interface site) (setf (serial-interface-process interface) (setq *serial-io-process* (process-run-function (format nil "~a ~d" *serial-process-name* (incf *serial-process-num*)) #'serial-process interface site)))) (defun serial-restart(interface &aux site) "Restart INTERFACE for current site" (setq site (serial-interface-site interface)) (if site (serial-start interface site) (serial-warn interface "No site set, cannot restart."))) (defun serial-enable(interface &optional site) ;;Network software calls this with no SITE arg "Call this manually to start serial interface on SITE (can be a tag id)" (serial-status interface "Enabling Serial-Interface") (flet((barf(fmt &rest args) (apply #'serial-warn interface fmt args))) (cond ((null site) ;restart on current site (serial-restart interface)) ((serial-interface-enabled interface) ;already enabled, punt (barf "Serial-Interface already enabled ~:[but not on any site?~;for site ~a~]" (serial-site-tag (serial-interface-site interface)))) (t (setq site (serial-site-locate site)) ;; ;;Enable on new site: locate first, error check ;; (cond ((null (serial-site-locate site)) (if (null (serial-site-list-sites)) (barf "No Serial-Interface sites are defined") (barf "Cannot locate Serial-Interface site ~s" site))) ((eq (setq site (serial-site-locate site)) ;??? (serial-interface-site interface)) ;restart on current site (serial-restart interface)) (t (serial-start interface site))))))) ;;Reset interface (defun serial-reset (interface) (serial-disable interface) (serial-enable interface)) ;;;Packet handling routines (internal) (defstruct (serial-packet (:conc-name "SPKT-")) (pkt-16 ;16-bit packet array (make-array *serial-max-packet-size* :element-type '(unsigned-byte 16) :fill-pointer 0)) (pkt-8 ;8-bit packet array (make-array (* 2 *serial-max-packet-size*) :element-type '(unsigned-byte 8) :fill-pointer 0 :displaced-to pkt-16)) (dest nil) ;packet destination ) (defun allocate-serial-packet() (or (pop-fifo *serial-free-list*) (make-serial-packet))) (defun stuff-send-packet(int-pkt spkt &aux pkt-16 len-16) (setq pkt-16 (spkt-pkt-16 spkt)) (setq len-16 (fill-pointer int-pkt)) (copy-array-portion int-pkt 0 len-16 pkt-16 0 len-16) (setf (fill-pointer pkt-16) len-16) (setf (fill-pointer (spkt-pkt-8 spkt)) (* 2 len-16))) (defun stuff-recv-pkt(spkt int-pkt &aux pkt-16 len-16) (setq pkt-16 (spkt-pkt-16 spkt)) (setq len-16 (fill-pointer pkt-16)) (copy-array-portion pkt-16 0 len-16 int-pkt 0 len-16) (setf (fill-pointer int-pkt) len-16)) (defun serial-send-int-pkt (int-pkt source dest type n-16-bit-words &aux spkt) (declare(ignore source type)) (setf (fill-pointer int-pkt) n-16-bit-words) (when (setq spkt (allocate-serial-packet)) (stuff-send-packet int-pkt spkt) (free-packet int-pkt) (setf (spkt-dest spkt) dest) ;log dest - will need for multi-interface (push-fifo spkt *serial-send-list*)) t) ;;;Listing packets (debug code) (defun print-pkts() (loop for spkt in (fifo-as-list *serial-free-list*) with count = 0 as pkt = (spkt-pkt-8 spkt) do (incf count) (format t "~%#~d: " count) (dotimes(i (fill-pointer pkt)) (format t "~c" (aref pkt i))))) ;;;Packet routines for network receiver (defun serial-packet-ready () (not (fifo-empty-p *serial-recv-list*))) (defvar *last-packet-returned* nil) (defun serial-get-next-packet (&aux int-pkt) (declare (values packet type source destination broadcast-p)) (let ((spkt (pop-fifo *serial-recv-list*))) (cond ((null spkt) nil) ((setq int-pkt (allocate-packet-for-receive *serial-interface*)) (stuff-recv-pkt spkt int-pkt) ;fill in int-pkt (values-list (setq *last-packet-returned* (list int-pkt ;the packet ip-ethernet-type ;packet is always ethernet type (serial-interface-site ;sender *serial-interface*) (serial-interface-address ;receiver *serial-interface*) nil ;broadcast-p )))) (t ;no free int-pkt (push-fifo-front spkt *serial-recv-list*) nil)))) ;;;Main setup routines (defun setup-serial(&optional site) (let((id "SERIAL") (site (serial-site-locate (or site *default-serial-ip-site*)))) ;; ;;Close the previous interface down, if known: ;; (when *serial-interface* (send *serial-interface* :close)) ;; ;;Instantiate Serial-Interface: ;; (setq *serial-interface* (make-serial-interface :interface :serial :keyword :serial :tag id :gauge-name id :point-to-point t :protocol-alist nil :network-alist nil :address-alist nil :minimum-data-length 0 :maximum-data-length 128. ;41 <= x <= *serial-proto-max-packet-size* :sent-header-length 0 :rcvd-header-length 0 :sent-trailer-length 0 :rcvd-trailer-length 0 :disable-function 'serial-disable :enable-function 'serial-enable :reset-function 'serial-reset :packet-ready-function 'serial-packet-ready :get-next-packet-function 'serial-get-next-packet :send-function 'serial-send-int-pkt )) ;; ;;Open the interface ;; (funcall *serial-interface* :open) ;; ;;If there's a default site, enable the interface ;; (if (serial-site-p site) (funcall *serial-interface* :enable site) (serial-warn *serial-interface* "Cannot enable, no default site")) *serial-interface*)) (add-initialization "Setup Serial-IP" '(setup-serial) :network-driver) (defun enable-serial-interface(&optional site) (funcall *serial-interface* :enable site)) (defun disable-serial-interface() (funcall *serial-interface* :disable)) (defun reenable-serial-interface(&optional site) (disable-serial-interface) (enable-serial-interface site)) ;;;Serial-IP process (defvar *serial-io-command* nil "Keyword indicating thing to process") ;;Debugging variables (defvar *debugging* nil) (defvar *spacket*) (defvar *rpacket*) (defvar *sdu* nil) (defvar *sp* nil) ;;;Kill serial process: (defun kill-serial-process(interface &aux process) (when (setq process (serial-interface-process interface)) ;;Only forget global process if it belongs to this interface (if (eq *serial-io-process* process) (setq *serial-io-process* nil)) ;;Forget local process (setf (serial-interface-process interface) nil) ;;Kill it, smack it dead (send process :kill))) ;;;Process-wait function for network receiver: (defun serial-ready-p(fifo stream) (setq *serial-io-command* (cond ;;Packet? ((not(fifo-empty-p fifo)) :send) ;;Input on stream? ((serial-port-listen stream) :receive)))) ;;;Start-up function: (defun serial-process(interface site) (if *debugging* (serial-warn interface "Debugging mode active (*debugging*=~s); to run, rebind and retry." *debugging*) (with-serial-port-allocated (serial-site-device-name site) (serial-status interface "Serial process has started") (unwind-protect (serial-process-top-level interface site) (serial-status interface "Serial process has shut down"))))) ;;;Top-level function (do this forever): (defun serial-process-top-level(interface site &aux connected-p) (setf (serial-interface-startup-time interface) (time:get-universal-time)) (let(send-spkt recv-spkt pkt-8 pkt-16 status (type (serial-site-type site)) (device (serial-site-device site)) (baud-rate (serial-site-baud-rate site)) (open-args (serial-site-open-args site)) (connect-args (serial-site-connect-args site))) ;; ;;This is where everything comes together: ;; ;; Interface is up and running this process, ;; with an associated site and its data, which are ;; used to open serial port and run protocol. ;; (with-open-stream(slip (make-serial-proto-stream :type type)) (setq *sp* slip) (apply slip :open device :baud-rate baud-rate open-args) (setq *sdu* (sproto:sp-stream *sp*)) (loop (process-wait "Serial-IP Command" #'serial-ready-p *serial-send-list* slip) (case *serial-io-command* ;;Send a packet? (:send (setq send-spkt (pop-fifo *serial-send-list*)) (setq *spacket* send-spkt) (unless (equal (spkt-dest send-spkt) site) (serial-warn interface "Serial-IP error: packet for ~s, not ~s" (spkt-dest send-spkt) site) (funcall interface :disable) (return-from serial-process-top-level)) (unless connected-p (setf (serial-interface-connected-p interface) (setq connected-p (apply slip :connect connect-args)))) ;;Send packet (serial-proto-send slip (spkt-pkt-8 send-spkt)) (push-fifo send-spkt *serial-free-list*)) ;;Receive a packet? (:receive (unless recv-spkt (setq recv-spkt (allocate-serial-packet)) (setq *rpacket* recv-spkt) (setq pkt-8 (spkt-pkt-8 recv-spkt)) (setq pkt-16 (spkt-pkt-16 recv-spkt)) (setf (fill-pointer pkt-8) 0.) (setf (fill-pointer pkt-16) 0.) ;;Associate pkt with proto-stream (send slip :new-packet pkt-8)) (setq status (serial-proto-receive slip)) (when status ;;Not doing anything about full packets!!?? (unwind-protect (progn (setf (fill-pointer pkt-16) (ceiling (fill-pointer pkt-8) 2.)) (push-fifo recv-spkt *serial-recv-list*)) (setq recv-spkt nil))))))))) (defun serial-process-message(&optional (interface *serial-interface*) &aux process (no-state "")) (cond ((null interface) "No interface.") ((null (setq process (serial-interface-process interface))) (if *serial-io-process* (format nil "No associated process~@[ but global process is in state ~*~a~]." *serial-io-process* (or (si:process-wait-whostate *serial-io-process*) no-state)) (format nil "No associated process~@[, debugging mode active~]." *debugging*))) ((not (typep process 'si:process)) "Process bashed.") ((null(send process :active-p)) (format nil "Process in state ~a - not active." (or (si:process-wait-whostate process) no-state))) (t (unless (eq process *serial-io-process*) (serial-warn interface "This is not the global process!")) (format nil "~a in state ~a~@[, last started ~a~],~@[ not~] connected." (send process :name) (or (si:process-wait-whostate process) no-state) (let((time (serial-interface-startup-time interface))) (and time (time:print-universal-time time nil))) (null (serial-interface-connected-p interface))))))