#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_dmc.c,v 1.30 2024/07/05 04:31:52 rin Exp $");
#undef DMCDEBUG
#include "opt_inet.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <net/if.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_var.h>
#endif
#include <sys/bus.h>
#include <dev/qbus/ubareg.h>
#include <dev/qbus/ubavar.h>
#include <dev/qbus/if_uba.h>
#include <dev/qbus/if_dmcreg.h>
static int dmc_timeout = 20;
#define NRCV 7
#define NXMT 3
#define NCMDS (NRCV+NXMT+4)
#define DMC_WBYTE(csr, val) \
bus_space_write_1(sc->sc_iot, sc->sc_ioh, csr, val)
#define DMC_WWORD(csr, val) \
bus_space_write_2(sc->sc_iot, sc->sc_ioh, csr, val)
#define DMC_RBYTE(csr) \
bus_space_read_1(sc->sc_iot, sc->sc_ioh, csr)
#define DMC_RWORD(csr) \
bus_space_read_2(sc->sc_iot, sc->sc_ioh, csr)
#ifdef DMCDEBUG
#define printd if(dmcdebug)printf
int dmcdebug = 0;
#endif
#define DMC_RPNBFS 50
#define DMC_RPDSC 1
#define DMC_RPTMO 10
#define DMC_RPDCK 10
struct dmc_command {
char qp_cmd;
short qp_ubaddr;
short qp_cc;
struct dmc_command *qp_next;
};
struct dmcbufs {
int ubinfo;
short cc;
short flags;
};
#define DBUF_OURS 0
#define DBUF_DMCS 1
#define DBUF_XMIT 4
#define DBUF_RCV 8
struct dmc_softc {
device_t sc_dev;
struct ifnet sc_if;
short sc_oused;
short sc_iused;
short sc_flag;
struct ubinfo sc_ui;
int sc_errors[4];
bus_space_tag_t sc_iot;
bus_addr_t sc_ioh;
bus_dma_tag_t sc_dmat;
struct evcnt sc_rintrcnt;
struct evcnt sc_tintrcnt;
#define sc_datck sc_errors[0]
#define sc_timeo sc_errors[1]
#define sc_nobuf sc_errors[2]
#define sc_disc sc_errors[3]
struct dmcbufs sc_rbufs[NRCV];
struct dmcbufs sc_xbufs[NXMT];
struct ifubinfo sc_ifuba;
struct ifrw sc_ifr[NRCV];
struct ifxmt sc_ifw[NXMT];
struct dmc_command sc_cmdbuf[NCMDS];
struct dmc_command *sc_qhead;
struct dmc_command *sc_qtail;
struct dmc_command *sc_qactive;
struct dmc_command *sc_qfreeh;
struct dmc_command *sc_qfreet;
struct dmc_base {
short d_base[128];
} dmc_base;
};
static int dmcmatch(device_t, cfdata_t, void *);
static void dmcattach(device_t, device_t, void *);
static int dmcinit(struct ifnet *);
static void dmcrint(void *);
static void dmcxint(void *);
static void dmcdown(struct dmc_softc *sc);
static void dmcrestart(struct dmc_softc *);
static void dmcload(struct dmc_softc *, int, u_short, u_short);
static void dmcstart(struct ifnet *);
static void dmctimeout(struct ifnet *);
static int dmcioctl(struct ifnet *, u_long, void *);
static int dmcoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
static void dmcreset(device_t);
CFATTACH_DECL_NEW(dmc, sizeof(struct dmc_softc),
dmcmatch, dmcattach, NULL, NULL);
#define DMC_RUNNING 0x01
#define DMC_BMAPPED 0x02
#define DMC_RESTART 0x04
#define DMC_ONLINE 0x08
#define QUEUE_AT_HEAD(qp, head, tail) \
(qp)->qp_next = (head); \
(head) = (qp); \
if ((tail) == (struct dmc_command *) 0) \
(tail) = (head)
#define QUEUE_AT_TAIL(qp, head, tail) \
if ((tail)) \
(tail)->qp_next = (qp); \
else \
(head) = (qp); \
(qp)->qp_next = (struct dmc_command *) 0; \
(tail) = (qp)
#define DEQUEUE(head, tail) \
(head) = (head)->qp_next;\
if ((head) == (struct dmc_command *) 0)\
(tail) = (head)
int
dmcmatch(device_t parent, cfdata_t cf, void *aux)
{
struct uba_attach_args *ua = aux;
struct dmc_softc ssc;
struct dmc_softc *sc = &ssc;
int i;
sc->sc_iot = ua->ua_iot;
sc->sc_ioh = ua->ua_ioh;
DMC_WBYTE(DMC_BSEL1, DMC_MCLR);
for (i = 100000; i && (DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0; i--)
;
if ((DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0) {
printf("dmcprobe: can't start device\n" );
return (0);
}
DMC_WBYTE(DMC_BSEL0, DMC_RQI|DMC_IEI);
DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) | DMC_RQI|DMC_IEI);
DELAY(1000000);
DMC_WBYTE(DMC_BSEL1, DMC_MCLR);
for (i = 100000; i && (DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0; i--)
;
return (1);
}
void
dmcattach(device_t parent, device_t self, void *aux)
{
struct uba_attach_args *ua = aux;
struct dmc_softc *sc = device_private(self);
sc->sc_dev = self;
sc->sc_iot = ua->ua_iot;
sc->sc_ioh = ua->ua_ioh;
sc->sc_dmat = ua->ua_dmat;
strlcpy(sc->sc_if.if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
sc->sc_if.if_mtu = DMCMTU;
sc->sc_if.if_init = dmcinit;
sc->sc_if.if_output = dmcoutput;
sc->sc_if.if_ioctl = dmcioctl;
sc->sc_if.if_watchdog = dmctimeout;
sc->sc_if.if_flags = IFF_POINTOPOINT;
sc->sc_if.if_softc = sc;
IFQ_SET_READY(&sc->sc_if.if_snd);
uba_intr_establish(ua->ua_icookie, ua->ua_cvec, dmcrint, sc,
&sc->sc_rintrcnt);
uba_intr_establish(ua->ua_icookie, ua->ua_cvec+4, dmcxint, sc,
&sc->sc_tintrcnt);
uba_reset_establish(dmcreset, sc->sc_dev);
evcnt_attach_dynamic(&sc->sc_rintrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
device_xname(sc->sc_dev), "intr");
evcnt_attach_dynamic(&sc->sc_tintrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
device_xname(sc->sc_dev), "intr");
if_attach(&sc->sc_if);
}
void
dmcreset(device_t dev)
{
struct dmc_softc *sc = device_private(dev);
sc->sc_flag = 0;
sc->sc_if.if_flags &= ~IFF_RUNNING;
dmcinit(&sc->sc_if);
}
int
dmcinit(struct ifnet *ifp)
{
struct dmc_softc *sc = ifp->if_softc;
struct ifrw *ifrw;
struct ifxmt *ifxp;
struct dmcbufs *rp;
struct dmc_command *qp;
struct ifaddr *ifa;
cfdata_t ui = device_cfdata(sc->sc_dev);
int base;
int s;
s = pserialize_read_enter();
IFADDR_READER_FOREACH(ifa, ifp) {
if (ifa->ifa_addr->sa_family && ifa->ifa_dstaddr->sa_family)
break;
}
pserialize_read_exit(s);
if (ifa == NULL)
return 0;
if ((DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0) {
printf("dmcinit: DMC not running\n");
ifp->if_flags &= ~IFF_UP;
return 0;
}
if ((sc->sc_flag & DMC_BMAPPED) == 0) {
sc->sc_ui.ui_size = sizeof(struct dmc_base);
sc->sc_ui.ui_vaddr = (void *)&sc->dmc_base;
uballoc(device_private(device_parent(sc->sc_dev)), &sc->sc_ui, 0);
sc->sc_flag |= DMC_BMAPPED;
}
sc->sc_iused = sc->sc_oused = 0;
if ((ifp->if_flags & IFF_RUNNING) == 0) {
if (if_ubaminit(&sc->sc_ifuba,
device_private(device_parent(sc->sc_dev)),
sizeof(struct dmc_header) + DMCMTU,
sc->sc_ifr, NRCV, sc->sc_ifw, NXMT) == 0) {
aprint_error_dev(sc->sc_dev, "can't allocate uba resources\n");
ifp->if_flags &= ~IFF_UP;
return 0;
}
ifp->if_flags |= IFF_RUNNING;
}
sc->sc_flag &= ~DMC_ONLINE;
sc->sc_flag |= DMC_RUNNING;
ifp->if_snd.ifq_maxlen = 3;
ifrw = &sc->sc_ifr[0];
for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
rp->ubinfo = ifrw->ifrw_info;
rp->cc = DMCMTU + sizeof (struct dmc_header);
rp->flags = DBUF_OURS|DBUF_RCV;
ifrw++;
}
ifxp = &sc->sc_ifw[0];
for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) {
rp->ubinfo = ifxp->ifw_info;
rp->cc = 0;
rp->flags = DBUF_OURS|DBUF_XMIT;
ifxp++;
}
sc->sc_qfreeh = sc->sc_qfreet
= sc->sc_qhead = sc->sc_qtail = sc->sc_qactive =
(struct dmc_command *)0;
for (qp = &sc->sc_cmdbuf[0]; qp < &sc->sc_cmdbuf[NCMDS]; qp++) {
QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet);
}
base = sc->sc_ui.ui_baddr;
dmcload(sc, DMC_BASEI, (u_short)base, (base>>2) & DMC_XMEM);
if (ui->cf_flags == 0)
dmcload(sc, DMC_CNTLI, 0, 0);
else if (ui->cf_flags == 1)
dmcload(sc, DMC_CNTLI, 0, DMC_MAINT );
else if (ui->cf_flags == 2)
dmcload(sc, DMC_CNTLI, 0, DMC_HDPLX);
else if (ui->cf_flags == 3)
dmcload(sc, DMC_CNTLI, 0, DMC_HDPLX | DMC_SEC);
while ((DMC_RBYTE(DMC_BSEL2) & DMC_IEO) == 0)
DMC_WBYTE(DMC_BSEL2, DMC_RBYTE(DMC_BSEL2) | DMC_IEO);
s = splnet();
for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
rp->flags |= DBUF_DMCS;
dmcload(sc, DMC_READ, rp->ubinfo,
(((rp->ubinfo>>2)&DMC_XMEM) | rp->cc));
sc->sc_iused++;
}
splx(s);
return 0;
}
void
dmcstart(struct ifnet *ifp)
{
struct dmc_softc *sc = ifp->if_softc;
struct mbuf *m;
struct dmcbufs *rp;
int n;
n = 0;
for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++ ) {
if ((rp->flags & DBUF_DMCS) == 0) {
IFQ_DEQUEUE(&sc->sc_if.if_snd, m);
if (m == 0)
return;
rp->flags |= (DBUF_DMCS);
rp->cc = if_ubaput(&sc->sc_ifuba, &sc->sc_ifw[n], m);
rp->cc &= DMC_CCOUNT;
if (++sc->sc_oused == 1)
sc->sc_if.if_timer = dmc_timeout;
dmcload(sc, DMC_WRITE, rp->ubinfo,
rp->cc | ((rp->ubinfo>>2)&DMC_XMEM));
}
n++;
}
}
void
dmcload(struct dmc_softc *sc, int type, u_short w0, u_short w1)
{
struct dmc_command *qp;
int sps;
sps = splnet();
if ((qp = sc->sc_qfreeh) == (struct dmc_command *)0)
panic("dmc command queue overflow");
DEQUEUE(sc->sc_qfreeh, sc->sc_qfreet);
qp->qp_cmd = (type | DMC_RQI);
qp->qp_ubaddr = w0;
qp->qp_cc = w1;
if (sc->sc_qactive) {
if (type == DMC_READ) {
QUEUE_AT_HEAD(qp, sc->sc_qhead, sc->sc_qtail);
} else {
QUEUE_AT_TAIL(qp, sc->sc_qhead, sc->sc_qtail);
}
} else {
sc->sc_qactive = qp;
DMC_WBYTE(DMC_BSEL0, qp->qp_cmd);
dmcrint(sc);
}
splx(sps);
}
void
dmcrint(void *arg)
{
struct dmc_softc *sc = arg;
struct dmc_command *qp;
int n;
if ((qp = sc->sc_qactive) == (struct dmc_command *) 0) {
printf("%s: dmcrint no command\n", device_xname(sc->sc_dev));
return;
}
while (DMC_RBYTE(DMC_BSEL0) & DMC_RDYI) {
DMC_WWORD(DMC_SEL4, qp->qp_ubaddr);
DMC_WWORD(DMC_SEL6, qp->qp_cc);
DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) & ~(DMC_IEI|DMC_RQI));
QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet);
while (DMC_RBYTE(DMC_BSEL0) & DMC_RDYI) {
DELAY(5);
}
if ((sc->sc_qactive = sc->sc_qhead) == (struct dmc_command *)0)
break;
qp = sc->sc_qactive;
DEQUEUE(sc->sc_qhead, sc->sc_qtail);
DMC_WBYTE(DMC_BSEL0, qp->qp_cmd);
n = RDYSCAN;
while (n-- > 0)
if ((DMC_RBYTE(DMC_BSEL0) & DMC_RDYI) ||
(DMC_RBYTE(DMC_BSEL2) & DMC_RDYO))
break;
}
if (sc->sc_qactive) {
DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) & (DMC_IEI|DMC_RQI));
DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) & (DMC_IEI|DMC_RQI));
}
}
void
dmcxint(void *a)
{
struct dmc_softc *sc = a;
struct ifnet *ifp;
struct mbuf *m;
int arg, pkaddr, cmd, len, s;
struct ifrw *ifrw;
struct dmcbufs *rp;
struct ifxmt *ifxp;
struct dmc_header *dh;
char buf[64];
ifp = &sc->sc_if;
while (DMC_RBYTE(DMC_BSEL2) & DMC_RDYO) {
cmd = DMC_RBYTE(DMC_BSEL2) & 0xff;
arg = DMC_RWORD(DMC_SEL6) & 0xffff;
pkaddr = ((arg&DMC_XMEM)<<2) | (DMC_RWORD(DMC_SEL4) & 0xffff);
DMC_WBYTE(DMC_BSEL2, DMC_RBYTE(DMC_BSEL2) & ~DMC_RDYO);
switch (cmd & 07) {
case DMC_OUR:
if_statinc(ifp, if_ipackets);
ifrw= &sc->sc_ifr[0];
for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
if(rp->ubinfo == pkaddr)
break;
ifrw++;
}
if (rp >= &sc->sc_rbufs[NRCV])
panic("dmc rcv");
if ((rp->flags & DBUF_DMCS) == 0)
aprint_error_dev(sc->sc_dev, "done unalloc rbuf\n");
len = (arg & DMC_CCOUNT) - sizeof (struct dmc_header);
if (len < 0 || len > DMCMTU) {
if_statinc(ifp, if_ierrors);
#ifdef DMCDEBUG
printd("%s: bad rcv pkt addr 0x%x len 0x%x\n",
device_xname(sc->sc_dev), pkaddr, len);
#endif
goto setup;
}
dh = (struct dmc_header *)ifrw->ifrw_addr;
dh->dmc_type = ntohs((u_short)dh->dmc_type);
if (len == 0)
goto setup;
m = if_ubaget(&sc->sc_ifuba, ifrw, ifp, len);
if (m == 0)
goto setup;
m_adj(m, sizeof(struct dmc_header));
switch (dh->dmc_type) {
#ifdef INET
case DMC_IPTYPE:
break;
#endif
default:
m_freem(m);
goto setup;
}
s = splnet();
if (__predict_false(!pktq_enqueue(ip_pktq, m, 0))) {
m_freem(m);
}
splx(s);
setup:
rp->ubinfo = ifrw->ifrw_info;
dmcload(sc, DMC_READ, rp->ubinfo,
((rp->ubinfo >> 2) & DMC_XMEM) | rp->cc);
break;
case DMC_OUX:
if_statinc(ifp, if_opackets);
ifxp = &sc->sc_ifw[0];
for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) {
if(rp->ubinfo == pkaddr)
break;
ifxp++;
}
if (rp >= &sc->sc_xbufs[NXMT]) {
aprint_error_dev(sc->sc_dev, "bad packet address 0x%x\n",
pkaddr);
break;
}
if ((rp->flags & DBUF_DMCS) == 0)
aprint_error_dev(sc->sc_dev, "unallocated packet 0x%x\n",
pkaddr);
if_ubaend(&sc->sc_ifuba, ifxp);
rp->flags &= ~DBUF_DMCS;
if (--sc->sc_oused == 0)
sc->sc_if.if_timer = 0;
else
sc->sc_if.if_timer = dmc_timeout;
if ((sc->sc_flag & DMC_ONLINE) == 0) {
extern int ifqmaxlen;
sc->sc_flag |= DMC_ONLINE;
ifp->if_snd.ifq_maxlen = ifqmaxlen;
}
break;
case DMC_CNTLO:
arg &= DMC_CNTMASK;
if (arg & DMC_FATAL) {
if (arg != DMC_START) {
snprintb(buf, sizeof(buf), CNTLO_BITS,
arg);
log(LOG_ERR,
"%s: fatal error, flags=%s\n",
device_xname(sc->sc_dev), buf);
}
dmcrestart(sc);
break;
}
switch(arg) {
case DMC_NOBUFS:
if_statinc(ifp, if_ierrors);
if ((sc->sc_nobuf++ % DMC_RPNBFS) == 0)
goto report;
break;
case DMC_DISCONN:
if ((sc->sc_disc++ % DMC_RPDSC) == 0)
goto report;
break;
case DMC_TIMEOUT:
if ((sc->sc_timeo++ % DMC_RPTMO) == 0)
goto report;
break;
case DMC_DATACK:
if_statinc(ifp, if_oerrors);
if ((sc->sc_datck++ % DMC_RPDCK) == 0)
goto report;
break;
default:
goto report;
}
break;
report:
#ifdef DMCDEBUG
snprintb(buf, sizeof(buf), CNTLO_BITS, arg);
printd("%s: soft error, flags=%s\n",
device_xname(sc->sc_dev), buf);
#endif
if ((sc->sc_flag & DMC_RESTART) == 0) {
sc->sc_flag |= DMC_RESTART;
arg = sc->sc_ui.ui_baddr;
dmcload(sc, DMC_BASEI, arg, (arg>>2)&DMC_XMEM);
}
break;
default:
printf("%s: bad control %o\n",
device_xname(sc->sc_dev), cmd);
break;
}
}
dmcstart(ifp);
}
int
dmcoutput(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst,
struct rtentry *rt)
{
int type, error, s;
struct mbuf *m = m0;
struct dmc_header *dh;
if ((ifp->if_flags & IFF_UP) == 0) {
error = ENETDOWN;
goto bad;
}
IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family);
switch (dst->sa_family) {
#ifdef INET
case AF_INET:
type = DMC_IPTYPE;
break;
#endif
case AF_UNSPEC:
dh = (struct dmc_header *)dst->sa_data;
type = dh->dmc_type;
break;
default:
printf("%s: can't handle af%d\n", ifp->if_xname,
dst->sa_family);
error = EAFNOSUPPORT;
goto bad;
}
M_PREPEND(m, sizeof(struct dmc_header), M_DONTWAIT);
if (m == 0) {
error = ENOBUFS;
goto bad;
}
dh = mtod(m, struct dmc_header *);
dh->dmc_type = htons((u_short)type);
s = splnet();
IFQ_ENQUEUE(&ifp->if_snd, m, error);
if (error) {
splx(s);
return (error);
}
dmcstart(ifp);
splx(s);
return (0);
bad:
m_freem(m0);
return (error);
}
int
dmcioctl(struct ifnet *ifp, u_long cmd, void *data)
{
int s = splnet(), error = 0;
register struct dmc_softc *sc = ifp->if_softc;
switch (cmd) {
case SIOCINITIFADDR:
ifp->if_flags |= IFF_UP;
if ((ifp->if_flags & IFF_RUNNING) == 0)
dmcinit(ifp);
break;
case SIOCSIFDSTADDR:
if ((ifp->if_flags & IFF_RUNNING) == 0)
dmcinit(ifp);
break;
case SIOCSIFFLAGS:
if ((error = ifioctl_common(ifp, cmd, data)) != 0)
break;
if ((ifp->if_flags & IFF_UP) == 0 &&
sc->sc_flag & DMC_RUNNING)
dmcdown(sc);
else if (ifp->if_flags & IFF_UP &&
(sc->sc_flag & DMC_RUNNING) == 0)
dmcrestart(sc);
break;
default:
error = ifioctl_common(ifp, cmd, data);
}
splx(s);
return (error);
}
void
dmcrestart(struct dmc_softc *sc)
{
int s, i;
#ifdef DMCDEBUG
printf("%s base table:\n", device_xname(sc->sc_dev));
for (i = 0; i < sizeof (struct dmc_base); i++)
printf("%o\n" ,dmc_base[unit].d_base[i]);
#endif
dmcdown(sc);
for (i = 100000; i && (DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0; i--)
;
if ((DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0) {
log(LOG_ERR, "%s: M820 Test Failed\n", device_xname(sc->sc_dev));
return;
}
dmcinit(&sc->sc_if);
sc->sc_flag &= ~DMC_RESTART;
s = splnet();
dmcstart(&sc->sc_if);
splx(s);
sc->sc_if.if_collisions++;
}
void
dmcdown(struct dmc_softc *sc)
{
struct ifxmt *ifxp;
DMC_WBYTE(DMC_BSEL1, DMC_MCLR);
sc->sc_flag &= ~(DMC_RUNNING | DMC_ONLINE);
for (ifxp = sc->sc_ifw; ifxp < &sc->sc_ifw[NXMT]; ifxp++) {
#ifdef notyet
m_freem(ifxp->ifw_xtofree);
ifxp->ifw_xtofree = NULL;
#endif
}
IF_PURGE(&sc->sc_if.if_snd);
}
void
dmctimeout(struct ifnet *ifp)
{
struct dmc_softc *sc = ifp->if_softc;
char buf1[64], buf2[64];
if (sc->sc_flag & DMC_ONLINE) {
snprintb(buf1, sizeof(buf1), DMC0BITS,
DMC_RBYTE(DMC_BSEL0) & 0xff);
snprintb(buf2, sizeof(buf2), DMC2BITS,
DMC_RBYTE(DMC_BSEL2) & 0xff);
log(LOG_ERR, "%s: output timeout, bsel0=%s bsel2=%s\n",
device_xname(sc->sc_dev), buf1, buf2);
dmcrestart(sc);
}
}