mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 11:41:38 -04:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (229 commits) USB: remove unused usb_buffer_alloc and usb_buffer_free macros usb: musb: update gfp/slab.h includes USB: ftdi_sio: fix legacy SIO-device header USB: kl5usb105: reimplement using generic framework USB: kl5usb105: minor clean ups USB: kl5usb105: fix memory leak USB: io_ti: use kfifo to implement write buffering USB: io_ti: remove unsused private counter USB: ti_usb: use kfifo to implement write buffering USB: ir-usb: fix incorrect write-buffer length USB: aircable: fix incorrect write-buffer length USB: safe_serial: straighten out read processing USB: safe_serial: reimplement read using generic framework USB: safe_serial: reimplement write using generic framework usb-storage: always print quirks USB: usb-storage: trivial debug improvements USB: oti6858: use port write fifo USB: oti6858: use kfifo to implement write buffering USB: cypress_m8: use kfifo to implement write buffering USB: cypress_m8: remove unused drain define ... Fix up conflicts (due to usb_buffer_alloc/free renaming) in drivers/input/tablet/acecad.c drivers/input/tablet/kbtab.c drivers/input/tablet/wacom_sys.c drivers/media/video/gspca/gspca.c sound/usb/usbaudio.c
This commit is contained in:
commit
7a9b149212
261 changed files with 17242 additions and 8360 deletions
|
|
@ -94,19 +94,19 @@
|
|||
} while (0)
|
||||
|
||||
#define uea_enters(usb_dev) \
|
||||
uea_vdbg(usb_dev, "entering %s\n", __func__)
|
||||
uea_vdbg(usb_dev, "entering %s\n" , __func__)
|
||||
|
||||
#define uea_leaves(usb_dev) \
|
||||
uea_vdbg(usb_dev, "leaving %s\n", __func__)
|
||||
uea_vdbg(usb_dev, "leaving %s\n" , __func__)
|
||||
|
||||
#define uea_err(usb_dev, format,args...) \
|
||||
dev_err(&(usb_dev)->dev ,"[UEAGLE-ATM] " format , ##args)
|
||||
#define uea_err(usb_dev, format, args...) \
|
||||
dev_err(&(usb_dev)->dev , "[UEAGLE-ATM] " format , ##args)
|
||||
|
||||
#define uea_warn(usb_dev, format,args...) \
|
||||
dev_warn(&(usb_dev)->dev ,"[Ueagle-atm] " format, ##args)
|
||||
#define uea_warn(usb_dev, format, args...) \
|
||||
dev_warn(&(usb_dev)->dev , "[Ueagle-atm] " format, ##args)
|
||||
|
||||
#define uea_info(usb_dev, format,args...) \
|
||||
dev_info(&(usb_dev)->dev ,"[ueagle-atm] " format, ##args)
|
||||
#define uea_info(usb_dev, format, args...) \
|
||||
dev_info(&(usb_dev)->dev , "[ueagle-atm] " format, ##args)
|
||||
|
||||
struct intr_pkt;
|
||||
|
||||
|
|
@ -289,7 +289,7 @@ enum {
|
|||
#define IS_ISDN(x) \
|
||||
((x)->annex & ANNEXB)
|
||||
|
||||
#define INS_TO_USBDEV(ins) ins->usb_dev
|
||||
#define INS_TO_USBDEV(ins) (ins->usb_dev)
|
||||
|
||||
#define GET_STATUS(data) \
|
||||
((data >> 8) & 0xf)
|
||||
|
|
@ -304,7 +304,7 @@ enum {
|
|||
* The FW_GET_BYTE() macro is provided only for consistency.
|
||||
*/
|
||||
|
||||
#define FW_GET_BYTE(p) *((__u8 *) (p))
|
||||
#define FW_GET_BYTE(p) (*((__u8 *) (p)))
|
||||
|
||||
#define FW_DIR "ueagle-atm/"
|
||||
#define UEA_FW_NAME_MAX 30
|
||||
|
|
@ -315,7 +315,7 @@ enum {
|
|||
|
||||
#define ACK_TIMEOUT msecs_to_jiffies(3000)
|
||||
|
||||
#define UEA_INTR_IFACE_NO 0
|
||||
#define UEA_INTR_IFACE_NO 0
|
||||
#define UEA_US_IFACE_NO 1
|
||||
#define UEA_DS_IFACE_NO 2
|
||||
|
||||
|
|
@ -326,9 +326,9 @@ enum {
|
|||
#define UEA_INTR_PIPE 0x04
|
||||
#define UEA_ISO_DATA_PIPE 0x08
|
||||
|
||||
#define UEA_E1_SET_BLOCK 0x0001
|
||||
#define UEA_E1_SET_BLOCK 0x0001
|
||||
#define UEA_E4_SET_BLOCK 0x002c
|
||||
#define UEA_SET_MODE 0x0003
|
||||
#define UEA_SET_MODE 0x0003
|
||||
#define UEA_SET_2183_DATA 0x0004
|
||||
#define UEA_SET_TIMEOUT 0x0011
|
||||
|
||||
|
|
@ -366,7 +366,7 @@ struct l1_code {
|
|||
u8 string_header[E4_L1_STRING_HEADER];
|
||||
u8 page_number_to_block_index[E4_MAX_PAGE_NUMBER];
|
||||
struct block_index page_header[E4_NO_SWAPPAGE_HEADERS];
|
||||
u8 code [0];
|
||||
u8 code[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* structures describing a block within a DSP page */
|
||||
|
|
@ -428,7 +428,8 @@ struct block_info_e4 {
|
|||
#define E4_MODEMREADY 0x1
|
||||
|
||||
#define E1_MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf))
|
||||
#define E4_MAKEFUNCTION(t, st, s) (((t) & 0xf) << 8 | ((st) & 0xf) << 4 | ((s) & 0xf))
|
||||
#define E4_MAKEFUNCTION(t, st, s) (((t) & 0xf) << 8 | \
|
||||
((st) & 0xf) << 4 | ((s) & 0xf))
|
||||
|
||||
#define E1_MAKESA(a, b, c, d) \
|
||||
(((c) & 0xff) << 24 | \
|
||||
|
|
@ -473,7 +474,7 @@ struct cmv_e4 {
|
|||
__be16 wFunction;
|
||||
__be16 wOffset;
|
||||
__be16 wAddress;
|
||||
__be32 dwData [6];
|
||||
__be32 dwData[6];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* structures representing swap information */
|
||||
|
|
@ -534,11 +535,13 @@ struct intr_pkt {
|
|||
|
||||
static struct usb_driver uea_driver;
|
||||
static DEFINE_MUTEX(uea_mutex);
|
||||
static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III", "Eagle IV"};
|
||||
static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III",
|
||||
"Eagle IV"};
|
||||
|
||||
static int modem_index;
|
||||
static unsigned int debug;
|
||||
static unsigned int altsetting[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = FASTEST_ISO_INTF};
|
||||
static unsigned int altsetting[NB_MODEM] = {
|
||||
[0 ... (NB_MODEM - 1)] = FASTEST_ISO_INTF};
|
||||
static int sync_wait[NB_MODEM];
|
||||
static char *cmv_file[NB_MODEM];
|
||||
static int annex[NB_MODEM];
|
||||
|
|
@ -555,7 +558,7 @@ MODULE_PARM_DESC(cmv_file,
|
|||
"file name with configuration and management variables");
|
||||
module_param_array(annex, uint, NULL, 0644);
|
||||
MODULE_PARM_DESC(annex,
|
||||
"manually set annex a/b (0=auto, 1=annex a, 2=annex b)");
|
||||
"manually set annex a/b (0=auto, 1=annex a, 2=annex b)");
|
||||
|
||||
#define uea_wait(sc, cond, timeo) \
|
||||
({ \
|
||||
|
|
@ -602,7 +605,8 @@ static int uea_send_modem_cmd(struct usb_device *usb,
|
|||
return (ret == size) ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *context)
|
||||
static void uea_upload_pre_firmware(const struct firmware *fw_entry,
|
||||
void *context)
|
||||
{
|
||||
struct usb_device *usb = context;
|
||||
const u8 *pfw;
|
||||
|
|
@ -707,7 +711,8 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
|
|||
}
|
||||
|
||||
ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev,
|
||||
GFP_KERNEL, usb, uea_upload_pre_firmware);
|
||||
GFP_KERNEL, usb,
|
||||
uea_upload_pre_firmware);
|
||||
if (ret)
|
||||
uea_err(usb, "firmware %s is not available\n", fw_name);
|
||||
else
|
||||
|
|
@ -876,7 +881,7 @@ static int request_dsp(struct uea_softc *sc)
|
|||
if (ret < 0) {
|
||||
uea_err(INS_TO_USBDEV(sc),
|
||||
"requesting firmware %s failed with error %d\n",
|
||||
dsp_name, ret);
|
||||
dsp_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -994,14 +999,17 @@ static void __uea_load_page_e4(struct uea_softc *sc, u8 pageno, int boot)
|
|||
|
||||
blockidx = &p->page_header[blockno];
|
||||
blocksize = E4_PAGE_BYTES(blockidx->PageSize);
|
||||
blockoffset = sc->dsp_firm->data + le32_to_cpu(blockidx->PageOffset);
|
||||
blockoffset = sc->dsp_firm->data + le32_to_cpu(
|
||||
blockidx->PageOffset);
|
||||
|
||||
bi.dwSize = cpu_to_be32(blocksize);
|
||||
bi.dwAddress = cpu_to_be32(le32_to_cpu(blockidx->PageAddress));
|
||||
|
||||
uea_dbg(INS_TO_USBDEV(sc),
|
||||
"sending block %u for DSP page %u size %u address %x\n",
|
||||
blockno, pageno, blocksize, le32_to_cpu(blockidx->PageAddress));
|
||||
"sending block %u for DSP page "
|
||||
"%u size %u address %x\n",
|
||||
blockno, pageno, blocksize,
|
||||
le32_to_cpu(blockidx->PageAddress));
|
||||
|
||||
/* send block info through the IDMA pipe */
|
||||
if (uea_idma_write(sc, &bi, E4_BLOCK_INFO_SIZE))
|
||||
|
|
@ -1042,7 +1050,8 @@ static void uea_load_page_e4(struct work_struct *work)
|
|||
|
||||
p = (struct l1_code *) sc->dsp_firm->data;
|
||||
if (pageno >= le16_to_cpu(p->page_header[0].PageNumber)) {
|
||||
uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n", pageno);
|
||||
uea_err(INS_TO_USBDEV(sc), "invalid DSP "
|
||||
"page %u requested\n", pageno);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1059,7 +1068,7 @@ static void uea_load_page_e4(struct work_struct *work)
|
|||
__uea_load_page_e4(sc, i, 1);
|
||||
}
|
||||
|
||||
uea_dbg(INS_TO_USBDEV(sc),"sending start bi\n");
|
||||
uea_dbg(INS_TO_USBDEV(sc) , "sending start bi\n");
|
||||
|
||||
bi.wHdr = cpu_to_be16(UEA_BIHDR);
|
||||
bi.bBootPage = 0;
|
||||
|
|
@ -1139,8 +1148,10 @@ static int uea_cmv_e1(struct uea_softc *sc,
|
|||
uea_enters(INS_TO_USBDEV(sc));
|
||||
uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, "
|
||||
"offset : 0x%04x, data : 0x%08x\n",
|
||||
E1_FUNCTION_TYPE(function), E1_FUNCTION_SUBTYPE(function),
|
||||
E1_GETSA1(address), E1_GETSA2(address), E1_GETSA3(address),
|
||||
E1_FUNCTION_TYPE(function),
|
||||
E1_FUNCTION_SUBTYPE(function),
|
||||
E1_GETSA1(address), E1_GETSA2(address),
|
||||
E1_GETSA3(address),
|
||||
E1_GETSA4(address), offset, data);
|
||||
|
||||
/* we send a request, but we expect a reply */
|
||||
|
|
@ -1157,7 +1168,8 @@ static int uea_cmv_e1(struct uea_softc *sc,
|
|||
cmv.wOffsetAddress = cpu_to_le16(offset);
|
||||
put_unaligned_le32(data >> 16 | data << 16, &cmv.dwData);
|
||||
|
||||
ret = uea_request(sc, UEA_E1_SET_BLOCK, UEA_MPTX_START, sizeof(cmv), &cmv);
|
||||
ret = uea_request(sc, UEA_E1_SET_BLOCK, UEA_MPTX_START,
|
||||
sizeof(cmv), &cmv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = wait_cmv_ack(sc);
|
||||
|
|
@ -1191,7 +1203,8 @@ static int uea_cmv_e4(struct uea_softc *sc,
|
|||
cmv.wOffset = cpu_to_be16(offset);
|
||||
cmv.dwData[0] = cpu_to_be32(data);
|
||||
|
||||
ret = uea_request(sc, UEA_E4_SET_BLOCK, UEA_MPTX_START, sizeof(cmv), &cmv);
|
||||
ret = uea_request(sc, UEA_E4_SET_BLOCK, UEA_MPTX_START,
|
||||
sizeof(cmv), &cmv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = wait_cmv_ack(sc);
|
||||
|
|
@ -1208,7 +1221,7 @@ static inline int uea_read_cmv_e1(struct uea_softc *sc,
|
|||
uea_err(INS_TO_USBDEV(sc),
|
||||
"reading cmv failed with error %d\n", ret);
|
||||
else
|
||||
*data = sc->data;
|
||||
*data = sc->data;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1216,13 +1229,14 @@ static inline int uea_read_cmv_e1(struct uea_softc *sc,
|
|||
static inline int uea_read_cmv_e4(struct uea_softc *sc,
|
||||
u8 size, u16 group, u16 address, u16 offset, u32 *data)
|
||||
{
|
||||
int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, E4_REQUESTREAD, size),
|
||||
int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS,
|
||||
E4_REQUESTREAD, size),
|
||||
group, address, offset, 0);
|
||||
if (ret < 0)
|
||||
uea_err(INS_TO_USBDEV(sc),
|
||||
"reading cmv failed with error %d\n", ret);
|
||||
else {
|
||||
*data = sc->data;
|
||||
*data = sc->data;
|
||||
/* size is in 16-bit word quantities */
|
||||
if (size > 2)
|
||||
*(data + 1) = sc->data1;
|
||||
|
|
@ -1245,7 +1259,8 @@ static inline int uea_write_cmv_e1(struct uea_softc *sc,
|
|||
static inline int uea_write_cmv_e4(struct uea_softc *sc,
|
||||
u8 size, u16 group, u16 address, u16 offset, u32 data)
|
||||
{
|
||||
int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, E4_REQUESTWRITE, size),
|
||||
int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS,
|
||||
E4_REQUESTWRITE, size),
|
||||
group, address, offset, data);
|
||||
if (ret < 0)
|
||||
uea_err(INS_TO_USBDEV(sc),
|
||||
|
|
@ -1442,27 +1457,29 @@ static int uea_stat_e4(struct uea_softc *sc)
|
|||
return ret;
|
||||
|
||||
switch (sc->stats.phy.state) {
|
||||
case 0x0: /* not yet synchronized */
|
||||
case 0x1:
|
||||
case 0x3:
|
||||
case 0x4:
|
||||
uea_dbg(INS_TO_USBDEV(sc), "modem not yet synchronized\n");
|
||||
return 0;
|
||||
case 0x5: /* initialization */
|
||||
case 0x6:
|
||||
case 0x9:
|
||||
case 0xa:
|
||||
uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n");
|
||||
return 0;
|
||||
case 0x2: /* fail ... */
|
||||
uea_info(INS_TO_USBDEV(sc), "modem synchronization failed"
|
||||
" (may be try other cmv/dsp)\n");
|
||||
return -EAGAIN;
|
||||
case 0x7: /* operational */
|
||||
break;
|
||||
default:
|
||||
uea_warn(INS_TO_USBDEV(sc), "unknown state: %x\n", sc->stats.phy.state);
|
||||
return 0;
|
||||
case 0x0: /* not yet synchronized */
|
||||
case 0x1:
|
||||
case 0x3:
|
||||
case 0x4:
|
||||
uea_dbg(INS_TO_USBDEV(sc), "modem not yet "
|
||||
"synchronized\n");
|
||||
return 0;
|
||||
case 0x5: /* initialization */
|
||||
case 0x6:
|
||||
case 0x9:
|
||||
case 0xa:
|
||||
uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n");
|
||||
return 0;
|
||||
case 0x2: /* fail ... */
|
||||
uea_info(INS_TO_USBDEV(sc), "modem synchronization "
|
||||
"failed (may be try other cmv/dsp)\n");
|
||||
return -EAGAIN;
|
||||
case 0x7: /* operational */
|
||||
break;
|
||||
default:
|
||||
uea_warn(INS_TO_USBDEV(sc), "unknown state: %x\n",
|
||||
sc->stats.phy.state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data != 7) {
|
||||
|
|
@ -1502,9 +1519,9 @@ static int uea_stat_e4(struct uea_softc *sc)
|
|||
if (sc->stats.phy.flags) {
|
||||
uea_dbg(INS_TO_USBDEV(sc), "Stat flag = 0x%x\n",
|
||||
sc->stats.phy.flags);
|
||||
if (sc->stats.phy.flags & 1) //delineation LOSS
|
||||
if (sc->stats.phy.flags & 1) /* delineation LOSS */
|
||||
return -EAGAIN;
|
||||
if (sc->stats.phy.flags & 0x4000) //Reset Flag
|
||||
if (sc->stats.phy.flags & 0x4000) /* Reset Flag */
|
||||
return -EAGAIN;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1618,7 +1635,8 @@ static int request_cmvs(struct uea_softc *sc,
|
|||
if (ret < 0) {
|
||||
/* if caller can handle old version, try to provide it */
|
||||
if (*ver == 1) {
|
||||
uea_warn(INS_TO_USBDEV(sc), "requesting firmware %s failed, "
|
||||
uea_warn(INS_TO_USBDEV(sc), "requesting "
|
||||
"firmware %s failed, "
|
||||
"try to get older cmvs\n", cmv_name);
|
||||
return request_cmvs_old(sc, cmvs, fw);
|
||||
}
|
||||
|
|
@ -1632,8 +1650,8 @@ static int request_cmvs(struct uea_softc *sc,
|
|||
data = (u8 *) (*fw)->data;
|
||||
if (size < 4 || strncmp(data, "cmv2", 4) != 0) {
|
||||
if (*ver == 1) {
|
||||
uea_warn(INS_TO_USBDEV(sc), "firmware %s is corrupted, "
|
||||
"try to get older cmvs\n", cmv_name);
|
||||
uea_warn(INS_TO_USBDEV(sc), "firmware %s is corrupted,"
|
||||
" try to get older cmvs\n", cmv_name);
|
||||
release_firmware(*fw);
|
||||
return request_cmvs_old(sc, cmvs, fw);
|
||||
}
|
||||
|
|
@ -1670,7 +1688,7 @@ static int uea_send_cmvs_e1(struct uea_softc *sc)
|
|||
int i, ret, len;
|
||||
void *cmvs_ptr;
|
||||
const struct firmware *cmvs_fw;
|
||||
int ver = 1; // we can handle v1 cmv firmware version;
|
||||
int ver = 1; /* we can handle v1 cmv firmware version; */
|
||||
|
||||
/* Enter in R-IDLE (cmv) until instructed otherwise */
|
||||
ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 1);
|
||||
|
|
@ -1685,7 +1703,7 @@ static int uea_send_cmvs_e1(struct uea_softc *sc)
|
|||
sc->stats.phy.firmid);
|
||||
|
||||
/* get options */
|
||||
ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver);
|
||||
ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -1697,9 +1715,10 @@ static int uea_send_cmvs_e1(struct uea_softc *sc)
|
|||
"please update your firmware\n");
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = uea_write_cmv_e1(sc, get_unaligned_le32(&cmvs_v1[i].address),
|
||||
get_unaligned_le16(&cmvs_v1[i].offset),
|
||||
get_unaligned_le32(&cmvs_v1[i].data));
|
||||
ret = uea_write_cmv_e1(sc,
|
||||
get_unaligned_le32(&cmvs_v1[i].address),
|
||||
get_unaligned_le16(&cmvs_v1[i].offset),
|
||||
get_unaligned_le32(&cmvs_v1[i].data));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1707,9 +1726,10 @@ static int uea_send_cmvs_e1(struct uea_softc *sc)
|
|||
struct uea_cmvs_v2 *cmvs_v2 = cmvs_ptr;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = uea_write_cmv_e1(sc, get_unaligned_le32(&cmvs_v2[i].address),
|
||||
(u16) get_unaligned_le32(&cmvs_v2[i].offset),
|
||||
get_unaligned_le32(&cmvs_v2[i].data));
|
||||
ret = uea_write_cmv_e1(sc,
|
||||
get_unaligned_le32(&cmvs_v2[i].address),
|
||||
(u16) get_unaligned_le32(&cmvs_v2[i].offset),
|
||||
get_unaligned_le32(&cmvs_v2[i].data));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1722,7 +1742,8 @@ static int uea_send_cmvs_e1(struct uea_softc *sc)
|
|||
/* Enter in R-ACT-REQ */
|
||||
ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 2);
|
||||
uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
|
||||
uea_info(INS_TO_USBDEV(sc), "modem started, waiting synchronization...\n");
|
||||
uea_info(INS_TO_USBDEV(sc), "modem started, waiting "
|
||||
"synchronization...\n");
|
||||
out:
|
||||
release_firmware(cmvs_fw);
|
||||
return ret;
|
||||
|
|
@ -1733,7 +1754,7 @@ static int uea_send_cmvs_e4(struct uea_softc *sc)
|
|||
int i, ret, len;
|
||||
void *cmvs_ptr;
|
||||
const struct firmware *cmvs_fw;
|
||||
int ver = 2; // we can only handle v2 cmv firmware version;
|
||||
int ver = 2; /* we can only handle v2 cmv firmware version; */
|
||||
|
||||
/* Enter in R-IDLE (cmv) until instructed otherwise */
|
||||
ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 1);
|
||||
|
|
@ -1750,7 +1771,7 @@ static int uea_send_cmvs_e4(struct uea_softc *sc)
|
|||
|
||||
|
||||
/* get options */
|
||||
ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver);
|
||||
ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -1760,10 +1781,10 @@ static int uea_send_cmvs_e4(struct uea_softc *sc)
|
|||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = uea_write_cmv_e4(sc, 1,
|
||||
get_unaligned_le32(&cmvs_v2[i].group),
|
||||
get_unaligned_le32(&cmvs_v2[i].address),
|
||||
get_unaligned_le32(&cmvs_v2[i].offset),
|
||||
get_unaligned_le32(&cmvs_v2[i].data));
|
||||
get_unaligned_le32(&cmvs_v2[i].group),
|
||||
get_unaligned_le32(&cmvs_v2[i].address),
|
||||
get_unaligned_le32(&cmvs_v2[i].offset),
|
||||
get_unaligned_le32(&cmvs_v2[i].data));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1776,7 +1797,8 @@ static int uea_send_cmvs_e4(struct uea_softc *sc)
|
|||
/* Enter in R-ACT-REQ */
|
||||
ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 2);
|
||||
uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
|
||||
uea_info(INS_TO_USBDEV(sc), "modem started, waiting synchronization...\n");
|
||||
uea_info(INS_TO_USBDEV(sc), "modem started, waiting "
|
||||
"synchronization...\n");
|
||||
out:
|
||||
release_firmware(cmvs_fw);
|
||||
return ret;
|
||||
|
|
@ -1812,7 +1834,7 @@ static int uea_start_reset(struct uea_softc *sc)
|
|||
uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL);
|
||||
uea_request(sc, UEA_SET_MODE, UEA_BOOT_IDMA, 0, NULL);
|
||||
|
||||
/* enter reset mode */
|
||||
/* enter reset mode */
|
||||
uea_request(sc, UEA_SET_MODE, UEA_START_RESET, 0, NULL);
|
||||
|
||||
/* original driver use 200ms, but windows driver use 100ms */
|
||||
|
|
@ -1824,7 +1846,7 @@ static int uea_start_reset(struct uea_softc *sc)
|
|||
uea_request(sc, UEA_SET_MODE, UEA_END_RESET, 0, NULL);
|
||||
|
||||
if (UEA_CHIP_VERSION(sc) != EAGLE_IV) {
|
||||
/* clear tx and rx mailboxes */
|
||||
/* clear tx and rx mailboxes */
|
||||
uea_request(sc, UEA_SET_2183_DATA, UEA_MPTX_MAILBOX, 2, &zero);
|
||||
uea_request(sc, UEA_SET_2183_DATA, UEA_MPRX_MAILBOX, 2, &zero);
|
||||
uea_request(sc, UEA_SET_2183_DATA, UEA_SWAP_MAILBOX, 2, &zero);
|
||||
|
|
@ -1835,9 +1857,11 @@ static int uea_start_reset(struct uea_softc *sc)
|
|||
return ret;
|
||||
|
||||
if (UEA_CHIP_VERSION(sc) == EAGLE_IV)
|
||||
sc->cmv_dsc.e4.function = E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1);
|
||||
sc->cmv_dsc.e4.function = E4_MAKEFUNCTION(E4_ADSLDIRECTIVE,
|
||||
E4_MODEMREADY, 1);
|
||||
else
|
||||
sc->cmv_dsc.e1.function = E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY);
|
||||
sc->cmv_dsc.e1.function = E1_MAKEFUNCTION(E1_ADSLDIRECTIVE,
|
||||
E1_MODEMREADY);
|
||||
|
||||
/* demask interrupt */
|
||||
sc->booting = 0;
|
||||
|
|
@ -1937,7 +1961,8 @@ static int load_XILINX_firmware(struct uea_softc *sc)
|
|||
value = 0;
|
||||
ret = uea_send_modem_cmd(sc->usb_dev, 0xe, 1, &value);
|
||||
if (ret < 0)
|
||||
uea_err(sc->usb_dev, "elsa de-assert failed with error %d\n", ret);
|
||||
uea_err(sc->usb_dev, "elsa de-assert failed with error"
|
||||
" %d\n", ret);
|
||||
|
||||
err1:
|
||||
release_firmware(fw_entry);
|
||||
|
|
@ -1966,13 +1991,15 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr)
|
|||
if (UEA_CHIP_VERSION(sc) == ADI930
|
||||
&& cmv->bFunction == E1_MAKEFUNCTION(2, 2)) {
|
||||
cmv->wIndex = cpu_to_le16(dsc->idx);
|
||||
put_unaligned_le32(dsc->address, &cmv->dwSymbolicAddress);
|
||||
put_unaligned_le32(dsc->address,
|
||||
&cmv->dwSymbolicAddress);
|
||||
cmv->wOffsetAddress = cpu_to_le16(dsc->offset);
|
||||
} else
|
||||
goto bad2;
|
||||
}
|
||||
|
||||
if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY)) {
|
||||
if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE,
|
||||
E1_MODEMREADY)) {
|
||||
wake_up_cmv_ack(sc);
|
||||
uea_leaves(INS_TO_USBDEV(sc));
|
||||
return;
|
||||
|
|
@ -2021,7 +2048,8 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr)
|
|||
if (be16_to_cpu(cmv->wFunction) != dsc->function)
|
||||
goto bad2;
|
||||
|
||||
if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1)) {
|
||||
if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE,
|
||||
E4_MODEMREADY, 1)) {
|
||||
wake_up_cmv_ack(sc);
|
||||
uea_leaves(INS_TO_USBDEV(sc));
|
||||
return;
|
||||
|
|
@ -2048,14 +2076,16 @@ bad2:
|
|||
return;
|
||||
}
|
||||
|
||||
static void uea_schedule_load_page_e1(struct uea_softc *sc, struct intr_pkt *intr)
|
||||
static void uea_schedule_load_page_e1(struct uea_softc *sc,
|
||||
struct intr_pkt *intr)
|
||||
{
|
||||
sc->pageno = intr->e1_bSwapPageNo;
|
||||
sc->ovl = intr->e1_bOvl >> 4 | intr->e1_bOvl << 4;
|
||||
queue_work(sc->work_q, &sc->task);
|
||||
}
|
||||
|
||||
static void uea_schedule_load_page_e4(struct uea_softc *sc, struct intr_pkt *intr)
|
||||
static void uea_schedule_load_page_e4(struct uea_softc *sc,
|
||||
struct intr_pkt *intr)
|
||||
{
|
||||
sc->pageno = intr->e4_bSwapPageNo;
|
||||
queue_work(sc->work_q, &sc->task);
|
||||
|
|
@ -2263,8 +2293,8 @@ out:
|
|||
|
||||
static DEVICE_ATTR(stat_status, S_IWUGO | S_IRUGO, read_status, reboot);
|
||||
|
||||
static ssize_t read_human_status(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
static ssize_t read_human_status(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
int modem_state;
|
||||
|
|
@ -2289,7 +2319,7 @@ static ssize_t read_human_status(struct device *dev, struct device_attribute *at
|
|||
case 0xa:
|
||||
modem_state = 1;
|
||||
break;
|
||||
case 0x7: /* operational */
|
||||
case 0x7: /* operational */
|
||||
modem_state = 2;
|
||||
break;
|
||||
case 0x2: /* fail ... */
|
||||
|
|
@ -2324,7 +2354,8 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(stat_human_status, S_IWUGO | S_IRUGO, read_human_status, NULL);
|
||||
static DEVICE_ATTR(stat_human_status, S_IWUGO | S_IRUGO,
|
||||
read_human_status, NULL);
|
||||
|
||||
static ssize_t read_delin(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -2358,25 +2389,25 @@ out:
|
|||
|
||||
static DEVICE_ATTR(stat_delin, S_IWUGO | S_IRUGO, read_delin, NULL);
|
||||
|
||||
#define UEA_ATTR(name, reset) \
|
||||
#define UEA_ATTR(name, reset) \
|
||||
\
|
||||
static ssize_t read_##name(struct device *dev, \
|
||||
static ssize_t read_##name(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
int ret = -ENODEV; \
|
||||
struct uea_softc *sc; \
|
||||
\
|
||||
mutex_lock(&uea_mutex); \
|
||||
{ \
|
||||
int ret = -ENODEV; \
|
||||
struct uea_softc *sc; \
|
||||
\
|
||||
mutex_lock(&uea_mutex); \
|
||||
sc = dev_to_uea(dev); \
|
||||
if (!sc) \
|
||||
goto out; \
|
||||
if (!sc) \
|
||||
goto out; \
|
||||
ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.name); \
|
||||
if (reset) \
|
||||
sc->stats.phy.name = 0; \
|
||||
out: \
|
||||
mutex_unlock(&uea_mutex); \
|
||||
return ret; \
|
||||
} \
|
||||
out: \
|
||||
mutex_unlock(&uea_mutex); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
static DEVICE_ATTR(stat_##name, S_IRUGO, read_##name, NULL)
|
||||
|
||||
|
|
@ -2527,12 +2558,14 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
|
|||
else if (sc->driver_info & AUTO_ANNEX_B)
|
||||
sc->annex = ANNEXB;
|
||||
else
|
||||
sc->annex = (le16_to_cpu(sc->usb_dev->descriptor.bcdDevice) & 0x80)?ANNEXB:ANNEXA;
|
||||
sc->annex = (le16_to_cpu
|
||||
(sc->usb_dev->descriptor.bcdDevice) & 0x80) ? ANNEXB : ANNEXA;
|
||||
|
||||
alt = altsetting[sc->modem_index];
|
||||
/* ADI930 don't support iso */
|
||||
if (UEA_CHIP_VERSION(id) != ADI930 && alt > 0) {
|
||||
if (alt <= 8 && usb_set_interface(usb, UEA_DS_IFACE_NO, alt) == 0) {
|
||||
if (alt <= 8 &&
|
||||
usb_set_interface(usb, UEA_DS_IFACE_NO, alt) == 0) {
|
||||
uea_dbg(usb, "set alternate %u for 2 interface\n", alt);
|
||||
uea_info(usb, "using iso mode\n");
|
||||
usbatm->flags |= UDSL_USE_ISOC | UDSL_IGNORE_EILSEQ;
|
||||
|
|
@ -2621,40 +2654,74 @@ static void uea_disconnect(struct usb_interface *intf)
|
|||
* List of supported VID/PID
|
||||
*/
|
||||
static const struct usb_device_id uea_ids[] = {
|
||||
{USB_DEVICE(ANALOG_VID, ADI930_PID_PREFIRM), .driver_info = ADI930 | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, ADI930_PID_PSTFIRM), .driver_info = ADI930 | PSTFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PREFIRM), .driver_info = EAGLE_III | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PSTFIRM), .driver_info = EAGLE_III | PSTFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PREFIRM), .driver_info = EAGLE_IV | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PSTFIRM), .driver_info = EAGLE_IV | PSTFIRM},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_A},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_B},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_PREFIRM), .driver_info = ADI930 | PREFIRM},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_PSTFIRM), .driver_info = ADI930 | PSTFIRM},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_A_PREFIRM), .driver_info = ADI930 | PREFIRM},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_A_PSTFIRM), .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_A},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_B_PREFIRM), .driver_info = ADI930 | PREFIRM},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_B_PSTFIRM), .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_B},
|
||||
{USB_DEVICE(USR_VID, MILLER_A_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(USR_VID, MILLER_A_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
|
||||
{USB_DEVICE(USR_VID, MILLER_B_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(USR_VID, MILLER_B_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
|
||||
{USB_DEVICE(USR_VID, HEINEKEN_A_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(USR_VID, HEINEKEN_A_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
|
||||
{USB_DEVICE(USR_VID, HEINEKEN_B_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(USR_VID, HEINEKEN_B_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
|
||||
{USB_DEVICE(ANALOG_VID, ADI930_PID_PREFIRM),
|
||||
.driver_info = ADI930 | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, ADI930_PID_PSTFIRM),
|
||||
.driver_info = ADI930 | PSTFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PREFIRM),
|
||||
.driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_I | PSTFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PREFIRM),
|
||||
.driver_info = EAGLE_II | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_II | PSTFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PREFIRM),
|
||||
.driver_info = EAGLE_II | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_II | PSTFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PREFIRM),
|
||||
.driver_info = EAGLE_III | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_III | PSTFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PREFIRM),
|
||||
.driver_info = EAGLE_IV | PREFIRM},
|
||||
{USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_IV | PSTFIRM},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PREFIRM),
|
||||
.driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PREFIRM),
|
||||
.driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PREFIRM),
|
||||
.driver_info = EAGLE_II | PREFIRM},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_A},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PREFIRM),
|
||||
.driver_info = EAGLE_II | PREFIRM},
|
||||
{USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_B},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_PREFIRM),
|
||||
.driver_info = ADI930 | PREFIRM},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_PSTFIRM),
|
||||
.driver_info = ADI930 | PSTFIRM},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_A_PREFIRM),
|
||||
.driver_info = ADI930 | PREFIRM},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_A_PSTFIRM),
|
||||
.driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_A},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_B_PREFIRM),
|
||||
.driver_info = ADI930 | PREFIRM},
|
||||
{USB_DEVICE(ELSA_VID, ELSA_PID_B_PSTFIRM),
|
||||
.driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_B},
|
||||
{USB_DEVICE(USR_VID, MILLER_A_PID_PREFIRM),
|
||||
.driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(USR_VID, MILLER_A_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
|
||||
{USB_DEVICE(USR_VID, MILLER_B_PID_PREFIRM),
|
||||
.driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(USR_VID, MILLER_B_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
|
||||
{USB_DEVICE(USR_VID, HEINEKEN_A_PID_PREFIRM),
|
||||
.driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(USR_VID, HEINEKEN_A_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
|
||||
{USB_DEVICE(USR_VID, HEINEKEN_B_PID_PREFIRM),
|
||||
.driver_info = EAGLE_I | PREFIRM},
|
||||
{USB_DEVICE(USR_VID, HEINEKEN_B_PID_PSTFIRM),
|
||||
.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/usb.h>
|
||||
#include "../core/hcd.h"
|
||||
#include <linux/usb/hcd.h>
|
||||
#include "c67x00.h"
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -892,7 +892,7 @@ static void acm_write_buffers_free(struct acm *acm)
|
|||
struct usb_device *usb_dev = interface_to_usbdev(acm->control);
|
||||
|
||||
for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
|
||||
usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
|
||||
usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
|
||||
}
|
||||
|
||||
static void acm_read_buffers_free(struct acm *acm)
|
||||
|
|
@ -901,8 +901,8 @@ static void acm_read_buffers_free(struct acm *acm)
|
|||
int i, n = acm->rx_buflimit;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
usb_buffer_free(usb_dev, acm->readsize,
|
||||
acm->rb[i].base, acm->rb[i].dma);
|
||||
usb_free_coherent(usb_dev, acm->readsize,
|
||||
acm->rb[i].base, acm->rb[i].dma);
|
||||
}
|
||||
|
||||
/* Little helper: write buffers allocate */
|
||||
|
|
@ -912,13 +912,13 @@ static int acm_write_buffers_alloc(struct acm *acm)
|
|||
struct acm_wb *wb;
|
||||
|
||||
for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
|
||||
wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
|
||||
wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
|
||||
&wb->dmah);
|
||||
if (!wb->buf) {
|
||||
while (i != 0) {
|
||||
--i;
|
||||
--wb;
|
||||
usb_buffer_free(acm->dev, acm->writesize,
|
||||
usb_free_coherent(acm->dev, acm->writesize,
|
||||
wb->buf, wb->dmah);
|
||||
}
|
||||
return -ENOMEM;
|
||||
|
|
@ -1177,7 +1177,7 @@ made_compressed_probe:
|
|||
tty_port_init(&acm->port);
|
||||
acm->port.ops = &acm_port_ops;
|
||||
|
||||
buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
|
||||
buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
|
||||
if (!buf) {
|
||||
dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
|
||||
goto alloc_fail2;
|
||||
|
|
@ -1210,11 +1210,11 @@ made_compressed_probe:
|
|||
for (i = 0; i < num_rx_buf; i++) {
|
||||
struct acm_rb *rb = &(acm->rb[i]);
|
||||
|
||||
rb->base = usb_buffer_alloc(acm->dev, readsize,
|
||||
rb->base = usb_alloc_coherent(acm->dev, readsize,
|
||||
GFP_KERNEL, &rb->dma);
|
||||
if (!rb->base) {
|
||||
dev_dbg(&intf->dev,
|
||||
"out of memory (read bufs usb_buffer_alloc)\n");
|
||||
"out of memory (read bufs usb_alloc_coherent)\n");
|
||||
goto alloc_fail7;
|
||||
}
|
||||
}
|
||||
|
|
@ -1306,7 +1306,7 @@ alloc_fail7:
|
|||
alloc_fail5:
|
||||
acm_write_buffers_free(acm);
|
||||
alloc_fail4:
|
||||
usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
|
||||
usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
|
||||
alloc_fail2:
|
||||
kfree(acm);
|
||||
alloc_fail:
|
||||
|
|
@ -1356,8 +1356,8 @@ static void acm_disconnect(struct usb_interface *intf)
|
|||
stop_data_traffic(acm);
|
||||
|
||||
acm_write_buffers_free(acm);
|
||||
usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
|
||||
acm->ctrl_dma);
|
||||
usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
|
||||
acm->ctrl_dma);
|
||||
acm_read_buffers_free(acm);
|
||||
|
||||
if (!acm->combined_interfaces)
|
||||
|
|
|
|||
|
|
@ -124,8 +124,8 @@ struct acm {
|
|||
unsigned char clocal; /* termios CLOCAL */
|
||||
unsigned int ctrl_caps; /* control capabilities from the class specific header */
|
||||
unsigned int susp_count; /* number of suspended interfaces */
|
||||
int combined_interfaces:1; /* control and data collapsed */
|
||||
int is_int_ep:1; /* interrupt endpoints contrary to spec used */
|
||||
unsigned int combined_interfaces:1; /* control and data collapsed */
|
||||
unsigned int is_int_ep:1; /* interrupt endpoints contrary to spec used */
|
||||
u8 bInterval;
|
||||
struct acm_wb *delayed_wb; /* write queued for a device about to be woken */
|
||||
};
|
||||
|
|
|
|||
|
|
@ -276,14 +276,14 @@ static void free_urbs(struct wdm_device *desc)
|
|||
|
||||
static void cleanup(struct wdm_device *desc)
|
||||
{
|
||||
usb_buffer_free(interface_to_usbdev(desc->intf),
|
||||
desc->wMaxPacketSize,
|
||||
desc->sbuf,
|
||||
desc->validity->transfer_dma);
|
||||
usb_buffer_free(interface_to_usbdev(desc->intf),
|
||||
desc->wMaxCommand,
|
||||
desc->inbuf,
|
||||
desc->response->transfer_dma);
|
||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
||||
desc->wMaxPacketSize,
|
||||
desc->sbuf,
|
||||
desc->validity->transfer_dma);
|
||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
||||
desc->wMaxCommand,
|
||||
desc->inbuf,
|
||||
desc->response->transfer_dma);
|
||||
kfree(desc->orq);
|
||||
kfree(desc->irq);
|
||||
kfree(desc->ubuf);
|
||||
|
|
@ -705,17 +705,17 @@ next_desc:
|
|||
if (!desc->ubuf)
|
||||
goto err;
|
||||
|
||||
desc->sbuf = usb_buffer_alloc(interface_to_usbdev(intf),
|
||||
desc->sbuf = usb_alloc_coherent(interface_to_usbdev(intf),
|
||||
desc->wMaxPacketSize,
|
||||
GFP_KERNEL,
|
||||
&desc->validity->transfer_dma);
|
||||
if (!desc->sbuf)
|
||||
goto err;
|
||||
|
||||
desc->inbuf = usb_buffer_alloc(interface_to_usbdev(intf),
|
||||
desc->bMaxPacketSize0,
|
||||
GFP_KERNEL,
|
||||
&desc->response->transfer_dma);
|
||||
desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf),
|
||||
desc->bMaxPacketSize0,
|
||||
GFP_KERNEL,
|
||||
&desc->response->transfer_dma);
|
||||
if (!desc->inbuf)
|
||||
goto err2;
|
||||
|
||||
|
|
@ -742,15 +742,15 @@ out:
|
|||
return rv;
|
||||
err3:
|
||||
usb_set_intfdata(intf, NULL);
|
||||
usb_buffer_free(interface_to_usbdev(desc->intf),
|
||||
desc->bMaxPacketSize0,
|
||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
||||
desc->bMaxPacketSize0,
|
||||
desc->inbuf,
|
||||
desc->response->transfer_dma);
|
||||
err2:
|
||||
usb_buffer_free(interface_to_usbdev(desc->intf),
|
||||
desc->wMaxPacketSize,
|
||||
desc->sbuf,
|
||||
desc->validity->transfer_dma);
|
||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
||||
desc->wMaxPacketSize,
|
||||
desc->sbuf,
|
||||
desc->validity->transfer_dma);
|
||||
err:
|
||||
free_urbs(desc);
|
||||
kfree(desc->ubuf);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
* v0.11 - add proto_bias option (Pete Zaitcev)
|
||||
* v0.12 - add hpoj.sourceforge.net ioctls (David Paschal)
|
||||
* v0.13 - alloc space for statusbuf (<status> not on stack);
|
||||
* use usb_buffer_alloc() for read buf & write buf;
|
||||
* use usb_alloc_coherent() for read buf & write buf;
|
||||
* none - Maintained in Linux kernel after v0.13
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/usb.h>
|
||||
#include "hcd.h"
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
#include <linux/usb.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
|
||||
|
||||
#define USB_MAXALTSETTING 128 /* Hard limit */
|
||||
#define USB_MAXENDPOINTS 30 /* Hard limit */
|
||||
|
|
@ -19,32 +21,6 @@ static inline const char *plural(int n)
|
|||
return (n == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
/* FIXME: this is a kludge */
|
||||
static int find_next_descriptor_more(unsigned char *buffer, int size,
|
||||
int dt1, int dt2, int dt3, int *num_skipped)
|
||||
{
|
||||
struct usb_descriptor_header *h;
|
||||
int n = 0;
|
||||
unsigned char *buffer0 = buffer;
|
||||
|
||||
/* Find the next descriptor of type dt1 or dt2 or dt3 */
|
||||
while (size > 0) {
|
||||
h = (struct usb_descriptor_header *) buffer;
|
||||
if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2 ||
|
||||
h->bDescriptorType == dt3)
|
||||
break;
|
||||
buffer += h->bLength;
|
||||
size -= h->bLength;
|
||||
++n;
|
||||
}
|
||||
|
||||
/* Store the number of descriptors skipped and return the
|
||||
* number of bytes skipped */
|
||||
if (num_skipped)
|
||||
*num_skipped = n;
|
||||
return buffer - buffer0;
|
||||
}
|
||||
|
||||
static int find_next_descriptor(unsigned char *buffer, int size,
|
||||
int dt1, int dt2, int *num_skipped)
|
||||
{
|
||||
|
|
@ -69,47 +45,41 @@ static int find_next_descriptor(unsigned char *buffer, int size,
|
|||
return buffer - buffer0;
|
||||
}
|
||||
|
||||
static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
||||
static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
||||
int inum, int asnum, struct usb_host_endpoint *ep,
|
||||
int num_ep, unsigned char *buffer, int size)
|
||||
unsigned char *buffer, int size)
|
||||
{
|
||||
unsigned char *buffer_start = buffer;
|
||||
struct usb_ss_ep_comp_descriptor *desc;
|
||||
int retval;
|
||||
int num_skipped;
|
||||
struct usb_ss_ep_comp_descriptor *desc;
|
||||
int max_tx;
|
||||
int i;
|
||||
|
||||
/* The SuperSpeed endpoint companion descriptor is supposed to
|
||||
* be the first thing immediately following the endpoint descriptor.
|
||||
*/
|
||||
desc = (struct usb_ss_ep_comp_descriptor *) buffer;
|
||||
if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) {
|
||||
if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP ||
|
||||
size < USB_DT_SS_EP_COMP_SIZE) {
|
||||
dev_warn(ddev, "No SuperSpeed endpoint companion for config %d "
|
||||
" interface %d altsetting %d ep %d: "
|
||||
"using minimum values\n",
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
/*
|
||||
* The next descriptor is for an Endpoint or Interface,
|
||||
* no extra descriptors to copy into the companion structure,
|
||||
* and we didn't eat up any of the buffer.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
memcpy(&ep->ss_ep_comp->desc, desc, USB_DT_SS_EP_COMP_SIZE);
|
||||
desc = &ep->ss_ep_comp->desc;
|
||||
buffer += desc->bLength;
|
||||
size -= desc->bLength;
|
||||
|
||||
/* Eat up the other descriptors we don't care about */
|
||||
ep->ss_ep_comp->extra = buffer;
|
||||
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
||||
USB_DT_INTERFACE, &num_skipped);
|
||||
ep->ss_ep_comp->extralen = i;
|
||||
buffer += i;
|
||||
size -= i;
|
||||
retval = buffer - buffer_start;
|
||||
if (num_skipped > 0)
|
||||
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
|
||||
num_skipped, plural(num_skipped),
|
||||
"SuperSpeed endpoint companion");
|
||||
/* Fill in some default values.
|
||||
* Leave bmAttributes as zero, which will mean no streams for
|
||||
* bulk, and isoc won't support multiple bursts of packets.
|
||||
* With bursts of only one packet, and a Mult of 1, the max
|
||||
* amount of data moved per endpoint service interval is one
|
||||
* packet.
|
||||
*/
|
||||
ep->ss_ep_comp.bLength = USB_DT_SS_EP_COMP_SIZE;
|
||||
ep->ss_ep_comp.bDescriptorType = USB_DT_SS_ENDPOINT_COMP;
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc) ||
|
||||
usb_endpoint_xfer_int(&ep->desc))
|
||||
ep->ss_ep_comp.wBytesPerInterval =
|
||||
ep->desc.wMaxPacketSize;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&ep->ss_ep_comp, desc, USB_DT_SS_EP_COMP_SIZE);
|
||||
|
||||
/* Check the various values */
|
||||
if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) {
|
||||
|
|
@ -117,47 +87,48 @@ static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
|||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to zero\n", desc->bMaxBurst,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
desc->bMaxBurst = 0;
|
||||
}
|
||||
if (desc->bMaxBurst > 15) {
|
||||
ep->ss_ep_comp.bMaxBurst = 0;
|
||||
} else if (desc->bMaxBurst > 15) {
|
||||
dev_warn(ddev, "Endpoint with bMaxBurst = %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to 15\n", desc->bMaxBurst,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
desc->bMaxBurst = 15;
|
||||
ep->ss_ep_comp.bMaxBurst = 15;
|
||||
}
|
||||
if ((usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_int(&ep->desc))
|
||||
&& desc->bmAttributes != 0) {
|
||||
|
||||
if ((usb_endpoint_xfer_control(&ep->desc) ||
|
||||
usb_endpoint_xfer_int(&ep->desc)) &&
|
||||
desc->bmAttributes != 0) {
|
||||
dev_warn(ddev, "%s endpoint with bmAttributes = %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to zero\n",
|
||||
usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk",
|
||||
desc->bmAttributes,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
desc->bmAttributes = 0;
|
||||
}
|
||||
if (usb_endpoint_xfer_bulk(&ep->desc) && desc->bmAttributes > 16) {
|
||||
ep->ss_ep_comp.bmAttributes = 0;
|
||||
} else if (usb_endpoint_xfer_bulk(&ep->desc) &&
|
||||
desc->bmAttributes > 16) {
|
||||
dev_warn(ddev, "Bulk endpoint with more than 65536 streams in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to max\n",
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
desc->bmAttributes = 16;
|
||||
}
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc) && desc->bmAttributes > 2) {
|
||||
ep->ss_ep_comp.bmAttributes = 16;
|
||||
} else if (usb_endpoint_xfer_isoc(&ep->desc) &&
|
||||
desc->bmAttributes > 2) {
|
||||
dev_warn(ddev, "Isoc endpoint has Mult of %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to 3\n", desc->bmAttributes + 1,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
desc->bmAttributes = 2;
|
||||
ep->ss_ep_comp.bmAttributes = 2;
|
||||
}
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc)) {
|
||||
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc))
|
||||
max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1) *
|
||||
(desc->bmAttributes + 1);
|
||||
} else if (usb_endpoint_xfer_int(&ep->desc)) {
|
||||
else if (usb_endpoint_xfer_int(&ep->desc))
|
||||
max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1);
|
||||
} else {
|
||||
goto valid;
|
||||
}
|
||||
else
|
||||
max_tx = 999999;
|
||||
if (desc->wBytesPerInterval > max_tx) {
|
||||
dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
|
|
@ -166,10 +137,8 @@ static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
|||
desc->wBytesPerInterval,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress,
|
||||
max_tx);
|
||||
desc->wBytesPerInterval = max_tx;
|
||||
ep->ss_ep_comp.wBytesPerInterval = max_tx;
|
||||
}
|
||||
valid:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
||||
|
|
@ -291,61 +260,19 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
|||
cfgno, inum, asnum, d->bEndpointAddress,
|
||||
maxp);
|
||||
}
|
||||
/* Allocate room for and parse any SS endpoint companion descriptors */
|
||||
if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) {
|
||||
endpoint->extra = buffer;
|
||||
i = find_next_descriptor_more(buffer, size, USB_DT_SS_ENDPOINT_COMP,
|
||||
USB_DT_ENDPOINT, USB_DT_INTERFACE, &n);
|
||||
endpoint->extralen = i;
|
||||
buffer += i;
|
||||
size -= i;
|
||||
|
||||
/* Allocate space for the SS endpoint companion descriptor */
|
||||
endpoint->ss_ep_comp = kzalloc(sizeof(struct usb_host_ss_ep_comp),
|
||||
GFP_KERNEL);
|
||||
if (!endpoint->ss_ep_comp)
|
||||
return -ENOMEM;
|
||||
/* Parse a possible SuperSpeed endpoint companion descriptor */
|
||||
if (to_usb_device(ddev)->speed == USB_SPEED_SUPER)
|
||||
usb_parse_ss_endpoint_companion(ddev, cfgno,
|
||||
inum, asnum, endpoint, buffer, size);
|
||||
|
||||
/* Fill in some default values (may be overwritten later) */
|
||||
endpoint->ss_ep_comp->desc.bLength = USB_DT_SS_EP_COMP_SIZE;
|
||||
endpoint->ss_ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP;
|
||||
endpoint->ss_ep_comp->desc.bMaxBurst = 0;
|
||||
/*
|
||||
* Leave bmAttributes as zero, which will mean no streams for
|
||||
* bulk, and isoc won't support multiple bursts of packets.
|
||||
* With bursts of only one packet, and a Mult of 1, the max
|
||||
* amount of data moved per endpoint service interval is one
|
||||
* packet.
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(&endpoint->desc) ||
|
||||
usb_endpoint_xfer_int(&endpoint->desc))
|
||||
endpoint->ss_ep_comp->desc.wBytesPerInterval =
|
||||
endpoint->desc.wMaxPacketSize;
|
||||
|
||||
if (size > 0) {
|
||||
retval = usb_parse_ss_endpoint_companion(ddev, cfgno,
|
||||
inum, asnum, endpoint, num_ep, buffer,
|
||||
size);
|
||||
if (retval >= 0) {
|
||||
buffer += retval;
|
||||
retval = buffer - buffer0;
|
||||
}
|
||||
} else {
|
||||
dev_warn(ddev, "config %d interface %d altsetting %d "
|
||||
"endpoint 0x%X has no "
|
||||
"SuperSpeed companion descriptor\n",
|
||||
cfgno, inum, asnum, d->bEndpointAddress);
|
||||
retval = buffer - buffer0;
|
||||
}
|
||||
} else {
|
||||
/* Skip over any Class Specific or Vendor Specific descriptors;
|
||||
* find the next endpoint or interface descriptor */
|
||||
endpoint->extra = buffer;
|
||||
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
||||
USB_DT_INTERFACE, &n);
|
||||
endpoint->extralen = i;
|
||||
retval = buffer - buffer0 + i;
|
||||
}
|
||||
/* Skip over any Class Specific or Vendor Specific descriptors;
|
||||
* find the next endpoint or interface descriptor */
|
||||
endpoint->extra = buffer;
|
||||
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
||||
USB_DT_INTERFACE, &n);
|
||||
endpoint->extralen = i;
|
||||
retval = buffer - buffer0 + i;
|
||||
if (n > 0)
|
||||
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
|
||||
n, plural(n), "endpoint");
|
||||
|
|
@ -478,9 +405,10 @@ skip_to_next_interface_descriptor:
|
|||
return buffer - buffer0 + i;
|
||||
}
|
||||
|
||||
static int usb_parse_configuration(struct device *ddev, int cfgidx,
|
||||
static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
|
||||
struct usb_host_config *config, unsigned char *buffer, int size)
|
||||
{
|
||||
struct device *ddev = &dev->dev;
|
||||
unsigned char *buffer0 = buffer;
|
||||
int cfgno;
|
||||
int nintf, nintf_orig;
|
||||
|
|
@ -549,6 +477,16 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
|
|||
}
|
||||
|
||||
inum = d->bInterfaceNumber;
|
||||
|
||||
if ((dev->quirks & USB_QUIRK_HONOR_BNUMINTERFACES) &&
|
||||
n >= nintf_orig) {
|
||||
dev_warn(ddev, "config %d has more interface "
|
||||
"descriptors, than it declares in "
|
||||
"bNumInterfaces, ignoring interface "
|
||||
"number: %d\n", cfgno, inum);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inum >= nintf_orig)
|
||||
dev_warn(ddev, "config %d has an invalid "
|
||||
"interface number: %d but max is %d\n",
|
||||
|
|
@ -722,7 +660,6 @@ int usb_get_configuration(struct usb_device *dev)
|
|||
int ncfg = dev->descriptor.bNumConfigurations;
|
||||
int result = 0;
|
||||
unsigned int cfgno, length;
|
||||
unsigned char *buffer;
|
||||
unsigned char *bigbuffer;
|
||||
struct usb_config_descriptor *desc;
|
||||
|
||||
|
|
@ -751,17 +688,16 @@ int usb_get_configuration(struct usb_device *dev)
|
|||
if (!dev->rawdescriptors)
|
||||
goto err2;
|
||||
|
||||
buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
|
||||
if (!desc)
|
||||
goto err2;
|
||||
desc = (struct usb_config_descriptor *)buffer;
|
||||
|
||||
result = 0;
|
||||
for (; cfgno < ncfg; cfgno++) {
|
||||
/* We grab just the first descriptor so we know how long
|
||||
* the whole configuration is */
|
||||
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
|
||||
buffer, USB_DT_CONFIG_SIZE);
|
||||
desc, USB_DT_CONFIG_SIZE);
|
||||
if (result < 0) {
|
||||
dev_err(ddev, "unable to read config index %d "
|
||||
"descriptor/%s: %d\n", cfgno, "start", result);
|
||||
|
|
@ -800,7 +736,7 @@ int usb_get_configuration(struct usb_device *dev)
|
|||
|
||||
dev->rawdescriptors[cfgno] = bigbuffer;
|
||||
|
||||
result = usb_parse_configuration(&dev->dev, cfgno,
|
||||
result = usb_parse_configuration(dev, cfgno,
|
||||
&dev->config[cfgno], bigbuffer, length);
|
||||
if (result < 0) {
|
||||
++cfgno;
|
||||
|
|
@ -810,7 +746,7 @@ int usb_get_configuration(struct usb_device *dev)
|
|||
result = 0;
|
||||
|
||||
err:
|
||||
kfree(buffer);
|
||||
kfree(desc);
|
||||
out_not_authorized:
|
||||
dev->descriptor.bNumConfigurations = cfgno;
|
||||
err2:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
/*
|
||||
* devices.c
|
||||
* (C) Copyright 1999 Randy Dunlap.
|
||||
* (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. (proc file per device)
|
||||
* (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>.
|
||||
* (proc file per device)
|
||||
* (C) Copyright 1999 Deti Fliegl (new USB architecture)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
|
@ -55,11 +56,11 @@
|
|||
#include <linux/usb.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
|
||||
/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
|
||||
#define ALLOW_SERIAL_NUMBER
|
||||
|
|
@ -138,8 +139,8 @@ struct class_info {
|
|||
char *class_name;
|
||||
};
|
||||
|
||||
static const struct class_info clas_info[] =
|
||||
{ /* max. 5 chars. per name string */
|
||||
static const struct class_info clas_info[] = {
|
||||
/* max. 5 chars. per name string */
|
||||
{USB_CLASS_PER_INTERFACE, ">ifc"},
|
||||
{USB_CLASS_AUDIO, "audio"},
|
||||
{USB_CLASS_COMM, "comm."},
|
||||
|
|
@ -191,8 +192,10 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
|
|||
|
||||
if (speed == USB_SPEED_HIGH) {
|
||||
switch (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) {
|
||||
case 1 << 11: bandwidth = 2; break;
|
||||
case 2 << 11: bandwidth = 3; break;
|
||||
case 1 << 11:
|
||||
bandwidth = 2; break;
|
||||
case 2 << 11:
|
||||
bandwidth = 3; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -200,7 +203,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
|
|||
switch (usb_endpoint_type(desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
type = "Ctrl";
|
||||
if (speed == USB_SPEED_HIGH) /* uframes per NAK */
|
||||
if (speed == USB_SPEED_HIGH) /* uframes per NAK */
|
||||
interval = desc->bInterval;
|
||||
else
|
||||
interval = 0;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/usb/hcd.h> /* for usbcore internals */
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/security.h>
|
||||
|
|
@ -50,9 +51,7 @@
|
|||
#include <asm/byteorder.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include "hcd.h" /* for usbcore internals */
|
||||
#include "usb.h"
|
||||
#include "hub.h"
|
||||
|
||||
#define USB_MAXBUS 64
|
||||
#define USB_DEVICE_MAX USB_MAXBUS * 128
|
||||
|
|
|
|||
|
|
@ -26,8 +26,9 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "hcd.h"
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
|
||||
|
|
@ -333,7 +334,8 @@ static int usb_probe_interface(struct device *dev)
|
|||
usb_cancel_queued_reset(intf);
|
||||
|
||||
/* Unbound interfaces are always runtime-PM-disabled and -suspended */
|
||||
pm_runtime_disable(dev);
|
||||
if (driver->supports_autosuspend)
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
|
||||
usb_autosuspend_device(udev);
|
||||
|
|
@ -388,7 +390,8 @@ static int usb_unbind_interface(struct device *dev)
|
|||
intf->needs_remote_wakeup = 0;
|
||||
|
||||
/* Unbound interfaces are always runtime-PM-disabled and -suspended */
|
||||
pm_runtime_disable(dev);
|
||||
if (driver->supports_autosuspend)
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
|
||||
/* Undo any residual pm_autopm_get_interface_* calls */
|
||||
|
|
@ -437,14 +440,17 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|||
|
||||
iface->condition = USB_INTERFACE_BOUND;
|
||||
|
||||
/* Claimed interfaces are initially inactive (suspended). They are
|
||||
* runtime-PM-enabled only if the driver has autosuspend support.
|
||||
* They are sensitive to their children's power states.
|
||||
/* Claimed interfaces are initially inactive (suspended) and
|
||||
* runtime-PM-enabled, but only if the driver has autosuspend
|
||||
* support. Otherwise they are marked active, to prevent the
|
||||
* device from being autosuspended, but left disabled. In either
|
||||
* case they are sensitive to their children's power states.
|
||||
*/
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_suspend_ignore_children(dev, false);
|
||||
if (driver->supports_autosuspend)
|
||||
pm_runtime_enable(dev);
|
||||
else
|
||||
pm_runtime_set_active(dev);
|
||||
|
||||
/* if interface was already added, bind now; else let
|
||||
* the future device_add() bind it, bypassing probe()
|
||||
|
|
@ -1355,13 +1361,9 @@ int usb_resume(struct device *dev, pm_message_t msg)
|
|||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*/
|
||||
int usb_enable_autosuspend(struct usb_device *udev)
|
||||
void usb_enable_autosuspend(struct usb_device *udev)
|
||||
{
|
||||
if (udev->autosuspend_disabled) {
|
||||
udev->autosuspend_disabled = 0;
|
||||
usb_autosuspend_device(udev);
|
||||
}
|
||||
return 0;
|
||||
pm_runtime_allow(&udev->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_enable_autosuspend);
|
||||
|
||||
|
|
@ -1374,16 +1376,9 @@ EXPORT_SYMBOL_GPL(usb_enable_autosuspend);
|
|||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*/
|
||||
int usb_disable_autosuspend(struct usb_device *udev)
|
||||
void usb_disable_autosuspend(struct usb_device *udev)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!udev->autosuspend_disabled) {
|
||||
rc = usb_autoresume_device(udev);
|
||||
if (rc == 0)
|
||||
udev->autosuspend_disabled = 1;
|
||||
}
|
||||
return rc;
|
||||
pm_runtime_forbid(&udev->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_disable_autosuspend);
|
||||
|
||||
|
|
@ -1485,9 +1480,6 @@ int usb_autoresume_device(struct usb_device *udev)
|
|||
* 0, a delayed autosuspend request for @intf's device is attempted. The
|
||||
* attempt may fail (see autosuspend_check()).
|
||||
*
|
||||
* If the driver has set @intf->needs_remote_wakeup then autosuspend will
|
||||
* take place only if the device's remote-wakeup facility is enabled.
|
||||
*
|
||||
* This routine can run only in process context.
|
||||
*/
|
||||
void usb_autopm_put_interface(struct usb_interface *intf)
|
||||
|
|
@ -1530,7 +1522,7 @@ void usb_autopm_put_interface_async(struct usb_interface *intf)
|
|||
atomic_dec(&intf->pm_usage_cnt);
|
||||
pm_runtime_put_noidle(&intf->dev);
|
||||
|
||||
if (!udev->autosuspend_disabled) {
|
||||
if (udev->dev.power.runtime_auto) {
|
||||
/* Optimization: Don't schedule a delayed autosuspend if
|
||||
* the timer is already running and the expiration time
|
||||
* wouldn't change.
|
||||
|
|
@ -1672,14 +1664,14 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface_no_resume);
|
|||
/* Internal routine to check whether we may autosuspend a device. */
|
||||
static int autosuspend_check(struct usb_device *udev)
|
||||
{
|
||||
int i;
|
||||
int w, i;
|
||||
struct usb_interface *intf;
|
||||
unsigned long suspend_time, j;
|
||||
|
||||
/* Fail if autosuspend is disabled, or any interfaces are in use, or
|
||||
* any interface drivers require remote wakeup but it isn't available.
|
||||
*/
|
||||
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
|
||||
w = 0;
|
||||
if (udev->actconfig) {
|
||||
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
|
||||
intf = udev->actconfig->interface[i];
|
||||
|
|
@ -1693,12 +1685,7 @@ static int autosuspend_check(struct usb_device *udev)
|
|||
continue;
|
||||
if (atomic_read(&intf->dev.power.usage_count) > 0)
|
||||
return -EBUSY;
|
||||
if (intf->needs_remote_wakeup &&
|
||||
!udev->do_remote_wakeup) {
|
||||
dev_dbg(&udev->dev, "remote wakeup needed "
|
||||
"for autosuspend\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
w |= intf->needs_remote_wakeup;
|
||||
|
||||
/* Don't allow autosuspend if the device will need
|
||||
* a reset-resume and any of its interface drivers
|
||||
|
|
@ -1714,6 +1701,11 @@ static int autosuspend_check(struct usb_device *udev)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (w && !device_can_wakeup(&udev->dev)) {
|
||||
dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
udev->do_remote_wakeup = w;
|
||||
|
||||
/* If everything is okay but the device hasn't been idle for long
|
||||
* enough, queue a delayed autosuspend request.
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
|
||||
static inline const char *plural(int n)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
|
@ -33,7 +34,6 @@
|
|||
#endif
|
||||
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
|
||||
|
||||
/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
|
||||
|
|
|
|||
|
|
@ -38,14 +38,12 @@
|
|||
#include <asm/unaligned.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
#include "hub.h"
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
@ -1261,6 +1259,51 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle,
|
|||
*dma_handle = 0;
|
||||
}
|
||||
|
||||
static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
enum dma_data_direction dir;
|
||||
|
||||
if (urb->transfer_flags & URB_SETUP_MAP_SINGLE)
|
||||
dma_unmap_single(hcd->self.controller,
|
||||
urb->setup_dma,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
DMA_TO_DEVICE);
|
||||
else if (urb->transfer_flags & URB_SETUP_MAP_LOCAL)
|
||||
hcd_free_coherent(urb->dev->bus,
|
||||
&urb->setup_dma,
|
||||
(void **) &urb->setup_packet,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||
if (urb->transfer_flags & URB_DMA_MAP_SG)
|
||||
dma_unmap_sg(hcd->self.controller,
|
||||
urb->sg,
|
||||
urb->num_sgs,
|
||||
dir);
|
||||
else if (urb->transfer_flags & URB_DMA_MAP_PAGE)
|
||||
dma_unmap_page(hcd->self.controller,
|
||||
urb->transfer_dma,
|
||||
urb->transfer_buffer_length,
|
||||
dir);
|
||||
else if (urb->transfer_flags & URB_DMA_MAP_SINGLE)
|
||||
dma_unmap_single(hcd->self.controller,
|
||||
urb->transfer_dma,
|
||||
urb->transfer_buffer_length,
|
||||
dir);
|
||||
else if (urb->transfer_flags & URB_MAP_LOCAL)
|
||||
hcd_free_coherent(urb->dev->bus,
|
||||
&urb->transfer_dma,
|
||||
&urb->transfer_buffer,
|
||||
urb->transfer_buffer_length,
|
||||
dir);
|
||||
|
||||
/* Make it safe to call this routine more than once */
|
||||
urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
|
||||
URB_DMA_MAP_SG | URB_DMA_MAP_PAGE |
|
||||
URB_DMA_MAP_SINGLE | URB_MAP_LOCAL);
|
||||
}
|
||||
|
||||
static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
|
|
@ -1272,11 +1315,8 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
|||
* unless it uses pio or talks to another transport,
|
||||
* or uses the provided scatter gather list for bulk.
|
||||
*/
|
||||
if (is_root_hub(urb->dev))
|
||||
return 0;
|
||||
|
||||
if (usb_endpoint_xfer_control(&urb->ep->desc)
|
||||
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
|
||||
if (usb_endpoint_xfer_control(&urb->ep->desc)) {
|
||||
if (hcd->self.uses_dma) {
|
||||
urb->setup_dma = dma_map_single(
|
||||
hcd->self.controller,
|
||||
|
|
@ -1286,27 +1326,64 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
|||
if (dma_mapping_error(hcd->self.controller,
|
||||
urb->setup_dma))
|
||||
return -EAGAIN;
|
||||
} else if (hcd->driver->flags & HCD_LOCAL_MEM)
|
||||
urb->transfer_flags |= URB_SETUP_MAP_SINGLE;
|
||||
} else if (hcd->driver->flags & HCD_LOCAL_MEM) {
|
||||
ret = hcd_alloc_coherent(
|
||||
urb->dev->bus, mem_flags,
|
||||
&urb->setup_dma,
|
||||
(void **)&urb->setup_packet,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
DMA_TO_DEVICE);
|
||||
if (ret)
|
||||
return ret;
|
||||
urb->transfer_flags |= URB_SETUP_MAP_LOCAL;
|
||||
}
|
||||
}
|
||||
|
||||
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||
if (ret == 0 && urb->transfer_buffer_length != 0
|
||||
if (urb->transfer_buffer_length != 0
|
||||
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
|
||||
if (hcd->self.uses_dma) {
|
||||
urb->transfer_dma = dma_map_single (
|
||||
hcd->self.controller,
|
||||
urb->transfer_buffer,
|
||||
urb->transfer_buffer_length,
|
||||
dir);
|
||||
if (dma_mapping_error(hcd->self.controller,
|
||||
if (urb->num_sgs) {
|
||||
int n = dma_map_sg(
|
||||
hcd->self.controller,
|
||||
urb->sg,
|
||||
urb->num_sgs,
|
||||
dir);
|
||||
if (n <= 0)
|
||||
ret = -EAGAIN;
|
||||
else
|
||||
urb->transfer_flags |= URB_DMA_MAP_SG;
|
||||
if (n != urb->num_sgs) {
|
||||
urb->num_sgs = n;
|
||||
urb->transfer_flags |=
|
||||
URB_DMA_SG_COMBINED;
|
||||
}
|
||||
} else if (urb->sg) {
|
||||
struct scatterlist *sg = urb->sg;
|
||||
urb->transfer_dma = dma_map_page(
|
||||
hcd->self.controller,
|
||||
sg_page(sg),
|
||||
sg->offset,
|
||||
urb->transfer_buffer_length,
|
||||
dir);
|
||||
if (dma_mapping_error(hcd->self.controller,
|
||||
urb->transfer_dma))
|
||||
return -EAGAIN;
|
||||
ret = -EAGAIN;
|
||||
else
|
||||
urb->transfer_flags |= URB_DMA_MAP_PAGE;
|
||||
} else {
|
||||
urb->transfer_dma = dma_map_single(
|
||||
hcd->self.controller,
|
||||
urb->transfer_buffer,
|
||||
urb->transfer_buffer_length,
|
||||
dir);
|
||||
if (dma_mapping_error(hcd->self.controller,
|
||||
urb->transfer_dma))
|
||||
ret = -EAGAIN;
|
||||
else
|
||||
urb->transfer_flags |= URB_DMA_MAP_SINGLE;
|
||||
}
|
||||
} else if (hcd->driver->flags & HCD_LOCAL_MEM) {
|
||||
ret = hcd_alloc_coherent(
|
||||
urb->dev->bus, mem_flags,
|
||||
|
|
@ -1314,55 +1391,16 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
|||
&urb->transfer_buffer,
|
||||
urb->transfer_buffer_length,
|
||||
dir);
|
||||
|
||||
if (ret && usb_endpoint_xfer_control(&urb->ep->desc)
|
||||
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
|
||||
hcd_free_coherent(urb->dev->bus,
|
||||
&urb->setup_dma,
|
||||
(void **)&urb->setup_packet,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
DMA_TO_DEVICE);
|
||||
if (ret == 0)
|
||||
urb->transfer_flags |= URB_MAP_LOCAL;
|
||||
}
|
||||
if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE |
|
||||
URB_SETUP_MAP_LOCAL)))
|
||||
unmap_urb_for_dma(hcd, urb);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
enum dma_data_direction dir;
|
||||
|
||||
if (is_root_hub(urb->dev))
|
||||
return;
|
||||
|
||||
if (usb_endpoint_xfer_control(&urb->ep->desc)
|
||||
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
|
||||
if (hcd->self.uses_dma)
|
||||
dma_unmap_single(hcd->self.controller, urb->setup_dma,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
DMA_TO_DEVICE);
|
||||
else if (hcd->driver->flags & HCD_LOCAL_MEM)
|
||||
hcd_free_coherent(urb->dev->bus, &urb->setup_dma,
|
||||
(void **)&urb->setup_packet,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||
if (urb->transfer_buffer_length != 0
|
||||
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
|
||||
if (hcd->self.uses_dma)
|
||||
dma_unmap_single(hcd->self.controller,
|
||||
urb->transfer_dma,
|
||||
urb->transfer_buffer_length,
|
||||
dir);
|
||||
else if (hcd->driver->flags & HCD_LOCAL_MEM)
|
||||
hcd_free_coherent(urb->dev->bus, &urb->transfer_dma,
|
||||
&urb->transfer_buffer,
|
||||
urb->transfer_buffer_length,
|
||||
dir);
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* may be called in any context with a valid urb->dev usecount
|
||||
|
|
@ -1391,21 +1429,20 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
|
|||
* URBs must be submitted in process context with interrupts
|
||||
* enabled.
|
||||
*/
|
||||
status = map_urb_for_dma(hcd, urb, mem_flags);
|
||||
if (unlikely(status)) {
|
||||
usbmon_urb_submit_error(&hcd->self, urb, status);
|
||||
goto error;
|
||||
|
||||
if (is_root_hub(urb->dev)) {
|
||||
status = rh_urb_enqueue(hcd, urb);
|
||||
} else {
|
||||
status = map_urb_for_dma(hcd, urb, mem_flags);
|
||||
if (likely(status == 0)) {
|
||||
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
|
||||
if (unlikely(status))
|
||||
unmap_urb_for_dma(hcd, urb);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_root_hub(urb->dev))
|
||||
status = rh_urb_enqueue(hcd, urb);
|
||||
else
|
||||
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
|
||||
|
||||
if (unlikely(status)) {
|
||||
usbmon_urb_submit_error(&hcd->self, urb, status);
|
||||
unmap_urb_for_dma(hcd, urb);
|
||||
error:
|
||||
urb->hcpriv = NULL;
|
||||
INIT_LIST_HEAD(&urb->urb_list);
|
||||
atomic_dec(&urb->use_count);
|
||||
|
|
@ -1775,6 +1812,75 @@ void usb_hcd_reset_endpoint(struct usb_device *udev,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_alloc_streams - allocate bulk endpoint stream IDs.
|
||||
* @interface: alternate setting that includes all endpoints.
|
||||
* @eps: array of endpoints that need streams.
|
||||
* @num_eps: number of endpoints in the array.
|
||||
* @num_streams: number of streams to allocate.
|
||||
* @mem_flags: flags hcd should use to allocate memory.
|
||||
*
|
||||
* Sets up a group of bulk endpoints to have num_streams stream IDs available.
|
||||
* Drivers may queue multiple transfers to different stream IDs, which may
|
||||
* complete in a different order than they were queued.
|
||||
*/
|
||||
int usb_alloc_streams(struct usb_interface *interface,
|
||||
struct usb_host_endpoint **eps, unsigned int num_eps,
|
||||
unsigned int num_streams, gfp_t mem_flags)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct usb_device *dev;
|
||||
int i;
|
||||
|
||||
dev = interface_to_usbdev(interface);
|
||||
hcd = bus_to_hcd(dev->bus);
|
||||
if (!hcd->driver->alloc_streams || !hcd->driver->free_streams)
|
||||
return -EINVAL;
|
||||
if (dev->speed != USB_SPEED_SUPER)
|
||||
return -EINVAL;
|
||||
|
||||
/* Streams only apply to bulk endpoints. */
|
||||
for (i = 0; i < num_eps; i++)
|
||||
if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
|
||||
return -EINVAL;
|
||||
|
||||
return hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
|
||||
num_streams, mem_flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_alloc_streams);
|
||||
|
||||
/**
|
||||
* usb_free_streams - free bulk endpoint stream IDs.
|
||||
* @interface: alternate setting that includes all endpoints.
|
||||
* @eps: array of endpoints to remove streams from.
|
||||
* @num_eps: number of endpoints in the array.
|
||||
* @mem_flags: flags hcd should use to allocate memory.
|
||||
*
|
||||
* Reverts a group of bulk endpoints back to not using stream IDs.
|
||||
* Can fail if we are given bad arguments, or HCD is broken.
|
||||
*/
|
||||
void usb_free_streams(struct usb_interface *interface,
|
||||
struct usb_host_endpoint **eps, unsigned int num_eps,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct usb_device *dev;
|
||||
int i;
|
||||
|
||||
dev = interface_to_usbdev(interface);
|
||||
hcd = bus_to_hcd(dev->bus);
|
||||
if (dev->speed != USB_SPEED_SUPER)
|
||||
return;
|
||||
|
||||
/* Streams only apply to bulk endpoints. */
|
||||
for (i = 0; i < num_eps; i++)
|
||||
if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
|
||||
return;
|
||||
|
||||
hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_free_streams);
|
||||
|
||||
/* Protect against drivers that try to unlink URBs after the device
|
||||
* is gone, by waiting until all unlinks for @udev are finished.
|
||||
* Since we don't currently track URBs by device, simply wait until
|
||||
|
|
|
|||
|
|
@ -1,578 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2001-2002 by David Brownell
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __USB_CORE_HCD_H
|
||||
#define __USB_CORE_HCD_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
#define MAX_TOPO_LEVEL 6
|
||||
|
||||
/* This file contains declarations of usbcore internals that are mostly
|
||||
* used or exposed by Host Controller Drivers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* USB Packet IDs (PIDs)
|
||||
*/
|
||||
#define USB_PID_EXT 0xf0 /* USB 2.0 LPM ECN */
|
||||
#define USB_PID_OUT 0xe1
|
||||
#define USB_PID_ACK 0xd2
|
||||
#define USB_PID_DATA0 0xc3
|
||||
#define USB_PID_PING 0xb4 /* USB 2.0 */
|
||||
#define USB_PID_SOF 0xa5
|
||||
#define USB_PID_NYET 0x96 /* USB 2.0 */
|
||||
#define USB_PID_DATA2 0x87 /* USB 2.0 */
|
||||
#define USB_PID_SPLIT 0x78 /* USB 2.0 */
|
||||
#define USB_PID_IN 0x69
|
||||
#define USB_PID_NAK 0x5a
|
||||
#define USB_PID_DATA1 0x4b
|
||||
#define USB_PID_PREAMBLE 0x3c /* Token mode */
|
||||
#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */
|
||||
#define USB_PID_SETUP 0x2d
|
||||
#define USB_PID_STALL 0x1e
|
||||
#define USB_PID_MDATA 0x0f /* USB 2.0 */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* USB Host Controller Driver (usb_hcd) framework
|
||||
*
|
||||
* Since "struct usb_bus" is so thin, you can't share much code in it.
|
||||
* This framework is a layer over that, and should be more sharable.
|
||||
*
|
||||
* @authorized_default: Specifies if new devices are authorized to
|
||||
* connect by default or they require explicit
|
||||
* user space authorization; this bit is settable
|
||||
* through /sys/class/usb_host/X/authorized_default.
|
||||
* For the rest is RO, so we don't lock to r/w it.
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
struct usb_hcd {
|
||||
|
||||
/*
|
||||
* housekeeping
|
||||
*/
|
||||
struct usb_bus self; /* hcd is-a bus */
|
||||
struct kref kref; /* reference counter */
|
||||
|
||||
const char *product_desc; /* product/vendor string */
|
||||
char irq_descr[24]; /* driver + bus # */
|
||||
|
||||
struct timer_list rh_timer; /* drives root-hub polling */
|
||||
struct urb *status_urb; /* the current status urb */
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
struct work_struct wakeup_work; /* for remote wakeup */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* hardware info/state
|
||||
*/
|
||||
const struct hc_driver *driver; /* hw-specific hooks */
|
||||
|
||||
/* Flags that need to be manipulated atomically */
|
||||
unsigned long flags;
|
||||
#define HCD_FLAG_HW_ACCESSIBLE 0x00000001
|
||||
#define HCD_FLAG_SAW_IRQ 0x00000002
|
||||
|
||||
unsigned rh_registered:1;/* is root hub registered? */
|
||||
|
||||
/* The next flag is a stopgap, to be removed when all the HCDs
|
||||
* support the new root-hub polling mechanism. */
|
||||
unsigned uses_new_polling:1;
|
||||
unsigned poll_rh:1; /* poll for rh status? */
|
||||
unsigned poll_pending:1; /* status has changed? */
|
||||
unsigned wireless:1; /* Wireless USB HCD */
|
||||
unsigned authorized_default:1;
|
||||
unsigned has_tt:1; /* Integrated TT in root hub */
|
||||
|
||||
int irq; /* irq allocated */
|
||||
void __iomem *regs; /* device memory/io */
|
||||
u64 rsrc_start; /* memory/io resource start */
|
||||
u64 rsrc_len; /* memory/io resource length */
|
||||
unsigned power_budget; /* in mA, 0 = no limit */
|
||||
|
||||
/* bandwidth_mutex should be taken before adding or removing
|
||||
* any new bus bandwidth constraints:
|
||||
* 1. Before adding a configuration for a new device.
|
||||
* 2. Before removing the configuration to put the device into
|
||||
* the addressed state.
|
||||
* 3. Before selecting a different configuration.
|
||||
* 4. Before selecting an alternate interface setting.
|
||||
*
|
||||
* bandwidth_mutex should be dropped after a successful control message
|
||||
* to the device, or resetting the bandwidth after a failed attempt.
|
||||
*/
|
||||
struct mutex bandwidth_mutex;
|
||||
|
||||
|
||||
#define HCD_BUFFER_POOLS 4
|
||||
struct dma_pool *pool [HCD_BUFFER_POOLS];
|
||||
|
||||
int state;
|
||||
# define __ACTIVE 0x01
|
||||
# define __SUSPEND 0x04
|
||||
# define __TRANSIENT 0x80
|
||||
|
||||
# define HC_STATE_HALT 0
|
||||
# define HC_STATE_RUNNING (__ACTIVE)
|
||||
# define HC_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)
|
||||
# define HC_STATE_RESUMING (__SUSPEND|__TRANSIENT)
|
||||
# define HC_STATE_SUSPENDED (__SUSPEND)
|
||||
|
||||
#define HC_IS_RUNNING(state) ((state) & __ACTIVE)
|
||||
#define HC_IS_SUSPENDED(state) ((state) & __SUSPEND)
|
||||
|
||||
/* more shared queuing code would be good; it should support
|
||||
* smarter scheduling, handle transaction translators, etc;
|
||||
* input size of periodic table to an interrupt scheduler.
|
||||
* (ohci 32, uhci 1024, ehci 256/512/1024).
|
||||
*/
|
||||
|
||||
/* The HC driver's private data is stored at the end of
|
||||
* this structure.
|
||||
*/
|
||||
unsigned long hcd_priv[0]
|
||||
__attribute__ ((aligned(sizeof(unsigned long))));
|
||||
};
|
||||
|
||||
/* 2.4 does this a bit differently ... */
|
||||
static inline struct usb_bus *hcd_to_bus(struct usb_hcd *hcd)
|
||||
{
|
||||
return &hcd->self;
|
||||
}
|
||||
|
||||
static inline struct usb_hcd *bus_to_hcd(struct usb_bus *bus)
|
||||
{
|
||||
return container_of(bus, struct usb_hcd, self);
|
||||
}
|
||||
|
||||
struct hcd_timeout { /* timeouts we allocate */
|
||||
struct list_head timeout_list;
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
struct hc_driver {
|
||||
const char *description; /* "ehci-hcd" etc */
|
||||
const char *product_desc; /* product/vendor string */
|
||||
size_t hcd_priv_size; /* size of private data */
|
||||
|
||||
/* irq handler */
|
||||
irqreturn_t (*irq) (struct usb_hcd *hcd);
|
||||
|
||||
int flags;
|
||||
#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
|
||||
#define HCD_LOCAL_MEM 0x0002 /* HC needs local memory */
|
||||
#define HCD_USB11 0x0010 /* USB 1.1 */
|
||||
#define HCD_USB2 0x0020 /* USB 2.0 */
|
||||
#define HCD_USB3 0x0040 /* USB 3.0 */
|
||||
#define HCD_MASK 0x0070
|
||||
|
||||
/* called to init HCD and root hub */
|
||||
int (*reset) (struct usb_hcd *hcd);
|
||||
int (*start) (struct usb_hcd *hcd);
|
||||
|
||||
/* NOTE: these suspend/resume calls relate to the HC as
|
||||
* a whole, not just the root hub; they're for PCI bus glue.
|
||||
*/
|
||||
/* called after suspending the hub, before entering D3 etc */
|
||||
int (*pci_suspend)(struct usb_hcd *hcd);
|
||||
|
||||
/* called after entering D0 (etc), before resuming the hub */
|
||||
int (*pci_resume)(struct usb_hcd *hcd, bool hibernated);
|
||||
|
||||
/* cleanly make HCD stop writing memory and doing I/O */
|
||||
void (*stop) (struct usb_hcd *hcd);
|
||||
|
||||
/* shutdown HCD */
|
||||
void (*shutdown) (struct usb_hcd *hcd);
|
||||
|
||||
/* return current frame number */
|
||||
int (*get_frame_number) (struct usb_hcd *hcd);
|
||||
|
||||
/* manage i/o requests, device state */
|
||||
int (*urb_enqueue)(struct usb_hcd *hcd,
|
||||
struct urb *urb, gfp_t mem_flags);
|
||||
int (*urb_dequeue)(struct usb_hcd *hcd,
|
||||
struct urb *urb, int status);
|
||||
|
||||
/* hw synch, freeing endpoint resources that urb_dequeue can't */
|
||||
void (*endpoint_disable)(struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *ep);
|
||||
|
||||
/* (optional) reset any endpoint state such as sequence number
|
||||
and current window */
|
||||
void (*endpoint_reset)(struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *ep);
|
||||
|
||||
/* root hub support */
|
||||
int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
|
||||
int (*hub_control) (struct usb_hcd *hcd,
|
||||
u16 typeReq, u16 wValue, u16 wIndex,
|
||||
char *buf, u16 wLength);
|
||||
int (*bus_suspend)(struct usb_hcd *);
|
||||
int (*bus_resume)(struct usb_hcd *);
|
||||
int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
|
||||
|
||||
/* force handover of high-speed port to full-speed companion */
|
||||
void (*relinquish_port)(struct usb_hcd *, int);
|
||||
/* has a port been handed over to a companion? */
|
||||
int (*port_handed_over)(struct usb_hcd *, int);
|
||||
|
||||
/* CLEAR_TT_BUFFER completion callback */
|
||||
void (*clear_tt_buffer_complete)(struct usb_hcd *,
|
||||
struct usb_host_endpoint *);
|
||||
|
||||
/* xHCI specific functions */
|
||||
/* Called by usb_alloc_dev to alloc HC device structures */
|
||||
int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
|
||||
/* Called by usb_disconnect to free HC device structures */
|
||||
void (*free_dev)(struct usb_hcd *, struct usb_device *);
|
||||
|
||||
/* Bandwidth computation functions */
|
||||
/* Note that add_endpoint() can only be called once per endpoint before
|
||||
* check_bandwidth() or reset_bandwidth() must be called.
|
||||
* drop_endpoint() can only be called once per endpoint also.
|
||||
* A call to xhci_drop_endpoint() followed by a call to xhci_add_endpoint() will
|
||||
* add the endpoint to the schedule with possibly new parameters denoted by a
|
||||
* different endpoint descriptor in usb_host_endpoint.
|
||||
* A call to xhci_add_endpoint() followed by a call to xhci_drop_endpoint() is
|
||||
* not allowed.
|
||||
*/
|
||||
/* Allocate endpoint resources and add them to a new schedule */
|
||||
int (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
|
||||
/* Drop an endpoint from a new schedule */
|
||||
int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
|
||||
/* Check that a new hardware configuration, set using
|
||||
* endpoint_enable and endpoint_disable, does not exceed bus
|
||||
* bandwidth. This must be called before any set configuration
|
||||
* or set interface requests are sent to the device.
|
||||
*/
|
||||
int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
|
||||
/* Reset the device schedule to the last known good schedule,
|
||||
* which was set from a previous successful call to
|
||||
* check_bandwidth(). This reverts any add_endpoint() and
|
||||
* drop_endpoint() calls since that last successful call.
|
||||
* Used for when a check_bandwidth() call fails due to resource
|
||||
* or bandwidth constraints.
|
||||
*/
|
||||
void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
|
||||
/* Returns the hardware-chosen device address */
|
||||
int (*address_device)(struct usb_hcd *, struct usb_device *udev);
|
||||
/* Notifies the HCD after a hub descriptor is fetched.
|
||||
* Will block.
|
||||
*/
|
||||
int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev,
|
||||
struct usb_tt *tt, gfp_t mem_flags);
|
||||
int (*reset_device)(struct usb_hcd *, struct usb_device *);
|
||||
};
|
||||
|
||||
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
|
||||
extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
|
||||
int status);
|
||||
extern void usb_hcd_unlink_urb_from_ep(struct usb_hcd *hcd, struct urb *urb);
|
||||
|
||||
extern int usb_hcd_submit_urb(struct urb *urb, gfp_t mem_flags);
|
||||
extern int usb_hcd_unlink_urb(struct urb *urb, int status);
|
||||
extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb,
|
||||
int status);
|
||||
extern void usb_hcd_flush_endpoint(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep);
|
||||
extern void usb_hcd_disable_endpoint(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep);
|
||||
extern void usb_hcd_reset_endpoint(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep);
|
||||
extern void usb_hcd_synchronize_unlinks(struct usb_device *udev);
|
||||
extern int usb_hcd_alloc_bandwidth(struct usb_device *udev,
|
||||
struct usb_host_config *new_config,
|
||||
struct usb_host_interface *old_alt,
|
||||
struct usb_host_interface *new_alt);
|
||||
extern int usb_hcd_get_frame_number(struct usb_device *udev);
|
||||
|
||||
extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
|
||||
struct device *dev, const char *bus_name);
|
||||
extern struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd);
|
||||
extern void usb_put_hcd(struct usb_hcd *hcd);
|
||||
extern int usb_add_hcd(struct usb_hcd *hcd,
|
||||
unsigned int irqnum, unsigned long irqflags);
|
||||
extern void usb_remove_hcd(struct usb_hcd *hcd);
|
||||
|
||||
struct platform_device;
|
||||
extern void usb_hcd_platform_shutdown(struct platform_device *dev);
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
struct pci_dev;
|
||||
struct pci_device_id;
|
||||
extern int usb_hcd_pci_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id);
|
||||
extern void usb_hcd_pci_remove(struct pci_dev *dev);
|
||||
extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
|
||||
#endif
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
/* pci-ish (pdev null is ok) buffer alloc/mapping support */
|
||||
int hcd_buffer_create(struct usb_hcd *hcd);
|
||||
void hcd_buffer_destroy(struct usb_hcd *hcd);
|
||||
|
||||
void *hcd_buffer_alloc(struct usb_bus *bus, size_t size,
|
||||
gfp_t mem_flags, dma_addr_t *dma);
|
||||
void hcd_buffer_free(struct usb_bus *bus, size_t size,
|
||||
void *addr, dma_addr_t dma);
|
||||
|
||||
/* generic bus glue, needed for host controllers that don't use PCI */
|
||||
extern irqreturn_t usb_hcd_irq(int irq, void *__hcd);
|
||||
|
||||
extern void usb_hc_died(struct usb_hcd *hcd);
|
||||
extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd);
|
||||
|
||||
/* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */
|
||||
#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)
|
||||
#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << (ep)))
|
||||
#define usb_settoggle(dev, ep, out, bit) \
|
||||
((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | \
|
||||
((bit) << (ep)))
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/* Enumeration is only for the hub driver, or HCD virtual root hubs */
|
||||
extern struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
||||
struct usb_bus *, unsigned port);
|
||||
extern int usb_new_device(struct usb_device *dev);
|
||||
extern void usb_disconnect(struct usb_device **);
|
||||
|
||||
extern int usb_get_configuration(struct usb_device *dev);
|
||||
extern void usb_destroy_configuration(struct usb_device *dev);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* HCD Root Hub support
|
||||
*/
|
||||
|
||||
#include "hub.h"
|
||||
|
||||
/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
|
||||
#define DeviceRequest \
|
||||
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
#define DeviceOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
|
||||
#define InterfaceRequest \
|
||||
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
|
||||
#define EndpointRequest \
|
||||
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
#define EndpointOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
|
||||
/* class requests from the USB 2.0 hub spec, table 11-15 */
|
||||
/* GetBusState and SetHubDescriptor are optional, omitted */
|
||||
#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
|
||||
#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
|
||||
#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
|
||||
#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)
|
||||
#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)
|
||||
#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)
|
||||
#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Generic bandwidth allocation constants/support
|
||||
*/
|
||||
#define FRAME_TIME_USECS 1000L
|
||||
#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */
|
||||
/* Trying not to use worst-case bit-stuffing
|
||||
* of (7/6 * 8 * bytecount) = 9.33 * bytecount */
|
||||
/* bytecount = data payload byte count */
|
||||
|
||||
#define NS_TO_US(ns) ((ns + 500L) / 1000L)
|
||||
/* convert & round nanoseconds to microseconds */
|
||||
|
||||
|
||||
/*
|
||||
* Full/low speed bandwidth allocation constants/support.
|
||||
*/
|
||||
#define BW_HOST_DELAY 1000L /* nanoseconds */
|
||||
#define BW_HUB_LS_SETUP 333L /* nanoseconds */
|
||||
/* 4 full-speed bit times (est.) */
|
||||
|
||||
#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */
|
||||
#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)
|
||||
#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)
|
||||
|
||||
/*
|
||||
* Ceiling [nano/micro]seconds (typical) for that many bytes at high speed
|
||||
* ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
|
||||
* to preallocate bandwidth)
|
||||
*/
|
||||
#define USB2_HOST_DELAY 5 /* nsec, guess */
|
||||
#define HS_NSECS(bytes) (((55 * 8 * 2083) \
|
||||
+ (2083UL * (3 + BitTime(bytes))))/1000 \
|
||||
+ USB2_HOST_DELAY)
|
||||
#define HS_NSECS_ISO(bytes) (((38 * 8 * 2083) \
|
||||
+ (2083UL * (3 + BitTime(bytes))))/1000 \
|
||||
+ USB2_HOST_DELAY)
|
||||
#define HS_USECS(bytes) NS_TO_US (HS_NSECS(bytes))
|
||||
#define HS_USECS_ISO(bytes) NS_TO_US (HS_NSECS_ISO(bytes))
|
||||
|
||||
extern long usb_calc_bus_time(int speed, int is_input,
|
||||
int isoc, int bytecount);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
extern void usb_set_device_state(struct usb_device *udev,
|
||||
enum usb_device_state new_state);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* exported only within usbcore */
|
||||
|
||||
extern struct list_head usb_bus_list;
|
||||
extern struct mutex usb_bus_list_lock;
|
||||
extern wait_queue_head_t usb_kill_urb_queue;
|
||||
|
||||
extern int usb_find_interface_driver(struct usb_device *dev,
|
||||
struct usb_interface *interface);
|
||||
|
||||
#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN))
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern void usb_root_hub_lost_power(struct usb_device *rhdev);
|
||||
extern int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg);
|
||||
extern int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
extern void usb_hcd_resume_root_hub(struct usb_hcd *hcd);
|
||||
#else
|
||||
static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_USB_SUSPEND */
|
||||
|
||||
|
||||
/*
|
||||
* USB device fs stuff
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_DEVICEFS
|
||||
|
||||
/*
|
||||
* these are expected to be called from the USB core/hub thread
|
||||
* with the kernel lock held
|
||||
*/
|
||||
extern void usbfs_update_special(void);
|
||||
extern int usbfs_init(void);
|
||||
extern void usbfs_cleanup(void);
|
||||
|
||||
#else /* CONFIG_USB_DEVICEFS */
|
||||
|
||||
static inline void usbfs_update_special(void) {}
|
||||
static inline int usbfs_init(void) { return 0; }
|
||||
static inline void usbfs_cleanup(void) { }
|
||||
|
||||
#endif /* CONFIG_USB_DEVICEFS */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
|
||||
|
||||
struct usb_mon_operations {
|
||||
void (*urb_submit)(struct usb_bus *bus, struct urb *urb);
|
||||
void (*urb_submit_error)(struct usb_bus *bus, struct urb *urb, int err);
|
||||
void (*urb_complete)(struct usb_bus *bus, struct urb *urb, int status);
|
||||
/* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */
|
||||
};
|
||||
|
||||
extern struct usb_mon_operations *mon_ops;
|
||||
|
||||
static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb)
|
||||
{
|
||||
if (bus->monitored)
|
||||
(*mon_ops->urb_submit)(bus, urb);
|
||||
}
|
||||
|
||||
static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
|
||||
int error)
|
||||
{
|
||||
if (bus->monitored)
|
||||
(*mon_ops->urb_submit_error)(bus, urb, error);
|
||||
}
|
||||
|
||||
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,
|
||||
int status)
|
||||
{
|
||||
if (bus->monitored)
|
||||
(*mon_ops->urb_complete)(bus, urb, status);
|
||||
}
|
||||
|
||||
int usb_mon_register(struct usb_mon_operations *ops);
|
||||
void usb_mon_deregister(void);
|
||||
|
||||
#else
|
||||
|
||||
static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) {}
|
||||
static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
|
||||
int error) {}
|
||||
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,
|
||||
int status) {}
|
||||
|
||||
#endif /* CONFIG_USB_MON || CONFIG_USB_MON_MODULE */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
|
||||
/* bleech -- resurfaced in 2.4.11 or 2.4.12 */
|
||||
#define bitmap DeviceRemovable
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* random stuff */
|
||||
|
||||
#define RUN_CONTEXT (in_irq() ? "in_irq" \
|
||||
: (in_interrupt() ? "in_interrupt" : "can sleep"))
|
||||
|
||||
|
||||
/* This rwsem is for use only by the hub driver and ehci-hcd.
|
||||
* Nobody else should touch it.
|
||||
*/
|
||||
extern struct rw_semaphore ehci_cf_port_reset_rwsem;
|
||||
|
||||
/* Keep track of which host controller drivers are loaded */
|
||||
#define USB_UHCI_LOADED 0
|
||||
#define USB_OHCI_LOADED 1
|
||||
#define USB_EHCI_LOADED 2
|
||||
extern unsigned long usb_hcds_loaded;
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* __USB_CORE_HCD_H */
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/ioctl.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/freezer.h>
|
||||
|
|
@ -28,8 +29,6 @@
|
|||
#include <asm/byteorder.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
#include "hub.h"
|
||||
|
||||
/* if we are in debug mode, always announce new devices */
|
||||
#ifdef DEBUG
|
||||
|
|
@ -154,11 +153,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev);
|
|||
|
||||
static inline char *portspeed(int portstatus)
|
||||
{
|
||||
if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))
|
||||
if (portstatus & USB_PORT_STAT_HIGH_SPEED)
|
||||
return "480 Mb/s";
|
||||
else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
|
||||
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
|
||||
return "1.5 Mb/s";
|
||||
else if (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED))
|
||||
else if (portstatus & USB_PORT_STAT_SUPER_SPEED)
|
||||
return "5.0 Gb/s";
|
||||
else
|
||||
return "12 Mb/s";
|
||||
|
|
@ -745,8 +744,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
!(portstatus & USB_PORT_STAT_CONNECTION) ||
|
||||
!udev ||
|
||||
udev->state == USB_STATE_NOTATTACHED)) {
|
||||
clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
|
||||
portstatus &= ~USB_PORT_STAT_ENABLE;
|
||||
/*
|
||||
* USB3 protocol ports will automatically transition
|
||||
* to Enabled state when detect an USB3.0 device attach.
|
||||
* Do not disable USB3 protocol ports.
|
||||
* FIXME: USB3 root hub and external hubs are treated
|
||||
* differently here.
|
||||
*/
|
||||
if (hdev->descriptor.bDeviceProtocol != 3 ||
|
||||
(!hdev->parent &&
|
||||
!(portstatus & USB_PORT_STAT_SUPER_SPEED))) {
|
||||
clear_port_feature(hdev, port1,
|
||||
USB_PORT_FEAT_ENABLE);
|
||||
portstatus &= ~USB_PORT_STAT_ENABLE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear status-change flags; we'll debounce later */
|
||||
|
|
@ -1784,7 +1795,6 @@ int usb_new_device(struct usb_device *udev)
|
|||
* sysfs power/wakeup controls wakeup enabled/disabled
|
||||
*/
|
||||
device_init_wakeup(&udev->dev, 0);
|
||||
device_set_wakeup_enable(&udev->dev, 1);
|
||||
}
|
||||
|
||||
/* Tell the runtime-PM framework the device is active */
|
||||
|
|
@ -3038,7 +3048,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
|||
|
||||
/* maybe switch power back on (e.g. root hub was reset) */
|
||||
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
|
||||
&& !(portstatus & (1 << USB_PORT_FEAT_POWER)))
|
||||
&& !(portstatus & USB_PORT_STAT_POWER))
|
||||
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
|
||||
|
||||
if (portstatus & USB_PORT_STAT_ENABLE)
|
||||
|
|
@ -3076,7 +3086,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
|||
if (!(hcd->driver->flags & HCD_USB3))
|
||||
udev->speed = USB_SPEED_UNKNOWN;
|
||||
else if ((hdev->parent == NULL) &&
|
||||
(portstatus & (1 << USB_PORT_FEAT_SUPERSPEED)))
|
||||
(portstatus & USB_PORT_STAT_SUPER_SPEED))
|
||||
udev->speed = USB_SPEED_SUPER;
|
||||
else
|
||||
udev->speed = USB_SPEED_UNKNOWN;
|
||||
|
|
|
|||
|
|
@ -1,205 +0,0 @@
|
|||
#ifndef __LINUX_HUB_H
|
||||
#define __LINUX_HUB_H
|
||||
|
||||
/*
|
||||
* Hub protocol and driver data structures.
|
||||
*
|
||||
* Some of these are known to the "virtual root hub" code
|
||||
* in host controller drivers.
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/compiler.h> /* likely()/unlikely() */
|
||||
|
||||
/*
|
||||
* Hub request types
|
||||
*/
|
||||
|
||||
#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
|
||||
#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
|
||||
|
||||
/*
|
||||
* Hub class requests
|
||||
* See USB 2.0 spec Table 11-16
|
||||
*/
|
||||
#define HUB_CLEAR_TT_BUFFER 8
|
||||
#define HUB_RESET_TT 9
|
||||
#define HUB_GET_TT_STATE 10
|
||||
#define HUB_STOP_TT 11
|
||||
|
||||
/*
|
||||
* Hub Class feature numbers
|
||||
* See USB 2.0 spec Table 11-17
|
||||
*/
|
||||
#define C_HUB_LOCAL_POWER 0
|
||||
#define C_HUB_OVER_CURRENT 1
|
||||
|
||||
/*
|
||||
* Port feature numbers
|
||||
* See USB 2.0 spec Table 11-17
|
||||
*/
|
||||
#define USB_PORT_FEAT_CONNECTION 0
|
||||
#define USB_PORT_FEAT_ENABLE 1
|
||||
#define USB_PORT_FEAT_SUSPEND 2 /* L2 suspend */
|
||||
#define USB_PORT_FEAT_OVER_CURRENT 3
|
||||
#define USB_PORT_FEAT_RESET 4
|
||||
#define USB_PORT_FEAT_L1 5 /* L1 suspend */
|
||||
#define USB_PORT_FEAT_POWER 8
|
||||
#define USB_PORT_FEAT_LOWSPEED 9
|
||||
/* This value was never in Table 11-17 */
|
||||
#define USB_PORT_FEAT_HIGHSPEED 10
|
||||
/* This value is also fake */
|
||||
#define USB_PORT_FEAT_SUPERSPEED 11
|
||||
#define USB_PORT_FEAT_C_CONNECTION 16
|
||||
#define USB_PORT_FEAT_C_ENABLE 17
|
||||
#define USB_PORT_FEAT_C_SUSPEND 18
|
||||
#define USB_PORT_FEAT_C_OVER_CURRENT 19
|
||||
#define USB_PORT_FEAT_C_RESET 20
|
||||
#define USB_PORT_FEAT_TEST 21
|
||||
#define USB_PORT_FEAT_INDICATOR 22
|
||||
#define USB_PORT_FEAT_C_PORT_L1 23
|
||||
|
||||
/*
|
||||
* Hub Status and Hub Change results
|
||||
* See USB 2.0 spec Table 11-19 and Table 11-20
|
||||
*/
|
||||
struct usb_port_status {
|
||||
__le16 wPortStatus;
|
||||
__le16 wPortChange;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* wPortStatus bit field
|
||||
* See USB 2.0 spec Table 11-21
|
||||
*/
|
||||
#define USB_PORT_STAT_CONNECTION 0x0001
|
||||
#define USB_PORT_STAT_ENABLE 0x0002
|
||||
#define USB_PORT_STAT_SUSPEND 0x0004
|
||||
#define USB_PORT_STAT_OVERCURRENT 0x0008
|
||||
#define USB_PORT_STAT_RESET 0x0010
|
||||
#define USB_PORT_STAT_L1 0x0020
|
||||
/* bits 6 to 7 are reserved */
|
||||
#define USB_PORT_STAT_POWER 0x0100
|
||||
#define USB_PORT_STAT_LOW_SPEED 0x0200
|
||||
#define USB_PORT_STAT_HIGH_SPEED 0x0400
|
||||
#define USB_PORT_STAT_TEST 0x0800
|
||||
#define USB_PORT_STAT_INDICATOR 0x1000
|
||||
/* bits 13 to 15 are reserved */
|
||||
|
||||
/*
|
||||
* wPortChange bit field
|
||||
* See USB 2.0 spec Table 11-22
|
||||
* Bits 0 to 4 shown, bits 5 to 15 are reserved
|
||||
*/
|
||||
#define USB_PORT_STAT_C_CONNECTION 0x0001
|
||||
#define USB_PORT_STAT_C_ENABLE 0x0002
|
||||
#define USB_PORT_STAT_C_SUSPEND 0x0004
|
||||
#define USB_PORT_STAT_C_OVERCURRENT 0x0008
|
||||
#define USB_PORT_STAT_C_RESET 0x0010
|
||||
#define USB_PORT_STAT_C_L1 0x0020
|
||||
|
||||
/*
|
||||
* wHubCharacteristics (masks)
|
||||
* See USB 2.0 spec Table 11-13, offset 3
|
||||
*/
|
||||
#define HUB_CHAR_LPSM 0x0003 /* D1 .. D0 */
|
||||
#define HUB_CHAR_COMPOUND 0x0004 /* D2 */
|
||||
#define HUB_CHAR_OCPM 0x0018 /* D4 .. D3 */
|
||||
#define HUB_CHAR_TTTT 0x0060 /* D6 .. D5 */
|
||||
#define HUB_CHAR_PORTIND 0x0080 /* D7 */
|
||||
|
||||
struct usb_hub_status {
|
||||
__le16 wHubStatus;
|
||||
__le16 wHubChange;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* Hub Status & Hub Change bit masks
|
||||
* See USB 2.0 spec Table 11-19 and Table 11-20
|
||||
* Bits 0 and 1 for wHubStatus and wHubChange
|
||||
* Bits 2 to 15 are reserved for both
|
||||
*/
|
||||
#define HUB_STATUS_LOCAL_POWER 0x0001
|
||||
#define HUB_STATUS_OVERCURRENT 0x0002
|
||||
#define HUB_CHANGE_LOCAL_POWER 0x0001
|
||||
#define HUB_CHANGE_OVERCURRENT 0x0002
|
||||
|
||||
|
||||
/*
|
||||
* Hub descriptor
|
||||
* See USB 2.0 spec Table 11-13
|
||||
*/
|
||||
|
||||
#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
|
||||
#define USB_DT_HUB_NONVAR_SIZE 7
|
||||
|
||||
struct usb_hub_descriptor {
|
||||
__u8 bDescLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bNbrPorts;
|
||||
__le16 wHubCharacteristics;
|
||||
__u8 bPwrOn2PwrGood;
|
||||
__u8 bHubContrCurrent;
|
||||
/* add 1 bit for hub status change; round to bytes */
|
||||
__u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
|
||||
__u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* port indicator status selectors, tables 11-7 and 11-25 */
|
||||
#define HUB_LED_AUTO 0
|
||||
#define HUB_LED_AMBER 1
|
||||
#define HUB_LED_GREEN 2
|
||||
#define HUB_LED_OFF 3
|
||||
|
||||
enum hub_led_mode {
|
||||
INDICATOR_AUTO = 0,
|
||||
INDICATOR_CYCLE,
|
||||
/* software blinks for attention: software, hardware, reserved */
|
||||
INDICATOR_GREEN_BLINK, INDICATOR_GREEN_BLINK_OFF,
|
||||
INDICATOR_AMBER_BLINK, INDICATOR_AMBER_BLINK_OFF,
|
||||
INDICATOR_ALT_BLINK, INDICATOR_ALT_BLINK_OFF
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct usb_device;
|
||||
|
||||
/* Transaction Translator Think Times, in bits */
|
||||
#define HUB_TTTT_8_BITS 0x00
|
||||
#define HUB_TTTT_16_BITS 0x20
|
||||
#define HUB_TTTT_24_BITS 0x40
|
||||
#define HUB_TTTT_32_BITS 0x60
|
||||
|
||||
/*
|
||||
* As of USB 2.0, full/low speed devices are segregated into trees.
|
||||
* One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
|
||||
* The other type grows from high speed hubs when they connect to
|
||||
* full/low speed devices using "Transaction Translators" (TTs).
|
||||
*
|
||||
* TTs should only be known to the hub driver, and high speed bus
|
||||
* drivers (only EHCI for now). They affect periodic scheduling and
|
||||
* sometimes control/bulk error recovery.
|
||||
*/
|
||||
struct usb_tt {
|
||||
struct usb_device *hub; /* upstream highspeed hub */
|
||||
int multi; /* true means one TT per port */
|
||||
unsigned think_time; /* think time in ns */
|
||||
|
||||
/* for control/bulk error recovery (CLEAR_TT_BUFFER) */
|
||||
spinlock_t lock;
|
||||
struct list_head clear_list; /* of usb_tt_clear */
|
||||
struct work_struct clear_work;
|
||||
};
|
||||
|
||||
struct usb_tt_clear {
|
||||
struct list_head clear_list;
|
||||
unsigned tt;
|
||||
u16 devinfo;
|
||||
struct usb_hcd *hcd;
|
||||
struct usb_host_endpoint *ep;
|
||||
};
|
||||
|
||||
extern int usb_hub_clear_tt_buffer(struct urb *urb);
|
||||
extern void usb_ep0_reinit(struct usb_device *);
|
||||
|
||||
#endif /* __LINUX_HUB_H */
|
||||
|
|
@ -40,9 +40,9 @@
|
|||
#include <linux/notifier.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
|
||||
#define USBFS_DEFAULT_DEVMODE (S_IWUSR | S_IRUGO)
|
||||
#define USBFS_DEFAULT_BUSMODE (S_IXUGO | S_IRUGO)
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/usb/hcd.h> /* for usbcore internals */
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "hcd.h" /* for usbcore internals */
|
||||
#include "usb.h"
|
||||
|
||||
static void cancel_async_set_config(struct usb_device *udev);
|
||||
|
|
@ -226,8 +226,7 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
|
|||
struct urb *urb;
|
||||
struct usb_host_endpoint *ep;
|
||||
|
||||
ep = (usb_pipein(pipe) ? usb_dev->ep_in : usb_dev->ep_out)
|
||||
[usb_pipeendpoint(pipe)];
|
||||
ep = usb_pipe_endpoint(usb_dev, pipe);
|
||||
if (!ep || len < 0)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -259,9 +258,6 @@ static void sg_clean(struct usb_sg_request *io)
|
|||
kfree(io->urbs);
|
||||
io->urbs = NULL;
|
||||
}
|
||||
if (io->dev->dev.dma_mask != NULL)
|
||||
usb_buffer_unmap_sg(io->dev, usb_pipein(io->pipe),
|
||||
io->sg, io->nents);
|
||||
io->dev = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -364,7 +360,6 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
|
|||
{
|
||||
int i;
|
||||
int urb_flags;
|
||||
int dma;
|
||||
int use_sg;
|
||||
|
||||
if (!io || !dev || !sg
|
||||
|
|
@ -376,114 +371,76 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
|
|||
spin_lock_init(&io->lock);
|
||||
io->dev = dev;
|
||||
io->pipe = pipe;
|
||||
io->sg = sg;
|
||||
io->nents = nents;
|
||||
|
||||
/* not all host controllers use DMA (like the mainstream pci ones);
|
||||
* they can use PIO (sl811) or be software over another transport.
|
||||
*/
|
||||
dma = (dev->dev.dma_mask != NULL);
|
||||
if (dma)
|
||||
io->entries = usb_buffer_map_sg(dev, usb_pipein(pipe),
|
||||
sg, nents);
|
||||
else
|
||||
io->entries = nents;
|
||||
|
||||
/* initialize all the urbs we'll use */
|
||||
if (io->entries <= 0)
|
||||
return io->entries;
|
||||
|
||||
if (dev->bus->sg_tablesize > 0) {
|
||||
io->urbs = kmalloc(sizeof *io->urbs, mem_flags);
|
||||
use_sg = true;
|
||||
io->entries = 1;
|
||||
} else {
|
||||
io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags);
|
||||
use_sg = false;
|
||||
io->entries = nents;
|
||||
}
|
||||
|
||||
/* initialize all the urbs we'll use */
|
||||
io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags);
|
||||
if (!io->urbs)
|
||||
goto nomem;
|
||||
|
||||
urb_flags = 0;
|
||||
if (dma)
|
||||
urb_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
urb_flags = URB_NO_INTERRUPT;
|
||||
if (usb_pipein(pipe))
|
||||
urb_flags |= URB_SHORT_NOT_OK;
|
||||
|
||||
if (use_sg) {
|
||||
io->urbs[0] = usb_alloc_urb(0, mem_flags);
|
||||
if (!io->urbs[0]) {
|
||||
io->entries = 0;
|
||||
for_each_sg(sg, sg, io->entries, i) {
|
||||
struct urb *urb;
|
||||
unsigned len;
|
||||
|
||||
urb = usb_alloc_urb(0, mem_flags);
|
||||
if (!urb) {
|
||||
io->entries = i;
|
||||
goto nomem;
|
||||
}
|
||||
io->urbs[i] = urb;
|
||||
|
||||
io->urbs[0]->dev = NULL;
|
||||
io->urbs[0]->pipe = pipe;
|
||||
io->urbs[0]->interval = period;
|
||||
io->urbs[0]->transfer_flags = urb_flags;
|
||||
urb->dev = NULL;
|
||||
urb->pipe = pipe;
|
||||
urb->interval = period;
|
||||
urb->transfer_flags = urb_flags;
|
||||
urb->complete = sg_complete;
|
||||
urb->context = io;
|
||||
urb->sg = sg;
|
||||
|
||||
io->urbs[0]->complete = sg_complete;
|
||||
io->urbs[0]->context = io;
|
||||
/* A length of zero means transfer the whole sg list */
|
||||
io->urbs[0]->transfer_buffer_length = length;
|
||||
if (length == 0) {
|
||||
for_each_sg(sg, sg, io->entries, i) {
|
||||
io->urbs[0]->transfer_buffer_length +=
|
||||
sg_dma_len(sg);
|
||||
if (use_sg) {
|
||||
/* There is no single transfer buffer */
|
||||
urb->transfer_buffer = NULL;
|
||||
urb->num_sgs = nents;
|
||||
|
||||
/* A length of zero means transfer the whole sg list */
|
||||
len = length;
|
||||
if (len == 0) {
|
||||
for_each_sg(sg, sg, nents, i)
|
||||
len += sg->length;
|
||||
}
|
||||
}
|
||||
io->urbs[0]->sg = io;
|
||||
io->urbs[0]->num_sgs = io->entries;
|
||||
io->entries = 1;
|
||||
} else {
|
||||
urb_flags |= URB_NO_INTERRUPT;
|
||||
for_each_sg(sg, sg, io->entries, i) {
|
||||
unsigned len;
|
||||
|
||||
io->urbs[i] = usb_alloc_urb(0, mem_flags);
|
||||
if (!io->urbs[i]) {
|
||||
io->entries = i;
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
io->urbs[i]->dev = NULL;
|
||||
io->urbs[i]->pipe = pipe;
|
||||
io->urbs[i]->interval = period;
|
||||
io->urbs[i]->transfer_flags = urb_flags;
|
||||
|
||||
io->urbs[i]->complete = sg_complete;
|
||||
io->urbs[i]->context = io;
|
||||
|
||||
} else {
|
||||
/*
|
||||
* Some systems need to revert to PIO when DMA is temporarily
|
||||
* unavailable. For their sakes, both transfer_buffer and
|
||||
* transfer_dma are set when possible.
|
||||
*
|
||||
* Note that if IOMMU coalescing occurred, we cannot
|
||||
* trust sg_page anymore, so check if S/G list shrunk.
|
||||
* Some systems can't use DMA; they use PIO instead.
|
||||
* For their sakes, transfer_buffer is set whenever
|
||||
* possible.
|
||||
*/
|
||||
if (io->nents == io->entries && !PageHighMem(sg_page(sg)))
|
||||
io->urbs[i]->transfer_buffer = sg_virt(sg);
|
||||
if (!PageHighMem(sg_page(sg)))
|
||||
urb->transfer_buffer = sg_virt(sg);
|
||||
else
|
||||
io->urbs[i]->transfer_buffer = NULL;
|
||||
|
||||
if (dma) {
|
||||
io->urbs[i]->transfer_dma = sg_dma_address(sg);
|
||||
len = sg_dma_len(sg);
|
||||
} else {
|
||||
/* hc may use _only_ transfer_buffer */
|
||||
len = sg->length;
|
||||
}
|
||||
urb->transfer_buffer = NULL;
|
||||
|
||||
len = sg->length;
|
||||
if (length) {
|
||||
len = min_t(unsigned, len, length);
|
||||
length -= len;
|
||||
if (length == 0)
|
||||
io->entries = i + 1;
|
||||
}
|
||||
io->urbs[i]->transfer_buffer_length = len;
|
||||
}
|
||||
io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT;
|
||||
urb->transfer_buffer_length = len;
|
||||
}
|
||||
io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT;
|
||||
|
||||
/* transaction state */
|
||||
io->count = io->entries;
|
||||
|
|
|
|||
|
|
@ -71,6 +71,10 @@ static const struct usb_device_id usb_quirk_list[] = {
|
|||
/* SKYMEDI USB_DRIVE */
|
||||
{ USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* BUILDWIN Photo Frame */
|
||||
{ USB_DEVICE(0x1908, 0x1315), .driver_info =
|
||||
USB_QUIRK_HONOR_BNUMINTERFACES },
|
||||
|
||||
/* INTEL VALUE SSD */
|
||||
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
|
|
|
|||
|
|
@ -383,13 +383,24 @@ static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR,
|
|||
static const char on_string[] = "on";
|
||||
static const char auto_string[] = "auto";
|
||||
|
||||
static void warn_level(void) {
|
||||
static int level_warned;
|
||||
|
||||
if (!level_warned) {
|
||||
level_warned = 1;
|
||||
printk(KERN_WARNING "WARNING! power/level is deprecated; "
|
||||
"use power/control instead\n");
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
show_level(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
const char *p = auto_string;
|
||||
|
||||
if (udev->state != USB_STATE_SUSPENDED && udev->autosuspend_disabled)
|
||||
warn_level();
|
||||
if (udev->state != USB_STATE_SUSPENDED && !udev->dev.power.runtime_auto)
|
||||
p = on_string;
|
||||
return sprintf(buf, "%s\n", p);
|
||||
}
|
||||
|
|
@ -401,8 +412,9 @@ set_level(struct device *dev, struct device_attribute *attr,
|
|||
struct usb_device *udev = to_usb_device(dev);
|
||||
int len = count;
|
||||
char *cp;
|
||||
int rc;
|
||||
int rc = count;
|
||||
|
||||
warn_level();
|
||||
cp = memchr(buf, '\n', count);
|
||||
if (cp)
|
||||
len = cp - buf;
|
||||
|
|
@ -411,17 +423,17 @@ set_level(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
if (len == sizeof on_string - 1 &&
|
||||
strncmp(buf, on_string, len) == 0)
|
||||
rc = usb_disable_autosuspend(udev);
|
||||
usb_disable_autosuspend(udev);
|
||||
|
||||
else if (len == sizeof auto_string - 1 &&
|
||||
strncmp(buf, auto_string, len) == 0)
|
||||
rc = usb_enable_autosuspend(udev);
|
||||
usb_enable_autosuspend(udev);
|
||||
|
||||
else
|
||||
rc = -EINVAL;
|
||||
|
||||
usb_unlock_device(udev);
|
||||
return (rc < 0 ? rc : count);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#include <linux/log2.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/wait.h>
|
||||
#include "hcd.h"
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#define to_urb(d) container_of(d, struct urb, kref)
|
||||
|
||||
|
|
@ -308,8 +308,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
|||
* will be required to set urb->ep directly and we will eliminate
|
||||
* urb->pipe.
|
||||
*/
|
||||
ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out)
|
||||
[usb_pipeendpoint(urb->pipe)];
|
||||
ep = usb_pipe_endpoint(dev, urb->pipe);
|
||||
if (!ep)
|
||||
return -ENOENT;
|
||||
|
||||
|
|
@ -333,9 +332,12 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
|||
is_out = usb_endpoint_dir_out(&ep->desc);
|
||||
}
|
||||
|
||||
/* Cache the direction for later use */
|
||||
urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) |
|
||||
(is_out ? URB_DIR_OUT : URB_DIR_IN);
|
||||
/* Clear the internal flags and cache the direction for later use */
|
||||
urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |
|
||||
URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |
|
||||
URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
|
||||
URB_DMA_SG_COMBINED);
|
||||
urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);
|
||||
|
||||
if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
|
||||
dev->state < USB_STATE_CONFIGURED)
|
||||
|
|
@ -396,8 +398,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
|||
return -EPIPE; /* The most suitable error code :-) */
|
||||
|
||||
/* enforce simple/standard policy */
|
||||
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |
|
||||
URB_NO_INTERRUPT | URB_DIR_MASK | URB_FREE_BUFFER);
|
||||
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK |
|
||||
URB_FREE_BUFFER);
|
||||
switch (xfertype) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (is_out)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
|
@ -41,7 +42,6 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "hcd.h"
|
||||
#include "usb.h"
|
||||
|
||||
|
||||
|
|
@ -593,76 +593,6 @@ int usb_lock_device_for_reset(struct usb_device *udev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_lock_device_for_reset);
|
||||
|
||||
static struct usb_device *match_device(struct usb_device *dev,
|
||||
u16 vendor_id, u16 product_id)
|
||||
{
|
||||
struct usb_device *ret_dev = NULL;
|
||||
int child;
|
||||
|
||||
dev_dbg(&dev->dev, "check for vendor %04x, product %04x ...\n",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
/* see if this device matches */
|
||||
if ((vendor_id == le16_to_cpu(dev->descriptor.idVendor)) &&
|
||||
(product_id == le16_to_cpu(dev->descriptor.idProduct))) {
|
||||
dev_dbg(&dev->dev, "matched this device!\n");
|
||||
ret_dev = usb_get_dev(dev);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* look through all of the children of this device */
|
||||
for (child = 0; child < dev->maxchild; ++child) {
|
||||
if (dev->children[child]) {
|
||||
usb_lock_device(dev->children[child]);
|
||||
ret_dev = match_device(dev->children[child],
|
||||
vendor_id, product_id);
|
||||
usb_unlock_device(dev->children[child]);
|
||||
if (ret_dev)
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
return ret_dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_find_device - find a specific usb device in the system
|
||||
* @vendor_id: the vendor id of the device to find
|
||||
* @product_id: the product id of the device to find
|
||||
*
|
||||
* Returns a pointer to a struct usb_device if such a specified usb
|
||||
* device is present in the system currently. The usage count of the
|
||||
* device will be incremented if a device is found. Make sure to call
|
||||
* usb_put_dev() when the caller is finished with the device.
|
||||
*
|
||||
* If a device with the specified vendor and product id is not found,
|
||||
* NULL is returned.
|
||||
*/
|
||||
struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)
|
||||
{
|
||||
struct list_head *buslist;
|
||||
struct usb_bus *bus;
|
||||
struct usb_device *dev = NULL;
|
||||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
for (buslist = usb_bus_list.next;
|
||||
buslist != &usb_bus_list;
|
||||
buslist = buslist->next) {
|
||||
bus = container_of(buslist, struct usb_bus, bus_list);
|
||||
if (!bus->root_hub)
|
||||
continue;
|
||||
usb_lock_device(bus->root_hub);
|
||||
dev = match_device(bus->root_hub, vendor_id, product_id);
|
||||
usb_unlock_device(bus->root_hub);
|
||||
if (dev)
|
||||
goto exit;
|
||||
}
|
||||
exit:
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
return dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_get_current_frame_number - return current bus frame number
|
||||
* @dev: the device whose bus is being queried
|
||||
|
|
@ -775,7 +705,7 @@ EXPORT_SYMBOL_GPL(usb_free_coherent);
|
|||
* @urb: urb whose transfer_buffer/setup_packet will be mapped
|
||||
*
|
||||
* Return value is either null (indicating no buffer could be mapped), or
|
||||
* the parameter. URB_NO_TRANSFER_DMA_MAP and URB_NO_SETUP_DMA_MAP are
|
||||
* the parameter. URB_NO_TRANSFER_DMA_MAP is
|
||||
* added to urb->transfer_flags if the operation succeeds. If the device
|
||||
* is connected to this system through a non-DMA controller, this operation
|
||||
* always succeeds.
|
||||
|
|
@ -803,17 +733,11 @@ struct urb *usb_buffer_map(struct urb *urb)
|
|||
urb->transfer_buffer, urb->transfer_buffer_length,
|
||||
usb_pipein(urb->pipe)
|
||||
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (usb_pipecontrol(urb->pipe))
|
||||
urb->setup_dma = dma_map_single(controller,
|
||||
urb->setup_packet,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
DMA_TO_DEVICE);
|
||||
/* FIXME generic api broken like pci, can't report errors */
|
||||
/* if (urb->transfer_dma == DMA_ADDR_INVALID) return 0; */
|
||||
} else
|
||||
urb->transfer_dma = ~0;
|
||||
urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
|
||||
| URB_NO_SETUP_DMA_MAP);
|
||||
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
return urb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_buffer_map);
|
||||
|
|
@ -881,18 +805,13 @@ void usb_buffer_unmap(struct urb *urb)
|
|||
urb->transfer_dma, urb->transfer_buffer_length,
|
||||
usb_pipein(urb->pipe)
|
||||
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (usb_pipecontrol(urb->pipe))
|
||||
dma_unmap_single(controller,
|
||||
urb->setup_dma,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
urb->transfer_flags &= ~(URB_NO_TRANSFER_DMA_MAP
|
||||
| URB_NO_SETUP_DMA_MAP);
|
||||
urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_buffer_unmap);
|
||||
#endif /* 0 */
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint
|
||||
* @dev: device to which the scatterlist will be mapped
|
||||
|
|
@ -936,6 +855,7 @@ int usb_buffer_map_sg(const struct usb_device *dev, int is_in,
|
|||
is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE) ? : -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_buffer_map_sg);
|
||||
#endif
|
||||
|
||||
/* XXX DISABLED, no users currently. If you wish to re-enable this
|
||||
* XXX please determine whether the sync is to transfer ownership of
|
||||
|
|
@ -972,6 +892,7 @@ void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in,
|
|||
EXPORT_SYMBOL_GPL(usb_buffer_dmasync_sg);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist
|
||||
* @dev: device to which the scatterlist will be mapped
|
||||
|
|
@ -997,6 +918,7 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in,
|
|||
is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg);
|
||||
#endif
|
||||
|
||||
/* To disable USB, kernel command line is 'nousb' not 'usbcore.nousb' */
|
||||
#ifdef MODULE
|
||||
|
|
|
|||
|
|
@ -710,6 +710,43 @@ config USB_GADGETFS
|
|||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "gadgetfs".
|
||||
|
||||
config USB_FUNCTIONFS
|
||||
tristate "Function Filesystem (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
help
|
||||
The Function Filesystem (FunctioFS) lets one create USB
|
||||
composite functions in user space in the same way as GadgetFS
|
||||
lets one create USB gadgets in user space. This allows creation
|
||||
of composite gadgets such that some of the functions are
|
||||
implemented in kernel space (for instance Ethernet, serial or
|
||||
mass storage) and other are implemented in user space.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build
|
||||
a dynamically linked module called "g_ffs".
|
||||
|
||||
config USB_FUNCTIONFS_ETH
|
||||
bool "Include CDC ECM (Ethernet) function"
|
||||
depends on USB_FUNCTIONFS && NET
|
||||
help
|
||||
Include an CDC ECM (Ethernet) funcion in the CDC ECM (Funcion)
|
||||
Filesystem. If you also say "y" to the RNDIS query below the
|
||||
gadget will have two configurations.
|
||||
|
||||
config USB_FUNCTIONFS_RNDIS
|
||||
bool "Include RNDIS (Ethernet) function"
|
||||
depends on USB_FUNCTIONFS && NET
|
||||
help
|
||||
Include an RNDIS (Ethernet) funcion in the Funcion Filesystem.
|
||||
If you also say "y" to the CDC ECM query above the gadget will
|
||||
have two configurations.
|
||||
|
||||
config USB_FUNCTIONFS_GENERIC
|
||||
bool "Include 'pure' configuration"
|
||||
depends on USB_FUNCTIONFS && (USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
|
||||
help
|
||||
Include a configuration with FunctionFS and no Ethernet
|
||||
configuration.
|
||||
|
||||
config USB_FILE_STORAGE
|
||||
tristate "File-backed Storage Gadget"
|
||||
depends on BLOCK
|
||||
|
|
@ -863,11 +900,30 @@ config USB_G_MULTI_CDC
|
|||
|
||||
If unsure, say "y".
|
||||
|
||||
config USB_G_HID
|
||||
tristate "HID Gadget"
|
||||
help
|
||||
The HID gadget driver provides generic emulation of USB
|
||||
Human Interface Devices (HID).
|
||||
|
||||
For more information, see Documentation/usb/gadget_hid.txt which
|
||||
includes sample code for accessing the device files.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_hid".
|
||||
|
||||
# put drivers that need isochronous transfer support (for audio
|
||||
# or video class gadget drivers), or specific hardware, here.
|
||||
config USB_G_WEBCAM
|
||||
tristate "USB Webcam Gadget"
|
||||
depends on VIDEO_DEV
|
||||
help
|
||||
The Webcam Gadget acts as a composite USB Audio and Video Class
|
||||
device. It provides a userspace API to process UVC control requests
|
||||
and stream video data to the host.
|
||||
|
||||
# - none yet
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_webcam".
|
||||
|
||||
endchoice
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
|
|||
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
|
||||
fsl_usb2_udc-objs := fsl_udc_core.o
|
||||
ifeq ($(CONFIG_ARCH_MXC),y)
|
||||
fsl_usb2_udc-objs += fsl_mx3_udc.o
|
||||
fsl_usb2_udc-objs += fsl_mxc_udc.o
|
||||
endif
|
||||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
|
||||
|
|
@ -43,18 +43,24 @@ g_mass_storage-objs := mass_storage.o
|
|||
g_printer-objs := printer.o
|
||||
g_cdc-objs := cdc2.o
|
||||
g_multi-objs := multi.o
|
||||
g_hid-objs := hid.o
|
||||
g_nokia-objs := nokia.o
|
||||
g_webcam-objs := webcam.o
|
||||
|
||||
obj-$(CONFIG_USB_ZERO) += g_zero.o
|
||||
obj-$(CONFIG_USB_AUDIO) += g_audio.o
|
||||
obj-$(CONFIG_USB_ETH) += g_ether.o
|
||||
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
|
||||
obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
|
||||
obj-$(CONFIG_USB_ETH_FUNCTIONFS) += g_eth_ffs.o
|
||||
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
|
||||
obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
|
||||
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
|
||||
obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
|
||||
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
|
||||
obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
|
||||
obj-$(CONFIG_USB_G_HID) += g_hid.o
|
||||
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
|
||||
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
|
||||
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
|
||||
|
||||
|
|
|
|||
|
|
@ -48,10 +48,9 @@ static int queue_dbg_open(struct inode *inode, struct file *file)
|
|||
|
||||
spin_lock_irq(&ep->udc->lock);
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
req_copy = kmalloc(sizeof(*req_copy), GFP_ATOMIC);
|
||||
req_copy = kmemdup(req, sizeof(*req_copy), GFP_ATOMIC);
|
||||
if (!req_copy)
|
||||
goto fail;
|
||||
memcpy(req_copy, req, sizeof(*req_copy));
|
||||
list_add_tail(&req_copy->queue, queue_data);
|
||||
}
|
||||
spin_unlock_irq(&ep->udc->lock);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
*/
|
||||
|
||||
/* big enough to hold our biggest descriptor */
|
||||
#define USB_BUFSIZ 512
|
||||
#define USB_BUFSIZ 1024
|
||||
|
||||
static struct usb_composite_driver *composite;
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
|
|||
* This function returns the value of the function's bind(), which is
|
||||
* zero for success else a negative errno value.
|
||||
*/
|
||||
int __init usb_add_function(struct usb_configuration *config,
|
||||
int usb_add_function(struct usb_configuration *config,
|
||||
struct usb_function *function)
|
||||
{
|
||||
int value = -EINVAL;
|
||||
|
|
@ -215,7 +215,7 @@ int usb_function_activate(struct usb_function *function)
|
|||
* Returns the interface ID which was allocated; or -ENODEV if no
|
||||
* more interface IDs can be allocated.
|
||||
*/
|
||||
int __init usb_interface_id(struct usb_configuration *config,
|
||||
int usb_interface_id(struct usb_configuration *config,
|
||||
struct usb_function *function)
|
||||
{
|
||||
unsigned id = config->next_interface_id;
|
||||
|
|
@ -480,7 +480,7 @@ done:
|
|||
* assigns global resources including string IDs, and per-configuration
|
||||
* resources such as interface IDs and endpoints.
|
||||
*/
|
||||
int __init usb_add_config(struct usb_composite_dev *cdev,
|
||||
int usb_add_config(struct usb_composite_dev *cdev,
|
||||
struct usb_configuration *config)
|
||||
{
|
||||
int status = -EINVAL;
|
||||
|
|
@ -677,7 +677,7 @@ static int get_string(struct usb_composite_dev *cdev,
|
|||
* ensure that for example different functions don't wrongly assign
|
||||
* different meanings to the same identifier.
|
||||
*/
|
||||
int __init usb_string_id(struct usb_composite_dev *cdev)
|
||||
int usb_string_id(struct usb_composite_dev *cdev)
|
||||
{
|
||||
if (cdev->next_string_id < 254) {
|
||||
/* string id 0 is reserved */
|
||||
|
|
@ -898,7 +898,19 @@ static void composite_disconnect(struct usb_gadget *gadget)
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void /* __init_or_exit */
|
||||
static ssize_t composite_show_suspended(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_gadget *gadget = dev_to_usb_gadget(dev);
|
||||
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
||||
|
||||
return sprintf(buf, "%d\n", cdev->suspended);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(suspended, 0444, composite_show_suspended, NULL);
|
||||
|
||||
static void
|
||||
composite_unbind(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
||||
|
|
@ -944,10 +956,11 @@ composite_unbind(struct usb_gadget *gadget)
|
|||
}
|
||||
kfree(cdev);
|
||||
set_gadget_data(gadget, NULL);
|
||||
device_remove_file(&gadget->dev, &dev_attr_suspended);
|
||||
composite = NULL;
|
||||
}
|
||||
|
||||
static void __init
|
||||
static void
|
||||
string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
|
||||
{
|
||||
struct usb_string *str = tab->strings;
|
||||
|
|
@ -960,7 +973,7 @@ string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
|
|||
}
|
||||
}
|
||||
|
||||
static void __init
|
||||
static void
|
||||
string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
|
||||
{
|
||||
while (*tab) {
|
||||
|
|
@ -969,7 +982,7 @@ string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
|
|||
}
|
||||
}
|
||||
|
||||
static int __init composite_bind(struct usb_gadget *gadget)
|
||||
static int composite_bind(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_composite_dev *cdev;
|
||||
int status = -ENOMEM;
|
||||
|
|
@ -1004,6 +1017,14 @@ static int __init composite_bind(struct usb_gadget *gadget)
|
|||
*/
|
||||
usb_ep_autoconfig_reset(cdev->gadget);
|
||||
|
||||
/* standardized runtime overrides for device ID data */
|
||||
if (idVendor)
|
||||
cdev->desc.idVendor = cpu_to_le16(idVendor);
|
||||
if (idProduct)
|
||||
cdev->desc.idProduct = cpu_to_le16(idProduct);
|
||||
if (bcdDevice)
|
||||
cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
|
||||
|
||||
/* composite gadget needs to assign strings for whole device (like
|
||||
* serial number), register function drivers, potentially update
|
||||
* power state and consumption, etc
|
||||
|
|
@ -1015,14 +1036,6 @@ static int __init composite_bind(struct usb_gadget *gadget)
|
|||
cdev->desc = *composite->dev;
|
||||
cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
|
||||
|
||||
/* standardized runtime overrides for device ID data */
|
||||
if (idVendor)
|
||||
cdev->desc.idVendor = cpu_to_le16(idVendor);
|
||||
if (idProduct)
|
||||
cdev->desc.idProduct = cpu_to_le16(idProduct);
|
||||
if (bcdDevice)
|
||||
cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
|
||||
|
||||
/* strings can't be assigned before bind() allocates the
|
||||
* releavnt identifiers
|
||||
*/
|
||||
|
|
@ -1036,6 +1049,10 @@ static int __init composite_bind(struct usb_gadget *gadget)
|
|||
string_override(composite->strings,
|
||||
cdev->desc.iSerialNumber, iSerialNumber);
|
||||
|
||||
status = device_create_file(&gadget->dev, &dev_attr_suspended);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
INFO(cdev, "%s ready\n", composite->name);
|
||||
return 0;
|
||||
|
||||
|
|
@ -1064,6 +1081,8 @@ composite_suspend(struct usb_gadget *gadget)
|
|||
}
|
||||
if (composite->suspend)
|
||||
composite->suspend(cdev);
|
||||
|
||||
cdev->suspended = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1084,6 +1103,8 @@ composite_resume(struct usb_gadget *gadget)
|
|||
f->resume(f);
|
||||
}
|
||||
}
|
||||
|
||||
cdev->suspended = 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
@ -1092,7 +1113,6 @@ static struct usb_gadget_driver composite_driver = {
|
|||
.speed = USB_SPEED_HIGH,
|
||||
|
||||
.bind = composite_bind,
|
||||
/* .unbind = __exit_p(composite_unbind), */
|
||||
.unbind = composite_unbind,
|
||||
|
||||
.setup = composite_setup,
|
||||
|
|
@ -1121,7 +1141,7 @@ static struct usb_gadget_driver composite_driver = {
|
|||
* while it was binding. That would usually be done in order to wait for
|
||||
* some userspace participation.
|
||||
*/
|
||||
int __init usb_composite_register(struct usb_composite_driver *driver)
|
||||
int usb_composite_register(struct usb_composite_driver *driver)
|
||||
{
|
||||
if (!driver || !driver->dev || !driver->bind || composite)
|
||||
return -EINVAL;
|
||||
|
|
@ -1142,7 +1162,7 @@ int __init usb_composite_register(struct usb_composite_driver *driver)
|
|||
* This function is used to unregister drivers using the composite
|
||||
* driver framework.
|
||||
*/
|
||||
void /* __exit */ usb_composite_unregister(struct usb_composite_driver *driver)
|
||||
void usb_composite_unregister(struct usb_composite_driver *driver)
|
||||
{
|
||||
if (composite != driver)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ int usb_gadget_config_buf(
|
|||
* with identifiers (for interfaces, strings, endpoints, and more)
|
||||
* as needed by a given function instance.
|
||||
*/
|
||||
struct usb_descriptor_header **__init
|
||||
struct usb_descriptor_header **
|
||||
usb_copy_descriptors(struct usb_descriptor_header **src)
|
||||
{
|
||||
struct usb_descriptor_header **tmp;
|
||||
|
|
@ -175,7 +175,7 @@ usb_copy_descriptors(struct usb_descriptor_header **src)
|
|||
* intended use is to help remembering the endpoint descriptor to use
|
||||
* when enabling a given endpoint.
|
||||
*/
|
||||
struct usb_endpoint_descriptor *__init
|
||||
struct usb_endpoint_descriptor *
|
||||
usb_find_endpoint(
|
||||
struct usb_descriptor_header **src,
|
||||
struct usb_descriptor_header **copy,
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
|
|
@ -55,9 +56,6 @@
|
|||
#include <asm/unaligned.h>
|
||||
|
||||
|
||||
#include "../core/hcd.h"
|
||||
|
||||
|
||||
#define DRIVER_DESC "USB Host+Gadget Emulator"
|
||||
#define DRIVER_VERSION "02 May 2005"
|
||||
|
||||
|
|
|
|||
|
|
@ -34,12 +34,12 @@
|
|||
|
||||
|
||||
/* we must assign addresses for configurable endpoints (like net2280) */
|
||||
static __initdata unsigned epnum;
|
||||
static unsigned epnum;
|
||||
|
||||
// #define MANY_ENDPOINTS
|
||||
#ifdef MANY_ENDPOINTS
|
||||
/* more than 15 configurable endpoints */
|
||||
static __initdata unsigned in_epnum;
|
||||
static unsigned in_epnum;
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ static __initdata unsigned in_epnum;
|
|||
* NOTE: each endpoint is unidirectional, as specified by its USB
|
||||
* descriptor; and isn't specific to a configuration or altsetting.
|
||||
*/
|
||||
static int __init
|
||||
static int
|
||||
ep_matches (
|
||||
struct usb_gadget *gadget,
|
||||
struct usb_ep *ep,
|
||||
|
|
@ -187,7 +187,7 @@ ep_matches (
|
|||
return 1;
|
||||
}
|
||||
|
||||
static struct usb_ep * __init
|
||||
static struct usb_ep *
|
||||
find_ep (struct usb_gadget *gadget, const char *name)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
|
|
@ -229,7 +229,7 @@ find_ep (struct usb_gadget *gadget, const char *name)
|
|||
*
|
||||
* On failure, this returns a null endpoint descriptor.
|
||||
*/
|
||||
struct usb_ep * __init usb_ep_autoconfig (
|
||||
struct usb_ep *usb_ep_autoconfig (
|
||||
struct usb_gadget *gadget,
|
||||
struct usb_endpoint_descriptor *desc
|
||||
)
|
||||
|
|
@ -304,7 +304,7 @@ struct usb_ep * __init usb_ep_autoconfig (
|
|||
* state such as ep->driver_data and the record of assigned endpoints
|
||||
* used by usb_ep_autoconfig().
|
||||
*/
|
||||
void __init usb_ep_autoconfig_reset (struct usb_gadget *gadget)
|
||||
void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ acm_iad_descriptor = {
|
|||
};
|
||||
|
||||
|
||||
static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
|
||||
static struct usb_interface_descriptor acm_control_interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
|
|
@ -127,7 +127,7 @@ static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
|
|||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor acm_data_interface_desc __initdata = {
|
||||
static struct usb_interface_descriptor acm_data_interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
|
|
@ -138,7 +138,7 @@ static struct usb_interface_descriptor acm_data_interface_desc __initdata = {
|
|||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_cdc_header_desc acm_header_desc __initdata = {
|
||||
static struct usb_cdc_header_desc acm_header_desc = {
|
||||
.bLength = sizeof(acm_header_desc),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
|
||||
|
|
@ -146,7 +146,7 @@ static struct usb_cdc_header_desc acm_header_desc __initdata = {
|
|||
};
|
||||
|
||||
static struct usb_cdc_call_mgmt_descriptor
|
||||
acm_call_mgmt_descriptor __initdata = {
|
||||
acm_call_mgmt_descriptor = {
|
||||
.bLength = sizeof(acm_call_mgmt_descriptor),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
|
||||
|
|
@ -154,14 +154,14 @@ acm_call_mgmt_descriptor __initdata = {
|
|||
/* .bDataInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
|
||||
static struct usb_cdc_acm_descriptor acm_descriptor = {
|
||||
.bLength = sizeof(acm_descriptor),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_ACM_TYPE,
|
||||
.bmCapabilities = USB_CDC_CAP_LINE,
|
||||
};
|
||||
|
||||
static struct usb_cdc_union_desc acm_union_desc __initdata = {
|
||||
static struct usb_cdc_union_desc acm_union_desc = {
|
||||
.bLength = sizeof(acm_union_desc),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_UNION_TYPE,
|
||||
|
|
@ -171,7 +171,7 @@ static struct usb_cdc_union_desc acm_union_desc __initdata = {
|
|||
|
||||
/* full speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor acm_fs_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
|
|
@ -180,21 +180,21 @@ static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = {
|
|||
.bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor acm_fs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor acm_fs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *acm_fs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *acm_fs_function[] = {
|
||||
(struct usb_descriptor_header *) &acm_iad_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_control_interface_desc,
|
||||
(struct usb_descriptor_header *) &acm_header_desc,
|
||||
|
|
@ -210,7 +210,7 @@ static struct usb_descriptor_header *acm_fs_function[] __initdata = {
|
|||
|
||||
/* high speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor acm_hs_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
|
|
@ -219,21 +219,21 @@ static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = {
|
|||
.bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor acm_hs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor acm_hs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *acm_hs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *acm_hs_function[] = {
|
||||
(struct usb_descriptor_header *) &acm_iad_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_control_interface_desc,
|
||||
(struct usb_descriptor_header *) &acm_header_desc,
|
||||
|
|
@ -571,7 +571,7 @@ static int acm_send_break(struct gserial *port, int duration)
|
|||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* ACM function driver setup/binding */
|
||||
static int __init
|
||||
static int
|
||||
acm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
|
|
@ -719,7 +719,7 @@ static inline bool can_support_cdc(struct usb_configuration *c)
|
|||
* handle all the ones it binds. Caller is also responsible
|
||||
* for calling @gserial_cleanup() before module unload.
|
||||
*/
|
||||
int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
|
||||
int acm_bind_config(struct usb_configuration *c, u8 port_num)
|
||||
{
|
||||
struct f_acm *acm;
|
||||
int status;
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ static inline unsigned ecm_bitrate(struct usb_gadget *g)
|
|||
|
||||
/* interface descriptor: */
|
||||
|
||||
static struct usb_interface_descriptor ecm_control_intf __initdata = {
|
||||
static struct usb_interface_descriptor ecm_control_intf = {
|
||||
.bLength = sizeof ecm_control_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ static struct usb_interface_descriptor ecm_control_intf __initdata = {
|
|||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_cdc_header_desc ecm_header_desc __initdata = {
|
||||
static struct usb_cdc_header_desc ecm_header_desc = {
|
||||
.bLength = sizeof ecm_header_desc,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
|
||||
|
|
@ -134,7 +134,7 @@ static struct usb_cdc_header_desc ecm_header_desc __initdata = {
|
|||
.bcdCDC = cpu_to_le16(0x0110),
|
||||
};
|
||||
|
||||
static struct usb_cdc_union_desc ecm_union_desc __initdata = {
|
||||
static struct usb_cdc_union_desc ecm_union_desc = {
|
||||
.bLength = sizeof(ecm_union_desc),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_UNION_TYPE,
|
||||
|
|
@ -142,7 +142,7 @@ static struct usb_cdc_union_desc ecm_union_desc __initdata = {
|
|||
/* .bSlaveInterface0 = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_cdc_ether_desc ecm_desc __initdata = {
|
||||
static struct usb_cdc_ether_desc ecm_desc = {
|
||||
.bLength = sizeof ecm_desc,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
|
||||
|
|
@ -157,7 +157,7 @@ static struct usb_cdc_ether_desc ecm_desc __initdata = {
|
|||
|
||||
/* the default data interface has no endpoints ... */
|
||||
|
||||
static struct usb_interface_descriptor ecm_data_nop_intf __initdata = {
|
||||
static struct usb_interface_descriptor ecm_data_nop_intf = {
|
||||
.bLength = sizeof ecm_data_nop_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ static struct usb_interface_descriptor ecm_data_nop_intf __initdata = {
|
|||
|
||||
/* ... but the "real" data interface has two bulk endpoints */
|
||||
|
||||
static struct usb_interface_descriptor ecm_data_intf __initdata = {
|
||||
static struct usb_interface_descriptor ecm_data_intf = {
|
||||
.bLength = sizeof ecm_data_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ static struct usb_interface_descriptor ecm_data_intf __initdata = {
|
|||
|
||||
/* full speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor fs_ecm_notify_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor fs_ecm_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ static struct usb_endpoint_descriptor fs_ecm_notify_desc __initdata = {
|
|||
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_ecm_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor fs_ecm_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -205,7 +205,7 @@ static struct usb_endpoint_descriptor fs_ecm_in_desc __initdata = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_ecm_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor fs_ecm_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -213,7 +213,7 @@ static struct usb_endpoint_descriptor fs_ecm_out_desc __initdata = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *ecm_fs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *ecm_fs_function[] = {
|
||||
/* CDC ECM control descriptors */
|
||||
(struct usb_descriptor_header *) &ecm_control_intf,
|
||||
(struct usb_descriptor_header *) &ecm_header_desc,
|
||||
|
|
@ -231,7 +231,7 @@ static struct usb_descriptor_header *ecm_fs_function[] __initdata = {
|
|||
|
||||
/* high speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor hs_ecm_notify_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor hs_ecm_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -240,7 +240,7 @@ static struct usb_endpoint_descriptor hs_ecm_notify_desc __initdata = {
|
|||
.wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT),
|
||||
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
|
||||
};
|
||||
static struct usb_endpoint_descriptor hs_ecm_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor hs_ecm_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -249,7 +249,7 @@ static struct usb_endpoint_descriptor hs_ecm_in_desc __initdata = {
|
|||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hs_ecm_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor hs_ecm_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -258,7 +258,7 @@ static struct usb_endpoint_descriptor hs_ecm_out_desc __initdata = {
|
|||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *ecm_hs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *ecm_hs_function[] = {
|
||||
/* CDC ECM control descriptors */
|
||||
(struct usb_descriptor_header *) &ecm_control_intf,
|
||||
(struct usb_descriptor_header *) &ecm_header_desc,
|
||||
|
|
@ -597,7 +597,7 @@ static void ecm_close(struct gether *geth)
|
|||
|
||||
/* ethernet function driver setup/binding */
|
||||
|
||||
static int __init
|
||||
static int
|
||||
ecm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
|
|
@ -763,7 +763,8 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||
* Caller must have called @gether_setup(). Caller is also responsible
|
||||
* for calling @gether_cleanup() before module unload.
|
||||
*/
|
||||
int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
int
|
||||
ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
{
|
||||
struct f_ecm *ecm;
|
||||
int status;
|
||||
|
|
|
|||
2442
drivers/usb/gadget/f_fs.c
Normal file
2442
drivers/usb/gadget/f_fs.c
Normal file
File diff suppressed because it is too large
Load diff
673
drivers/usb/gadget/f_hid.c
Normal file
673
drivers/usb/gadget/f_hid.c
Normal file
|
|
@ -0,0 +1,673 @@
|
|||
/*
|
||||
* f_hid.c -- USB HID function driver
|
||||
*
|
||||
* Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/usb/g_hid.h>
|
||||
|
||||
static int major, minors;
|
||||
static struct class *hidg_class;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* HID gadget struct */
|
||||
|
||||
struct f_hidg {
|
||||
/* configuration */
|
||||
unsigned char bInterfaceSubClass;
|
||||
unsigned char bInterfaceProtocol;
|
||||
unsigned short report_desc_length;
|
||||
char *report_desc;
|
||||
unsigned short report_length;
|
||||
|
||||
/* recv report */
|
||||
char *set_report_buff;
|
||||
unsigned short set_report_length;
|
||||
spinlock_t spinlock;
|
||||
wait_queue_head_t read_queue;
|
||||
|
||||
/* send report */
|
||||
struct mutex lock;
|
||||
bool write_pending;
|
||||
wait_queue_head_t write_queue;
|
||||
struct usb_request *req;
|
||||
|
||||
int minor;
|
||||
struct cdev cdev;
|
||||
struct usb_function func;
|
||||
struct usb_ep *in_ep;
|
||||
struct usb_endpoint_descriptor *fs_in_ep_desc;
|
||||
struct usb_endpoint_descriptor *hs_in_ep_desc;
|
||||
};
|
||||
|
||||
static inline struct f_hidg *func_to_hidg(struct usb_function *f)
|
||||
{
|
||||
return container_of(f, struct f_hidg, func);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* Static descriptors */
|
||||
|
||||
static struct usb_interface_descriptor hidg_interface_desc = {
|
||||
.bLength = sizeof hidg_interface_desc,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
/* .bInterfaceSubClass = DYNAMIC */
|
||||
/* .bInterfaceProtocol = DYNAMIC */
|
||||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct hid_descriptor hidg_desc = {
|
||||
.bLength = sizeof hidg_desc,
|
||||
.bDescriptorType = HID_DT_HID,
|
||||
.bcdHID = 0x0101,
|
||||
.bCountryCode = 0x00,
|
||||
.bNumDescriptors = 0x1,
|
||||
/*.desc[0].bDescriptorType = DYNAMIC */
|
||||
/*.desc[0].wDescriptorLenght = DYNAMIC */
|
||||
};
|
||||
|
||||
/* High-Speed Support */
|
||||
|
||||
static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
/*.wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 4, /* FIXME: Add this field in the
|
||||
* HID gadget configuration?
|
||||
* (struct hidg_func_descriptor)
|
||||
*/
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hidg_hs_descriptors[] = {
|
||||
(struct usb_descriptor_header *)&hidg_interface_desc,
|
||||
(struct usb_descriptor_header *)&hidg_desc,
|
||||
(struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Full-Speed Support */
|
||||
|
||||
static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
/*.wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 10, /* FIXME: Add this field in the
|
||||
* HID gadget configuration?
|
||||
* (struct hidg_func_descriptor)
|
||||
*/
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hidg_fs_descriptors[] = {
|
||||
(struct usb_descriptor_header *)&hidg_interface_desc,
|
||||
(struct usb_descriptor_header *)&hidg_desc,
|
||||
(struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* Char Device */
|
||||
|
||||
static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
||||
size_t count, loff_t *ptr)
|
||||
{
|
||||
struct f_hidg *hidg = (struct f_hidg *)file->private_data;
|
||||
char *tmp_buff = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, buffer, count))
|
||||
return -EFAULT;
|
||||
|
||||
spin_lock_irqsave(&hidg->spinlock, flags);
|
||||
|
||||
#define READ_COND (hidg->set_report_buff != NULL)
|
||||
|
||||
while (!READ_COND) {
|
||||
spin_unlock_irqrestore(&hidg->spinlock, flags);
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
if (wait_event_interruptible(hidg->read_queue, READ_COND))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
spin_lock_irqsave(&hidg->spinlock, flags);
|
||||
}
|
||||
|
||||
|
||||
count = min_t(unsigned, count, hidg->set_report_length);
|
||||
tmp_buff = hidg->set_report_buff;
|
||||
hidg->set_report_buff = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&hidg->spinlock, flags);
|
||||
|
||||
if (tmp_buff != NULL) {
|
||||
/* copy to user outside spinlock */
|
||||
count -= copy_to_user(buffer, tmp_buff, count);
|
||||
kfree(tmp_buff);
|
||||
} else
|
||||
count = -ENOMEM;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
|
||||
|
||||
if (req->status != 0) {
|
||||
ERROR(hidg->func.config->cdev,
|
||||
"End Point Request ERROR: %d\n", req->status);
|
||||
}
|
||||
|
||||
hidg->write_pending = 0;
|
||||
wake_up(&hidg->write_queue);
|
||||
}
|
||||
|
||||
static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *offp)
|
||||
{
|
||||
struct f_hidg *hidg = (struct f_hidg *)file->private_data;
|
||||
ssize_t status = -ENOMEM;
|
||||
|
||||
if (!access_ok(VERIFY_READ, buffer, count))
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&hidg->lock);
|
||||
|
||||
#define WRITE_COND (!hidg->write_pending)
|
||||
|
||||
/* write queue */
|
||||
while (!WRITE_COND) {
|
||||
mutex_unlock(&hidg->lock);
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
if (wait_event_interruptible_exclusive(
|
||||
hidg->write_queue, WRITE_COND))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
mutex_lock(&hidg->lock);
|
||||
}
|
||||
|
||||
count = min_t(unsigned, count, hidg->report_length);
|
||||
status = copy_from_user(hidg->req->buf, buffer, count);
|
||||
|
||||
if (status != 0) {
|
||||
ERROR(hidg->func.config->cdev,
|
||||
"copy_from_user error\n");
|
||||
mutex_unlock(&hidg->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hidg->req->status = 0;
|
||||
hidg->req->zero = 0;
|
||||
hidg->req->length = count;
|
||||
hidg->req->complete = f_hidg_req_complete;
|
||||
hidg->req->context = hidg;
|
||||
hidg->write_pending = 1;
|
||||
|
||||
status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC);
|
||||
if (status < 0) {
|
||||
ERROR(hidg->func.config->cdev,
|
||||
"usb_ep_queue error on int endpoint %zd\n", status);
|
||||
hidg->write_pending = 0;
|
||||
wake_up(&hidg->write_queue);
|
||||
} else {
|
||||
status = count;
|
||||
}
|
||||
|
||||
mutex_unlock(&hidg->lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static unsigned int f_hidg_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct f_hidg *hidg = (struct f_hidg *)file->private_data;
|
||||
unsigned int ret = 0;
|
||||
|
||||
poll_wait(file, &hidg->read_queue, wait);
|
||||
poll_wait(file, &hidg->write_queue, wait);
|
||||
|
||||
if (WRITE_COND)
|
||||
ret |= POLLOUT | POLLWRNORM;
|
||||
|
||||
if (READ_COND)
|
||||
ret |= POLLIN | POLLRDNORM;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#undef WRITE_COND
|
||||
#undef READ_COND
|
||||
|
||||
static int f_hidg_release(struct inode *inode, struct file *fd)
|
||||
{
|
||||
fd->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f_hidg_open(struct inode *inode, struct file *fd)
|
||||
{
|
||||
struct f_hidg *hidg =
|
||||
container_of(inode->i_cdev, struct f_hidg, cdev);
|
||||
|
||||
fd->private_data = hidg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* usb_function */
|
||||
|
||||
static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_hidg *hidg = (struct f_hidg *)req->context;
|
||||
|
||||
if (req->status != 0 || req->buf == NULL || req->actual == 0) {
|
||||
ERROR(hidg->func.config->cdev, "%s FAILED\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock(&hidg->spinlock);
|
||||
|
||||
hidg->set_report_buff = krealloc(hidg->set_report_buff,
|
||||
req->actual, GFP_ATOMIC);
|
||||
|
||||
if (hidg->set_report_buff == NULL) {
|
||||
spin_unlock(&hidg->spinlock);
|
||||
return;
|
||||
}
|
||||
hidg->set_report_length = req->actual;
|
||||
memcpy(hidg->set_report_buff, req->buf, req->actual);
|
||||
|
||||
spin_unlock(&hidg->spinlock);
|
||||
|
||||
wake_up(&hidg->read_queue);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int hidg_setup(struct usb_function *f,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct f_hidg *hidg = func_to_hidg(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_request *req = cdev->req;
|
||||
int status = 0;
|
||||
__u16 value, length;
|
||||
|
||||
value = __le16_to_cpu(ctrl->wValue);
|
||||
length = __le16_to_cpu(ctrl->wLength);
|
||||
|
||||
VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x "
|
||||
"Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value);
|
||||
|
||||
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
|
||||
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
||||
| HID_REQ_GET_REPORT):
|
||||
VDBG(cdev, "get_report\n");
|
||||
|
||||
/* send an empty report */
|
||||
length = min_t(unsigned, length, hidg->report_length);
|
||||
memset(req->buf, 0x0, length);
|
||||
|
||||
goto respond;
|
||||
break;
|
||||
|
||||
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
||||
| HID_REQ_GET_PROTOCOL):
|
||||
VDBG(cdev, "get_protocol\n");
|
||||
goto stall;
|
||||
break;
|
||||
|
||||
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
||||
| HID_REQ_SET_REPORT):
|
||||
VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength);
|
||||
req->context = hidg;
|
||||
req->complete = hidg_set_report_complete;
|
||||
goto respond;
|
||||
break;
|
||||
|
||||
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
||||
| HID_REQ_SET_PROTOCOL):
|
||||
VDBG(cdev, "set_protocol\n");
|
||||
goto stall;
|
||||
break;
|
||||
|
||||
case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
|
||||
| USB_REQ_GET_DESCRIPTOR):
|
||||
switch (value >> 8) {
|
||||
case HID_DT_REPORT:
|
||||
VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
|
||||
length = min_t(unsigned short, length,
|
||||
hidg->report_desc_length);
|
||||
memcpy(req->buf, hidg->report_desc, length);
|
||||
goto respond;
|
||||
break;
|
||||
|
||||
default:
|
||||
VDBG(cdev, "Unknown decriptor request 0x%x\n",
|
||||
value >> 8);
|
||||
goto stall;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
VDBG(cdev, "Unknown request 0x%x\n",
|
||||
ctrl->bRequest);
|
||||
goto stall;
|
||||
break;
|
||||
}
|
||||
|
||||
stall:
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
respond:
|
||||
req->zero = 0;
|
||||
req->length = length;
|
||||
status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
|
||||
if (status < 0)
|
||||
ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void hidg_disable(struct usb_function *f)
|
||||
{
|
||||
struct f_hidg *hidg = func_to_hidg(f);
|
||||
|
||||
usb_ep_disable(hidg->in_ep);
|
||||
hidg->in_ep->driver_data = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
{
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct f_hidg *hidg = func_to_hidg(f);
|
||||
const struct usb_endpoint_descriptor *ep_desc;
|
||||
int status = 0;
|
||||
|
||||
VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
|
||||
|
||||
if (hidg->in_ep != NULL) {
|
||||
/* restart endpoint */
|
||||
if (hidg->in_ep->driver_data != NULL)
|
||||
usb_ep_disable(hidg->in_ep);
|
||||
|
||||
ep_desc = ep_choose(f->config->cdev->gadget,
|
||||
hidg->hs_in_ep_desc, hidg->fs_in_ep_desc);
|
||||
status = usb_ep_enable(hidg->in_ep, ep_desc);
|
||||
if (status < 0) {
|
||||
ERROR(cdev, "Enable endpoint FAILED!\n");
|
||||
goto fail;
|
||||
}
|
||||
hidg->in_ep->driver_data = hidg;
|
||||
}
|
||||
fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
const struct file_operations f_hidg_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = f_hidg_open,
|
||||
.release = f_hidg_release,
|
||||
.write = f_hidg_write,
|
||||
.read = f_hidg_read,
|
||||
.poll = f_hidg_poll,
|
||||
};
|
||||
|
||||
static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
struct f_hidg *hidg = func_to_hidg(f);
|
||||
int status;
|
||||
dev_t dev;
|
||||
|
||||
/* allocate instance-specific interface IDs, and patch descriptors */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
hidg_interface_desc.bInterfaceNumber = status;
|
||||
|
||||
|
||||
/* allocate instance-specific endpoints */
|
||||
status = -ENODEV;
|
||||
ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
ep->driver_data = c->cdev; /* claim */
|
||||
hidg->in_ep = ep;
|
||||
|
||||
/* preallocate request and buffer */
|
||||
status = -ENOMEM;
|
||||
hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
|
||||
if (!hidg->req)
|
||||
goto fail;
|
||||
|
||||
|
||||
hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
|
||||
if (!hidg->req->buf)
|
||||
goto fail;
|
||||
|
||||
/* set descriptor dynamic values */
|
||||
hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
|
||||
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
|
||||
hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
||||
hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
||||
hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
|
||||
hidg_desc.desc[0].wDescriptorLength =
|
||||
cpu_to_le16(hidg->report_desc_length);
|
||||
|
||||
hidg->set_report_buff = NULL;
|
||||
|
||||
/* copy descriptors */
|
||||
f->descriptors = usb_copy_descriptors(hidg_fs_descriptors);
|
||||
if (!f->descriptors)
|
||||
goto fail;
|
||||
|
||||
hidg->fs_in_ep_desc = usb_find_endpoint(hidg_fs_descriptors,
|
||||
f->descriptors,
|
||||
&hidg_fs_in_ep_desc);
|
||||
|
||||
if (gadget_is_dualspeed(c->cdev->gadget)) {
|
||||
hidg_hs_in_ep_desc.bEndpointAddress =
|
||||
hidg_fs_in_ep_desc.bEndpointAddress;
|
||||
f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors);
|
||||
if (!f->hs_descriptors)
|
||||
goto fail;
|
||||
hidg->hs_in_ep_desc = usb_find_endpoint(hidg_hs_descriptors,
|
||||
f->hs_descriptors,
|
||||
&hidg_hs_in_ep_desc);
|
||||
} else {
|
||||
hidg->hs_in_ep_desc = NULL;
|
||||
}
|
||||
|
||||
mutex_init(&hidg->lock);
|
||||
spin_lock_init(&hidg->spinlock);
|
||||
init_waitqueue_head(&hidg->write_queue);
|
||||
init_waitqueue_head(&hidg->read_queue);
|
||||
|
||||
/* create char device */
|
||||
cdev_init(&hidg->cdev, &f_hidg_fops);
|
||||
dev = MKDEV(major, hidg->minor);
|
||||
status = cdev_add(&hidg->cdev, dev, 1);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
ERROR(f->config->cdev, "hidg_bind FAILED\n");
|
||||
if (hidg->req != NULL) {
|
||||
kfree(hidg->req->buf);
|
||||
if (hidg->in_ep != NULL)
|
||||
usb_ep_free_request(hidg->in_ep, hidg->req);
|
||||
}
|
||||
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
usb_free_descriptors(f->descriptors);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_hidg *hidg = func_to_hidg(f);
|
||||
|
||||
device_destroy(hidg_class, MKDEV(major, hidg->minor));
|
||||
cdev_del(&hidg->cdev);
|
||||
|
||||
/* disable/free request and end point */
|
||||
usb_ep_disable(hidg->in_ep);
|
||||
usb_ep_dequeue(hidg->in_ep, hidg->req);
|
||||
kfree(hidg->req->buf);
|
||||
usb_ep_free_request(hidg->in_ep, hidg->req);
|
||||
|
||||
/* free descriptors copies */
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
usb_free_descriptors(f->descriptors);
|
||||
|
||||
kfree(hidg->report_desc);
|
||||
kfree(hidg->set_report_buff);
|
||||
kfree(hidg);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* Strings */
|
||||
|
||||
#define CT_FUNC_HID_IDX 0
|
||||
|
||||
static struct usb_string ct_func_string_defs[] = {
|
||||
[CT_FUNC_HID_IDX].s = "HID Interface",
|
||||
{}, /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings ct_func_string_table = {
|
||||
.language = 0x0409, /* en-US */
|
||||
.strings = ct_func_string_defs,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *ct_func_strings[] = {
|
||||
&ct_func_string_table,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* usb_configuration */
|
||||
|
||||
int __init hidg_bind_config(struct usb_configuration *c,
|
||||
struct hidg_func_descriptor *fdesc, int index)
|
||||
{
|
||||
struct f_hidg *hidg;
|
||||
int status;
|
||||
|
||||
if (index >= minors)
|
||||
return -ENOENT;
|
||||
|
||||
/* maybe allocate device-global string IDs, and patch descriptors */
|
||||
if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) {
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
ct_func_string_defs[CT_FUNC_HID_IDX].id = status;
|
||||
hidg_interface_desc.iInterface = status;
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
hidg = kzalloc(sizeof *hidg, GFP_KERNEL);
|
||||
if (!hidg)
|
||||
return -ENOMEM;
|
||||
|
||||
hidg->minor = index;
|
||||
hidg->bInterfaceSubClass = fdesc->subclass;
|
||||
hidg->bInterfaceProtocol = fdesc->protocol;
|
||||
hidg->report_length = fdesc->report_length;
|
||||
hidg->report_desc_length = fdesc->report_desc_length;
|
||||
hidg->report_desc = kmemdup(fdesc->report_desc,
|
||||
fdesc->report_desc_length,
|
||||
GFP_KERNEL);
|
||||
if (!hidg->report_desc) {
|
||||
kfree(hidg);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hidg->func.name = "hid";
|
||||
hidg->func.strings = ct_func_strings;
|
||||
hidg->func.bind = hidg_bind;
|
||||
hidg->func.unbind = hidg_unbind;
|
||||
hidg->func.set_alt = hidg_set_alt;
|
||||
hidg->func.disable = hidg_disable;
|
||||
hidg->func.setup = hidg_setup;
|
||||
|
||||
status = usb_add_function(c, &hidg->func);
|
||||
if (status)
|
||||
kfree(hidg);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int __init ghid_setup(struct usb_gadget *g, int count)
|
||||
{
|
||||
int status;
|
||||
dev_t dev;
|
||||
|
||||
hidg_class = class_create(THIS_MODULE, "hidg");
|
||||
|
||||
status = alloc_chrdev_region(&dev, 0, count, "hidg");
|
||||
if (!status) {
|
||||
major = MAJOR(dev);
|
||||
minors = count;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void ghid_cleanup(void)
|
||||
{
|
||||
if (major) {
|
||||
unregister_chrdev_region(MKDEV(major, 0), minors);
|
||||
major = minors = 0;
|
||||
}
|
||||
|
||||
class_destroy(hidg_class);
|
||||
hidg_class = NULL;
|
||||
}
|
||||
|
|
@ -163,6 +163,10 @@
|
|||
* ro setting are not allowed when the medium is loaded or if CD-ROM
|
||||
* emulation is being used.
|
||||
*
|
||||
* When a LUN receive an "eject" SCSI request (Start/Stop Unit),
|
||||
* if the LUN is removable, the backing file is released to simulate
|
||||
* ejection.
|
||||
*
|
||||
*
|
||||
* This function is heavily based on "File-backed Storage Gadget" by
|
||||
* Alan Stern which in turn is heavily based on "Gadget Zero" by David
|
||||
|
|
@ -302,7 +306,6 @@ static const char fsg_string_interface[] = "Mass Storage";
|
|||
|
||||
|
||||
#define FSG_NO_INTR_EP 1
|
||||
#define FSG_BUFFHD_STATIC_BUFFER 1
|
||||
#define FSG_NO_DEVICE_STRINGS 1
|
||||
#define FSG_NO_OTG 1
|
||||
#define FSG_NO_INTR_EP 1
|
||||
|
|
@ -1385,12 +1388,50 @@ static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
|
|||
|
||||
static int do_start_stop(struct fsg_common *common)
|
||||
{
|
||||
if (!common->curlun) {
|
||||
struct fsg_lun *curlun = common->curlun;
|
||||
int loej, start;
|
||||
|
||||
if (!curlun) {
|
||||
return -EINVAL;
|
||||
} else if (!common->curlun->removable) {
|
||||
common->curlun->sense_data = SS_INVALID_COMMAND;
|
||||
} else if (!curlun->removable) {
|
||||
curlun->sense_data = SS_INVALID_COMMAND;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
loej = common->cmnd[4] & 0x02;
|
||||
start = common->cmnd[4] & 0x01;
|
||||
|
||||
/* eject code from file_storage.c:do_start_stop() */
|
||||
|
||||
if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
|
||||
(common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
|
||||
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!start) {
|
||||
/* Are we allowed to unload the media? */
|
||||
if (curlun->prevent_medium_removal) {
|
||||
LDBG(curlun, "unload attempt prevented\n");
|
||||
curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (loej) { /* Simulate an unload/eject */
|
||||
up_read(&common->filesem);
|
||||
down_write(&common->filesem);
|
||||
fsg_lun_close(curlun);
|
||||
up_write(&common->filesem);
|
||||
down_read(&common->filesem);
|
||||
}
|
||||
} else {
|
||||
|
||||
/* Our emulation doesn't support mounting; the medium is
|
||||
* available for use as soon as it is loaded. */
|
||||
if (!fsg_lun_is_open(curlun)) {
|
||||
curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2701,10 +2742,8 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
|||
/* Maybe allocate device-global string IDs, and patch descriptors */
|
||||
if (fsg_strings[FSG_STRING_INTERFACE].id == 0) {
|
||||
rc = usb_string_id(cdev);
|
||||
if (rc < 0) {
|
||||
kfree(common);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
if (unlikely(rc < 0))
|
||||
goto error_release;
|
||||
fsg_strings[FSG_STRING_INTERFACE].id = rc;
|
||||
fsg_intf_desc.iInterface = rc;
|
||||
}
|
||||
|
|
@ -2712,9 +2751,9 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
|||
/* Create the LUNs, open their backing files, and register the
|
||||
* LUN devices in sysfs. */
|
||||
curlun = kzalloc(nluns * sizeof *curlun, GFP_KERNEL);
|
||||
if (!curlun) {
|
||||
kfree(common);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (unlikely(!curlun)) {
|
||||
rc = -ENOMEM;
|
||||
goto error_release;
|
||||
}
|
||||
common->luns = curlun;
|
||||
|
||||
|
|
@ -2762,13 +2801,19 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
|||
|
||||
|
||||
/* Data buffers cyclic list */
|
||||
/* Buffers in buffhds are static -- no need for additional
|
||||
* allocation. */
|
||||
bh = common->buffhds;
|
||||
i = FSG_NUM_BUFFERS - 1;
|
||||
i = FSG_NUM_BUFFERS;
|
||||
goto buffhds_first_it;
|
||||
do {
|
||||
bh->next = bh + 1;
|
||||
} while (++bh, --i);
|
||||
++bh;
|
||||
buffhds_first_it:
|
||||
bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
|
||||
if (unlikely(!bh->buf)) {
|
||||
rc = -ENOMEM;
|
||||
goto error_release;
|
||||
}
|
||||
} while (--i);
|
||||
bh->next = common->buffhds;
|
||||
|
||||
|
||||
|
|
@ -2867,10 +2912,7 @@ error_release:
|
|||
|
||||
static void fsg_common_release(struct kref *ref)
|
||||
{
|
||||
struct fsg_common *common =
|
||||
container_of(ref, struct fsg_common, ref);
|
||||
unsigned i = common->nluns;
|
||||
struct fsg_lun *lun = common->luns;
|
||||
struct fsg_common *common = container_of(ref, struct fsg_common, ref);
|
||||
|
||||
/* If the thread isn't already dead, tell it to exit now */
|
||||
if (common->state != FSG_STATE_TERMINATED) {
|
||||
|
|
@ -2881,17 +2923,29 @@ static void fsg_common_release(struct kref *ref)
|
|||
complete(&common->thread_notifier);
|
||||
}
|
||||
|
||||
/* Beware tempting for -> do-while optimization: when in error
|
||||
* recovery nluns may be zero. */
|
||||
if (likely(common->luns)) {
|
||||
struct fsg_lun *lun = common->luns;
|
||||
unsigned i = common->nluns;
|
||||
|
||||
for (; i; --i, ++lun) {
|
||||
device_remove_file(&lun->dev, &dev_attr_ro);
|
||||
device_remove_file(&lun->dev, &dev_attr_file);
|
||||
fsg_lun_close(lun);
|
||||
device_unregister(&lun->dev);
|
||||
/* In error recovery common->nluns may be zero. */
|
||||
for (; i; --i, ++lun) {
|
||||
device_remove_file(&lun->dev, &dev_attr_ro);
|
||||
device_remove_file(&lun->dev, &dev_attr_file);
|
||||
fsg_lun_close(lun);
|
||||
device_unregister(&lun->dev);
|
||||
}
|
||||
|
||||
kfree(common->luns);
|
||||
}
|
||||
|
||||
{
|
||||
struct fsg_buffhd *bh = common->buffhds;
|
||||
unsigned i = FSG_NUM_BUFFERS;
|
||||
do {
|
||||
kfree(bh->buf);
|
||||
} while (++bh, --i);
|
||||
}
|
||||
|
||||
kfree(common->luns);
|
||||
if (common->free_storage_on_release)
|
||||
kfree(common);
|
||||
}
|
||||
|
|
@ -2906,11 +2960,13 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
DBG(fsg, "unbind\n");
|
||||
fsg_common_put(fsg->common);
|
||||
usb_free_descriptors(fsg->function.descriptors);
|
||||
usb_free_descriptors(fsg->function.hs_descriptors);
|
||||
kfree(fsg);
|
||||
}
|
||||
|
||||
|
||||
static int __init fsg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct fsg_dev *fsg = fsg_from_func(f);
|
||||
struct usb_gadget *gadget = c->cdev->gadget;
|
||||
|
|
@ -2946,7 +3002,9 @@ static int __init fsg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
fsg_fs_bulk_in_desc.bEndpointAddress;
|
||||
fsg_hs_bulk_out_desc.bEndpointAddress =
|
||||
fsg_fs_bulk_out_desc.bEndpointAddress;
|
||||
f->hs_descriptors = fsg_hs_function;
|
||||
f->hs_descriptors = usb_copy_descriptors(fsg_hs_function);
|
||||
if (unlikely(!f->hs_descriptors))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -2978,7 +3036,11 @@ static int fsg_add(struct usb_composite_dev *cdev,
|
|||
|
||||
fsg->function.name = FSG_DRIVER_DESC;
|
||||
fsg->function.strings = fsg_strings_array;
|
||||
fsg->function.descriptors = fsg_fs_function;
|
||||
fsg->function.descriptors = usb_copy_descriptors(fsg_fs_function);
|
||||
if (unlikely(!fsg->function.descriptors)) {
|
||||
rc = -ENOMEM;
|
||||
goto error_free_fsg;
|
||||
}
|
||||
fsg->function.bind = fsg_bind;
|
||||
fsg->function.unbind = fsg_unbind;
|
||||
fsg->function.setup = fsg_setup;
|
||||
|
|
@ -2993,11 +3055,19 @@ static int fsg_add(struct usb_composite_dev *cdev,
|
|||
* call to usb_add_function() was successful. */
|
||||
|
||||
rc = usb_add_function(c, &fsg->function);
|
||||
if (unlikely(rc))
|
||||
goto error_free_all;
|
||||
|
||||
if (likely(rc == 0))
|
||||
fsg_common_get(fsg->common);
|
||||
else
|
||||
kfree(fsg);
|
||||
fsg_common_get(fsg->common);
|
||||
return 0;
|
||||
|
||||
error_free_all:
|
||||
usb_free_descriptors(fsg->function.descriptors);
|
||||
/* fsg_bind() might have copied those; or maybe not? who cares
|
||||
* -- free it just in case. */
|
||||
usb_free_descriptors(fsg->function.hs_descriptors);
|
||||
error_free_fsg:
|
||||
kfree(fsg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ static unsigned int bitrate(struct usb_gadget *g)
|
|||
|
||||
/* interface descriptor: */
|
||||
|
||||
static struct usb_interface_descriptor rndis_control_intf __initdata = {
|
||||
static struct usb_interface_descriptor rndis_control_intf = {
|
||||
.bLength = sizeof rndis_control_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ static struct usb_interface_descriptor rndis_control_intf __initdata = {
|
|||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_cdc_header_desc header_desc __initdata = {
|
||||
static struct usb_cdc_header_desc header_desc = {
|
||||
.bLength = sizeof header_desc,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
|
||||
|
|
@ -143,7 +143,7 @@ static struct usb_cdc_header_desc header_desc __initdata = {
|
|||
.bcdCDC = cpu_to_le16(0x0110),
|
||||
};
|
||||
|
||||
static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
|
||||
static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
|
||||
.bLength = sizeof call_mgmt_descriptor,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
|
||||
|
|
@ -152,7 +152,7 @@ static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
|
|||
.bDataInterface = 0x01,
|
||||
};
|
||||
|
||||
static struct usb_cdc_acm_descriptor rndis_acm_descriptor __initdata = {
|
||||
static struct usb_cdc_acm_descriptor rndis_acm_descriptor = {
|
||||
.bLength = sizeof rndis_acm_descriptor,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_ACM_TYPE,
|
||||
|
|
@ -160,7 +160,7 @@ static struct usb_cdc_acm_descriptor rndis_acm_descriptor __initdata = {
|
|||
.bmCapabilities = 0x00,
|
||||
};
|
||||
|
||||
static struct usb_cdc_union_desc rndis_union_desc __initdata = {
|
||||
static struct usb_cdc_union_desc rndis_union_desc = {
|
||||
.bLength = sizeof(rndis_union_desc),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_UNION_TYPE,
|
||||
|
|
@ -170,7 +170,7 @@ static struct usb_cdc_union_desc rndis_union_desc __initdata = {
|
|||
|
||||
/* the data interface has two bulk endpoints */
|
||||
|
||||
static struct usb_interface_descriptor rndis_data_intf __initdata = {
|
||||
static struct usb_interface_descriptor rndis_data_intf = {
|
||||
.bLength = sizeof rndis_data_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
|
|
@ -198,7 +198,7 @@ rndis_iad_descriptor = {
|
|||
|
||||
/* full speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor fs_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
|
|||
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor fs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -216,7 +216,7 @@ static struct usb_endpoint_descriptor fs_in_desc __initdata = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor fs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *eth_fs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *eth_fs_function[] = {
|
||||
(struct usb_descriptor_header *) &rndis_iad_descriptor,
|
||||
/* control interface matches ACM, not Ethernet */
|
||||
(struct usb_descriptor_header *) &rndis_control_intf,
|
||||
|
|
@ -242,7 +242,7 @@ static struct usb_descriptor_header *eth_fs_function[] __initdata = {
|
|||
|
||||
/* high speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor hs_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -251,7 +251,7 @@ static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
|
|||
.wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
|
||||
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
|
||||
};
|
||||
static struct usb_endpoint_descriptor hs_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor hs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -260,7 +260,7 @@ static struct usb_endpoint_descriptor hs_in_desc __initdata = {
|
|||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hs_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor hs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = {
|
|||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *eth_hs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *eth_hs_function[] = {
|
||||
(struct usb_descriptor_header *) &rndis_iad_descriptor,
|
||||
/* control interface matches ACM, not Ethernet */
|
||||
(struct usb_descriptor_header *) &rndis_control_intf,
|
||||
|
|
@ -594,7 +594,7 @@ static void rndis_close(struct gether *geth)
|
|||
|
||||
/* ethernet function driver setup/binding */
|
||||
|
||||
static int __init
|
||||
static int
|
||||
rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
|
|
@ -786,7 +786,8 @@ static inline bool can_support_rndis(struct usb_configuration *c)
|
|||
* Caller must have called @gether_setup(). Caller is also responsible
|
||||
* for calling @gether_cleanup() before module unload.
|
||||
*/
|
||||
int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
int
|
||||
rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
{
|
||||
struct f_rndis *rndis;
|
||||
int status;
|
||||
|
|
|
|||
661
drivers/usb/gadget/f_uvc.c
Normal file
661
drivers/usb/gadget/f_uvc.c
Normal file
|
|
@ -0,0 +1,661 @@
|
|||
/*
|
||||
* uvc_gadget.c -- USB Video Class Gadget driver
|
||||
*
|
||||
* Copyright (C) 2009-2010
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/video.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-event.h>
|
||||
|
||||
#include "uvc.h"
|
||||
|
||||
unsigned int uvc_trace_param;
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Function descriptors
|
||||
*/
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define UVC_STRING_ASSOCIATION_IDX 0
|
||||
#define UVC_STRING_CONTROL_IDX 1
|
||||
#define UVC_STRING_STREAMING_IDX 2
|
||||
|
||||
static struct usb_string uvc_en_us_strings[] = {
|
||||
[UVC_STRING_ASSOCIATION_IDX].s = "UVC Camera",
|
||||
[UVC_STRING_CONTROL_IDX].s = "Video Control",
|
||||
[UVC_STRING_STREAMING_IDX].s = "Video Streaming",
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings uvc_stringtab = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = uvc_en_us_strings,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *uvc_function_strings[] = {
|
||||
&uvc_stringtab,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define UVC_INTF_VIDEO_CONTROL 0
|
||||
#define UVC_INTF_VIDEO_STREAMING 1
|
||||
|
||||
static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
|
||||
.bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
|
||||
.bFirstInterface = 0,
|
||||
.bInterfaceCount = 2,
|
||||
.bFunctionClass = USB_CLASS_VIDEO,
|
||||
.bFunctionSubClass = 0x03,
|
||||
.bFunctionProtocol = 0x00,
|
||||
.iFunction = 0,
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor uvc_control_intf __initdata = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = UVC_INTF_VIDEO_CONTROL,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_VIDEO,
|
||||
.bInterfaceSubClass = 0x01,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.iInterface = 0,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(16),
|
||||
.bInterval = 8,
|
||||
};
|
||||
|
||||
static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = {
|
||||
.bLength = UVC_DT_CONTROL_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_ENDPOINT,
|
||||
.bDescriptorSubType = UVC_EP_INTERRUPT,
|
||||
.wMaxTransferSize = cpu_to_le16(16),
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = UVC_INTF_VIDEO_STREAMING,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 0,
|
||||
.bInterfaceClass = USB_CLASS_VIDEO,
|
||||
.bInterfaceSubClass = 0x02,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.iInterface = 0,
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = UVC_INTF_VIDEO_STREAMING,
|
||||
.bAlternateSetting = 1,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_VIDEO,
|
||||
.bInterfaceSubClass = 0x02,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.iInterface = 0,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor uvc_streaming_ep = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
.bInterval = 1,
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
|
||||
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
|
||||
(struct usb_descriptor_header *) &uvc_streaming_ep,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header * const uvc_hs_streaming[] = {
|
||||
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
|
||||
(struct usb_descriptor_header *) &uvc_streaming_ep,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Control requests
|
||||
*/
|
||||
|
||||
static void
|
||||
uvc_function_ep0_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct uvc_device *uvc = req->context;
|
||||
struct v4l2_event v4l2_event;
|
||||
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
|
||||
|
||||
if (uvc->event_setup_out) {
|
||||
uvc->event_setup_out = 0;
|
||||
|
||||
memset(&v4l2_event, 0, sizeof(v4l2_event));
|
||||
v4l2_event.type = UVC_EVENT_DATA;
|
||||
uvc_event->data.length = req->actual;
|
||||
memcpy(&uvc_event->data.data, req->buf, req->actual);
|
||||
v4l2_event_queue(uvc->vdev, &v4l2_event);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
struct v4l2_event v4l2_event;
|
||||
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
|
||||
|
||||
/* printk(KERN_INFO "setup request %02x %02x value %04x index %04x %04x\n",
|
||||
* ctrl->bRequestType, ctrl->bRequest, le16_to_cpu(ctrl->wValue),
|
||||
* le16_to_cpu(ctrl->wIndex), le16_to_cpu(ctrl->wLength));
|
||||
*/
|
||||
|
||||
if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) {
|
||||
INFO(f->config->cdev, "invalid request type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Stall too big requests. */
|
||||
if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&v4l2_event, 0, sizeof(v4l2_event));
|
||||
v4l2_event.type = UVC_EVENT_SETUP;
|
||||
memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req));
|
||||
v4l2_event_queue(uvc->vdev, &v4l2_event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_function_get_alt(struct usb_function *f, unsigned interface)
|
||||
{
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
|
||||
INFO(f->config->cdev, "uvc_function_get_alt(%u)\n", interface);
|
||||
|
||||
if (interface == uvc->control_intf)
|
||||
return 0;
|
||||
else if (interface != uvc->streaming_intf)
|
||||
return -EINVAL;
|
||||
else
|
||||
return uvc->state == UVC_STATE_STREAMING ? 1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
|
||||
{
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
struct v4l2_event v4l2_event;
|
||||
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
|
||||
|
||||
INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
|
||||
|
||||
if (interface == uvc->control_intf) {
|
||||
if (alt)
|
||||
return -EINVAL;
|
||||
|
||||
if (uvc->state == UVC_STATE_DISCONNECTED) {
|
||||
memset(&v4l2_event, 0, sizeof(v4l2_event));
|
||||
v4l2_event.type = UVC_EVENT_CONNECT;
|
||||
uvc_event->speed = f->config->cdev->gadget->speed;
|
||||
v4l2_event_queue(uvc->vdev, &v4l2_event);
|
||||
|
||||
uvc->state = UVC_STATE_CONNECTED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (interface != uvc->streaming_intf)
|
||||
return -EINVAL;
|
||||
|
||||
/* TODO
|
||||
if (usb_endpoint_xfer_bulk(&uvc->desc.vs_ep))
|
||||
return alt ? -EINVAL : 0;
|
||||
*/
|
||||
|
||||
switch (alt) {
|
||||
case 0:
|
||||
if (uvc->state != UVC_STATE_STREAMING)
|
||||
return 0;
|
||||
|
||||
if (uvc->video.ep)
|
||||
usb_ep_disable(uvc->video.ep);
|
||||
|
||||
memset(&v4l2_event, 0, sizeof(v4l2_event));
|
||||
v4l2_event.type = UVC_EVENT_STREAMOFF;
|
||||
v4l2_event_queue(uvc->vdev, &v4l2_event);
|
||||
|
||||
uvc->state = UVC_STATE_CONNECTED;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (uvc->state != UVC_STATE_CONNECTED)
|
||||
return 0;
|
||||
|
||||
if (uvc->video.ep)
|
||||
usb_ep_enable(uvc->video.ep, &uvc_streaming_ep);
|
||||
|
||||
memset(&v4l2_event, 0, sizeof(v4l2_event));
|
||||
v4l2_event.type = UVC_EVENT_STREAMON;
|
||||
v4l2_event_queue(uvc->vdev, &v4l2_event);
|
||||
|
||||
uvc->state = UVC_STATE_STREAMING;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
uvc_function_disable(struct usb_function *f)
|
||||
{
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
struct v4l2_event v4l2_event;
|
||||
|
||||
INFO(f->config->cdev, "uvc_function_disable\n");
|
||||
|
||||
memset(&v4l2_event, 0, sizeof(v4l2_event));
|
||||
v4l2_event.type = UVC_EVENT_DISCONNECT;
|
||||
v4l2_event_queue(uvc->vdev, &v4l2_event);
|
||||
|
||||
uvc->state = UVC_STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Connection / disconnection
|
||||
*/
|
||||
|
||||
void
|
||||
uvc_function_connect(struct uvc_device *uvc)
|
||||
{
|
||||
struct usb_composite_dev *cdev = uvc->func.config->cdev;
|
||||
int ret;
|
||||
|
||||
if ((ret = usb_function_activate(&uvc->func)) < 0)
|
||||
INFO(cdev, "UVC connect failed with %d\n", ret);
|
||||
}
|
||||
|
||||
void
|
||||
uvc_function_disconnect(struct uvc_device *uvc)
|
||||
{
|
||||
struct usb_composite_dev *cdev = uvc->func.config->cdev;
|
||||
int ret;
|
||||
|
||||
if ((ret = usb_function_deactivate(&uvc->func)) < 0)
|
||||
INFO(cdev, "UVC disconnect failed with %d\n", ret);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* USB probe and disconnect
|
||||
*/
|
||||
|
||||
static int
|
||||
uvc_register_video(struct uvc_device *uvc)
|
||||
{
|
||||
struct usb_composite_dev *cdev = uvc->func.config->cdev;
|
||||
struct video_device *video;
|
||||
|
||||
/* TODO reference counting. */
|
||||
video = video_device_alloc();
|
||||
if (video == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
video->parent = &cdev->gadget->dev;
|
||||
video->minor = -1;
|
||||
video->fops = &uvc_v4l2_fops;
|
||||
video->release = video_device_release;
|
||||
strncpy(video->name, cdev->gadget->name, sizeof(video->name));
|
||||
|
||||
uvc->vdev = video;
|
||||
video_set_drvdata(video, uvc);
|
||||
|
||||
return video_register_device(video, VFL_TYPE_GRABBER, -1);
|
||||
}
|
||||
|
||||
#define UVC_COPY_DESCRIPTOR(mem, dst, desc) \
|
||||
do { \
|
||||
memcpy(mem, desc, (desc)->bLength); \
|
||||
*(dst)++ = mem; \
|
||||
mem += (desc)->bLength; \
|
||||
} while (0);
|
||||
|
||||
#define UVC_COPY_DESCRIPTORS(mem, dst, src) \
|
||||
do { \
|
||||
const struct usb_descriptor_header * const *__src; \
|
||||
for (__src = src; *__src; ++__src) { \
|
||||
memcpy(mem, *__src, (*__src)->bLength); \
|
||||
*dst++ = mem; \
|
||||
mem += (*__src)->bLength; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static struct usb_descriptor_header ** __init
|
||||
uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
|
||||
{
|
||||
struct uvc_input_header_descriptor *uvc_streaming_header;
|
||||
struct uvc_header_descriptor *uvc_control_header;
|
||||
const struct uvc_descriptor_header * const *uvc_streaming_cls;
|
||||
const struct usb_descriptor_header * const *uvc_streaming_std;
|
||||
const struct usb_descriptor_header * const *src;
|
||||
struct usb_descriptor_header **dst;
|
||||
struct usb_descriptor_header **hdr;
|
||||
unsigned int control_size;
|
||||
unsigned int streaming_size;
|
||||
unsigned int n_desc;
|
||||
unsigned int bytes;
|
||||
void *mem;
|
||||
|
||||
uvc_streaming_cls = (speed == USB_SPEED_FULL)
|
||||
? uvc->desc.fs_streaming : uvc->desc.hs_streaming;
|
||||
uvc_streaming_std = (speed == USB_SPEED_FULL)
|
||||
? uvc_fs_streaming : uvc_hs_streaming;
|
||||
|
||||
/* Descriptors layout
|
||||
*
|
||||
* uvc_iad
|
||||
* uvc_control_intf
|
||||
* Class-specific UVC control descriptors
|
||||
* uvc_control_ep
|
||||
* uvc_control_cs_ep
|
||||
* uvc_streaming_intf_alt0
|
||||
* Class-specific UVC streaming descriptors
|
||||
* uvc_{fs|hs}_streaming
|
||||
*/
|
||||
|
||||
/* Count descriptors and compute their size. */
|
||||
control_size = 0;
|
||||
streaming_size = 0;
|
||||
bytes = uvc_iad.bLength + uvc_control_intf.bLength
|
||||
+ uvc_control_ep.bLength + uvc_control_cs_ep.bLength
|
||||
+ uvc_streaming_intf_alt0.bLength;
|
||||
n_desc = 5;
|
||||
|
||||
for (src = (const struct usb_descriptor_header**)uvc->desc.control; *src; ++src) {
|
||||
control_size += (*src)->bLength;
|
||||
bytes += (*src)->bLength;
|
||||
n_desc++;
|
||||
}
|
||||
for (src = (const struct usb_descriptor_header**)uvc_streaming_cls; *src; ++src) {
|
||||
streaming_size += (*src)->bLength;
|
||||
bytes += (*src)->bLength;
|
||||
n_desc++;
|
||||
}
|
||||
for (src = uvc_streaming_std; *src; ++src) {
|
||||
bytes += (*src)->bLength;
|
||||
n_desc++;
|
||||
}
|
||||
|
||||
mem = kmalloc((n_desc + 1) * sizeof(*src) + bytes, GFP_KERNEL);
|
||||
if (mem == NULL)
|
||||
return NULL;
|
||||
|
||||
hdr = mem;
|
||||
dst = mem;
|
||||
mem += (n_desc + 1) * sizeof(*src);
|
||||
|
||||
/* Copy the descriptors. */
|
||||
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_iad);
|
||||
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_intf);
|
||||
|
||||
uvc_control_header = mem;
|
||||
UVC_COPY_DESCRIPTORS(mem, dst,
|
||||
(const struct usb_descriptor_header**)uvc->desc.control);
|
||||
uvc_control_header->wTotalLength = cpu_to_le16(control_size);
|
||||
uvc_control_header->bInCollection = 1;
|
||||
uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf;
|
||||
|
||||
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep);
|
||||
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep);
|
||||
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);
|
||||
|
||||
uvc_streaming_header = mem;
|
||||
UVC_COPY_DESCRIPTORS(mem, dst,
|
||||
(const struct usb_descriptor_header**)uvc_streaming_cls);
|
||||
uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size);
|
||||
uvc_streaming_header->bEndpointAddress = uvc_streaming_ep.bEndpointAddress;
|
||||
|
||||
UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std);
|
||||
|
||||
*dst = NULL;
|
||||
return hdr;
|
||||
}
|
||||
|
||||
static void
|
||||
uvc_function_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
|
||||
INFO(cdev, "uvc_function_unbind\n");
|
||||
|
||||
if (uvc->vdev) {
|
||||
if (uvc->vdev->minor == -1)
|
||||
video_device_release(uvc->vdev);
|
||||
else
|
||||
video_unregister_device(uvc->vdev);
|
||||
uvc->vdev = NULL;
|
||||
}
|
||||
|
||||
if (uvc->control_ep)
|
||||
uvc->control_ep->driver_data = NULL;
|
||||
if (uvc->video.ep)
|
||||
uvc->video.ep->driver_data = NULL;
|
||||
|
||||
if (uvc->control_req) {
|
||||
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
|
||||
kfree(uvc->control_buf);
|
||||
}
|
||||
|
||||
kfree(f->descriptors);
|
||||
kfree(f->hs_descriptors);
|
||||
|
||||
kfree(uvc);
|
||||
}
|
||||
|
||||
static int __init
|
||||
uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
struct usb_ep *ep;
|
||||
int ret = -EINVAL;
|
||||
|
||||
INFO(cdev, "uvc_function_bind\n");
|
||||
|
||||
/* Allocate endpoints. */
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
|
||||
if (!ep) {
|
||||
INFO(cdev, "Unable to allocate control EP\n");
|
||||
goto error;
|
||||
}
|
||||
uvc->control_ep = ep;
|
||||
ep->driver_data = uvc;
|
||||
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &uvc_streaming_ep);
|
||||
if (!ep) {
|
||||
INFO(cdev, "Unable to allocate streaming EP\n");
|
||||
goto error;
|
||||
}
|
||||
uvc->video.ep = ep;
|
||||
ep->driver_data = uvc;
|
||||
|
||||
/* Allocate interface IDs. */
|
||||
if ((ret = usb_interface_id(c, f)) < 0)
|
||||
goto error;
|
||||
uvc_iad.bFirstInterface = ret;
|
||||
uvc_control_intf.bInterfaceNumber = ret;
|
||||
uvc->control_intf = ret;
|
||||
|
||||
if ((ret = usb_interface_id(c, f)) < 0)
|
||||
goto error;
|
||||
uvc_streaming_intf_alt0.bInterfaceNumber = ret;
|
||||
uvc_streaming_intf_alt1.bInterfaceNumber = ret;
|
||||
uvc->streaming_intf = ret;
|
||||
|
||||
/* Copy descriptors. */
|
||||
f->descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
|
||||
f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
|
||||
|
||||
/* Preallocate control endpoint request. */
|
||||
uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
|
||||
uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL);
|
||||
if (uvc->control_req == NULL || uvc->control_buf == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
uvc->control_req->buf = uvc->control_buf;
|
||||
uvc->control_req->complete = uvc_function_ep0_complete;
|
||||
uvc->control_req->context = uvc;
|
||||
|
||||
/* Avoid letting this gadget enumerate until the userspace server is
|
||||
* active.
|
||||
*/
|
||||
if ((ret = usb_function_deactivate(f)) < 0)
|
||||
goto error;
|
||||
|
||||
/* Initialise video. */
|
||||
ret = uvc_video_init(&uvc->video);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* Register a V4L2 device. */
|
||||
ret = uvc_register_video(uvc);
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "Unable to register video device\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
uvc_function_unbind(c, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* USB gadget function
|
||||
*/
|
||||
|
||||
/**
|
||||
* uvc_bind_config - add a UVC function to a configuration
|
||||
* @c: the configuration to support the UVC instance
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* Caller must have called @uvc_setup(). Caller is also responsible for
|
||||
* calling @uvc_cleanup() before module unload.
|
||||
*/
|
||||
int __init
|
||||
uvc_bind_config(struct usb_configuration *c,
|
||||
const struct uvc_descriptor_header * const *control,
|
||||
const struct uvc_descriptor_header * const *fs_streaming,
|
||||
const struct uvc_descriptor_header * const *hs_streaming)
|
||||
{
|
||||
struct uvc_device *uvc;
|
||||
int ret = 0;
|
||||
|
||||
/* TODO Check if the USB device controller supports the required
|
||||
* features.
|
||||
*/
|
||||
if (!gadget_is_dualspeed(c->cdev->gadget))
|
||||
return -EINVAL;
|
||||
|
||||
uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
|
||||
if (uvc == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
uvc->state = UVC_STATE_DISCONNECTED;
|
||||
|
||||
/* Validate the descriptors. */
|
||||
if (control == NULL || control[0] == NULL ||
|
||||
control[0]->bDescriptorSubType != UVC_DT_HEADER)
|
||||
goto error;
|
||||
|
||||
if (fs_streaming == NULL || fs_streaming[0] == NULL ||
|
||||
fs_streaming[0]->bDescriptorSubType != UVC_DT_INPUT_HEADER)
|
||||
goto error;
|
||||
|
||||
if (hs_streaming == NULL || hs_streaming[0] == NULL ||
|
||||
hs_streaming[0]->bDescriptorSubType != UVC_DT_INPUT_HEADER)
|
||||
goto error;
|
||||
|
||||
uvc->desc.control = control;
|
||||
uvc->desc.fs_streaming = fs_streaming;
|
||||
uvc->desc.hs_streaming = hs_streaming;
|
||||
|
||||
/* Allocate string descriptor numbers. */
|
||||
if ((ret = usb_string_id(c->cdev)) < 0)
|
||||
goto error;
|
||||
uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id = ret;
|
||||
uvc_iad.iFunction = ret;
|
||||
|
||||
if ((ret = usb_string_id(c->cdev)) < 0)
|
||||
goto error;
|
||||
uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = ret;
|
||||
uvc_control_intf.iInterface = ret;
|
||||
|
||||
if ((ret = usb_string_id(c->cdev)) < 0)
|
||||
goto error;
|
||||
uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id = ret;
|
||||
uvc_streaming_intf_alt0.iInterface = ret;
|
||||
uvc_streaming_intf_alt1.iInterface = ret;
|
||||
|
||||
/* Register the function. */
|
||||
uvc->func.name = "uvc";
|
||||
uvc->func.strings = uvc_function_strings;
|
||||
uvc->func.bind = uvc_function_bind;
|
||||
uvc->func.unbind = uvc_function_unbind;
|
||||
uvc->func.get_alt = uvc_function_get_alt;
|
||||
uvc->func.set_alt = uvc_function_set_alt;
|
||||
uvc->func.disable = uvc_function_disable;
|
||||
uvc->func.setup = uvc_function_setup;
|
||||
|
||||
ret = usb_add_function(c, &uvc->func);
|
||||
if (ret)
|
||||
kfree(uvc);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(uvc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(trace, "Trace level bitmask");
|
||||
|
||||
376
drivers/usb/gadget/f_uvc.h
Normal file
376
drivers/usb/gadget/f_uvc.h
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* f_uvc.h -- USB Video Class Gadget driver
|
||||
*
|
||||
* Copyright (C) 2009-2010
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _F_UVC_H_
|
||||
#define _F_UVC_H_
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#define USB_CLASS_VIDEO_CONTROL 1
|
||||
#define USB_CLASS_VIDEO_STREAMING 2
|
||||
|
||||
struct uvc_descriptor_header {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct uvc_header_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u16 bcdUVC;
|
||||
__u16 wTotalLength;
|
||||
__u32 dwClockFrequency;
|
||||
__u8 bInCollection;
|
||||
__u8 baInterfaceNr[];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define UVC_HEADER_DESCRIPTOR(n) uvc_header_descriptor_##n
|
||||
|
||||
#define DECLARE_UVC_HEADER_DESCRIPTOR(n) \
|
||||
struct UVC_HEADER_DESCRIPTOR(n) { \
|
||||
__u8 bLength; \
|
||||
__u8 bDescriptorType; \
|
||||
__u8 bDescriptorSubType; \
|
||||
__u16 bcdUVC; \
|
||||
__u16 wTotalLength; \
|
||||
__u32 dwClockFrequency; \
|
||||
__u8 bInCollection; \
|
||||
__u8 baInterfaceNr[n]; \
|
||||
} __attribute__ ((packed))
|
||||
|
||||
struct uvc_input_terminal_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bTerminalID;
|
||||
__u16 wTerminalType;
|
||||
__u8 bAssocTerminal;
|
||||
__u8 iTerminal;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct uvc_output_terminal_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bTerminalID;
|
||||
__u16 wTerminalType;
|
||||
__u8 bAssocTerminal;
|
||||
__u8 bSourceID;
|
||||
__u8 iTerminal;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct uvc_camera_terminal_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bTerminalID;
|
||||
__u16 wTerminalType;
|
||||
__u8 bAssocTerminal;
|
||||
__u8 iTerminal;
|
||||
__u16 wObjectiveFocalLengthMin;
|
||||
__u16 wObjectiveFocalLengthMax;
|
||||
__u16 wOcularFocalLength;
|
||||
__u8 bControlSize;
|
||||
__u8 bmControls[3];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct uvc_selector_unit_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bUnitID;
|
||||
__u8 bNrInPins;
|
||||
__u8 baSourceID[0];
|
||||
__u8 iSelector;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define UVC_SELECTOR_UNIT_DESCRIPTOR(n) \
|
||||
uvc_selector_unit_descriptor_##n
|
||||
|
||||
#define DECLARE_UVC_SELECTOR_UNIT_DESCRIPTOR(n) \
|
||||
struct UVC_SELECTOR_UNIT_DESCRIPTOR(n) { \
|
||||
__u8 bLength; \
|
||||
__u8 bDescriptorType; \
|
||||
__u8 bDescriptorSubType; \
|
||||
__u8 bUnitID; \
|
||||
__u8 bNrInPins; \
|
||||
__u8 baSourceID[n]; \
|
||||
__u8 iSelector; \
|
||||
} __attribute__ ((packed))
|
||||
|
||||
struct uvc_processing_unit_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bUnitID;
|
||||
__u8 bSourceID;
|
||||
__u16 wMaxMultiplier;
|
||||
__u8 bControlSize;
|
||||
__u8 bmControls[2];
|
||||
__u8 iProcessing;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct uvc_extension_unit_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bUnitID;
|
||||
__u8 guidExtensionCode[16];
|
||||
__u8 bNumControls;
|
||||
__u8 bNrInPins;
|
||||
__u8 baSourceID[0];
|
||||
__u8 bControlSize;
|
||||
__u8 bmControls[0];
|
||||
__u8 iExtension;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) \
|
||||
uvc_extension_unit_descriptor_##p_##n
|
||||
|
||||
#define DECLARE_UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) \
|
||||
struct UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) { \
|
||||
__u8 bLength; \
|
||||
__u8 bDescriptorType; \
|
||||
__u8 bDescriptorSubType; \
|
||||
__u8 bUnitID; \
|
||||
__u8 guidExtensionCode[16]; \
|
||||
__u8 bNumControls; \
|
||||
__u8 bNrInPins; \
|
||||
__u8 baSourceID[p]; \
|
||||
__u8 bControlSize; \
|
||||
__u8 bmControls[n]; \
|
||||
__u8 iExtension; \
|
||||
} __attribute__ ((packed))
|
||||
|
||||
struct uvc_control_endpoint_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u16 wMaxTransferSize;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define UVC_DT_HEADER 1
|
||||
#define UVC_DT_INPUT_TERMINAL 2
|
||||
#define UVC_DT_OUTPUT_TERMINAL 3
|
||||
#define UVC_DT_SELECTOR_UNIT 4
|
||||
#define UVC_DT_PROCESSING_UNIT 5
|
||||
#define UVC_DT_EXTENSION_UNIT 6
|
||||
|
||||
#define UVC_DT_HEADER_SIZE(n) (12+(n))
|
||||
#define UVC_DT_INPUT_TERMINAL_SIZE 8
|
||||
#define UVC_DT_OUTPUT_TERMINAL_SIZE 9
|
||||
#define UVC_DT_CAMERA_TERMINAL_SIZE(n) (15+(n))
|
||||
#define UVC_DT_SELECTOR_UNIT_SIZE(n) (6+(n))
|
||||
#define UVC_DT_PROCESSING_UNIT_SIZE(n) (9+(n))
|
||||
#define UVC_DT_EXTENSION_UNIT_SIZE(p,n) (24+(p)+(n))
|
||||
#define UVC_DT_CONTROL_ENDPOINT_SIZE 5
|
||||
|
||||
struct uvc_input_header_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bNumFormats;
|
||||
__u16 wTotalLength;
|
||||
__u8 bEndpointAddress;
|
||||
__u8 bmInfo;
|
||||
__u8 bTerminalLink;
|
||||
__u8 bStillCaptureMethod;
|
||||
__u8 bTriggerSupport;
|
||||
__u8 bTriggerUsage;
|
||||
__u8 bControlSize;
|
||||
__u8 bmaControls[];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define UVC_INPUT_HEADER_DESCRIPTOR(n, p) \
|
||||
uvc_input_header_descriptor_##n_##p
|
||||
|
||||
#define DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(n, p) \
|
||||
struct UVC_INPUT_HEADER_DESCRIPTOR(n, p) { \
|
||||
__u8 bLength; \
|
||||
__u8 bDescriptorType; \
|
||||
__u8 bDescriptorSubType; \
|
||||
__u8 bNumFormats; \
|
||||
__u16 wTotalLength; \
|
||||
__u8 bEndpointAddress; \
|
||||
__u8 bmInfo; \
|
||||
__u8 bTerminalLink; \
|
||||
__u8 bStillCaptureMethod; \
|
||||
__u8 bTriggerSupport; \
|
||||
__u8 bTriggerUsage; \
|
||||
__u8 bControlSize; \
|
||||
__u8 bmaControls[p][n]; \
|
||||
} __attribute__ ((packed))
|
||||
|
||||
struct uvc_output_header_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bNumFormats;
|
||||
__u16 wTotalLength;
|
||||
__u8 bEndpointAddress;
|
||||
__u8 bTerminalLink;
|
||||
__u8 bControlSize;
|
||||
__u8 bmaControls[];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) \
|
||||
uvc_output_header_descriptor_##n_##p
|
||||
|
||||
#define DECLARE_UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) \
|
||||
struct UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) { \
|
||||
__u8 bLength; \
|
||||
__u8 bDescriptorType; \
|
||||
__u8 bDescriptorSubType; \
|
||||
__u8 bNumFormats; \
|
||||
__u16 wTotalLength; \
|
||||
__u8 bEndpointAddress; \
|
||||
__u8 bTerminalLink; \
|
||||
__u8 bControlSize; \
|
||||
__u8 bmaControls[p][n]; \
|
||||
} __attribute__ ((packed))
|
||||
|
||||
struct uvc_format_uncompressed {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bFormatIndex;
|
||||
__u8 bNumFrameDescriptors;
|
||||
__u8 guidFormat[16];
|
||||
__u8 bBitsPerPixel;
|
||||
__u8 bDefaultFrameIndex;
|
||||
__u8 bAspectRatioX;
|
||||
__u8 bAspectRatioY;
|
||||
__u8 bmInterfaceFlags;
|
||||
__u8 bCopyProtect;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct uvc_frame_uncompressed {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bFrameIndex;
|
||||
__u8 bmCapabilities;
|
||||
__u16 wWidth;
|
||||
__u16 wHeight;
|
||||
__u32 dwMinBitRate;
|
||||
__u32 dwMaxBitRate;
|
||||
__u32 dwMaxVideoFrameBufferSize;
|
||||
__u32 dwDefaultFrameInterval;
|
||||
__u8 bFrameIntervalType;
|
||||
__u32 dwFrameInterval[];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define UVC_FRAME_UNCOMPRESSED(n) \
|
||||
uvc_frame_uncompressed_##n
|
||||
|
||||
#define DECLARE_UVC_FRAME_UNCOMPRESSED(n) \
|
||||
struct UVC_FRAME_UNCOMPRESSED(n) { \
|
||||
__u8 bLength; \
|
||||
__u8 bDescriptorType; \
|
||||
__u8 bDescriptorSubType; \
|
||||
__u8 bFrameIndex; \
|
||||
__u8 bmCapabilities; \
|
||||
__u16 wWidth; \
|
||||
__u16 wHeight; \
|
||||
__u32 dwMinBitRate; \
|
||||
__u32 dwMaxBitRate; \
|
||||
__u32 dwMaxVideoFrameBufferSize; \
|
||||
__u32 dwDefaultFrameInterval; \
|
||||
__u8 bFrameIntervalType; \
|
||||
__u32 dwFrameInterval[n]; \
|
||||
} __attribute__ ((packed))
|
||||
|
||||
struct uvc_format_mjpeg {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bFormatIndex;
|
||||
__u8 bNumFrameDescriptors;
|
||||
__u8 bmFlags;
|
||||
__u8 bDefaultFrameIndex;
|
||||
__u8 bAspectRatioX;
|
||||
__u8 bAspectRatioY;
|
||||
__u8 bmInterfaceFlags;
|
||||
__u8 bCopyProtect;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct uvc_frame_mjpeg {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bFrameIndex;
|
||||
__u8 bmCapabilities;
|
||||
__u16 wWidth;
|
||||
__u16 wHeight;
|
||||
__u32 dwMinBitRate;
|
||||
__u32 dwMaxBitRate;
|
||||
__u32 dwMaxVideoFrameBufferSize;
|
||||
__u32 dwDefaultFrameInterval;
|
||||
__u8 bFrameIntervalType;
|
||||
__u32 dwFrameInterval[];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define UVC_FRAME_MJPEG(n) \
|
||||
uvc_frame_mjpeg_##n
|
||||
|
||||
#define DECLARE_UVC_FRAME_MJPEG(n) \
|
||||
struct UVC_FRAME_MJPEG(n) { \
|
||||
__u8 bLength; \
|
||||
__u8 bDescriptorType; \
|
||||
__u8 bDescriptorSubType; \
|
||||
__u8 bFrameIndex; \
|
||||
__u8 bmCapabilities; \
|
||||
__u16 wWidth; \
|
||||
__u16 wHeight; \
|
||||
__u32 dwMinBitRate; \
|
||||
__u32 dwMaxBitRate; \
|
||||
__u32 dwMaxVideoFrameBufferSize; \
|
||||
__u32 dwDefaultFrameInterval; \
|
||||
__u8 bFrameIntervalType; \
|
||||
__u32 dwFrameInterval[n]; \
|
||||
} __attribute__ ((packed))
|
||||
|
||||
struct uvc_color_matching_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u8 bColorPrimaries;
|
||||
__u8 bTransferCharacteristics;
|
||||
__u8 bMatrixCoefficients;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define UVC_DT_INPUT_HEADER 1
|
||||
#define UVC_DT_OUTPUT_HEADER 2
|
||||
#define UVC_DT_FORMAT_UNCOMPRESSED 4
|
||||
#define UVC_DT_FRAME_UNCOMPRESSED 5
|
||||
#define UVC_DT_FORMAT_MJPEG 6
|
||||
#define UVC_DT_FRAME_MJPEG 7
|
||||
#define UVC_DT_COLOR_MATCHING 13
|
||||
|
||||
#define UVC_DT_INPUT_HEADER_SIZE(n, p) (13+(n*p))
|
||||
#define UVC_DT_OUTPUT_HEADER_SIZE(n, p) (9+(n*p))
|
||||
#define UVC_DT_FORMAT_UNCOMPRESSED_SIZE 27
|
||||
#define UVC_DT_FRAME_UNCOMPRESSED_SIZE(n) (26+4*(n))
|
||||
#define UVC_DT_FORMAT_MJPEG_SIZE 11
|
||||
#define UVC_DT_FRAME_MJPEG_SIZE(n) (26+4*(n))
|
||||
#define UVC_DT_COLOR_MATCHING_SIZE 6
|
||||
|
||||
extern int uvc_bind_config(struct usb_configuration *c,
|
||||
const struct uvc_descriptor_header * const *control,
|
||||
const struct uvc_descriptor_header * const *fs_streaming,
|
||||
const struct uvc_descriptor_header * const *hs_streaming);
|
||||
|
||||
#endif /* _F_UVC_H_ */
|
||||
|
||||
|
|
@ -50,12 +50,14 @@ int fsl_udc_clk_init(struct platform_device *pdev)
|
|||
goto egusb;
|
||||
}
|
||||
|
||||
freq = clk_get_rate(mxc_usb_clk);
|
||||
if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
|
||||
(freq < 59999000 || freq > 60001000)) {
|
||||
dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq);
|
||||
ret = -EINVAL;
|
||||
goto eclkrate;
|
||||
if (!cpu_is_mx51()) {
|
||||
freq = clk_get_rate(mxc_usb_clk);
|
||||
if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
|
||||
(freq < 59999000 || freq > 60001000)) {
|
||||
dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq);
|
||||
ret = -EINVAL;
|
||||
goto eclkrate;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_enable(mxc_usb_clk);
|
||||
|
|
@ -489,7 +489,7 @@ static int fsl_ep_enable(struct usb_ep *_ep,
|
|||
case USB_ENDPOINT_XFER_ISOC:
|
||||
/* Calculate transactions needed for high bandwidth iso */
|
||||
mult = (unsigned char)(1 + ((max >> 11) & 0x03));
|
||||
max = max & 0x8ff; /* bit 0~10 */
|
||||
max = max & 0x7ff; /* bit 0~10 */
|
||||
/* 3 transactions at most */
|
||||
if (mult > 3)
|
||||
goto en_done;
|
||||
|
|
|
|||
426
drivers/usb/gadget/g_ffs.c
Normal file
426
drivers/usb/gadget/g_ffs.c
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
|
||||
/*
|
||||
* kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
|
||||
#include "composite.c"
|
||||
#include "usbstring.c"
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
|
||||
#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
# if defined USB_ETH_RNDIS
|
||||
# undef USB_ETH_RNDIS
|
||||
# endif
|
||||
# ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
# define USB_ETH_RNDIS y
|
||||
# endif
|
||||
|
||||
# include "f_ecm.c"
|
||||
# include "f_subset.c"
|
||||
# ifdef USB_ETH_RNDIS
|
||||
# include "f_rndis.c"
|
||||
# include "rndis.c"
|
||||
# endif
|
||||
# include "u_ether.c"
|
||||
|
||||
static u8 gfs_hostaddr[ETH_ALEN];
|
||||
#else
|
||||
# if !defined CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
# define CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
# endif
|
||||
# define gether_cleanup() do { } while (0)
|
||||
# define gether_setup(gadget, hostaddr) ((int)0)
|
||||
#endif
|
||||
|
||||
#include "f_fs.c"
|
||||
|
||||
|
||||
#define DRIVER_NAME "g_ffs"
|
||||
#define DRIVER_DESC "USB Function Filesystem"
|
||||
#define DRIVER_VERSION "24 Aug 2004"
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Michal Nazarewicz");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
static unsigned short gfs_vendor_id = 0x0525; /* XXX NetChip */
|
||||
static unsigned short gfs_product_id = 0xa4ac; /* XXX */
|
||||
|
||||
static struct usb_device_descriptor gfs_dev_desc = {
|
||||
.bLength = sizeof gfs_dev_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
|
||||
/* Vendor and product id can be overridden by module parameters. */
|
||||
/* .idVendor = cpu_to_le16(gfs_vendor_id), */
|
||||
/* .idProduct = cpu_to_le16(gfs_product_id), */
|
||||
/* .bcdDevice = f(hardware) */
|
||||
/* .iManufacturer = DYNAMIC */
|
||||
/* .iProduct = DYNAMIC */
|
||||
/* NO SERIAL NUMBER */
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
#define GFS_MODULE_PARAM_DESC(name, field) \
|
||||
MODULE_PARM_DESC(name, "Value of the " #field " field of the device descriptor sent to the host. Takes effect only prior to the user-space driver registering to the FunctionFS.")
|
||||
|
||||
module_param_named(usb_class, gfs_dev_desc.bDeviceClass, byte, 0644);
|
||||
GFS_MODULE_PARAM_DESC(usb_class, bDeviceClass);
|
||||
module_param_named(usb_subclass, gfs_dev_desc.bDeviceSubClass, byte, 0644);
|
||||
GFS_MODULE_PARAM_DESC(usb_subclass, bDeviceSubClass);
|
||||
module_param_named(usb_protocol, gfs_dev_desc.bDeviceProtocol, byte, 0644);
|
||||
GFS_MODULE_PARAM_DESC(usb_protocol, bDeviceProtocol);
|
||||
module_param_named(usb_vendor, gfs_vendor_id, ushort, 0644);
|
||||
GFS_MODULE_PARAM_DESC(usb_vendor, idVendor);
|
||||
module_param_named(usb_product, gfs_product_id, ushort, 0644);
|
||||
GFS_MODULE_PARAM_DESC(usb_product, idProduct);
|
||||
|
||||
|
||||
|
||||
static const struct usb_descriptor_header *gfs_otg_desc[] = {
|
||||
(const struct usb_descriptor_header *)
|
||||
&(const struct usb_otg_descriptor) {
|
||||
.bLength = sizeof(struct usb_otg_descriptor),
|
||||
.bDescriptorType = USB_DT_OTG,
|
||||
|
||||
/* REVISIT SRP-only hardware is possible, although
|
||||
* it would not be called "OTG" ... */
|
||||
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
|
||||
},
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
enum {
|
||||
GFS_STRING_MANUFACTURER_IDX,
|
||||
GFS_STRING_PRODUCT_IDX,
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
GFS_STRING_RNDIS_CONFIG_IDX,
|
||||
#endif
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
GFS_STRING_ECM_CONFIG_IDX,
|
||||
#endif
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
GFS_STRING_GENERIC_CONFIG_IDX,
|
||||
#endif
|
||||
};
|
||||
|
||||
static char gfs_manufacturer[50];
|
||||
static const char gfs_driver_desc[] = DRIVER_DESC;
|
||||
static const char gfs_short_name[] = DRIVER_NAME;
|
||||
|
||||
static struct usb_string gfs_strings[] = {
|
||||
[GFS_STRING_MANUFACTURER_IDX].s = gfs_manufacturer,
|
||||
[GFS_STRING_PRODUCT_IDX].s = gfs_driver_desc,
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
[GFS_STRING_RNDIS_CONFIG_IDX].s = "FunctionFS + RNDIS",
|
||||
#endif
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
[GFS_STRING_ECM_CONFIG_IDX].s = "FunctionFS + ECM",
|
||||
#endif
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
[GFS_STRING_GENERIC_CONFIG_IDX].s = "FunctionFS",
|
||||
#endif
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *gfs_dev_strings[] = {
|
||||
&(struct usb_gadget_strings) {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = gfs_strings,
|
||||
},
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
static int gfs_do_rndis_config(struct usb_configuration *c);
|
||||
|
||||
static struct usb_configuration gfs_rndis_config_driver = {
|
||||
.label = "FunctionFS + RNDIS",
|
||||
.bind = gfs_do_rndis_config,
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
# define gfs_add_rndis_config(cdev) \
|
||||
usb_add_config(cdev, &gfs_rndis_config_driver)
|
||||
#else
|
||||
# define gfs_add_rndis_config(cdev) 0
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
static int gfs_do_ecm_config(struct usb_configuration *c);
|
||||
|
||||
static struct usb_configuration gfs_ecm_config_driver = {
|
||||
.label = "FunctionFS + ECM",
|
||||
.bind = gfs_do_ecm_config,
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
# define gfs_add_ecm_config(cdev) \
|
||||
usb_add_config(cdev, &gfs_ecm_config_driver)
|
||||
#else
|
||||
# define gfs_add_ecm_config(cdev) 0
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
static int gfs_do_generic_config(struct usb_configuration *c);
|
||||
|
||||
static struct usb_configuration gfs_generic_config_driver = {
|
||||
.label = "FunctionFS",
|
||||
.bind = gfs_do_generic_config,
|
||||
.bConfigurationValue = 2,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
# define gfs_add_generic_config(cdev) \
|
||||
usb_add_config(cdev, &gfs_generic_config_driver)
|
||||
#else
|
||||
# define gfs_add_generic_config(cdev) 0
|
||||
#endif
|
||||
|
||||
|
||||
static int gfs_bind(struct usb_composite_dev *cdev);
|
||||
static int gfs_unbind(struct usb_composite_dev *cdev);
|
||||
|
||||
static struct usb_composite_driver gfs_driver = {
|
||||
.name = gfs_short_name,
|
||||
.dev = &gfs_dev_desc,
|
||||
.strings = gfs_dev_strings,
|
||||
.bind = gfs_bind,
|
||||
.unbind = gfs_unbind,
|
||||
};
|
||||
|
||||
|
||||
static struct ffs_data *gfs_ffs_data;
|
||||
static unsigned long gfs_registered;
|
||||
|
||||
|
||||
static int gfs_init(void)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
return functionfs_init();
|
||||
}
|
||||
module_init(gfs_init);
|
||||
|
||||
static void gfs_exit(void)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
if (test_and_clear_bit(0, &gfs_registered))
|
||||
usb_composite_unregister(&gfs_driver);
|
||||
|
||||
functionfs_cleanup();
|
||||
}
|
||||
module_exit(gfs_exit);
|
||||
|
||||
|
||||
static int functionfs_ready_callback(struct ffs_data *ffs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ENTER();
|
||||
|
||||
if (WARN_ON(test_and_set_bit(0, &gfs_registered)))
|
||||
return -EBUSY;
|
||||
|
||||
gfs_ffs_data = ffs;
|
||||
ret = usb_composite_register(&gfs_driver);
|
||||
if (unlikely(ret < 0))
|
||||
clear_bit(0, &gfs_registered);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void functionfs_closed_callback(struct ffs_data *ffs)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
if (test_and_clear_bit(0, &gfs_registered))
|
||||
usb_composite_unregister(&gfs_driver);
|
||||
}
|
||||
|
||||
|
||||
static int functionfs_check_dev_callback(const char *dev_name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int gfs_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ENTER();
|
||||
|
||||
if (WARN_ON(!gfs_ffs_data))
|
||||
return -ENODEV;
|
||||
|
||||
ret = gether_setup(cdev->gadget, gfs_hostaddr);
|
||||
if (unlikely(ret < 0))
|
||||
goto error_quick;
|
||||
|
||||
gfs_dev_desc.idVendor = cpu_to_le16(gfs_vendor_id);
|
||||
gfs_dev_desc.idProduct = cpu_to_le16(gfs_product_id);
|
||||
|
||||
snprintf(gfs_manufacturer, sizeof gfs_manufacturer, "%s %s with %s",
|
||||
init_utsname()->sysname, init_utsname()->release,
|
||||
cdev->gadget->name);
|
||||
ret = usb_string_id(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
gfs_strings[GFS_STRING_MANUFACTURER_IDX].id = ret;
|
||||
gfs_dev_desc.iManufacturer = ret;
|
||||
|
||||
ret = usb_string_id(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
gfs_strings[GFS_STRING_PRODUCT_IDX].id = ret;
|
||||
gfs_dev_desc.iProduct = ret;
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
ret = usb_string_id(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
gfs_strings[GFS_STRING_RNDIS_CONFIG_IDX].id = ret;
|
||||
gfs_rndis_config_driver.iConfiguration = ret;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
ret = usb_string_id(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
gfs_strings[GFS_STRING_ECM_CONFIG_IDX].id = ret;
|
||||
gfs_ecm_config_driver.iConfiguration = ret;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
ret = usb_string_id(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
gfs_strings[GFS_STRING_GENERIC_CONFIG_IDX].id = ret;
|
||||
gfs_generic_config_driver.iConfiguration = ret;
|
||||
#endif
|
||||
|
||||
ret = functionfs_bind(gfs_ffs_data, cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
|
||||
ret = gfs_add_rndis_config(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error_unbind;
|
||||
|
||||
ret = gfs_add_ecm_config(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error_unbind;
|
||||
|
||||
ret = gfs_add_generic_config(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error_unbind;
|
||||
|
||||
return 0;
|
||||
|
||||
error_unbind:
|
||||
functionfs_unbind(gfs_ffs_data);
|
||||
error:
|
||||
gether_cleanup();
|
||||
error_quick:
|
||||
gfs_ffs_data = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gfs_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
/* We may have been called in an error recovery frem
|
||||
* composite_bind() after gfs_unbind() failure so we need to
|
||||
* check if gfs_ffs_data is not NULL since gfs_bind() handles
|
||||
* all error recovery itself. I'd rather we werent called
|
||||
* from composite on orror recovery, but what you're gonna
|
||||
* do...? */
|
||||
|
||||
if (gfs_ffs_data) {
|
||||
gether_cleanup();
|
||||
functionfs_unbind(gfs_ffs_data);
|
||||
gfs_ffs_data = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int __gfs_do_config(struct usb_configuration *c,
|
||||
int (*eth)(struct usb_configuration *c, u8 *ethaddr),
|
||||
u8 *ethaddr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!gfs_ffs_data))
|
||||
return -ENODEV;
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
c->descriptors = gfs_otg_desc;
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
if (eth) {
|
||||
ret = eth(c, ethaddr);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = functionfs_add(c->cdev, c, gfs_ffs_data);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
static int gfs_do_rndis_config(struct usb_configuration *c)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
return __gfs_do_config(c, rndis_bind_config, gfs_hostaddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
static int gfs_do_ecm_config(struct usb_configuration *c)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
return __gfs_do_config(c,
|
||||
can_support_ecm(c->cdev->gadget)
|
||||
? ecm_bind_config : geth_bind_config,
|
||||
gfs_hostaddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
static int gfs_do_generic_config(struct usb_configuration *c)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
return __gfs_do_config(c, NULL, NULL);
|
||||
}
|
||||
#endif
|
||||
298
drivers/usb/gadget/hid.c
Normal file
298
drivers/usb/gadget/hid.c
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
* hid.c -- HID Composite driver
|
||||
*
|
||||
* Based on multi.c
|
||||
*
|
||||
* Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#define DRIVER_DESC "HID Gadget"
|
||||
#define DRIVER_VERSION "2010/03/16"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#define HIDG_VENDOR_NUM 0x0525 /* XXX NetChip */
|
||||
#define HIDG_PRODUCT_NUM 0xa4ac /* Linux-USB HID gadget */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
|
||||
#include "composite.c"
|
||||
#include "usbstring.c"
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
|
||||
#include "f_hid.c"
|
||||
|
||||
|
||||
struct hidg_func_node {
|
||||
struct list_head node;
|
||||
struct hidg_func_descriptor *func;
|
||||
};
|
||||
|
||||
static LIST_HEAD(hidg_func_list);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
|
||||
/* .bDeviceClass = USB_CLASS_COMM, */
|
||||
/* .bDeviceSubClass = 0, */
|
||||
/* .bDeviceProtocol = 0, */
|
||||
.bDeviceClass = 0xEF,
|
||||
.bDeviceSubClass = 2,
|
||||
.bDeviceProtocol = 1,
|
||||
/* .bMaxPacketSize0 = f(hardware) */
|
||||
|
||||
/* Vendor and product id can be overridden by module parameters. */
|
||||
.idVendor = cpu_to_le16(HIDG_VENDOR_NUM),
|
||||
.idProduct = cpu_to_le16(HIDG_PRODUCT_NUM),
|
||||
/* .bcdDevice = f(hardware) */
|
||||
/* .iManufacturer = DYNAMIC */
|
||||
/* .iProduct = DYNAMIC */
|
||||
/* NO SERIAL NUMBER */
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
static struct usb_otg_descriptor otg_descriptor = {
|
||||
.bLength = sizeof otg_descriptor,
|
||||
.bDescriptorType = USB_DT_OTG,
|
||||
|
||||
/* REVISIT SRP-only hardware is possible, although
|
||||
* it would not be called "OTG" ...
|
||||
*/
|
||||
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header *otg_desc[] = {
|
||||
(struct usb_descriptor_header *) &otg_descriptor,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
|
||||
static char manufacturer[50];
|
||||
|
||||
static struct usb_string strings_dev[] = {
|
||||
[STRING_MANUFACTURER_IDX].s = manufacturer,
|
||||
[STRING_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings stringtab_dev = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = strings_dev,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *dev_strings[] = {
|
||||
&stringtab_dev,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/****************************** Configurations ******************************/
|
||||
|
||||
static int __init do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct hidg_func_node *e;
|
||||
int func = 0, status = 0;
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
c->descriptors = otg_desc;
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
list_for_each_entry(e, &hidg_func_list, node) {
|
||||
status = hidg_bind_config(c, e->func, func++);
|
||||
if (status)
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct usb_configuration config_driver = {
|
||||
.label = "HID Gadget",
|
||||
.bind = do_config,
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
|
||||
/****************************** Gadget Bind ******************************/
|
||||
|
||||
static int __init hid_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct list_head *tmp;
|
||||
int status, gcnum, funcs = 0;
|
||||
|
||||
list_for_each(tmp, &hidg_func_list)
|
||||
funcs++;
|
||||
|
||||
if (!funcs)
|
||||
return -ENODEV;
|
||||
|
||||
/* set up HID */
|
||||
status = ghid_setup(cdev->gadget, funcs);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
gcnum = usb_gadget_controller_number(gadget);
|
||||
if (gcnum >= 0)
|
||||
device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
|
||||
else
|
||||
device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
|
||||
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
*/
|
||||
|
||||
/* device descriptor strings: manufacturer, product */
|
||||
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
|
||||
init_utsname()->sysname, init_utsname()->release,
|
||||
gadget->name);
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
strings_dev[STRING_MANUFACTURER_IDX].id = status;
|
||||
device_desc.iManufacturer = status;
|
||||
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
strings_dev[STRING_PRODUCT_IDX].id = status;
|
||||
device_desc.iProduct = status;
|
||||
|
||||
/* register our configuration */
|
||||
status = usb_add_config(cdev, &config_driver);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit hid_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
ghid_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init hidg_plat_driver_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hidg_func_descriptor *func = pdev->dev.platform_data;
|
||||
struct hidg_func_node *entry;
|
||||
|
||||
if (!func) {
|
||||
dev_err(&pdev->dev, "Platform data missing\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
entry->func = func;
|
||||
list_add_tail(&entry->node, &hidg_func_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit hidg_plat_driver_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hidg_func_node *e, *n;
|
||||
|
||||
list_for_each_entry_safe(e, n, &hidg_func_list, node) {
|
||||
list_del(&e->node);
|
||||
kfree(e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/****************************** Some noise ******************************/
|
||||
|
||||
|
||||
static struct usb_composite_driver hidg_driver = {
|
||||
.name = "g_hid",
|
||||
.dev = &device_desc,
|
||||
.strings = dev_strings,
|
||||
.bind = hid_bind,
|
||||
.unbind = __exit_p(hid_unbind),
|
||||
};
|
||||
|
||||
static struct platform_driver hidg_plat_driver = {
|
||||
.remove = __devexit_p(hidg_plat_driver_remove),
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "hidg",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Fabien Chouteau, Peter Korsgaard");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int __init hidg_init(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = platform_driver_probe(&hidg_plat_driver,
|
||||
hidg_plat_driver_probe);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
status = usb_composite_register(&hidg_driver);
|
||||
if (status < 0)
|
||||
platform_driver_unregister(&hidg_plat_driver);
|
||||
|
||||
return status;
|
||||
}
|
||||
module_init(hidg_init);
|
||||
|
||||
static void __exit hidg_cleanup(void)
|
||||
{
|
||||
platform_driver_unregister(&hidg_plat_driver);
|
||||
usb_composite_unregister(&hidg_driver);
|
||||
}
|
||||
module_exit(hidg_cleanup);
|
||||
|
|
@ -360,7 +360,7 @@ struct pxa_ep {
|
|||
* Specific pxa endpoint data, needed for hardware initialization
|
||||
*/
|
||||
unsigned dir_in:1;
|
||||
unsigned addr:3;
|
||||
unsigned addr:4;
|
||||
unsigned config:2;
|
||||
unsigned interface:3;
|
||||
unsigned alternate:3;
|
||||
|
|
|
|||
|
|
@ -715,7 +715,7 @@ static u8 __init nibble(unsigned char c)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init get_ether_addr(const char *str, u8 *dev_addr)
|
||||
static int get_ether_addr(const char *str, u8 *dev_addr)
|
||||
{
|
||||
if (str) {
|
||||
unsigned i;
|
||||
|
|
@ -764,7 +764,7 @@ static struct device_type gadget_type = {
|
|||
*
|
||||
* Returns negative errno, or zero on success
|
||||
*/
|
||||
int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
|
||||
int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
struct net_device *net;
|
||||
|
|
|
|||
241
drivers/usb/gadget/uvc.h
Normal file
241
drivers/usb/gadget/uvc.h
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* uvc_gadget.h -- USB Video Class Gadget driver
|
||||
*
|
||||
* Copyright (C) 2009-2010
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _UVC_GADGET_H_
|
||||
#define _UVC_GADGET_H_
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
#define UVC_EVENT_FIRST (V4L2_EVENT_PRIVATE_START + 0)
|
||||
#define UVC_EVENT_CONNECT (V4L2_EVENT_PRIVATE_START + 0)
|
||||
#define UVC_EVENT_DISCONNECT (V4L2_EVENT_PRIVATE_START + 1)
|
||||
#define UVC_EVENT_STREAMON (V4L2_EVENT_PRIVATE_START + 2)
|
||||
#define UVC_EVENT_STREAMOFF (V4L2_EVENT_PRIVATE_START + 3)
|
||||
#define UVC_EVENT_SETUP (V4L2_EVENT_PRIVATE_START + 4)
|
||||
#define UVC_EVENT_DATA (V4L2_EVENT_PRIVATE_START + 5)
|
||||
#define UVC_EVENT_LAST (V4L2_EVENT_PRIVATE_START + 5)
|
||||
|
||||
struct uvc_request_data
|
||||
{
|
||||
unsigned int length;
|
||||
__u8 data[60];
|
||||
};
|
||||
|
||||
struct uvc_event
|
||||
{
|
||||
union {
|
||||
enum usb_device_speed speed;
|
||||
struct usb_ctrlrequest req;
|
||||
struct uvc_request_data data;
|
||||
};
|
||||
};
|
||||
|
||||
#define UVCIOC_SEND_RESPONSE _IOW('U', 1, struct uvc_request_data)
|
||||
|
||||
#define UVC_INTF_CONTROL 0
|
||||
#define UVC_INTF_STREAMING 1
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* UVC constants & structures
|
||||
*/
|
||||
|
||||
/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
|
||||
#define UVC_STREAM_EOH (1 << 7)
|
||||
#define UVC_STREAM_ERR (1 << 6)
|
||||
#define UVC_STREAM_STI (1 << 5)
|
||||
#define UVC_STREAM_RES (1 << 4)
|
||||
#define UVC_STREAM_SCR (1 << 3)
|
||||
#define UVC_STREAM_PTS (1 << 2)
|
||||
#define UVC_STREAM_EOF (1 << 1)
|
||||
#define UVC_STREAM_FID (1 << 0)
|
||||
|
||||
struct uvc_streaming_control {
|
||||
__u16 bmHint;
|
||||
__u8 bFormatIndex;
|
||||
__u8 bFrameIndex;
|
||||
__u32 dwFrameInterval;
|
||||
__u16 wKeyFrameRate;
|
||||
__u16 wPFrameRate;
|
||||
__u16 wCompQuality;
|
||||
__u16 wCompWindowSize;
|
||||
__u16 wDelay;
|
||||
__u32 dwMaxVideoFrameSize;
|
||||
__u32 dwMaxPayloadTransferSize;
|
||||
__u32 dwClockFrequency;
|
||||
__u8 bmFramingInfo;
|
||||
__u8 bPreferedVersion;
|
||||
__u8 bMinVersion;
|
||||
__u8 bMaxVersion;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Debugging, printing and logging
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/usb.h> /* For usb_endpoint_* */
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-fh.h>
|
||||
|
||||
#include "uvc_queue.h"
|
||||
|
||||
#define UVC_TRACE_PROBE (1 << 0)
|
||||
#define UVC_TRACE_DESCR (1 << 1)
|
||||
#define UVC_TRACE_CONTROL (1 << 2)
|
||||
#define UVC_TRACE_FORMAT (1 << 3)
|
||||
#define UVC_TRACE_CAPTURE (1 << 4)
|
||||
#define UVC_TRACE_CALLS (1 << 5)
|
||||
#define UVC_TRACE_IOCTL (1 << 6)
|
||||
#define UVC_TRACE_FRAME (1 << 7)
|
||||
#define UVC_TRACE_SUSPEND (1 << 8)
|
||||
#define UVC_TRACE_STATUS (1 << 9)
|
||||
|
||||
#define UVC_WARN_MINMAX 0
|
||||
#define UVC_WARN_PROBE_DEF 1
|
||||
|
||||
extern unsigned int uvc_trace_param;
|
||||
|
||||
#define uvc_trace(flag, msg...) \
|
||||
do { \
|
||||
if (uvc_trace_param & flag) \
|
||||
printk(KERN_DEBUG "uvcvideo: " msg); \
|
||||
} while (0)
|
||||
|
||||
#define uvc_warn_once(dev, warn, msg...) \
|
||||
do { \
|
||||
if (!test_and_set_bit(warn, &dev->warnings)) \
|
||||
printk(KERN_INFO "uvcvideo: " msg); \
|
||||
} while (0)
|
||||
|
||||
#define uvc_printk(level, msg...) \
|
||||
printk(level "uvcvideo: " msg)
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Driver specific constants
|
||||
*/
|
||||
|
||||
#define DRIVER_VERSION "0.1.0"
|
||||
#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0)
|
||||
|
||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
#define UVC_NUM_REQUESTS 4
|
||||
#define UVC_MAX_REQUEST_SIZE 64
|
||||
#define UVC_MAX_EVENTS 4
|
||||
|
||||
#define USB_DT_INTERFACE_ASSOCIATION_SIZE 8
|
||||
#define USB_CLASS_MISC 0xef
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Structures
|
||||
*/
|
||||
|
||||
struct uvc_video
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
|
||||
/* Frame parameters */
|
||||
u8 bpp;
|
||||
u32 fcc;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int imagesize;
|
||||
|
||||
/* Requests */
|
||||
unsigned int req_size;
|
||||
struct usb_request *req[UVC_NUM_REQUESTS];
|
||||
__u8 *req_buffer[UVC_NUM_REQUESTS];
|
||||
struct list_head req_free;
|
||||
spinlock_t req_lock;
|
||||
|
||||
void (*encode) (struct usb_request *req, struct uvc_video *video,
|
||||
struct uvc_buffer *buf);
|
||||
|
||||
/* Context data used by the completion handler */
|
||||
__u32 payload_size;
|
||||
__u32 max_payload_size;
|
||||
|
||||
struct uvc_video_queue queue;
|
||||
unsigned int fid;
|
||||
};
|
||||
|
||||
enum uvc_state
|
||||
{
|
||||
UVC_STATE_DISCONNECTED,
|
||||
UVC_STATE_CONNECTED,
|
||||
UVC_STATE_STREAMING,
|
||||
};
|
||||
|
||||
struct uvc_device
|
||||
{
|
||||
struct video_device *vdev;
|
||||
enum uvc_state state;
|
||||
struct usb_function func;
|
||||
struct uvc_video video;
|
||||
|
||||
/* Descriptors */
|
||||
struct {
|
||||
const struct uvc_descriptor_header * const *control;
|
||||
const struct uvc_descriptor_header * const *fs_streaming;
|
||||
const struct uvc_descriptor_header * const *hs_streaming;
|
||||
} desc;
|
||||
|
||||
unsigned int control_intf;
|
||||
struct usb_ep *control_ep;
|
||||
struct usb_request *control_req;
|
||||
void *control_buf;
|
||||
|
||||
unsigned int streaming_intf;
|
||||
|
||||
/* Events */
|
||||
unsigned int event_length;
|
||||
unsigned int event_setup_out : 1;
|
||||
};
|
||||
|
||||
static inline struct uvc_device *to_uvc(struct usb_function *f)
|
||||
{
|
||||
return container_of(f, struct uvc_device, func);
|
||||
}
|
||||
|
||||
struct uvc_file_handle
|
||||
{
|
||||
struct v4l2_fh vfh;
|
||||
struct uvc_video *device;
|
||||
};
|
||||
|
||||
#define to_uvc_file_handle(handle) \
|
||||
container_of(handle, struct uvc_file_handle, vfh)
|
||||
|
||||
extern struct v4l2_file_operations uvc_v4l2_fops;
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Functions
|
||||
*/
|
||||
|
||||
extern int uvc_video_enable(struct uvc_video *video, int enable);
|
||||
extern int uvc_video_init(struct uvc_video *video);
|
||||
extern int uvc_video_pump(struct uvc_video *video);
|
||||
|
||||
extern void uvc_endpoint_stream(struct uvc_device *dev);
|
||||
|
||||
extern void uvc_function_connect(struct uvc_device *uvc);
|
||||
extern void uvc_function_disconnect(struct uvc_device *uvc);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _UVC_GADGET_H_ */
|
||||
|
||||
583
drivers/usb/gadget/uvc_queue.c
Normal file
583
drivers/usb/gadget/uvc_queue.c
Normal file
|
|
@ -0,0 +1,583 @@
|
|||
/*
|
||||
* uvc_queue.c -- USB Video Class driver - Buffers management
|
||||
*
|
||||
* Copyright (C) 2005-2010
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
#include "uvc.h"
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Video buffers queue management.
|
||||
*
|
||||
* Video queues is initialized by uvc_queue_init(). The function performs
|
||||
* basic initialization of the uvc_video_queue struct and never fails.
|
||||
*
|
||||
* Video buffer allocation and freeing are performed by uvc_alloc_buffers and
|
||||
* uvc_free_buffers respectively. The former acquires the video queue lock,
|
||||
* while the later must be called with the lock held (so that allocation can
|
||||
* free previously allocated buffers). Trying to free buffers that are mapped
|
||||
* to user space will return -EBUSY.
|
||||
*
|
||||
* Video buffers are managed using two queues. However, unlike most USB video
|
||||
* drivers that use an in queue and an out queue, we use a main queue to hold
|
||||
* all queued buffers (both 'empty' and 'done' buffers), and an irq queue to
|
||||
* hold empty buffers. This design (copied from video-buf) minimizes locking
|
||||
* in interrupt, as only one queue is shared between interrupt and user
|
||||
* contexts.
|
||||
*
|
||||
* Use cases
|
||||
* ---------
|
||||
*
|
||||
* Unless stated otherwise, all operations that modify the irq buffers queue
|
||||
* are protected by the irq spinlock.
|
||||
*
|
||||
* 1. The user queues the buffers, starts streaming and dequeues a buffer.
|
||||
*
|
||||
* The buffers are added to the main and irq queues. Both operations are
|
||||
* protected by the queue lock, and the later is protected by the irq
|
||||
* spinlock as well.
|
||||
*
|
||||
* The completion handler fetches a buffer from the irq queue and fills it
|
||||
* with video data. If no buffer is available (irq queue empty), the handler
|
||||
* returns immediately.
|
||||
*
|
||||
* When the buffer is full, the completion handler removes it from the irq
|
||||
* queue, marks it as ready (UVC_BUF_STATE_DONE) and wakes its wait queue.
|
||||
* At that point, any process waiting on the buffer will be woken up. If a
|
||||
* process tries to dequeue a buffer after it has been marked ready, the
|
||||
* dequeing will succeed immediately.
|
||||
*
|
||||
* 2. Buffers are queued, user is waiting on a buffer and the device gets
|
||||
* disconnected.
|
||||
*
|
||||
* When the device is disconnected, the kernel calls the completion handler
|
||||
* with an appropriate status code. The handler marks all buffers in the
|
||||
* irq queue as being erroneous (UVC_BUF_STATE_ERROR) and wakes them up so
|
||||
* that any process waiting on a buffer gets woken up.
|
||||
*
|
||||
* Waking up up the first buffer on the irq list is not enough, as the
|
||||
* process waiting on the buffer might restart the dequeue operation
|
||||
* immediately.
|
||||
*
|
||||
*/
|
||||
|
||||
void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
|
||||
{
|
||||
mutex_init(&queue->mutex);
|
||||
spin_lock_init(&queue->irqlock);
|
||||
INIT_LIST_HEAD(&queue->mainqueue);
|
||||
INIT_LIST_HEAD(&queue->irqqueue);
|
||||
queue->type = type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate the video buffers.
|
||||
*
|
||||
* Pages are reserved to make sure they will not be swapped, as they will be
|
||||
* filled in the URB completion handler.
|
||||
*
|
||||
* Buffers will be individually mapped, so they must all be page aligned.
|
||||
*/
|
||||
int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers,
|
||||
unsigned int buflength)
|
||||
{
|
||||
unsigned int bufsize = PAGE_ALIGN(buflength);
|
||||
unsigned int i;
|
||||
void *mem = NULL;
|
||||
int ret;
|
||||
|
||||
if (nbuffers > UVC_MAX_VIDEO_BUFFERS)
|
||||
nbuffers = UVC_MAX_VIDEO_BUFFERS;
|
||||
|
||||
mutex_lock(&queue->mutex);
|
||||
|
||||
if ((ret = uvc_free_buffers(queue)) < 0)
|
||||
goto done;
|
||||
|
||||
/* Bail out if no buffers should be allocated. */
|
||||
if (nbuffers == 0)
|
||||
goto done;
|
||||
|
||||
/* Decrement the number of buffers until allocation succeeds. */
|
||||
for (; nbuffers > 0; --nbuffers) {
|
||||
mem = vmalloc_32(nbuffers * bufsize);
|
||||
if (mem != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mem == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < nbuffers; ++i) {
|
||||
memset(&queue->buffer[i], 0, sizeof queue->buffer[i]);
|
||||
queue->buffer[i].buf.index = i;
|
||||
queue->buffer[i].buf.m.offset = i * bufsize;
|
||||
queue->buffer[i].buf.length = buflength;
|
||||
queue->buffer[i].buf.type = queue->type;
|
||||
queue->buffer[i].buf.sequence = 0;
|
||||
queue->buffer[i].buf.field = V4L2_FIELD_NONE;
|
||||
queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
|
||||
queue->buffer[i].buf.flags = 0;
|
||||
init_waitqueue_head(&queue->buffer[i].wait);
|
||||
}
|
||||
|
||||
queue->mem = mem;
|
||||
queue->count = nbuffers;
|
||||
queue->buf_size = bufsize;
|
||||
ret = nbuffers;
|
||||
|
||||
done:
|
||||
mutex_unlock(&queue->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free the video buffers.
|
||||
*
|
||||
* This function must be called with the queue lock held.
|
||||
*/
|
||||
int uvc_free_buffers(struct uvc_video_queue *queue)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < queue->count; ++i) {
|
||||
if (queue->buffer[i].vma_use_count != 0)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (queue->count) {
|
||||
vfree(queue->mem);
|
||||
queue->count = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __uvc_query_buffer(struct uvc_buffer *buf,
|
||||
struct v4l2_buffer *v4l2_buf)
|
||||
{
|
||||
memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf);
|
||||
|
||||
if (buf->vma_use_count)
|
||||
v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED;
|
||||
|
||||
switch (buf->state) {
|
||||
case UVC_BUF_STATE_ERROR:
|
||||
case UVC_BUF_STATE_DONE:
|
||||
v4l2_buf->flags |= V4L2_BUF_FLAG_DONE;
|
||||
break;
|
||||
case UVC_BUF_STATE_QUEUED:
|
||||
case UVC_BUF_STATE_ACTIVE:
|
||||
v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
|
||||
break;
|
||||
case UVC_BUF_STATE_IDLE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int uvc_query_buffer(struct uvc_video_queue *queue,
|
||||
struct v4l2_buffer *v4l2_buf)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&queue->mutex);
|
||||
if (v4l2_buf->index >= queue->count) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
__uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf);
|
||||
|
||||
done:
|
||||
mutex_unlock(&queue->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Queue a video buffer. Attempting to queue a buffer that has already been
|
||||
* queued will return -EINVAL.
|
||||
*/
|
||||
int uvc_queue_buffer(struct uvc_video_queue *queue,
|
||||
struct v4l2_buffer *v4l2_buf)
|
||||
{
|
||||
struct uvc_buffer *buf;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index);
|
||||
|
||||
if (v4l2_buf->type != queue->type ||
|
||||
v4l2_buf->memory != V4L2_MEMORY_MMAP) {
|
||||
uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
|
||||
"and/or memory (%u).\n", v4l2_buf->type,
|
||||
v4l2_buf->memory);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&queue->mutex);
|
||||
if (v4l2_buf->index >= queue->count) {
|
||||
uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n");
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
buf = &queue->buffer[v4l2_buf->index];
|
||||
if (buf->state != UVC_BUF_STATE_IDLE) {
|
||||
uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state "
|
||||
"(%u).\n", buf->state);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
|
||||
v4l2_buf->bytesused > buf->buf.length) {
|
||||
uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
||||
buf->buf.bytesused = 0;
|
||||
else
|
||||
buf->buf.bytesused = v4l2_buf->bytesused;
|
||||
|
||||
spin_lock_irqsave(&queue->irqlock, flags);
|
||||
if (queue->flags & UVC_QUEUE_DISCONNECTED) {
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
ret = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
buf->state = UVC_BUF_STATE_QUEUED;
|
||||
|
||||
ret = (queue->flags & UVC_QUEUE_PAUSED) != 0;
|
||||
queue->flags &= ~UVC_QUEUE_PAUSED;
|
||||
|
||||
list_add_tail(&buf->stream, &queue->mainqueue);
|
||||
list_add_tail(&buf->queue, &queue->irqqueue);
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
|
||||
done:
|
||||
mutex_unlock(&queue->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uvc_queue_waiton(struct uvc_buffer *buf, int nonblocking)
|
||||
{
|
||||
if (nonblocking) {
|
||||
return (buf->state != UVC_BUF_STATE_QUEUED &&
|
||||
buf->state != UVC_BUF_STATE_ACTIVE)
|
||||
? 0 : -EAGAIN;
|
||||
}
|
||||
|
||||
return wait_event_interruptible(buf->wait,
|
||||
buf->state != UVC_BUF_STATE_QUEUED &&
|
||||
buf->state != UVC_BUF_STATE_ACTIVE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue a video buffer. If nonblocking is false, block until a buffer is
|
||||
* available.
|
||||
*/
|
||||
int uvc_dequeue_buffer(struct uvc_video_queue *queue,
|
||||
struct v4l2_buffer *v4l2_buf, int nonblocking)
|
||||
{
|
||||
struct uvc_buffer *buf;
|
||||
int ret = 0;
|
||||
|
||||
if (v4l2_buf->type != queue->type ||
|
||||
v4l2_buf->memory != V4L2_MEMORY_MMAP) {
|
||||
uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
|
||||
"and/or memory (%u).\n", v4l2_buf->type,
|
||||
v4l2_buf->memory);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&queue->mutex);
|
||||
if (list_empty(&queue->mainqueue)) {
|
||||
uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n");
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
|
||||
if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0)
|
||||
goto done;
|
||||
|
||||
uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u (%u, %u bytes).\n",
|
||||
buf->buf.index, buf->state, buf->buf.bytesused);
|
||||
|
||||
switch (buf->state) {
|
||||
case UVC_BUF_STATE_ERROR:
|
||||
uvc_trace(UVC_TRACE_CAPTURE, "[W] Corrupted data "
|
||||
"(transmission error).\n");
|
||||
ret = -EIO;
|
||||
case UVC_BUF_STATE_DONE:
|
||||
buf->state = UVC_BUF_STATE_IDLE;
|
||||
break;
|
||||
|
||||
case UVC_BUF_STATE_IDLE:
|
||||
case UVC_BUF_STATE_QUEUED:
|
||||
case UVC_BUF_STATE_ACTIVE:
|
||||
default:
|
||||
uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state %u "
|
||||
"(driver bug?).\n", buf->state);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
list_del(&buf->stream);
|
||||
__uvc_query_buffer(buf, v4l2_buf);
|
||||
|
||||
done:
|
||||
mutex_unlock(&queue->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll the video queue.
|
||||
*
|
||||
* This function implements video queue polling and is intended to be used by
|
||||
* the device poll handler.
|
||||
*/
|
||||
unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
|
||||
poll_table *wait)
|
||||
{
|
||||
struct uvc_buffer *buf;
|
||||
unsigned int mask = 0;
|
||||
|
||||
mutex_lock(&queue->mutex);
|
||||
if (list_empty(&queue->mainqueue))
|
||||
goto done;
|
||||
|
||||
buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
|
||||
|
||||
poll_wait(file, &buf->wait, wait);
|
||||
if (buf->state == UVC_BUF_STATE_DONE ||
|
||||
buf->state == UVC_BUF_STATE_ERROR)
|
||||
mask |= POLLOUT | POLLWRNORM;
|
||||
|
||||
done:
|
||||
mutex_unlock(&queue->mutex);
|
||||
return mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* VMA operations.
|
||||
*/
|
||||
static void uvc_vm_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct uvc_buffer *buffer = vma->vm_private_data;
|
||||
buffer->vma_use_count++;
|
||||
}
|
||||
|
||||
static void uvc_vm_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct uvc_buffer *buffer = vma->vm_private_data;
|
||||
buffer->vma_use_count--;
|
||||
}
|
||||
|
||||
static struct vm_operations_struct uvc_vm_ops = {
|
||||
.open = uvc_vm_open,
|
||||
.close = uvc_vm_close,
|
||||
};
|
||||
|
||||
/*
|
||||
* Memory-map a buffer.
|
||||
*
|
||||
* This function implements video buffer memory mapping and is intended to be
|
||||
* used by the device mmap handler.
|
||||
*/
|
||||
int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
|
||||
{
|
||||
struct uvc_buffer *uninitialized_var(buffer);
|
||||
struct page *page;
|
||||
unsigned long addr, start, size;
|
||||
unsigned int i;
|
||||
int ret = 0;
|
||||
|
||||
start = vma->vm_start;
|
||||
size = vma->vm_end - vma->vm_start;
|
||||
|
||||
mutex_lock(&queue->mutex);
|
||||
|
||||
for (i = 0; i < queue->count; ++i) {
|
||||
buffer = &queue->buffer[i];
|
||||
if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == queue->count || size != queue->buf_size) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* VM_IO marks the area as being an mmaped region for I/O to a
|
||||
* device. It also prevents the region from being core dumped.
|
||||
*/
|
||||
vma->vm_flags |= VM_IO;
|
||||
|
||||
addr = (unsigned long)queue->mem + buffer->buf.m.offset;
|
||||
while (size > 0) {
|
||||
page = vmalloc_to_page((void *)addr);
|
||||
if ((ret = vm_insert_page(vma, start, page)) < 0)
|
||||
goto done;
|
||||
|
||||
start += PAGE_SIZE;
|
||||
addr += PAGE_SIZE;
|
||||
size -= PAGE_SIZE;
|
||||
}
|
||||
|
||||
vma->vm_ops = &uvc_vm_ops;
|
||||
vma->vm_private_data = buffer;
|
||||
uvc_vm_open(vma);
|
||||
|
||||
done:
|
||||
mutex_unlock(&queue->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable or disable the video buffers queue.
|
||||
*
|
||||
* The queue must be enabled before starting video acquisition and must be
|
||||
* disabled after stopping it. This ensures that the video buffers queue
|
||||
* state can be properly initialized before buffers are accessed from the
|
||||
* interrupt handler.
|
||||
*
|
||||
* Enabling the video queue initializes parameters (such as sequence number,
|
||||
* sync pattern, ...). If the queue is already enabled, return -EBUSY.
|
||||
*
|
||||
* Disabling the video queue cancels the queue and removes all buffers from
|
||||
* the main queue.
|
||||
*
|
||||
* This function can't be called from interrupt context. Use
|
||||
* uvc_queue_cancel() instead.
|
||||
*/
|
||||
int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&queue->mutex);
|
||||
if (enable) {
|
||||
if (uvc_queue_streaming(queue)) {
|
||||
ret = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
queue->sequence = 0;
|
||||
queue->flags |= UVC_QUEUE_STREAMING;
|
||||
queue->buf_used = 0;
|
||||
} else {
|
||||
uvc_queue_cancel(queue, 0);
|
||||
INIT_LIST_HEAD(&queue->mainqueue);
|
||||
|
||||
for (i = 0; i < queue->count; ++i)
|
||||
queue->buffer[i].state = UVC_BUF_STATE_IDLE;
|
||||
|
||||
queue->flags &= ~UVC_QUEUE_STREAMING;
|
||||
}
|
||||
|
||||
done:
|
||||
mutex_unlock(&queue->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancel the video buffers queue.
|
||||
*
|
||||
* Cancelling the queue marks all buffers on the irq queue as erroneous,
|
||||
* wakes them up and removes them from the queue.
|
||||
*
|
||||
* If the disconnect parameter is set, further calls to uvc_queue_buffer will
|
||||
* fail with -ENODEV.
|
||||
*
|
||||
* This function acquires the irq spinlock and can be called from interrupt
|
||||
* context.
|
||||
*/
|
||||
void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
|
||||
{
|
||||
struct uvc_buffer *buf;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&queue->irqlock, flags);
|
||||
while (!list_empty(&queue->irqqueue)) {
|
||||
buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
|
||||
queue);
|
||||
list_del(&buf->queue);
|
||||
buf->state = UVC_BUF_STATE_ERROR;
|
||||
wake_up(&buf->wait);
|
||||
}
|
||||
/* This must be protected by the irqlock spinlock to avoid race
|
||||
* conditions between uvc_queue_buffer and the disconnection event that
|
||||
* could result in an interruptible wait in uvc_dequeue_buffer. Do not
|
||||
* blindly replace this logic by checking for the UVC_DEV_DISCONNECTED
|
||||
* state outside the queue code.
|
||||
*/
|
||||
if (disconnect)
|
||||
queue->flags |= UVC_QUEUE_DISCONNECTED;
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
}
|
||||
|
||||
struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
|
||||
struct uvc_buffer *buf)
|
||||
{
|
||||
struct uvc_buffer *nextbuf;
|
||||
unsigned long flags;
|
||||
|
||||
if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
|
||||
buf->buf.length != buf->buf.bytesused) {
|
||||
buf->state = UVC_BUF_STATE_QUEUED;
|
||||
buf->buf.bytesused = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&queue->irqlock, flags);
|
||||
list_del(&buf->queue);
|
||||
if (!list_empty(&queue->irqqueue))
|
||||
nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
|
||||
queue);
|
||||
else
|
||||
nextbuf = NULL;
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
|
||||
buf->buf.sequence = queue->sequence++;
|
||||
do_gettimeofday(&buf->buf.timestamp);
|
||||
|
||||
wake_up(&buf->wait);
|
||||
return nextbuf;
|
||||
}
|
||||
|
||||
struct uvc_buffer *uvc_queue_head(struct uvc_video_queue *queue)
|
||||
{
|
||||
struct uvc_buffer *buf = NULL;
|
||||
|
||||
if (!list_empty(&queue->irqqueue))
|
||||
buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
|
||||
queue);
|
||||
else
|
||||
queue->flags |= UVC_QUEUE_PAUSED;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
89
drivers/usb/gadget/uvc_queue.h
Normal file
89
drivers/usb/gadget/uvc_queue.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#ifndef _UVC_QUEUE_H_
|
||||
#define _UVC_QUEUE_H_
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
/* Maximum frame size in bytes, for sanity checking. */
|
||||
#define UVC_MAX_FRAME_SIZE (16*1024*1024)
|
||||
/* Maximum number of video buffers. */
|
||||
#define UVC_MAX_VIDEO_BUFFERS 32
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Structures.
|
||||
*/
|
||||
|
||||
enum uvc_buffer_state {
|
||||
UVC_BUF_STATE_IDLE = 0,
|
||||
UVC_BUF_STATE_QUEUED = 1,
|
||||
UVC_BUF_STATE_ACTIVE = 2,
|
||||
UVC_BUF_STATE_DONE = 3,
|
||||
UVC_BUF_STATE_ERROR = 4,
|
||||
};
|
||||
|
||||
struct uvc_buffer {
|
||||
unsigned long vma_use_count;
|
||||
struct list_head stream;
|
||||
|
||||
/* Touched by interrupt handler. */
|
||||
struct v4l2_buffer buf;
|
||||
struct list_head queue;
|
||||
wait_queue_head_t wait;
|
||||
enum uvc_buffer_state state;
|
||||
};
|
||||
|
||||
#define UVC_QUEUE_STREAMING (1 << 0)
|
||||
#define UVC_QUEUE_DISCONNECTED (1 << 1)
|
||||
#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2)
|
||||
#define UVC_QUEUE_PAUSED (1 << 3)
|
||||
|
||||
struct uvc_video_queue {
|
||||
enum v4l2_buf_type type;
|
||||
|
||||
void *mem;
|
||||
unsigned int flags;
|
||||
__u32 sequence;
|
||||
|
||||
unsigned int count;
|
||||
unsigned int buf_size;
|
||||
unsigned int buf_used;
|
||||
struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];
|
||||
struct mutex mutex; /* protects buffers and mainqueue */
|
||||
spinlock_t irqlock; /* protects irqqueue */
|
||||
|
||||
struct list_head mainqueue;
|
||||
struct list_head irqqueue;
|
||||
};
|
||||
|
||||
extern void uvc_queue_init(struct uvc_video_queue *queue,
|
||||
enum v4l2_buf_type type);
|
||||
extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
|
||||
unsigned int nbuffers, unsigned int buflength);
|
||||
extern int uvc_free_buffers(struct uvc_video_queue *queue);
|
||||
extern int uvc_query_buffer(struct uvc_video_queue *queue,
|
||||
struct v4l2_buffer *v4l2_buf);
|
||||
extern int uvc_queue_buffer(struct uvc_video_queue *queue,
|
||||
struct v4l2_buffer *v4l2_buf);
|
||||
extern int uvc_dequeue_buffer(struct uvc_video_queue *queue,
|
||||
struct v4l2_buffer *v4l2_buf, int nonblocking);
|
||||
extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable);
|
||||
extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
|
||||
extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
|
||||
struct uvc_buffer *buf);
|
||||
extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
|
||||
struct file *file, poll_table *wait);
|
||||
extern int uvc_queue_mmap(struct uvc_video_queue *queue,
|
||||
struct vm_area_struct *vma);
|
||||
static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
|
||||
{
|
||||
return queue->flags & UVC_QUEUE_STREAMING;
|
||||
}
|
||||
extern struct uvc_buffer *uvc_queue_head(struct uvc_video_queue *queue);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _UVC_QUEUE_H_ */
|
||||
|
||||
374
drivers/usb/gadget/uvc_v4l2.c
Normal file
374
drivers/usb/gadget/uvc_v4l2.c
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* uvc_v4l2.c -- USB Video Class Gadget driver
|
||||
*
|
||||
* Copyright (C) 2009-2010
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-event.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
|
||||
#include "uvc.h"
|
||||
#include "uvc_queue.h"
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Requests handling
|
||||
*/
|
||||
|
||||
static int
|
||||
uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
|
||||
{
|
||||
struct usb_composite_dev *cdev = uvc->func.config->cdev;
|
||||
struct usb_request *req = uvc->control_req;
|
||||
|
||||
if (data->length < 0)
|
||||
return usb_ep_set_halt(cdev->gadget->ep0);
|
||||
|
||||
req->length = min(uvc->event_length, data->length);
|
||||
req->zero = data->length < uvc->event_length;
|
||||
req->dma = DMA_ADDR_INVALID;
|
||||
|
||||
memcpy(req->buf, data->data, data->length);
|
||||
|
||||
return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* V4L2
|
||||
*/
|
||||
|
||||
struct uvc_format
|
||||
{
|
||||
u8 bpp;
|
||||
u32 fcc;
|
||||
};
|
||||
|
||||
static struct uvc_format uvc_formats[] = {
|
||||
{ 16, V4L2_PIX_FMT_YUYV },
|
||||
{ 0, V4L2_PIX_FMT_MJPEG },
|
||||
};
|
||||
|
||||
static int
|
||||
uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt)
|
||||
{
|
||||
fmt->fmt.pix.pixelformat = video->fcc;
|
||||
fmt->fmt.pix.width = video->width;
|
||||
fmt->fmt.pix.height = video->height;
|
||||
fmt->fmt.pix.field = V4L2_FIELD_NONE;
|
||||
fmt->fmt.pix.bytesperline = video->bpp * video->width / 8;
|
||||
fmt->fmt.pix.sizeimage = video->imagesize;
|
||||
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
|
||||
fmt->fmt.pix.priv = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt)
|
||||
{
|
||||
struct uvc_format *format;
|
||||
unsigned int imagesize;
|
||||
unsigned int bpl;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(uvc_formats); ++i) {
|
||||
format = &uvc_formats[i];
|
||||
if (format->fcc == fmt->fmt.pix.pixelformat)
|
||||
break;
|
||||
}
|
||||
|
||||
if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) {
|
||||
printk(KERN_INFO "Unsupported format 0x%08x.\n",
|
||||
fmt->fmt.pix.pixelformat);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bpl = format->bpp * fmt->fmt.pix.width / 8;
|
||||
imagesize = bpl ? bpl * fmt->fmt.pix.height : fmt->fmt.pix.sizeimage;
|
||||
|
||||
video->fcc = format->fcc;
|
||||
video->bpp = format->bpp;
|
||||
video->width = fmt->fmt.pix.width;
|
||||
video->height = fmt->fmt.pix.height;
|
||||
video->imagesize = imagesize;
|
||||
|
||||
fmt->fmt.pix.field = V4L2_FIELD_NONE;
|
||||
fmt->fmt.pix.bytesperline = bpl;
|
||||
fmt->fmt.pix.sizeimage = imagesize;
|
||||
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
|
||||
fmt->fmt.pix.priv = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_open(struct file *file)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_file_handle *handle;
|
||||
int ret;
|
||||
|
||||
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
|
||||
if (handle == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = v4l2_fh_init(&handle->vfh, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = v4l2_event_init(&handle->vfh);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = v4l2_event_alloc(&handle->vfh, 8);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
v4l2_fh_add(&handle->vfh);
|
||||
|
||||
handle->device = &uvc->video;
|
||||
file->private_data = &handle->vfh;
|
||||
|
||||
uvc_function_connect(uvc);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
v4l2_fh_exit(&handle->vfh);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_release(struct file *file)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
|
||||
struct uvc_video *video = handle->device;
|
||||
|
||||
uvc_function_disconnect(uvc);
|
||||
|
||||
uvc_video_enable(video, 0);
|
||||
mutex_lock(&video->queue.mutex);
|
||||
if (uvc_free_buffers(&video->queue) < 0)
|
||||
printk(KERN_ERR "uvc_v4l2_release: Unable to free "
|
||||
"buffers.\n");
|
||||
mutex_unlock(&video->queue.mutex);
|
||||
|
||||
file->private_data = NULL;
|
||||
v4l2_fh_del(&handle->vfh);
|
||||
v4l2_fh_exit(&handle->vfh);
|
||||
kfree(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long
|
||||
uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
|
||||
struct usb_composite_dev *cdev = uvc->func.config->cdev;
|
||||
struct uvc_video *video = &uvc->video;
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
/* Query capabilities */
|
||||
case VIDIOC_QUERYCAP:
|
||||
{
|
||||
struct v4l2_capability *cap = arg;
|
||||
|
||||
memset(cap, 0, sizeof *cap);
|
||||
strncpy(cap->driver, "g_uvc", sizeof(cap->driver));
|
||||
strncpy(cap->card, cdev->gadget->name, sizeof(cap->card));
|
||||
strncpy(cap->bus_info, dev_name(&cdev->gadget->dev),
|
||||
sizeof cap->bus_info);
|
||||
cap->version = DRIVER_VERSION_NUMBER;
|
||||
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get & Set format */
|
||||
case VIDIOC_G_FMT:
|
||||
{
|
||||
struct v4l2_format *fmt = arg;
|
||||
|
||||
if (fmt->type != video->queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
return uvc_v4l2_get_format(video, fmt);
|
||||
}
|
||||
|
||||
case VIDIOC_S_FMT:
|
||||
{
|
||||
struct v4l2_format *fmt = arg;
|
||||
|
||||
if (fmt->type != video->queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
return uvc_v4l2_set_format(video, fmt);
|
||||
}
|
||||
|
||||
/* Buffers & streaming */
|
||||
case VIDIOC_REQBUFS:
|
||||
{
|
||||
struct v4l2_requestbuffers *rb = arg;
|
||||
|
||||
if (rb->type != video->queue.type ||
|
||||
rb->memory != V4L2_MEMORY_MMAP)
|
||||
return -EINVAL;
|
||||
|
||||
ret = uvc_alloc_buffers(&video->queue, rb->count,
|
||||
video->imagesize);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
rb->count = ret;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_QUERYBUF:
|
||||
{
|
||||
struct v4l2_buffer *buf = arg;
|
||||
|
||||
if (buf->type != video->queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
return uvc_query_buffer(&video->queue, buf);
|
||||
}
|
||||
|
||||
case VIDIOC_QBUF:
|
||||
if ((ret = uvc_queue_buffer(&video->queue, arg)) < 0)
|
||||
return ret;
|
||||
|
||||
return uvc_video_pump(video);
|
||||
|
||||
case VIDIOC_DQBUF:
|
||||
return uvc_dequeue_buffer(&video->queue, arg,
|
||||
file->f_flags & O_NONBLOCK);
|
||||
|
||||
case VIDIOC_STREAMON:
|
||||
{
|
||||
int *type = arg;
|
||||
|
||||
if (*type != video->queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
return uvc_video_enable(video, 1);
|
||||
}
|
||||
|
||||
case VIDIOC_STREAMOFF:
|
||||
{
|
||||
int *type = arg;
|
||||
|
||||
if (*type != video->queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
return uvc_video_enable(video, 0);
|
||||
}
|
||||
|
||||
/* Events */
|
||||
case VIDIOC_DQEVENT:
|
||||
{
|
||||
struct v4l2_event *event = arg;
|
||||
|
||||
ret = v4l2_event_dequeue(&handle->vfh, event,
|
||||
file->f_flags & O_NONBLOCK);
|
||||
if (ret == 0 && event->type == UVC_EVENT_SETUP) {
|
||||
struct uvc_event *uvc_event = (void *)&event->u.data;
|
||||
|
||||
/* Tell the complete callback to generate an event for
|
||||
* the next request that will be enqueued by
|
||||
* uvc_event_write.
|
||||
*/
|
||||
uvc->event_setup_out =
|
||||
!(uvc_event->req.bRequestType & USB_DIR_IN);
|
||||
uvc->event_length = uvc_event->req.wLength;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
case VIDIOC_SUBSCRIBE_EVENT:
|
||||
{
|
||||
struct v4l2_event_subscription *sub = arg;
|
||||
|
||||
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
|
||||
return -EINVAL;
|
||||
|
||||
return v4l2_event_subscribe(&handle->vfh, arg);
|
||||
}
|
||||
|
||||
case VIDIOC_UNSUBSCRIBE_EVENT:
|
||||
return v4l2_event_unsubscribe(&handle->vfh, arg);
|
||||
|
||||
case UVCIOC_SEND_RESPONSE:
|
||||
ret = uvc_send_response(uvc, arg);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long
|
||||
uvc_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
|
||||
return uvc_queue_mmap(&uvc->video.queue, vma);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
uvc_v4l2_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
|
||||
unsigned int mask = 0;
|
||||
|
||||
poll_wait(file, &handle->vfh.events->wait, wait);
|
||||
if (v4l2_event_pending(&handle->vfh))
|
||||
mask |= POLLPRI;
|
||||
|
||||
mask |= uvc_queue_poll(&uvc->video.queue, file, wait);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
struct v4l2_file_operations uvc_v4l2_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = uvc_v4l2_open,
|
||||
.release = uvc_v4l2_release,
|
||||
.ioctl = uvc_v4l2_ioctl,
|
||||
.mmap = uvc_v4l2_mmap,
|
||||
.poll = uvc_v4l2_poll,
|
||||
};
|
||||
|
||||
386
drivers/usb/gadget/uvc_video.c
Normal file
386
drivers/usb/gadget/uvc_video.c
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
/*
|
||||
* uvc_video.c -- USB Video Class Gadget driver
|
||||
*
|
||||
* Copyright (C) 2009-2010
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include <media/v4l2-dev.h>
|
||||
|
||||
#include "uvc.h"
|
||||
#include "uvc_queue.h"
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Video codecs
|
||||
*/
|
||||
|
||||
static int
|
||||
uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
|
||||
u8 *data, int len)
|
||||
{
|
||||
data[0] = 2;
|
||||
data[1] = UVC_STREAM_EOH | video->fid;
|
||||
|
||||
if (buf->buf.bytesused - video->queue.buf_used <= len - 2)
|
||||
data[1] |= UVC_STREAM_EOF;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf,
|
||||
u8 *data, int len)
|
||||
{
|
||||
struct uvc_video_queue *queue = &video->queue;
|
||||
unsigned int nbytes;
|
||||
void *mem;
|
||||
|
||||
/* Copy video data to the USB buffer. */
|
||||
mem = queue->mem + buf->buf.m.offset + queue->buf_used;
|
||||
nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used);
|
||||
|
||||
memcpy(data, mem, nbytes);
|
||||
queue->buf_used += nbytes;
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
static void
|
||||
uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
|
||||
struct uvc_buffer *buf)
|
||||
{
|
||||
void *mem = req->buf;
|
||||
int len = video->req_size;
|
||||
int ret;
|
||||
|
||||
/* Add a header at the beginning of the payload. */
|
||||
if (video->payload_size == 0) {
|
||||
ret = uvc_video_encode_header(video, buf, mem, len);
|
||||
video->payload_size += ret;
|
||||
mem += ret;
|
||||
len -= ret;
|
||||
}
|
||||
|
||||
/* Process video data. */
|
||||
len = min((int)(video->max_payload_size - video->payload_size), len);
|
||||
ret = uvc_video_encode_data(video, buf, mem, len);
|
||||
|
||||
video->payload_size += ret;
|
||||
len -= ret;
|
||||
|
||||
req->length = video->req_size - len;
|
||||
req->zero = video->payload_size == video->max_payload_size;
|
||||
|
||||
if (buf->buf.bytesused == video->queue.buf_used) {
|
||||
video->queue.buf_used = 0;
|
||||
buf->state = UVC_BUF_STATE_DONE;
|
||||
uvc_queue_next_buffer(&video->queue, buf);
|
||||
video->fid ^= UVC_STREAM_FID;
|
||||
|
||||
video->payload_size = 0;
|
||||
}
|
||||
|
||||
if (video->payload_size == video->max_payload_size ||
|
||||
buf->buf.bytesused == video->queue.buf_used)
|
||||
video->payload_size = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
|
||||
struct uvc_buffer *buf)
|
||||
{
|
||||
void *mem = req->buf;
|
||||
int len = video->req_size;
|
||||
int ret;
|
||||
|
||||
/* Add the header. */
|
||||
ret = uvc_video_encode_header(video, buf, mem, len);
|
||||
mem += ret;
|
||||
len -= ret;
|
||||
|
||||
/* Process video data. */
|
||||
ret = uvc_video_encode_data(video, buf, mem, len);
|
||||
len -= ret;
|
||||
|
||||
req->length = video->req_size - len;
|
||||
|
||||
if (buf->buf.bytesused == video->queue.buf_used) {
|
||||
video->queue.buf_used = 0;
|
||||
buf->state = UVC_BUF_STATE_DONE;
|
||||
uvc_queue_next_buffer(&video->queue, buf);
|
||||
video->fid ^= UVC_STREAM_FID;
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Request handling
|
||||
*/
|
||||
|
||||
/*
|
||||
* I somehow feel that synchronisation won't be easy to achieve here. We have
|
||||
* three events that control USB requests submission:
|
||||
*
|
||||
* - USB request completion: the completion handler will resubmit the request
|
||||
* if a video buffer is available.
|
||||
*
|
||||
* - USB interface setting selection: in response to a SET_INTERFACE request,
|
||||
* the handler will start streaming if a video buffer is available and if
|
||||
* video is not currently streaming.
|
||||
*
|
||||
* - V4L2 buffer queueing: the driver will start streaming if video is not
|
||||
* currently streaming.
|
||||
*
|
||||
* Race conditions between those 3 events might lead to deadlocks or other
|
||||
* nasty side effects.
|
||||
*
|
||||
* The "video currently streaming" condition can't be detected by the irqqueue
|
||||
* being empty, as a request can still be in flight. A separate "queue paused"
|
||||
* flag is thus needed.
|
||||
*
|
||||
* The paused flag will be set when we try to retrieve the irqqueue head if the
|
||||
* queue is empty, and cleared when we queue a buffer.
|
||||
*
|
||||
* The USB request completion handler will get the buffer at the irqqueue head
|
||||
* under protection of the queue spinlock. If the queue is empty, the streaming
|
||||
* paused flag will be set. Right after releasing the spinlock a userspace
|
||||
* application can queue a buffer. The flag will then cleared, and the ioctl
|
||||
* handler will restart the video stream.
|
||||
*/
|
||||
static void
|
||||
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct uvc_video *video = req->context;
|
||||
struct uvc_buffer *buf;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
switch (req->status) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case -ESHUTDOWN:
|
||||
printk(KERN_INFO "VS request cancelled.\n");
|
||||
goto requeue;
|
||||
|
||||
default:
|
||||
printk(KERN_INFO "VS request completed with status %d.\n",
|
||||
req->status);
|
||||
goto requeue;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&video->queue.irqlock, flags);
|
||||
buf = uvc_queue_head(&video->queue);
|
||||
if (buf == NULL) {
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
goto requeue;
|
||||
}
|
||||
|
||||
video->encode(req, video, buf);
|
||||
|
||||
if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) {
|
||||
printk(KERN_INFO "Failed to queue request (%d).\n", ret);
|
||||
usb_ep_set_halt(ep);
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
goto requeue;
|
||||
}
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
|
||||
return;
|
||||
|
||||
requeue:
|
||||
spin_lock_irqsave(&video->req_lock, flags);
|
||||
list_add_tail(&req->list, &video->req_free);
|
||||
spin_unlock_irqrestore(&video->req_lock, flags);
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_video_free_requests(struct uvc_video *video)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
|
||||
if (video->req[i]) {
|
||||
usb_ep_free_request(video->ep, video->req[i]);
|
||||
video->req[i] = NULL;
|
||||
}
|
||||
|
||||
if (video->req_buffer[i]) {
|
||||
kfree(video->req_buffer[i]);
|
||||
video->req_buffer[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&video->req_free);
|
||||
video->req_size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_video_alloc_requests(struct uvc_video *video)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
BUG_ON(video->req_size);
|
||||
|
||||
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
|
||||
video->req_buffer[i] = kmalloc(video->ep->maxpacket, GFP_KERNEL);
|
||||
if (video->req_buffer[i] == NULL)
|
||||
goto error;
|
||||
|
||||
video->req[i] = usb_ep_alloc_request(video->ep, GFP_KERNEL);
|
||||
if (video->req[i] == NULL)
|
||||
goto error;
|
||||
|
||||
video->req[i]->buf = video->req_buffer[i];
|
||||
video->req[i]->length = 0;
|
||||
video->req[i]->dma = DMA_ADDR_INVALID;
|
||||
video->req[i]->complete = uvc_video_complete;
|
||||
video->req[i]->context = video;
|
||||
|
||||
list_add_tail(&video->req[i]->list, &video->req_free);
|
||||
}
|
||||
|
||||
video->req_size = video->ep->maxpacket;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
uvc_video_free_requests(video);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Video streaming
|
||||
*/
|
||||
|
||||
/*
|
||||
* uvc_video_pump - Pump video data into the USB requests
|
||||
*
|
||||
* This function fills the available USB requests (listed in req_free) with
|
||||
* video data from the queued buffers.
|
||||
*/
|
||||
int
|
||||
uvc_video_pump(struct uvc_video *video)
|
||||
{
|
||||
struct usb_request *req;
|
||||
struct uvc_buffer *buf;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/* FIXME TODO Race between uvc_video_pump and requests completion
|
||||
* handler ???
|
||||
*/
|
||||
|
||||
while (1) {
|
||||
/* Retrieve the first available USB request, protected by the
|
||||
* request lock.
|
||||
*/
|
||||
spin_lock_irqsave(&video->req_lock, flags);
|
||||
if (list_empty(&video->req_free)) {
|
||||
spin_unlock_irqrestore(&video->req_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
req = list_first_entry(&video->req_free, struct usb_request,
|
||||
list);
|
||||
list_del(&req->list);
|
||||
spin_unlock_irqrestore(&video->req_lock, flags);
|
||||
|
||||
/* Retrieve the first available video buffer and fill the
|
||||
* request, protected by the video queue irqlock.
|
||||
*/
|
||||
spin_lock_irqsave(&video->queue.irqlock, flags);
|
||||
buf = uvc_queue_head(&video->queue);
|
||||
if (buf == NULL) {
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
break;
|
||||
}
|
||||
|
||||
video->encode(req, video, buf);
|
||||
|
||||
/* Queue the USB request */
|
||||
if ((ret = usb_ep_queue(video->ep, req, GFP_KERNEL)) < 0) {
|
||||
printk(KERN_INFO "Failed to queue request (%d)\n", ret);
|
||||
usb_ep_set_halt(video->ep);
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&video->req_lock, flags);
|
||||
list_add_tail(&req->list, &video->req_free);
|
||||
spin_unlock_irqrestore(&video->req_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable or disable the video stream.
|
||||
*/
|
||||
int
|
||||
uvc_video_enable(struct uvc_video *video, int enable)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (video->ep == NULL) {
|
||||
printk(KERN_INFO "Video enable failed, device is "
|
||||
"uninitialized.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!enable) {
|
||||
for (i = 0; i < UVC_NUM_REQUESTS; ++i)
|
||||
usb_ep_dequeue(video->ep, video->req[i]);
|
||||
|
||||
uvc_video_free_requests(video);
|
||||
uvc_queue_enable(&video->queue, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
|
||||
return ret;
|
||||
|
||||
if ((ret = uvc_video_alloc_requests(video)) < 0)
|
||||
return ret;
|
||||
|
||||
if (video->max_payload_size) {
|
||||
video->encode = uvc_video_encode_bulk;
|
||||
video->payload_size = 0;
|
||||
} else
|
||||
video->encode = uvc_video_encode_isoc;
|
||||
|
||||
return uvc_video_pump(video);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the UVC video stream.
|
||||
*/
|
||||
int
|
||||
uvc_video_init(struct uvc_video *video)
|
||||
{
|
||||
INIT_LIST_HEAD(&video->req_free);
|
||||
spin_lock_init(&video->req_lock);
|
||||
|
||||
video->fcc = V4L2_PIX_FMT_YUYV;
|
||||
video->bpp = 16;
|
||||
video->width = 320;
|
||||
video->height = 240;
|
||||
video->imagesize = 320 * 240 * 2;
|
||||
|
||||
/* Initialize the video buffers queue. */
|
||||
uvc_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
399
drivers/usb/gadget/webcam.c
Normal file
399
drivers/usb/gadget/webcam.c
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
/*
|
||||
* webcam.c -- USB webcam gadget driver
|
||||
*
|
||||
* Copyright (C) 2009-2010
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.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.
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb/video.h>
|
||||
|
||||
#include "f_uvc.h"
|
||||
|
||||
/*
|
||||
* Kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "composite.c"
|
||||
#include "usbstring.c"
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
|
||||
#include "f_uvc.c"
|
||||
#include "uvc_queue.c"
|
||||
#include "uvc_v4l2.c"
|
||||
#include "uvc_video.c"
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Device descriptor
|
||||
*/
|
||||
|
||||
#define WEBCAM_VENDOR_ID 0x1d6b /* Linux Foundation */
|
||||
#define WEBCAM_PRODUCT_ID 0x0102 /* Webcam A/V gadget */
|
||||
#define WEBCAM_DEVICE_BCD 0x0010 /* 0.10 */
|
||||
|
||||
static char webcam_vendor_label[] = "Linux Foundation";
|
||||
static char webcam_product_label[] = "Webcam gadget";
|
||||
static char webcam_config_label[] = "Video";
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
#define STRING_DESCRIPTION_IDX 2
|
||||
|
||||
static struct usb_string webcam_strings[] = {
|
||||
[STRING_MANUFACTURER_IDX].s = webcam_vendor_label,
|
||||
[STRING_PRODUCT_IDX].s = webcam_product_label,
|
||||
[STRING_DESCRIPTION_IDX].s = webcam_config_label,
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings webcam_stringtab = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = webcam_strings,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *webcam_device_strings[] = {
|
||||
&webcam_stringtab,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_device_descriptor webcam_device_descriptor = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
.bDeviceClass = USB_CLASS_MISC,
|
||||
.bDeviceSubClass = 0x02,
|
||||
.bDeviceProtocol = 0x01,
|
||||
.bMaxPacketSize0 = 0, /* dynamic */
|
||||
.idVendor = cpu_to_le16(WEBCAM_VENDOR_ID),
|
||||
.idProduct = cpu_to_le16(WEBCAM_PRODUCT_ID),
|
||||
.bcdDevice = cpu_to_le16(WEBCAM_DEVICE_BCD),
|
||||
.iManufacturer = 0, /* dynamic */
|
||||
.iProduct = 0, /* dynamic */
|
||||
.iSerialNumber = 0, /* dynamic */
|
||||
.bNumConfigurations = 0, /* dynamic */
|
||||
};
|
||||
|
||||
DECLARE_UVC_HEADER_DESCRIPTOR(1);
|
||||
|
||||
static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = {
|
||||
.bLength = UVC_DT_HEADER_SIZE(1),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_HEADER,
|
||||
.bcdUVC = cpu_to_le16(0x0100),
|
||||
.wTotalLength = 0, /* dynamic */
|
||||
.dwClockFrequency = cpu_to_le32(48000000),
|
||||
.bInCollection = 0, /* dynamic */
|
||||
.baInterfaceNr[0] = 0, /* dynamic */
|
||||
};
|
||||
|
||||
static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = {
|
||||
.bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_INPUT_TERMINAL,
|
||||
.bTerminalID = 1,
|
||||
.wTerminalType = cpu_to_le16(0x0201),
|
||||
.bAssocTerminal = 0,
|
||||
.iTerminal = 0,
|
||||
.wObjectiveFocalLengthMin = cpu_to_le16(0),
|
||||
.wObjectiveFocalLengthMax = cpu_to_le16(0),
|
||||
.wOcularFocalLength = cpu_to_le16(0),
|
||||
.bControlSize = 3,
|
||||
.bmControls[0] = 2,
|
||||
.bmControls[1] = 0,
|
||||
.bmControls[2] = 0,
|
||||
};
|
||||
|
||||
static const struct uvc_processing_unit_descriptor uvc_processing = {
|
||||
.bLength = UVC_DT_PROCESSING_UNIT_SIZE(2),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_PROCESSING_UNIT,
|
||||
.bUnitID = 2,
|
||||
.bSourceID = 1,
|
||||
.wMaxMultiplier = cpu_to_le16(16*1024),
|
||||
.bControlSize = 2,
|
||||
.bmControls[0] = 1,
|
||||
.bmControls[1] = 0,
|
||||
.iProcessing = 0,
|
||||
};
|
||||
|
||||
static const struct uvc_output_terminal_descriptor uvc_output_terminal = {
|
||||
.bLength = UVC_DT_OUTPUT_TERMINAL_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_OUTPUT_TERMINAL,
|
||||
.bTerminalID = 3,
|
||||
.wTerminalType = cpu_to_le16(0x0101),
|
||||
.bAssocTerminal = 0,
|
||||
.bSourceID = 2,
|
||||
.iTerminal = 0,
|
||||
};
|
||||
|
||||
DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2);
|
||||
|
||||
static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = {
|
||||
.bLength = UVC_DT_INPUT_HEADER_SIZE(1, 2),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_INPUT_HEADER,
|
||||
.bNumFormats = 2,
|
||||
.wTotalLength = 0, /* dynamic */
|
||||
.bEndpointAddress = 0, /* dynamic */
|
||||
.bmInfo = 0,
|
||||
.bTerminalLink = 3,
|
||||
.bStillCaptureMethod = 0,
|
||||
.bTriggerSupport = 0,
|
||||
.bTriggerUsage = 0,
|
||||
.bControlSize = 1,
|
||||
.bmaControls[0][0] = 0,
|
||||
.bmaControls[1][0] = 4,
|
||||
};
|
||||
|
||||
static const struct uvc_format_uncompressed uvc_format_yuv = {
|
||||
.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_FORMAT_UNCOMPRESSED,
|
||||
.bFormatIndex = 1,
|
||||
.bNumFrameDescriptors = 2,
|
||||
.guidFormat =
|
||||
{ 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
|
||||
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71},
|
||||
.bBitsPerPixel = 16,
|
||||
.bDefaultFrameIndex = 1,
|
||||
.bAspectRatioX = 0,
|
||||
.bAspectRatioY = 0,
|
||||
.bmInterfaceFlags = 0,
|
||||
.bCopyProtect = 0,
|
||||
};
|
||||
|
||||
DECLARE_UVC_FRAME_UNCOMPRESSED(1);
|
||||
DECLARE_UVC_FRAME_UNCOMPRESSED(3);
|
||||
|
||||
static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = {
|
||||
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_FRAME_UNCOMPRESSED,
|
||||
.bFrameIndex = 1,
|
||||
.bmCapabilities = 0,
|
||||
.wWidth = cpu_to_le16(640),
|
||||
.wHeight = cpu_to_le16(360),
|
||||
.dwMinBitRate = cpu_to_le32(18432000),
|
||||
.dwMaxBitRate = cpu_to_le32(55296000),
|
||||
.dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
|
||||
.dwDefaultFrameInterval = cpu_to_le32(666666),
|
||||
.bFrameIntervalType = 3,
|
||||
.dwFrameInterval[0] = cpu_to_le32(666666),
|
||||
.dwFrameInterval[1] = cpu_to_le32(1000000),
|
||||
.dwFrameInterval[2] = cpu_to_le32(5000000),
|
||||
};
|
||||
|
||||
static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = {
|
||||
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_FRAME_UNCOMPRESSED,
|
||||
.bFrameIndex = 2,
|
||||
.bmCapabilities = 0,
|
||||
.wWidth = cpu_to_le16(1280),
|
||||
.wHeight = cpu_to_le16(720),
|
||||
.dwMinBitRate = cpu_to_le32(29491200),
|
||||
.dwMaxBitRate = cpu_to_le32(29491200),
|
||||
.dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
|
||||
.dwDefaultFrameInterval = cpu_to_le32(5000000),
|
||||
.bFrameIntervalType = 1,
|
||||
.dwFrameInterval[0] = cpu_to_le32(5000000),
|
||||
};
|
||||
|
||||
static const struct uvc_format_mjpeg uvc_format_mjpg = {
|
||||
.bLength = UVC_DT_FORMAT_MJPEG_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_FORMAT_MJPEG,
|
||||
.bFormatIndex = 2,
|
||||
.bNumFrameDescriptors = 2,
|
||||
.bmFlags = 0,
|
||||
.bDefaultFrameIndex = 1,
|
||||
.bAspectRatioX = 0,
|
||||
.bAspectRatioY = 0,
|
||||
.bmInterfaceFlags = 0,
|
||||
.bCopyProtect = 0,
|
||||
};
|
||||
|
||||
DECLARE_UVC_FRAME_MJPEG(1);
|
||||
DECLARE_UVC_FRAME_MJPEG(3);
|
||||
|
||||
static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = {
|
||||
.bLength = UVC_DT_FRAME_MJPEG_SIZE(3),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_FRAME_MJPEG,
|
||||
.bFrameIndex = 1,
|
||||
.bmCapabilities = 0,
|
||||
.wWidth = cpu_to_le16(640),
|
||||
.wHeight = cpu_to_le16(360),
|
||||
.dwMinBitRate = cpu_to_le32(18432000),
|
||||
.dwMaxBitRate = cpu_to_le32(55296000),
|
||||
.dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
|
||||
.dwDefaultFrameInterval = cpu_to_le32(666666),
|
||||
.bFrameIntervalType = 3,
|
||||
.dwFrameInterval[0] = cpu_to_le32(666666),
|
||||
.dwFrameInterval[1] = cpu_to_le32(1000000),
|
||||
.dwFrameInterval[2] = cpu_to_le32(5000000),
|
||||
};
|
||||
|
||||
static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
|
||||
.bLength = UVC_DT_FRAME_MJPEG_SIZE(1),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_FRAME_MJPEG,
|
||||
.bFrameIndex = 2,
|
||||
.bmCapabilities = 0,
|
||||
.wWidth = cpu_to_le16(1280),
|
||||
.wHeight = cpu_to_le16(720),
|
||||
.dwMinBitRate = cpu_to_le32(29491200),
|
||||
.dwMaxBitRate = cpu_to_le32(29491200),
|
||||
.dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
|
||||
.dwDefaultFrameInterval = cpu_to_le32(5000000),
|
||||
.bFrameIntervalType = 1,
|
||||
.dwFrameInterval[0] = cpu_to_le32(5000000),
|
||||
};
|
||||
|
||||
static const struct uvc_color_matching_descriptor uvc_color_matching = {
|
||||
.bLength = UVC_DT_COLOR_MATCHING_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = UVC_DT_COLOR_MATCHING,
|
||||
.bColorPrimaries = 1,
|
||||
.bTransferCharacteristics = 1,
|
||||
.bMatrixCoefficients = 4,
|
||||
};
|
||||
|
||||
static const struct uvc_descriptor_header * const uvc_control_cls[] = {
|
||||
(const struct uvc_descriptor_header *) &uvc_control_header,
|
||||
(const struct uvc_descriptor_header *) &uvc_camera_terminal,
|
||||
(const struct uvc_descriptor_header *) &uvc_processing,
|
||||
(const struct uvc_descriptor_header *) &uvc_output_terminal,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = {
|
||||
(const struct uvc_descriptor_header *) &uvc_input_header,
|
||||
(const struct uvc_descriptor_header *) &uvc_format_yuv,
|
||||
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
|
||||
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
|
||||
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
|
||||
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
|
||||
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
|
||||
(const struct uvc_descriptor_header *) &uvc_color_matching,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
|
||||
(const struct uvc_descriptor_header *) &uvc_input_header,
|
||||
(const struct uvc_descriptor_header *) &uvc_format_yuv,
|
||||
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
|
||||
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
|
||||
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
|
||||
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
|
||||
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
|
||||
(const struct uvc_descriptor_header *) &uvc_color_matching,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* USB configuration
|
||||
*/
|
||||
|
||||
static int __init
|
||||
webcam_config_bind(struct usb_configuration *c)
|
||||
{
|
||||
return uvc_bind_config(c, uvc_control_cls, uvc_fs_streaming_cls,
|
||||
uvc_hs_streaming_cls);
|
||||
}
|
||||
|
||||
static struct usb_configuration webcam_config_driver = {
|
||||
.label = webcam_config_label,
|
||||
.bind = webcam_config_bind,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = 0, /* dynamic */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
.bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
|
||||
};
|
||||
|
||||
static int /* __init_or_exit */
|
||||
webcam_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init
|
||||
webcam_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string contents
|
||||
* can be overridden by the composite_dev glue.
|
||||
*/
|
||||
if ((ret = usb_string_id(cdev)) < 0)
|
||||
goto error;
|
||||
webcam_strings[STRING_MANUFACTURER_IDX].id = ret;
|
||||
webcam_device_descriptor.iManufacturer = ret;
|
||||
|
||||
if ((ret = usb_string_id(cdev)) < 0)
|
||||
goto error;
|
||||
webcam_strings[STRING_PRODUCT_IDX].id = ret;
|
||||
webcam_device_descriptor.iProduct = ret;
|
||||
|
||||
if ((ret = usb_string_id(cdev)) < 0)
|
||||
goto error;
|
||||
webcam_strings[STRING_DESCRIPTION_IDX].id = ret;
|
||||
webcam_config_driver.iConfiguration = ret;
|
||||
|
||||
/* Register our configuration. */
|
||||
if ((ret = usb_add_config(cdev, &webcam_config_driver)) < 0)
|
||||
goto error;
|
||||
|
||||
INFO(cdev, "Webcam Video Gadget\n");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
webcam_unbind(cdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Driver
|
||||
*/
|
||||
|
||||
static struct usb_composite_driver webcam_driver = {
|
||||
.name = "g_webcam",
|
||||
.dev = &webcam_device_descriptor,
|
||||
.strings = webcam_device_strings,
|
||||
.bind = webcam_bind,
|
||||
.unbind = webcam_unbind,
|
||||
};
|
||||
|
||||
static int __init
|
||||
webcam_init(void)
|
||||
{
|
||||
return usb_composite_register(&webcam_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
webcam_cleanup(void)
|
||||
{
|
||||
usb_composite_unregister(&webcam_driver);
|
||||
}
|
||||
|
||||
module_init(webcam_init);
|
||||
module_exit(webcam_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Laurent Pinchart");
|
||||
MODULE_DESCRIPTION("Webcam Video Gadget");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.1.0");
|
||||
|
||||
|
|
@ -207,6 +207,21 @@ config USB_OHCI_HCD
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ohci-hcd.
|
||||
|
||||
config USB_OHCI_HCD_OMAP1
|
||||
bool "OHCI support for OMAP1/2 chips"
|
||||
depends on USB_OHCI_HCD && (ARCH_OMAP1 || ARCH_OMAP2)
|
||||
default y
|
||||
---help---
|
||||
Enables support for the OHCI controller on OMAP1/2 chips.
|
||||
|
||||
config USB_OHCI_HCD_OMAP3
|
||||
bool "OHCI support for OMAP3 and later chips"
|
||||
depends on USB_OHCI_HCD && (ARCH_OMAP3 || ARCH_OMAP4)
|
||||
default y
|
||||
---help---
|
||||
Enables support for the on-chip OHCI controller on
|
||||
OMAP3 and later chips.
|
||||
|
||||
config USB_OHCI_HCD_PPC_SOC
|
||||
bool "OHCI support for on-chip PPC USB controller"
|
||||
depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx)
|
||||
|
|
|
|||
|
|
@ -69,6 +69,15 @@ static void au1xxx_stop_ehc(void)
|
|||
au_sync();
|
||||
}
|
||||
|
||||
static int au1xxx_ehci_setup(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int ret = ehci_init(hcd);
|
||||
|
||||
ehci->need_io_watchdog = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct hc_driver ehci_au1xxx_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "Au1xxx EHCI",
|
||||
|
|
@ -86,7 +95,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
|
|||
* FIXME -- ehci_init() doesn't do enough here.
|
||||
* See ehci-ppc-soc for a complete implementation.
|
||||
*/
|
||||
.reset = ehci_init,
|
||||
.reset = au1xxx_ehci_setup,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
|
@ -215,26 +224,17 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
|
|||
msleep(10);
|
||||
|
||||
/* Root hub was already suspended. Disable irq emission and
|
||||
* mark HW unaccessible, bail out if RH has been resumed. Use
|
||||
* the spinlock to properly synchronize with possible pending
|
||||
* RH suspend or resume activity.
|
||||
*
|
||||
* This is still racy as hcd->state is manipulated outside of
|
||||
* any locks =P But that will be a different fix.
|
||||
* mark HW unaccessible. The PM and USB cores make sure that
|
||||
* the root hub is either suspended or stopped.
|
||||
*/
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
if (hcd->state != HC_STATE_SUSPENDED) {
|
||||
rc = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
ehci_prepare_ports_for_controller_suspend(ehci);
|
||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
|
||||
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
|
||||
au1xxx_stop_ehc();
|
||||
|
||||
bail:
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
|
||||
// could save FLADJ in case of Vaux power loss
|
||||
|
|
@ -264,6 +264,7 @@ static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
|
|||
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
|
||||
int mask = INTR_MASK;
|
||||
|
||||
ehci_prepare_ports_for_controller_resume(ehci);
|
||||
if (!hcd->self.root_hub->do_remote_wakeup)
|
||||
mask &= ~STS_PCD;
|
||||
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@ static int ehci_fsl_drv_suspend(struct device *dev)
|
|||
struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
|
||||
void __iomem *non_ehci = hcd->regs;
|
||||
|
||||
ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd));
|
||||
if (!fsl_deep_sleep())
|
||||
return 0;
|
||||
|
||||
|
|
@ -327,6 +328,7 @@ static int ehci_fsl_drv_resume(struct device *dev)
|
|||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
void __iomem *non_ehci = hcd->regs;
|
||||
|
||||
ehci_prepare_ports_for_controller_resume(ehci);
|
||||
if (!fsl_deep_sleep())
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,13 +31,12 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "../core/hcd.h"
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
|
|
|||
|
|
@ -106,12 +106,75 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
|
|||
ehci->owned_ports = 0;
|
||||
}
|
||||
|
||||
static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||
bool suspending)
|
||||
{
|
||||
int port;
|
||||
u32 temp;
|
||||
|
||||
/* If remote wakeup is enabled for the root hub but disabled
|
||||
* for the controller, we must adjust all the port wakeup flags
|
||||
* when the controller is suspended or resumed. In all other
|
||||
* cases they don't need to be changed.
|
||||
*/
|
||||
if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup ||
|
||||
device_may_wakeup(ehci_to_hcd(ehci)->self.controller))
|
||||
return;
|
||||
|
||||
/* clear phy low-power mode before changing wakeup flags */
|
||||
if (ehci->has_hostpc) {
|
||||
port = HCS_N_PORTS(ehci->hcs_params);
|
||||
while (port--) {
|
||||
u32 __iomem *hostpc_reg;
|
||||
|
||||
hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
|
||||
+ HOSTPC0 + 4 * port);
|
||||
temp = ehci_readl(ehci, hostpc_reg);
|
||||
ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg);
|
||||
}
|
||||
msleep(5);
|
||||
}
|
||||
|
||||
port = HCS_N_PORTS(ehci->hcs_params);
|
||||
while (port--) {
|
||||
u32 __iomem *reg = &ehci->regs->port_status[port];
|
||||
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
|
||||
u32 t2 = t1 & ~PORT_WAKE_BITS;
|
||||
|
||||
/* If we are suspending the controller, clear the flags.
|
||||
* If we are resuming the controller, set the wakeup flags.
|
||||
*/
|
||||
if (!suspending) {
|
||||
if (t1 & PORT_CONNECT)
|
||||
t2 |= PORT_WKOC_E | PORT_WKDISC_E;
|
||||
else
|
||||
t2 |= PORT_WKOC_E | PORT_WKCONN_E;
|
||||
}
|
||||
ehci_vdbg(ehci, "port %d, %08x -> %08x\n",
|
||||
port + 1, t1, t2);
|
||||
ehci_writel(ehci, t2, reg);
|
||||
}
|
||||
|
||||
/* enter phy low-power mode again */
|
||||
if (ehci->has_hostpc) {
|
||||
port = HCS_N_PORTS(ehci->hcs_params);
|
||||
while (port--) {
|
||||
u32 __iomem *hostpc_reg;
|
||||
|
||||
hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
|
||||
+ HOSTPC0 + 4 * port);
|
||||
temp = ehci_readl(ehci, hostpc_reg);
|
||||
ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
int port;
|
||||
int mask;
|
||||
u32 __iomem *hostpc_reg = NULL;
|
||||
int changed;
|
||||
|
||||
ehci_dbg(ehci, "suspend root hub\n");
|
||||
|
||||
|
|
@ -155,15 +218,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
|||
*/
|
||||
ehci->bus_suspended = 0;
|
||||
ehci->owned_ports = 0;
|
||||
changed = 0;
|
||||
port = HCS_N_PORTS(ehci->hcs_params);
|
||||
while (port--) {
|
||||
u32 __iomem *reg = &ehci->regs->port_status [port];
|
||||
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
|
||||
u32 t2 = t1;
|
||||
u32 t2 = t1 & ~PORT_WAKE_BITS;
|
||||
|
||||
if (ehci->has_hostpc)
|
||||
hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
|
||||
+ HOSTPC0 + 4 * (port & 0xff));
|
||||
/* keep track of which ports we suspend */
|
||||
if (t1 & PORT_OWNER)
|
||||
set_bit(port, &ehci->owned_ports);
|
||||
|
|
@ -172,40 +233,45 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
|||
set_bit(port, &ehci->bus_suspended);
|
||||
}
|
||||
|
||||
/* enable remote wakeup on all ports */
|
||||
/* enable remote wakeup on all ports, if told to do so */
|
||||
if (hcd->self.root_hub->do_remote_wakeup) {
|
||||
/* only enable appropriate wake bits, otherwise the
|
||||
* hardware can not go phy low power mode. If a race
|
||||
* condition happens here(connection change during bits
|
||||
* set), the port change detection will finally fix it.
|
||||
*/
|
||||
if (t1 & PORT_CONNECT) {
|
||||
if (t1 & PORT_CONNECT)
|
||||
t2 |= PORT_WKOC_E | PORT_WKDISC_E;
|
||||
t2 &= ~PORT_WKCONN_E;
|
||||
} else {
|
||||
else
|
||||
t2 |= PORT_WKOC_E | PORT_WKCONN_E;
|
||||
t2 &= ~PORT_WKDISC_E;
|
||||
}
|
||||
} else
|
||||
t2 &= ~PORT_WAKE_BITS;
|
||||
}
|
||||
|
||||
if (t1 != t2) {
|
||||
ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
|
||||
port + 1, t1, t2);
|
||||
ehci_writel(ehci, t2, reg);
|
||||
if (hostpc_reg) {
|
||||
u32 t3;
|
||||
changed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
msleep(5);/* 5ms for HCD enter low pwr mode */
|
||||
spin_lock_irq(&ehci->lock);
|
||||
t3 = ehci_readl(ehci, hostpc_reg);
|
||||
ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
|
||||
t3 = ehci_readl(ehci, hostpc_reg);
|
||||
ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
|
||||
if (changed && ehci->has_hostpc) {
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
msleep(5); /* 5 ms for HCD to enter low-power mode */
|
||||
spin_lock_irq(&ehci->lock);
|
||||
|
||||
port = HCS_N_PORTS(ehci->hcs_params);
|
||||
while (port--) {
|
||||
u32 __iomem *hostpc_reg;
|
||||
u32 t3;
|
||||
|
||||
hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
|
||||
+ HOSTPC0 + 4 * port);
|
||||
t3 = ehci_readl(ehci, hostpc_reg);
|
||||
ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
|
||||
t3 = ehci_readl(ehci, hostpc_reg);
|
||||
ehci_dbg(ehci, "Port %d phy low-power mode %s\n",
|
||||
port, (t3 & HOSTPC_PHCD) ?
|
||||
"succeeded" : "failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -291,6 +357,25 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
|||
msleep(8);
|
||||
spin_lock_irq(&ehci->lock);
|
||||
|
||||
/* clear phy low-power mode before resume */
|
||||
if (ehci->bus_suspended && ehci->has_hostpc) {
|
||||
i = HCS_N_PORTS(ehci->hcs_params);
|
||||
while (i--) {
|
||||
if (test_bit(i, &ehci->bus_suspended)) {
|
||||
u32 __iomem *hostpc_reg;
|
||||
|
||||
hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
|
||||
+ HOSTPC0 + 4 * i);
|
||||
temp = ehci_readl(ehci, hostpc_reg);
|
||||
ehci_writel(ehci, temp & ~HOSTPC_PHCD,
|
||||
hostpc_reg);
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
msleep(5);
|
||||
spin_lock_irq(&ehci->lock);
|
||||
}
|
||||
|
||||
/* manually resume the ports we suspended during bus_suspend() */
|
||||
i = HCS_N_PORTS (ehci->hcs_params);
|
||||
while (i--) {
|
||||
|
|
@ -659,7 +744,7 @@ static int ehci_hub_control (
|
|||
* Even if OWNER is set, so the port is owned by the
|
||||
* companion controller, khubd needs to be able to clear
|
||||
* the port-change status bits (especially
|
||||
* USB_PORT_FEAT_C_CONNECTION).
|
||||
* USB_PORT_STAT_C_CONNECTION).
|
||||
*/
|
||||
|
||||
switch (wValue) {
|
||||
|
|
@ -675,16 +760,25 @@ static int ehci_hub_control (
|
|||
goto error;
|
||||
if (ehci->no_selective_suspend)
|
||||
break;
|
||||
if (temp & PORT_SUSPEND) {
|
||||
if ((temp & PORT_PE) == 0)
|
||||
goto error;
|
||||
/* resume signaling for 20 msec */
|
||||
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
|
||||
ehci_writel(ehci, temp | PORT_RESUME,
|
||||
status_reg);
|
||||
ehci->reset_done [wIndex] = jiffies
|
||||
+ msecs_to_jiffies (20);
|
||||
if (!(temp & PORT_SUSPEND))
|
||||
break;
|
||||
if ((temp & PORT_PE) == 0)
|
||||
goto error;
|
||||
|
||||
/* clear phy low-power mode before resume */
|
||||
if (hostpc_reg) {
|
||||
temp1 = ehci_readl(ehci, hostpc_reg);
|
||||
ehci_writel(ehci, temp1 & ~HOSTPC_PHCD,
|
||||
hostpc_reg);
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
msleep(5);/* wait to leave low-power mode */
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
}
|
||||
/* resume signaling for 20 msec */
|
||||
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
|
||||
ehci_writel(ehci, temp | PORT_RESUME, status_reg);
|
||||
ehci->reset_done[wIndex] = jiffies
|
||||
+ msecs_to_jiffies(20);
|
||||
break;
|
||||
case USB_PORT_FEAT_C_SUSPEND:
|
||||
clear_bit(wIndex, &ehci->port_c_suspend);
|
||||
|
|
@ -729,12 +823,12 @@ static int ehci_hub_control (
|
|||
|
||||
// wPortChange bits
|
||||
if (temp & PORT_CSC)
|
||||
status |= 1 << USB_PORT_FEAT_C_CONNECTION;
|
||||
status |= USB_PORT_STAT_C_CONNECTION << 16;
|
||||
if (temp & PORT_PEC)
|
||||
status |= 1 << USB_PORT_FEAT_C_ENABLE;
|
||||
status |= USB_PORT_STAT_C_ENABLE << 16;
|
||||
|
||||
if ((temp & PORT_OCC) && !ignore_oc){
|
||||
status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
|
||||
status |= USB_PORT_STAT_C_OVERCURRENT << 16;
|
||||
|
||||
/*
|
||||
* Hubs should disable port power on over-current.
|
||||
|
|
@ -791,7 +885,7 @@ static int ehci_hub_control (
|
|||
if ((temp & PORT_RESET)
|
||||
&& time_after_eq(jiffies,
|
||||
ehci->reset_done[wIndex])) {
|
||||
status |= 1 << USB_PORT_FEAT_C_RESET;
|
||||
status |= USB_PORT_STAT_C_RESET << 16;
|
||||
ehci->reset_done [wIndex] = 0;
|
||||
|
||||
/* force reset to complete */
|
||||
|
|
@ -833,7 +927,7 @@ static int ehci_hub_control (
|
|||
*/
|
||||
|
||||
if (temp & PORT_CONNECT) {
|
||||
status |= 1 << USB_PORT_FEAT_CONNECTION;
|
||||
status |= USB_PORT_STAT_CONNECTION;
|
||||
// status may be from integrated TT
|
||||
if (ehci->has_hostpc) {
|
||||
temp1 = ehci_readl(ehci, hostpc_reg);
|
||||
|
|
@ -842,11 +936,11 @@ static int ehci_hub_control (
|
|||
status |= ehci_port_speed(ehci, temp);
|
||||
}
|
||||
if (temp & PORT_PE)
|
||||
status |= 1 << USB_PORT_FEAT_ENABLE;
|
||||
status |= USB_PORT_STAT_ENABLE;
|
||||
|
||||
/* maybe the port was unsuspended without our knowledge */
|
||||
if (temp & (PORT_SUSPEND|PORT_RESUME)) {
|
||||
status |= 1 << USB_PORT_FEAT_SUSPEND;
|
||||
status |= USB_PORT_STAT_SUSPEND;
|
||||
} else if (test_bit(wIndex, &ehci->suspended_ports)) {
|
||||
clear_bit(wIndex, &ehci->suspended_ports);
|
||||
ehci->reset_done[wIndex] = 0;
|
||||
|
|
@ -855,13 +949,13 @@ static int ehci_hub_control (
|
|||
}
|
||||
|
||||
if (temp & PORT_OC)
|
||||
status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
|
||||
status |= USB_PORT_STAT_OVERCURRENT;
|
||||
if (temp & PORT_RESET)
|
||||
status |= 1 << USB_PORT_FEAT_RESET;
|
||||
status |= USB_PORT_STAT_RESET;
|
||||
if (temp & PORT_POWER)
|
||||
status |= 1 << USB_PORT_FEAT_POWER;
|
||||
status |= USB_PORT_STAT_POWER;
|
||||
if (test_bit(wIndex, &ehci->port_c_suspend))
|
||||
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
|
||||
status |= USB_PORT_STAT_C_SUSPEND << 16;
|
||||
|
||||
#ifndef VERBOSE_DEBUG
|
||||
if (status & ~0xffff) /* only if wPortChange is interesting */
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@
|
|||
#define OMAP_UHH_DEBUG_CSR (0x44)
|
||||
|
||||
/* EHCI Register Set */
|
||||
#define EHCI_INSNREG04 (0xA0)
|
||||
#define EHCI_INSNREG04_DISABLE_UNSUSPEND (1 << 5)
|
||||
#define EHCI_INSNREG05_ULPI (0xA4)
|
||||
#define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31
|
||||
#define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24
|
||||
|
|
@ -352,8 +354,8 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
|
||||
|
||||
/* Bypass the TLL module for PHY mode operation */
|
||||
if (omap_rev() <= OMAP3430_REV_ES2_1) {
|
||||
dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1 \n");
|
||||
if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) {
|
||||
dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n");
|
||||
if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) ||
|
||||
(omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) ||
|
||||
(omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY))
|
||||
|
|
@ -382,6 +384,18 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
|
||||
|
||||
|
||||
/*
|
||||
* An undocumented "feature" in the OMAP3 EHCI controller,
|
||||
* causes suspended ports to be taken out of suspend when
|
||||
* the USBCMD.Run/Stop bit is cleared (for example when
|
||||
* we do ehci_bus_suspend).
|
||||
* This breaks suspend-resume if the root-hub is allowed
|
||||
* to suspend. Writing 1 to this undocumented register bit
|
||||
* disables this feature and restores normal behavior.
|
||||
*/
|
||||
ehci_omap_writel(omap->ehci_base, EHCI_INSNREG04,
|
||||
EHCI_INSNREG04_DISABLE_UNSUSPEND);
|
||||
|
||||
if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) ||
|
||||
(omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) ||
|
||||
(omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) {
|
||||
|
|
@ -659,6 +673,9 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
|
|||
goto err_add_hcd;
|
||||
}
|
||||
|
||||
/* root ports should always stay powered */
|
||||
ehci_port_power(omap->ehci, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
err_add_hcd:
|
||||
|
|
|
|||
|
|
@ -109,6 +109,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
|||
return retval;
|
||||
|
||||
switch (pdev->vendor) {
|
||||
case PCI_VENDOR_ID_NEC:
|
||||
ehci->need_io_watchdog = 0;
|
||||
break;
|
||||
case PCI_VENDOR_ID_INTEL:
|
||||
ehci->need_io_watchdog = 0;
|
||||
if (pdev->device == 0x27cc) {
|
||||
|
|
@ -284,23 +287,15 @@ static int ehci_pci_suspend(struct usb_hcd *hcd)
|
|||
msleep(10);
|
||||
|
||||
/* Root hub was already suspended. Disable irq emission and
|
||||
* mark HW unaccessible, bail out if RH has been resumed. Use
|
||||
* the spinlock to properly synchronize with possible pending
|
||||
* RH suspend or resume activity.
|
||||
*
|
||||
* This is still racy as hcd->state is manipulated outside of
|
||||
* any locks =P But that will be a different fix.
|
||||
* mark HW unaccessible. The PM and USB cores make sure that
|
||||
* the root hub is either suspended or stopped.
|
||||
*/
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
if (hcd->state != HC_STATE_SUSPENDED) {
|
||||
rc = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
ehci_prepare_ports_for_controller_suspend(ehci);
|
||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
|
||||
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
bail:
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
|
||||
// could save FLADJ in case of Vaux power loss
|
||||
|
|
@ -330,6 +325,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
|||
!hibernated) {
|
||||
int mask = INTR_MASK;
|
||||
|
||||
ehci_prepare_ports_for_controller_resume(ehci);
|
||||
if (!hcd->self.root_hub->do_remote_wakeup)
|
||||
mask &= ~STS_PCD;
|
||||
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
|
||||
|
|
|
|||
|
|
@ -663,7 +663,7 @@ qh_urb_transaction (
|
|||
*/
|
||||
i = urb->num_sgs;
|
||||
if (len > 0 && i > 0) {
|
||||
sg = urb->sg->sg;
|
||||
sg = urb->sg;
|
||||
buf = sg_dma_address(sg);
|
||||
|
||||
/* urb->transfer_buffer_length may be smaller than the
|
||||
|
|
|
|||
|
|
@ -536,6 +536,16 @@ struct ehci_fstn {
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* Prepare the PORTSC wakeup flags during controller suspend/resume */
|
||||
|
||||
#define ehci_prepare_ports_for_controller_suspend(ehci) \
|
||||
ehci_adjust_port_wakeup_flags(ehci, true);
|
||||
|
||||
#define ehci_prepare_ports_for_controller_resume(ehci) \
|
||||
ehci_adjust_port_wakeup_flags(ehci, false);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_ROOT_HUB_TT
|
||||
|
||||
/*
|
||||
|
|
@ -556,20 +566,20 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
|
|||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return (1<<USB_PORT_FEAT_LOWSPEED);
|
||||
return USB_PORT_STAT_LOW_SPEED;
|
||||
case 2:
|
||||
default:
|
||||
return (1<<USB_PORT_FEAT_HIGHSPEED);
|
||||
return USB_PORT_STAT_HIGH_SPEED;
|
||||
}
|
||||
}
|
||||
return (1<<USB_PORT_FEAT_HIGHSPEED);
|
||||
return USB_PORT_STAT_HIGH_SPEED;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define ehci_is_TDI(e) (0)
|
||||
|
||||
#define ehci_port_speed(ehci, portsc) (1<<USB_PORT_FEAT_HIGHSPEED)
|
||||
#define ehci_port_speed(ehci, portsc) USB_PORT_STAT_HIGH_SPEED
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/usb.h>
|
||||
#include "../core/hcd.h"
|
||||
#include <linux/usb/hcd.h>
|
||||
#include "fhci.h"
|
||||
|
||||
void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er)
|
||||
|
|
|
|||
|
|
@ -25,12 +25,12 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/qe.h>
|
||||
#include <asm/fsl_gtm.h>
|
||||
#include "../core/hcd.h"
|
||||
#include "fhci.h"
|
||||
|
||||
void fhci_start_sof_timer(struct fhci_hcd *fhci)
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <asm/qe.h>
|
||||
#include "../core/hcd.h"
|
||||
#include "fhci.h"
|
||||
|
||||
/* virtual root hub specific descriptor */
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/usb.h>
|
||||
#include "../core/hcd.h"
|
||||
#include <linux/usb/hcd.h>
|
||||
#include "fhci.h"
|
||||
|
||||
static void init_td(struct td *td)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/usb.h>
|
||||
#include "../core/hcd.h"
|
||||
#include <linux/usb/hcd.h>
|
||||
#include "fhci.h"
|
||||
|
||||
/* maps the hardware error code to the USB error code */
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <asm/qe.h>
|
||||
#include <asm/fsl_gtm.h>
|
||||
#include "../core/hcd.h"
|
||||
#include "fhci.h"
|
||||
|
||||
static void recycle_frame(struct fhci_usb *usb, struct packet *pkt)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/usb.h>
|
||||
#include "../core/hcd.h"
|
||||
#include <linux/usb/hcd.h>
|
||||
#include "fhci.h"
|
||||
|
||||
#define DUMMY_BD_BUFFER 0xdeadbeef
|
||||
|
|
|
|||
|
|
@ -20,13 +20,14 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <asm/qe.h>
|
||||
#include "../core/hcd.h"
|
||||
|
||||
#define USB_CLOCK 48000000
|
||||
|
||||
|
|
@ -515,9 +516,13 @@ static inline int cq_put(struct kfifo *kfifo, void *p)
|
|||
|
||||
static inline void *cq_get(struct kfifo *kfifo)
|
||||
{
|
||||
void *p = NULL;
|
||||
unsigned int sz;
|
||||
void *p;
|
||||
|
||||
sz = kfifo_out(kfifo, (void *)&p, sizeof(p));
|
||||
if (sz != sizeof(p))
|
||||
return NULL;
|
||||
|
||||
kfifo_out(kfifo, (void *)&p, sizeof(p));
|
||||
return p;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include "../core/hcd.h"
|
||||
#include "imx21-hcd.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/isp116x.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
|
@ -72,7 +73,6 @@
|
|||
#include <asm/system.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "../core/hcd.h"
|
||||
#include "isp116x.h"
|
||||
|
||||
#define DRIVER_VERSION "03 Nov 2005"
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/isp1362.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/io.h>
|
||||
|
|
@ -95,7 +96,6 @@ module_param(dbg_level, int, 0);
|
|||
#define STUB_DEBUG_FILE
|
||||
#endif
|
||||
|
||||
#include "../core/hcd.h"
|
||||
#include "../core/usb.h"
|
||||
#include "isp1362.h"
|
||||
|
||||
|
|
@ -1265,7 +1265,7 @@ static int isp1362_urb_enqueue(struct usb_hcd *hcd,
|
|||
|
||||
/* don't submit to a dead or disabled port */
|
||||
if (!((isp1362_hcd->rhport[0] | isp1362_hcd->rhport[1]) &
|
||||
(1 << USB_PORT_FEAT_ENABLE)) ||
|
||||
USB_PORT_STAT_ENABLE) ||
|
||||
!HC_IS_RUNNING(hcd->state)) {
|
||||
kfree(ep);
|
||||
retval = -ENODEV;
|
||||
|
|
@ -2217,7 +2217,7 @@ static void create_debug_file(struct isp1362_hcd *isp1362_hcd)
|
|||
static void remove_debug_file(struct isp1362_hcd *isp1362_hcd)
|
||||
{
|
||||
if (isp1362_hcd->pde)
|
||||
remove_proc_entry(proc_filename, 0);
|
||||
remove_proc_entry(proc_filename, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
|
|
@ -21,7 +22,6 @@
|
|||
#include <asm/unaligned.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include "../core/hcd.h"
|
||||
#include "isp1760-hcd.h"
|
||||
|
||||
static struct kmem_cache *qtd_cachep;
|
||||
|
|
@ -111,7 +111,7 @@ struct isp1760_qh {
|
|||
u32 ping;
|
||||
};
|
||||
|
||||
#define ehci_port_speed(priv, portsc) (1 << USB_PORT_FEAT_HIGHSPEED)
|
||||
#define ehci_port_speed(priv, portsc) USB_PORT_STAT_HIGH_SPEED
|
||||
|
||||
static unsigned int isp1760_readl(__u32 __iomem *regs)
|
||||
{
|
||||
|
|
@ -713,12 +713,11 @@ static int check_error(struct ptd *ptd)
|
|||
u32 dw3;
|
||||
|
||||
dw3 = le32_to_cpu(ptd->dw3);
|
||||
if (dw3 & DW3_HALT_BIT)
|
||||
if (dw3 & DW3_HALT_BIT) {
|
||||
error = -EPIPE;
|
||||
|
||||
if (dw3 & DW3_ERROR_BIT) {
|
||||
printk(KERN_ERR "error bit is set in DW3\n");
|
||||
error = -EPIPE;
|
||||
if (dw3 & DW3_ERROR_BIT)
|
||||
pr_err("error bit is set in DW3\n");
|
||||
}
|
||||
|
||||
if (dw3 & DW3_QTD_ACTIVE) {
|
||||
|
|
@ -1923,7 +1922,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
|
|||
* Even if OWNER is set, so the port is owned by the
|
||||
* companion controller, khubd needs to be able to clear
|
||||
* the port-change status bits (especially
|
||||
* USB_PORT_FEAT_C_CONNECTION).
|
||||
* USB_PORT_STAT_C_CONNECTION).
|
||||
*/
|
||||
|
||||
switch (wValue) {
|
||||
|
|
@ -1987,7 +1986,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
|
|||
|
||||
/* wPortChange bits */
|
||||
if (temp & PORT_CSC)
|
||||
status |= 1 << USB_PORT_FEAT_C_CONNECTION;
|
||||
status |= USB_PORT_STAT_C_CONNECTION << 16;
|
||||
|
||||
|
||||
/* whoever resumes must GetPortStatus to complete it!! */
|
||||
|
|
@ -2007,7 +2006,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
|
|||
/* resume completed? */
|
||||
else if (time_after_eq(jiffies,
|
||||
priv->reset_done)) {
|
||||
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
|
||||
status |= USB_PORT_STAT_C_SUSPEND << 16;
|
||||
priv->reset_done = 0;
|
||||
|
||||
/* stop resume signaling */
|
||||
|
|
@ -2031,7 +2030,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
|
|||
if ((temp & PORT_RESET)
|
||||
&& time_after_eq(jiffies,
|
||||
priv->reset_done)) {
|
||||
status |= 1 << USB_PORT_FEAT_C_RESET;
|
||||
status |= USB_PORT_STAT_C_RESET << 16;
|
||||
priv->reset_done = 0;
|
||||
|
||||
/* force reset to complete */
|
||||
|
|
@ -2062,18 +2061,18 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
|
|||
printk(KERN_ERR "Warning: PORT_OWNER is set\n");
|
||||
|
||||
if (temp & PORT_CONNECT) {
|
||||
status |= 1 << USB_PORT_FEAT_CONNECTION;
|
||||
status |= USB_PORT_STAT_CONNECTION;
|
||||
/* status may be from integrated TT */
|
||||
status |= ehci_port_speed(priv, temp);
|
||||
}
|
||||
if (temp & PORT_PE)
|
||||
status |= 1 << USB_PORT_FEAT_ENABLE;
|
||||
status |= USB_PORT_STAT_ENABLE;
|
||||
if (temp & (PORT_SUSPEND|PORT_RESUME))
|
||||
status |= 1 << USB_PORT_FEAT_SUSPEND;
|
||||
status |= USB_PORT_STAT_SUSPEND;
|
||||
if (temp & PORT_RESET)
|
||||
status |= 1 << USB_PORT_FEAT_RESET;
|
||||
status |= USB_PORT_STAT_RESET;
|
||||
if (temp & PORT_POWER)
|
||||
status |= 1 << USB_PORT_FEAT_POWER;
|
||||
status |= USB_PORT_STAT_POWER;
|
||||
|
||||
put_unaligned(cpu_to_le32(status), (__le32 *) buf);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/isp1760.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include "../core/hcd.h"
|
||||
#include "isp1760-hcd.h"
|
||||
|
||||
#ifdef CONFIG_PPC_OF
|
||||
|
|
@ -36,7 +36,7 @@ static int of_isp1760_probe(struct of_device *dev,
|
|||
struct resource memory;
|
||||
struct of_irq oirq;
|
||||
int virq;
|
||||
u64 res_len;
|
||||
resource_size_t res_len;
|
||||
int ret;
|
||||
const unsigned int *prop;
|
||||
unsigned int devflags = 0;
|
||||
|
|
@ -45,13 +45,12 @@ static int of_isp1760_probe(struct of_device *dev,
|
|||
if (ret)
|
||||
return -ENXIO;
|
||||
|
||||
res = request_mem_region(memory.start, memory.end - memory.start + 1,
|
||||
dev_name(&dev->dev));
|
||||
res_len = resource_size(&memory);
|
||||
|
||||
res = request_mem_region(memory.start, res_len, dev_name(&dev->dev));
|
||||
if (!res)
|
||||
return -EBUSY;
|
||||
|
||||
res_len = memory.end - memory.start + 1;
|
||||
|
||||
if (of_irq_map_one(dp, 0, &oirq)) {
|
||||
ret = -ENODEV;
|
||||
goto release_reg;
|
||||
|
|
@ -92,7 +91,7 @@ static int of_isp1760_probe(struct of_device *dev,
|
|||
return ret;
|
||||
|
||||
release_reg:
|
||||
release_mem_region(memory.start, memory.end - memory.start + 1);
|
||||
release_mem_region(memory.start, res_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
|
@ -43,7 +44,6 @@
|
|||
#include <asm/unaligned.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "../core/hcd.h"
|
||||
|
||||
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
|
||||
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
|
||||
|
|
@ -1006,9 +1006,14 @@ MODULE_LICENSE ("GPL");
|
|||
#define PLATFORM_DRIVER ohci_hcd_s3c2410_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP
|
||||
#ifdef CONFIG_USB_OHCI_HCD_OMAP1
|
||||
#include "ohci-omap.c"
|
||||
#define PLATFORM_DRIVER ohci_hcd_omap_driver
|
||||
#define OMAP1_PLATFORM_DRIVER ohci_hcd_omap_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_OHCI_HCD_OMAP3
|
||||
#include "ohci-omap3.c"
|
||||
#define OMAP3_PLATFORM_DRIVER ohci_hcd_omap3_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_LH7A404
|
||||
|
|
@ -1092,6 +1097,8 @@ MODULE_LICENSE ("GPL");
|
|||
|
||||
#if !defined(PCI_DRIVER) && \
|
||||
!defined(PLATFORM_DRIVER) && \
|
||||
!defined(OMAP1_PLATFORM_DRIVER) && \
|
||||
!defined(OMAP3_PLATFORM_DRIVER) && \
|
||||
!defined(OF_PLATFORM_DRIVER) && \
|
||||
!defined(SA1111_DRIVER) && \
|
||||
!defined(PS3_SYSTEM_BUS_DRIVER) && \
|
||||
|
|
@ -1133,6 +1140,18 @@ static int __init ohci_hcd_mod_init(void)
|
|||
goto error_platform;
|
||||
#endif
|
||||
|
||||
#ifdef OMAP1_PLATFORM_DRIVER
|
||||
retval = platform_driver_register(&OMAP1_PLATFORM_DRIVER);
|
||||
if (retval < 0)
|
||||
goto error_omap1_platform;
|
||||
#endif
|
||||
|
||||
#ifdef OMAP3_PLATFORM_DRIVER
|
||||
retval = platform_driver_register(&OMAP3_PLATFORM_DRIVER);
|
||||
if (retval < 0)
|
||||
goto error_omap3_platform;
|
||||
#endif
|
||||
|
||||
#ifdef OF_PLATFORM_DRIVER
|
||||
retval = of_register_platform_driver(&OF_PLATFORM_DRIVER);
|
||||
if (retval < 0)
|
||||
|
|
@ -1200,6 +1219,14 @@ static int __init ohci_hcd_mod_init(void)
|
|||
platform_driver_unregister(&PLATFORM_DRIVER);
|
||||
error_platform:
|
||||
#endif
|
||||
#ifdef OMAP1_PLATFORM_DRIVER
|
||||
platform_driver_unregister(&OMAP1_PLATFORM_DRIVER);
|
||||
error_omap1_platform:
|
||||
#endif
|
||||
#ifdef OMAP3_PLATFORM_DRIVER
|
||||
platform_driver_unregister(&OMAP3_PLATFORM_DRIVER);
|
||||
error_omap3_platform:
|
||||
#endif
|
||||
#ifdef PS3_SYSTEM_BUS_DRIVER
|
||||
ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
|
||||
error_ps3:
|
||||
|
|
|
|||
735
drivers/usb/host/ohci-omap3.c
Normal file
735
drivers/usb/host/ohci-omap3.c
Normal file
|
|
@ -0,0 +1,735 @@
|
|||
/*
|
||||
* ohci-omap3.c - driver for OHCI on OMAP3 and later processors
|
||||
*
|
||||
* Bus Glue for OMAP3 USBHOST 3 port OHCI controller
|
||||
* This controller is also used in later OMAPs and AM35x chips
|
||||
*
|
||||
* Copyright (C) 2007-2010 Texas Instruments, Inc.
|
||||
* Author: Vikram Pandita <vikram.pandita@ti.com>
|
||||
* Author: Anand Gadiyar <gadiyar@ti.com>
|
||||
*
|
||||
* Based on ehci-omap.c and some other ohci glue layers
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* TODO (last updated Mar 10th, 2010):
|
||||
* - add kernel-doc
|
||||
* - Factor out code common to EHCI to a separate file
|
||||
* - Make EHCI and OHCI coexist together
|
||||
* - needs newer silicon versions to actually work
|
||||
* - the last one to be loaded currently steps on the other's toes
|
||||
* - Add hooks for configuring transceivers, etc. at init/exit
|
||||
* - Add aggressive clock-management code
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <plat/usb.h>
|
||||
|
||||
/*
|
||||
* OMAP USBHOST Register addresses: VIRTUAL ADDRESSES
|
||||
* Use ohci_omap_readl()/ohci_omap_writel() functions
|
||||
*/
|
||||
|
||||
/* TLL Register Set */
|
||||
#define OMAP_USBTLL_REVISION (0x00)
|
||||
#define OMAP_USBTLL_SYSCONFIG (0x10)
|
||||
#define OMAP_USBTLL_SYSCONFIG_CACTIVITY (1 << 8)
|
||||
#define OMAP_USBTLL_SYSCONFIG_SIDLEMODE (1 << 3)
|
||||
#define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP (1 << 2)
|
||||
#define OMAP_USBTLL_SYSCONFIG_SOFTRESET (1 << 1)
|
||||
#define OMAP_USBTLL_SYSCONFIG_AUTOIDLE (1 << 0)
|
||||
|
||||
#define OMAP_USBTLL_SYSSTATUS (0x14)
|
||||
#define OMAP_USBTLL_SYSSTATUS_RESETDONE (1 << 0)
|
||||
|
||||
#define OMAP_USBTLL_IRQSTATUS (0x18)
|
||||
#define OMAP_USBTLL_IRQENABLE (0x1C)
|
||||
|
||||
#define OMAP_TLL_SHARED_CONF (0x30)
|
||||
#define OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN (1 << 6)
|
||||
#define OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN (1 << 5)
|
||||
#define OMAP_TLL_SHARED_CONF_USB_DIVRATION (1 << 2)
|
||||
#define OMAP_TLL_SHARED_CONF_FCLK_REQ (1 << 1)
|
||||
#define OMAP_TLL_SHARED_CONF_FCLK_IS_ON (1 << 0)
|
||||
|
||||
#define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num)
|
||||
#define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT 24
|
||||
#define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11)
|
||||
#define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10)
|
||||
#define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9)
|
||||
#define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8)
|
||||
#define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS (1 << 1)
|
||||
#define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0)
|
||||
|
||||
#define OMAP_TLL_CHANNEL_COUNT 3
|
||||
|
||||
/* UHH Register Set */
|
||||
#define OMAP_UHH_REVISION (0x00)
|
||||
#define OMAP_UHH_SYSCONFIG (0x10)
|
||||
#define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12)
|
||||
#define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8)
|
||||
#define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3)
|
||||
#define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2)
|
||||
#define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1)
|
||||
#define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0)
|
||||
|
||||
#define OMAP_UHH_SYSSTATUS (0x14)
|
||||
#define OMAP_UHH_SYSSTATUS_UHHRESETDONE (1 << 0)
|
||||
#define OMAP_UHH_SYSSTATUS_OHCIRESETDONE (1 << 1)
|
||||
#define OMAP_UHH_SYSSTATUS_EHCIRESETDONE (1 << 2)
|
||||
#define OMAP_UHH_HOSTCONFIG (0x40)
|
||||
#define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS (1 << 0)
|
||||
#define OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS (1 << 0)
|
||||
#define OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS (1 << 11)
|
||||
#define OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS (1 << 12)
|
||||
#define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN (1 << 2)
|
||||
#define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN (1 << 3)
|
||||
#define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN (1 << 4)
|
||||
#define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN (1 << 5)
|
||||
#define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS (1 << 8)
|
||||
#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9)
|
||||
#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10)
|
||||
|
||||
#define OMAP_UHH_DEBUG_CSR (0x44)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static inline void ohci_omap_writel(void __iomem *base, u32 reg, u32 val)
|
||||
{
|
||||
__raw_writel(val, base + reg);
|
||||
}
|
||||
|
||||
static inline u32 ohci_omap_readl(void __iomem *base, u32 reg)
|
||||
{
|
||||
return __raw_readl(base + reg);
|
||||
}
|
||||
|
||||
static inline void ohci_omap_writeb(void __iomem *base, u8 reg, u8 val)
|
||||
{
|
||||
__raw_writeb(val, base + reg);
|
||||
}
|
||||
|
||||
static inline u8 ohci_omap_readb(void __iomem *base, u8 reg)
|
||||
{
|
||||
return __raw_readb(base + reg);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
struct ohci_hcd_omap3 {
|
||||
struct ohci_hcd *ohci;
|
||||
struct device *dev;
|
||||
|
||||
struct clk *usbhost_ick;
|
||||
struct clk *usbhost2_120m_fck;
|
||||
struct clk *usbhost1_48m_fck;
|
||||
struct clk *usbtll_fck;
|
||||
struct clk *usbtll_ick;
|
||||
|
||||
/* port_mode: TLL/PHY, 2/3/4/6-PIN, DP-DM/DAT-SE0 */
|
||||
enum ohci_omap3_port_mode port_mode[OMAP3_HS_USB_PORTS];
|
||||
void __iomem *uhh_base;
|
||||
void __iomem *tll_base;
|
||||
void __iomem *ohci_base;
|
||||
|
||||
unsigned es2_compatibility:1;
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void ohci_omap3_clock_power(struct ohci_hcd_omap3 *omap, int on)
|
||||
{
|
||||
if (on) {
|
||||
clk_enable(omap->usbtll_ick);
|
||||
clk_enable(omap->usbtll_fck);
|
||||
clk_enable(omap->usbhost_ick);
|
||||
clk_enable(omap->usbhost1_48m_fck);
|
||||
clk_enable(omap->usbhost2_120m_fck);
|
||||
} else {
|
||||
clk_disable(omap->usbhost2_120m_fck);
|
||||
clk_disable(omap->usbhost1_48m_fck);
|
||||
clk_disable(omap->usbhost_ick);
|
||||
clk_disable(omap->usbtll_fck);
|
||||
clk_disable(omap->usbtll_ick);
|
||||
}
|
||||
}
|
||||
|
||||
static int ohci_omap3_init(struct usb_hcd *hcd)
|
||||
{
|
||||
dev_dbg(hcd->self.controller, "starting OHCI controller\n");
|
||||
|
||||
return ohci_init(hcd_to_ohci(hcd));
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int ohci_omap3_start(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* RemoteWakeupConnected has to be set explicitly before
|
||||
* calling ohci_run. The reset value of RWC is 0.
|
||||
*/
|
||||
ohci->hc_control = OHCI_CTRL_RWC;
|
||||
writel(OHCI_CTRL_RWC, &ohci->regs->control);
|
||||
|
||||
ret = ohci_run(ohci);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(hcd->self.controller, "can't start\n");
|
||||
ohci_stop(hcd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* convert the port-mode enum to a value we can use in the FSLSMODE
|
||||
* field of USBTLL_CHANNEL_CONF
|
||||
*/
|
||||
static unsigned ohci_omap3_fslsmode(enum ohci_omap3_port_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case OMAP_OHCI_PORT_MODE_UNUSED:
|
||||
case OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0:
|
||||
return 0x0;
|
||||
|
||||
case OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM:
|
||||
return 0x1;
|
||||
|
||||
case OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0:
|
||||
return 0x2;
|
||||
|
||||
case OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM:
|
||||
return 0x3;
|
||||
|
||||
case OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0:
|
||||
return 0x4;
|
||||
|
||||
case OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM:
|
||||
return 0x5;
|
||||
|
||||
case OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0:
|
||||
return 0x6;
|
||||
|
||||
case OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM:
|
||||
return 0x7;
|
||||
|
||||
case OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0:
|
||||
return 0xA;
|
||||
|
||||
case OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM:
|
||||
return 0xB;
|
||||
default:
|
||||
pr_warning("Invalid port mode, using default\n");
|
||||
return 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ohci_omap3_tll_config(struct ohci_hcd_omap3 *omap)
|
||||
{
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
/* Program TLL SHARED CONF */
|
||||
reg = ohci_omap_readl(omap->tll_base, OMAP_TLL_SHARED_CONF);
|
||||
reg &= ~OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN;
|
||||
reg &= ~OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN;
|
||||
reg |= OMAP_TLL_SHARED_CONF_USB_DIVRATION;
|
||||
reg |= OMAP_TLL_SHARED_CONF_FCLK_IS_ON;
|
||||
ohci_omap_writel(omap->tll_base, OMAP_TLL_SHARED_CONF, reg);
|
||||
|
||||
/* Program each TLL channel */
|
||||
/*
|
||||
* REVISIT: Only the 3-pin and 4-pin PHY modes have
|
||||
* actually been tested.
|
||||
*/
|
||||
for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
|
||||
|
||||
/* Enable only those channels that are actually used */
|
||||
if (omap->port_mode[i] == OMAP_OHCI_PORT_MODE_UNUSED)
|
||||
continue;
|
||||
|
||||
reg = ohci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i));
|
||||
reg |= ohci_omap3_fslsmode(omap->port_mode[i])
|
||||
<< OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT;
|
||||
reg |= OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS;
|
||||
reg |= OMAP_TLL_CHANNEL_CONF_CHANEN;
|
||||
ohci_omap_writel(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i), reg);
|
||||
}
|
||||
}
|
||||
|
||||
/* omap3_start_ohci
|
||||
* - Start the TI USBHOST controller
|
||||
*/
|
||||
static int omap3_start_ohci(struct ohci_hcd_omap3 *omap, struct usb_hcd *hcd)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
||||
u32 reg = 0;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(omap->dev, "starting TI OHCI USB Controller\n");
|
||||
|
||||
/* Get all the clock handles we need */
|
||||
omap->usbhost_ick = clk_get(omap->dev, "usbhost_ick");
|
||||
if (IS_ERR(omap->usbhost_ick)) {
|
||||
dev_err(omap->dev, "could not get usbhost_ick\n");
|
||||
ret = PTR_ERR(omap->usbhost_ick);
|
||||
goto err_host_ick;
|
||||
}
|
||||
|
||||
omap->usbhost2_120m_fck = clk_get(omap->dev, "usbhost_120m_fck");
|
||||
if (IS_ERR(omap->usbhost2_120m_fck)) {
|
||||
dev_err(omap->dev, "could not get usbhost_120m_fck\n");
|
||||
ret = PTR_ERR(omap->usbhost2_120m_fck);
|
||||
goto err_host_120m_fck;
|
||||
}
|
||||
|
||||
omap->usbhost1_48m_fck = clk_get(omap->dev, "usbhost_48m_fck");
|
||||
if (IS_ERR(omap->usbhost1_48m_fck)) {
|
||||
dev_err(omap->dev, "could not get usbhost_48m_fck\n");
|
||||
ret = PTR_ERR(omap->usbhost1_48m_fck);
|
||||
goto err_host_48m_fck;
|
||||
}
|
||||
|
||||
omap->usbtll_fck = clk_get(omap->dev, "usbtll_fck");
|
||||
if (IS_ERR(omap->usbtll_fck)) {
|
||||
dev_err(omap->dev, "could not get usbtll_fck\n");
|
||||
ret = PTR_ERR(omap->usbtll_fck);
|
||||
goto err_tll_fck;
|
||||
}
|
||||
|
||||
omap->usbtll_ick = clk_get(omap->dev, "usbtll_ick");
|
||||
if (IS_ERR(omap->usbtll_ick)) {
|
||||
dev_err(omap->dev, "could not get usbtll_ick\n");
|
||||
ret = PTR_ERR(omap->usbtll_ick);
|
||||
goto err_tll_ick;
|
||||
}
|
||||
|
||||
/* Now enable all the clocks in the correct order */
|
||||
ohci_omap3_clock_power(omap, 1);
|
||||
|
||||
/* perform TLL soft reset, and wait until reset is complete */
|
||||
ohci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
|
||||
OMAP_USBTLL_SYSCONFIG_SOFTRESET);
|
||||
|
||||
/* Wait for TLL reset to complete */
|
||||
while (!(ohci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS)
|
||||
& OMAP_USBTLL_SYSSTATUS_RESETDONE)) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_dbg(omap->dev, "operation timed out\n");
|
||||
ret = -EINVAL;
|
||||
goto err_sys_status;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(omap->dev, "TLL reset done\n");
|
||||
|
||||
/* (1<<3) = no idle mode only for initial debugging */
|
||||
ohci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
|
||||
OMAP_USBTLL_SYSCONFIG_ENAWAKEUP |
|
||||
OMAP_USBTLL_SYSCONFIG_SIDLEMODE |
|
||||
OMAP_USBTLL_SYSCONFIG_CACTIVITY);
|
||||
|
||||
|
||||
/* Put UHH in NoIdle/NoStandby mode */
|
||||
reg = ohci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG);
|
||||
reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP
|
||||
| OMAP_UHH_SYSCONFIG_SIDLEMODE
|
||||
| OMAP_UHH_SYSCONFIG_CACTIVITY
|
||||
| OMAP_UHH_SYSCONFIG_MIDLEMODE);
|
||||
reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE;
|
||||
reg &= ~OMAP_UHH_SYSCONFIG_SOFTRESET;
|
||||
|
||||
ohci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg);
|
||||
|
||||
reg = ohci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
|
||||
|
||||
/* setup ULPI bypass and burst configurations */
|
||||
reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN
|
||||
| OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN
|
||||
| OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN);
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
|
||||
|
||||
/*
|
||||
* REVISIT: Pi_CONNECT_STATUS controls MStandby
|
||||
* assertion and Swakeup generation - let us not
|
||||
* worry about this for now. OMAP HWMOD framework
|
||||
* might take care of this later. If not, we can
|
||||
* update these registers when adding aggressive
|
||||
* clock management code.
|
||||
*
|
||||
* For now, turn off all the Pi_CONNECT_STATUS bits
|
||||
*
|
||||
if (omap->port_mode[0] == OMAP_OHCI_PORT_MODE_UNUSED)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
|
||||
if (omap->port_mode[1] == OMAP_OHCI_PORT_MODE_UNUSED)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
|
||||
if (omap->port_mode[2] == OMAP_OHCI_PORT_MODE_UNUSED)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
|
||||
*/
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
|
||||
|
||||
if (omap->es2_compatibility) {
|
||||
/*
|
||||
* All OHCI modes need to go through the TLL,
|
||||
* unlike in the EHCI case. So use UTMI mode
|
||||
* for all ports for OHCI, on ES2.x silicon
|
||||
*/
|
||||
dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n");
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
|
||||
} else {
|
||||
dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n");
|
||||
if (omap->port_mode[0] == OMAP_OHCI_PORT_MODE_UNUSED)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
|
||||
else
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
|
||||
|
||||
if (omap->port_mode[1] == OMAP_OHCI_PORT_MODE_UNUSED)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
|
||||
else
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
|
||||
|
||||
if (omap->port_mode[2] == OMAP_OHCI_PORT_MODE_UNUSED)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
|
||||
else
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
|
||||
|
||||
}
|
||||
ohci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
|
||||
dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
|
||||
|
||||
ohci_omap3_tll_config(omap);
|
||||
|
||||
return 0;
|
||||
|
||||
err_sys_status:
|
||||
ohci_omap3_clock_power(omap, 0);
|
||||
clk_put(omap->usbtll_ick);
|
||||
|
||||
err_tll_ick:
|
||||
clk_put(omap->usbtll_fck);
|
||||
|
||||
err_tll_fck:
|
||||
clk_put(omap->usbhost1_48m_fck);
|
||||
|
||||
err_host_48m_fck:
|
||||
clk_put(omap->usbhost2_120m_fck);
|
||||
|
||||
err_host_120m_fck:
|
||||
clk_put(omap->usbhost_ick);
|
||||
|
||||
err_host_ick:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void omap3_stop_ohci(struct ohci_hcd_omap3 *omap, struct usb_hcd *hcd)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(100);
|
||||
|
||||
dev_dbg(omap->dev, "stopping TI EHCI USB Controller\n");
|
||||
|
||||
/* Reset USBHOST for insmod/rmmod to work */
|
||||
ohci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG,
|
||||
OMAP_UHH_SYSCONFIG_SOFTRESET);
|
||||
while (!(ohci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
|
||||
& OMAP_UHH_SYSSTATUS_UHHRESETDONE)) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout))
|
||||
dev_dbg(omap->dev, "operation timed out\n");
|
||||
}
|
||||
|
||||
while (!(ohci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
|
||||
& OMAP_UHH_SYSSTATUS_OHCIRESETDONE)) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout))
|
||||
dev_dbg(omap->dev, "operation timed out\n");
|
||||
}
|
||||
|
||||
while (!(ohci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
|
||||
& OMAP_UHH_SYSSTATUS_EHCIRESETDONE)) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout))
|
||||
dev_dbg(omap->dev, "operation timed out\n");
|
||||
}
|
||||
|
||||
ohci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1));
|
||||
|
||||
while (!(ohci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS)
|
||||
& (1 << 0))) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout))
|
||||
dev_dbg(omap->dev, "operation timed out\n");
|
||||
}
|
||||
|
||||
ohci_omap3_clock_power(omap, 0);
|
||||
|
||||
if (omap->usbtll_fck != NULL) {
|
||||
clk_put(omap->usbtll_fck);
|
||||
omap->usbtll_fck = NULL;
|
||||
}
|
||||
|
||||
if (omap->usbhost_ick != NULL) {
|
||||
clk_put(omap->usbhost_ick);
|
||||
omap->usbhost_ick = NULL;
|
||||
}
|
||||
|
||||
if (omap->usbhost1_48m_fck != NULL) {
|
||||
clk_put(omap->usbhost1_48m_fck);
|
||||
omap->usbhost1_48m_fck = NULL;
|
||||
}
|
||||
|
||||
if (omap->usbhost2_120m_fck != NULL) {
|
||||
clk_put(omap->usbhost2_120m_fck);
|
||||
omap->usbhost2_120m_fck = NULL;
|
||||
}
|
||||
|
||||
if (omap->usbtll_ick != NULL) {
|
||||
clk_put(omap->usbtll_ick);
|
||||
omap->usbtll_ick = NULL;
|
||||
}
|
||||
|
||||
dev_dbg(omap->dev, "Clock to USB host has been disabled\n");
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static const struct hc_driver ohci_omap3_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "OMAP3 OHCI Host Controller",
|
||||
.hcd_priv_size = sizeof(struct ohci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ohci_irq,
|
||||
.flags = HCD_USB11 | HCD_MEMORY,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = ohci_omap3_init,
|
||||
.start = ohci_omap3_start,
|
||||
.stop = ohci_stop,
|
||||
.shutdown = ohci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ohci_urb_enqueue,
|
||||
.urb_dequeue = ohci_urb_dequeue,
|
||||
.endpoint_disable = ohci_endpoint_disable,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ohci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ohci_hub_status_data,
|
||||
.hub_control = ohci_hub_control,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* configure so an HC device and id are always provided
|
||||
* always called with process context; sleeping is OK
|
||||
*/
|
||||
|
||||
/**
|
||||
* ohci_hcd_omap3_probe - initialize OMAP-based HCDs
|
||||
*
|
||||
* Allocates basic resources for this USB host controller, and
|
||||
* then invokes the start() method for the HCD associated with it
|
||||
* through the hotplug entry's driver_data.
|
||||
*/
|
||||
static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ohci_hcd_omap_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct ohci_hcd_omap3 *omap;
|
||||
struct resource *res;
|
||||
struct usb_hcd *hcd;
|
||||
int ret = -ENODEV;
|
||||
int irq;
|
||||
|
||||
if (usb_disabled())
|
||||
goto err_disabled;
|
||||
|
||||
if (!pdata) {
|
||||
dev_dbg(&pdev->dev, "missing platform_data\n");
|
||||
goto err_pdata;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
||||
omap = kzalloc(sizeof(*omap), GFP_KERNEL);
|
||||
if (!omap) {
|
||||
ret = -ENOMEM;
|
||||
goto err_disabled;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(&ohci_omap3_hc_driver, &pdev->dev,
|
||||
dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
ret = -ENOMEM;
|
||||
goto err_create_hcd;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, omap);
|
||||
omap->dev = &pdev->dev;
|
||||
omap->port_mode[0] = pdata->port_mode[0];
|
||||
omap->port_mode[1] = pdata->port_mode[1];
|
||||
omap->port_mode[2] = pdata->port_mode[2];
|
||||
omap->es2_compatibility = pdata->es2_compatibility;
|
||||
omap->ohci = hcd_to_ohci(hcd);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
dev_err(&pdev->dev, "OHCI ioremap failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
omap->uhh_base = ioremap(res->start, resource_size(res));
|
||||
if (!omap->uhh_base) {
|
||||
dev_err(&pdev->dev, "UHH ioremap failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_uhh_ioremap;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
||||
omap->tll_base = ioremap(res->start, resource_size(res));
|
||||
if (!omap->tll_base) {
|
||||
dev_err(&pdev->dev, "TLL ioremap failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_tll_ioremap;
|
||||
}
|
||||
|
||||
ret = omap3_start_ohci(omap, hcd);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "failed to start ehci\n");
|
||||
goto err_start;
|
||||
}
|
||||
|
||||
ohci_hcd_init(omap->ohci);
|
||||
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret);
|
||||
goto err_add_hcd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_add_hcd:
|
||||
omap3_stop_ohci(omap, hcd);
|
||||
|
||||
err_start:
|
||||
iounmap(omap->tll_base);
|
||||
|
||||
err_tll_ioremap:
|
||||
iounmap(omap->uhh_base);
|
||||
|
||||
err_uhh_ioremap:
|
||||
iounmap(hcd->regs);
|
||||
|
||||
err_ioremap:
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
err_create_hcd:
|
||||
kfree(omap);
|
||||
err_pdata:
|
||||
err_disabled:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* may be called without controller electrically present
|
||||
* may be called with controller, bus, and devices active
|
||||
*/
|
||||
|
||||
/**
|
||||
* ohci_hcd_omap3_remove - shutdown processing for OHCI HCDs
|
||||
* @pdev: USB Host Controller being removed
|
||||
*
|
||||
* Reverses the effect of ohci_hcd_omap3_probe(), first invoking
|
||||
* the HCD's stop() method. It is always called from a thread
|
||||
* context, normally "rmmod", "apmd", or something similar.
|
||||
*/
|
||||
static int __devexit ohci_hcd_omap3_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ohci_hcd_omap3 *omap = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = ohci_to_hcd(omap->ohci);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
omap3_stop_ohci(omap, hcd);
|
||||
iounmap(hcd->regs);
|
||||
iounmap(omap->tll_base);
|
||||
iounmap(omap->uhh_base);
|
||||
usb_put_hcd(hcd);
|
||||
kfree(omap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ohci_hcd_omap3_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct ohci_hcd_omap3 *omap = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = ohci_to_hcd(omap->ohci);
|
||||
|
||||
if (hcd->driver->shutdown)
|
||||
hcd->driver->shutdown(hcd);
|
||||
}
|
||||
|
||||
static struct platform_driver ohci_hcd_omap3_driver = {
|
||||
.probe = ohci_hcd_omap3_probe,
|
||||
.remove = __devexit_p(ohci_hcd_omap3_remove),
|
||||
.shutdown = ohci_hcd_omap3_shutdown,
|
||||
.driver = {
|
||||
.name = "ohci-omap3",
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:ohci-omap3");
|
||||
MODULE_AUTHOR("Anand Gadiyar <[email protected]>");
|
||||
|
|
@ -34,12 +34,11 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "../core/hcd.h"
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
|
@ -3154,10 +3153,10 @@ static inline unsigned int oxu_port_speed(struct oxu_hcd *oxu,
|
|||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return 1 << USB_PORT_FEAT_LOWSPEED;
|
||||
return USB_PORT_STAT_LOW_SPEED;
|
||||
case 2:
|
||||
default:
|
||||
return 1 << USB_PORT_FEAT_HIGHSPEED;
|
||||
return USB_PORT_STAT_HIGH_SPEED;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3202,7 +3201,7 @@ static int oxu_hub_control(struct usb_hcd *hcd, u16 typeReq,
|
|||
* Even if OWNER is set, so the port is owned by the
|
||||
* companion controller, khubd needs to be able to clear
|
||||
* the port-change status bits (especially
|
||||
* USB_PORT_FEAT_C_CONNECTION).
|
||||
* USB_PORT_STAT_C_CONNECTION).
|
||||
*/
|
||||
|
||||
switch (wValue) {
|
||||
|
|
@ -3264,11 +3263,11 @@ static int oxu_hub_control(struct usb_hcd *hcd, u16 typeReq,
|
|||
|
||||
/* wPortChange bits */
|
||||
if (temp & PORT_CSC)
|
||||
status |= 1 << USB_PORT_FEAT_C_CONNECTION;
|
||||
status |= USB_PORT_STAT_C_CONNECTION << 16;
|
||||
if (temp & PORT_PEC)
|
||||
status |= 1 << USB_PORT_FEAT_C_ENABLE;
|
||||
status |= USB_PORT_STAT_C_ENABLE << 16;
|
||||
if ((temp & PORT_OCC) && !ignore_oc)
|
||||
status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
|
||||
status |= USB_PORT_STAT_C_OVERCURRENT << 16;
|
||||
|
||||
/* whoever resumes must GetPortStatus to complete it!! */
|
||||
if (temp & PORT_RESUME) {
|
||||
|
|
@ -3286,7 +3285,7 @@ static int oxu_hub_control(struct usb_hcd *hcd, u16 typeReq,
|
|||
/* resume completed? */
|
||||
else if (time_after_eq(jiffies,
|
||||
oxu->reset_done[wIndex])) {
|
||||
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
|
||||
status |= USB_PORT_STAT_C_SUSPEND << 16;
|
||||
oxu->reset_done[wIndex] = 0;
|
||||
|
||||
/* stop resume signaling */
|
||||
|
|
@ -3309,7 +3308,7 @@ static int oxu_hub_control(struct usb_hcd *hcd, u16 typeReq,
|
|||
if ((temp & PORT_RESET)
|
||||
&& time_after_eq(jiffies,
|
||||
oxu->reset_done[wIndex])) {
|
||||
status |= 1 << USB_PORT_FEAT_C_RESET;
|
||||
status |= USB_PORT_STAT_C_RESET << 16;
|
||||
oxu->reset_done[wIndex] = 0;
|
||||
|
||||
/* force reset to complete */
|
||||
|
|
@ -3348,20 +3347,20 @@ static int oxu_hub_control(struct usb_hcd *hcd, u16 typeReq,
|
|||
*/
|
||||
|
||||
if (temp & PORT_CONNECT) {
|
||||
status |= 1 << USB_PORT_FEAT_CONNECTION;
|
||||
status |= USB_PORT_STAT_CONNECTION;
|
||||
/* status may be from integrated TT */
|
||||
status |= oxu_port_speed(oxu, temp);
|
||||
}
|
||||
if (temp & PORT_PE)
|
||||
status |= 1 << USB_PORT_FEAT_ENABLE;
|
||||
status |= USB_PORT_STAT_ENABLE;
|
||||
if (temp & (PORT_SUSPEND|PORT_RESUME))
|
||||
status |= 1 << USB_PORT_FEAT_SUSPEND;
|
||||
status |= USB_PORT_STAT_SUSPEND;
|
||||
if (temp & PORT_OC)
|
||||
status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
|
||||
status |= USB_PORT_STAT_OVERCURRENT;
|
||||
if (temp & PORT_RESET)
|
||||
status |= 1 << USB_PORT_FEAT_RESET;
|
||||
status |= USB_PORT_STAT_RESET;
|
||||
if (temp & PORT_POWER)
|
||||
status |= 1 << USB_PORT_FEAT_POWER;
|
||||
status |= USB_PORT_STAT_POWER;
|
||||
|
||||
#ifndef OXU_VERBOSE_DEBUG
|
||||
if (status & ~0xffff) /* only if wPortChange is interesting */
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mm.h>
|
||||
|
|
@ -40,7 +41,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include "../core/hcd.h"
|
||||
#include "r8a66597.h"
|
||||
|
||||
MODULE_DESCRIPTION("R8A66597 USB Host Controller Driver");
|
||||
|
|
@ -1018,10 +1018,10 @@ static void start_root_hub_sampling(struct r8a66597 *r8a66597, int port,
|
|||
rh->old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST;
|
||||
rh->scount = R8A66597_MAX_SAMPLING;
|
||||
if (connect)
|
||||
rh->port |= 1 << USB_PORT_FEAT_CONNECTION;
|
||||
rh->port |= USB_PORT_STAT_CONNECTION;
|
||||
else
|
||||
rh->port &= ~(1 << USB_PORT_FEAT_CONNECTION);
|
||||
rh->port |= 1 << USB_PORT_FEAT_C_CONNECTION;
|
||||
rh->port &= ~USB_PORT_STAT_CONNECTION;
|
||||
rh->port |= USB_PORT_STAT_C_CONNECTION << 16;
|
||||
|
||||
r8a66597_root_hub_start_polling(r8a66597);
|
||||
}
|
||||
|
|
@ -1059,15 +1059,14 @@ static void r8a66597_usb_connect(struct r8a66597 *r8a66597, int port)
|
|||
u16 speed = get_rh_usb_speed(r8a66597, port);
|
||||
struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
|
||||
|
||||
rh->port &= ~((1 << USB_PORT_FEAT_HIGHSPEED) |
|
||||
(1 << USB_PORT_FEAT_LOWSPEED));
|
||||
rh->port &= ~(USB_PORT_STAT_HIGH_SPEED | USB_PORT_STAT_LOW_SPEED);
|
||||
if (speed == HSMODE)
|
||||
rh->port |= (1 << USB_PORT_FEAT_HIGHSPEED);
|
||||
rh->port |= USB_PORT_STAT_HIGH_SPEED;
|
||||
else if (speed == LSMODE)
|
||||
rh->port |= (1 << USB_PORT_FEAT_LOWSPEED);
|
||||
rh->port |= USB_PORT_STAT_LOW_SPEED;
|
||||
|
||||
rh->port &= ~(1 << USB_PORT_FEAT_RESET);
|
||||
rh->port |= 1 << USB_PORT_FEAT_ENABLE;
|
||||
rh->port &= USB_PORT_STAT_RESET;
|
||||
rh->port |= USB_PORT_STAT_ENABLE;
|
||||
}
|
||||
|
||||
/* this function must be called with interrupt disabled */
|
||||
|
|
@ -1706,7 +1705,7 @@ static void r8a66597_root_hub_control(struct r8a66597 *r8a66597, int port)
|
|||
u16 tmp;
|
||||
struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
|
||||
|
||||
if (rh->port & (1 << USB_PORT_FEAT_RESET)) {
|
||||
if (rh->port & USB_PORT_STAT_RESET) {
|
||||
unsigned long dvstctr_reg = get_dvstctr_reg(port);
|
||||
|
||||
tmp = r8a66597_read(r8a66597, dvstctr_reg);
|
||||
|
|
@ -1718,7 +1717,7 @@ static void r8a66597_root_hub_control(struct r8a66597 *r8a66597, int port)
|
|||
r8a66597_usb_connect(r8a66597, port);
|
||||
}
|
||||
|
||||
if (!(rh->port & (1 << USB_PORT_FEAT_CONNECTION))) {
|
||||
if (!(rh->port & USB_PORT_STAT_CONNECTION)) {
|
||||
r8a66597_write(r8a66597, ~ATTCH, get_intsts_reg(port));
|
||||
r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port));
|
||||
}
|
||||
|
|
@ -2186,7 +2185,7 @@ static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||
|
||||
switch (wValue) {
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
rh->port &= ~(1 << USB_PORT_FEAT_POWER);
|
||||
rh->port &= ~USB_PORT_STAT_POWER;
|
||||
break;
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
break;
|
||||
|
|
@ -2227,12 +2226,12 @@ static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||
break;
|
||||
case USB_PORT_FEAT_POWER:
|
||||
r8a66597_port_power(r8a66597, port, 1);
|
||||
rh->port |= (1 << USB_PORT_FEAT_POWER);
|
||||
rh->port |= USB_PORT_STAT_POWER;
|
||||
break;
|
||||
case USB_PORT_FEAT_RESET: {
|
||||
struct r8a66597_device *dev = rh->dev;
|
||||
|
||||
rh->port |= (1 << USB_PORT_FEAT_RESET);
|
||||
rh->port |= USB_PORT_STAT_RESET;
|
||||
|
||||
disable_r8a66597_pipe_all(r8a66597, dev);
|
||||
free_usb_address(r8a66597, dev, 1);
|
||||
|
|
@ -2270,12 +2269,12 @@ static int r8a66597_bus_suspend(struct usb_hcd *hcd)
|
|||
struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
|
||||
unsigned long dvstctr_reg = get_dvstctr_reg(port);
|
||||
|
||||
if (!(rh->port & (1 << USB_PORT_FEAT_ENABLE)))
|
||||
if (!(rh->port & USB_PORT_STAT_ENABLE))
|
||||
continue;
|
||||
|
||||
dbg("suspend port = %d", port);
|
||||
r8a66597_bclr(r8a66597, UACT, dvstctr_reg); /* suspend */
|
||||
rh->port |= 1 << USB_PORT_FEAT_SUSPEND;
|
||||
rh->port |= USB_PORT_STAT_SUSPEND;
|
||||
|
||||
if (rh->dev->udev->do_remote_wakeup) {
|
||||
msleep(3); /* waiting last SOF */
|
||||
|
|
@ -2301,12 +2300,12 @@ static int r8a66597_bus_resume(struct usb_hcd *hcd)
|
|||
struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
|
||||
unsigned long dvstctr_reg = get_dvstctr_reg(port);
|
||||
|
||||
if (!(rh->port & (1 << USB_PORT_FEAT_SUSPEND)))
|
||||
if (!(rh->port & USB_PORT_STAT_SUSPEND))
|
||||
continue;
|
||||
|
||||
dbg("resume port = %d", port);
|
||||
rh->port &= ~(1 << USB_PORT_FEAT_SUSPEND);
|
||||
rh->port |= 1 << USB_PORT_FEAT_C_SUSPEND;
|
||||
rh->port &= ~USB_PORT_STAT_SUSPEND;
|
||||
rh->port |= USB_PORT_STAT_C_SUSPEND < 16;
|
||||
r8a66597_mdfy(r8a66597, RESUME, RESUME | UACT, dvstctr_reg);
|
||||
msleep(50);
|
||||
r8a66597_mdfy(r8a66597, UACT, RESUME | UACT, dvstctr_reg);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/sl811.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
|
@ -53,7 +54,6 @@
|
|||
#include <asm/byteorder.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "../core/hcd.h"
|
||||
#include "sl811.h"
|
||||
|
||||
|
||||
|
|
@ -90,10 +90,10 @@ static void port_power(struct sl811 *sl811, int is_on)
|
|||
|
||||
/* hub is inactive unless the port is powered */
|
||||
if (is_on) {
|
||||
if (sl811->port1 & (1 << USB_PORT_FEAT_POWER))
|
||||
if (sl811->port1 & USB_PORT_STAT_POWER)
|
||||
return;
|
||||
|
||||
sl811->port1 = (1 << USB_PORT_FEAT_POWER);
|
||||
sl811->port1 = USB_PORT_STAT_POWER;
|
||||
sl811->irq_enable = SL11H_INTMASK_INSRMV;
|
||||
} else {
|
||||
sl811->port1 = 0;
|
||||
|
|
@ -407,7 +407,7 @@ static struct sl811h_ep *start(struct sl811 *sl811, u8 bank)
|
|||
|
||||
static inline void start_transfer(struct sl811 *sl811)
|
||||
{
|
||||
if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))
|
||||
if (sl811->port1 & USB_PORT_STAT_SUSPEND)
|
||||
return;
|
||||
if (sl811->active_a == NULL) {
|
||||
sl811->active_a = start(sl811, SL811_EP_A(SL811_HOST_BUF));
|
||||
|
|
@ -721,23 +721,23 @@ retry:
|
|||
* force the reset and make khubd clean up later.
|
||||
*/
|
||||
if (irqstat & SL11H_INTMASK_RD)
|
||||
sl811->port1 &= ~(1 << USB_PORT_FEAT_CONNECTION);
|
||||
sl811->port1 &= ~USB_PORT_STAT_CONNECTION;
|
||||
else
|
||||
sl811->port1 |= 1 << USB_PORT_FEAT_CONNECTION;
|
||||
sl811->port1 |= USB_PORT_STAT_CONNECTION;
|
||||
|
||||
sl811->port1 |= 1 << USB_PORT_FEAT_C_CONNECTION;
|
||||
sl811->port1 |= USB_PORT_STAT_C_CONNECTION << 16;
|
||||
|
||||
} else if (irqstat & SL11H_INTMASK_RD) {
|
||||
if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) {
|
||||
if (sl811->port1 & USB_PORT_STAT_SUSPEND) {
|
||||
DBG("wakeup\n");
|
||||
sl811->port1 |= 1 << USB_PORT_FEAT_C_SUSPEND;
|
||||
sl811->port1 |= USB_PORT_STAT_C_SUSPEND << 16;
|
||||
sl811->stat_wake++;
|
||||
} else
|
||||
irqstat &= ~SL11H_INTMASK_RD;
|
||||
}
|
||||
|
||||
if (irqstat) {
|
||||
if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
|
||||
if (sl811->port1 & USB_PORT_STAT_ENABLE)
|
||||
start_transfer(sl811);
|
||||
ret = IRQ_HANDLED;
|
||||
if (retries--)
|
||||
|
|
@ -819,7 +819,7 @@ static int sl811h_urb_enqueue(
|
|||
spin_lock_irqsave(&sl811->lock, flags);
|
||||
|
||||
/* don't submit to a dead or disabled port */
|
||||
if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
|
||||
if (!(sl811->port1 & USB_PORT_STAT_ENABLE)
|
||||
|| !HC_IS_RUNNING(hcd->state)) {
|
||||
retval = -ENODEV;
|
||||
kfree(ep);
|
||||
|
|
@ -1119,9 +1119,9 @@ sl811h_timer(unsigned long _sl811)
|
|||
unsigned long flags;
|
||||
u8 irqstat;
|
||||
u8 signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE;
|
||||
const u32 mask = (1 << USB_PORT_FEAT_CONNECTION)
|
||||
| (1 << USB_PORT_FEAT_ENABLE)
|
||||
| (1 << USB_PORT_FEAT_LOWSPEED);
|
||||
const u32 mask = USB_PORT_STAT_CONNECTION
|
||||
| USB_PORT_STAT_ENABLE
|
||||
| USB_PORT_STAT_LOW_SPEED;
|
||||
|
||||
spin_lock_irqsave(&sl811->lock, flags);
|
||||
|
||||
|
|
@ -1135,8 +1135,8 @@ sl811h_timer(unsigned long _sl811)
|
|||
switch (signaling) {
|
||||
case SL11H_CTL1MASK_SE0:
|
||||
DBG("end reset\n");
|
||||
sl811->port1 = (1 << USB_PORT_FEAT_C_RESET)
|
||||
| (1 << USB_PORT_FEAT_POWER);
|
||||
sl811->port1 = (USB_PORT_STAT_C_RESET << 16)
|
||||
| USB_PORT_STAT_POWER;
|
||||
sl811->ctrl1 = 0;
|
||||
/* don't wrongly ack RD */
|
||||
if (irqstat & SL11H_INTMASK_INSRMV)
|
||||
|
|
@ -1144,7 +1144,7 @@ sl811h_timer(unsigned long _sl811)
|
|||
break;
|
||||
case SL11H_CTL1MASK_K:
|
||||
DBG("end resume\n");
|
||||
sl811->port1 &= ~(1 << USB_PORT_FEAT_SUSPEND);
|
||||
sl811->port1 &= ~USB_PORT_STAT_SUSPEND;
|
||||
break;
|
||||
default:
|
||||
DBG("odd timer signaling: %02x\n", signaling);
|
||||
|
|
@ -1154,26 +1154,26 @@ sl811h_timer(unsigned long _sl811)
|
|||
|
||||
if (irqstat & SL11H_INTMASK_RD) {
|
||||
/* usbcore nukes all pending transactions on disconnect */
|
||||
if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION))
|
||||
sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)
|
||||
| (1 << USB_PORT_FEAT_C_ENABLE);
|
||||
if (sl811->port1 & USB_PORT_STAT_CONNECTION)
|
||||
sl811->port1 |= (USB_PORT_STAT_C_CONNECTION << 16)
|
||||
| (USB_PORT_STAT_C_ENABLE << 16);
|
||||
sl811->port1 &= ~mask;
|
||||
sl811->irq_enable = SL11H_INTMASK_INSRMV;
|
||||
} else {
|
||||
sl811->port1 |= mask;
|
||||
if (irqstat & SL11H_INTMASK_DP)
|
||||
sl811->port1 &= ~(1 << USB_PORT_FEAT_LOWSPEED);
|
||||
sl811->port1 &= ~USB_PORT_STAT_LOW_SPEED;
|
||||
sl811->irq_enable = SL11H_INTMASK_INSRMV | SL11H_INTMASK_RD;
|
||||
}
|
||||
|
||||
if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) {
|
||||
if (sl811->port1 & USB_PORT_STAT_CONNECTION) {
|
||||
u8 ctrl2 = SL811HS_CTL2_INIT;
|
||||
|
||||
sl811->irq_enable |= SL11H_INTMASK_DONE_A;
|
||||
#ifdef USE_B
|
||||
sl811->irq_enable |= SL11H_INTMASK_DONE_B;
|
||||
#endif
|
||||
if (sl811->port1 & (1 << USB_PORT_FEAT_LOWSPEED)) {
|
||||
if (sl811->port1 & USB_PORT_STAT_LOW_SPEED) {
|
||||
sl811->ctrl1 |= SL11H_CTL1MASK_LSPD;
|
||||
ctrl2 |= SL811HS_CTL2MASK_DSWAP;
|
||||
}
|
||||
|
|
@ -1233,7 +1233,7 @@ sl811h_hub_control(
|
|||
|
||||
switch (wValue) {
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
sl811->port1 &= (1 << USB_PORT_FEAT_POWER);
|
||||
sl811->port1 &= USB_PORT_STAT_POWER;
|
||||
sl811->ctrl1 = 0;
|
||||
sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
|
||||
sl811->irq_enable = SL11H_INTMASK_INSRMV;
|
||||
|
|
@ -1241,7 +1241,7 @@ sl811h_hub_control(
|
|||
sl811->irq_enable);
|
||||
break;
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
if (!(sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)))
|
||||
if (!(sl811->port1 & USB_PORT_STAT_SUSPEND))
|
||||
break;
|
||||
|
||||
/* 20 msec of resume/K signaling, other irqs blocked */
|
||||
|
|
@ -1290,9 +1290,9 @@ sl811h_hub_control(
|
|||
goto error;
|
||||
switch (wValue) {
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
if (sl811->port1 & (1 << USB_PORT_FEAT_RESET))
|
||||
if (sl811->port1 & USB_PORT_STAT_RESET)
|
||||
goto error;
|
||||
if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)))
|
||||
if (!(sl811->port1 & USB_PORT_STAT_ENABLE))
|
||||
goto error;
|
||||
|
||||
DBG("suspend...\n");
|
||||
|
|
@ -1303,9 +1303,9 @@ sl811h_hub_control(
|
|||
port_power(sl811, 1);
|
||||
break;
|
||||
case USB_PORT_FEAT_RESET:
|
||||
if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))
|
||||
if (sl811->port1 & USB_PORT_STAT_SUSPEND)
|
||||
goto error;
|
||||
if (!(sl811->port1 & (1 << USB_PORT_FEAT_POWER)))
|
||||
if (!(sl811->port1 & USB_PORT_STAT_POWER))
|
||||
break;
|
||||
|
||||
/* 50 msec of reset/SE0 signaling, irqs blocked */
|
||||
|
|
@ -1314,7 +1314,7 @@ sl811h_hub_control(
|
|||
sl811->irq_enable);
|
||||
sl811->ctrl1 = SL11H_CTL1MASK_SE0;
|
||||
sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
|
||||
sl811->port1 |= (1 << USB_PORT_FEAT_RESET);
|
||||
sl811->port1 |= USB_PORT_STAT_RESET;
|
||||
mod_timer(&sl811->timer, jiffies
|
||||
+ msecs_to_jiffies(50));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
|
|
@ -56,7 +57,6 @@
|
|||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include "../core/hcd.h"
|
||||
|
||||
/* FIXME ohci.h is ONLY for internal use by the OHCI driver.
|
||||
* If you're going to try stuff like this, you need to split
|
||||
|
|
@ -1446,9 +1446,9 @@ static void u132_hcd_endp_work_scheduler(struct work_struct *work)
|
|||
return;
|
||||
} else {
|
||||
int retval;
|
||||
u8 address = u132->addr[endp->usb_addr].address;
|
||||
struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
|
||||
endp->queue_next];
|
||||
address = u132->addr[endp->usb_addr].address;
|
||||
endp->active = 1;
|
||||
ring->curr_endp = endp;
|
||||
ring->in_use = 1;
|
||||
|
|
@ -3120,8 +3120,8 @@ static int __devinit u132_probe(struct platform_device *pdev)
|
|||
ftdi_elan_gone_away(pdev);
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
int retval = 0;
|
||||
struct u132 *u132 = hcd_to_u132(hcd);
|
||||
retval = 0;
|
||||
hcd->rsrc_start = 0;
|
||||
mutex_lock(&u132_module_lock);
|
||||
list_add_tail(&u132->u132_list, &u132_static_list);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <linux/dmapool.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
|
|
@ -46,7 +47,6 @@
|
|||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include "../core/hcd.h"
|
||||
#include "uhci-hcd.h"
|
||||
#include "pci-quirks.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ struct whc_dbg {
|
|||
struct dentry *pzl_f;
|
||||
};
|
||||
|
||||
void qset_print(struct seq_file *s, struct whc_qset *qset)
|
||||
static void qset_print(struct seq_file *s, struct whc_qset *qset)
|
||||
{
|
||||
static const char *qh_type[] = {
|
||||
"ctrl", "isoc", "bulk", "intr", "rsvd", "rsvd", "rsvd", "lpintr", };
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u
|
|||
|
||||
remaining = urb->transfer_buffer_length;
|
||||
|
||||
for_each_sg(urb->sg->sg, sg, urb->num_sgs, i) {
|
||||
for_each_sg(urb->sg, sg, urb->num_sgs, i) {
|
||||
dma_addr_t dma_addr;
|
||||
size_t dma_remaining;
|
||||
dma_addr_t sp, ep;
|
||||
|
|
@ -561,7 +561,7 @@ static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset,
|
|||
|
||||
remaining = urb->transfer_buffer_length;
|
||||
|
||||
for_each_sg(urb->sg->sg, sg, urb->sg->nents, i) {
|
||||
for_each_sg(urb->sg, sg, urb->num_sgs, i) {
|
||||
size_t len;
|
||||
size_t sg_remaining;
|
||||
void *orig;
|
||||
|
|
@ -646,7 +646,7 @@ int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
|
|||
wurb->urb = urb;
|
||||
INIT_WORK(&wurb->dequeue_work, urb_dequeue_work);
|
||||
|
||||
if (urb->sg) {
|
||||
if (urb->num_sgs) {
|
||||
ret = qset_add_urb_sg(whc, qset, urb, mem_flags);
|
||||
if (ret == -EINVAL) {
|
||||
qset_free_stds(qset, urb);
|
||||
|
|
|
|||
|
|
@ -364,6 +364,30 @@ void xhci_debug_ring(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
|||
xhci_debug_segment(xhci, seg);
|
||||
}
|
||||
|
||||
void xhci_dbg_ep_rings(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
struct xhci_virt_ep *ep)
|
||||
{
|
||||
int i;
|
||||
struct xhci_ring *ring;
|
||||
|
||||
if (ep->ep_state & EP_HAS_STREAMS) {
|
||||
for (i = 1; i < ep->stream_info->num_streams; i++) {
|
||||
ring = ep->stream_info->stream_rings[i];
|
||||
xhci_dbg(xhci, "Dev %d endpoint %d stream ID %d:\n",
|
||||
slot_id, ep_index, i);
|
||||
xhci_debug_segment(xhci, ring->deq_seg);
|
||||
}
|
||||
} else {
|
||||
ring = ep->ring;
|
||||
if (!ring)
|
||||
return;
|
||||
xhci_dbg(xhci, "Dev %d endpoint ring %d:\n",
|
||||
slot_id, ep_index);
|
||||
xhci_debug_segment(xhci, ring->deq_seg);
|
||||
}
|
||||
}
|
||||
|
||||
void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
|
||||
{
|
||||
u32 addr = (u32) erst->erst_dma_addr;
|
||||
|
|
|
|||
|
|
@ -64,15 +64,15 @@ static void xhci_hub_descriptor(struct xhci_hcd *xhci,
|
|||
static unsigned int xhci_port_speed(unsigned int port_status)
|
||||
{
|
||||
if (DEV_LOWSPEED(port_status))
|
||||
return 1 << USB_PORT_FEAT_LOWSPEED;
|
||||
return USB_PORT_STAT_LOW_SPEED;
|
||||
if (DEV_HIGHSPEED(port_status))
|
||||
return 1 << USB_PORT_FEAT_HIGHSPEED;
|
||||
return USB_PORT_STAT_HIGH_SPEED;
|
||||
if (DEV_SUPERSPEED(port_status))
|
||||
return 1 << USB_PORT_FEAT_SUPERSPEED;
|
||||
return USB_PORT_STAT_SUPER_SPEED;
|
||||
/*
|
||||
* FIXME: Yes, we should check for full speed, but the core uses that as
|
||||
* a default in portspeed() in usb/core/hub.c (which is the only place
|
||||
* USB_PORT_FEAT_*SPEED is used).
|
||||
* USB_PORT_STAT_*_SPEED is used).
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -205,27 +205,27 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||
|
||||
/* wPortChange bits */
|
||||
if (temp & PORT_CSC)
|
||||
status |= 1 << USB_PORT_FEAT_C_CONNECTION;
|
||||
status |= USB_PORT_STAT_C_CONNECTION << 16;
|
||||
if (temp & PORT_PEC)
|
||||
status |= 1 << USB_PORT_FEAT_C_ENABLE;
|
||||
status |= USB_PORT_STAT_C_ENABLE << 16;
|
||||
if ((temp & PORT_OCC))
|
||||
status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
|
||||
status |= USB_PORT_STAT_C_OVERCURRENT << 16;
|
||||
/*
|
||||
* FIXME ignoring suspend, reset, and USB 2.1/3.0 specific
|
||||
* changes
|
||||
*/
|
||||
if (temp & PORT_CONNECT) {
|
||||
status |= 1 << USB_PORT_FEAT_CONNECTION;
|
||||
status |= USB_PORT_STAT_CONNECTION;
|
||||
status |= xhci_port_speed(temp);
|
||||
}
|
||||
if (temp & PORT_PE)
|
||||
status |= 1 << USB_PORT_FEAT_ENABLE;
|
||||
status |= USB_PORT_STAT_ENABLE;
|
||||
if (temp & PORT_OC)
|
||||
status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
|
||||
status |= USB_PORT_STAT_OVERCURRENT;
|
||||
if (temp & PORT_RESET)
|
||||
status |= 1 << USB_PORT_FEAT_RESET;
|
||||
status |= USB_PORT_STAT_RESET;
|
||||
if (temp & PORT_POWER)
|
||||
status |= 1 << USB_PORT_FEAT_POWER;
|
||||
status |= USB_PORT_STAT_POWER;
|
||||
xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
|
||||
put_unaligned(cpu_to_le32(status), (__le32 *) buf);
|
||||
break;
|
||||
|
|
@ -298,7 +298,6 @@ error:
|
|||
* Returns 0 if the status hasn't changed, or the number of bytes in buf.
|
||||
* Ports are 0-indexed from the HCD point of view,
|
||||
* and 1-indexed from the USB core pointer of view.
|
||||
* xHCI instances can have up to 127 ports, so FIXME if you see more than 15.
|
||||
*
|
||||
* Note that the status change bits will be cleared as soon as a port status
|
||||
* change event is generated, so we use the saved status from that event.
|
||||
|
|
@ -315,14 +314,9 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
|
|||
ports = HCS_MAX_PORTS(xhci->hcs_params1);
|
||||
|
||||
/* Initial status is no changes */
|
||||
buf[0] = 0;
|
||||
retval = (ports + 8) / 8;
|
||||
memset(buf, 0, retval);
|
||||
status = 0;
|
||||
if (ports > 7) {
|
||||
buf[1] = 0;
|
||||
retval = 2;
|
||||
} else {
|
||||
retval = 1;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
/* For each port, did anything change? If so, set that bit in buf. */
|
||||
|
|
@ -331,10 +325,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
|
|||
NUM_PORT_REGS*i;
|
||||
temp = xhci_readl(xhci, addr);
|
||||
if (temp & (PORT_CSC | PORT_PEC | PORT_OCC)) {
|
||||
if (i < 7)
|
||||
buf[0] |= 1 << (i + 1);
|
||||
else
|
||||
buf[1] |= 1 << (i - 7);
|
||||
buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
|
||||
status = 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,13 +41,13 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, gfp_t flag
|
|||
|
||||
seg = kzalloc(sizeof *seg, flags);
|
||||
if (!seg)
|
||||
return 0;
|
||||
return NULL;
|
||||
xhci_dbg(xhci, "Allocating priv segment structure at %p\n", seg);
|
||||
|
||||
seg->trbs = dma_pool_alloc(xhci->segment_pool, flags, &dma);
|
||||
if (!seg->trbs) {
|
||||
kfree(seg);
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
xhci_dbg(xhci, "// Allocating segment at %p (virtual) 0x%llx (DMA)\n",
|
||||
seg->trbs, (unsigned long long)dma);
|
||||
|
|
@ -159,7 +159,7 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
|
|||
ring = kzalloc(sizeof *(ring), flags);
|
||||
xhci_dbg(xhci, "Allocating ring at %p\n", ring);
|
||||
if (!ring)
|
||||
return 0;
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&ring->td_list);
|
||||
if (num_segs == 0)
|
||||
|
|
@ -196,7 +196,7 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
|
|||
|
||||
fail:
|
||||
xhci_ring_free(xhci, ring);
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci,
|
||||
|
|
@ -247,7 +247,7 @@ static void xhci_reinit_cached_ring(struct xhci_hcd *xhci,
|
|||
|
||||
#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
|
||||
|
||||
struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
||||
static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
||||
int type, gfp_t flags)
|
||||
{
|
||||
struct xhci_container_ctx *ctx = kzalloc(sizeof(*ctx), flags);
|
||||
|
|
@ -265,7 +265,7 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
|||
return ctx;
|
||||
}
|
||||
|
||||
void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
||||
static void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
|
|
@ -304,6 +304,422 @@ struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci,
|
|||
(ctx->bytes + (ep_index * CTX_SIZE(xhci->hcc_params)));
|
||||
}
|
||||
|
||||
|
||||
/***************** Streams structures manipulation *************************/
|
||||
|
||||
void xhci_free_stream_ctx(struct xhci_hcd *xhci,
|
||||
unsigned int num_stream_ctxs,
|
||||
struct xhci_stream_ctx *stream_ctx, dma_addr_t dma)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
|
||||
|
||||
if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE)
|
||||
pci_free_consistent(pdev,
|
||||
sizeof(struct xhci_stream_ctx)*num_stream_ctxs,
|
||||
stream_ctx, dma);
|
||||
else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE)
|
||||
return dma_pool_free(xhci->small_streams_pool,
|
||||
stream_ctx, dma);
|
||||
else
|
||||
return dma_pool_free(xhci->medium_streams_pool,
|
||||
stream_ctx, dma);
|
||||
}
|
||||
|
||||
/*
|
||||
* The stream context array for each endpoint with bulk streams enabled can
|
||||
* vary in size, based on:
|
||||
* - how many streams the endpoint supports,
|
||||
* - the maximum primary stream array size the host controller supports,
|
||||
* - and how many streams the device driver asks for.
|
||||
*
|
||||
* The stream context array must be a power of 2, and can be as small as
|
||||
* 64 bytes or as large as 1MB.
|
||||
*/
|
||||
struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci,
|
||||
unsigned int num_stream_ctxs, dma_addr_t *dma,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
|
||||
|
||||
if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE)
|
||||
return pci_alloc_consistent(pdev,
|
||||
sizeof(struct xhci_stream_ctx)*num_stream_ctxs,
|
||||
dma);
|
||||
else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE)
|
||||
return dma_pool_alloc(xhci->small_streams_pool,
|
||||
mem_flags, dma);
|
||||
else
|
||||
return dma_pool_alloc(xhci->medium_streams_pool,
|
||||
mem_flags, dma);
|
||||
}
|
||||
|
||||
struct xhci_ring *xhci_dma_to_transfer_ring(
|
||||
struct xhci_virt_ep *ep,
|
||||
u64 address)
|
||||
{
|
||||
if (ep->ep_state & EP_HAS_STREAMS)
|
||||
return radix_tree_lookup(&ep->stream_info->trb_address_map,
|
||||
address >> SEGMENT_SHIFT);
|
||||
return ep->ring;
|
||||
}
|
||||
|
||||
/* Only use this when you know stream_info is valid */
|
||||
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
|
||||
static struct xhci_ring *dma_to_stream_ring(
|
||||
struct xhci_stream_info *stream_info,
|
||||
u64 address)
|
||||
{
|
||||
return radix_tree_lookup(&stream_info->trb_address_map,
|
||||
address >> SEGMENT_SHIFT);
|
||||
}
|
||||
#endif /* CONFIG_USB_XHCI_HCD_DEBUGGING */
|
||||
|
||||
struct xhci_ring *xhci_stream_id_to_ring(
|
||||
struct xhci_virt_device *dev,
|
||||
unsigned int ep_index,
|
||||
unsigned int stream_id)
|
||||
{
|
||||
struct xhci_virt_ep *ep = &dev->eps[ep_index];
|
||||
|
||||
if (stream_id == 0)
|
||||
return ep->ring;
|
||||
if (!ep->stream_info)
|
||||
return NULL;
|
||||
|
||||
if (stream_id > ep->stream_info->num_streams)
|
||||
return NULL;
|
||||
return ep->stream_info->stream_rings[stream_id];
|
||||
}
|
||||
|
||||
struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
unsigned int stream_id)
|
||||
{
|
||||
struct xhci_virt_ep *ep;
|
||||
|
||||
ep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
/* Common case: no streams */
|
||||
if (!(ep->ep_state & EP_HAS_STREAMS))
|
||||
return ep->ring;
|
||||
|
||||
if (stream_id == 0) {
|
||||
xhci_warn(xhci,
|
||||
"WARN: Slot ID %u, ep index %u has streams, "
|
||||
"but URB has no stream ID.\n",
|
||||
slot_id, ep_index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (stream_id < ep->stream_info->num_streams)
|
||||
return ep->stream_info->stream_rings[stream_id];
|
||||
|
||||
xhci_warn(xhci,
|
||||
"WARN: Slot ID %u, ep index %u has "
|
||||
"stream IDs 1 to %u allocated, "
|
||||
"but stream ID %u is requested.\n",
|
||||
slot_id, ep_index,
|
||||
ep->stream_info->num_streams - 1,
|
||||
stream_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the right ring for the given URB.
|
||||
* If the endpoint supports streams, boundary check the URB's stream ID.
|
||||
* If the endpoint doesn't support streams, return the singular endpoint ring.
|
||||
*/
|
||||
struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
struct urb *urb)
|
||||
{
|
||||
return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id,
|
||||
xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
|
||||
static int xhci_test_radix_tree(struct xhci_hcd *xhci,
|
||||
unsigned int num_streams,
|
||||
struct xhci_stream_info *stream_info)
|
||||
{
|
||||
u32 cur_stream;
|
||||
struct xhci_ring *cur_ring;
|
||||
u64 addr;
|
||||
|
||||
for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
|
||||
struct xhci_ring *mapped_ring;
|
||||
int trb_size = sizeof(union xhci_trb);
|
||||
|
||||
cur_ring = stream_info->stream_rings[cur_stream];
|
||||
for (addr = cur_ring->first_seg->dma;
|
||||
addr < cur_ring->first_seg->dma + SEGMENT_SIZE;
|
||||
addr += trb_size) {
|
||||
mapped_ring = dma_to_stream_ring(stream_info, addr);
|
||||
if (cur_ring != mapped_ring) {
|
||||
xhci_warn(xhci, "WARN: DMA address 0x%08llx "
|
||||
"didn't map to stream ID %u; "
|
||||
"mapped to ring %p\n",
|
||||
(unsigned long long) addr,
|
||||
cur_stream,
|
||||
mapped_ring);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
/* One TRB after the end of the ring segment shouldn't return a
|
||||
* pointer to the current ring (although it may be a part of a
|
||||
* different ring).
|
||||
*/
|
||||
mapped_ring = dma_to_stream_ring(stream_info, addr);
|
||||
if (mapped_ring != cur_ring) {
|
||||
/* One TRB before should also fail */
|
||||
addr = cur_ring->first_seg->dma - trb_size;
|
||||
mapped_ring = dma_to_stream_ring(stream_info, addr);
|
||||
}
|
||||
if (mapped_ring == cur_ring) {
|
||||
xhci_warn(xhci, "WARN: Bad DMA address 0x%08llx "
|
||||
"mapped to valid stream ID %u; "
|
||||
"mapped ring = %p\n",
|
||||
(unsigned long long) addr,
|
||||
cur_stream,
|
||||
mapped_ring);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_USB_XHCI_HCD_DEBUGGING */
|
||||
|
||||
/*
|
||||
* Change an endpoint's internal structure so it supports stream IDs. The
|
||||
* number of requested streams includes stream 0, which cannot be used by device
|
||||
* drivers.
|
||||
*
|
||||
* The number of stream contexts in the stream context array may be bigger than
|
||||
* the number of streams the driver wants to use. This is because the number of
|
||||
* stream context array entries must be a power of two.
|
||||
*
|
||||
* We need a radix tree for mapping physical addresses of TRBs to which stream
|
||||
* ID they belong to. We need to do this because the host controller won't tell
|
||||
* us which stream ring the TRB came from. We could store the stream ID in an
|
||||
* event data TRB, but that doesn't help us for the cancellation case, since the
|
||||
* endpoint may stop before it reaches that event data TRB.
|
||||
*
|
||||
* The radix tree maps the upper portion of the TRB DMA address to a ring
|
||||
* segment that has the same upper portion of DMA addresses. For example, say I
|
||||
* have segments of size 1KB, that are always 64-byte aligned. A segment may
|
||||
* start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the
|
||||
* key to the stream ID is 0x43244. I can use the DMA address of the TRB to
|
||||
* pass the radix tree a key to get the right stream ID:
|
||||
*
|
||||
* 0x10c90fff >> 10 = 0x43243
|
||||
* 0x10c912c0 >> 10 = 0x43244
|
||||
* 0x10c91400 >> 10 = 0x43245
|
||||
*
|
||||
* Obviously, only those TRBs with DMA addresses that are within the segment
|
||||
* will make the radix tree return the stream ID for that ring.
|
||||
*
|
||||
* Caveats for the radix tree:
|
||||
*
|
||||
* The radix tree uses an unsigned long as a key pair. On 32-bit systems, an
|
||||
* unsigned long will be 32-bits; on a 64-bit system an unsigned long will be
|
||||
* 64-bits. Since we only request 32-bit DMA addresses, we can use that as the
|
||||
* key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit
|
||||
* PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit
|
||||
* extended systems (where the DMA address can be bigger than 32-bits),
|
||||
* if we allow the PCI dma mask to be bigger than 32-bits. So don't do that.
|
||||
*/
|
||||
struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
|
||||
unsigned int num_stream_ctxs,
|
||||
unsigned int num_streams, gfp_t mem_flags)
|
||||
{
|
||||
struct xhci_stream_info *stream_info;
|
||||
u32 cur_stream;
|
||||
struct xhci_ring *cur_ring;
|
||||
unsigned long key;
|
||||
u64 addr;
|
||||
int ret;
|
||||
|
||||
xhci_dbg(xhci, "Allocating %u streams and %u "
|
||||
"stream context array entries.\n",
|
||||
num_streams, num_stream_ctxs);
|
||||
if (xhci->cmd_ring_reserved_trbs == MAX_RSVD_CMD_TRBS) {
|
||||
xhci_dbg(xhci, "Command ring has no reserved TRBs available\n");
|
||||
return NULL;
|
||||
}
|
||||
xhci->cmd_ring_reserved_trbs++;
|
||||
|
||||
stream_info = kzalloc(sizeof(struct xhci_stream_info), mem_flags);
|
||||
if (!stream_info)
|
||||
goto cleanup_trbs;
|
||||
|
||||
stream_info->num_streams = num_streams;
|
||||
stream_info->num_stream_ctxs = num_stream_ctxs;
|
||||
|
||||
/* Initialize the array of virtual pointers to stream rings. */
|
||||
stream_info->stream_rings = kzalloc(
|
||||
sizeof(struct xhci_ring *)*num_streams,
|
||||
mem_flags);
|
||||
if (!stream_info->stream_rings)
|
||||
goto cleanup_info;
|
||||
|
||||
/* Initialize the array of DMA addresses for stream rings for the HW. */
|
||||
stream_info->stream_ctx_array = xhci_alloc_stream_ctx(xhci,
|
||||
num_stream_ctxs, &stream_info->ctx_array_dma,
|
||||
mem_flags);
|
||||
if (!stream_info->stream_ctx_array)
|
||||
goto cleanup_ctx;
|
||||
memset(stream_info->stream_ctx_array, 0,
|
||||
sizeof(struct xhci_stream_ctx)*num_stream_ctxs);
|
||||
|
||||
/* Allocate everything needed to free the stream rings later */
|
||||
stream_info->free_streams_command =
|
||||
xhci_alloc_command(xhci, true, true, mem_flags);
|
||||
if (!stream_info->free_streams_command)
|
||||
goto cleanup_ctx;
|
||||
|
||||
INIT_RADIX_TREE(&stream_info->trb_address_map, GFP_ATOMIC);
|
||||
|
||||
/* Allocate rings for all the streams that the driver will use,
|
||||
* and add their segment DMA addresses to the radix tree.
|
||||
* Stream 0 is reserved.
|
||||
*/
|
||||
for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
|
||||
stream_info->stream_rings[cur_stream] =
|
||||
xhci_ring_alloc(xhci, 1, true, mem_flags);
|
||||
cur_ring = stream_info->stream_rings[cur_stream];
|
||||
if (!cur_ring)
|
||||
goto cleanup_rings;
|
||||
cur_ring->stream_id = cur_stream;
|
||||
/* Set deq ptr, cycle bit, and stream context type */
|
||||
addr = cur_ring->first_seg->dma |
|
||||
SCT_FOR_CTX(SCT_PRI_TR) |
|
||||
cur_ring->cycle_state;
|
||||
stream_info->stream_ctx_array[cur_stream].stream_ring = addr;
|
||||
xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n",
|
||||
cur_stream, (unsigned long long) addr);
|
||||
|
||||
key = (unsigned long)
|
||||
(cur_ring->first_seg->dma >> SEGMENT_SHIFT);
|
||||
ret = radix_tree_insert(&stream_info->trb_address_map,
|
||||
key, cur_ring);
|
||||
if (ret) {
|
||||
xhci_ring_free(xhci, cur_ring);
|
||||
stream_info->stream_rings[cur_stream] = NULL;
|
||||
goto cleanup_rings;
|
||||
}
|
||||
}
|
||||
/* Leave the other unused stream ring pointers in the stream context
|
||||
* array initialized to zero. This will cause the xHC to give us an
|
||||
* error if the device asks for a stream ID we don't have setup (if it
|
||||
* was any other way, the host controller would assume the ring is
|
||||
* "empty" and wait forever for data to be queued to that stream ID).
|
||||
*/
|
||||
#if XHCI_DEBUG
|
||||
/* Do a little test on the radix tree to make sure it returns the
|
||||
* correct values.
|
||||
*/
|
||||
if (xhci_test_radix_tree(xhci, num_streams, stream_info))
|
||||
goto cleanup_rings;
|
||||
#endif
|
||||
|
||||
return stream_info;
|
||||
|
||||
cleanup_rings:
|
||||
for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
|
||||
cur_ring = stream_info->stream_rings[cur_stream];
|
||||
if (cur_ring) {
|
||||
addr = cur_ring->first_seg->dma;
|
||||
radix_tree_delete(&stream_info->trb_address_map,
|
||||
addr >> SEGMENT_SHIFT);
|
||||
xhci_ring_free(xhci, cur_ring);
|
||||
stream_info->stream_rings[cur_stream] = NULL;
|
||||
}
|
||||
}
|
||||
xhci_free_command(xhci, stream_info->free_streams_command);
|
||||
cleanup_ctx:
|
||||
kfree(stream_info->stream_rings);
|
||||
cleanup_info:
|
||||
kfree(stream_info);
|
||||
cleanup_trbs:
|
||||
xhci->cmd_ring_reserved_trbs--;
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Sets the MaxPStreams field and the Linear Stream Array field.
|
||||
* Sets the dequeue pointer to the stream context array.
|
||||
*/
|
||||
void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_ep_ctx *ep_ctx,
|
||||
struct xhci_stream_info *stream_info)
|
||||
{
|
||||
u32 max_primary_streams;
|
||||
/* MaxPStreams is the number of stream context array entries, not the
|
||||
* number we're actually using. Must be in 2^(MaxPstreams + 1) format.
|
||||
* fls(0) = 0, fls(0x1) = 1, fls(0x10) = 2, fls(0x100) = 3, etc.
|
||||
*/
|
||||
max_primary_streams = fls(stream_info->num_stream_ctxs) - 2;
|
||||
xhci_dbg(xhci, "Setting number of stream ctx array entries to %u\n",
|
||||
1 << (max_primary_streams + 1));
|
||||
ep_ctx->ep_info &= ~EP_MAXPSTREAMS_MASK;
|
||||
ep_ctx->ep_info |= EP_MAXPSTREAMS(max_primary_streams);
|
||||
ep_ctx->ep_info |= EP_HAS_LSA;
|
||||
ep_ctx->deq = stream_info->ctx_array_dma;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the MaxPStreams field and the Linear Stream Array field to 0.
|
||||
* Reinstalls the "normal" endpoint ring (at its previous dequeue mark,
|
||||
* not at the beginning of the ring).
|
||||
*/
|
||||
void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_ep_ctx *ep_ctx,
|
||||
struct xhci_virt_ep *ep)
|
||||
{
|
||||
dma_addr_t addr;
|
||||
ep_ctx->ep_info &= ~EP_MAXPSTREAMS_MASK;
|
||||
ep_ctx->ep_info &= ~EP_HAS_LSA;
|
||||
addr = xhci_trb_virt_to_dma(ep->ring->deq_seg, ep->ring->dequeue);
|
||||
ep_ctx->deq = addr | ep->ring->cycle_state;
|
||||
}
|
||||
|
||||
/* Frees all stream contexts associated with the endpoint,
|
||||
*
|
||||
* Caller should fix the endpoint context streams fields.
|
||||
*/
|
||||
void xhci_free_stream_info(struct xhci_hcd *xhci,
|
||||
struct xhci_stream_info *stream_info)
|
||||
{
|
||||
int cur_stream;
|
||||
struct xhci_ring *cur_ring;
|
||||
dma_addr_t addr;
|
||||
|
||||
if (!stream_info)
|
||||
return;
|
||||
|
||||
for (cur_stream = 1; cur_stream < stream_info->num_streams;
|
||||
cur_stream++) {
|
||||
cur_ring = stream_info->stream_rings[cur_stream];
|
||||
if (cur_ring) {
|
||||
addr = cur_ring->first_seg->dma;
|
||||
radix_tree_delete(&stream_info->trb_address_map,
|
||||
addr >> SEGMENT_SHIFT);
|
||||
xhci_ring_free(xhci, cur_ring);
|
||||
stream_info->stream_rings[cur_stream] = NULL;
|
||||
}
|
||||
}
|
||||
xhci_free_command(xhci, stream_info->free_streams_command);
|
||||
xhci->cmd_ring_reserved_trbs--;
|
||||
if (stream_info->stream_ctx_array)
|
||||
xhci_free_stream_ctx(xhci,
|
||||
stream_info->num_stream_ctxs,
|
||||
stream_info->stream_ctx_array,
|
||||
stream_info->ctx_array_dma);
|
||||
|
||||
if (stream_info)
|
||||
kfree(stream_info->stream_rings);
|
||||
kfree(stream_info);
|
||||
}
|
||||
|
||||
|
||||
/***************** Device context manipulation *************************/
|
||||
|
||||
static void xhci_init_endpoint_timer(struct xhci_hcd *xhci,
|
||||
struct xhci_virt_ep *ep)
|
||||
{
|
||||
|
|
@ -328,9 +744,13 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
|
|||
if (!dev)
|
||||
return;
|
||||
|
||||
for (i = 0; i < 31; ++i)
|
||||
for (i = 0; i < 31; ++i) {
|
||||
if (dev->eps[i].ring)
|
||||
xhci_ring_free(xhci, dev->eps[i].ring);
|
||||
if (dev->eps[i].stream_info)
|
||||
xhci_free_stream_info(xhci,
|
||||
dev->eps[i].stream_info);
|
||||
}
|
||||
|
||||
if (dev->ring_cache) {
|
||||
for (i = 0; i < dev->num_rings_cached; i++)
|
||||
|
|
@ -344,7 +764,7 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
|
|||
xhci_free_container_ctx(xhci, dev->out_ctx);
|
||||
|
||||
kfree(xhci->devs[slot_id]);
|
||||
xhci->devs[slot_id] = 0;
|
||||
xhci->devs[slot_id] = NULL;
|
||||
}
|
||||
|
||||
int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
|
||||
|
|
@ -590,9 +1010,9 @@ static inline unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
|
|||
static inline u32 xhci_get_endpoint_mult(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep)
|
||||
{
|
||||
if (udev->speed != USB_SPEED_SUPER || !ep->ss_ep_comp)
|
||||
if (udev->speed != USB_SPEED_SUPER)
|
||||
return 0;
|
||||
return ep->ss_ep_comp->desc.bmAttributes;
|
||||
return ep->ss_ep_comp.bmAttributes;
|
||||
}
|
||||
|
||||
static inline u32 xhci_get_endpoint_type(struct usb_device *udev,
|
||||
|
|
@ -641,13 +1061,8 @@ static inline u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci,
|
|||
usb_endpoint_xfer_bulk(&ep->desc))
|
||||
return 0;
|
||||
|
||||
if (udev->speed == USB_SPEED_SUPER) {
|
||||
if (ep->ss_ep_comp)
|
||||
return ep->ss_ep_comp->desc.wBytesPerInterval;
|
||||
xhci_warn(xhci, "WARN no SS endpoint companion descriptor.\n");
|
||||
/* Assume no bursts, no multiple opportunities to send. */
|
||||
return ep->desc.wMaxPacketSize;
|
||||
}
|
||||
if (udev->speed == USB_SPEED_SUPER)
|
||||
return ep->ss_ep_comp.wBytesPerInterval;
|
||||
|
||||
max_packet = ep->desc.wMaxPacketSize & 0x3ff;
|
||||
max_burst = (ep->desc.wMaxPacketSize & 0x1800) >> 11;
|
||||
|
|
@ -655,6 +1070,9 @@ static inline u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci,
|
|||
return max_packet * (max_burst + 1);
|
||||
}
|
||||
|
||||
/* Set up an endpoint with one ring segment. Do not allocate stream rings.
|
||||
* Drivers will have to call usb_alloc_streams() to do that.
|
||||
*/
|
||||
int xhci_endpoint_init(struct xhci_hcd *xhci,
|
||||
struct xhci_virt_device *virt_dev,
|
||||
struct usb_device *udev,
|
||||
|
|
@ -708,12 +1126,9 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
|
|||
max_packet = ep->desc.wMaxPacketSize;
|
||||
ep_ctx->ep_info2 |= MAX_PACKET(max_packet);
|
||||
/* dig out max burst from ep companion desc */
|
||||
if (!ep->ss_ep_comp) {
|
||||
xhci_warn(xhci, "WARN no SS endpoint companion descriptor.\n");
|
||||
max_packet = 0;
|
||||
} else {
|
||||
max_packet = ep->ss_ep_comp->desc.bMaxBurst;
|
||||
}
|
||||
max_packet = ep->ss_ep_comp.bMaxBurst;
|
||||
if (!max_packet)
|
||||
xhci_warn(xhci, "WARN no SS endpoint bMaxBurst\n");
|
||||
ep_ctx->ep_info2 |= MAX_BURST(max_packet);
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
|
|
@ -1003,6 +1418,16 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
|||
xhci->device_pool = NULL;
|
||||
xhci_dbg(xhci, "Freed device context pool\n");
|
||||
|
||||
if (xhci->small_streams_pool)
|
||||
dma_pool_destroy(xhci->small_streams_pool);
|
||||
xhci->small_streams_pool = NULL;
|
||||
xhci_dbg(xhci, "Freed small stream array pool\n");
|
||||
|
||||
if (xhci->medium_streams_pool)
|
||||
dma_pool_destroy(xhci->medium_streams_pool);
|
||||
xhci->medium_streams_pool = NULL;
|
||||
xhci_dbg(xhci, "Freed medium stream array pool\n");
|
||||
|
||||
xhci_write_64(xhci, 0, &xhci->op_regs->dcbaa_ptr);
|
||||
if (xhci->dcbaa)
|
||||
pci_free_consistent(pdev, sizeof(*xhci->dcbaa),
|
||||
|
|
@ -1239,6 +1664,22 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
if (!xhci->segment_pool || !xhci->device_pool)
|
||||
goto fail;
|
||||
|
||||
/* Linear stream context arrays don't have any boundary restrictions,
|
||||
* and only need to be 16-byte aligned.
|
||||
*/
|
||||
xhci->small_streams_pool =
|
||||
dma_pool_create("xHCI 256 byte stream ctx arrays",
|
||||
dev, SMALL_STREAM_ARRAY_SIZE, 16, 0);
|
||||
xhci->medium_streams_pool =
|
||||
dma_pool_create("xHCI 1KB stream ctx arrays",
|
||||
dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0);
|
||||
/* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE
|
||||
* will be allocated with pci_alloc_consistent()
|
||||
*/
|
||||
|
||||
if (!xhci->small_streams_pool || !xhci->medium_streams_pool)
|
||||
goto fail;
|
||||
|
||||
/* Set up the command ring to have one segments for now. */
|
||||
xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, flags);
|
||||
if (!xhci->cmd_ring)
|
||||
|
|
@ -1330,7 +1771,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
*/
|
||||
init_completion(&xhci->addr_dev);
|
||||
for (i = 0; i < MAX_HC_SLOTS; ++i)
|
||||
xhci->devs[i] = 0;
|
||||
xhci->devs[i] = NULL;
|
||||
|
||||
if (scratchpad_alloc(xhci, flags))
|
||||
goto fail;
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
|
|||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
int retval;
|
||||
|
||||
hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 1;
|
||||
hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2;
|
||||
|
||||
xhci->cap_regs = hcd->regs;
|
||||
xhci->op_regs = hcd->regs +
|
||||
|
|
@ -132,6 +132,8 @@ static const struct hc_driver xhci_pci_hc_driver = {
|
|||
.urb_dequeue = xhci_urb_dequeue,
|
||||
.alloc_dev = xhci_alloc_dev,
|
||||
.free_dev = xhci_free_dev,
|
||||
.alloc_streams = xhci_alloc_streams,
|
||||
.free_streams = xhci_free_streams,
|
||||
.add_endpoint = xhci_add_endpoint,
|
||||
.drop_endpoint = xhci_drop_endpoint,
|
||||
.endpoint_reset = xhci_endpoint_reset,
|
||||
|
|
@ -175,12 +177,12 @@ static struct pci_driver xhci_pci_driver = {
|
|||
.shutdown = usb_hcd_pci_shutdown,
|
||||
};
|
||||
|
||||
int xhci_register_pci()
|
||||
int xhci_register_pci(void)
|
||||
{
|
||||
return pci_register_driver(&xhci_pci_driver);
|
||||
}
|
||||
|
||||
void xhci_unregister_pci()
|
||||
void xhci_unregister_pci(void)
|
||||
{
|
||||
pci_unregister_driver(&xhci_pci_driver);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,12 @@ static inline int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
|||
return (trb->link.control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK);
|
||||
}
|
||||
|
||||
static inline int enqueue_is_link_trb(struct xhci_ring *ring)
|
||||
{
|
||||
struct xhci_link_trb *link = &ring->enqueue->link;
|
||||
return ((link->control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK));
|
||||
}
|
||||
|
||||
/* Updates trb to point to the next TRB in the ring, and updates seg if the next
|
||||
* TRB is in a new segment. This does not skip over link TRBs, and it does not
|
||||
* effect the ring dequeue or enqueue pointers.
|
||||
|
|
@ -193,20 +199,15 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer
|
|||
while (last_trb(xhci, ring, ring->enq_seg, next)) {
|
||||
if (!consumer) {
|
||||
if (ring != xhci->event_ring) {
|
||||
/* If we're not dealing with 0.95 hardware,
|
||||
* carry over the chain bit of the previous TRB
|
||||
* (which may mean the chain bit is cleared).
|
||||
*/
|
||||
if (!xhci_link_trb_quirk(xhci)) {
|
||||
next->link.control &= ~TRB_CHAIN;
|
||||
next->link.control |= chain;
|
||||
if (chain) {
|
||||
next->link.control |= TRB_CHAIN;
|
||||
|
||||
/* Give this link TRB to the hardware */
|
||||
wmb();
|
||||
next->link.control ^= TRB_CYCLE;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
/* Give this link TRB to the hardware */
|
||||
wmb();
|
||||
if (next->link.control & TRB_CYCLE)
|
||||
next->link.control &= (u32) ~TRB_CYCLE;
|
||||
else
|
||||
next->link.control |= (u32) TRB_CYCLE;
|
||||
}
|
||||
/* Toggle the cycle bit after the last ring segment. */
|
||||
if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
|
||||
|
|
@ -242,10 +243,34 @@ static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
|||
int i;
|
||||
union xhci_trb *enq = ring->enqueue;
|
||||
struct xhci_segment *enq_seg = ring->enq_seg;
|
||||
struct xhci_segment *cur_seg;
|
||||
unsigned int left_on_ring;
|
||||
|
||||
/* If we are currently pointing to a link TRB, advance the
|
||||
* enqueue pointer before checking for space */
|
||||
while (last_trb(xhci, ring, enq_seg, enq)) {
|
||||
enq_seg = enq_seg->next;
|
||||
enq = enq_seg->trbs;
|
||||
}
|
||||
|
||||
/* Check if ring is empty */
|
||||
if (enq == ring->dequeue)
|
||||
if (enq == ring->dequeue) {
|
||||
/* Can't use link trbs */
|
||||
left_on_ring = TRBS_PER_SEGMENT - 1;
|
||||
for (cur_seg = enq_seg->next; cur_seg != enq_seg;
|
||||
cur_seg = cur_seg->next)
|
||||
left_on_ring += TRBS_PER_SEGMENT - 1;
|
||||
|
||||
/* Always need one TRB free in the ring. */
|
||||
left_on_ring -= 1;
|
||||
if (num_trbs > left_on_ring) {
|
||||
xhci_warn(xhci, "Not enough room on ring; "
|
||||
"need %u TRBs, %u TRBs left\n",
|
||||
num_trbs, left_on_ring);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/* Make sure there's an extra empty TRB available */
|
||||
for (i = 0; i <= num_trbs; ++i) {
|
||||
if (enq == ring->dequeue)
|
||||
|
|
@ -295,7 +320,8 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
|
|||
|
||||
static void ring_ep_doorbell(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id,
|
||||
unsigned int ep_index)
|
||||
unsigned int ep_index,
|
||||
unsigned int stream_id)
|
||||
{
|
||||
struct xhci_virt_ep *ep;
|
||||
unsigned int ep_state;
|
||||
|
|
@ -306,11 +332,16 @@ static void ring_ep_doorbell(struct xhci_hcd *xhci,
|
|||
ep_state = ep->ep_state;
|
||||
/* Don't ring the doorbell for this endpoint if there are pending
|
||||
* cancellations because the we don't want to interrupt processing.
|
||||
* We don't want to restart any stream rings if there's a set dequeue
|
||||
* pointer command pending because the device can choose to start any
|
||||
* stream once the endpoint is on the HW schedule.
|
||||
* FIXME - check all the stream rings for pending cancellations.
|
||||
*/
|
||||
if (!(ep_state & EP_HALT_PENDING) && !(ep_state & SET_DEQ_PENDING)
|
||||
&& !(ep_state & EP_HALTED)) {
|
||||
field = xhci_readl(xhci, db_addr) & DB_MASK;
|
||||
xhci_writel(xhci, field | EPI_TO_DB(ep_index), db_addr);
|
||||
field |= EPI_TO_DB(ep_index) | STREAM_ID_TO_DB(stream_id);
|
||||
xhci_writel(xhci, field, db_addr);
|
||||
/* Flush PCI posted writes - FIXME Matthew Wilcox says this
|
||||
* isn't time-critical and we shouldn't make the CPU wait for
|
||||
* the flush.
|
||||
|
|
@ -319,6 +350,31 @@ static void ring_ep_doorbell(struct xhci_hcd *xhci,
|
|||
}
|
||||
}
|
||||
|
||||
/* Ring the doorbell for any rings with pending URBs */
|
||||
static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id,
|
||||
unsigned int ep_index)
|
||||
{
|
||||
unsigned int stream_id;
|
||||
struct xhci_virt_ep *ep;
|
||||
|
||||
ep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
|
||||
/* A ring has pending URBs if its TD list is not empty */
|
||||
if (!(ep->ep_state & EP_HAS_STREAMS)) {
|
||||
if (!(list_empty(&ep->ring->td_list)))
|
||||
ring_ep_doorbell(xhci, slot_id, ep_index, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
for (stream_id = 1; stream_id < ep->stream_info->num_streams;
|
||||
stream_id++) {
|
||||
struct xhci_stream_info *stream_info = ep->stream_info;
|
||||
if (!list_empty(&stream_info->stream_rings[stream_id]->td_list))
|
||||
ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the segment that trb is in. Start searching in start_seg.
|
||||
* If we must move past a segment that has a link TRB with a toggle cycle state
|
||||
|
|
@ -334,13 +390,14 @@ static struct xhci_segment *find_trb_seg(
|
|||
while (cur_seg->trbs > trb ||
|
||||
&cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) {
|
||||
generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic;
|
||||
if (TRB_TYPE(generic_trb->field[3]) == TRB_LINK &&
|
||||
if ((generic_trb->field[3] & TRB_TYPE_BITMASK) ==
|
||||
TRB_TYPE(TRB_LINK) &&
|
||||
(generic_trb->field[3] & LINK_TOGGLE))
|
||||
*cycle_state = ~(*cycle_state) & 0x1;
|
||||
cur_seg = cur_seg->next;
|
||||
if (cur_seg == start_seg)
|
||||
/* Looped over the entire list. Oops! */
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
return cur_seg;
|
||||
}
|
||||
|
|
@ -361,14 +418,23 @@ static struct xhci_segment *find_trb_seg(
|
|||
*/
|
||||
void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
struct xhci_td *cur_td, struct xhci_dequeue_state *state)
|
||||
unsigned int stream_id, struct xhci_td *cur_td,
|
||||
struct xhci_dequeue_state *state)
|
||||
{
|
||||
struct xhci_virt_device *dev = xhci->devs[slot_id];
|
||||
struct xhci_ring *ep_ring = dev->eps[ep_index].ring;
|
||||
struct xhci_ring *ep_ring;
|
||||
struct xhci_generic_trb *trb;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
dma_addr_t addr;
|
||||
|
||||
ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id,
|
||||
ep_index, stream_id);
|
||||
if (!ep_ring) {
|
||||
xhci_warn(xhci, "WARN can't find new dequeue state "
|
||||
"for invalid stream ID %u.\n",
|
||||
stream_id);
|
||||
return;
|
||||
}
|
||||
state->new_cycle_state = 0;
|
||||
xhci_dbg(xhci, "Finding segment containing stopped TRB.\n");
|
||||
state->new_deq_seg = find_trb_seg(cur_td->start_seg,
|
||||
|
|
@ -390,7 +456,7 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
|
|||
BUG();
|
||||
|
||||
trb = &state->new_deq_ptr->generic;
|
||||
if (TRB_TYPE(trb->field[3]) == TRB_LINK &&
|
||||
if ((trb->field[3] & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK) &&
|
||||
(trb->field[3] & LINK_TOGGLE))
|
||||
state->new_cycle_state = ~(state->new_cycle_state) & 0x1;
|
||||
next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr);
|
||||
|
|
@ -448,11 +514,13 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
|
|||
}
|
||||
|
||||
static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
|
||||
unsigned int ep_index, struct xhci_segment *deq_seg,
|
||||
unsigned int ep_index, unsigned int stream_id,
|
||||
struct xhci_segment *deq_seg,
|
||||
union xhci_trb *deq_ptr, u32 cycle_state);
|
||||
|
||||
void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
unsigned int stream_id,
|
||||
struct xhci_dequeue_state *deq_state)
|
||||
{
|
||||
struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
|
|
@ -464,7 +532,7 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
|
|||
deq_state->new_deq_ptr,
|
||||
(unsigned long long)xhci_trb_virt_to_dma(deq_state->new_deq_seg, deq_state->new_deq_ptr),
|
||||
deq_state->new_cycle_state);
|
||||
queue_set_tr_deq(xhci, slot_id, ep_index,
|
||||
queue_set_tr_deq(xhci, slot_id, ep_index, stream_id,
|
||||
deq_state->new_deq_seg,
|
||||
deq_state->new_deq_ptr,
|
||||
(u32) deq_state->new_cycle_state);
|
||||
|
|
@ -523,7 +591,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
|
|||
struct xhci_ring *ep_ring;
|
||||
struct xhci_virt_ep *ep;
|
||||
struct list_head *entry;
|
||||
struct xhci_td *cur_td = 0;
|
||||
struct xhci_td *cur_td = NULL;
|
||||
struct xhci_td *last_unlinked_td;
|
||||
|
||||
struct xhci_dequeue_state deq_state;
|
||||
|
|
@ -532,11 +600,10 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
|
|||
slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]);
|
||||
ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]);
|
||||
ep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
ep_ring = ep->ring;
|
||||
|
||||
if (list_empty(&ep->cancelled_td_list)) {
|
||||
xhci_stop_watchdog_timer_in_irq(xhci, ep);
|
||||
ring_ep_doorbell(xhci, slot_id, ep_index);
|
||||
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -550,15 +617,36 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
|
|||
xhci_dbg(xhci, "Cancelling TD starting at %p, 0x%llx (dma).\n",
|
||||
cur_td->first_trb,
|
||||
(unsigned long long)xhci_trb_virt_to_dma(cur_td->start_seg, cur_td->first_trb));
|
||||
ep_ring = xhci_urb_to_transfer_ring(xhci, cur_td->urb);
|
||||
if (!ep_ring) {
|
||||
/* This shouldn't happen unless a driver is mucking
|
||||
* with the stream ID after submission. This will
|
||||
* leave the TD on the hardware ring, and the hardware
|
||||
* will try to execute it, and may access a buffer
|
||||
* that has already been freed. In the best case, the
|
||||
* hardware will execute it, and the event handler will
|
||||
* ignore the completion event for that TD, since it was
|
||||
* removed from the td_list for that endpoint. In
|
||||
* short, don't muck with the stream ID after
|
||||
* submission.
|
||||
*/
|
||||
xhci_warn(xhci, "WARN Cancelled URB %p "
|
||||
"has invalid stream ID %u.\n",
|
||||
cur_td->urb,
|
||||
cur_td->urb->stream_id);
|
||||
goto remove_finished_td;
|
||||
}
|
||||
/*
|
||||
* If we stopped on the TD we need to cancel, then we have to
|
||||
* move the xHC endpoint ring dequeue pointer past this TD.
|
||||
*/
|
||||
if (cur_td == ep->stopped_td)
|
||||
xhci_find_new_dequeue_state(xhci, slot_id, ep_index, cur_td,
|
||||
&deq_state);
|
||||
xhci_find_new_dequeue_state(xhci, slot_id, ep_index,
|
||||
cur_td->urb->stream_id,
|
||||
cur_td, &deq_state);
|
||||
else
|
||||
td_to_noop(xhci, ep_ring, cur_td);
|
||||
remove_finished_td:
|
||||
/*
|
||||
* The event handler won't see a completion for this TD anymore,
|
||||
* so remove it from the endpoint ring's TD list. Keep it in
|
||||
|
|
@ -572,12 +660,16 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
|
|||
/* If necessary, queue a Set Transfer Ring Dequeue Pointer command */
|
||||
if (deq_state.new_deq_ptr && deq_state.new_deq_seg) {
|
||||
xhci_queue_new_dequeue_state(xhci,
|
||||
slot_id, ep_index, &deq_state);
|
||||
slot_id, ep_index,
|
||||
ep->stopped_td->urb->stream_id,
|
||||
&deq_state);
|
||||
xhci_ring_cmd_db(xhci);
|
||||
} else {
|
||||
/* Otherwise just ring the doorbell to restart the ring */
|
||||
ring_ep_doorbell(xhci, slot_id, ep_index);
|
||||
/* Otherwise ring the doorbell(s) to restart queued transfers */
|
||||
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||||
}
|
||||
ep->stopped_td = NULL;
|
||||
ep->stopped_trb = NULL;
|
||||
|
||||
/*
|
||||
* Drop the lock and complete the URBs in the cancelled TD list.
|
||||
|
|
@ -734,6 +826,7 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
|
|||
{
|
||||
unsigned int slot_id;
|
||||
unsigned int ep_index;
|
||||
unsigned int stream_id;
|
||||
struct xhci_ring *ep_ring;
|
||||
struct xhci_virt_device *dev;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
|
|
@ -741,8 +834,19 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
|
|||
|
||||
slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]);
|
||||
ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]);
|
||||
stream_id = TRB_TO_STREAM_ID(trb->generic.field[2]);
|
||||
dev = xhci->devs[slot_id];
|
||||
ep_ring = dev->eps[ep_index].ring;
|
||||
|
||||
ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id);
|
||||
if (!ep_ring) {
|
||||
xhci_warn(xhci, "WARN Set TR deq ptr command for "
|
||||
"freed stream ID %u\n",
|
||||
stream_id);
|
||||
/* XXX: Harmless??? */
|
||||
dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING;
|
||||
return;
|
||||
}
|
||||
|
||||
ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
|
||||
slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx);
|
||||
|
||||
|
|
@ -787,7 +891,8 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
|
|||
}
|
||||
|
||||
dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING;
|
||||
ring_ep_doorbell(xhci, slot_id, ep_index);
|
||||
/* Restart any rings with pending URBs */
|
||||
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||||
}
|
||||
|
||||
static void handle_reset_ep_completion(struct xhci_hcd *xhci,
|
||||
|
|
@ -796,11 +901,9 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci,
|
|||
{
|
||||
int slot_id;
|
||||
unsigned int ep_index;
|
||||
struct xhci_ring *ep_ring;
|
||||
|
||||
slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]);
|
||||
ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]);
|
||||
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
|
||||
/* This command will only fail if the endpoint wasn't halted,
|
||||
* but we don't care.
|
||||
*/
|
||||
|
|
@ -818,9 +921,9 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci,
|
|||
false);
|
||||
xhci_ring_cmd_db(xhci);
|
||||
} else {
|
||||
/* Clear our internal halted state and restart the ring */
|
||||
/* Clear our internal halted state and restart the ring(s) */
|
||||
xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
|
||||
ring_ep_doorbell(xhci, slot_id, ep_index);
|
||||
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -897,16 +1000,19 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
|
|||
* Configure endpoint commands can come from the USB core
|
||||
* configuration or alt setting changes, or because the HW
|
||||
* needed an extra configure endpoint command after a reset
|
||||
* endpoint command. In the latter case, the xHCI driver is
|
||||
* not waiting on the configure endpoint command.
|
||||
* endpoint command or streams were being configured.
|
||||
* If the command was for a halted endpoint, the xHCI driver
|
||||
* is not waiting on the configure endpoint command.
|
||||
*/
|
||||
ctrl_ctx = xhci_get_input_control_ctx(xhci,
|
||||
virt_dev->in_ctx);
|
||||
/* Input ctx add_flags are the endpoint index plus one */
|
||||
ep_index = xhci_last_valid_endpoint(ctrl_ctx->add_flags) - 1;
|
||||
/* A usb_set_interface() call directly after clearing a halted
|
||||
* condition may race on this quirky hardware.
|
||||
* Not worth worrying about, since this is prototype hardware.
|
||||
* condition may race on this quirky hardware. Not worth
|
||||
* worrying about, since this is prototype hardware. Not sure
|
||||
* if this will work for streams, but streams support was
|
||||
* untested on this prototype.
|
||||
*/
|
||||
if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
|
||||
ep_index != (unsigned int) -1 &&
|
||||
|
|
@ -919,10 +1025,10 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
|
|||
xhci_dbg(xhci, "Completed config ep cmd - "
|
||||
"last ep index = %d, state = %d\n",
|
||||
ep_index, ep_state);
|
||||
/* Clear our internal halted state and restart ring */
|
||||
/* Clear internal halted state and restart ring(s) */
|
||||
xhci->devs[slot_id]->eps[ep_index].ep_state &=
|
||||
~EP_HALTED;
|
||||
ring_ep_doorbell(xhci, slot_id, ep_index);
|
||||
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||||
break;
|
||||
}
|
||||
bandwidth_change:
|
||||
|
|
@ -1018,7 +1124,7 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
|
|||
|
||||
do {
|
||||
if (start_dma == 0)
|
||||
return 0;
|
||||
return NULL;
|
||||
/* We may get an event for a Link TRB in the middle of a TD */
|
||||
end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
|
||||
&cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
|
||||
|
|
@ -1040,7 +1146,7 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
|
|||
suspect_dma <= end_trb_dma))
|
||||
return cur_seg;
|
||||
}
|
||||
return 0;
|
||||
return NULL;
|
||||
} else {
|
||||
/* Might still be somewhere in this segment */
|
||||
if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma)
|
||||
|
|
@ -1050,19 +1156,27 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
|
|||
start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
|
||||
} while (cur_seg != start_seg);
|
||||
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
unsigned int stream_id,
|
||||
struct xhci_td *td, union xhci_trb *event_trb)
|
||||
{
|
||||
struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
ep->ep_state |= EP_HALTED;
|
||||
ep->stopped_td = td;
|
||||
ep->stopped_trb = event_trb;
|
||||
ep->stopped_stream = stream_id;
|
||||
|
||||
xhci_queue_reset_ep(xhci, slot_id, ep_index);
|
||||
xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index);
|
||||
|
||||
ep->stopped_td = NULL;
|
||||
ep->stopped_trb = NULL;
|
||||
ep->stopped_stream = 0;
|
||||
|
||||
xhci_ring_cmd_db(xhci);
|
||||
}
|
||||
|
||||
|
|
@ -1119,11 +1233,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||
struct xhci_ring *ep_ring;
|
||||
unsigned int slot_id;
|
||||
int ep_index;
|
||||
struct xhci_td *td = 0;
|
||||
struct xhci_td *td = NULL;
|
||||
dma_addr_t event_dma;
|
||||
struct xhci_segment *event_seg;
|
||||
union xhci_trb *event_trb;
|
||||
struct urb *urb = 0;
|
||||
struct urb *urb = NULL;
|
||||
int status = -EINPROGRESS;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
u32 trb_comp_code;
|
||||
|
|
@ -1140,10 +1254,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||
ep_index = TRB_TO_EP_ID(event->flags) - 1;
|
||||
xhci_dbg(xhci, "%s - ep index = %d\n", __func__, ep_index);
|
||||
ep = &xdev->eps[ep_index];
|
||||
ep_ring = ep->ring;
|
||||
ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
|
||||
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
|
||||
if (!ep_ring || (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) {
|
||||
xhci_err(xhci, "ERROR Transfer event pointed to disabled endpoint\n");
|
||||
xhci_err(xhci, "ERROR Transfer event for disabled endpoint "
|
||||
"or incorrect stream ring\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
|
@ -1274,7 +1389,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||
td->urb->actual_length = 0;
|
||||
|
||||
xhci_cleanup_halted_endpoint(xhci,
|
||||
slot_id, ep_index, td, event_trb);
|
||||
slot_id, ep_index, 0, td, event_trb);
|
||||
goto td_cleanup;
|
||||
}
|
||||
/*
|
||||
|
|
@ -1390,8 +1505,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||
for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg;
|
||||
cur_trb != event_trb;
|
||||
next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
|
||||
if (TRB_TYPE(cur_trb->generic.field[3]) != TRB_TR_NOOP &&
|
||||
TRB_TYPE(cur_trb->generic.field[3]) != TRB_LINK)
|
||||
if ((cur_trb->generic.field[3] &
|
||||
TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) &&
|
||||
(cur_trb->generic.field[3] &
|
||||
TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK))
|
||||
td->urb->actual_length +=
|
||||
TRB_LEN(cur_trb->generic.field[2]);
|
||||
}
|
||||
|
|
@ -1423,6 +1540,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||
*/
|
||||
ep->stopped_td = td;
|
||||
ep->stopped_trb = event_trb;
|
||||
ep->stopped_stream = ep_ring->stream_id;
|
||||
} else if (xhci_requires_manual_halt_cleanup(xhci,
|
||||
ep_ctx, trb_comp_code)) {
|
||||
/* Other types of errors halt the endpoint, but the
|
||||
|
|
@ -1431,7 +1549,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||
* xHCI hardware manually.
|
||||
*/
|
||||
xhci_cleanup_halted_endpoint(xhci,
|
||||
slot_id, ep_index, td, event_trb);
|
||||
slot_id, ep_index, ep_ring->stream_id, td, event_trb);
|
||||
} else {
|
||||
/* Update ring dequeue pointer */
|
||||
while (ep_ring->dequeue != td->last_trb)
|
||||
|
|
@ -1621,20 +1739,66 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
|
|||
xhci_err(xhci, "ERROR no room on ep ring\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (enqueue_is_link_trb(ep_ring)) {
|
||||
struct xhci_ring *ring = ep_ring;
|
||||
union xhci_trb *next;
|
||||
|
||||
xhci_dbg(xhci, "prepare_ring: pointing to link trb\n");
|
||||
next = ring->enqueue;
|
||||
|
||||
while (last_trb(xhci, ring, ring->enq_seg, next)) {
|
||||
|
||||
/* If we're not dealing with 0.95 hardware,
|
||||
* clear the chain bit.
|
||||
*/
|
||||
if (!xhci_link_trb_quirk(xhci))
|
||||
next->link.control &= ~TRB_CHAIN;
|
||||
else
|
||||
next->link.control |= TRB_CHAIN;
|
||||
|
||||
wmb();
|
||||
next->link.control ^= (u32) TRB_CYCLE;
|
||||
|
||||
/* Toggle the cycle bit after the last ring segment. */
|
||||
if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
|
||||
ring->cycle_state = (ring->cycle_state ? 0 : 1);
|
||||
if (!in_interrupt()) {
|
||||
xhci_dbg(xhci, "queue_trb: Toggle cycle "
|
||||
"state for ring %p = %i\n",
|
||||
ring, (unsigned int)ring->cycle_state);
|
||||
}
|
||||
}
|
||||
ring->enq_seg = ring->enq_seg->next;
|
||||
ring->enqueue = ring->enq_seg->trbs;
|
||||
next = ring->enqueue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_transfer(struct xhci_hcd *xhci,
|
||||
struct xhci_virt_device *xdev,
|
||||
unsigned int ep_index,
|
||||
unsigned int stream_id,
|
||||
unsigned int num_trbs,
|
||||
struct urb *urb,
|
||||
struct xhci_td **td,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
int ret;
|
||||
struct xhci_ring *ep_ring;
|
||||
struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
|
||||
ret = prepare_ring(xhci, xdev->eps[ep_index].ring,
|
||||
|
||||
ep_ring = xhci_stream_id_to_ring(xdev, ep_index, stream_id);
|
||||
if (!ep_ring) {
|
||||
xhci_dbg(xhci, "Can't prepare ring for bad stream ID %u\n",
|
||||
stream_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = prepare_ring(xhci, ep_ring,
|
||||
ep_ctx->ep_info & EP_STATE_MASK,
|
||||
num_trbs, mem_flags);
|
||||
if (ret)
|
||||
|
|
@ -1654,9 +1818,9 @@ static int prepare_transfer(struct xhci_hcd *xhci,
|
|||
(*td)->urb = urb;
|
||||
urb->hcpriv = (void *) (*td);
|
||||
/* Add this TD to the tail of the endpoint ring's TD list */
|
||||
list_add_tail(&(*td)->td_list, &xdev->eps[ep_index].ring->td_list);
|
||||
(*td)->start_seg = xdev->eps[ep_index].ring->enq_seg;
|
||||
(*td)->first_trb = xdev->eps[ep_index].ring->enqueue;
|
||||
list_add_tail(&(*td)->td_list, &ep_ring->td_list);
|
||||
(*td)->start_seg = ep_ring->enq_seg;
|
||||
(*td)->first_trb = ep_ring->enqueue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1672,7 +1836,7 @@ static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb)
|
|||
|
||||
xhci_dbg(xhci, "count sg list trbs: \n");
|
||||
num_trbs = 0;
|
||||
for_each_sg(urb->sg->sg, sg, num_sgs, i) {
|
||||
for_each_sg(urb->sg, sg, num_sgs, i) {
|
||||
unsigned int previous_total_trbs = num_trbs;
|
||||
unsigned int len = sg_dma_len(sg);
|
||||
|
||||
|
|
@ -1722,7 +1886,7 @@ static void check_trb_math(struct urb *urb, int num_trbs, int running_total)
|
|||
}
|
||||
|
||||
static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
|
||||
unsigned int ep_index, int start_cycle,
|
||||
unsigned int ep_index, unsigned int stream_id, int start_cycle,
|
||||
struct xhci_generic_trb *start_trb, struct xhci_td *td)
|
||||
{
|
||||
/*
|
||||
|
|
@ -1731,7 +1895,7 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
|
|||
*/
|
||||
wmb();
|
||||
start_trb->field[3] |= start_cycle;
|
||||
ring_ep_doorbell(xhci, slot_id, ep_index);
|
||||
ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1805,12 +1969,16 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
struct xhci_generic_trb *start_trb;
|
||||
int start_cycle;
|
||||
|
||||
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
|
||||
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
|
||||
if (!ep_ring)
|
||||
return -EINVAL;
|
||||
|
||||
num_trbs = count_sg_trbs_needed(xhci, urb);
|
||||
num_sgs = urb->num_sgs;
|
||||
|
||||
trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, num_trbs, urb, &td, mem_flags);
|
||||
ep_index, urb->stream_id,
|
||||
num_trbs, urb, &td, mem_flags);
|
||||
if (trb_buff_len < 0)
|
||||
return trb_buff_len;
|
||||
/*
|
||||
|
|
@ -1831,7 +1999,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
* the amount of memory allocated for this scatter-gather list.
|
||||
* 3. TRBs buffers can't cross 64KB boundaries.
|
||||
*/
|
||||
sg = urb->sg->sg;
|
||||
sg = urb->sg;
|
||||
addr = (u64) sg_dma_address(sg);
|
||||
this_sg_len = sg_dma_len(sg);
|
||||
trb_buff_len = TRB_MAX_BUFF_SIZE -
|
||||
|
|
@ -1919,7 +2087,8 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
} while (running_total < urb->transfer_buffer_length);
|
||||
|
||||
check_trb_math(urb, num_trbs, running_total);
|
||||
giveback_first_trb(xhci, slot_id, ep_index, start_cycle, start_trb, td);
|
||||
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
|
||||
start_cycle, start_trb, td);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1938,10 +2107,12 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
int running_total, trb_buff_len, ret;
|
||||
u64 addr;
|
||||
|
||||
if (urb->sg)
|
||||
if (urb->num_sgs)
|
||||
return queue_bulk_sg_tx(xhci, mem_flags, urb, slot_id, ep_index);
|
||||
|
||||
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
|
||||
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
|
||||
if (!ep_ring)
|
||||
return -EINVAL;
|
||||
|
||||
num_trbs = 0;
|
||||
/* How much data is (potentially) left before the 64KB boundary? */
|
||||
|
|
@ -1968,7 +2139,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
(unsigned long long)urb->transfer_dma,
|
||||
num_trbs);
|
||||
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
num_trbs, urb, &td, mem_flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -2038,7 +2210,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
} while (running_total < urb->transfer_buffer_length);
|
||||
|
||||
check_trb_math(urb, num_trbs, running_total);
|
||||
giveback_first_trb(xhci, slot_id, ep_index, start_cycle, start_trb, td);
|
||||
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
|
||||
start_cycle, start_trb, td);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2055,7 +2228,9 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
u32 field, length_field;
|
||||
struct xhci_td *td;
|
||||
|
||||
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
|
||||
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
|
||||
if (!ep_ring)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Need to copy setup packet into setup TRB, so we can't use the setup
|
||||
|
|
@ -2076,8 +2251,9 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
*/
|
||||
if (urb->transfer_buffer_length > 0)
|
||||
num_trbs++;
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, num_trbs,
|
||||
urb, &td, mem_flags);
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
num_trbs, urb, &td, mem_flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -2132,7 +2308,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
/* Event on completion */
|
||||
field | TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state);
|
||||
|
||||
giveback_first_trb(xhci, slot_id, ep_index, start_cycle, start_trb, td);
|
||||
giveback_first_trb(xhci, slot_id, ep_index, 0,
|
||||
start_cycle, start_trb, td);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2244,12 +2421,14 @@ int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
|
|||
* This should not be used for endpoints that have streams enabled.
|
||||
*/
|
||||
static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
|
||||
unsigned int ep_index, struct xhci_segment *deq_seg,
|
||||
unsigned int ep_index, unsigned int stream_id,
|
||||
struct xhci_segment *deq_seg,
|
||||
union xhci_trb *deq_ptr, u32 cycle_state)
|
||||
{
|
||||
dma_addr_t addr;
|
||||
u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
|
||||
u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
|
||||
u32 trb_stream_id = STREAM_ID_FOR_TRB(stream_id);
|
||||
u32 type = TRB_TYPE(TRB_SET_DEQ);
|
||||
|
||||
addr = xhci_trb_virt_to_dma(deq_seg, deq_ptr);
|
||||
|
|
@ -2260,7 +2439,7 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
|
|||
return 0;
|
||||
}
|
||||
return queue_command(xhci, lower_32_bits(addr) | cycle_state,
|
||||
upper_32_bits(addr), 0,
|
||||
upper_32_bits(addr), trb_stream_id,
|
||||
trb_slot_id | trb_ep_index | type, false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
@ -352,11 +353,7 @@ void xhci_event_ring_work(unsigned long arg)
|
|||
if (!xhci->devs[i])
|
||||
continue;
|
||||
for (j = 0; j < 31; ++j) {
|
||||
struct xhci_ring *ring = xhci->devs[i]->eps[j].ring;
|
||||
if (!ring)
|
||||
continue;
|
||||
xhci_dbg(xhci, "Dev %d endpoint ring %d:\n", i, j);
|
||||
xhci_debug_segment(xhci, ring->deq_seg);
|
||||
xhci_dbg_ep_rings(xhci, i, j, &xhci->devs[i]->eps[j]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -726,8 +723,21 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
|
|||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
if (xhci->xhc_state & XHCI_STATE_DYING)
|
||||
goto dying;
|
||||
ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
|
||||
slot_id, ep_index);
|
||||
if (xhci->devs[slot_id]->eps[ep_index].ep_state &
|
||||
EP_GETTING_STREAMS) {
|
||||
xhci_warn(xhci, "WARN: Can't enqueue URB while bulk ep "
|
||||
"is transitioning to using streams.\n");
|
||||
ret = -EINVAL;
|
||||
} else if (xhci->devs[slot_id]->eps[ep_index].ep_state &
|
||||
EP_GETTING_NO_STREAMS) {
|
||||
xhci_warn(xhci, "WARN: Can't enqueue URB while bulk ep "
|
||||
"is transitioning to "
|
||||
"not having streams.\n");
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
|
||||
slot_id, ep_index);
|
||||
}
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
} else if (usb_endpoint_xfer_int(&urb->ep->desc)) {
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
|
@ -825,7 +835,12 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
|||
xhci_debug_ring(xhci, xhci->event_ring);
|
||||
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
|
||||
ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index];
|
||||
ep_ring = ep->ring;
|
||||
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
|
||||
if (!ep_ring) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
xhci_dbg(xhci, "Endpoint ring:\n");
|
||||
xhci_debug_ring(xhci, ep_ring);
|
||||
td = (struct xhci_td *) urb->hcpriv;
|
||||
|
|
@ -1369,7 +1384,7 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci,
|
|||
* or it will attempt to resend it on the next doorbell ring.
|
||||
*/
|
||||
xhci_find_new_dequeue_state(xhci, udev->slot_id,
|
||||
ep_index, ep->stopped_td,
|
||||
ep_index, ep->stopped_stream, ep->stopped_td,
|
||||
&deq_state);
|
||||
|
||||
/* HW with the reset endpoint quirk will use the saved dequeue state to
|
||||
|
|
@ -1378,10 +1393,12 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci,
|
|||
if (!(xhci->quirks & XHCI_RESET_EP_QUIRK)) {
|
||||
xhci_dbg(xhci, "Queueing new dequeue state\n");
|
||||
xhci_queue_new_dequeue_state(xhci, udev->slot_id,
|
||||
ep_index, &deq_state);
|
||||
ep_index, ep->stopped_stream, &deq_state);
|
||||
} else {
|
||||
/* Better hope no one uses the input context between now and the
|
||||
* reset endpoint completion!
|
||||
* XXX: No idea how this hardware will react when stream rings
|
||||
* are enabled.
|
||||
*/
|
||||
xhci_dbg(xhci, "Setting up input context for "
|
||||
"configure endpoint command\n");
|
||||
|
|
@ -1438,12 +1455,391 @@ void xhci_endpoint_reset(struct usb_hcd *hcd,
|
|||
kfree(virt_ep->stopped_td);
|
||||
xhci_ring_cmd_db(xhci);
|
||||
}
|
||||
virt_ep->stopped_td = NULL;
|
||||
virt_ep->stopped_trb = NULL;
|
||||
virt_ep->stopped_stream = 0;
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
if (ret)
|
||||
xhci_warn(xhci, "FIXME allocate a new ring segment\n");
|
||||
}
|
||||
|
||||
static int xhci_check_streams_endpoint(struct xhci_hcd *xhci,
|
||||
struct usb_device *udev, struct usb_host_endpoint *ep,
|
||||
unsigned int slot_id)
|
||||
{
|
||||
int ret;
|
||||
unsigned int ep_index;
|
||||
unsigned int ep_state;
|
||||
|
||||
if (!ep)
|
||||
return -EINVAL;
|
||||
ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, __func__);
|
||||
if (ret <= 0)
|
||||
return -EINVAL;
|
||||
if (ep->ss_ep_comp.bmAttributes == 0) {
|
||||
xhci_warn(xhci, "WARN: SuperSpeed Endpoint Companion"
|
||||
" descriptor for ep 0x%x does not support streams\n",
|
||||
ep->desc.bEndpointAddress);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ep_index = xhci_get_endpoint_index(&ep->desc);
|
||||
ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
|
||||
if (ep_state & EP_HAS_STREAMS ||
|
||||
ep_state & EP_GETTING_STREAMS) {
|
||||
xhci_warn(xhci, "WARN: SuperSpeed bulk endpoint 0x%x "
|
||||
"already has streams set up.\n",
|
||||
ep->desc.bEndpointAddress);
|
||||
xhci_warn(xhci, "Send email to xHCI maintainer and ask for "
|
||||
"dynamic stream context array reallocation.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!list_empty(&xhci->devs[slot_id]->eps[ep_index].ring->td_list)) {
|
||||
xhci_warn(xhci, "Cannot setup streams for SuperSpeed bulk "
|
||||
"endpoint 0x%x; URBs are pending.\n",
|
||||
ep->desc.bEndpointAddress);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xhci_calculate_streams_entries(struct xhci_hcd *xhci,
|
||||
unsigned int *num_streams, unsigned int *num_stream_ctxs)
|
||||
{
|
||||
unsigned int max_streams;
|
||||
|
||||
/* The stream context array size must be a power of two */
|
||||
*num_stream_ctxs = roundup_pow_of_two(*num_streams);
|
||||
/*
|
||||
* Find out how many primary stream array entries the host controller
|
||||
* supports. Later we may use secondary stream arrays (similar to 2nd
|
||||
* level page entries), but that's an optional feature for xHCI host
|
||||
* controllers. xHCs must support at least 4 stream IDs.
|
||||
*/
|
||||
max_streams = HCC_MAX_PSA(xhci->hcc_params);
|
||||
if (*num_stream_ctxs > max_streams) {
|
||||
xhci_dbg(xhci, "xHCI HW only supports %u stream ctx entries.\n",
|
||||
max_streams);
|
||||
*num_stream_ctxs = max_streams;
|
||||
*num_streams = max_streams;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns an error code if one of the endpoint already has streams.
|
||||
* This does not change any data structures, it only checks and gathers
|
||||
* information.
|
||||
*/
|
||||
static int xhci_calculate_streams_and_bitmask(struct xhci_hcd *xhci,
|
||||
struct usb_device *udev,
|
||||
struct usb_host_endpoint **eps, unsigned int num_eps,
|
||||
unsigned int *num_streams, u32 *changed_ep_bitmask)
|
||||
{
|
||||
unsigned int max_streams;
|
||||
unsigned int endpoint_flag;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
ret = xhci_check_streams_endpoint(xhci, udev,
|
||||
eps[i], udev->slot_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
max_streams = USB_SS_MAX_STREAMS(
|
||||
eps[i]->ss_ep_comp.bmAttributes);
|
||||
if (max_streams < (*num_streams - 1)) {
|
||||
xhci_dbg(xhci, "Ep 0x%x only supports %u stream IDs.\n",
|
||||
eps[i]->desc.bEndpointAddress,
|
||||
max_streams);
|
||||
*num_streams = max_streams+1;
|
||||
}
|
||||
|
||||
endpoint_flag = xhci_get_endpoint_flag(&eps[i]->desc);
|
||||
if (*changed_ep_bitmask & endpoint_flag)
|
||||
return -EINVAL;
|
||||
*changed_ep_bitmask |= endpoint_flag;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 xhci_calculate_no_streams_bitmask(struct xhci_hcd *xhci,
|
||||
struct usb_device *udev,
|
||||
struct usb_host_endpoint **eps, unsigned int num_eps)
|
||||
{
|
||||
u32 changed_ep_bitmask = 0;
|
||||
unsigned int slot_id;
|
||||
unsigned int ep_index;
|
||||
unsigned int ep_state;
|
||||
int i;
|
||||
|
||||
slot_id = udev->slot_id;
|
||||
if (!xhci->devs[slot_id])
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
ep_index = xhci_get_endpoint_index(&eps[i]->desc);
|
||||
ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
|
||||
/* Are streams already being freed for the endpoint? */
|
||||
if (ep_state & EP_GETTING_NO_STREAMS) {
|
||||
xhci_warn(xhci, "WARN Can't disable streams for "
|
||||
"endpoint 0x%x\n, "
|
||||
"streams are being disabled already.",
|
||||
eps[i]->desc.bEndpointAddress);
|
||||
return 0;
|
||||
}
|
||||
/* Are there actually any streams to free? */
|
||||
if (!(ep_state & EP_HAS_STREAMS) &&
|
||||
!(ep_state & EP_GETTING_STREAMS)) {
|
||||
xhci_warn(xhci, "WARN Can't disable streams for "
|
||||
"endpoint 0x%x\n, "
|
||||
"streams are already disabled!",
|
||||
eps[i]->desc.bEndpointAddress);
|
||||
xhci_warn(xhci, "WARN xhci_free_streams() called "
|
||||
"with non-streams endpoint\n");
|
||||
return 0;
|
||||
}
|
||||
changed_ep_bitmask |= xhci_get_endpoint_flag(&eps[i]->desc);
|
||||
}
|
||||
return changed_ep_bitmask;
|
||||
}
|
||||
|
||||
/*
|
||||
* The USB device drivers use this function (though the HCD interface in USB
|
||||
* core) to prepare a set of bulk endpoints to use streams. Streams are used to
|
||||
* coordinate mass storage command queueing across multiple endpoints (basically
|
||||
* a stream ID == a task ID).
|
||||
*
|
||||
* Setting up streams involves allocating the same size stream context array
|
||||
* for each endpoint and issuing a configure endpoint command for all endpoints.
|
||||
*
|
||||
* Don't allow the call to succeed if one endpoint only supports one stream
|
||||
* (which means it doesn't support streams at all).
|
||||
*
|
||||
* Drivers may get less stream IDs than they asked for, if the host controller
|
||||
* hardware or endpoints claim they can't support the number of requested
|
||||
* stream IDs.
|
||||
*/
|
||||
int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
struct usb_host_endpoint **eps, unsigned int num_eps,
|
||||
unsigned int num_streams, gfp_t mem_flags)
|
||||
{
|
||||
int i, ret;
|
||||
struct xhci_hcd *xhci;
|
||||
struct xhci_virt_device *vdev;
|
||||
struct xhci_command *config_cmd;
|
||||
unsigned int ep_index;
|
||||
unsigned int num_stream_ctxs;
|
||||
unsigned long flags;
|
||||
u32 changed_ep_bitmask = 0;
|
||||
|
||||
if (!eps)
|
||||
return -EINVAL;
|
||||
|
||||
/* Add one to the number of streams requested to account for
|
||||
* stream 0 that is reserved for xHCI usage.
|
||||
*/
|
||||
num_streams += 1;
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
xhci_dbg(xhci, "Driver wants %u stream IDs (including stream 0).\n",
|
||||
num_streams);
|
||||
|
||||
config_cmd = xhci_alloc_command(xhci, true, true, mem_flags);
|
||||
if (!config_cmd) {
|
||||
xhci_dbg(xhci, "Could not allocate xHCI command structure.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Check to make sure all endpoints are not already configured for
|
||||
* streams. While we're at it, find the maximum number of streams that
|
||||
* all the endpoints will support and check for duplicate endpoints.
|
||||
*/
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
ret = xhci_calculate_streams_and_bitmask(xhci, udev, eps,
|
||||
num_eps, &num_streams, &changed_ep_bitmask);
|
||||
if (ret < 0) {
|
||||
xhci_free_command(xhci, config_cmd);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
if (num_streams <= 1) {
|
||||
xhci_warn(xhci, "WARN: endpoints can't handle "
|
||||
"more than one stream.\n");
|
||||
xhci_free_command(xhci, config_cmd);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
vdev = xhci->devs[udev->slot_id];
|
||||
/* Mark each endpoint as being in transistion, so
|
||||
* xhci_urb_enqueue() will reject all URBs.
|
||||
*/
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
ep_index = xhci_get_endpoint_index(&eps[i]->desc);
|
||||
vdev->eps[ep_index].ep_state |= EP_GETTING_STREAMS;
|
||||
}
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
/* Setup internal data structures and allocate HW data structures for
|
||||
* streams (but don't install the HW structures in the input context
|
||||
* until we're sure all memory allocation succeeded).
|
||||
*/
|
||||
xhci_calculate_streams_entries(xhci, &num_streams, &num_stream_ctxs);
|
||||
xhci_dbg(xhci, "Need %u stream ctx entries for %u stream IDs.\n",
|
||||
num_stream_ctxs, num_streams);
|
||||
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
ep_index = xhci_get_endpoint_index(&eps[i]->desc);
|
||||
vdev->eps[ep_index].stream_info = xhci_alloc_stream_info(xhci,
|
||||
num_stream_ctxs,
|
||||
num_streams, mem_flags);
|
||||
if (!vdev->eps[ep_index].stream_info)
|
||||
goto cleanup;
|
||||
/* Set maxPstreams in endpoint context and update deq ptr to
|
||||
* point to stream context array. FIXME
|
||||
*/
|
||||
}
|
||||
|
||||
/* Set up the input context for a configure endpoint command. */
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
|
||||
ep_index = xhci_get_endpoint_index(&eps[i]->desc);
|
||||
ep_ctx = xhci_get_ep_ctx(xhci, config_cmd->in_ctx, ep_index);
|
||||
|
||||
xhci_endpoint_copy(xhci, config_cmd->in_ctx,
|
||||
vdev->out_ctx, ep_index);
|
||||
xhci_setup_streams_ep_input_ctx(xhci, ep_ctx,
|
||||
vdev->eps[ep_index].stream_info);
|
||||
}
|
||||
/* Tell the HW to drop its old copy of the endpoint context info
|
||||
* and add the updated copy from the input context.
|
||||
*/
|
||||
xhci_setup_input_ctx_for_config_ep(xhci, config_cmd->in_ctx,
|
||||
vdev->out_ctx, changed_ep_bitmask, changed_ep_bitmask);
|
||||
|
||||
/* Issue and wait for the configure endpoint command */
|
||||
ret = xhci_configure_endpoint(xhci, udev, config_cmd,
|
||||
false, false);
|
||||
|
||||
/* xHC rejected the configure endpoint command for some reason, so we
|
||||
* leave the old ring intact and free our internal streams data
|
||||
* structure.
|
||||
*/
|
||||
if (ret < 0)
|
||||
goto cleanup;
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
ep_index = xhci_get_endpoint_index(&eps[i]->desc);
|
||||
vdev->eps[ep_index].ep_state &= ~EP_GETTING_STREAMS;
|
||||
xhci_dbg(xhci, "Slot %u ep ctx %u now has streams.\n",
|
||||
udev->slot_id, ep_index);
|
||||
vdev->eps[ep_index].ep_state |= EP_HAS_STREAMS;
|
||||
}
|
||||
xhci_free_command(xhci, config_cmd);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
/* Subtract 1 for stream 0, which drivers can't use */
|
||||
return num_streams - 1;
|
||||
|
||||
cleanup:
|
||||
/* If it didn't work, free the streams! */
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
ep_index = xhci_get_endpoint_index(&eps[i]->desc);
|
||||
xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info);
|
||||
vdev->eps[ep_index].stream_info = NULL;
|
||||
/* FIXME Unset maxPstreams in endpoint context and
|
||||
* update deq ptr to point to normal string ring.
|
||||
*/
|
||||
vdev->eps[ep_index].ep_state &= ~EP_GETTING_STREAMS;
|
||||
vdev->eps[ep_index].ep_state &= ~EP_HAS_STREAMS;
|
||||
xhci_endpoint_zero(xhci, vdev, eps[i]);
|
||||
}
|
||||
xhci_free_command(xhci, config_cmd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Transition the endpoint from using streams to being a "normal" endpoint
|
||||
* without streams.
|
||||
*
|
||||
* Modify the endpoint context state, submit a configure endpoint command,
|
||||
* and free all endpoint rings for streams if that completes successfully.
|
||||
*/
|
||||
int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
struct usb_host_endpoint **eps, unsigned int num_eps,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
int i, ret;
|
||||
struct xhci_hcd *xhci;
|
||||
struct xhci_virt_device *vdev;
|
||||
struct xhci_command *command;
|
||||
unsigned int ep_index;
|
||||
unsigned long flags;
|
||||
u32 changed_ep_bitmask;
|
||||
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
vdev = xhci->devs[udev->slot_id];
|
||||
|
||||
/* Set up a configure endpoint command to remove the streams rings */
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
changed_ep_bitmask = xhci_calculate_no_streams_bitmask(xhci,
|
||||
udev, eps, num_eps);
|
||||
if (changed_ep_bitmask == 0) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Use the xhci_command structure from the first endpoint. We may have
|
||||
* allocated too many, but the driver may call xhci_free_streams() for
|
||||
* each endpoint it grouped into one call to xhci_alloc_streams().
|
||||
*/
|
||||
ep_index = xhci_get_endpoint_index(&eps[0]->desc);
|
||||
command = vdev->eps[ep_index].stream_info->free_streams_command;
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
|
||||
ep_index = xhci_get_endpoint_index(&eps[i]->desc);
|
||||
ep_ctx = xhci_get_ep_ctx(xhci, command->in_ctx, ep_index);
|
||||
xhci->devs[udev->slot_id]->eps[ep_index].ep_state |=
|
||||
EP_GETTING_NO_STREAMS;
|
||||
|
||||
xhci_endpoint_copy(xhci, command->in_ctx,
|
||||
vdev->out_ctx, ep_index);
|
||||
xhci_setup_no_streams_ep_input_ctx(xhci, ep_ctx,
|
||||
&vdev->eps[ep_index]);
|
||||
}
|
||||
xhci_setup_input_ctx_for_config_ep(xhci, command->in_ctx,
|
||||
vdev->out_ctx, changed_ep_bitmask, changed_ep_bitmask);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
/* Issue and wait for the configure endpoint command,
|
||||
* which must succeed.
|
||||
*/
|
||||
ret = xhci_configure_endpoint(xhci, udev, command,
|
||||
false, true);
|
||||
|
||||
/* xHC rejected the configure endpoint command for some reason, so we
|
||||
* leave the streams rings intact.
|
||||
*/
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
ep_index = xhci_get_endpoint_index(&eps[i]->desc);
|
||||
xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info);
|
||||
vdev->eps[ep_index].stream_info = NULL;
|
||||
/* FIXME Unset maxPstreams in endpoint context and
|
||||
* update deq ptr to point to normal string ring.
|
||||
*/
|
||||
vdev->eps[ep_index].ep_state &= ~EP_GETTING_NO_STREAMS;
|
||||
vdev->eps[ep_index].ep_state &= ~EP_HAS_STREAMS;
|
||||
}
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This submits a Reset Device Command, which will set the device state to 0,
|
||||
* set the device address to 0, and disable all the endpoints except the default
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@
|
|||
#include <linux/usb.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include "../core/hcd.h"
|
||||
/* Code sharing between pci-quirks and xhci hcd */
|
||||
#include "xhci-ext-caps.h"
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ struct xhci_cap_regs {
|
|||
/* true: no secondary Stream ID Support */
|
||||
#define HCC_NSS(p) ((p) & (1 << 7))
|
||||
/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */
|
||||
#define HCC_MAX_PSA (1 << ((((p) >> 12) & 0xf) + 1))
|
||||
#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1))
|
||||
/* Extended Capabilities pointer from PCI base - section 5.3.6 */
|
||||
#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p)
|
||||
|
||||
|
|
@ -444,6 +444,7 @@ struct xhci_doorbell_array {
|
|||
|
||||
/* Endpoint Target - bits 0:7 */
|
||||
#define EPI_TO_DB(p) (((p) + 1) & 0xff)
|
||||
#define STREAM_ID_TO_DB(p) (((p) & 0xffff) << 16)
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -585,6 +586,10 @@ struct xhci_ep_ctx {
|
|||
/* Interval - period between requests to an endpoint - 125u increments. */
|
||||
#define EP_INTERVAL(p) ((p & 0xff) << 16)
|
||||
#define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff))
|
||||
#define EP_MAXPSTREAMS_MASK (0x1f << 10)
|
||||
#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK)
|
||||
/* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */
|
||||
#define EP_HAS_LSA (1 << 15)
|
||||
|
||||
/* ep_info2 bitmasks */
|
||||
/*
|
||||
|
|
@ -648,8 +653,50 @@ struct xhci_command {
|
|||
/* add context bitmasks */
|
||||
#define ADD_EP(x) (0x1 << x)
|
||||
|
||||
struct xhci_stream_ctx {
|
||||
/* 64-bit stream ring address, cycle state, and stream type */
|
||||
u64 stream_ring;
|
||||
/* offset 0x14 - 0x1f reserved for HC internal use */
|
||||
u32 reserved[2];
|
||||
};
|
||||
|
||||
/* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */
|
||||
#define SCT_FOR_CTX(p) (((p) << 1) & 0x7)
|
||||
/* Secondary stream array type, dequeue pointer is to a transfer ring */
|
||||
#define SCT_SEC_TR 0
|
||||
/* Primary stream array type, dequeue pointer is to a transfer ring */
|
||||
#define SCT_PRI_TR 1
|
||||
/* Dequeue pointer is for a secondary stream array (SSA) with 8 entries */
|
||||
#define SCT_SSA_8 2
|
||||
#define SCT_SSA_16 3
|
||||
#define SCT_SSA_32 4
|
||||
#define SCT_SSA_64 5
|
||||
#define SCT_SSA_128 6
|
||||
#define SCT_SSA_256 7
|
||||
|
||||
/* Assume no secondary streams for now */
|
||||
struct xhci_stream_info {
|
||||
struct xhci_ring **stream_rings;
|
||||
/* Number of streams, including stream 0 (which drivers can't use) */
|
||||
unsigned int num_streams;
|
||||
/* The stream context array may be bigger than
|
||||
* the number of streams the driver asked for
|
||||
*/
|
||||
struct xhci_stream_ctx *stream_ctx_array;
|
||||
unsigned int num_stream_ctxs;
|
||||
dma_addr_t ctx_array_dma;
|
||||
/* For mapping physical TRB addresses to segments in stream rings */
|
||||
struct radix_tree_root trb_address_map;
|
||||
struct xhci_command *free_streams_command;
|
||||
};
|
||||
|
||||
#define SMALL_STREAM_ARRAY_SIZE 256
|
||||
#define MEDIUM_STREAM_ARRAY_SIZE 1024
|
||||
|
||||
struct xhci_virt_ep {
|
||||
struct xhci_ring *ring;
|
||||
/* Related to endpoints that are configured to use stream IDs only */
|
||||
struct xhci_stream_info *stream_info;
|
||||
/* Temporary storage in case the configure endpoint command fails and we
|
||||
* have to restore the device state to the previous state
|
||||
*/
|
||||
|
|
@ -658,11 +705,17 @@ struct xhci_virt_ep {
|
|||
#define SET_DEQ_PENDING (1 << 0)
|
||||
#define EP_HALTED (1 << 1) /* For stall handling */
|
||||
#define EP_HALT_PENDING (1 << 2) /* For URB cancellation */
|
||||
/* Transitioning the endpoint to using streams, don't enqueue URBs */
|
||||
#define EP_GETTING_STREAMS (1 << 3)
|
||||
#define EP_HAS_STREAMS (1 << 4)
|
||||
/* Transitioning the endpoint to not using streams, don't enqueue URBs */
|
||||
#define EP_GETTING_NO_STREAMS (1 << 5)
|
||||
/* ---- Related to URB cancellation ---- */
|
||||
struct list_head cancelled_td_list;
|
||||
/* The TRB that was last reported in a stopped endpoint ring */
|
||||
union xhci_trb *stopped_trb;
|
||||
struct xhci_td *stopped_td;
|
||||
unsigned int stopped_stream;
|
||||
/* Watchdog timer for stop endpoint command to cancel URBs */
|
||||
struct timer_list stop_cmd_timer;
|
||||
int stop_cmds_pending;
|
||||
|
|
@ -710,14 +763,6 @@ struct xhci_device_context_array {
|
|||
*/
|
||||
|
||||
|
||||
struct xhci_stream_ctx {
|
||||
/* 64-bit stream ring address, cycle state, and stream type */
|
||||
u64 stream_ring;
|
||||
/* offset 0x14 - 0x1f reserved for HC internal use */
|
||||
u32 reserved[2];
|
||||
};
|
||||
|
||||
|
||||
struct xhci_transfer_event {
|
||||
/* 64-bit buffer address, or immediate data */
|
||||
u64 buffer;
|
||||
|
|
@ -828,6 +873,10 @@ struct xhci_event_cmd {
|
|||
#define TRB_TO_EP_INDEX(p) ((((p) & (0x1f << 16)) >> 16) - 1)
|
||||
#define EP_ID_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16)
|
||||
|
||||
/* Set TR Dequeue Pointer command TRB fields */
|
||||
#define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16))
|
||||
#define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16)
|
||||
|
||||
|
||||
/* Port Status Change Event TRB fields */
|
||||
/* Port ID - bits 31:24 */
|
||||
|
|
@ -952,6 +1001,10 @@ union xhci_trb {
|
|||
/* Allow two commands + a link TRB, along with any reserved command TRBs */
|
||||
#define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3)
|
||||
#define SEGMENT_SIZE (TRBS_PER_SEGMENT*16)
|
||||
/* SEGMENT_SHIFT should be log2(SEGMENT_SIZE).
|
||||
* Change this if you change TRBS_PER_SEGMENT!
|
||||
*/
|
||||
#define SEGMENT_SHIFT 10
|
||||
/* TRB buffer pointers can't cross 64KB boundaries */
|
||||
#define TRB_MAX_BUFF_SHIFT 16
|
||||
#define TRB_MAX_BUFF_SIZE (1 << TRB_MAX_BUFF_SHIFT)
|
||||
|
|
@ -993,6 +1046,7 @@ struct xhci_ring {
|
|||
* if we own the TRB (if we are the consumer). See section 4.9.1.
|
||||
*/
|
||||
u32 cycle_state;
|
||||
unsigned int stream_id;
|
||||
};
|
||||
|
||||
struct xhci_erst_entry {
|
||||
|
|
@ -1088,6 +1142,8 @@ struct xhci_hcd {
|
|||
/* DMA pools */
|
||||
struct dma_pool *device_pool;
|
||||
struct dma_pool *segment_pool;
|
||||
struct dma_pool *small_streams_pool;
|
||||
struct dma_pool *medium_streams_pool;
|
||||
|
||||
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
|
||||
/* Poll the rings - for debugging */
|
||||
|
|
@ -1216,6 +1272,9 @@ void xhci_dbg_ring_ptrs(struct xhci_hcd *xhci, struct xhci_ring *ring);
|
|||
void xhci_dbg_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int last_ep);
|
||||
char *xhci_get_slot_state(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx);
|
||||
void xhci_dbg_ep_rings(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
struct xhci_virt_ep *ep);
|
||||
|
||||
/* xHCI memory management */
|
||||
void xhci_mem_cleanup(struct xhci_hcd *xhci);
|
||||
|
|
@ -1242,6 +1301,29 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring);
|
|||
void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci,
|
||||
struct xhci_virt_device *virt_dev,
|
||||
unsigned int ep_index);
|
||||
struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
|
||||
unsigned int num_stream_ctxs,
|
||||
unsigned int num_streams, gfp_t flags);
|
||||
void xhci_free_stream_info(struct xhci_hcd *xhci,
|
||||
struct xhci_stream_info *stream_info);
|
||||
void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_ep_ctx *ep_ctx,
|
||||
struct xhci_stream_info *stream_info);
|
||||
void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_ep_ctx *ep_ctx,
|
||||
struct xhci_virt_ep *ep);
|
||||
struct xhci_ring *xhci_dma_to_transfer_ring(
|
||||
struct xhci_virt_ep *ep,
|
||||
u64 address);
|
||||
struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
struct urb *urb);
|
||||
struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
unsigned int stream_id);
|
||||
struct xhci_ring *xhci_stream_id_to_ring(
|
||||
struct xhci_virt_device *dev,
|
||||
unsigned int ep_index,
|
||||
unsigned int stream_id);
|
||||
struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
|
||||
bool allocate_in_ctx, bool allocate_completion,
|
||||
gfp_t mem_flags);
|
||||
|
|
@ -1266,6 +1348,12 @@ int xhci_get_frame(struct usb_hcd *hcd);
|
|||
irqreturn_t xhci_irq(struct usb_hcd *hcd);
|
||||
int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev);
|
||||
void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev);
|
||||
int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
struct usb_host_endpoint **eps, unsigned int num_eps,
|
||||
unsigned int num_streams, gfp_t mem_flags);
|
||||
int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
struct usb_host_endpoint **eps, unsigned int num_eps,
|
||||
gfp_t mem_flags);
|
||||
int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev);
|
||||
int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
|
||||
struct usb_tt *tt, gfp_t mem_flags);
|
||||
|
|
@ -1308,9 +1396,11 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id,
|
|||
int xhci_queue_reset_device(struct xhci_hcd *xhci, u32 slot_id);
|
||||
void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
struct xhci_td *cur_td, struct xhci_dequeue_state *state);
|
||||
unsigned int stream_id, struct xhci_td *cur_td,
|
||||
struct xhci_dequeue_state *state);
|
||||
void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
unsigned int stream_id,
|
||||
struct xhci_dequeue_state *deq_state);
|
||||
void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci,
|
||||
struct usb_device *udev, unsigned int ep_index);
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ static int appledisplay_probe(struct usb_interface *iface,
|
|||
}
|
||||
|
||||
/* Allocate buffer for interrupt data */
|
||||
pdata->urbdata = usb_buffer_alloc(pdata->udev, ACD_URB_BUFFER_LEN,
|
||||
pdata->urbdata = usb_alloc_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
|
||||
GFP_KERNEL, &pdata->urb->transfer_dma);
|
||||
if (!pdata->urbdata) {
|
||||
retval = -ENOMEM;
|
||||
|
|
@ -316,7 +316,7 @@ error:
|
|||
if (pdata->urb) {
|
||||
usb_kill_urb(pdata->urb);
|
||||
if (pdata->urbdata)
|
||||
usb_buffer_free(pdata->udev, ACD_URB_BUFFER_LEN,
|
||||
usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
|
||||
pdata->urbdata, pdata->urb->transfer_dma);
|
||||
usb_free_urb(pdata->urb);
|
||||
}
|
||||
|
|
@ -337,7 +337,7 @@ static void appledisplay_disconnect(struct usb_interface *iface)
|
|||
usb_kill_urb(pdata->urb);
|
||||
cancel_delayed_work(&pdata->work);
|
||||
backlight_device_unregister(pdata->bd);
|
||||
usb_buffer_free(pdata->udev, ACD_URB_BUFFER_LEN,
|
||||
usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
|
||||
pdata->urbdata, pdata->urb->transfer_dma);
|
||||
usb_free_urb(pdata->urb);
|
||||
kfree(pdata->msgdata);
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ static struct list_head ftdi_static_list;
|
|||
*/
|
||||
#include "usb_u132.h"
|
||||
#include <asm/io.h>
|
||||
#include "../core/hcd.h"
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
/* FIXME ohci.h is ONLY for internal use by the OHCI driver.
|
||||
* If you're going to try stuff like this, you need to split
|
||||
|
|
@ -734,7 +734,7 @@ static void ftdi_elan_write_bulk_callback(struct urb *urb)
|
|||
dev_err(&ftdi->udev->dev, "urb=%p write bulk status received: %"
|
||||
"d\n", urb, status);
|
||||
}
|
||||
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
|
||||
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
|
||||
urb->transfer_buffer, urb->transfer_dma);
|
||||
}
|
||||
|
||||
|
|
@ -795,7 +795,7 @@ static int ftdi_elan_command_engine(struct usb_ftdi *ftdi)
|
|||
total_size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = usb_buffer_alloc(ftdi->udev, total_size, GFP_KERNEL,
|
||||
buf = usb_alloc_coherent(ftdi->udev, total_size, GFP_KERNEL,
|
||||
&urb->transfer_dma);
|
||||
if (!buf) {
|
||||
dev_err(&ftdi->udev->dev, "could not get a buffer to write %d c"
|
||||
|
|
@ -829,7 +829,7 @@ static int ftdi_elan_command_engine(struct usb_ftdi *ftdi)
|
|||
dev_err(&ftdi->udev->dev, "failed %d to submit urb %p to write "
|
||||
"%d commands totaling %d bytes to the Uxxx\n", retval,
|
||||
urb, command_size, total_size);
|
||||
usb_buffer_free(ftdi->udev, total_size, buf, urb->transfer_dma);
|
||||
usb_free_coherent(ftdi->udev, total_size, buf, urb->transfer_dma);
|
||||
usb_free_urb(urb);
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -1167,7 +1167,7 @@ static ssize_t ftdi_elan_write(struct file *file,
|
|||
retval = -ENOMEM;
|
||||
goto error_1;
|
||||
}
|
||||
buf = usb_buffer_alloc(ftdi->udev, count, GFP_KERNEL,
|
||||
buf = usb_alloc_coherent(ftdi->udev, count, GFP_KERNEL,
|
||||
&urb->transfer_dma);
|
||||
if (!buf) {
|
||||
retval = -ENOMEM;
|
||||
|
|
@ -1192,7 +1192,7 @@ static ssize_t ftdi_elan_write(struct file *file,
|
|||
exit:
|
||||
return count;
|
||||
error_3:
|
||||
usb_buffer_free(ftdi->udev, count, buf, urb->transfer_dma);
|
||||
usb_free_coherent(ftdi->udev, count, buf, urb->transfer_dma);
|
||||
error_2:
|
||||
usb_free_urb(urb);
|
||||
error_1:
|
||||
|
|
@ -1968,7 +1968,7 @@ static int ftdi_elan_synchronize_flush(struct usb_ftdi *ftdi)
|
|||
"ence\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = usb_buffer_alloc(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
|
||||
buf = usb_alloc_coherent(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
|
||||
if (!buf) {
|
||||
dev_err(&ftdi->udev->dev, "could not get a buffer for flush seq"
|
||||
"uence\n");
|
||||
|
|
@ -1985,7 +1985,7 @@ static int ftdi_elan_synchronize_flush(struct usb_ftdi *ftdi)
|
|||
if (retval) {
|
||||
dev_err(&ftdi->udev->dev, "failed to submit urb containing the "
|
||||
"flush sequence\n");
|
||||
usb_buffer_free(ftdi->udev, i, buf, urb->transfer_dma);
|
||||
usb_free_coherent(ftdi->udev, i, buf, urb->transfer_dma);
|
||||
usb_free_urb(urb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -2011,7 +2011,7 @@ static int ftdi_elan_synchronize_reset(struct usb_ftdi *ftdi)
|
|||
"quence\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = usb_buffer_alloc(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
|
||||
buf = usb_alloc_coherent(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
|
||||
if (!buf) {
|
||||
dev_err(&ftdi->udev->dev, "could not get a buffer for the reset"
|
||||
" sequence\n");
|
||||
|
|
@ -2030,7 +2030,7 @@ static int ftdi_elan_synchronize_reset(struct usb_ftdi *ftdi)
|
|||
if (retval) {
|
||||
dev_err(&ftdi->udev->dev, "failed to submit urb containing the "
|
||||
"reset sequence\n");
|
||||
usb_buffer_free(ftdi->udev, i, buf, urb->transfer_dma);
|
||||
usb_free_coherent(ftdi->udev, i, buf, urb->transfer_dma);
|
||||
usb_free_urb(urb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,8 +239,8 @@ static void iowarrior_write_callback(struct urb *urb)
|
|||
__func__, status);
|
||||
}
|
||||
/* free up our allocated buffer */
|
||||
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
|
||||
urb->transfer_buffer, urb->transfer_dma);
|
||||
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
|
||||
urb->transfer_buffer, urb->transfer_dma);
|
||||
/* tell a waiting writer the interrupt-out-pipe is available again */
|
||||
atomic_dec(&dev->write_busy);
|
||||
wake_up_interruptible(&dev->write_wait);
|
||||
|
|
@ -421,8 +421,8 @@ static ssize_t iowarrior_write(struct file *file,
|
|||
dbg("%s Unable to allocate urb ", __func__);
|
||||
goto error_no_urb;
|
||||
}
|
||||
buf = usb_buffer_alloc(dev->udev, dev->report_size,
|
||||
GFP_KERNEL, &int_out_urb->transfer_dma);
|
||||
buf = usb_alloc_coherent(dev->udev, dev->report_size,
|
||||
GFP_KERNEL, &int_out_urb->transfer_dma);
|
||||
if (!buf) {
|
||||
retval = -ENOMEM;
|
||||
dbg("%s Unable to allocate buffer ", __func__);
|
||||
|
|
@ -459,8 +459,8 @@ static ssize_t iowarrior_write(struct file *file,
|
|||
break;
|
||||
}
|
||||
error:
|
||||
usb_buffer_free(dev->udev, dev->report_size, buf,
|
||||
int_out_urb->transfer_dma);
|
||||
usb_free_coherent(dev->udev, dev->report_size, buf,
|
||||
int_out_urb->transfer_dma);
|
||||
error_no_buffer:
|
||||
usb_free_urb(int_out_urb);
|
||||
error_no_urb:
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "sisusb.h"
|
||||
|
|
@ -2416,14 +2415,11 @@ sisusb_open(struct inode *inode, struct file *file)
|
|||
struct usb_interface *interface;
|
||||
int subminor = iminor(inode);
|
||||
|
||||
lock_kernel();
|
||||
if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
|
||||
unlock_kernel();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!(sisusb = usb_get_intfdata(interface))) {
|
||||
unlock_kernel();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
|
@ -2431,13 +2427,11 @@ sisusb_open(struct inode *inode, struct file *file)
|
|||
|
||||
if (!sisusb->present || !sisusb->ready) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
unlock_kernel();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (sisusb->isopen) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
unlock_kernel();
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
|
@ -2446,13 +2440,11 @@ sisusb_open(struct inode *inode, struct file *file)
|
|||
if (sisusb_init_gfxdevice(sisusb, 0)) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to initialize device\n");
|
||||
unlock_kernel();
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Device not attached to USB 2.0 hub\n");
|
||||
unlock_kernel();
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
|
@ -2465,7 +2457,6 @@ sisusb_open(struct inode *inode, struct file *file)
|
|||
file->private_data = sisusb;
|
||||
|
||||
mutex_unlock(&sisusb->lock);
|
||||
unlock_kernel();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2974,13 +2965,12 @@ sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
struct sisusb_usb_data *sisusb;
|
||||
struct sisusb_info x;
|
||||
struct sisusb_command y;
|
||||
int retval = 0;
|
||||
long retval = 0;
|
||||
u32 __user *argp = (u32 __user *)arg;
|
||||
|
||||
if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
|
||||
return -ENODEV;
|
||||
|
||||
lock_kernel();
|
||||
mutex_lock(&sisusb->lock);
|
||||
|
||||
/* Sanity check */
|
||||
|
|
@ -3039,7 +3029,6 @@ sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
|
||||
err_out:
|
||||
mutex_unlock(&sisusb->lock);
|
||||
unlock_kernel();
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1187,9 +1187,9 @@ sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
|
|||
* And so is the hi_font_mask.
|
||||
*/
|
||||
for (i = 0; i < MAX_NR_CONSOLES; i++) {
|
||||
struct vc_data *c = vc_cons[i].d;
|
||||
if (c && c->vc_sw == &sisusb_con)
|
||||
c->vc_hi_font_mask = ch512 ? 0x0800 : 0;
|
||||
struct vc_data *d = vc_cons[i].d;
|
||||
if (d && d->vc_sw == &sisusb_con)
|
||||
d->vc_hi_font_mask = ch512 ? 0x0800 : 0;
|
||||
}
|
||||
|
||||
sisusb->current_font_512 = ch512;
|
||||
|
|
@ -1249,7 +1249,7 @@ sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
|
|||
mutex_unlock(&sisusb->lock);
|
||||
|
||||
if (dorecalc && c) {
|
||||
int i, rows = c->vc_scan_lines / fh;
|
||||
int rows = c->vc_scan_lines / fh;
|
||||
|
||||
/* Now adjust our consoles' size */
|
||||
|
||||
|
|
|
|||
|
|
@ -205,8 +205,8 @@ static void lcd_write_bulk_callback(struct urb *urb)
|
|||
}
|
||||
|
||||
/* free up our allocated buffer */
|
||||
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
|
||||
urb->transfer_buffer, urb->transfer_dma);
|
||||
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
|
||||
urb->transfer_buffer, urb->transfer_dma);
|
||||
up(&dev->limit_sem);
|
||||
}
|
||||
|
||||
|
|
@ -234,7 +234,7 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz
|
|||
goto err_no_buf;
|
||||
}
|
||||
|
||||
buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
|
||||
buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
|
||||
if (!buf) {
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
|
|
@ -268,7 +268,7 @@ exit:
|
|||
error_unanchor:
|
||||
usb_unanchor_urb(urb);
|
||||
error:
|
||||
usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
|
||||
usb_free_coherent(dev->udev, count, buf, urb->transfer_dma);
|
||||
usb_free_urb(urb);
|
||||
err_no_buf:
|
||||
up(&dev->limit_sem);
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ static struct urb *simple_alloc_urb (
|
|||
urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
|
||||
if (usb_pipein (pipe))
|
||||
urb->transfer_flags |= URB_SHORT_NOT_OK;
|
||||
urb->transfer_buffer = usb_buffer_alloc (udev, bytes, GFP_KERNEL,
|
||||
urb->transfer_buffer = usb_alloc_coherent (udev, bytes, GFP_KERNEL,
|
||||
&urb->transfer_dma);
|
||||
if (!urb->transfer_buffer) {
|
||||
usb_free_urb (urb);
|
||||
|
|
@ -272,8 +272,8 @@ static inline int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb)
|
|||
|
||||
static void simple_free_urb (struct urb *urb)
|
||||
{
|
||||
usb_buffer_free (urb->dev, urb->transfer_buffer_length,
|
||||
urb->transfer_buffer, urb->transfer_dma);
|
||||
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
|
||||
urb->transfer_buffer, urb->transfer_dma);
|
||||
usb_free_urb (urb);
|
||||
}
|
||||
|
||||
|
|
@ -977,15 +977,13 @@ test_ctrl_queue (struct usbtest_dev *dev, struct usbtest_param *param)
|
|||
if (!u)
|
||||
goto cleanup;
|
||||
|
||||
reqp = usb_buffer_alloc (udev, sizeof *reqp, GFP_KERNEL,
|
||||
&u->setup_dma);
|
||||
reqp = kmalloc(sizeof *reqp, GFP_KERNEL);
|
||||
if (!reqp)
|
||||
goto cleanup;
|
||||
reqp->setup = req;
|
||||
reqp->number = i % NUM_SUBCASES;
|
||||
reqp->expected = expected;
|
||||
u->setup_packet = (char *) &reqp->setup;
|
||||
u->transfer_flags |= URB_NO_SETUP_DMA_MAP;
|
||||
|
||||
u->context = &context;
|
||||
u->complete = ctrl_complete;
|
||||
|
|
@ -1017,10 +1015,7 @@ cleanup:
|
|||
if (!urb [i])
|
||||
continue;
|
||||
urb [i]->dev = udev;
|
||||
if (urb [i]->setup_packet)
|
||||
usb_buffer_free (udev, sizeof (struct usb_ctrlrequest),
|
||||
urb [i]->setup_packet,
|
||||
urb [i]->setup_dma);
|
||||
kfree(urb[i]->setup_packet);
|
||||
simple_free_urb (urb [i]);
|
||||
}
|
||||
kfree (urb);
|
||||
|
|
@ -1421,7 +1416,7 @@ static struct urb *iso_alloc_urb (
|
|||
|
||||
urb->number_of_packets = packets;
|
||||
urb->transfer_buffer_length = bytes;
|
||||
urb->transfer_buffer = usb_buffer_alloc (udev, bytes, GFP_KERNEL,
|
||||
urb->transfer_buffer = usb_alloc_coherent (udev, bytes, GFP_KERNEL,
|
||||
&urb->transfer_dma);
|
||||
if (!urb->transfer_buffer) {
|
||||
usb_free_urb (urb);
|
||||
|
|
|
|||
|
|
@ -416,13 +416,13 @@ static unsigned int mon_bin_get_data(const struct mon_reader_bin *rp,
|
|||
|
||||
} else {
|
||||
/* If IOMMU coalescing occurred, we cannot trust sg_page */
|
||||
if (urb->sg->nents != urb->num_sgs) {
|
||||
if (urb->transfer_flags & URB_DMA_SG_COMBINED) {
|
||||
*flag = 'D';
|
||||
return length;
|
||||
}
|
||||
|
||||
/* Copy up to the first non-addressable segment */
|
||||
for_each_sg(urb->sg->sg, sg, urb->num_sgs, i) {
|
||||
for_each_sg(urb->sg, sg, urb->num_sgs, i) {
|
||||
if (length == 0 || PageHighMem(sg_page(sg)))
|
||||
break;
|
||||
this_len = min_t(unsigned int, sg->length, length);
|
||||
|
|
|
|||
|
|
@ -9,12 +9,13 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include "usb_mon.h"
|
||||
#include "../core/hcd.h"
|
||||
|
||||
|
||||
static void mon_stop(struct mon_bus *mbus);
|
||||
static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
|
||||
|
|
|
|||
|
|
@ -159,11 +159,9 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
|
|||
if (src == NULL)
|
||||
return 'Z'; /* '0' would be not as pretty. */
|
||||
} else {
|
||||
struct scatterlist *sg = urb->sg->sg;
|
||||
struct scatterlist *sg = urb->sg;
|
||||
|
||||
/* If IOMMU coalescing occurred, we cannot trust sg_page */
|
||||
if (urb->sg->nents != urb->num_sgs ||
|
||||
PageHighMem(sg_page(sg)))
|
||||
if (PageHighMem(sg_page(sg)))
|
||||
return 'D';
|
||||
|
||||
/* For the text interface we copy only the first sg buffer */
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ config USB_MUSB_SOC
|
|||
default y if ARCH_DAVINCI
|
||||
default y if ARCH_OMAP2430
|
||||
default y if ARCH_OMAP3
|
||||
default y if ARCH_OMAP4
|
||||
default y if (BF54x && !BF544)
|
||||
default y if (BF52x && !BF522 && !BF523)
|
||||
|
||||
|
|
@ -50,6 +51,9 @@ comment "OMAP 243x high speed USB support"
|
|||
comment "OMAP 343x high speed USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_OMAP3
|
||||
|
||||
comment "OMAP 44xx high speed USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_OMAP4
|
||||
|
||||
comment "Blackfin high speed USB Support"
|
||||
depends on USB_MUSB_HDRC && ((BF54x && !BF544) || (BF52x && !BF522 && !BF523))
|
||||
|
||||
|
|
@ -153,7 +157,7 @@ config MUSB_PIO_ONLY
|
|||
config USB_INVENTRA_DMA
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
|
||||
default ARCH_OMAP2430 || ARCH_OMAP3 || BLACKFIN
|
||||
default ARCH_OMAP2430 || ARCH_OMAP3 || BLACKFIN || ARCH_OMAP4
|
||||
help
|
||||
Enable DMA transfers using Mentor's engine.
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ ifeq ($(CONFIG_ARCH_OMAP3430),y)
|
|||
musb_hdrc-objs += omap2430.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_ARCH_OMAP4),y)
|
||||
musb_hdrc-objs += omap2430.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_BF54x),y)
|
||||
musb_hdrc-objs += blackfin.o
|
||||
endif
|
||||
|
|
@ -38,6 +42,10 @@ ifeq ($(CONFIG_USB_MUSB_HDRC_HCD),y)
|
|||
musb_hdrc-objs += musb_virthub.o musb_host.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_DEBUG_FS),y)
|
||||
musb_hdrc-objs += musb_debugfs.o
|
||||
endif
|
||||
|
||||
# the kconfig must guarantee that only one of the
|
||||
# possible I/O schemes will be enabled at a time ...
|
||||
# PIO only, or DMA (several potential schemes).
|
||||
|
|
@ -64,12 +72,6 @@ endif
|
|||
|
||||
################################################################################
|
||||
|
||||
# FIXME remove all these extra "-DMUSB_* things, stick to CONFIG_*
|
||||
|
||||
ifeq ($(CONFIG_USB_INVENTRA_MUSB_HAS_AHB_ID),y)
|
||||
EXTRA_CFLAGS += -DMUSB_AHB_ID
|
||||
endif
|
||||
|
||||
# Debugging
|
||||
|
||||
ifeq ($(CONFIG_USB_MUSB_DEBUG),y)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue