?? hso.c
字號:
} }}static void put_rxbuf_data_and_resubmit_ctrl_urb(struct hso_serial *serial){ int count = 0; struct urb *urb; urb = serial->rx_urb[0]; if (serial->open_count > 0) { count = put_rxbuf_data(urb, serial); if (count == -1) return; } /* Re issue a read as long as we receive data. */ if (count == 0 && ((urb->actual_length != 0) || (serial->rx_state == RX_PENDING))) { serial->rx_state = RX_SENT; hso_mux_serial_read(serial); } else serial->rx_state = RX_IDLE;}/* read callback for Diag and CS port */static void hso_std_serial_read_bulk_callback(CALLBACK_ARGS){ struct hso_serial *serial = urb->context; int status = urb->status; /* sanity check */ if (!serial) { D1("serial == NULL"); return; } else if (status) { log_usb_status(status, __func__); return; } D4("\n--- Got serial_read_bulk callback %02x ---", status); D1("Actual length = %d\n", urb->actual_length); DUMP1(urb->transfer_buffer, urb->actual_length); /* Anyone listening? */ if (serial->open_count == 0) return; if (status == 0) { if (serial->parent->port_spec & HSO_INFO_CRC_BUG) { u32 rest; u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF }; rest = urb->actual_length % serial->in_endp->wMaxPacketSize; if (((rest == 5) || (rest == 6)) && !memcmp(((u8 *) urb->transfer_buffer) + urb->actual_length - 4, crc_check, 4)) { urb->actual_length -= 4; } } /* Valid data, handle RX data */ spin_lock(&serial->serial_lock); serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 1; put_rxbuf_data_and_resubmit_bulk_urb(serial); spin_unlock(&serial->serial_lock); } else if (status == -ENOENT || status == -ECONNRESET) { /* Unlinked - check for throttled port. */ D2("Port %d, successfully unlinked urb", serial->minor); spin_lock(&serial->serial_lock); serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0; hso_resubmit_rx_bulk_urb(serial, urb); spin_unlock(&serial->serial_lock); } else { D2("Port %d, status = %d for read urb", serial->minor, status); return; }}/* * This needs to be a tasklet otherwise we will * end up recursively calling this function. */void hso_unthrottle_tasklet(struct hso_serial *serial){ unsigned long flags; spin_lock_irqsave(&serial->serial_lock, flags); if ((serial->parent->port_spec & HSO_INTF_MUX)) put_rxbuf_data_and_resubmit_ctrl_urb(serial); else put_rxbuf_data_and_resubmit_bulk_urb(serial); spin_unlock_irqrestore(&serial->serial_lock, flags);}static void hso_unthrottle(struct tty_struct *tty){ struct hso_serial *serial = get_serial_by_tty(tty); tasklet_hi_schedule(&serial->unthrottle_tasklet);}/* open the requested serial port */static int hso_serial_open(struct tty_struct *tty, struct file *filp){ struct hso_serial *serial = get_serial_by_index(tty->index); int result1 = 0, result2 = 0; struct MUTEX *hso_mutex = NULL; int released = 0; /* sanity check */ if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { tty->driver_data = NULL; D1("Failed to open port"); return -ENODEV; } hso_mutex = &serial->parent->mutex->mutex; mutex_lock(hso_mutex); /* check for port already opened, if not set the termios */ /* The serial->open count needs to be here as hso_serial_close * will be called even if hso_serial_open returns -ENODEV. */ serial->open_count++;#ifdef CONFIG_HSO_AUTOPM result1 = usb_autopm_get_interface(serial->parent->interface); if (result1 < 0) goto err_out;#endif D1("Opening %d", serial->minor); kref_get(&serial->parent->ref); /* setup */ tty->driver_data = serial; serial->tty = tty; if (serial->open_count == 1) { tty->low_latency = 1; serial->rx_state = RX_IDLE; /* Force default termio settings */ _hso_serial_set_termios(tty, NULL); tasklet_init(&serial->unthrottle_tasklet, (void (*)(unsigned long))hso_unthrottle_tasklet, (unsigned long)serial); result2 = hso_start_serial_device(serial->parent, GFP_KERNEL); if (result2) { hso_stop_serial_device(serial->parent); serial->open_count--; } } else { D1("Port was already open"); }#ifdef CONFIG_HSO_AUTOPM usb_autopm_put_interface(serial->parent->interface);#endif /* done */ if (!result2) hso_serial_tiocmset(tty, NULL, TIOCM_RTS | TIOCM_DTR, 0);#ifdef CONFIG_HSO_AUTOPMerr_out:#endif if (result2) released = hso_kref_put(&serial->parent->ref, hso_serial_ref_free); mutex_unlock(hso_mutex); if (released) hso_free_mutex(container_of(hso_mutex, struct hso_mutex_t, mutex)); return result1 == 0 ? result2 : result1;}/* close the requested serial port */static void hso_serial_close(struct tty_struct *tty, struct file *filp){ struct hso_serial *serial = tty->driver_data; u8 usb_gone; struct MUTEX *hso_mutex; int released; D1("Closing serial port"); if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { D1("invalid serial structure bailing out. "); return; } hso_mutex = &serial->parent->mutex->mutex; mutex_lock(hso_mutex); usb_gone = serial->parent->usb_gone;#ifdef CONFIG_HSO_AUTOPM if (!usb_gone) usb_autopm_get_interface(serial->parent->interface);#endif /* reset the rts and dtr */ /* do the actual close */ serial->open_count--; if (serial->open_count <= 0) { serial->open_count = 0; if (serial->tty) { serial->tty->driver_data = NULL; serial->tty = NULL; } if (!usb_gone) hso_stop_serial_device(serial->parent); tasklet_kill(&serial->unthrottle_tasklet); }#ifdef CONFIG_HSO_AUTOPM if (!usb_gone) usb_autopm_put_interface(serial->parent->interface);#endif released = hso_kref_put(&serial->parent->ref, hso_serial_ref_free); mutex_unlock(hso_mutex); if (released) hso_free_mutex(container_of(hso_mutex, struct hso_mutex_t, mutex));}/* close the requested serial port */static int hso_serial_write(struct tty_struct *tty, const unsigned char *buf, int count){ struct hso_serial *serial = get_serial_by_tty(tty); int space, tx_bytes; unsigned long flags; /* sanity check */ if (serial == NULL) { printk(KERN_ERR "%s: serial is NULL\n", __func__); return -ENODEV; } spin_lock_irqsave(&serial->serial_lock, flags); space = serial->tx_data_length - serial->tx_buffer_count; tx_bytes = (count < space) ? count : space; if (!tx_bytes) goto out; memcpy(serial->tx_buffer + serial->tx_buffer_count, buf, tx_bytes); serial->tx_buffer_count += tx_bytes;out: spin_unlock_irqrestore(&serial->serial_lock, flags); hso_kick_transmit(serial); /* done */ return tx_bytes;}/* how much room is there for writing */static int hso_serial_write_room(struct tty_struct *tty){ struct hso_serial *serial = get_serial_by_tty(tty); int room; unsigned long flags; spin_lock_irqsave(&serial->serial_lock, flags); room = serial->tx_data_length - serial->tx_buffer_count; spin_unlock_irqrestore(&serial->serial_lock, flags); /* return free room */ return room;}/* setup the term */static void hso_serial_set_termios(struct tty_struct *tty, struct TERMIOS *old){ struct hso_serial *serial = get_serial_by_tty(tty); unsigned long flags; if (old) D5("Termios called with: cflags new[%d] - old[%d]", tty->termios->c_cflag, old->c_cflag); /* the actual setup */ spin_lock_irqsave(&serial->serial_lock, flags); if (serial->open_count) _hso_serial_set_termios(tty, old); else tty->termios = old; spin_unlock_irqrestore(&serial->serial_lock, flags); /* done */ return;}/* how many characters in the buffer */static int hso_serial_chars_in_buffer(struct tty_struct *tty){ struct hso_serial *serial = get_serial_by_tty(tty); int chars; unsigned long flags; /* sanity check */ if (serial == NULL) return 0; spin_lock_irqsave(&serial->serial_lock, flags); chars = serial->tx_buffer_count; spin_unlock_irqrestore(&serial->serial_lock, flags); return chars;}int tiocmget_submit_urb(struct hso_serial *serial, struct hso_tiocmget *tiocmget, struct usb_device *usb){ int result; if (serial->parent->usb_gone) return -ENODEV; usb_fill_int_urb(tiocmget->urb, usb, usb_rcvintpipe(usb, tiocmget->endp-> bEndpointAddress & 0x7F), &tiocmget->serial_state_notification, sizeof(struct hso_serial_state_notification), tiocmget_intr_callback, serial, tiocmget->endp->bInterval); result = usb_submit_urb(tiocmget->urb, GFP_ATOMIC); if (result) { dev_warn(&usb->dev, "%s usb_submit_urb failed %d\n", __func__, result); } return result;}static void tiocmget_intr_callback(CALLBACK_ARGS){ struct hso_serial *serial = urb->context; struct hso_tiocmget *tiocmget; int status = urb->status; u16 UART_state_bitmap, prev_UART_state_bitmap; struct uart_icount *icount; struct hso_serial_state_notification *serial_state_notification; struct usb_device *usb; /* Sanity checks */ if (!serial) return; if (status) { log_usb_status(status, __func__); return; } tiocmget = serial->tiocmget; if (!tiocmget) return; usb = serial->parent->usb; serial_state_notification = &tiocmget->serial_state_notification; if (serial_state_notification->bmRequestType != BM_REQUEST_TYPE || serial_state_notification->bNotification != B_NOTIFICATION || le16_to_cpu(serial_state_notification->wValue) != W_VALUE || le16_to_cpu(serial_state_notification->wIndex) != W_INDEX || le16_to_cpu(serial_state_notification->wLength) != W_LENGTH) { dev_warn(&usb->dev, "hso received invalid serial state notification\n"); DUMP((unsigned char *)serial_state_notification, sizeof(struct hso_serial_state_notification)); } else { UART_state_bitmap = le16_to_cpu(serial_state_notification-> UART_state_bitmap); prev_UART_state_bitmap = tiocmget->prev_UART_state_bitmap; icount = &tiocmget->icount; spin_lock(&serial->serial_lock); if ((UART_state_bitmap & B_OVERRUN) != (prev_UART_state_bitmap & B_OVERRUN)) icount->parity++; if ((UART_state_bitmap & B_PARITY) != (prev_UART_state_bitmap & B_PARITY)) icount->parity++; if ((UART_state_bitmap & B_FRAMING) != (prev_UART_state_bitmap & B_FRAMING)) icount->frame++; if ((UART_state_bitmap & B_RING_SIGNAL) && !(prev_UART_state_bitmap & B_RING_SIGNAL)) icount->rng++; if ((UART_state_bitmap & B_BREAK) != (prev_UART_state_bitmap & B_BREAK)) icount->brk++; if ((UART_state_bitmap & B_TX_CARRIER) != (prev_UART_state_bitmap & B_TX_CARRIER)) icount->dsr++; if ((UART_state_bitmap & B_RX_CARRIER) != (prev_UART_state_bitmap & B_RX_CARRIER)) icount->dcd++; tiocmget->prev_UART_state_bitmap = UART_state_bitmap; spin_unlock(&serial->serial_lock); tiocmget->intr_completed = 1; wake_up_interruptible(&tiocmget->waitq); } memset(serial_state_notification, 0, sizeof(struct hso_serial_state_notification)); tiocmget_submit_urb(serial, tiocmget, serial->parent->usb);}/* * next few functions largely stolen from drivers/serial/serial_core.c *//* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */static inthso_wait_modem_status(struct hso_serial *serial, unsigned long arg){ DECLARE_WAITQUEUE(wait, current); struct uart_icount cprev, cnow; struct hso_tiocmget *tiocmget; int ret; tiocmget = serial->tiocmget; if (!tiocmget) return -ENOENT; /* * note the counters on entry */ spin_lock_irq(&serial->serial_lock); memcpy(&cprev, &tiocmget->icount, sizeof(struct uart_icount)); spin_unlock_irq(&serial->serial_lock); add_wait_queue(&tiocmget->waitq, &wait); for (;;) { spin_lock_irq(&serial->serial_lock); memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount)); spin_unlock_irq(&serial->serial_lock); set_current_state(TASK_INTERRUPTIBLE); if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd))) { ret = 0; break; } schedule(); /* see if a signal did it */ if (signal_pending(current)) { ret = -ERESTARTSYS; break; } cprev = cnow; } current->state = TASK_RUNNING; remove_wait_queue(&tiocmget->waitq, &wait); return ret;}/* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */static int hso_get_count(struct hso_serial *serial, struct serial_icounter_struct __user *icnt){ struct serial_icounter_struct icount; struct uart_icount cnow; struct hso_tiocmget *tiocmget = serial->tiocmget; if (!tiocmget) return -ENOENT; spin_lock_irq(&serial->serial_lock); memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount)); spin_unlock_irq(&serial->serial_lock); icount.cts = cnow.cts; icount.dsr = cnow.dsr; icount.rng = cnow.rng; icount.dcd = cnow.dcd; icount.rx = cnow.rx; icount.tx = cnow.tx; icount.frame = cnow.frame; icount.overrun = cnow.overrun; icount.parity = cnow.parity; icount.brk = cnow.brk; icount.buf_overrun = cnow.buf_overrun; return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -