作為工程師,通常我們在開發板上開發usb device驅動比較多。比如說我們開發板通過usb連接一個usb攝像頭。在這種情況下,我們的開發板中有一個usb host controller,簡稱UHC。開發板充當了usb host角色。這個時候我們在開發板中開發的usb 攝像頭等usb設備驅動,稱之為usb device驅動。
那么有沒有可能使得我們的開發板作為一個usb設備呢?比如在實際應用場景中,開發板A沒有網卡,開發板B有網卡。開發板A想上網,只能通過usb連接開發板B,讓開發板B共享自己的網卡資源.這個時候開發板A是host角色。開發板B是device角色,它里面有usb device controller,簡稱UDC。這個時候很明顯我們需要在開發板B開發一個驅動,使得它自己可以作為一個usb網卡設備被A識別并使用。那么,開發板B中的驅動,我們稱之為usb gadget驅動。常見的usb gadget驅動有RNDIS(usb 網卡),file_storage(比如安卓手機插到電腦可以當u盤),adb(開發過安卓的應該都知道是啥)
相信大家已經明白了usb device驅動及usb gadget驅動的區別了。那么,我們平時把手機通過usb線接到電腦上面當u盤使用,我們的手機中需要usb gadget驅動還是usb device驅動呢?歡迎大家將你們的答案寫在留言區。
/* USB_DT_ENDPOINT: Endpoint descriptor */
struct 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;
} __attribute__ ((packed));
#define USB_DT_ENDPOINT_SIZE 7
struct usb_ep {
void *driver_data;
const char *name;
const struct usb_ep_ops *ops;
struct list_head ep_list;
struct usb_ep_caps caps;
bool claimed;
bool enabled;
unsigned maxpacket:16;
unsigned maxpacket_limit:16;
unsigned max_streams:16;
unsigned mult:2;
unsigned maxburst:5;
u8 address;
const struct usb_endpoint_descriptor *desc;
const struct usb_ss_ep_comp_descriptor *comp_desc;
};
struct s3c2410_ep {
struct list_head queue;
unsigned long last_io; /* jiffies timestamp */
struct usb_gadget *gadget;
struct s3c2410_udc *dev;
struct usb_ep ep;
u8 num;
unsigned short fifo_size;
u8 bEndpointAddress;
u8 bmAttributes;
unsigned halted : 1;
unsigned already_seen : 1;
unsigned setup_stage : 1;
};
static const struct usb_ep_ops s3c2410_ep_ops={
.enable=s3c2410_udc_ep_enable,
.disable=s3c2410_udc_ep_disable,
};
*
* s3c2410_udc_ep_enable
*/
static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct s3c2410_udc *dev;
struct s3c2410_ep *ep;
u32 max, tmp;
unsigned long flags;
u32 csr1, csr2;
u32 int_en_reg;
ep=to_s3c2410_ep(_ep);
if (!_ep || !desc
|| _ep->name==ep0name
|| desc->bDescriptorType !=USB_DT_ENDPOINT)
return -EINVAL;
dev=ep->dev;
if (!dev->driver || dev->gadget.speed==USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
max=usb_endpoint_maxp(desc) & 0x1fff; ////根據endpoint 描述符設置 udc reg
local_irq_save(flags);
_ep->maxpacket=max & 0x7ff;
ep->ep.desc=desc;
ep->halted=0;
ep->bEndpointAddress=desc->bEndpointAddress;
/* set max packet */
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(max >> 3, S3C2410_UDC_MAXP_REG);
/* set type, direction, address; reset fifo counters */
if (desc->bEndpointAddress & USB_DIR_IN) {
csr1=S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
csr2=S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
} else {
/* don't flush in fifo or it will cause endpoint interrupt */
csr1=S3C2410_UDC_ICSR1_CLRDT;
csr2=S3C2410_UDC_ICSR2_DMAIEN;
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
csr1=S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
csr2=S3C2410_UDC_OCSR2_DMAIEN;
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr1, S3C2410_UDC_OUT_CSR1_REG);
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr2, S3C2410_UDC_OUT_CSR2_REG);
}
/* enable irqs */
int_en_reg=udc_read(S3C2410_UDC_EP_INT_EN_REG);
udc_write(int_en_reg | (1 << ep->num), S3C2410_UDC_EP_INT_EN_REG);
/* print some debug message */
tmp=desc->bEndpointAddress;
dprintk(DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
_ep->name, ep->num, tmp,
desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
local_irq_restore(flags);
s3c2410_udc_set_halt(_ep, 0);
return 0;
}
/*
* s3c2410_udc_ep_disable
*/
static int s3c2410_udc_ep_disable(struct usb_ep *_ep)
{
struct s3c2410_ep *ep=to_s3c2410_ep(_ep);
unsigned long flags;
u32 int_en_reg;
if (!_ep || !ep->ep.desc) {
dprintk(DEBUG_NORMAL, "%s not enabled\n",
_ep ? ep->ep.name : NULL);
return -EINVAL;
}
local_irq_save(flags);
dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name);
ep->ep.desc=NULL;
ep->halted=1;
s3c2410_udc_nuke(ep->dev, ep, -ESHUTDOWN);
/* disable irqs */
int_en_reg=udc_read(S3C2410_UDC_EP_INT_EN_REG);
udc_write(int_en_reg & ~(1<<ep->num), S3C2410_UDC_EP_INT_EN_REG);
local_irq_restore(flags);
dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);
return 0;
}