status_t SerialDevice::Open(uint32 flags) { if (fDeviceOpen) return B_BUSY; if (fDeviceRemoved) return B_DEV_NOT_READY; gTTYModule->ttyinit(&fTTY, true); fTTYFile.tty = &fTTY; fTTYFile.flags = flags; ResetDevice(); struct ddrover *ddr = gTTYModule->ddrstart(NULL); if (!ddr) return B_NO_MEMORY; gTTYModule->ddacquire(ddr, &gSerialDomain); status_t status = gTTYModule->ttyopen(&fTTYFile, ddr, usb_serial_service); gTTYModule->ddrdone(ddr); if (status < B_OK) { TRACE_ALWAYS("open: failed to open tty\n"); return status; } fDeviceThread = spawn_kernel_thread(DeviceThread, "usb_serial device thread", B_NORMAL_PRIORITY, this); if (fDeviceThread < B_OK) { TRACE_ALWAYS("open: failed to spawn kernel thread\n"); return fDeviceThread; } resume_thread(fDeviceThread); fControlOut = CLS_LINE_DTR | CLS_LINE_RTS; SetControlLineState(fControlOut); status = gUSBModule->queue_interrupt(fControlPipe, fInterruptBuffer, fInterruptBufferSize, InterruptCallbackFunction, this); if (status < B_OK) TRACE_ALWAYS("failed to queue initial interrupt\n"); fDeviceOpen = true; return B_OK; }
//***************************************************************************** // // Handles CDC driver notifications related to control and setup of the device. // // \param pvCBData is the client-supplied callback pointer for this channel. // \param ui32Event identifies the event we are being notified about. // \param ui32MsgValue is an event-specific value. // \param pvMsgData is an event-specific pointer. // // This function is called by the CDC driver to perform control-related // operations on behalf of the USB host. These functions include setting // and querying the serial communication parameters, setting handshake line // states and sending break conditions. // // \return The return value is event-specific. // //***************************************************************************** uint32_t ControlHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue, void *pvMsgData) { uint32_t ui32IntsOff; // // Which event are we being asked to process? // switch(ui32Event) { // // We are connected to a host and communication is now possible. // case USB_EVENT_CONNECTED: g_bUSBConfigured = true; // // Flush our buffers. // USBBufferFlush(&g_sTxBuffer); USBBufferFlush(&g_sRxBuffer); // // Tell the main loop to update the display. // ui32IntsOff = ROM_IntMasterDisable(); g_pcStatus = "Connected"; g_ui32Flags |= COMMAND_STATUS_UPDATE; if(!ui32IntsOff) { ROM_IntMasterEnable(); } break; // // The host has disconnected. // case USB_EVENT_DISCONNECTED: g_bUSBConfigured = false; ui32IntsOff = ROM_IntMasterDisable(); g_pcStatus = "Disconnected"; g_ui32Flags |= COMMAND_STATUS_UPDATE; if(!ui32IntsOff) { ROM_IntMasterEnable(); } break; // // Return the current serial communication parameters. // case USBD_CDC_EVENT_GET_LINE_CODING: GetLineCoding(pvMsgData); break; // // Set the current serial communication parameters. // case USBD_CDC_EVENT_SET_LINE_CODING: SetLineCoding(pvMsgData); break; // // Set the current serial communication parameters. // case USBD_CDC_EVENT_SET_CONTROL_LINE_STATE: SetControlLineState((uint16_t)ui32MsgValue); break; // // Send a break condition on the serial line. // case USBD_CDC_EVENT_SEND_BREAK: SendBreak(true); break; // // Clear the break condition on the serial line. // case USBD_CDC_EVENT_CLEAR_BREAK: SendBreak(false); break; // // Ignore SUSPEND and RESUME for now. // case USB_EVENT_SUSPEND: case USB_EVENT_RESUME: break; // // We don't expect to receive any other events. Ignore any that show // up in a release build or hang in a debug build. // default: #ifdef DEBUG while(1); #else break; #endif } return(0); }
//***************************************************************************** // // Handles CDC driver notifications related to control and setup of the device. // // \param pvCBData is the client-supplied callback pointer for this channel. // \param ulEvent identifies the event we are being notified about. // \param ulMsgValue is an event-specific value. // \param pvMsgData is an event-specific pointer. // // This function is called by the CDC driver to perform control-related // operations on behalf of the USB host. These functions include setting // and querying the serial communication parameters, setting handshake line // states and sending break conditions. // // \return The return value is event-specific. // //***************************************************************************** uint32_t ControlHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue, void *pvMsgData) { // // Which event are we being asked to process? // switch(ui32Event) { // // We are connected to a host and communication is now possible. // case USB_EVENT_CONNECTED: { // // Now connected and ready for normal operation. // HWREGBITW(&g_ui32Flags, FLAG_USB_CONFIGURED) = 1; // // Flush our buffers. // USBBufferFlush(&g_sTxBuffer); USBBufferFlush(&g_sRxBuffer); // // Tell the main loop to update the display. // g_pcStatus = "Host connected."; // // Set the command status update flag. // HWREGBITW(&g_ui32Flags, FLAG_STATUS_UPDATE) = 1; break; } // // The host has disconnected. // case USB_EVENT_DISCONNECTED: { // // No longer connected. // HWREGBITW(&g_ui32Flags, FLAG_USB_CONFIGURED) = 0; g_pcStatus = "Host disconnected."; // // Set the command status update flag. // HWREGBITW(&g_ui32Flags, FLAG_STATUS_UPDATE) = 1; break; } // // Return the current serial communication parameters. // case USBD_CDC_EVENT_GET_LINE_CODING: { GetLineCoding(pvMsgData, UART_CLOCK); break; } // // Set the current serial communication parameters. // case USBD_CDC_EVENT_SET_LINE_CODING: { SetLineCoding(pvMsgData, UART_CLOCK); break; } // // Set the current serial communication parameters. // case USBD_CDC_EVENT_SET_CONTROL_LINE_STATE: { SetControlLineState((uint16_t)ui32MsgValue); break; } // // Send a break condition on the serial line. // case USBD_CDC_EVENT_SEND_BREAK: { SendBreak(true); break; } // // Clear the break condition on the serial line. // case USBD_CDC_EVENT_CLEAR_BREAK: { SendBreak(false); break; } // // Ignore SUSPEND and RESUME for now. // case USB_EVENT_SUSPEND: case USB_EVENT_RESUME: { break; } // // We don't expect to receive any other events. Ignore any that show // up in a release build or hang in a debug build. // default: { #ifdef DEBUG while(1); #else break; #endif } } return(0); }
bool SerialDevice::Service(struct tty *ptty, struct ddrover *ddr, uint flags) { if (&fTTY != ptty) return false; if (flags <= TTYGETSIGNALS) { switch (flags) { case TTYENABLE: TRACE("TTYENABLE\n"); gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, false); gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true); fControlOut = CLS_LINE_DTR | CLS_LINE_RTS; SetControlLineState(fControlOut); break; case TTYDISABLE: TRACE("TTYDISABLE\n"); gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, false); fControlOut = 0x0; SetControlLineState(fControlOut); break; case TTYISTOP: TRACE("TTYISTOP\n"); fInputStopped = true; gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, false); break; case TTYIRESUME: TRACE("TTYIRESUME\n"); gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true); fInputStopped = false; break; case TTYGETSIGNALS: TRACE("TTYGETSIGNALS\n"); gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, true); gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true); gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDSR, false); gTTYModule->ttyhwsignal(ptty, ddr, TTYHWRI, false); break; case TTYSETMODES: TRACE("TTYSETMODES\n"); SetModes(); break; case TTYOSTART: case TTYOSYNC: case TTYSETBREAK: case TTYCLRBREAK: case TTYSETDTR: case TTYCLRDTR: TRACE("TTY other\n"); break; } return true; } return false; }
void SerialDevice::SetModes() { struct termios tios; memcpy(&tios, &fTTY.t, sizeof(struct termios)); uint16 newControl = fControlOut; TRACE_FUNCRES(trace_termios, &tios); static uint32 baudRates[] = { 0x00000000, //B0 0x00000032, //B50 0x0000004B, //B75 0x0000006E, //B110 0x00000086, //B134 0x00000096, //B150 0x000000C8, //B200 0x0000012C, //B300 0x00000258, //B600 0x000004B0, //B1200 0x00000708, //B1800 0x00000960, //B2400 0x000012C0, //B4800 0x00002580, //B9600 0x00004B00, //B19200 0x00009600, //B38400 0x0000E100, //B57600 0x0001C200, //B115200 0x00038400, //B230400 0x00070800, //460800 0x000E1000, //921600 }; uint32 baudCount = sizeof(baudRates) / sizeof(baudRates[0]); uint32 baudIndex = tios.c_cflag & CBAUD; if (baudIndex > baudCount) baudIndex = baudCount - 1; usb_serial_line_coding lineCoding; lineCoding.speed = baudRates[baudIndex]; lineCoding.stopbits = (tios.c_cflag & CSTOPB) ? LC_STOP_BIT_2 : LC_STOP_BIT_1; if (tios.c_cflag & PARENB) { lineCoding.parity = LC_PARITY_EVEN; if (tios.c_cflag & PARODD) lineCoding.parity = LC_PARITY_ODD; } else lineCoding.parity = LC_PARITY_NONE; lineCoding.databits = (tios.c_cflag & CS8) ? 8 : 7; if (lineCoding.speed == 0) { newControl &= 0xfffffffe; lineCoding.speed = fLineCoding.speed; } else newControl = CLS_LINE_DTR; if (fControlOut != newControl) { fControlOut = newControl; TRACE("newctrl send to modem: 0x%08x\n", newControl); SetControlLineState(newControl); } if (memcmp(&lineCoding, &fLineCoding, sizeof(usb_serial_line_coding)) != 0) { fLineCoding.speed = lineCoding.speed; fLineCoding.stopbits = lineCoding.stopbits; fLineCoding.databits = lineCoding.databits; fLineCoding.parity = lineCoding.parity; TRACE("send to modem: speed %d sb: 0x%08x db: 0x%08x parity: 0x%08x\n", fLineCoding.speed, fLineCoding.stopbits, fLineCoding.databits, fLineCoding.parity); SetLineCoding(&fLineCoding); } }
status_t SerialDevice::Open(uint32 flags) { if (fDeviceOpen) return B_BUSY; if (fDeviceRemoved) return B_DEV_NOT_READY; fMasterTTY = gTTYModule->tty_create(usb_serial_service, true); if (fMasterTTY == NULL) { TRACE_ALWAYS("open: failed to init master tty\n"); return B_NO_MEMORY; } fSlaveTTY = gTTYModule->tty_create(usb_serial_service, false); if (fSlaveTTY == NULL) { TRACE_ALWAYS("open: failed to init slave tty\n"); gTTYModule->tty_destroy(fMasterTTY); return B_NO_MEMORY; } fSystemTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY, O_RDWR); if (fSystemTTYCookie == NULL) { TRACE_ALWAYS("open: failed to init system tty cookie\n"); gTTYModule->tty_destroy(fMasterTTY); gTTYModule->tty_destroy(fSlaveTTY); return B_NO_MEMORY; } fDeviceTTYCookie = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY, O_RDWR); if (fDeviceTTYCookie == NULL) { TRACE_ALWAYS("open: failed to init device tty cookie\n"); gTTYModule->tty_destroy_cookie(fSystemTTYCookie); gTTYModule->tty_destroy(fMasterTTY); gTTYModule->tty_destroy(fSlaveTTY); return B_NO_MEMORY; } ResetDevice(); fStopThreads = false; fInputThread = spawn_kernel_thread(_InputThread, "usb_serial input thread", B_NORMAL_PRIORITY, this); if (fInputThread < 0) { TRACE_ALWAYS("open: failed to spawn input thread\n"); return fInputThread; } resume_thread(fInputThread); fControlOut = USB_CDC_CONTROL_SIGNAL_STATE_DTR | USB_CDC_CONTROL_SIGNAL_STATE_RTS; SetControlLineState(fControlOut); status_t status = gUSBModule->queue_interrupt(fControlPipe, fInterruptBuffer, fInterruptBufferSize, _InterruptCallbackFunction, this); if (status < B_OK) TRACE_ALWAYS("failed to queue initial interrupt\n"); // set our config (will propagate to the slave config as well in SetModes() gTTYModule->tty_control(fSystemTTYCookie, TCSETA, &fTTYConfig, sizeof(termios)); fDeviceOpen = true; return B_OK; }
bool SerialDevice::Service(struct tty *tty, uint32 op, void *buffer, size_t length) { if (tty != fMasterTTY) return false; switch (op) { case TTYENABLE: { bool enable = *(bool *)buffer; TRACE("TTYENABLE: %sable\n", enable ? "en" : "dis"); gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD, enable); gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS, enable); fControlOut = enable ? USB_CDC_CONTROL_SIGNAL_STATE_DTR | USB_CDC_CONTROL_SIGNAL_STATE_RTS : 0; SetControlLineState(fControlOut); return true; } case TTYISTOP: fInputStopped = *(bool *)buffer; TRACE("TTYISTOP: %sstopped\n", fInputStopped ? "" : "not "); gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS, !fInputStopped); return true; case TTYGETSIGNALS: TRACE("TTYGETSIGNALS\n"); gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD, (fControlOut & (USB_CDC_CONTROL_SIGNAL_STATE_DTR | USB_CDC_CONTROL_SIGNAL_STATE_RTS)) != 0); gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS, !fInputStopped); gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDSR, false); gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWRI, false); return true; case TTYSETMODES: TRACE("TTYSETMODES\n"); SetModes((struct termios *)buffer); return true; case TTYSETDTR: case TTYSETRTS: { bool set = *(bool *)buffer; uint8 bit = op == TTYSETDTR ? USB_CDC_CONTROL_SIGNAL_STATE_DTR : USB_CDC_CONTROL_SIGNAL_STATE_RTS; if (set) fControlOut |= bit; else fControlOut &= ~bit; SetControlLineState(fControlOut); return true; } case TTYOSTART: case TTYOSYNC: case TTYSETBREAK: TRACE("TTY other\n"); return true; } return false; }
/****************************************************************************** * * * \brief Handles CDC driver notifications related to control and setup of * * the device.\n * * * * \param pvCBData is the client-supplied callback pointer for this channel. * * * * \param ulEvent identifies the event we are being notified about. * * * * \param ulMsgValue is an event-specific value. * * * * \param pvMsgData is an event-specific pointer. * * * * \return The return value is event-specific. * * * ******************************************************************************/ unsigned int ControlHandler(void *pvCBData, unsigned int ulEvent, unsigned int ulMsgValue, void *pvMsgData) { unsigned char ulIntsOff; /* Which event are we being asked to process? */ switch(ulEvent) { /* We are connected to a host and communication is now possible. */ case USB_EVENT_CONNECTED: /* Flush our buffers. */ USBBufferFlush(&g_sTxBuffer); USBBufferFlush(&g_sRxBuffer); ulIntsOff = IntDisable(); g_pcStatus = "Host connected."; g_ulFlags |= COMMAND_STATUS_UPDATE; IntEnable(ulIntsOff); break; /* The host has disconnected. */ case USB_EVENT_DISCONNECTED: ulIntsOff = IntDisable(); g_pcStatus = "Host disconnected."; g_ulFlags |= COMMAND_STATUS_UPDATE; IntEnable(ulIntsOff); break; /* Return the current serial communication parameters. */ case USBD_CDC_EVENT_GET_LINE_CODING: GetLineCoding(pvMsgData); break; /* Set the current serial communication parameters. */ case USBD_CDC_EVENT_SET_LINE_CODING: SetLineCoding(pvMsgData); break; /* Set the current serial communication parameters. */ case USBD_CDC_EVENT_SET_CONTROL_LINE_STATE: SetControlLineState((unsigned short)ulMsgValue); break; /* Send a break condition on the serial line.*/ case USBD_CDC_EVENT_SEND_BREAK: SendBreak(true); break; /* Clear the break condition on the serial line. */ case USBD_CDC_EVENT_CLEAR_BREAK: SendBreak(false); break; /* Ignore SUSPEND and RESUME for now. */ case USB_EVENT_SUSPEND: case USB_EVENT_RESUME: break; /* We don't expect to receive any other events. Ignore any that show up in a release build or hang in a debug build. */ default: break; } return(0); }