📄 cdc-acm.c
字号:
acm->readsize, acm_read_bulk, rcv); rcv->urb->transfer_dma = buf->dma; rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); /* This shouldn't kill the driver as unsuccessful URBs are returned to the free-urbs-pool and resubmited ASAP */ if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { list_add(&buf->list, &acm->spare_read_bufs); spin_lock_irqsave(&acm->read_lock, flags); list_add(&rcv->list, &acm->spare_read_urbs); spin_unlock_irqrestore(&acm->read_lock, flags); return; } }}/* data interface wrote those outgoing bytes */static void acm_write_bulk(struct urb *urb){ struct acm *acm = (struct acm *)urb->context; dbg("Entering acm_write_bulk with status %d", urb->status); acm_write_done(acm); acm_write_start(acm); if (ACM_READY(acm)) schedule_work(&acm->work);}static void acm_softint(struct work_struct *work){ struct acm *acm = container_of(work, struct acm, work); dbg("Entering acm_softint."); if (!ACM_READY(acm)) return; tty_wakeup(acm->tty);}/* * TTY handlers */static int acm_tty_open(struct tty_struct *tty, struct file *filp){ struct acm *acm; int rv = -EINVAL; int i; dbg("Entering acm_tty_open."); mutex_lock(&open_mutex); acm = acm_table[tty->index]; if (!acm || !acm->dev) goto err_out; else rv = 0; tty->driver_data = acm; acm->tty = tty; /* force low_latency on so that our tty_push actually forces the data through, otherwise it is scheduled, and with high data rates data can get lost. */ tty->low_latency = 1; if (acm->used++) { goto done; } acm->ctrlurb->dev = acm->dev; if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { dbg("usb_submit_urb(ctrl irq) failed"); goto bail_out; } if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && (acm->ctrl_caps & USB_CDC_CAP_LINE)) goto full_bailout; INIT_LIST_HEAD(&acm->spare_read_urbs); INIT_LIST_HEAD(&acm->spare_read_bufs); INIT_LIST_HEAD(&acm->filled_read_bufs); for (i = 0; i < acm->rx_buflimit; i++) { list_add(&(acm->ru[i].list), &acm->spare_read_urbs); } for (i = 0; i < acm->rx_buflimit; i++) { list_add(&(acm->rb[i].list), &acm->spare_read_bufs); } acm->throttle = 0; tasklet_schedule(&acm->urb_task);done:err_out: mutex_unlock(&open_mutex); return rv;full_bailout: usb_kill_urb(acm->ctrlurb);bail_out: acm->used--; mutex_unlock(&open_mutex); return -EIO;}static void acm_tty_unregister(struct acm *acm){ int i,nr; nr = acm->rx_buflimit; tty_unregister_device(acm_tty_driver, acm->minor); usb_put_intf(acm->control); acm_table[acm->minor] = NULL; usb_free_urb(acm->ctrlurb); usb_free_urb(acm->writeurb); for (i = 0; i < nr; i++) usb_free_urb(acm->ru[i].urb); kfree(acm->country_codes); kfree(acm);}static void acm_tty_close(struct tty_struct *tty, struct file *filp){ struct acm *acm = tty->driver_data; int i,nr; if (!acm || !acm->used) return; nr = acm->rx_buflimit; mutex_lock(&open_mutex); if (!--acm->used) { if (acm->dev) { acm_set_control(acm, acm->ctrlout = 0); usb_kill_urb(acm->ctrlurb); usb_kill_urb(acm->writeurb); for (i = 0; i < nr; i++) usb_kill_urb(acm->ru[i].urb); } else acm_tty_unregister(acm); } mutex_unlock(&open_mutex);}static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count){ struct acm *acm = tty->driver_data; int stat; unsigned long flags; int wbn; struct acm_wb *wb; dbg("Entering acm_tty_write to write %d bytes,", count); if (!ACM_READY(acm)) return -EINVAL; if (!count) return 0; spin_lock_irqsave(&acm->write_lock, flags); if ((wbn = acm_wb_alloc(acm)) < 0) { spin_unlock_irqrestore(&acm->write_lock, flags); acm_write_start(acm); return 0; } wb = &acm->wb[wbn]; count = (count > acm->writesize) ? acm->writesize : count; dbg("Get %d bytes...", count); memcpy(wb->buf, buf, count); wb->len = count; spin_unlock_irqrestore(&acm->write_lock, flags); if ((stat = acm_write_start(acm)) < 0) return stat; return count;}static int acm_tty_write_room(struct tty_struct *tty){ struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return -EINVAL; /* * Do not let the line discipline to know that we have a reserve, * or it might get too enthusiastic. */ return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;}static int acm_tty_chars_in_buffer(struct tty_struct *tty){ struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return -EINVAL; /* * This is inaccurate (overcounts), but it works. */ return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;}static void acm_tty_throttle(struct tty_struct *tty){ struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return; spin_lock_bh(&acm->throttle_lock); acm->throttle = 1; spin_unlock_bh(&acm->throttle_lock);}static void acm_tty_unthrottle(struct tty_struct *tty){ struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return; spin_lock_bh(&acm->throttle_lock); acm->throttle = 0; spin_unlock_bh(&acm->throttle_lock); tasklet_schedule(&acm->urb_task);}static void acm_tty_break_ctl(struct tty_struct *tty, int state){ struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return; if (acm_send_break(acm, state ? 0xffff : 0)) dbg("send break failed");}static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file){ struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return -EINVAL; return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | TIOCM_CTS;}static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear){ struct acm *acm = tty->driver_data; unsigned int newctrl; if (!ACM_READY(acm)) return -EINVAL; newctrl = acm->ctrlout; set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); newctrl = (newctrl & ~clear) | set; if (acm->ctrlout == newctrl) return 0; return acm_set_control(acm, acm->ctrlout = newctrl);}static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return -EINVAL; return -ENOIOCTLCMD;}static const __u32 acm_tty_speed[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000};static const __u8 acm_tty_size[] = { 5, 6, 7, 8};static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old){ struct acm *acm = tty->driver_data; struct ktermios *termios = tty->termios; struct usb_cdc_line_coding newline; int newctrl = acm->ctrlout; if (!ACM_READY(acm)) return; newline.dwDTERate = cpu_to_le32p(acm_tty_speed + (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0)); newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0; newline.bParityType = termios->c_cflag & PARENB ? (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; acm->clocal = ((termios->c_cflag & CLOCAL) != 0); if (!newline.dwDTERate) { newline.dwDTERate = acm->line.dwDTERate; newctrl &= ~ACM_CTRL_DTR; } else newctrl |= ACM_CTRL_DTR; if (newctrl != acm->ctrlout) acm_set_control(acm, acm->ctrlout = newctrl); if (memcmp(&acm->line, &newline, sizeof newline)) { memcpy(&acm->line, &newline, sizeof newline); dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate), newline.bCharFormat, newline.bParityType, newline.bDataBits); acm_set_line(acm, &acm->line); }}/* * USB probe and disconnect routines. *//* Little helper: write buffers free */static void acm_write_buffers_free(struct acm *acm){ int i; struct acm_wb *wb; for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah); }}/* Little helper: write buffers allocate */static int acm_write_buffers_alloc(struct acm *acm){ int i; struct acm_wb *wb; for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL, &wb->dmah); if (!wb->buf) { while (i != 0) { --i; --wb; usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah); } return -ENOMEM; } } return 0;}static int acm_probe (struct usb_interface *intf, const struct usb_device_id *id){ struct usb_cdc_union_desc *union_header = NULL; struct usb_cdc_country_functional_desc *cfd = NULL; char *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; struct usb_interface *control_interface; struct usb_interface *data_interface; struct usb_endpoint_descriptor *epctrl; struct usb_endpoint_descriptor *epread; struct usb_endpoint_descriptor *epwrite; struct usb_device *usb_dev = interface_to_usbdev(intf); struct acm *acm; int minor; int ctrlsize,readsize; u8 *buf; u8 ac_management_function = 0; u8 call_management_function = 0; int call_interface_num = -1; int data_interface_num; unsigned long quirks; int num_rx_buf; int i; /* normal quirks */ quirks = (unsigned long)id->driver_info; num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; /* handle quirks deadly to normal probing*/ if (quirks == NO_UNION_NORMAL) { data_interface = usb_ifnum_to_if(usb_dev, 1); control_interface = usb_ifnum_to_if(usb_dev, 0); goto skip_normal_probe; } /* normal probing*/ if (!buffer) { err("Wierd descriptor references\n"); return -EINVAL; } if (!buflen) { if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) { dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint"); buflen = intf->cur_altsetting->endpoint->extralen; buffer = intf->cur_altsetting->endpoint->extra; } else { err("Zero length descriptor references\n"); return -EINVAL; } } while (buflen > 0) { if (buffer [1] != USB_DT_CS_INTERFACE) { err("skipping garbage\n"); goto next_desc; } switch (buffer [2]) { case USB_CDC_UNION_TYPE: /* we've found it */ if (union_header) { err("More than one union descriptor, skipping ...");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -