Chinaunix首页 | 论坛 | 博客
  • 博客访问: 711412
  • 博文数量: 105
  • 博客积分: 3532
  • 博客等级: 中校
  • 技术积分: 1328
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-20 18:57
文章分类

全部博文(105)

文章存档

2015年(1)

2014年(1)

2013年(7)

2012年(14)

2011年(21)

2010年(61)

分类: 嵌入式

2011-02-28 00:28:21

硬件平台:S3C2440
KERNEL VERSION:LINUX-2.6.34
GCC:arm-none-eabi-gcc 4.4.1
这样写只是为了更好了解USB而已,便于裸机移植,LINUX系统并不建议使用这种形式
未完成,仅仅把USB设备中断挂上去了而已,待续...


#include "../ypublic.h"
#include <plat/regs-udc.h>
#include <plat/map.h>

#define GADGET_MAJOR    100
#define GADGET_DRIVER_NAME    "Mry gadget"

static const char ep0name [] = "ep0";

static const char *const ep_name[] = {
    ep0name, /* everyone has ep0 */
    /* s3c2410 four bidirectional bulk endpoints */
    "ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk",
};

#define S3C2410_ENDPOINTS ARRAY_SIZE(ep_name)

struct _t_usb_gadget;



typedef enum _t_ep0_state {
        EP0_IDLE,
        EP0_IN_DATA_PHASE,
        EP0_OUT_DATA_PHASE,
        EP0_END_XFER,
        EP0_STALL,
}t_ep0_state;

typedef enum _t_usb_device_speed {
    USB_SPEED_UNKNOWN = 0,            /* enumerating */
    USB_SPEED_LOW, USB_SPEED_FULL,        /* usb 1.1 */
    USB_SPEED_HIGH,                /* usb 2.0 */
    USB_SPEED_WIRELESS,            /* wireless (usb 2.5) */
    USB_SPEED_SUPER,            /* usb 3.0 */
}t_usb_device_speed;

/* USB_DT_ENDPOINT: Endpoint descriptor */
typedef struct _t_usb_endpoint_descriptor {
    __u8 bLength;
    __u8 bDescriptorType;

    __u8 bEndpointAddress;
    __u8 bmAttributes;
    __le16 wMaxPacketSize;
    __u8 bInterval;

    /* NOTE: these two are _only_ in audio endpoints. */
    /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
    __u8 bRefresh;
    __u8 bSynchAddress;
}t_usb_endpoint_descriptor,*t_usb_endpoint_descriptor_p;

/**
 * struct usb_request - describes one i/o request
 * @buf: Buffer used for data. Always provide this; some controllers
 *    only use PIO, or don't use DMA for some endpoints.
 * @dma: DMA address corresponding to 'buf'. If you don't set this
 *    field, and the usb controller needs one, it is responsible
 *    for mapping and unmapping the buffer.
 * @length: Length of that data
 * @no_interrupt: If true, hints that no completion irq is needed.
 *    Helpful sometimes with deep request queues that are handled
 *    directly by DMA controllers.
 * @zero: If true, when writing data, makes the last packet be "short"
 * by adding a zero length packet as needed;
 * @short_not_ok: When reading data, makes short packets be
 * treated as errors (queue stops advancing till cleanup).
 * @complete: Function called when request completes, so this request and
 *    its buffer may be re-used. The function will always be called with
 *    interrupts disabled, and it must not sleep.
 *    Reads terminate with a short packet, or when the buffer fills,
 *    whichever comes first. When writes terminate, some data bytes
 *    will usually still be in flight (often in a hardware fifo).
 *    Errors (for reads or writes) stop the queue from advancing
 *    until the completion function returns, so that any transfers
 *    invalidated by the error may first be dequeued.
 * @context: For use by the completion callback
 * @list: For use by the gadget driver.
 * @status: Reports completion code, zero or a negative errno.
 *    Normally, faults block the transfer queue from advancing until
 *    the completion callback returns.
 *    Code "-ESHUTDOWN" indicates completion caused by device disconnect,
 *    or when the driver disabled the endpoint.
 * @actual: Reports bytes transferred to/from the buffer. For reads (OUT
 *    transfers) this may be less than the requested length. If the
 *    short_not_ok flag is set, short reads are treated as errors
 *    even when status otherwise indicates successful completion.
 *    Note that for writes (IN transfers) some data bytes may still
 *    reside in a device-side FIFO when the request is reported as
 *    complete.
 *
 * These are allocated/freed through the endpoint they're used with. The
 * hardware's driver can add extra per-request data to the memory it returns,
 * which often avoids separate memory allocations (potential failures),
 * later when the request is queued.
 *
 * Request flags affect request handling, such as whether a zero length
 * packet is written (the "zero" flag), whether a short read should be
 * treated as an error (blocking request queue advance, the "short_not_ok"
 * flag), or hinting that an interrupt is not required (the "no_interrupt"
 * flag, for use with deep request queues).
 *
 * Bulk endpoints can use any size buffers, and can also be used for interrupt
 * transfers. interrupt-only endpoints can be much less functional.
 *
 * NOTE: this is analagous to 'struct urb' on the host side, except that
 * it's thinner and promotes more pre-allocation.
 */


typedef struct _t_usb_request {
    void            *buf;
    unsigned        length;
    dma_addr_t        dma;

    unsigned        no_interrupt:1;
    unsigned        zero:1;
    unsigned        short_not_ok:1;

    void            (*complete)(t_usb_ep_p ep,
                    struct usb_request *req);
    void            *context;
    struct list_head    list;

    int            status;
    unsigned        actual;

    struct list_head queue;
}t_usb_request ,*t_usb_request_p;



typedef struct _t_usb_ep_ops {
    int (*enable) (t_usb_ep ep,
        const t_usb_endpoint_descriptor_p desc);
    int (*disable) (t_usb_ep_p ep);

    t_usb_request_p (*alloc_request) (t_usb_ep_p ep,
        gfp_t gfp_flags);
    void (*free_request) (t_usb_ep_p ep, t_usb_request_p req);

    int (*queue) (t_usb_ep_p ep, t_usb_request_p req,
        gfp_t gfp_flags);
    int (*dequeue) (t_usb_ep_p ep, t_usb_request_p req);

    int (*set_halt) (t_usb_ep_p ep, int value);
    int (*set_wedge) (t_usb_ep_p ep);

    int (*fifo_status) (t_usb_ep_p ep);
    void (*fifo_flush) (t_usb_ep_p ep);
}t_usb_ep_ops,*t_usb_ep_ops_p;




typedef struct _t_usb_ep {
    void            *driver_data;

    const char        *name;
    const struct usb_ep_ops    *ops;
    struct list_head    ep_list;
    unsigned        maxpacket:16;

    const t_usb_endpoint_descriptor_p desc;
    u8                num;
    unsigned short            fifo_size;
    u8                bEndpointAddress;
    u8                bmAttributes;
    struct list_head    queue;
}t_usb_ep,*t_usb_ep_p;

typedef struct _t_usb_gadget {
    /* readonly to gadget driver */
    //const struct usb_gadget_ops    *ops;

    t_usb_ep_p        ep0;
    //struct list_head        ep_list;    /* of usb_ep */

    t_usb_device_speed        speed;
    unsigned            is_dualspeed:1;
    unsigned            is_otg:1;
    unsigned            is_a_peripheral:1;
    unsigned            b_hnp_enable:1;
    unsigned            a_hnp_support:1;
    unsigned            a_alt_hnp_support:1;
    const char            *name;
    //struct device            dev;

}t_usb_gadget,*t_usb_gadget_p;

typedef struct _t_usb_ctrlrequest {
    __u8 bRequestType;
    __u8 bRequest;
    __le16 wValue;
    __le16 wIndex;
    __le16 wLength;
}t_usb_ctrlrequest,*t_usb_ctrlrequest_p;


