📄 usb_pdc.c
字号:
/*************************************************************
* Philips USB peripheral controller driver
*
* (c) 2002 Koninklijke Philips Electronics N.V., All rights reserved
*
* This source code and any compilation or derivative thereof is the
* proprietary information of Koninklijke Philips Electronics N.V.
* and is confidential in nature.
* Under no circumstances is this software to be exposed to or placed
* under an Open Source License of any type without the expressed
* written permission of Koninklijke Philips Electronics N.V.
*
* File Name: usb_pdc.c
*
* History:
*
* Version Date Author Comments
* -------------------------------------------------
* 1.0 09/23/02 SBANSAL Initial Creation
*
* Note: use tab space 4
*************************************************************/
/* Philips Peripheral Controller Driver
*
* The mapping of the endpoint is simple: it basically behaves as a device,
* which can be opened, closed, and have reads and writes performed
* on it. Due to the high bandwidth of the USB (12Mbit) we maintain local
* buffers to ensure that we don't get starved of data during transmissions -
* and have a receive buffer to allow the process dealing with USB to read in
* bigger blocks than the packet size.
*
* The implementation is designed to be pretty transparent: this is for a
* number of reasons, this is same protocol over both the USB and the serial
* ports on the customers unit.Implementing the endpoint as a simple 'open/close' device
* as opposed to a more complex network-style interface also means that we can
* do froody stuff like run PPP over a 12Mbit usb link (host permitting, of
* course...). To this end, there is limited control over the way the USB
* device works - endpoint 0 is handled totally under software control, and
* only a limited number of events are passed though for the user-side task
* to worry about (like connection/disconnection of the USB cable).
*
*/
#include <linux/config.h>
#define MODULE
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/tqueue.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <linux/proc_fs.h>
#include <linux/poll.h>
#include "pdc_intf.h"
#include "usb_pdc.h"
#define DRIVER_AUTHOR "Philips Semiconductors"
#define DRIVER_DESC "ISP1362 Peripheral Controller Driver"
#define PDC_DRIVER_VERSION "1.0"
#define DRIVER_NAME "usb-pdc"
/*--------------------------------------------------------------*
* external functions
*--------------------------------------------------------------*/
extern int pdc_bus_init(struct pdc_dev *pdc);
extern void pdc_bus_deinit(void);
/*--------------------------------------------------------------*
* Device Controller endpoint related functions
*--------------------------------------------------------------*/
static void writeendpoint(int endpoint, unsigned char *buffer, int length);
static __inline__ int checkendpoint(int endpoint);
static void pdc_configure_eps(struct pdc_dev *pdc);
static int pdc_submit_control_urb(struct pdc_dev *pdc, struct pdc_urb *urb_req);
/*--------------------------------------------------------------*
* Interrupt Service Routine related functions
*--------------------------------------------------------------*/
void pdc_isr(struct isp1362_dev *dev, void *isr_data);
static void tx_data(struct pdc_dev *pdc, __u8 ep, int kick);
static void rx_data(struct pdc_dev *pdc, __u8 ep);
static void rx_command(struct pdc_dev *pdc);
static void tx_command(struct pdc_dev *pdc);
/*--------------------------------------------------------------*
* initialization function declerations
*--------------------------------------------------------------*/
static int __init pdc_module_init (void);
static void __exit pdc_module_cleanup (void);
static int pdc_probe (struct isp1362_dev *dev);
static void pdc_remove (struct isp1362_dev *dev);
static void pdc_init(struct isp1362_dev *dev);
static void pdc_connect(void);
/*--------------------------------------------------------------*
* local variable definitions
*--------------------------------------------------------------*/
struct pdc_dev usb_devices[1]; /* local interface */
static struct isp1362_dev *isp1362_dc_dev; /* HOSAL interface */
static spinlock_t pdc_rdwr_lock = SPIN_LOCK_UNLOCKED;
struct pdc_pipe pdc_eps[PDC_MAX_PIPES];
struct pdc_ep_desc pdc_ctrl_ep_desc[2]; /* Control IN and Control OUT */
/*--------------------------------------------------------------*
* Device Controller register access functions
*--------------------------------------------------------------*/
static __inline__ void pdc_command(__u16 cmd)
{
isp1362_command(cmd,isp1362_dc_dev);
isp1362_udelay(1);
}
static __inline__ __u16 pdc_cread(void)
{
return isp1362_read16(isp1362_dc_dev);
}
static __inline__ void pdc_cwrite(__u16 data)
{
isp1362_write16(data,isp1362_dc_dev);
return;
}
static __inline__ __u16 pdc_read16(__u16 reg)
{
__u16 data;
isp1362_reg_read16(isp1362_dc_dev, reg, data);
return data;
}
static __inline__ void pdc_write16(__u16 reg, __u16 data)
{
isp1362_reg_write16(isp1362_dc_dev, reg, data);
}
static __inline__ __u32 pdc_read32(__u16 reg)
{
__u32 data;
isp1362_reg_read32(isp1362_dc_dev, reg, data);
return data;
}
static __inline__ void pdc_write32(__u16 reg, __u32 data)
{
isp1362_reg_write32(isp1362_dc_dev, reg, data);
return;
}
#define pdc_set_mps_reg_value(mps,reg_mps,iso) \
if(iso){ \
if(mps <= 16) {reg_mps = EPCONFIG_ISO_FIFO16; mps =16;} \
else if(mps <= 32) {reg_mps = EPCONFIG_ISO_FIFO32; mps = 32;} \
else if(mps <= 48) {reg_mps = EPCONFIG_ISO_FIFO48; mps = 48;} \
else if(mps <= 64) {reg_mps = EPCONFIG_ISO_FIFO64; mps = 64;} \
else if(mps <= 96) {reg_mps = EPCONFIG_ISO_FIFO96; mps = 96;} \
else if(mps <= 128) {reg_mps = EPCONFIG_ISO_FIFO128; mps = 128;}\
else if(mps <= 160) {reg_mps = EPCONFIG_ISO_FIFO160; mps = 160;}\
else if(mps <= 192) {reg_mps = EPCONFIG_ISO_FIFO192; mps = 192;}\
else if(mps <= 256) {reg_mps = EPCONFIG_ISO_FIFO256; mps = 256;}\
else if(mps <= 320) {reg_mps = EPCONFIG_ISO_FIFO320; mps = 320;}\
else if(mps <= 384) {reg_mps = EPCONFIG_ISO_FIFO384; mps = 384;}\
else if(mps <= 512) {reg_mps = EPCONFIG_ISO_FIFO512; mps = 512;}\
else if(mps <= 640) {reg_mps = EPCONFIG_ISO_FIFO640; mps = 640;}\
else if(mps <= 768) {reg_mps = EPCONFIG_ISO_FIFO768; mps = 768;}\
else if(mps <= 896) {reg_mps = EPCONFIG_ISO_FIFO896; mps = 896;}\
else {reg_mps = EPCONFIG_ISO_FIFO1023; mps = 1023;} \
} else { \
if(mps <= 8) {reg_mps = EPCONFIG_FIFO8; mps = 8;} \
else if(mps <= 16) {reg_mps = EPCONFIG_FIFO16; mps = 16;} \
else if(mps <= 32) {reg_mps = EPCONFIG_FIFO32; mps = 32;} \
else {reg_mps = EPCONFIG_FIFO64; mps = 64;} \
}
/*--------------------------------------------------------------*
* Device Controller endpoint related functions
*--------------------------------------------------------------*/
/* Check to see if endpoint is full */
static __inline__ int checkendpoint(int endpoint)
{
/* Possible CMD_CHECKEPSTATUS? */
return(pdc_read16(CMD_READEPSTATUS+endpoint)&
(STATUS_EPFULL0|STATUS_EPFULL1));
}
/* Read a packet out of a FIFO */
static int readendpoint(int endpoint, unsigned char *buffer, int length)
{
/* Any data? */
if (!checkendpoint(endpoint)) return(0);
isp1362_buff_read(isp1362_dc_dev, (CMD_READEP+endpoint), buffer, length);
/* Now we've read it, clear the buffer */
pdc_command(CMD_CLEAREP+endpoint);
/* Return bytes read */
return length;
}
/* Write a packet into the fifo */
static void writeendpoint(int endpoint, unsigned char *buffer, int length)
{
isp1362_buff_write(isp1362_dc_dev, (CMD_WRITEEP+endpoint), buffer, length);
/* Validate the buffer so the chip will send it */
pdc_command(CMD_VALIDATEEP+endpoint);
}
/*
* Configure the Endpoints in the Hardware for those
* pipes that are configured. For others Just diables
* them. This function also adjusts the Endpoint max packet size to the
* neareds available max packet size of the hardware.
*/
void pdc_configure_eps(struct pdc_dev *pdc)
{
__u16 ep_config = 0;
__u8 handle;
struct pdc_pipe *pipe;
__u8 mps;
__u32 int_en;
func_debug(("pdc_configure_eps(pdc=%p)\n",pdc))
int_en = pdc_read32(CMD_READIRQENABLE);
/* Configure all the Pipes (endpoint registers) in the device */
for(handle=0;handle<PDC_MAX_PIPES;handle++) {
pipe = pdc->ep_pipes+handle; /* Get the pipe data structure */
ep_config = 0;
if(pipe->ep_state != PDC_PIPE_UNCONFIG) {
/* pipe is configured */
/* MPS, DBL BUFF, DIR, enable,disable */
/* Use double buffering for non control pipes */
ep_config |= EPCONFIG_FIFOEN;
if(pipe->ep_desc->ep_dir) ep_config |= EPCONFIG_EPDIR;
if(handle > EP0IN) ep_config |= EPCONFIG_DBLBUF;
/*
* Set the maximum pkt size and get the device configuration
* maximum packet size
*/
if(pipe->ep_desc->attributes != PDC_EP_ISOCHRONOUS) {
pdc_set_mps_reg_value((pipe->ep_desc->max_pkt_size), mps, 0);
} else {
pdc_set_mps_reg_value((pipe->ep_desc->max_pkt_size), mps, 1);
}
ep_config |= mps;
int_en |= (IE_EP0OUT << handle);
}
/* Configure the end point register */
pdc_write16(CMD_WRITEEPCONFIG+handle, ep_config);
}
pdc_write32(CMD_WRITEIRQENABLE,int_en);
} /* End of pdc_configure_eps() */
void pdc_dev_control(unsigned long opr)
{
__u32 data;
__u16 data_u16;
struct pdc_dev *pdc = usb_devices;
switch(opr) {
case PDC_ENABLE:
data = pdc_read32(CMD_READIRQ);
/* Bus is already in suspend state before enabling the DC
* If the interrupt is already cleared, then we never get any
* suspend interrupt later. So inform upper layers that the
* bus is suspended */
if((data & BUSTATUS) && pdc->pdc_bus && pdc->pdc_bus->notif)
pdc->pdc_bus->notif(pdc->pdc_bus->context, PDC_BUSTATUS);
/* Set device address & enable */
pdc_write16(CMD_WRITEADDRESS,ADDRESS_DEVEN|0);
/* Enable interrupts */
pdc_write32(CMD_WRITEIRQENABLE,IE_EP0OUT|IE_EP0IN|IE_SUSP|IE_RST|IE_RESM);
/* Connect to the bus */
pdc_write16(CMD_WRITEMODE,MODE_SOFTCT|MODE_INTENA);
break;
case PDC_DISABLE:
/* Go off bus */
pdc_write16(CMD_WRITEADDRESS,0);
/* Turn off IRQs */
pdc_write32(CMD_WRITEIRQENABLE,0);
/* Global IRQ disable & turn off softconnect */
#if 0
data_u16 = pdc_read16(CMD_READMODE);
data_u16 &= (~MODE_INTENA);
pdc_write16(CMD_WRITEMODE,data_u16);
#else
pdc_write16(CMD_WRITEMODE,0);
#endif
break;
case PDC_CONNECT:
/* Connect it to the USB bus */
data_u16 = pdc_read16(CMD_READMODE);
data_u16 |= MODE_SOFTCT;
pdc_write16(CMD_WRITEMODE, data_u16);
break;
case PDC_DISCONNECT:
/* Disconnect it from the USB bus */
data_u16 = pdc_read16(CMD_READMODE);
data_u16 &= (~MODE_SOFTCT);
pdc_write16(CMD_WRITEMODE, data_u16);
break;
}
return;
} /* End of pdc_dev_control() */
/*
* Deal with the received data on the USB pipe
* @pdc: pdc device data structure
* @handle: USB pipe handle
*/
void rx_data(struct pdc_dev *pdc, pdc_pipe_handle_t handle)
{
struct pdc_pipe *pipe = pdc->ep_pipes + handle;
struct pdc_urb *urb = pipe->urb;
__u16 status;
__u8 fifos = 0;
__u32 bytes, rcvd_bytes;
func_debug(("rx_data(pdc=%p,handle=%x)\n",pdc,handle))
if(in_interrupt()) {
status = pdc_read16(CMD_READEPSTATUS+handle);
pipe->ep_status = status;
} else {
status = pipe->ep_status;
}
if((status & STATUS_EPFULL) == STATUS_EPFULL) {
fifos = 2;
} else if(status&STATUS_EPFULL) {
fifos = 1;
}
if (!(status & STATUS_EPFULL) || (status & STATUS_EPSTAL)) {
return ;
}
while((fifos--) && urb) {
/* How many bytes do we need? */
bytes = urb->transfer_buffer_length - urb->actual_length;
if(bytes > pipe->ep_desc->max_pkt_size) bytes = pipe->ep_desc->max_pkt_size;
rcvd_bytes = readendpoint(handle,&(((__u8*)urb->transfer_buffer)[urb->actual_length]),bytes);
urb->actual_length += rcvd_bytes;
/* Clear the full bits */
if(pipe->ep_status & STATUS_EPFULL0) {
pipe->ep_status &= ~(STATUS_EPFULL0);
} else if(pipe->ep_status & STATUS_EPFULL1) {
pipe->ep_status &= ~(STATUS_EPFULL1);
}
/* Did we receive a short packet?? */
if(rcvd_bytes < bytes) {
urb->status = PDC_SHORT_PACKET;
detail_debug(("Short packet\n"))
}
/* Complete the urb if all the bytes are received from host or terminated
* because of short packet
*/
if(urb->transfer_buffer_length == urb->actual_length|| urb->status == PDC_SHORT_PACKET) {
pipe->urb = urb->next;
if(urb->status == PDC_URB_PENDING) {
urb->status = PDC_URB_COMPLETE;
}
if(urb->complete) urb->complete(urb);
urb = pipe->urb;
}
} //while(fifo--)
} /* End of rx_data() */
/*
* Deal with the transmit data on a USB pipe
* @pdc : pdc device data structure
* @pipe : USB pipe
*/
void tx_data(struct pdc_dev *pdc, pdc_pipe_handle_t handle, int kick)
{
int txstat,tofill=1;
struct pdc_pipe *pipe = pdc->ep_pipes + handle;
struct pdc_urb *ep_urb = pipe->urb;
int usb_txsize;
func_debug(("tx_data(handle=%x, kick=%x)\n", handle, kick))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -