📄 superh.c
字号:
/* * linux/drivers/usbd/superh_bi/udc.c -- USB Device Controller driver. * * Copyright (c) 2000, 2001, 2002 Lineo * * By: * Stuart Lynne <sl@lineo.com>, * Tom Rushworth <tbr@lineo.com>, * Bruce Balden <balden@lineo.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *//*****************************************************************************/#include <linux/config.h>#include <linux/module.h>#include "../usbd-export.h"#include "../usbd-build.h"#include "../usbd-module.h"MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com");MODULE_DESCRIPTION ("USB Device SuperH Bus Interface");USBD_MODULE_INFO ("superh_bi 0.1-alpha");#include <linux/kernel.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/delay.h>#include <asm/types.h>#include <asm/atomic.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/sh7727.h>#include "../usbd.h"#include "../usbd-func.h"#include "../usbd-bus.h"#include "../usbd-inline.h"#include "usbd-bi.h"#include "superh.h"#include "superh-hardware.h"#define MIN(a,b) ((a) < (b) ? (a) : (b))#define MAX(a,b) ((a) > (b) ? (a) : (b))/* * Define what interrupts are high versus low priority */#define F0_HIGH (EP1_FULL | EP2_TR | EP2_EMPTY )#define F0_LOW (BRST | SETUP_TS | EP0o_TS | EP0i_TR | EP0i_TS)#define F1_HIGH (0)#define F1_LOW (EP3_TR | EP3_TS | VBUSF)static struct usb_device_instance *udc_device; // required for the interrupt handler/* * ep_endpoints - map physical endpoints to logical endpoints */static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS];static struct urb *ep0_urb;static unsigned char usb_address;extern unsigned int udc_interrupts;unsigned int udc_f0_interrupts;unsigned int udc_f1_interrupts;unsigned long udc_vbusf_time;/* ********************************************************************************************* *//* * IO */void and_b (unsigned short mask, unsigned long addr){ ctrl_outb (ctrl_inw (addr) & mask, addr);}void or_b (unsigned short mask, unsigned long addr){ ctrl_outb (ctrl_inw (addr) | mask, addr);}void and_w (unsigned short mask, unsigned long addr){ ctrl_outw (ctrl_inw (addr) & mask, addr);}void or_w (unsigned short mask, unsigned long addr){ ctrl_outw (ctrl_inw (addr) | mask, addr);}// IO addresses for each physical ports FIFOunsigned long ep_address_o[4] = { USBEPDR0O, USBEPDR1, 0L, 0L, };unsigned long ep_address_i[4] = { USBEPDR0I, 0L, USBEPDR2, USBEPDR3, };// IO addresses for each physical ports triggerunsigned char ep_trigger_o[4] = { EP0o_PKTE, EP1_PKTE, 0L, 0L, };unsigned char ep_trigger_i[4] = { EP0i_PKTE, 0L, EP2_PKTE, EP3_PKTE, };/** * superh_write_buffer - write a buffer to the superh fifo * @ep: endpoint * @b: pointer to buffer to write * @size: number of bytes to write */static /*__inline__*/ void superh_write_buffer (unsigned char ep, unsigned char *b, unsigned char size){ if ((ep < UDC_MAX_ENDPOINTS) && ep_address_i[ep]) { while (size-- > 0) { ctrl_outb (*b++, ep_address_i[ep]); } ctrl_outb (ep_trigger_i[ep], USBTRG); }}/** * superh_read_buffer - fill a buffer from the superh fifo * @ep: endpoint * @b: pointer to buffer to fill * @size: number of bytes to read */static /*__inline__*/ void superh_read_buffer (unsigned char ep, unsigned char *b, unsigned char size){ if ((ep < UDC_MAX_ENDPOINTS) && ep_address_o[ep]) { while (size-- > 0) { *b++ = ctrl_inb (ep_address_o[ep]); } ctrl_outb (ep_trigger_o[ep], USBTRG); }}/* ********************************************************************************************* *//* Bulk OUT (recv) */static void /*__inline__*/ superh_out_ep1 (struct usb_endpoint_instance *endpoint){ int size = ctrl_inb (USBEPSZ1); if (endpoint && endpoint->rcv_urb && size) { // read data superh_read_buffer (1, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, size); usbd_rcv_complete_irq (endpoint, size, 0); } else { // reset fifo or_b (EP1_CLEAR, USBFCLR); usbd_rcv_complete_irq (endpoint, 0, EINVAL); }}/* ********************************************************************************************* *//* Bulk IN (tx) *//** * superh_in_epn - process tx interrupt * @ep: * @endpoint: * * Determine status of last data sent, queue new data. */static /* __inline__ */ void superh_in_epn (int ep, int restart){ if (ctrl_inb (USBIFR0) & EP2_EMPTY) { if ((ep < UDC_MAX_ENDPOINTS) && ep_endpoints[ep]) { struct usb_endpoint_instance *endpoint = ep_endpoints[ep]; usbd_tx_complete_irq (endpoint, restart); if (endpoint->tx_urb) { struct urb *urb = endpoint->tx_urb; if ((urb->actual_length - endpoint->sent) > 0) { endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->tx_packetSize); superh_write_buffer (ep, urb->buffer + endpoint->sent, endpoint->last); } else { // XXX ZLP endpoint->last = 0; superh_write_buffer (ep, urb->buffer + endpoint->sent, 0); } // enable transmit done interrupt switch (ep) { case 2: or_b (EP2_TR | EP2_EMPTY, USBIER0); break; case 3: or_b (EP3_TR | EP3_TS, USBIER1); break; } } else { // disable transmit done interrupt switch (ep) { case 2: if (ctrl_inb (USBIER0) & (EP2_TR | EP2_EMPTY)) { and_b (~(EP2_TR | EP2_EMPTY), USBIER0); } break; case 3: and_b (~(EP3_TR | EP3_TS), USBIER1); break; } } } }}/* ********************************************************************************************* *//* Control (endpoint zero) *//** * superh_in_ep0 - start transmit * @ep: */static void /*__inline__*/ superh_in_ep0 (struct usb_endpoint_instance *endpoint){ if (endpoint && endpoint->tx_urb) { struct urb *urb = endpoint->tx_urb; printk (KERN_DEBUG "superh_in_ep0: length: %d\n", endpoint->tx_urb->actual_length); // XXX if ((urb->actual_length - endpoint->sent) > 0) { endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->tx_packetSize); superh_write_buffer (0, urb->buffer + endpoint->sent, endpoint->last); } else { // XXX ZLP endpoint->last = 0; superh_write_buffer (0, urb->buffer + endpoint->sent, 0); } }}/** * superh_ep0_setup */static void superh_ep0_setup (void){ if (ep_endpoints[0]) { int i; struct usb_endpoint_instance *endpoint = ep_endpoints[0]; unsigned char *cp = (unsigned char *) &ep0_urb->device_request; for (i = 0; i < 8; i++) { cp[i] = ctrl_inb (USBEPDR0S); } // process setup packet if (usbd_recv_setup (ep0_urb)) { printk (KERN_DEBUG "superh_ep0: setup failed\n"); return; } // check data direction if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { // should we setup to receive data if (le16_to_cpu (ep0_urb->device_request.wLength)) { printk (KERN_DEBUG "superh_ep0: setup to read data %d\n", le16_to_cpu (ep0_urb->device_request.wLength)); endpoint->rcv_urb = ep0_urb; endpoint->rcv_urb->actual_length = 0; //superh_out(0, endpoint); return; } printk (KERN_DEBUG "superh_ep0: send ack %d\n", le16_to_cpu (ep0_urb->device_request.wLength)); // XXX // should be finished, send ack ctrl_outb (EP0s_PKTE, USBTRG); return; } // we should be sending data back // verify that we have non-zero request length if (!le16_to_cpu (ep0_urb->device_request.wLength)) { udc_stall_ep (0); return; } // verify that we have non-zero length response if (!ep0_urb->actual_length) { udc_stall_ep (0); return; } // send ack prior to sending data ctrl_outb (EP0s_PKTE, USBTRG); // start sending endpoint->tx_urb = ep0_urb; endpoint->sent = 0; endpoint->last = 0; superh_in_ep0 (endpoint); }}/* ********************************************************************************************* *//* Interrupt Handler(s) *//** * superh_int_hndlr_f0 - high priority interrupt handler * */static void superh_int_hndlr_f0 (int irq, void *dev_id, struct pt_regs *regs){ unsigned char f0_status; udc_interrupts++; udc_f0_interrupts++; f0_status = ctrl_inb (USBIFR0); if (f0_status & EP1_FULL) { superh_out_ep1 (ep_endpoints[1]); f0_status = ctrl_inb (USBIFR0); if (f0_status & EP1_FULL) { superh_out_ep1 (ep_endpoints[1]); } } else if (f0_status & (EP2_TR | EP2_EMPTY)) { superh_in_epn (2, 0); // XXX status? ctrl_outb (~(f0_status & EP2_TR), USBIFR0); }}/** * superh_int_hndlr_f1 - low priority interrupt handler * */static void superh_int_hndlr_f1 (int irq, void *dev_id, struct pt_regs *regs){ unsigned char f0_status; unsigned char f1_status; udc_interrupts++; udc_f1_interrupts++; f0_status = ctrl_inb (USBIFR0); f1_status = ctrl_inb (USBIFR1); ctrl_outb (~(f0_status & F0_LOW), USBIFR0); ctrl_outb (~(f1_status & F1_LOW), USBIFR1); //printk(KERN_DEBUG"superh_int_hndlr_f1[%x] status %02x %02x\n", udc_interrupts, f0_status, f1_status); // XXX if (f1_status & VBUSF) { /* * XXX vbusf can bounce, probably should disarm vbusf interrupt, for now, just check * that we don't handle it more than once. */ if (!udc_vbusf_time || ((jiffies - udc_vbusf_time) > 1)) { if (udc_device) { if (f1_status & VBUSMN) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: VBUSF VBUSMN set\n", udc_interrupts); } else { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: VBUSF VBUSMN reset\n", udc_interrupts); } } } udc_vbusf_time = jiffies; } else { udc_vbusf_time = 0; } if (f0_status & BRST) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: BRST bus reset\n", udc_interrupts); // reset fifo's and stall's or_b (EP3_CLEAR | EP1_CLEAR | EP2_CLEAR | EP0o_CLEAR | EP0i_CLEAR, USBFCLR); or_b (0, USBEPSTL); if (udc_device->device_state != DEVICE_RESET) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: DEVICE RESET\n", udc_interrupts); usbd_device_event_irq (udc_device, DEVICE_RESET, 0); } } if (f0_status & SETUP_TS) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: SETUP TS\n", udc_interrupts); or_b (EP0o_CLEAR | EP0i_CLEAR, USBFCLR); superh_ep0_setup (); if (udc_device->device_state == STATE_DEFAULT) { /* * superh won't give us set address, configuration or interface, but at least * we know that at this point we know that a host is talking to us, close * enough. */ usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED, 0); udc_device->configuration = 0; usbd_device_event (udc_device, DEVICE_CONFIGURED, 0); udc_device->interface = 1; // no options udc_device->alternate = 0; // no options usbd_device_event (udc_device, DEVICE_SET_INTERFACE, 0); } } if (f0_status & EP0i_TR) { usbd_tx_complete_irq (ep_endpoints[0], 0); superh_in_ep0 (ep_endpoints[0]); } if (f0_status & EP0o_TS) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: ep0o TS\n", udc_interrupts); } if (f0_status & EP0i_TS) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: ep0iTS\n", udc_interrupts); } if (f1_status & EP3_TR) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: EP3 TR\n", udc_interrupts);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -