Changeset View
Changeset View
Standalone View
Standalone View
stand/i386/libi386/pxe.c
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
#include <net.h> | #include <net.h> | ||||
#include <netif.h> | #include <netif.h> | ||||
#include <nfsv2.h> | #include <nfsv2.h> | ||||
#include <iodesc.h> | #include <iodesc.h> | ||||
#include <bootp.h> | #include <bootp.h> | ||||
#include <bootstrap.h> | #include <bootstrap.h> | ||||
#include "libi386.h" | |||||
#include "btxv86.h" | #include "btxv86.h" | ||||
#include "pxe.h" | #include "pxe.h" | ||||
/* | |||||
* Allocate the PXE buffers statically instead of sticking grimy fingers into | |||||
* BTX's private data area. The scratch buffer is used to send information to | |||||
* the PXE BIOS, and the data buffer is used to receive data from the PXE BIOS. | |||||
*/ | |||||
#define PXE_BUFFER_SIZE 0x2000 | |||||
static char scratch_buffer[PXE_BUFFER_SIZE]; | |||||
static char data_buffer[PXE_BUFFER_SIZE]; | |||||
static pxenv_t *pxenv_p = NULL; /* PXENV+ */ | static pxenv_t *pxenv_p = NULL; /* PXENV+ */ | ||||
static pxe_t *pxe_p = NULL; /* !PXE */ | static pxe_t *pxe_p = NULL; /* !PXE */ | ||||
#ifdef PXE_DEBUG | #ifdef PXE_DEBUG | ||||
static int pxe_debug = 0; | static int pxe_debug = 0; | ||||
#endif | #endif | ||||
void pxe_enable(void *pxeinfo); | void pxe_enable(void *pxeinfo); | ||||
static void (*pxe_call)(int func); | static void (*pxe_call)(int func, void *ptr); | ||||
static void pxenv_call(int func); | static void pxenv_call(int func, void *ptr); | ||||
static void bangpxe_call(int func); | static void bangpxe_call(int func, void *ptr); | ||||
static int pxe_init(void); | static int pxe_init(void); | ||||
static int pxe_print(int verbose); | static int pxe_print(int verbose); | ||||
static void pxe_cleanup(void); | static void pxe_cleanup(void); | ||||
static void pxe_perror(int error); | static void pxe_perror(int error); | ||||
static int pxe_netif_match(struct netif *nif, void *machdep_hint); | static int pxe_netif_match(struct netif *nif, void *machdep_hint); | ||||
static int pxe_netif_probe(struct netif *nif, void *machdep_hint); | static int pxe_netif_probe(struct netif *nif, void *machdep_hint); | ||||
▲ Show 20 Lines • Show All 138 Lines • ▼ Show 20 Lines | pxe_init(void) | ||||
if (pxe_call == bangpxe_call) | if (pxe_call == bangpxe_call) | ||||
printf("@%04x:%04x\n", | printf("@%04x:%04x\n", | ||||
pxe_p->EntryPointSP.segment, | pxe_p->EntryPointSP.segment, | ||||
pxe_p->EntryPointSP.offset); | pxe_p->EntryPointSP.offset); | ||||
else | else | ||||
printf("@%04x:%04x\n", | printf("@%04x:%04x\n", | ||||
pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset); | pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset); | ||||
gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer; | gci_p = bio_alloc(sizeof(*gci_p)); | ||||
if (gci_p == NULL) { | |||||
pxe_p = NULL; | |||||
return (0); | |||||
} | |||||
bzero(gci_p, sizeof(*gci_p)); | bzero(gci_p, sizeof(*gci_p)); | ||||
gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; | gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; | ||||
pxe_call(PXENV_GET_CACHED_INFO); | pxe_call(PXENV_GET_CACHED_INFO, gci_p); | ||||
if (gci_p->Status != 0) { | if (gci_p->Status != 0) { | ||||
pxe_perror(gci_p->Status); | pxe_perror(gci_p->Status); | ||||
bio_free(gci_p, sizeof(*gci_p)); | |||||
pxe_p = NULL; | pxe_p = NULL; | ||||
return (0); | return (0); | ||||
} | } | ||||
free(bootp_response); | free(bootp_response); | ||||
if ((bootp_response = malloc(gci_p->BufferSize)) != NULL) { | if ((bootp_response = malloc(gci_p->BufferSize)) != NULL) { | ||||
bootp_response_size = gci_p->BufferSize; | bootp_response_size = gci_p->BufferSize; | ||||
bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), | bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), | ||||
bootp_response, bootp_response_size); | bootp_response, bootp_response_size); | ||||
} | } | ||||
bio_free(gci_p, sizeof(*gci_p)); | |||||
return (1); | return (1); | ||||
} | } | ||||
static int | static int | ||||
pxe_print(int verbose) | pxe_print(int verbose) | ||||
{ | { | ||||
if (pxe_call == NULL) | if (pxe_call == NULL) | ||||
return (0); | return (0); | ||||
printf("%s devices:", pxedisk.dv_name); | printf("%s devices:", pxedisk.dv_name); | ||||
if (pager_output("\n") != 0) | if (pager_output("\n") != 0) | ||||
return (1); | return (1); | ||||
printf(" %s0:", pxedisk.dv_name); | printf(" %s0:", pxedisk.dv_name); | ||||
if (verbose) { | if (verbose) { | ||||
printf(" %s:%s", inet_ntoa(rootip), rootpath); | printf(" %s:%s", inet_ntoa(rootip), rootpath); | ||||
} | } | ||||
return (pager_output("\n")); | return (pager_output("\n")); | ||||
} | } | ||||
static void | static void | ||||
pxe_cleanup(void) | pxe_cleanup(void) | ||||
{ | { | ||||
#ifdef PXE_DEBUG | t_PXENV_UNLOAD_STACK *unload_stack_p; | ||||
t_PXENV_UNLOAD_STACK *unload_stack_p = | t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p; | ||||
(t_PXENV_UNLOAD_STACK *)scratch_buffer; | |||||
t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p = | |||||
(t_PXENV_UNDI_SHUTDOWN *)scratch_buffer; | |||||
#endif | |||||
if (pxe_call == NULL) | if (pxe_call == NULL) | ||||
return; | return; | ||||
pxe_call(PXENV_UNDI_SHUTDOWN); | undi_shutdown_p = bio_alloc(sizeof(*undi_shutdown_p)); | ||||
if (undi_shutdown_p != NULL) { | |||||
bzero(undi_shutdown_p, sizeof(*undi_shutdown_p)); | |||||
pxe_call(PXENV_UNDI_SHUTDOWN, undi_shutdown_p); | |||||
#ifdef PXE_DEBUG | #ifdef PXE_DEBUG | ||||
if (pxe_debug && undi_shutdown_p->Status != 0) | if (pxe_debug && undi_shutdown_p->Status != 0) | ||||
printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n", | printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n", | ||||
undi_shutdown_p->Status); | undi_shutdown_p->Status); | ||||
#endif | #endif | ||||
bio_free(undi_shutdown_p, sizeof(*undi_shutdown_p)); | |||||
} | |||||
pxe_call(PXENV_UNLOAD_STACK); | unload_stack_p = bio_alloc(sizeof(*unload_stack_p)); | ||||
if (unload_stack_p != NULL) { | |||||
bzero(unload_stack_p, sizeof(*unload_stack_p)); | |||||
pxe_call(PXENV_UNLOAD_STACK, unload_stack_p); | |||||
#ifdef PXE_DEBUG | #ifdef PXE_DEBUG | ||||
if (pxe_debug && unload_stack_p->Status != 0) | if (pxe_debug && unload_stack_p->Status != 0) | ||||
printf("pxe_cleanup: UNLOAD_STACK failed %x\n", | printf("pxe_cleanup: UNLOAD_STACK failed %x\n", | ||||
unload_stack_p->Status); | unload_stack_p->Status); | ||||
#endif | #endif | ||||
bio_free(unload_stack_p, sizeof(*unload_stack_p)); | |||||
} | } | ||||
} | |||||
void | void | ||||
pxe_perror(int err) | pxe_perror(int err) | ||||
{ | { | ||||
return; | return; | ||||
} | } | ||||
void | void | ||||
pxenv_call(int func) | pxenv_call(int func, void *ptr) | ||||
{ | { | ||||
#ifdef PXE_DEBUG | #ifdef PXE_DEBUG | ||||
if (pxe_debug) | if (pxe_debug) | ||||
printf("pxenv_call %x\n", func); | printf("pxenv_call %x\n", func); | ||||
#endif | #endif | ||||
bzero(&v86, sizeof(v86)); | bzero(&v86, sizeof(v86)); | ||||
bzero(data_buffer, sizeof(data_buffer)); | |||||
__pxenvseg = pxenv_p->RMEntry.segment; | __pxenvseg = pxenv_p->RMEntry.segment; | ||||
__pxenvoff = pxenv_p->RMEntry.offset; | __pxenvoff = pxenv_p->RMEntry.offset; | ||||
v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; | v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; | ||||
v86.es = VTOPSEG(scratch_buffer); | v86.es = VTOPSEG(ptr); | ||||
v86.edi = VTOPOFF(scratch_buffer); | v86.edi = VTOPOFF(ptr); | ||||
v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry); | v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry); | ||||
v86.ebx = func; | v86.ebx = func; | ||||
v86int(); | v86int(); | ||||
v86.ctl = V86_FLAGS; | v86.ctl = V86_FLAGS; | ||||
} | } | ||||
void | void | ||||
bangpxe_call(int func) | bangpxe_call(int func, void *ptr) | ||||
{ | { | ||||
#ifdef PXE_DEBUG | #ifdef PXE_DEBUG | ||||
if (pxe_debug) | if (pxe_debug) | ||||
printf("bangpxe_call %x\n", func); | printf("bangpxe_call %x\n", func); | ||||
#endif | #endif | ||||
bzero(&v86, sizeof(v86)); | bzero(&v86, sizeof(v86)); | ||||
bzero(data_buffer, sizeof(data_buffer)); | |||||
__bangpxeseg = pxe_p->EntryPointSP.segment; | __bangpxeseg = pxe_p->EntryPointSP.segment; | ||||
__bangpxeoff = pxe_p->EntryPointSP.offset; | __bangpxeoff = pxe_p->EntryPointSP.offset; | ||||
v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; | v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; | ||||
v86.edx = VTOPSEG(scratch_buffer); | v86.edx = VTOPSEG(ptr); | ||||
v86.eax = VTOPOFF(scratch_buffer); | v86.eax = VTOPOFF(ptr); | ||||
v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry); | v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry); | ||||
v86.ebx = func; | v86.ebx = func; | ||||
v86int(); | v86int(); | ||||
v86.ctl = V86_FLAGS; | v86.ctl = V86_FLAGS; | ||||
} | } | ||||
static int | static int | ||||
Show All 11 Lines | pxe_netif_probe(struct netif *nif, void *machdep_hint) | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
pxe_netif_end(struct netif *nif) | pxe_netif_end(struct netif *nif) | ||||
{ | { | ||||
t_PXENV_UNDI_CLOSE *undi_close_p; | t_PXENV_UNDI_CLOSE *undi_close_p; | ||||
undi_close_p = (t_PXENV_UNDI_CLOSE *)scratch_buffer; | undi_close_p = bio_alloc(sizeof(*undi_close_p)); | ||||
if (undi_close_p != NULL) { | |||||
bzero(undi_close_p, sizeof(*undi_close_p)); | bzero(undi_close_p, sizeof(*undi_close_p)); | ||||
pxe_call(PXENV_UNDI_CLOSE); | pxe_call(PXENV_UNDI_CLOSE, undi_close_p); | ||||
if (undi_close_p->Status != 0) | if (undi_close_p->Status != 0) | ||||
printf("undi close failed: %x\n", undi_close_p->Status); | printf("undi close failed: %x\n", undi_close_p->Status); | ||||
bio_free(undi_close_p, sizeof(*undi_close_p)); | |||||
} | } | ||||
} | |||||
static void | static void | ||||
pxe_netif_init(struct iodesc *desc, void *machdep_hint) | pxe_netif_init(struct iodesc *desc, void *machdep_hint) | ||||
{ | { | ||||
t_PXENV_UNDI_GET_INFORMATION *undi_info_p; | t_PXENV_UNDI_GET_INFORMATION *undi_info_p; | ||||
t_PXENV_UNDI_OPEN *undi_open_p; | t_PXENV_UNDI_OPEN *undi_open_p; | ||||
uint8_t *mac; | uint8_t *mac; | ||||
int i, len; | int i, len; | ||||
undi_info_p = (t_PXENV_UNDI_GET_INFORMATION *)scratch_buffer; | undi_info_p = bio_alloc(sizeof(*undi_info_p)); | ||||
if (undi_info_p == NULL) | |||||
return; | |||||
bzero(undi_info_p, sizeof(*undi_info_p)); | bzero(undi_info_p, sizeof(*undi_info_p)); | ||||
pxe_call(PXENV_UNDI_GET_INFORMATION); | pxe_call(PXENV_UNDI_GET_INFORMATION, undi_info_p); | ||||
if (undi_info_p->Status != 0) { | if (undi_info_p->Status != 0) { | ||||
printf("undi get info failed: %x\n", undi_info_p->Status); | printf("undi get info failed: %x\n", undi_info_p->Status); | ||||
bio_free(undi_info_p, sizeof(*undi_info_p)); | |||||
return; | return; | ||||
} | } | ||||
/* Make sure the CurrentNodeAddress is valid. */ | /* Make sure the CurrentNodeAddress is valid. */ | ||||
for (i = 0; i < undi_info_p->HwAddrLen; ++i) { | for (i = 0; i < undi_info_p->HwAddrLen; ++i) { | ||||
if (undi_info_p->CurrentNodeAddress[i] != 0) | if (undi_info_p->CurrentNodeAddress[i] != 0) | ||||
break; | break; | ||||
} | } | ||||
Show All 12 Lines | pxe_netif_init(struct iodesc *desc, void *machdep_hint) | ||||
for (i = 0; i < len; ++i) | for (i = 0; i < len; ++i) | ||||
desc->myea[i] = mac[i]; | desc->myea[i] = mac[i]; | ||||
if (bootp_response != NULL) | if (bootp_response != NULL) | ||||
desc->xid = bootp_response->bp_xid; | desc->xid = bootp_response->bp_xid; | ||||
else | else | ||||
desc->xid = 0; | desc->xid = 0; | ||||
undi_open_p = (t_PXENV_UNDI_OPEN *)scratch_buffer; | bio_free(undi_info_p, sizeof(*undi_info_p)); | ||||
undi_open_p = bio_alloc(sizeof(*undi_open_p)); | |||||
if (undi_open_p == NULL) | |||||
return; | |||||
bzero(undi_open_p, sizeof(*undi_open_p)); | bzero(undi_open_p, sizeof(*undi_open_p)); | ||||
undi_open_p->PktFilter = FLTR_DIRECTED | FLTR_BRDCST; | undi_open_p->PktFilter = FLTR_DIRECTED | FLTR_BRDCST; | ||||
pxe_call(PXENV_UNDI_OPEN); | pxe_call(PXENV_UNDI_OPEN, undi_open_p); | ||||
if (undi_open_p->Status != 0) | if (undi_open_p->Status != 0) | ||||
printf("undi open failed: %x\n", undi_open_p->Status); | printf("undi open failed: %x\n", undi_open_p->Status); | ||||
bio_free(undi_open_p, sizeof(*undi_open_p)); | |||||
} | } | ||||
static int | static int | ||||
pxe_netif_receive(void **pkt) | pxe_netif_receive(void **pkt) | ||||
{ | { | ||||
t_PXENV_UNDI_ISR *isr = (t_PXENV_UNDI_ISR *)scratch_buffer; | t_PXENV_UNDI_ISR *isr; | ||||
char *buf, *ptr, *frame; | char *buf, *ptr, *frame; | ||||
size_t size, rsize; | size_t size, rsize; | ||||
isr = bio_alloc(sizeof(*isr)); | |||||
if (isr == NULL) | |||||
return (-1); | |||||
bzero(isr, sizeof(*isr)); | bzero(isr, sizeof(*isr)); | ||||
isr->FuncFlag = PXENV_UNDI_ISR_IN_START; | isr->FuncFlag = PXENV_UNDI_ISR_IN_START; | ||||
pxe_call(PXENV_UNDI_ISR); | pxe_call(PXENV_UNDI_ISR, isr); | ||||
if (isr->Status != 0) | if (isr->Status != 0) { | ||||
bio_free(isr, sizeof(*isr)); | |||||
return (-1); | return (-1); | ||||
} | |||||
bzero(isr, sizeof(*isr)); | bzero(isr, sizeof(*isr)); | ||||
isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; | isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; | ||||
pxe_call(PXENV_UNDI_ISR); | pxe_call(PXENV_UNDI_ISR, isr); | ||||
if (isr->Status != 0) | if (isr->Status != 0) { | ||||
bio_free(isr, sizeof(*isr)); | |||||
return (-1); | return (-1); | ||||
} | |||||
while (isr->FuncFlag == PXENV_UNDI_ISR_OUT_TRANSMIT) { | while (isr->FuncFlag == PXENV_UNDI_ISR_OUT_TRANSMIT) { | ||||
/* | /* | ||||
* Wait till transmit is done. | * Wait till transmit is done. | ||||
*/ | */ | ||||
bzero(isr, sizeof(*isr)); | bzero(isr, sizeof(*isr)); | ||||
isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; | isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; | ||||
pxe_call(PXENV_UNDI_ISR); | pxe_call(PXENV_UNDI_ISR, isr); | ||||
if (isr->Status != 0 || | if (isr->Status != 0 || | ||||
isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) | isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) { | ||||
bio_free(isr, sizeof(*isr)); | |||||
return (-1); | return (-1); | ||||
} | } | ||||
} | |||||
while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE) { | while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE) { | ||||
if (isr->Status != 0 || | if (isr->Status != 0 || | ||||
isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) { | isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) { | ||||
bio_free(isr, sizeof(*isr)); | |||||
return (-1); | return (-1); | ||||
} | } | ||||
bzero(isr, sizeof(*isr)); | bzero(isr, sizeof(*isr)); | ||||
isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; | isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; | ||||
pxe_call(PXENV_UNDI_ISR); | pxe_call(PXENV_UNDI_ISR, isr); | ||||
} | } | ||||
size = isr->FrameLength; | size = isr->FrameLength; | ||||
buf = malloc(size + ETHER_ALIGN); | buf = malloc(size + ETHER_ALIGN); | ||||
if (buf == NULL) | if (buf == NULL) { | ||||
bio_free(isr, sizeof(*isr)); | |||||
return (-1); | return (-1); | ||||
} | |||||
ptr = buf + ETHER_ALIGN; | ptr = buf + ETHER_ALIGN; | ||||
rsize = 0; | rsize = 0; | ||||
while (rsize < size) { | while (rsize < size) { | ||||
frame = (char *)((uintptr_t)isr->Frame.segment << 4); | frame = (char *)((uintptr_t)isr->Frame.segment << 4); | ||||
frame += isr->Frame.offset; | frame += isr->Frame.offset; | ||||
bcopy(PTOV(frame), ptr, isr->BufferLength); | bcopy(PTOV(frame), ptr, isr->BufferLength); | ||||
ptr += isr->BufferLength; | ptr += isr->BufferLength; | ||||
rsize += isr->BufferLength; | rsize += isr->BufferLength; | ||||
bzero(isr, sizeof(*isr)); | bzero(isr, sizeof(*isr)); | ||||
isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; | isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; | ||||
pxe_call(PXENV_UNDI_ISR); | pxe_call(PXENV_UNDI_ISR, isr); | ||||
if (isr->Status != 0) { | if (isr->Status != 0) { | ||||
bio_free(isr, sizeof(*isr)); | |||||
free(buf); | free(buf); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
/* Did we got another update? */ | /* Did we got another update? */ | ||||
if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_RECEIVE) | if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_RECEIVE) | ||||
continue; | continue; | ||||
break; | break; | ||||
} | } | ||||
*pkt = buf; | *pkt = buf; | ||||
bio_free(isr, sizeof(*isr)); | |||||
return (rsize); | return (rsize); | ||||
} | } | ||||
static ssize_t | static ssize_t | ||||
pxe_netif_get(struct iodesc *desc, void **pkt, time_t timeout) | pxe_netif_get(struct iodesc *desc, void **pkt, time_t timeout) | ||||
{ | { | ||||
time_t t; | time_t t; | ||||
void *ptr; | void *ptr; | ||||
Show All 11 Lines | |||||
} | } | ||||
static ssize_t | static ssize_t | ||||
pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) | pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) | ||||
{ | { | ||||
t_PXENV_UNDI_TRANSMIT *trans_p; | t_PXENV_UNDI_TRANSMIT *trans_p; | ||||
t_PXENV_UNDI_TBD *tbd_p; | t_PXENV_UNDI_TBD *tbd_p; | ||||
char *data; | char *data; | ||||
ssize_t rv = -1; | |||||
trans_p = (t_PXENV_UNDI_TRANSMIT *)scratch_buffer; | trans_p = bio_alloc(sizeof(*trans_p)); | ||||
tbd_p = bio_alloc(sizeof(*tbd_p)); | |||||
data = bio_alloc(len); | |||||
if (trans_p != NULL && tbd_p != NULL && data != NULL) { | |||||
bzero(trans_p, sizeof(*trans_p)); | bzero(trans_p, sizeof(*trans_p)); | ||||
tbd_p = (t_PXENV_UNDI_TBD *)(scratch_buffer + sizeof(*trans_p)); | |||||
bzero(tbd_p, sizeof(*tbd_p)); | bzero(tbd_p, sizeof(*tbd_p)); | ||||
data = scratch_buffer + sizeof(*trans_p) + sizeof(*tbd_p); | |||||
trans_p->TBD.segment = VTOPSEG(tbd_p); | trans_p->TBD.segment = VTOPSEG(tbd_p); | ||||
trans_p->TBD.offset = VTOPOFF(tbd_p); | trans_p->TBD.offset = VTOPOFF(tbd_p); | ||||
tbd_p->ImmedLength = len; | tbd_p->ImmedLength = len; | ||||
tbd_p->Xmit.segment = VTOPSEG(data); | tbd_p->Xmit.segment = VTOPSEG(data); | ||||
tbd_p->Xmit.offset = VTOPOFF(data); | tbd_p->Xmit.offset = VTOPOFF(data); | ||||
bcopy(pkt, data, len); | bcopy(pkt, data, len); | ||||
pxe_call(PXENV_UNDI_TRANSMIT); | pxe_call(PXENV_UNDI_TRANSMIT, trans_p); | ||||
if (trans_p->Status != 0) { | if (trans_p->Status == 0) | ||||
return (-1); | rv = len; | ||||
} | } | ||||
return (len); | bio_free(data, len); | ||||
bio_free(tbd_p, sizeof(*tbd_p)); | |||||
bio_free(trans_p, sizeof(*trans_p)); | |||||
return (rv); | |||||
} | } |