📄 usbdev.c
字号:
static voidendpoint_reset_datatoggle(endpoint_t * ep){ // FIXME: is this possible?}#ifdef USBDEV_PIOstatic intendpoint_fifo_read(endpoint_t * ep){ unsigned long flags; int read_count = 0; u8 *bufptr; pkt_t *pkt = ep->outlist.tail; if (!pkt) return -EINVAL; spin_lock_irqsave(&ep->lock, flags); bufptr = pkt->bufptr; while (inl(ep->reg->read_fifo_status) & USBDEV_FSTAT_FCNT_MASK) { *bufptr++ = inl(ep->reg->read_fifo) & 0xff; read_count++; pkt->size++; } pkt->bufptr = bufptr; spin_unlock_irqrestore(&ep->lock, flags); return read_count;}static intendpoint_fifo_write(endpoint_t * ep){ unsigned long flags; int write_count = 0; u8 *bufptr; pkt_t *pkt = ep->inlist.head; if (!pkt) return -EINVAL; spin_lock_irqsave(&ep->lock, flags); bufptr = pkt->bufptr; while ((inl(ep->reg->write_fifo_status) & USBDEV_FSTAT_FCNT_MASK) < EP_FIFO_DEPTH) { if (bufptr < pkt->buf + pkt->size) { outl_sync(*bufptr++, ep->reg->write_fifo); write_count++; } else { break; } } pkt->bufptr = bufptr; spin_unlock_irqrestore(&ep->lock, flags); return write_count;}#endif // USBDEV_PIO/* * This routine is called to restart transmission of a packet. * The endpoint's TSIZE must be set to the new packet's size, * and DMA to the write FIFO needs to be restarted. */static voidkickstart_send_packet(endpoint_t * ep){ u32 cs; pkt_t *pkt = ep->inlist.head; dbg(__FUNCTION__ ": pkt=%p", pkt); if (!pkt) return; /* * The write fifo should already be drained if things are * working right, but flush it anyway just in case. */ flush_write_fifo(ep); cs = inl(ep->reg->ctrl_stat) & USBDEV_CS_STALL; cs |= (pkt->size << USBDEV_CS_TSIZE_BIT); outl_sync(cs, ep->reg->ctrl_stat);#ifdef USBDEV_PIO endpoint_fifo_write(ep);#else disable_dma(ep->indma); if (get_dma_active_buffer(ep->indma)) { set_dma_count1(ep->indma, pkt->size); set_dma_addr1(ep->indma, virt_to_phys(pkt->bufptr)); enable_dma_buffer1(ep->indma); // reenable } else { set_dma_count0(ep->indma, pkt->size); set_dma_addr0(ep->indma, virt_to_phys(pkt->bufptr)); enable_dma_buffer0(ep->indma); // reenable } enable_dma(ep->indma);#endif}/* * This routine is called when a packet in the inlist has been * completed. Frees the completed packet and starts sending the * next. */static voidsend_packet_complete(endpoint_t * ep){ if (ep->inlist.head) dbg(__FUNCTION__ ": pkt=%p, ab=%d", ep->inlist.head, get_dma_active_buffer(ep->indma)); outl_sync(inl(ep->reg->ctrl_stat) & USBDEV_CS_STALL, ep->reg->ctrl_stat); //disable_dma(ep->indma); free_packet(ep, &ep->inlist); // begin transmitting next packet in the inlist if (ep->inlist.count) kickstart_send_packet(ep);}/* * Unlink and return a packet from the head of the given ep's packet * outlist. It is the responsibility of the caller to free the packet. * The receive complete interrupt adds packets to the tail of this list. */static pkt_t *receive_packet(endpoint_t * ep){ pkt_t *pkt = unlink_packet(ep, &ep->outlist); //dma_cache_inv((unsigned long)pkt->buf, pkt->size); return pkt;}/* * This routine is called to restart reception of a packet. */static voidkickstart_receive_packet(endpoint_t * ep){ pkt_t *pkt; // get and link a new packet for next reception if (!(pkt = add_packet(ep, &ep->outlist, ep->max_pkt_size))) { err(__FUNCTION__ ": could not alloc new packet"); return; } /* * The read fifo should already be drained if things are * working right, but flush it anyway just in case. */ flush_read_fifo(ep);#ifndef USBDEV_PIO if (get_dma_active_buffer(ep->outdma)) { set_dma_count1(ep->outdma, ep->max_pkt_size); set_dma_addr1(ep->outdma, virt_to_phys(pkt->bufptr)); enable_dma_buffer1(ep->outdma); // reenable } else { set_dma_count0(ep->outdma, ep->max_pkt_size); set_dma_addr0(ep->outdma, virt_to_phys(pkt->bufptr)); enable_dma_buffer0(ep->outdma); // reenable } enable_dma(ep->outdma);#endif}/* * This routine is called when a packet in the outlist has been * completed (received) and we need to prepare for a new packet * to be received. Halts DMA and computes the packet size from the * remaining DMA counter. Then prepares a new packet for reception * and restarts DMA. FIXME: what if another packet comes in * on top of the completed packet? Counter would be wrong. */static voidreceive_packet_complete(endpoint_t * ep){ pkt_t *pkt = ep->outlist.tail; if (!pkt) return; disable_dma(ep->outdma); pkt->size = ep->max_pkt_size - get_dma_residue(ep->outdma);#ifdef USBDEV_PIO pkt->bufptr = pkt->buf; // reset bufptr#endif dbg(__FUNCTION__ ": size=%d", pkt->size); kickstart_receive_packet(ep);}/* * Add a new packet to the tail of the given ep's packet * inlist. The transmit complete interrupt frees packets from * the head of this list. */static intsend_packet(endpoint_t * ep, u8 * data, int data_len, int from_user){ unsigned long flags; pkt_list_t *list = &ep->inlist; pkt_t *pkt; if (!data || !data_len) return 0; if (!(pkt = alloc_packet(data_len))) { err(__FUNCTION__ ": could not alloc new packet"); return -ENOMEM; } if (from_user) copy_from_user(pkt->bufptr, data, data_len); else memcpy(pkt->bufptr, data, data_len); au_sync(); //dma_cache_wback_inv((unsigned long)pkt->buf, data_len); link_packet(ep, list, pkt); spin_lock_irqsave(&ep->lock, flags); dbg(__FUNCTION__ ": size=%d, list count=%d", pkt->size, list->count); if (list->count == 1) { /* * if the packet count is one, it means the list was empty, * and no more data will go out this ep until we kick-start * it again. */ kickstart_send_packet(ep); } spin_unlock_irqrestore(&ep->lock, flags); return data_len;}// SETUP packet request parserstatic voidprocess_setup (struct usb_serial* serial, devrequest* setup){ int desc_len, strnum; dbg(__FUNCTION__ ": req %d", setup->request); switch (setup->request) { case USB_REQ_SET_ADDRESS: serial->address = le16_to_cpu(setup->value); dbg(__FUNCTION__ ": our address=%d", serial->address); if (serial->address > 127 || serial->state == CONFIGURED) { // usb spec doesn't tell us what to do, so just go to // default state serial->state = DEFAULT; serial->address = 0; } else if (serial->address) serial->state = ADDRESS; else serial->state = DEFAULT; break; case USB_REQ_GET_DESCRIPTOR: desc_len = le16_to_cpu(setup->length); switch (le16_to_cpu(setup->value) >> 8) { case USB_DT_DEVICE: // send device descriptor! desc_len = desc_len > serial->dev_desc->bLength ? serial->dev_desc->bLength : desc_len; dbg("sending device desc, size=%d", desc_len); send_packet(&serial->ep_ctrl, (u8*)serial->dev_desc, desc_len, 0); break; case USB_DT_CONFIG: // If the config descr index in low-byte of // setup->value is valid, send config descr, // otherwise stall ep0. if ((le16_to_cpu(setup->value) & 0xff) == 0) { // send config descriptor! if (desc_len <= USB_DT_CONFIG_SIZE) { dbg("sending partial config desc, size=%d", desc_len); send_packet(&serial->ep_ctrl, (u8*)serial->conf_desc, desc_len, 0); } else { u8 full_conf_desc[CONFIG_DESC_LEN]; int i, index = 0; memcpy(&full_conf_desc[index], serial->conf_desc, USB_DT_CONFIG_SIZE); index += USB_DT_CONFIG_SIZE; memcpy(&full_conf_desc[index], serial->if_desc, USB_DT_INTERFACE_SIZE); index += USB_DT_INTERFACE_SIZE; for (i = 0; i < NUM_PORTS; i++) { memcpy(&full_conf_desc[index], serial->port[i].ep_bulkin.desc, USB_DT_ENDPOINT_SIZE); index += USB_DT_ENDPOINT_SIZE; memcpy(&full_conf_desc[index], serial->port[i].ep_bulkout.desc, USB_DT_ENDPOINT_SIZE); index += USB_DT_ENDPOINT_SIZE; } dbg("sending whole config desc, size=%d, our size=%d", desc_len, CONFIG_DESC_LEN); desc_len = desc_len > CONFIG_DESC_LEN ? CONFIG_DESC_LEN : desc_len; send_packet(&serial->ep_ctrl, full_conf_desc, desc_len, 0); } } else endpoint_stall(&serial->ep_ctrl); break; case USB_DT_STRING: // If the string descr index in low-byte of setup->value // is valid, send string descr, otherwise stall ep0. strnum = le16_to_cpu(setup->value) & 0xff; if (strnum >= 0 && strnum < 6) { struct usb_string_descriptor *desc = serial->str_desc[strnum]; desc_len = desc_len > desc->bLength ? desc->bLength : desc_len; dbg("sending string desc %d", strnum); send_packet(&serial->ep_ctrl, (u8 *) desc, desc_len, 0); } else endpoint_stall(&serial->ep_ctrl); break; default: // Invalid request dbg("invalid get desc=%d, stalled", le16_to_cpu(setup->value) >> 8); endpoint_stall(&serial->ep_ctrl); // Stall endpoint 0 break; } break; case USB_REQ_SET_DESCRIPTOR: // FIXME: anything to set here? break; case USB_REQ_GET_INTERFACE: // interface must be zero. if ((le16_to_cpu(setup->index) & 0xff) || serial->state == ADDRESS) { // FIXME: respond with "request error". how? } else if (serial->state == CONFIGURED) { // send serial->alternate_setting dbg("sending alt setting"); send_packet(&serial->ep_ctrl, &serial->alternate_setting, 1, 0); } break; case USB_REQ_SET_INTERFACE: if (serial->state == ADDRESS) { // FIXME: respond with "request error". how? } else if (serial->state == CONFIGURED) { serial->interface = le16_to_cpu(setup->index) & 0xff; serial->alternate_setting = le16_to_cpu(setup->value) & 0xff; // interface and alternate_setting must be zero if (serial->interface || serial->alternate_setting) { // FIXME: respond with "request error". how? } } break; case USB_REQ_SET_CONFIGURATION: // set active config to low-byte of serial.value serial->configuration = le16_to_cpu(setup->value) & 0xff; dbg("set config, config=%d", serial->configuration); if (!serial->configuration && serial->state > DEFAULT) serial->state = ADDRESS; else if (serial->configuration == 1) serial->state = CONFIGURED; else { // FIXME: "respond with request error" - how? } break; case USB_REQ_GET_CONFIGURATION: // send serial->configuration dbg("sending config"); send_packet(&serial->ep_ctrl, &serial->configuration, 1, 0); break; case USB_REQ_GET_STATUS: // FIXME: looks like the h/w handles this one switch (setup->requesttype) { case 0x80: // Device // FIXME: send device status break; case 0x81: // Interface // FIXME: send interface status break; case 0x82: // End Point // FIXME: send endpoint status break; default: // Invalid Command endpoint_stall(&serial->ep_ctrl); // Stall End Point 0 break; } break; case USB_REQ_CLEAR_FEATURE: switch (setup->requesttype) { case 0x00: // Device if ((le16_to_cpu(setup->value) & 0xff) == 1) serial->remote_wakeup_en = 0; else endpoint_stall(&serial->ep_ctrl); break; case 0x02: // End Point if ((le16_to_cpu(setup->value) & 0xff) == 0) { endpoint_t *ep = epnum_to_ep(serial, le16_to_cpu(setup->index) & 0xff); endpoint_unstall(ep); endpoint_reset_datatoggle(ep); } else endpoint_stall(&serial->ep_ctrl); break; } break; case USB_REQ_SET_FEATURE: switch (setup->requesttype) { case 0x00: // Device if ((le16_to_cpu(setup->value) & 0xff) == 1) serial->remote_wakeup_en = 1; else endpoint_stall(&serial->ep_ctrl); break; case 0x02: // End Point if ((le16_to_cpu(setup->value) & 0xff) == 0) { endpoint_t *ep = epnum_to_ep(serial, le16_to_cpu(setup->index) & 0xff); endpoint_stall(ep); } else endpoint_stall(&serial->ep_ctrl); break; } break; default: endpoint_stall(&serial->ep_ctrl); // Stall End Point 0 break; }}/* * A complete packet (SETUP, DATA0, or DATA1) has been received * on the given endpoint's fifo. */static voidprocess_complete (struct usb_serial* serial, int fifo_num){ endpoint_t *ep = fifonum_to_ep(serial, fifo_num); struct usb_serial_port *port = NULL; pkt_t *pkt = 0; u32 cs; cs = inl(ep->reg->ctrl_stat); switch (fifo_num) { case 0: spin_lock(&ep->lock); // complete packet and prepare a new packet receive_packet_complete(ep); // Get it immediately from endpoint. if (!(pkt = receive_packet(ep))) { spin_unlock(&ep->lock); return; } // SETUP packet received ? //if (cs & USBDEV_CS_SU) { FIXME: uncomment! if (pkt->size == sizeof(devrequest)) { devrequest setup; if ((cs & (USBDEV_CS_NAK | USBDEV_CS_ACK)) == USBDEV_CS_ACK) dbg("got SETUP"); else dbg("got NAK SETUP, cs=%08x", cs); memcpy(&setup, pkt->bufptr, sizeof(devrequest)); process_setup(serial, &setup); //} else FIXME: uncomment! //dbg(__FUNCTION__ ": wrong size SETUP received"); } else { // DATAx packet received on endpoint 0 // FIXME: will need a state machine for control // OUT transactions dbg("got DATAx on EP0, size=%d, cs=%08x", pkt->size, cs); } spin_unlock(&ep->lock); // we're done processing the packet, free it kfree(pkt); break; case 4: case 5: port = fifonum_to_port(serial, fifo_num); dbg("got DATAx on port %d, cs=%08x", port->number, cs); spin_lock(&ep->lock); receive_packet_complete(ep); spin_unlock(&ep->lock); // mark a bh to push this data up to the tty queue_task(&port->receive_complete_tq, &tq_immediate); mark_bh(IMMEDIATE_BH); break; default: break; }}// This ISR needs to ack both the complete and suspend eventsstatic voidreq_sus_intr (int irq, void *dev_id, struct pt_regs *regs){ struct usb_serial *serial = (struct usb_serial *) dev_id; int i; u32 status; status = inl(USB_DEV_INT_STATUS); outl_sync(status, USB_DEV_INT_STATUS); // ack'em#ifdef USBDEV_PIO for (i = 0; i < 6; i++) { if (status & (1 << (USBDEV_INT_HF_BIT + i))) { endpoint_t *ep = fifonum_to_ep(serial, i); if (ep->desc->bEndpointAddress & USB_DIR_IN) endpoint_fifo_write(ep); else endpoint_fifo_read(ep); } }#endif for (i = 0; i < 6; i++) { if (status & (1 << i)) process_complete(serial, i); }}static voiddma_done_ctrl(struct usb_serial* serial){ endpoint_t *ep = &serial->ep_ctrl; u32 cs0, buff_done; spin_lock(&ep->lock); cs0 = inl(ep->reg->ctrl_stat); // first check packet transmit done if ((buff_done = get_dma_buffer_done(ep->indma)) != 0) { // transmitted a DATAx packet on control endpoint 0 // clear DMA done bit if (buff_done == DMA_D0) clear_dma_done0(ep->indma); else clear_dma_done1(ep->indma); send_packet_complete(ep); } /* * Now check packet receive done. Shouldn't get these, * the receive packet complete intr should happen * before the DMA done intr occurs. */ if ((buff_done = get_dma_buffer_done(ep->outdma)) != 0) { // clear DMA done bit if (buff_done == DMA_D0) clear_dma_done0(ep->outdma); else clear_dma_done1(ep->outdma); } spin_unlock(&ep->lock);}static voiddma_done_port(struct usb_serial_port * port){ endpoint_t *ep; u32 buff_done; // first check packet transmit done (bulk IN ep) ep = &port->ep_bulkin; spin_lock(&ep->lock); if ((buff_done = get_dma_buffer_done(ep->indma)) != 0) { // transmitted a DATAx packet on the port's bulk IN endpoint // clear DMA done bit if (buff_done == DMA_D0) clear_dma_done0(ep->indma); else clear_dma_done1(ep->indma);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -