📄 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){ isp1362_command(reg,isp1362_dc_dev); return isp1362_read16(isp1362_dc_dev);}static __inline__ void pdc_write16(__u16 reg, __u16 data){ isp1362_command(reg,isp1362_dc_dev); isp1362_write16(data,isp1362_dc_dev);}static __inline__ __u32 pdc_read32(__u16 reg){ __u32 data; __u16 w_data; isp1362_command(reg, isp1362_dc_dev); w_data = isp1362_read16(isp1362_dc_dev); data = w_data & 0x0000FFFF; w_data = isp1362_read16(isp1362_dc_dev); data |= (w_data & 0x0000FFFF) << 16; return data;}static __inline__ void pdc_write32(__u16 reg, __u32 data){ __u16 w_data; isp1362_command(reg, isp1362_dc_dev); w_data = data & 0x0000FFFF; isp1362_write16(w_data,isp1362_dc_dev); w_data = (data & 0xFFFF0000) >> 16; isp1362_write16(w_data,isp1362_dc_dev);}#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){ int a,c; /* Any data? */ if (!checkendpoint(endpoint)) return(0); /* Read it from the fifo */ pdc_command(CMD_READEP+endpoint); /* Fetch length */ c=pdc_cread(); /* Trim length & read packet */ if (c>length) c=length; a=c; while(a>0) { unsigned short w=pdc_cread(); *buffer++=w; if (a!=1) *buffer++=(w>>8); a-=2; } /* Now we've read it, clear the buffer */ pdc_command(CMD_CLEAREP+endpoint); /* Return bytes read */ return(c);}/* Write a packet into the fifo */static void writeendpoint(int endpoint, unsigned char *buffer, int length){ int a; /* Select the endpoint */ pdc_command(CMD_WRITEEP+endpoint); /* Write the data */ pdc_cwrite(length); a=length; while(a>0) { unsigned short w=*buffer++; if (a!=1) w|=(*buffer++)<<8; pdc_cwrite(w); a-=2; } /* 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; switch(opr) { case PDC_ENABLE: /* Set device address & enable */ pdc_write16(CMD_WRITEADDRESS,ADDRESS_DEVEN|0); data = pdc_read32(CMD_READIRQ); /* 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 */ pdc_write16(CMD_WRITEMODE,0); 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() *//*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -