status_t UnixEndpoint::Listen(int backlog) { TRACE("[%ld] %p->UnixEndpoint::Listen(%d)\n", find_thread(NULL), this, backlog); UnixEndpointLocker endpointLocker(this); if (!IsBound()) RETURN_ERROR(EDESTADDRREQ); if (fState != UNIX_ENDPOINT_NOT_CONNECTED) RETURN_ERROR(EINVAL); gSocketModule->set_max_backlog(socket, backlog); fAcceptSemaphore = create_sem(0, "unix accept"); if (fAcceptSemaphore < 0) RETURN_ERROR(ENOBUFS); _UnsetReceiveFifo(); fCredentials.pid = getpid(); fCredentials.uid = geteuid(); fCredentials.gid = getegid(); fState = UNIX_ENDPOINT_LISTENING; RETURN_ERROR(B_OK); }
status_t UnixEndpoint::Unbind() { TRACE("[%ld] %p->UnixEndpoint::Unbind()\n", find_thread(NULL), this); UnixEndpointLocker endpointLocker(this); RETURN_ERROR(_Unbind()); }
status_t OHCI::_SubmitTransfer(Transfer *transfer) { Pipe *pipe = transfer->TransferPipe(); bool directionIn = (pipe->Direction() == Pipe::In); ohci_general_td *firstDescriptor = NULL; ohci_general_td *lastDescriptor = NULL; status_t result = _CreateDescriptorChain(&firstDescriptor, &lastDescriptor, directionIn ? OHCI_TD_DIRECTION_PID_IN : OHCI_TD_DIRECTION_PID_OUT, transfer->VectorLength()); if (result < B_OK) return result; // Apply data toggle to the first descriptor (the others will use the carry) firstDescriptor->flags &= ~OHCI_TD_TOGGLE_CARRY; firstDescriptor->flags |= pipe->DataToggle() ? OHCI_TD_TOGGLE_1 : OHCI_TD_TOGGLE_0; // Set the last descriptor to generate an interrupt lastDescriptor->flags &= ~OHCI_TD_INTERRUPT_MASK; lastDescriptor->flags |= OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE); if (!directionIn) { _WriteDescriptorChain(firstDescriptor, transfer->Vector(), transfer->VectorCount()); } // Add to the transfer list ohci_endpoint_descriptor *endpoint = (ohci_endpoint_descriptor *)pipe->ControllerCookie(); MutexLocker endpointLocker(endpoint->lock); result = _AddPendingTransfer(transfer, endpoint, firstDescriptor, firstDescriptor, lastDescriptor, directionIn); if (result < B_OK) { TRACE_ERROR("failed to add pending transfer\n"); _FreeDescriptorChain(firstDescriptor); return result; } // Add the descriptor chain to the endpoint _SwitchEndpointTail(endpoint, firstDescriptor, lastDescriptor); endpointLocker.Unlock(); endpoint->flags &= ~OHCI_ENDPOINT_SKIP; if (pipe->Type() & USB_OBJECT_BULK_PIPE) { // Tell the controller to process the bulk list _WriteReg(OHCI_COMMAND_STATUS, OHCI_BULK_LIST_FILLED); } return B_OK; }
status_t UnixEndpoint::Connect(const struct sockaddr *_address) { if (_address->sa_family != AF_UNIX) RETURN_ERROR(EAFNOSUPPORT); TRACE("[%ld] %p->UnixEndpoint::Connect(\"%s\")\n", find_thread(NULL), this, ConstSocketAddress(&gAddressModule, _address).AsString().Data()); const sockaddr_un* address = (const sockaddr_un*)_address; UnixEndpointLocker endpointLocker(this); if (fState == UNIX_ENDPOINT_CONNECTED) RETURN_ERROR(EISCONN); if (fState != UNIX_ENDPOINT_NOT_CONNECTED) RETURN_ERROR(B_BAD_VALUE); // TODO: If listening, we could set the backlog to 0 and connect. // check the address first UnixAddress unixAddress; if (address->sun_path[0] == '\0') { // internal address space (or empty address) int32 internalID; if (UnixAddress::IsEmptyAddress(*address)) RETURN_ERROR(B_BAD_VALUE); internalID = UnixAddress::InternalID(*address); if (internalID < 0) RETURN_ERROR(internalID); unixAddress.SetTo(internalID); } else { // FS address space size_t pathLen = strnlen(address->sun_path, sizeof(address->sun_path)); if (pathLen == 0 || pathLen == sizeof(address->sun_path)) RETURN_ERROR(B_BAD_VALUE); struct stat st; status_t error = vfs_read_stat(-1, address->sun_path, true, &st, !gStackModule->is_syscall()); if (error != B_OK) RETURN_ERROR(error); if (!S_ISSOCK(st.st_mode)) RETURN_ERROR(B_BAD_VALUE); unixAddress.SetTo(st.st_dev, st.st_ino, NULL); } // get the peer endpoint UnixAddressManagerLocker addressLocker(gAddressManager); UnixEndpoint* listeningEndpoint = gAddressManager.Lookup(unixAddress); if (listeningEndpoint == NULL) RETURN_ERROR(ECONNREFUSED); BReference<UnixEndpoint> peerReference(listeningEndpoint); addressLocker.Unlock(); UnixEndpointLocker peerLocker(listeningEndpoint); if (!listeningEndpoint->IsBound() || listeningEndpoint->fState != UNIX_ENDPOINT_LISTENING || listeningEndpoint->fAddress != unixAddress) { RETURN_ERROR(ECONNREFUSED); } // Allocate FIFOs for us and the socket we're going to spawn. We do that // now, so that the mess we need to cleanup, if allocating them fails, is // harmless. UnixFifo* fifo = new(nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT); UnixFifo* peerFifo = new(nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT); ObjectDeleter<UnixFifo> fifoDeleter(fifo); ObjectDeleter<UnixFifo> peerFifoDeleter(peerFifo); status_t error; if ((error = fifo->Init()) != B_OK || (error = peerFifo->Init()) != B_OK) return error; // spawn new endpoint for accept() net_socket* newSocket; error = gSocketModule->spawn_pending_socket(listeningEndpoint->socket, &newSocket); if (error != B_OK) RETURN_ERROR(error); // init connected peer endpoint UnixEndpoint* connectedEndpoint = (UnixEndpoint*)newSocket->first_protocol; UnixEndpointLocker connectedLocker(connectedEndpoint); connectedEndpoint->_Spawn(this, listeningEndpoint, peerFifo); // update our attributes _UnsetReceiveFifo(); fPeerEndpoint = connectedEndpoint; PeerAddress().SetTo(&connectedEndpoint->socket->address); fPeerEndpoint->AcquireReference(); fReceiveFifo = fifo; fCredentials.pid = getpid(); fCredentials.uid = geteuid(); fCredentials.gid = getegid(); fifoDeleter.Detach(); peerFifoDeleter.Detach(); fState = UNIX_ENDPOINT_CONNECTED; gSocketModule->set_connected(newSocket); release_sem(listeningEndpoint->fAcceptSemaphore); connectedLocker.Unlock(); peerLocker.Unlock(); endpointLocker.Unlock(); RETURN_ERROR(B_OK); }
status_t UnixEndpoint::Bind(const struct sockaddr *_address) { if (_address->sa_family != AF_UNIX) RETURN_ERROR(EAFNOSUPPORT); TRACE("[%ld] %p->UnixEndpoint::Bind(\"%s\")\n", find_thread(NULL), this, ConstSocketAddress(&gAddressModule, _address).AsString().Data()); const sockaddr_un* address = (const sockaddr_un*)_address; UnixEndpointLocker endpointLocker(this); if (fState != UNIX_ENDPOINT_NOT_CONNECTED || IsBound()) RETURN_ERROR(B_BAD_VALUE); if (address->sun_path[0] == '\0') { UnixAddressManagerLocker addressLocker(gAddressManager); // internal address space (or empty address) int32 internalID; if (UnixAddress::IsEmptyAddress(*address)) internalID = gAddressManager.NextUnusedInternalID(); else internalID = UnixAddress::InternalID(*address); if (internalID < 0) RETURN_ERROR(internalID); status_t error = _Bind(internalID); if (error != B_OK) RETURN_ERROR(error); sockaddr_un* outAddress = (sockaddr_un*)&socket->address; outAddress->sun_path[0] = '\0'; sprintf(outAddress->sun_path + 1, "%05lx", internalID); outAddress->sun_len = INTERNAL_UNIX_ADDRESS_LEN; // null-byte + 5 hex digits gAddressManager.Add(this); } else { // FS address space size_t pathLen = strnlen(address->sun_path, sizeof(address->sun_path)); if (pathLen == 0 || pathLen == sizeof(address->sun_path)) RETURN_ERROR(B_BAD_VALUE); struct vnode* vnode; status_t error = vfs_create_special_node(address->sun_path, NULL, S_IFSOCK | 0644, 0, !gStackModule->is_syscall(), NULL, &vnode); if (error != B_OK) RETURN_ERROR(error == B_FILE_EXISTS ? EADDRINUSE : error); error = _Bind(vnode); if (error != B_OK) { vfs_put_vnode(vnode); RETURN_ERROR(error); } size_t addressLen = address->sun_path + pathLen + 1 - (char*)address; memcpy(&socket->address, address, addressLen); socket->address.ss_len = addressLen; UnixAddressManagerLocker addressLocker(gAddressManager); gAddressManager.Add(this); } RETURN_ERROR(B_OK); }
void OHCI::_FinishTransfers() { while (!fStopFinishThread) { if (acquire_sem(fFinishTransfersSem) < B_OK) continue; // eat up sems that have been released by multiple interrupts int32 semCount = 0; get_sem_count(fFinishTransfersSem, &semCount); if (semCount > 0) acquire_sem_etc(fFinishTransfersSem, semCount, B_RELATIVE_TIMEOUT, 0); if (!Lock()) continue; TRACE("finishing transfers (first transfer: %p; last" " transfer: %p)\n", fFirstTransfer, fLastTransfer); transfer_data *lastTransfer = NULL; transfer_data *transfer = fFirstTransfer; Unlock(); while (transfer) { bool transferDone = false; ohci_general_td *descriptor = transfer->first_descriptor; ohci_endpoint_descriptor *endpoint = transfer->endpoint; status_t callbackStatus = B_OK; MutexLocker endpointLocker(endpoint->lock); if ((endpoint->head_physical_descriptor & OHCI_ENDPOINT_HEAD_MASK) != endpoint->tail_physical_descriptor) { // there are still active transfers on this endpoint, we need // to wait for all of them to complete, otherwise we'd read // a potentially bogus data toggle value below TRACE("endpoint %p still has active tds\n", endpoint); lastTransfer = transfer; transfer = transfer->link; continue; } endpointLocker.Unlock(); while (descriptor && !transfer->canceled) { uint32 status = OHCI_TD_GET_CONDITION_CODE(descriptor->flags); if (status == OHCI_TD_CONDITION_NOT_ACCESSED) { // td is still active TRACE("td %p still active\n", descriptor); break; } if (status != OHCI_TD_CONDITION_NO_ERROR) { // an error occured, but we must ensure that the td // was actually done if (endpoint->head_physical_descriptor & OHCI_ENDPOINT_HALTED) { // the endpoint is halted, this guaratees us that this // descriptor has passed (we don't know if the endpoint // was halted because of this td, but we do not need // to know, as when it was halted by another td this // still ensures that this td was handled before). TRACE_ERROR("td error: 0x%08lx\n", status); switch (status) { case OHCI_TD_CONDITION_CRC_ERROR: case OHCI_TD_CONDITION_BIT_STUFFING: case OHCI_TD_CONDITION_TOGGLE_MISMATCH: callbackStatus = B_DEV_CRC_ERROR; break; case OHCI_TD_CONDITION_STALL: callbackStatus = B_DEV_STALLED; break; case OHCI_TD_CONDITION_NO_RESPONSE: callbackStatus = B_TIMED_OUT; break; case OHCI_TD_CONDITION_PID_CHECK_FAILURE: callbackStatus = B_DEV_BAD_PID; break; case OHCI_TD_CONDITION_UNEXPECTED_PID: callbackStatus = B_DEV_UNEXPECTED_PID; break; case OHCI_TD_CONDITION_DATA_OVERRUN: callbackStatus = B_DEV_DATA_OVERRUN; break; case OHCI_TD_CONDITION_DATA_UNDERRUN: callbackStatus = B_DEV_DATA_UNDERRUN; break; case OHCI_TD_CONDITION_BUFFER_OVERRUN: callbackStatus = B_DEV_FIFO_OVERRUN; break; case OHCI_TD_CONDITION_BUFFER_UNDERRUN: callbackStatus = B_DEV_FIFO_UNDERRUN; break; default: callbackStatus = B_ERROR; break; } transferDone = true; break; } else { // an error occured but the endpoint is not halted so // the td is in fact still active TRACE("td %p active with error\n", descriptor); break; } } // the td has completed without an error TRACE("td %p done\n", descriptor); if (descriptor == transfer->last_descriptor || descriptor->buffer_physical != 0) { // this is the last td of the transfer or a short packet callbackStatus = B_OK; transferDone = true; break; } descriptor = (ohci_general_td *)descriptor->next_logical_descriptor; } if (transfer->canceled) { // when a transfer is canceled, all transfers to that endpoint // are canceled by setting the head pointer to the tail pointer // which causes all of the tds to become "free" (as they are // inaccessible and not accessed anymore (as setting the head // pointer required disabling the endpoint)) callbackStatus = B_OK; transferDone = true; } if (!transferDone) { lastTransfer = transfer; transfer = transfer->link; continue; } // remove the transfer from the list first so we are sure // it doesn't get canceled while we still process it transfer_data *next = transfer->link; if (Lock()) { if (lastTransfer) lastTransfer->link = transfer->link; if (transfer == fFirstTransfer) fFirstTransfer = transfer->link; if (transfer == fLastTransfer) fLastTransfer = lastTransfer; // store the currently processing pipe here so we can wait // in cancel if we are processing something on the target pipe if (!transfer->canceled) fProcessingPipe = transfer->transfer->TransferPipe(); transfer->link = NULL; Unlock(); } // break the descriptor chain on the last descriptor transfer->last_descriptor->next_logical_descriptor = NULL; TRACE("transfer %p done with status 0x%08lx\n", transfer, callbackStatus); // if canceled the callback has already been called if (!transfer->canceled) { size_t actualLength = 0; if (callbackStatus == B_OK) { if (transfer->data_descriptor && transfer->incoming) { // data to read out iovec *vector = transfer->transfer->Vector(); size_t vectorCount = transfer->transfer->VectorCount(); transfer->transfer->PrepareKernelAccess(); actualLength = _ReadDescriptorChain( transfer->data_descriptor, vector, vectorCount); } else if (transfer->data_descriptor) { // read the actual length that was sent actualLength = _ReadActualLength( transfer->data_descriptor); } // get the last data toggle and store it for next time transfer->transfer->TransferPipe()->SetDataToggle( (endpoint->head_physical_descriptor & OHCI_ENDPOINT_TOGGLE_CARRY) != 0); if (transfer->transfer->IsFragmented()) { // this transfer may still have data left TRACE("advancing fragmented transfer\n"); transfer->transfer->AdvanceByFragment(actualLength); if (transfer->transfer->VectorLength() > 0) { TRACE("still %ld bytes left on transfer\n", transfer->transfer->VectorLength()); // TODO actually resubmit the transfer } // the transfer is done, but we already set the // actualLength with AdvanceByFragment() actualLength = 0; } } transfer->transfer->Finished(callbackStatus, actualLength); fProcessingPipe = NULL; } if (callbackStatus != B_OK) { // remove the transfer and make the head pointer valid again // (including clearing the halt state) _RemoveTransferFromEndpoint(transfer); } // free the descriptors _FreeDescriptorChain(transfer->first_descriptor); delete transfer->transfer; delete transfer; transfer = next; } } }
status_t OHCI::_SubmitRequest(Transfer *transfer) { usb_request_data *requestData = transfer->RequestData(); bool directionIn = (requestData->RequestType & USB_REQTYPE_DEVICE_IN) != 0; ohci_general_td *setupDescriptor = _CreateGeneralDescriptor(sizeof(usb_request_data)); if (!setupDescriptor) { TRACE_ERROR("failed to allocate setup descriptor\n"); return B_NO_MEMORY; } setupDescriptor->flags = OHCI_TD_DIRECTION_PID_SETUP | OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_TOGGLE_0 | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE); ohci_general_td *statusDescriptor = _CreateGeneralDescriptor(0); if (!statusDescriptor) { TRACE_ERROR("failed to allocate status descriptor\n"); _FreeGeneralDescriptor(setupDescriptor); return B_NO_MEMORY; } statusDescriptor->flags = (directionIn ? OHCI_TD_DIRECTION_PID_OUT : OHCI_TD_DIRECTION_PID_IN) | OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE); iovec vector; vector.iov_base = requestData; vector.iov_len = sizeof(usb_request_data); _WriteDescriptorChain(setupDescriptor, &vector, 1); status_t result; ohci_general_td *dataDescriptor = NULL; if (transfer->VectorCount() > 0) { ohci_general_td *lastDescriptor = NULL; result = _CreateDescriptorChain(&dataDescriptor, &lastDescriptor, directionIn ? OHCI_TD_DIRECTION_PID_IN : OHCI_TD_DIRECTION_PID_OUT, transfer->VectorLength()); if (result < B_OK) { _FreeGeneralDescriptor(setupDescriptor); _FreeGeneralDescriptor(statusDescriptor); return result; } if (!directionIn) { _WriteDescriptorChain(dataDescriptor, transfer->Vector(), transfer->VectorCount()); } _LinkDescriptors(setupDescriptor, dataDescriptor); _LinkDescriptors(lastDescriptor, statusDescriptor); } else { _LinkDescriptors(setupDescriptor, statusDescriptor); } // Add to the transfer list ohci_endpoint_descriptor *endpoint = (ohci_endpoint_descriptor *)transfer->TransferPipe()->ControllerCookie(); MutexLocker endpointLocker(endpoint->lock); result = _AddPendingTransfer(transfer, endpoint, setupDescriptor, dataDescriptor, statusDescriptor, directionIn); if (result < B_OK) { TRACE_ERROR("failed to add pending transfer\n"); _FreeDescriptorChain(setupDescriptor); return result; } // Add the descriptor chain to the endpoint _SwitchEndpointTail(endpoint, setupDescriptor, statusDescriptor); endpointLocker.Unlock(); // Tell the controller to process the control list endpoint->flags &= ~OHCI_ENDPOINT_SKIP; _WriteReg(OHCI_COMMAND_STATUS, OHCI_CONTROL_LIST_FILLED); return B_OK; }