typedef struct _t_usb_gadget_driver {
    char            *function;
    t_usb_device_speed    speed;
    int            (*bind)(t_usb_gadget_p);
    void            (*unbind)(t_usb_gadget_p);
    int            (*setup)(t_usb_gadget_p,
                    const t_usb_ctrlrequest_p);
    void            (*disconnect)(t_usb_gadget_p);
    void            (*suspend)(t_usb_gadget_p);
    void            (*resume)(t_usb_gadget_p);

    /* FIXME support safe rmmod */
    //struct device_driver    driver;

}t_usb_gadget_driver,*t_usb_gadget_driver_p;


typedef struct _t_gadget_dev{
    struct cdev dev;
    int major;
    uchar    irq_state;
    spinlock_t lock;
    uchar    connected;
    t_usb_gadget gadget;
    t_usb_ep    ep[S3C2410_ENDPOINTS];
    uchar address;
    t_ep0_state ep0state;
    t_usb_gadget_driver_p driver;
}t_gadget_dev,*t_gadget_dev_p;

t_gadget_dev_p _gadget_dev_p;


static const struct file_operations _fops=
{
.owner=THIS_MODULE,
/*
.read=mry_read,
.write=mry_write,
.ioctl=mry_ioctl,
.open=mry_open,
.release=mry_release,
*/

};

static void __iomem        *_udc_base_addr_p;

static inline u32 y_udc_read(u32 reg)
{
    return readb(_udc_base_addr_p + reg);
}

static inline void y_udc_write(u32 value, u32 reg)
{
    writeb(value, _udc_base_addr_p + reg);
}

static inline void y_udc_writeb(void __iomem *base, u32 value, u32 reg)
{
    writeb(value, base + reg);
}

static inline int s3c2440_udc_write_packet(int fifo,
        t_usb_request_p req,
        unsigned max)
{
    unsigned len = min(req->length - req->actual, max);
    u8 *buf = req->buf + req->actual;

    prefetch(buf);

    printk(KERN_INFO "%s %d %d %d %d\n", __func__,
        req->actual, req->length, len, req->actual + len);

    req->actual += len;

    udelay(5);
    writesb(_udc_base_addr_p + fifo, buf, len);
    return len;
}

static inline void s3c2440_udc_set_ep0_de_in(void __iomem *base)
{
    y_udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
    y_udc_writeb(base, (S3C2410_UDC_EP0_CSR_IPKRDY
            | S3C2410_UDC_EP0_CSR_DE),
        S3C2410_UDC_EP0_CSR_REG);
}


static void s3c2440_udc_done(t_usb_ep_p ep,
        t_usb_request_p req, int status)
{
    unsigned halted = ep->halted;

    list_del_init(&req->queue);

    if (likely (req->status == -EINPROGRESS))
        req->status = status;
    else
        status = req->status;

    ep->halted = 1;
    req->complete(ep, req);
    ep->halted = halted;
}


static inline void s3c2440_udc_set_ep0_ipr(void __iomem *base)
{
    udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
    udc_writeb(base, S3C2410_UDC_EP0_CSR_IPKRDY, S3C2410_UDC_EP0_CSR_REG);
}



/*
 *    s3c2410_udc_write_fifo
 *
 * return: 0 = still running, 1 = completed, negative = errno
 */

static int s3c2440_udc_write_fifo(t_usb_ep_p ep,
        t_usb_request_p req)
{
    unsigned    count;
    int        is_last;
    u32        idx;
    int        fifo_reg;
    u32        ep_csr;

    idx = ep->bEndpointAddress & 0x7F;
    switch (idx) {
    default:
        idx = 0;
    case 0:
        fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
        break;
    case 1:
        fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
        break;
    case 2:
        fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
        break;
    case 3:
        fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
        break;
    case 4:
        fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
        break;
    }

    count = s3c2440_udc_write_packet(fifo_reg, req, ep->maxpacket);

    /* last packet is often short (sometimes a zlp) */
    if (count != ep->maxpacket)
        is_last = 1;
    else if (req->length != req->actual || req->zero)
        is_last = 0;
    else
        is_last = 2;

    /* Only ep0 debug messages are interesting */
    if (idx == 0)
        dprintk(DEBUG_NORMAL,
            "Written ep%d %d.%d of %d b [last %d,z %d]\n",
            idx, count, req->actual, req->length,
            is_last, req->zero);

    if (is_last) {
        /* The order is important. It prevents sending 2 packets
         * at the same time */


        if (idx == 0) {
            /* Reset signal => no need to say 'data sent' */
            if (! (y_udc_read(S3C2410_UDC_USB_INT_REG)
                    & S3C2410_UDC_USBINT_RESET))
                s3c2440_udc_set_ep0_de_in(base_addr);
            ep->dev->ep0state=EP0_IDLE;
        } else {
            y_udc_write(idx, S3C2410_UDC_INDEX_REG);
            ep_csr = y_udc_read(S3C2410_UDC_IN_CSR1_REG);
            y_udc_write(idx, S3C2410_UDC_INDEX_REG);
            y_udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
                    S3C2410_UDC_IN_CSR1_REG);
        }

        s3c2440_udc_done(ep, req, 0);
        is_last = 1;
    } else {
        if (idx == 0) {
            /* Reset signal => no need to say 'data sent' */
            if (! (y_udc_read(S3C2410_UDC_USB_INT_REG)
                    & S3C2410_UDC_USBINT_RESET))
                s3c2440_udc_set_ep0_ipr(base_addr);
        } else {
            y_udc_write(idx, S3C2410_UDC_INDEX_REG);
            ep_csr = y_udc_read(S3C2410_UDC_IN_CSR1_REG);
            y_udc_write(idx, S3C2410_UDC_INDEX_REG);
            y_udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
                    S3C2410_UDC_IN_CSR1_REG);
        }
    }

    return is_last;
}


/*
 *    handle_ep - Manage I/O endpoints
 */


static void s3c2440_udc_handle_ep(t_usb_ep_p ep)
{
    struct s3c2410_request    *req;
    int            is_in = ep->bEndpointAddress & USB_DIR_IN;
    u32            ep_csr1;
    u32            idx;

    if (likely (!list_empty(&ep->queue)))
        req = list_entry(ep->queue.next,
                t_usb_request, queue);
    else
        req = NULL;

    idx = ep->bEndpointAddress & 0x7F;

    if (is_in) {
        y_udc_write(idx, S3C2410_UDC_INDEX_REG);
        ep_csr1 = y_udc_read(S3C2410_UDC_IN_CSR1_REG);
        printk(KERN_INFO "ep%01d write csr:%02x %d\n",
            idx, ep_csr1, req ? 1 : 0);

        if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {
            printk(KERN_INFO "st\n");
            y_udc_write(idx, S3C2410_UDC_INDEX_REG);
            y_udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL,
                    S3C2410_UDC_IN_CSR1_REG);
            return;
        }

        if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) {
            s3c2440_udc_write_fifo(ep,req);
        }
    } else {
        y_udc_write(idx, S3C2410_UDC_INDEX_REG);
        ep_csr1 = y_udc_read(S3C2410_UDC_OUT_CSR1_REG);
        printk(KERN_INFO "ep%01d rd csr:%02x\n", idx, ep_csr1);

        if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {
            y_udc_write(idx, S3C2410_UDC_INDEX_REG);
            y_udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL,
                    S3C2410_UDC_OUT_CSR1_REG);
            return;
        }

        if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) {
            s3c2410_udc_read_fifo(ep,req);
        }
    }
}



static irqreturn_t gadget_on_irq(int irq, void *p)
{
    
    t_gadget_dev_p dev = p;
    int usb_status;
    int usbd_status;
    int pwr_reg;
    int ep0csr;
    int i;
    u32 idx;
    unsigned long flags;
    
    spin_lock_irqsave(&dev->lock, flags);

    /* Driver connected ? */
    if (!dev->driver) {
        /* Clear interrupts */
        y_udc_write(y_udc_read(S3C2410_UDC_USB_INT_REG),
                S3C2410_UDC_USB_INT_REG);
        y_udc_write(y_udc_read(S3C2410_UDC_EP_INT_REG),
                S3C2410_UDC_EP_INT_REG);
    }

    /* Save index */
    idx = y_udc_read(S3C2410_UDC_INDEX_REG);

    /* Read status registers */
    usb_status = y_udc_read(S3C2410_UDC_USB_INT_REG);
    usbd_status = y_udc_read(S3C2410_UDC_EP_INT_REG);
    pwr_reg = y_udc_read(S3C2410_UDC_PWR_REG);

    y_udc_writeb(_udc_base_addr_p, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
    ep0csr = y_udc_read(S3C2410_UDC_IN_CSR1_REG);

    printk(KERN_INFO"usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n",
        usb_status, usbd_status, pwr_reg, ep0csr);

    /*
     * Now, handle interrupts. There's two types :
     * - Reset, Resume, Suspend coming -> usb_int_reg
     * - EP -> ep_int_reg
     */


    /* RESET */
    if (usb_status & S3C2410_UDC_USBINT_RESET) {
        /* two kind of reset :
         * - reset start -> pwr reg = 8
         * - reset end -> pwr reg = 0
         **/

        printk(KERN_INFO "USB reset csr %x pwr %x\n",
            ep0csr, pwr_reg);

        dev->gadget.speed = USB_SPEED_UNKNOWN;
        y_udc_write(0x00, S3C2410_UDC_INDEX_REG);
        y_udc_write((dev->ep[0].maxpacket & 0x7ff) >> 3,
                S3C2410_UDC_MAXP_REG);
        dev->address = 0;

        dev->ep0state = EP0_IDLE;
        dev->gadget.speed = USB_SPEED_FULL;

        /* clear interrupt */
        y_udc_write(S3C2410_UDC_USBINT_RESET,
                S3C2410_UDC_USB_INT_REG);

        y_udc_write(idx, S3C2410_UDC_INDEX_REG);
        spin_unlock_irqrestore(&dev->lock, flags);
        return IRQ_HANDLED;
    }

    /* RESUME */
    if (usb_status & S3C2410_UDC_USBINT_RESUME) {
        printk(KERN_INFO"USB resume\n");

        /* clear interrupt */
        y_udc_write(S3C2410_UDC_USBINT_RESUME,
                S3C2410_UDC_USB_INT_REG);

        if (dev->gadget.speed != USB_SPEED_UNKNOWN
                && dev->driver
                && dev->driver->resume)
            dev->driver->resume(&dev->gadget);
    }

    /* SUSPEND */
    if (usb_status & S3C2410_UDC_USBINT_SUSPEND) {
        printk(KERN_INFO"USB suspend\n");

        /* clear interrupt */
        y_udc_write(S3C2410_UDC_USBINT_SUSPEND,
                S3C2410_UDC_USB_INT_REG);

        if (dev->gadget.speed != USB_SPEED_UNKNOWN
                && dev->driver
                && dev->driver->suspend)
            dev->driver->suspend(&dev->gadget);

        dev->ep0state = EP0_IDLE;
    }

    /* EP */
    /* control traffic */
    /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
     * generate an interrupt
     */

    if (usbd_status & S3C2410_UDC_INT_EP0) {
        printk(KERN_INFO"USB ep0 irq\n");
        /* Clear the interrupt bit by setting it to 1 */
        y_udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
        //s3c2410_udc_handle_ep0(dev);

    }

    /* endpoint data transfers */
    for (i = 1; i < S3C2410_ENDPOINTS; i++) {
        u32 tmp = 1 << i;
        if (usbd_status & tmp) {
            printk(KERN_INFO "USB ep%d irq\n", i);

            /* Clear the interrupt bit by setting it to 1 */
            y_udc_write(tmp, S3C2410_UDC_EP_INT_REG);
            s3c2440_udc_handle_ep(&dev->ep[i]);
        }
    }

    printk(KERN_INFO"irq: %d s3c2440_udc_done.\n", IRQ_USBD);

    /* Restore old index */
    y_udc_write(idx, S3C2410_UDC_INDEX_REG);

    spin_unlock_irqrestore(&dev->lock, flags);
    return IRQ_HANDLED;
}

static int gadget_init_do(t_gadget_dev_p pdev)
{
    int err;
    int rsrc_start = S3C2410_PA_USBDEV;
    int rsrc_len = S3C24XX_SZ_USBDEV;

    if (!request_mem_region(rsrc_start, rsrc_len, GADGET_DRIVER_NAME))
    {
        err=-EBUSY;
        goto err1;
    }

    _udc_base_addr_p = ioremap(rsrc_start, rsrc_len);
    if (!_udc_base_addr_p) {
        err = -ENOMEM;
        goto err2;
    }
    err=request_irq(IRQ_USBD,gadget_on_irq,IRQF_DISABLED,"gadget_irq",pdev);
    pdev->irq_state=err;
    if(err)
    {
        printk(KERN_ERR"usb irq busy");
        err=-EBUSY;
    }
    else printk(KERN_ERR"usb irq ok");
    spin_lock_init (&pdev->lock);
    return err;
err2:
    release_mem_region(rsrc_start,rsrc_len);
err1:
    printk(KERN_ERR"error: fuction:gadget_init_do id:%d",err);
    return err;
}

static int gadget_setup_cdev(t_gadget_dev_p pdev,int index)
{
    int err,devno=MKDEV(pdev->major,index);
    cdev_init(&pdev->dev,&_fops);
    pdev->dev.owner=THIS_MODULE;
    pdev->dev.ops=&_fops;
    err=cdev_add(&pdev->dev,devno,1);
    return err;
}

static int gadget_init(void)
{
    int rs;
    int mry_major=GADGET_MAJOR;
    dev_t devno=MKDEV(mry_major,0);
    
    if(mry_major)
        rs=register_chrdev_region(devno,1,GADGET_DRIVER_NAME);
    else
    {
        rs=alloc_chrdev_region(&devno,0,1,GADGET_DRIVER_NAME);
        mry_major=MAJOR(devno);
    }
    
    if(rs<0){
        goto err1;
    }
    _gadget_dev_p=kzalloc(sizeof(t_gadget_dev),GFP_KERNEL);
    
    if(!_gadget_dev_p)
    {
        rs=-ENOMEM;
        goto err2;
    }
    //memset(_gadget_dev_p,0,sizeof(t_gadget_dev));

    _gadget_dev_p->major=mry_major;
    rs=gadget_setup_cdev(_gadget_dev_p,0);
    if(rs)
    {
        goto err3;
    }
    printk(KERN_INFO "gadget driver ok");
    gadget_init_do(_gadget_dev_p);
    return 0;
err3:
err2:
    unregister_chrdev_region(devno,1);
err1:
    printk(KERN_ERR "gadget driver failed,ERROR:%d",rs);
    return rs;
}

static void gadget_exit(void)
{
    int mry_major=_gadget_dev_p->major;
    if(_udc_base_addr_p)
    {
        yiounmap(S3C2410_PA_USBDEV,_udc_base_addr_p,S3C24XX_SZ_USBDEV);
    }
    if(!_gadget_dev_p->irq_state)
        free_irq(IRQ_USBD,_gadget_dev_p);
    cdev_del(&_gadget_dev_p->dev);
    kfree(_gadget_dev_p);
    unregister_chrdev_region(MKDEV(mry_major,0),1);
    printk(KERN_INFO "exit gadget");
}

module_init(gadget_init);
module_exit(gadget_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("MrY");


阅读(1762) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~