diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 04:26:12 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 04:26:12 (GMT) |
commit | 7a9b149212f3716c598afe973b6261fd58453b7a (patch) | |
tree | 477716d84c71da124448b72278e98da28aadbd3d /drivers/usb | |
parent | 3d62e3fdce8ef265a3706c52ae1ca6ab84e30f0e (diff) | |
parent | e26bcf37234c67624f62d9fc95f922b8dbda1363 (diff) | |
download | linux-7a9b149212f3716c598afe973b6261fd58453b7a.tar.xz |
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
Diffstat (limited to 'drivers/usb')
176 files changed, 14096 insertions, 8598 deletions
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 25f01b5..e213d3f 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -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}, {} }; diff --git a/drivers/usb/c67x00/c67x00-hcd.h b/drivers/usb/c67x00/c67x00-hcd.h index e8c6d94..74e4462 100644 --- a/drivers/usb/c67x00/c67x00-hcd.h +++ b/drivers/usb/c67x00/c67x00-hcd.h @@ -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" /* diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5e1a253..0c2f14f 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -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) diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 4a8e87e..5eeb570 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -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 */ }; diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 189141c..094c76b 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -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); diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 93b5f85..2250095 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -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 */ diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 3ba2fff..2c69654 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -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> /* diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 0d3af6a..83126b0 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -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. + + /* 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. */ - return 0; + 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, 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"); + 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; - /* 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; - } + /* 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); + + /* 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: diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 19bc03a..3449742 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -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; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 3466fdc..c2f62a3 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -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 diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 2f3dc4c..ded550e 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -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. diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 2c95153..9a34ccb 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -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) { diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 1528653..1cf2d1e 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -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 */ diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 2f8cedd..12742f1 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -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)) + if (is_root_hub(urb->dev)) { status = rh_urb_enqueue(hcd, urb); - else - status = hcd->driver->urb_enqueue(hcd, urb, mem_flags); + } 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 (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 diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h deleted file mode 100644 index a3cdb09..0000000 --- a/drivers/usb/core/hcd.h +++ /dev/null @@ -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 */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0940ccd..83e7bbb 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -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; diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h deleted file mode 100644 index de8081f..0000000 --- a/drivers/usb/core/hub.h +++ /dev/null @@ -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 */ diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 111a01a..1a27618 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -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) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index cd22027..a73e08f 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -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; - goto nomem; - } + for_each_sg(sg, sg, io->entries, i) { + struct urb *urb; + unsigned len; - io->urbs[0]->dev = NULL; - io->urbs[0]->pipe = pipe; - io->urbs[0]->interval = period; - io->urbs[0]->transfer_flags = urb_flags; - - 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); - } + urb = usb_alloc_urb(0, mem_flags); + if (!urb) { + io->entries = i; + goto nomem; } - 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] = urb; + + urb->dev = NULL; + urb->pipe = pipe; + urb->interval = period; + urb->transfer_flags = urb_flags; + urb->complete = sg_complete; + urb->context = io; + urb->sg = 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[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; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index f073c5c..f22d03d 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -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 }, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 43c002e..06863be 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -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); diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 45a32da..7c05555 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -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) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 0561430..397b678 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -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 diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 11a3e0f..649c0c5 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -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 diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 43b51da..9bcde11 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -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 diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 75a256f..d623c7b 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -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); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 09289bb..391d169 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -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; diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index 47e8e72..09084fd 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -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, diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 5e09664..4f9e578 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -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" diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 3568de2..8a83248 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -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; diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 400e1eb..d47a123 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -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; diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 4e59532..544257a8 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -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; diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c new file mode 100644 index 0000000..d69eccf --- /dev/null +++ b/drivers/usb/gadget/f_fs.c @@ -0,0 +1,2442 @@ +/* + * f_fs.c -- user mode filesystem api for usb composite funtcion controllers + * + * Copyright (C) 2010 Samsung Electronics + * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> + * + * Based on inode.c (GadgetFS): + * Copyright (C) 2003-2004 David Brownell + * Copyright (C) 2003 Agilent Technologies + * + * 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 + */ + + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include <linux/blkdev.h> +#include <linux/pagemap.h> +#include <asm/unaligned.h> +#include <linux/smp_lock.h> + +#include <linux/usb/composite.h> +#include <linux/usb/functionfs.h> + + +#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ + + +/* Debuging *****************************************************************/ + +#define ffs_printk(level, fmt, args...) printk(level "f_fs: " fmt "\n", ## args) + +#define FERR(...) ffs_printk(KERN_ERR, __VA_ARGS__) +#define FINFO(...) ffs_printk(KERN_INFO, __VA_ARGS__) + +#ifdef DEBUG +# define FDBG(...) ffs_printk(KERN_DEBUG, __VA_ARGS__) +#else +# define FDBG(...) do { } while (0) +#endif /* DEBUG */ + +#ifdef VERBOSE_DEBUG +# define FVDBG FDBG +#else +# define FVDBG(...) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +#define ENTER() FVDBG("%s()", __func__) + +#ifdef VERBOSE_DEBUG +# define ffs_dump_mem(prefix, ptr, len) \ + print_hex_dump_bytes("f_fs" prefix ": ", DUMP_PREFIX_NONE, ptr, len) +#else +# define ffs_dump_mem(prefix, ptr, len) do { } while (0) +#endif + + +/* The data structure and setup file ****************************************/ + +enum ffs_state { + /* Waiting for descriptors and strings. */ + /* In this state no open(2), read(2) or write(2) on epfiles + * may succeed (which should not be the problem as there + * should be no such files opened in the firts place). */ + FFS_READ_DESCRIPTORS, + FFS_READ_STRINGS, + + /* We've got descriptors and strings. We are or have called + * functionfs_ready_callback(). functionfs_bind() may have + * been called but we don't know. */ + /* This is the only state in which operations on epfiles may + * succeed. */ + FFS_ACTIVE, + + /* All endpoints have been closed. This state is also set if + * we encounter an unrecoverable error. The only + * unrecoverable error is situation when after reading strings + * from user space we fail to initialise EP files or + * functionfs_ready_callback() returns with error (<0). */ + /* In this state no open(2), read(2) or write(2) (both on ep0 + * as well as epfile) may succeed (at this point epfiles are + * unlinked and all closed so this is not a problem; ep0 is + * also closed but ep0 file exists and so open(2) on ep0 must + * fail). */ + FFS_CLOSING +}; + + +enum ffs_setup_state { + /* There is no setup request pending. */ + FFS_NO_SETUP, + /* User has read events and there was a setup request event + * there. The next read/write on ep0 will handle the + * request. */ + FFS_SETUP_PENDING, + /* There was event pending but before user space handled it + * some other event was introduced which canceled existing + * setup. If this state is set read/write on ep0 return + * -EIDRM. This state is only set when adding event. */ + FFS_SETUP_CANCELED +}; + + + +struct ffs_epfile; +struct ffs_function; + +struct ffs_data { + struct usb_gadget *gadget; + + /* Protect access read/write operations, only one read/write + * at a time. As a consequence protects ep0req and company. + * While setup request is being processed (queued) this is + * held. */ + struct mutex mutex; + + /* Protect access to enpoint related structures (basically + * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for + * endpint zero. */ + spinlock_t eps_lock; + + /* XXX REVISIT do we need our own request? Since we are not + * handling setup requests immidiatelly user space may be so + * slow that another setup will be sent to the gadget but this + * time not to us but another function and then there could be + * a race. Is taht the case? Or maybe we can use cdev->req + * after all, maybe we just need some spinlock for that? */ + struct usb_request *ep0req; /* P: mutex */ + struct completion ep0req_completion; /* P: mutex */ + int ep0req_status; /* P: mutex */ + + /* reference counter */ + atomic_t ref; + /* how many files are opened (EP0 and others) */ + atomic_t opened; + + /* EP0 state */ + enum ffs_state state; + + /* + * Possible transations: + * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock + * happens only in ep0 read which is P: mutex + * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock + * happens only in ep0 i/o which is P: mutex + * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock + * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg + */ + enum ffs_setup_state setup_state; + +#define FFS_SETUP_STATE(ffs) \ + ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \ + FFS_SETUP_CANCELED, FFS_NO_SETUP)) + + /* Events & such. */ + struct { + u8 types[4]; + unsigned short count; + /* XXX REVISIT need to update it in some places, or do we? */ + unsigned short can_stall; + struct usb_ctrlrequest setup; + + wait_queue_head_t waitq; + } ev; /* the whole structure, P: ev.waitq.lock */ + + /* Flags */ + unsigned long flags; +#define FFS_FL_CALL_CLOSED_CALLBACK 0 +#define FFS_FL_BOUND 1 + + /* Active function */ + struct ffs_function *func; + + /* Device name, write once when file system is mounted. + * Intendet for user to read if she wants. */ + const char *dev_name; + /* Private data for our user (ie. gadget). Managed by + * user. */ + void *private_data; + + /* filled by __ffs_data_got_descs() */ + /* real descriptors are 16 bytes after raw_descs (so you need + * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the + * first full speed descriptor). raw_descs_length and + * raw_fs_descs_length do not have those 16 bytes added. */ + const void *raw_descs; + unsigned raw_descs_length; + unsigned raw_fs_descs_length; + unsigned fs_descs_count; + unsigned hs_descs_count; + + unsigned short strings_count; + unsigned short interfaces_count; + unsigned short eps_count; + unsigned short _pad1; + + /* filled by __ffs_data_got_strings() */ + /* ids in stringtabs are set in functionfs_bind() */ + const void *raw_strings; + struct usb_gadget_strings **stringtabs; + + /* File system's super block, write once when file system is mounted. */ + struct super_block *sb; + + /* File permissions, written once when fs is mounted*/ + struct ffs_file_perms { + umode_t mode; + uid_t uid; + gid_t gid; + } file_perms; + + /* The endpoint files, filled by ffs_epfiles_create(), + * destroyed by ffs_epfiles_destroy(). */ + struct ffs_epfile *epfiles; +}; + +/* Reference counter handling */ +static void ffs_data_get(struct ffs_data *ffs); +static void ffs_data_put(struct ffs_data *ffs); +/* Creates new ffs_data object. */ +static struct ffs_data *__must_check ffs_data_new(void) __attribute__((malloc)); + +/* Opened counter handling. */ +static void ffs_data_opened(struct ffs_data *ffs); +static void ffs_data_closed(struct ffs_data *ffs); + +/* Called with ffs->mutex held; take over ownerrship of data. */ +static int __must_check +__ffs_data_got_descs(struct ffs_data *ffs, char *data, size_t len); +static int __must_check +__ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len); + + +/* The function structure ***************************************************/ + +struct ffs_ep; + +struct ffs_function { + struct usb_configuration *conf; + struct usb_gadget *gadget; + struct ffs_data *ffs; + + struct ffs_ep *eps; + u8 eps_revmap[16]; + short *interfaces_nums; + + struct usb_function function; +}; + + +static struct ffs_function *ffs_func_from_usb(struct usb_function *f) +{ + return container_of(f, struct ffs_function, function); +} + +static void ffs_func_free(struct ffs_function *func); + + +static void ffs_func_eps_disable(struct ffs_function *func); +static int __must_check ffs_func_eps_enable(struct ffs_function *func); + + +static int ffs_func_bind(struct usb_configuration *, + struct usb_function *); +static void ffs_func_unbind(struct usb_configuration *, + struct usb_function *); +static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned); +static void ffs_func_disable(struct usb_function *); +static int ffs_func_setup(struct usb_function *, + const struct usb_ctrlrequest *); +static void ffs_func_suspend(struct usb_function *); +static void ffs_func_resume(struct usb_function *); + + +static int ffs_func_revmap_ep(struct ffs_function *func, u8 num); +static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf); + + + +/* The endpoints structures *************************************************/ + +struct ffs_ep { + struct usb_ep *ep; /* P: ffs->eps_lock */ + struct usb_request *req; /* P: epfile->mutex */ + + /* [0]: full speed, [1]: high speed */ + struct usb_endpoint_descriptor *descs[2]; + + u8 num; + + int status; /* P: epfile->mutex */ +}; + +struct ffs_epfile { + /* Protects ep->ep and ep->req. */ + struct mutex mutex; + wait_queue_head_t wait; + + struct ffs_data *ffs; + struct ffs_ep *ep; /* P: ffs->eps_lock */ + + struct dentry *dentry; + + char name[5]; + + unsigned char in; /* P: ffs->eps_lock */ + unsigned char isoc; /* P: ffs->eps_lock */ + + unsigned char _pad; +}; + + +static int __must_check ffs_epfiles_create(struct ffs_data *ffs); +static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count); + +static struct inode *__must_check +ffs_sb_create_file(struct super_block *sb, const char *name, void *data, + const struct file_operations *fops, + struct dentry **dentry_p); + + +/* Misc helper functions ****************************************************/ + +static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock) + __attribute__((warn_unused_result, nonnull)); +static char *ffs_prepare_buffer(const char * __user buf, size_t len) + __attribute__((warn_unused_result, nonnull)); + + +/* Control file aka ep0 *****************************************************/ + +static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct ffs_data *ffs = req->context; + + complete_all(&ffs->ep0req_completion); +} + + +static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len) +{ + struct usb_request *req = ffs->ep0req; + int ret; + + req->zero = len < le16_to_cpu(ffs->ev.setup.wLength); + + spin_unlock_irq(&ffs->ev.waitq.lock); + + req->buf = data; + req->length = len; + + INIT_COMPLETION(ffs->ep0req_completion); + + ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC); + if (unlikely(ret < 0)) + return ret; + + ret = wait_for_completion_interruptible(&ffs->ep0req_completion); + if (unlikely(ret)) { + usb_ep_dequeue(ffs->gadget->ep0, req); + return -EINTR; + } + + ffs->setup_state = FFS_NO_SETUP; + return ffs->ep0req_status; +} + +static int __ffs_ep0_stall(struct ffs_data *ffs) +{ + if (ffs->ev.can_stall) { + FVDBG("ep0 stall\n"); + usb_ep_set_halt(ffs->gadget->ep0); + ffs->setup_state = FFS_NO_SETUP; + return -EL2HLT; + } else { + FDBG("bogus ep0 stall!\n"); + return -ESRCH; + } +} + + +static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, + size_t len, loff_t *ptr) +{ + struct ffs_data *ffs = file->private_data; + ssize_t ret; + char *data; + + ENTER(); + + /* Fast check if setup was canceled */ + if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) + return -EIDRM; + + /* Acquire mutex */ + ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); + if (unlikely(ret < 0)) + return ret; + + + /* Check state */ + switch (ffs->state) { + case FFS_READ_DESCRIPTORS: + case FFS_READ_STRINGS: + /* Copy data */ + if (unlikely(len < 16)) { + ret = -EINVAL; + break; + } + + data = ffs_prepare_buffer(buf, len); + if (unlikely(IS_ERR(data))) { + ret = PTR_ERR(data); + break; + } + + /* Handle data */ + if (ffs->state == FFS_READ_DESCRIPTORS) { + FINFO("read descriptors"); + ret = __ffs_data_got_descs(ffs, data, len); + if (unlikely(ret < 0)) + break; + + ffs->state = FFS_READ_STRINGS; + ret = len; + } else { + FINFO("read strings"); + ret = __ffs_data_got_strings(ffs, data, len); + if (unlikely(ret < 0)) + break; + + ret = ffs_epfiles_create(ffs); + if (unlikely(ret)) { + ffs->state = FFS_CLOSING; + break; + } + + ffs->state = FFS_ACTIVE; + mutex_unlock(&ffs->mutex); + + ret = functionfs_ready_callback(ffs); + if (unlikely(ret < 0)) { + ffs->state = FFS_CLOSING; + return ret; + } + + set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags); + return len; + } + break; + + + case FFS_ACTIVE: + data = NULL; + /* We're called from user space, we can use _irq + * rather then _irqsave */ + spin_lock_irq(&ffs->ev.waitq.lock); + switch (FFS_SETUP_STATE(ffs)) { + case FFS_SETUP_CANCELED: + ret = -EIDRM; + goto done_spin; + + case FFS_NO_SETUP: + ret = -ESRCH; + goto done_spin; + + case FFS_SETUP_PENDING: + break; + } + + /* FFS_SETUP_PENDING */ + if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) { + spin_unlock_irq(&ffs->ev.waitq.lock); + ret = __ffs_ep0_stall(ffs); + break; + } + + /* FFS_SETUP_PENDING and not stall */ + len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength)); + + spin_unlock_irq(&ffs->ev.waitq.lock); + + data = ffs_prepare_buffer(buf, len); + if (unlikely(IS_ERR(data))) { + ret = PTR_ERR(data); + break; + } + + spin_lock_irq(&ffs->ev.waitq.lock); + + /* We are guaranteed to be still in FFS_ACTIVE state + * but the state of setup could have changed from + * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need + * to check for that. If that happened we copied data + * from user space in vain but it's unlikely. */ + /* For sure we are not in FFS_NO_SETUP since this is + * the only place FFS_SETUP_PENDING -> FFS_NO_SETUP + * transition can be performed and it's protected by + * mutex. */ + + if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) { + ret = -EIDRM; +done_spin: + spin_unlock_irq(&ffs->ev.waitq.lock); + } else { + /* unlocks spinlock */ + ret = __ffs_ep0_queue_wait(ffs, data, len); + } + kfree(data); + break; + + + default: + ret = -EBADFD; + break; + } + + + mutex_unlock(&ffs->mutex); + return ret; +} + + + +static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf, + size_t n) +{ + /* We are holding ffs->ev.waitq.lock and ffs->mutex and we need + * to release them. */ + + struct usb_functionfs_event events[n]; + unsigned i = 0; + + memset(events, 0, sizeof events); + + do { + events[i].type = ffs->ev.types[i]; + if (events[i].type == FUNCTIONFS_SETUP) { + events[i].u.setup = ffs->ev.setup; + ffs->setup_state = FFS_SETUP_PENDING; + } + } while (++i < n); + + if (n < ffs->ev.count) { + ffs->ev.count -= n; + memmove(ffs->ev.types, ffs->ev.types + n, + ffs->ev.count * sizeof *ffs->ev.types); + } else { + ffs->ev.count = 0; + } + + spin_unlock_irq(&ffs->ev.waitq.lock); + mutex_unlock(&ffs->mutex); + + return unlikely(__copy_to_user(buf, events, sizeof events)) + ? -EFAULT : sizeof events; +} + + +static ssize_t ffs_ep0_read(struct file *file, char __user *buf, + size_t len, loff_t *ptr) +{ + struct ffs_data *ffs = file->private_data; + char *data = NULL; + size_t n; + int ret; + + ENTER(); + + /* Fast check if setup was canceled */ + if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) + return -EIDRM; + + /* Acquire mutex */ + ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); + if (unlikely(ret < 0)) + return ret; + + + /* Check state */ + if (ffs->state != FFS_ACTIVE) { + ret = -EBADFD; + goto done_mutex; + } + + + /* We're called from user space, we can use _irq rather then + * _irqsave */ + spin_lock_irq(&ffs->ev.waitq.lock); + + switch (FFS_SETUP_STATE(ffs)) { + case FFS_SETUP_CANCELED: + ret = -EIDRM; + break; + + case FFS_NO_SETUP: + n = len / sizeof(struct usb_functionfs_event); + if (unlikely(!n)) { + ret = -EINVAL; + break; + } + + if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) { + ret = -EAGAIN; + break; + } + + if (unlikely(wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq, ffs->ev.count))) { + ret = -EINTR; + break; + } + + return __ffs_ep0_read_events(ffs, buf, + min(n, (size_t)ffs->ev.count)); + + + case FFS_SETUP_PENDING: + if (ffs->ev.setup.bRequestType & USB_DIR_IN) { + spin_unlock_irq(&ffs->ev.waitq.lock); + ret = __ffs_ep0_stall(ffs); + goto done_mutex; + } + + len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength)); + + spin_unlock_irq(&ffs->ev.waitq.lock); + + if (likely(len)) { + data = kmalloc(len, GFP_KERNEL); + if (unlikely(!data)) { + ret = -ENOMEM; + goto done_mutex; + } + } + + spin_lock_irq(&ffs->ev.waitq.lock); + + /* See ffs_ep0_write() */ + if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) { + ret = -EIDRM; + break; + } + + /* unlocks spinlock */ + ret = __ffs_ep0_queue_wait(ffs, data, len); + if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len))) + ret = -EFAULT; + goto done_mutex; + + default: + ret = -EBADFD; + break; + } + + spin_unlock_irq(&ffs->ev.waitq.lock); +done_mutex: + mutex_unlock(&ffs->mutex); + kfree(data); + return ret; +} + + + +static int ffs_ep0_open(struct inode *inode, struct file *file) +{ + struct ffs_data *ffs = inode->i_private; + + ENTER(); + + if (unlikely(ffs->state == FFS_CLOSING)) + return -EBUSY; + + file->private_data = ffs; + ffs_data_opened(ffs); + + return 0; +} + + +static int ffs_ep0_release(struct inode *inode, struct file *file) +{ + struct ffs_data *ffs = file->private_data; + + ENTER(); + + ffs_data_closed(ffs); + + return 0; +} + + +static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value) +{ + struct ffs_data *ffs = file->private_data; + struct usb_gadget *gadget = ffs->gadget; + long ret; + + ENTER(); + + if (code == FUNCTIONFS_INTERFACE_REVMAP) { + struct ffs_function *func = ffs->func; + ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV; + } else if (gadget->ops->ioctl) { + lock_kernel(); + ret = gadget->ops->ioctl(gadget, code, value); + unlock_kernel(); + } else { + ret = -ENOTTY; + } + + return ret; +} + + +static const struct file_operations ffs_ep0_operations = { + .owner = THIS_MODULE, + .llseek = no_llseek, + + .open = ffs_ep0_open, + .write = ffs_ep0_write, + .read = ffs_ep0_read, + .release = ffs_ep0_release, + .unlocked_ioctl = ffs_ep0_ioctl, +}; + + +/* "Normal" endpoints operations ********************************************/ + + +static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) +{ + ENTER(); + if (likely(req->context)) { + struct ffs_ep *ep = _ep->driver_data; + ep->status = req->status ? req->status : req->actual; + complete(req->context); + } +} + + +static ssize_t ffs_epfile_io(struct file *file, + char __user *buf, size_t len, int read) +{ + struct ffs_epfile *epfile = file->private_data; + struct ffs_ep *ep; + char *data = NULL; + ssize_t ret; + int halt; + + goto first_try; + do { + spin_unlock_irq(&epfile->ffs->eps_lock); + mutex_unlock(&epfile->mutex); + +first_try: + /* Are we still active? */ + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) { + ret = -ENODEV; + goto error; + } + + /* Wait for endpoint to be enabled */ + ep = epfile->ep; + if (!ep) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto error; + } + + if (unlikely(wait_event_interruptible + (epfile->wait, (ep = epfile->ep)))) { + ret = -EINTR; + goto error; + } + } + + /* Do we halt? */ + halt = !read == !epfile->in; + if (halt && epfile->isoc) { + ret = -EINVAL; + goto error; + } + + /* Allocate & copy */ + if (!halt && !data) { + data = kzalloc(len, GFP_KERNEL); + if (unlikely(!data)) + return -ENOMEM; + + if (!read && + unlikely(__copy_from_user(data, buf, len))) { + ret = -EFAULT; + goto error; + } + } + + /* We will be using request */ + ret = ffs_mutex_lock(&epfile->mutex, + file->f_flags & O_NONBLOCK); + if (unlikely(ret)) + goto error; + + /* We're called from user space, we can use _irq rather then + * _irqsave */ + spin_lock_irq(&epfile->ffs->eps_lock); + + /* While we were acquiring mutex endpoint got disabled + * or changed? */ + } while (unlikely(epfile->ep != ep)); + + /* Halt */ + if (unlikely(halt)) { + if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep)) + usb_ep_set_halt(ep->ep); + spin_unlock_irq(&epfile->ffs->eps_lock); + ret = -EBADMSG; + } else { + /* Fire the request */ + DECLARE_COMPLETION_ONSTACK(done); + + struct usb_request *req = ep->req; + req->context = &done; + req->complete = ffs_epfile_io_complete; + req->buf = data; + req->length = len; + + ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + + spin_unlock_irq(&epfile->ffs->eps_lock); + + if (unlikely(ret < 0)) { + /* nop */ + } else if (unlikely(wait_for_completion_interruptible(&done))) { + ret = -EINTR; + usb_ep_dequeue(ep->ep, req); + } else { + ret = ep->status; + if (read && ret > 0 && + unlikely(copy_to_user(buf, data, ret))) + ret = -EFAULT; + } + } + + mutex_unlock(&epfile->mutex); +error: + kfree(data); + return ret; +} + + +static ssize_t +ffs_epfile_write(struct file *file, const char __user *buf, size_t len, + loff_t *ptr) +{ + ENTER(); + + return ffs_epfile_io(file, (char __user *)buf, len, 0); +} + +static ssize_t +ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr) +{ + ENTER(); + + return ffs_epfile_io(file, buf, len, 1); +} + +static int +ffs_epfile_open(struct inode *inode, struct file *file) +{ + struct ffs_epfile *epfile = inode->i_private; + + ENTER(); + + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) + return -ENODEV; + + file->private_data = epfile; + ffs_data_opened(epfile->ffs); + + return 0; +} + +static int +ffs_epfile_release(struct inode *inode, struct file *file) +{ + struct ffs_epfile *epfile = inode->i_private; + + ENTER(); + + ffs_data_closed(epfile->ffs); + + return 0; +} + + +static long ffs_epfile_ioctl(struct file *file, unsigned code, + unsigned long value) +{ + struct ffs_epfile *epfile = file->private_data; + int ret; + + ENTER(); + + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) + return -ENODEV; + + spin_lock_irq(&epfile->ffs->eps_lock); + if (likely(epfile->ep)) { + switch (code) { + case FUNCTIONFS_FIFO_STATUS: + ret = usb_ep_fifo_status(epfile->ep->ep); + break; + case FUNCTIONFS_FIFO_FLUSH: + usb_ep_fifo_flush(epfile->ep->ep); + ret = 0; + break; + case FUNCTIONFS_CLEAR_HALT: + ret = usb_ep_clear_halt(epfile->ep->ep); + break; + case FUNCTIONFS_ENDPOINT_REVMAP: + ret = epfile->ep->num; + break; + default: + ret = -ENOTTY; + } + } else { + ret = -ENODEV; + } + spin_unlock_irq(&epfile->ffs->eps_lock); + + return ret; +} + + +static const struct file_operations ffs_epfile_operations = { + .owner = THIS_MODULE, + .llseek = no_llseek, + + .open = ffs_epfile_open, + .write = ffs_epfile_write, + .read = ffs_epfile_read, + .release = ffs_epfile_release, + .unlocked_ioctl = ffs_epfile_ioctl, +}; + + + +/* File system and super block operations ***********************************/ + +/* + * Mounting the filesystem creates a controller file, used first for + * function configuration then later for event monitoring. + */ + + +static struct inode *__must_check +ffs_sb_make_inode(struct super_block *sb, void *data, + const struct file_operations *fops, + const struct inode_operations *iops, + struct ffs_file_perms *perms) +{ + struct inode *inode; + + ENTER(); + + inode = new_inode(sb); + + if (likely(inode)) { + struct timespec current_time = CURRENT_TIME; + + inode->i_mode = perms->mode; + inode->i_uid = perms->uid; + inode->i_gid = perms->gid; + inode->i_atime = current_time; + inode->i_mtime = current_time; + inode->i_ctime = current_time; + inode->i_private = data; + if (fops) + inode->i_fop = fops; + if (iops) + inode->i_op = iops; + } + + return inode; +} + + +/* Create "regular" file */ + +static struct inode *ffs_sb_create_file(struct super_block *sb, + const char *name, void *data, + const struct file_operations *fops, + struct dentry **dentry_p) +{ + struct ffs_data *ffs = sb->s_fs_info; + struct dentry *dentry; + struct inode *inode; + + ENTER(); + + dentry = d_alloc_name(sb->s_root, name); + if (unlikely(!dentry)) + return NULL; + + inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms); + if (unlikely(!inode)) { + dput(dentry); + return NULL; + } + + d_add(dentry, inode); + if (dentry_p) + *dentry_p = dentry; + + return inode; +} + + +/* Super block */ + +static const struct super_operations ffs_sb_operations = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, +}; + +struct ffs_sb_fill_data { + struct ffs_file_perms perms; + umode_t root_mode; + const char *dev_name; +}; + +static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) +{ + struct ffs_sb_fill_data *data = _data; + struct inode *inode; + struct dentry *d; + struct ffs_data *ffs; + + ENTER(); + + /* Initialize data */ + ffs = ffs_data_new(); + if (unlikely(!ffs)) + goto enomem0; + + ffs->sb = sb; + ffs->dev_name = data->dev_name; + ffs->file_perms = data->perms; + + sb->s_fs_info = ffs; + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = FUNCTIONFS_MAGIC; + sb->s_op = &ffs_sb_operations; + sb->s_time_gran = 1; + + /* Root inode */ + data->perms.mode = data->root_mode; + inode = ffs_sb_make_inode(sb, NULL, + &simple_dir_operations, + &simple_dir_inode_operations, + &data->perms); + if (unlikely(!inode)) + goto enomem1; + d = d_alloc_root(inode); + if (unlikely(!d)) + goto enomem2; + sb->s_root = d; + + /* EP0 file */ + if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs, + &ffs_ep0_operations, NULL))) + goto enomem3; + + return 0; + +enomem3: + dput(d); +enomem2: + iput(inode); +enomem1: + ffs_data_put(ffs); +enomem0: + return -ENOMEM; +} + + +static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) +{ + ENTER(); + + if (!opts || !*opts) + return 0; + + for (;;) { + char *end, *eq, *comma; + unsigned long value; + + /* Option limit */ + comma = strchr(opts, ','); + if (comma) + *comma = 0; + + /* Value limit */ + eq = strchr(opts, '='); + if (unlikely(!eq)) { + FERR("'=' missing in %s", opts); + return -EINVAL; + } + *eq = 0; + + /* Parse value */ + value = simple_strtoul(eq + 1, &end, 0); + if (unlikely(*end != ',' && *end != 0)) { + FERR("%s: invalid value: %s", opts, eq + 1); + return -EINVAL; + } + + /* Interpret option */ + switch (eq - opts) { + case 5: + if (!memcmp(opts, "rmode", 5)) + data->root_mode = (value & 0555) | S_IFDIR; + else if (!memcmp(opts, "fmode", 5)) + data->perms.mode = (value & 0666) | S_IFREG; + else + goto invalid; + break; + + case 4: + if (!memcmp(opts, "mode", 4)) { + data->root_mode = (value & 0555) | S_IFDIR; + data->perms.mode = (value & 0666) | S_IFREG; + } else { + goto invalid; + } + break; + + case 3: + if (!memcmp(opts, "uid", 3)) + data->perms.uid = value; + else if (!memcmp(opts, "gid", 3)) + data->perms.gid = value; + else + goto invalid; + break; + + default: +invalid: + FERR("%s: invalid option", opts); + return -EINVAL; + } + + /* Next iteration */ + if (!comma) + break; + opts = comma + 1; + } + + return 0; +} + + +/* "mount -t functionfs dev_name /dev/function" ends up here */ + +static int +ffs_fs_get_sb(struct file_system_type *t, int flags, + const char *dev_name, void *opts, struct vfsmount *mnt) +{ + struct ffs_sb_fill_data data = { + .perms = { + .mode = S_IFREG | 0600, + .uid = 0, + .gid = 0 + }, + .root_mode = S_IFDIR | 0500, + }; + int ret; + + ENTER(); + + ret = functionfs_check_dev_callback(dev_name); + if (unlikely(ret < 0)) + return ret; + + ret = ffs_fs_parse_opts(&data, opts); + if (unlikely(ret < 0)) + return ret; + + data.dev_name = dev_name; + return get_sb_single(t, flags, &data, ffs_sb_fill, mnt); +} + +static void +ffs_fs_kill_sb(struct super_block *sb) +{ + void *ptr; + + ENTER(); + + kill_litter_super(sb); + ptr = xchg(&sb->s_fs_info, NULL); + if (ptr) + ffs_data_put(ptr); +} + +static struct file_system_type ffs_fs_type = { + .owner = THIS_MODULE, + .name = "functionfs", + .get_sb = ffs_fs_get_sb, + .kill_sb = ffs_fs_kill_sb, +}; + + + +/* Driver's main init/cleanup functions *************************************/ + + +static int functionfs_init(void) +{ + int ret; + + ENTER(); + + ret = register_filesystem(&ffs_fs_type); + if (likely(!ret)) + FINFO("file system registered"); + else + FERR("failed registering file system (%d)", ret); + + return ret; +} + +static void functionfs_cleanup(void) +{ + ENTER(); + + FINFO("unloading"); + unregister_filesystem(&ffs_fs_type); +} + + + +/* ffs_data and ffs_function construction and destruction code **************/ + +static void ffs_data_clear(struct ffs_data *ffs); +static void ffs_data_reset(struct ffs_data *ffs); + + +static void ffs_data_get(struct ffs_data *ffs) +{ + ENTER(); + + atomic_inc(&ffs->ref); +} + +static void ffs_data_opened(struct ffs_data *ffs) +{ + ENTER(); + + atomic_inc(&ffs->ref); + atomic_inc(&ffs->opened); +} + +static void ffs_data_put(struct ffs_data *ffs) +{ + ENTER(); + + if (unlikely(atomic_dec_and_test(&ffs->ref))) { + FINFO("%s(): freeing", __func__); + ffs_data_clear(ffs); + BUG_ON(mutex_is_locked(&ffs->mutex) || + spin_is_locked(&ffs->ev.waitq.lock) || + waitqueue_active(&ffs->ev.waitq) || + waitqueue_active(&ffs->ep0req_completion.wait)); + kfree(ffs); + } +} + + + +static void ffs_data_closed(struct ffs_data *ffs) +{ + ENTER(); + + if (atomic_dec_and_test(&ffs->opened)) { + ffs->state = FFS_CLOSING; + ffs_data_reset(ffs); + } + + ffs_data_put(ffs); +} + + +static struct ffs_data *ffs_data_new(void) +{ + struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL); + if (unlikely(!ffs)) + return 0; + + ENTER(); + + atomic_set(&ffs->ref, 1); + atomic_set(&ffs->opened, 0); + ffs->state = FFS_READ_DESCRIPTORS; + mutex_init(&ffs->mutex); + spin_lock_init(&ffs->eps_lock); + init_waitqueue_head(&ffs->ev.waitq); + init_completion(&ffs->ep0req_completion); + + /* XXX REVISIT need to update it in some places, or do we? */ + ffs->ev.can_stall = 1; + + return ffs; +} + + +static void ffs_data_clear(struct ffs_data *ffs) +{ + ENTER(); + + if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags)) + functionfs_closed_callback(ffs); + + BUG_ON(ffs->gadget); + + if (ffs->epfiles) + ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count); + + kfree(ffs->raw_descs); + kfree(ffs->raw_strings); + kfree(ffs->stringtabs); +} + + +static void ffs_data_reset(struct ffs_data *ffs) +{ + ENTER(); + + ffs_data_clear(ffs); + + ffs->epfiles = NULL; + ffs->raw_descs = NULL; + ffs->raw_strings = NULL; + ffs->stringtabs = NULL; + + ffs->raw_descs_length = 0; + ffs->raw_fs_descs_length = 0; + ffs->fs_descs_count = 0; + ffs->hs_descs_count = 0; + + ffs->strings_count = 0; + ffs->interfaces_count = 0; + ffs->eps_count = 0; + + ffs->ev.count = 0; + + ffs->state = FFS_READ_DESCRIPTORS; + ffs->setup_state = FFS_NO_SETUP; + ffs->flags = 0; +} + + +static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) +{ + unsigned i, count; + + ENTER(); + + if (WARN_ON(ffs->state != FFS_ACTIVE + || test_and_set_bit(FFS_FL_BOUND, &ffs->flags))) + return -EBADFD; + + ffs_data_get(ffs); + + ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); + if (unlikely(!ffs->ep0req)) + return -ENOMEM; + ffs->ep0req->complete = ffs_ep0_complete; + ffs->ep0req->context = ffs; + + /* Get strings identifiers */ + for (count = ffs->strings_count, i = 0; i < count; ++i) { + struct usb_gadget_strings **lang; + + int id = usb_string_id(cdev); + if (unlikely(id < 0)) { + usb_ep_free_request(cdev->gadget->ep0, ffs->ep0req); + ffs->ep0req = NULL; + return id; + } + + lang = ffs->stringtabs; + do { + (*lang)->strings[i].id = id; + ++lang; + } while (*lang); + } + + ffs->gadget = cdev->gadget; + return 0; +} + + +static void functionfs_unbind(struct ffs_data *ffs) +{ + ENTER(); + + if (!WARN_ON(!ffs->gadget)) { + usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req); + ffs->ep0req = NULL; + ffs->gadget = NULL; + ffs_data_put(ffs); + } +} + + +static int ffs_epfiles_create(struct ffs_data *ffs) +{ + struct ffs_epfile *epfile, *epfiles; + unsigned i, count; + + ENTER(); + + count = ffs->eps_count; + epfiles = kzalloc(count * sizeof *epfiles, GFP_KERNEL); + if (!epfiles) + return -ENOMEM; + + epfile = epfiles; + for (i = 1; i <= count; ++i, ++epfile) { + epfile->ffs = ffs; + mutex_init(&epfile->mutex); + init_waitqueue_head(&epfile->wait); + sprintf(epfiles->name, "ep%u", i); + if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile, + &ffs_epfile_operations, + &epfile->dentry))) { + ffs_epfiles_destroy(epfiles, i - 1); + return -ENOMEM; + } + } + + ffs->epfiles = epfiles; + return 0; +} + + +static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) +{ + struct ffs_epfile *epfile = epfiles; + + ENTER(); + + for (; count; --count, ++epfile) { + BUG_ON(mutex_is_locked(&epfile->mutex) || + waitqueue_active(&epfile->wait)); + if (epfile->dentry) { + d_delete(epfile->dentry); + dput(epfile->dentry); + epfile->dentry = NULL; + } + } + + kfree(epfiles); +} + + +static int functionfs_add(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct ffs_data *ffs) +{ + struct ffs_function *func; + int ret; + + ENTER(); + + func = kzalloc(sizeof *func, GFP_KERNEL); + if (unlikely(!func)) + return -ENOMEM; + + func->function.name = "Function FS Gadget"; + func->function.strings = ffs->stringtabs; + + func->function.bind = ffs_func_bind; + func->function.unbind = ffs_func_unbind; + func->function.set_alt = ffs_func_set_alt; + /*func->function.get_alt = ffs_func_get_alt;*/ + func->function.disable = ffs_func_disable; + func->function.setup = ffs_func_setup; + func->function.suspend = ffs_func_suspend; + func->function.resume = ffs_func_resume; + + func->conf = c; + func->gadget = cdev->gadget; + func->ffs = ffs; + ffs_data_get(ffs); + + ret = usb_add_function(c, &func->function); + if (unlikely(ret)) + ffs_func_free(func); + + return ret; +} + +static void ffs_func_free(struct ffs_function *func) +{ + ENTER(); + + ffs_data_put(func->ffs); + + kfree(func->eps); + /* eps and interfaces_nums are allocated in the same chunk so + * only one free is required. Descriptors are also allocated + * in the same chunk. */ + + kfree(func); +} + + +static void ffs_func_eps_disable(struct ffs_function *func) +{ + struct ffs_ep *ep = func->eps; + struct ffs_epfile *epfile = func->ffs->epfiles; + unsigned count = func->ffs->eps_count; + unsigned long flags; + + spin_lock_irqsave(&func->ffs->eps_lock, flags); + do { + /* pending requests get nuked */ + if (likely(ep->ep)) + usb_ep_disable(ep->ep); + epfile->ep = NULL; + + ++ep; + ++epfile; + } while (--count); + spin_unlock_irqrestore(&func->ffs->eps_lock, flags); +} + +static int ffs_func_eps_enable(struct ffs_function *func) +{ + struct ffs_data *ffs = func->ffs; + struct ffs_ep *ep = func->eps; + struct ffs_epfile *epfile = ffs->epfiles; + unsigned count = ffs->eps_count; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&func->ffs->eps_lock, flags); + do { + struct usb_endpoint_descriptor *ds; + ds = ep->descs[ep->descs[1] ? 1 : 0]; + + ep->ep->driver_data = ep; + ret = usb_ep_enable(ep->ep, ds); + if (likely(!ret)) { + epfile->ep = ep; + epfile->in = usb_endpoint_dir_in(ds); + epfile->isoc = usb_endpoint_xfer_isoc(ds); + } else { + break; + } + + wake_up(&epfile->wait); + + ++ep; + ++epfile; + } while (--count); + spin_unlock_irqrestore(&func->ffs->eps_lock, flags); + + return ret; +} + + +/* Parsing and building descriptors and strings *****************************/ + + +/* This validates if data pointed by data is a valid USB descriptor as + * well as record how many interfaces, endpoints and strings are + * required by given configuration. Returns address afther the + * descriptor or NULL if data is invalid. */ + +enum ffs_entity_type { + FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT +}; + +typedef int (*ffs_entity_callback)(enum ffs_entity_type entity, + u8 *valuep, + struct usb_descriptor_header *desc, + void *priv); + +static int __must_check ffs_do_desc(char *data, unsigned len, + ffs_entity_callback entity, void *priv) +{ + struct usb_descriptor_header *_ds = (void *)data; + u8 length; + int ret; + + ENTER(); + + /* At least two bytes are required: length and type */ + if (len < 2) { + FVDBG("descriptor too short"); + return -EINVAL; + } + + /* If we have at least as many bytes as the descriptor takes? */ + length = _ds->bLength; + if (len < length) { + FVDBG("descriptor longer then available data"); + return -EINVAL; + } + +#define __entity_check_INTERFACE(val) 1 +#define __entity_check_STRING(val) (val) +#define __entity_check_ENDPOINT(val) ((val) & USB_ENDPOINT_NUMBER_MASK) +#define __entity(type, val) do { \ + FVDBG("entity " #type "(%02x)", (val)); \ + if (unlikely(!__entity_check_ ##type(val))) { \ + FVDBG("invalid entity's value"); \ + return -EINVAL; \ + } \ + ret = entity(FFS_ ##type, &val, _ds, priv); \ + if (unlikely(ret < 0)) { \ + FDBG("entity " #type "(%02x); ret = %d", \ + (val), ret); \ + return ret; \ + } \ + } while (0) + + /* Parse descriptor depending on type. */ + switch (_ds->bDescriptorType) { + case USB_DT_DEVICE: + case USB_DT_CONFIG: + case USB_DT_STRING: + case USB_DT_DEVICE_QUALIFIER: + /* function can't have any of those */ + FVDBG("descriptor reserved for gadget: %d", _ds->bDescriptorType); + return -EINVAL; + + case USB_DT_INTERFACE: { + struct usb_interface_descriptor *ds = (void *)_ds; + FVDBG("interface descriptor"); + if (length != sizeof *ds) + goto inv_length; + + __entity(INTERFACE, ds->bInterfaceNumber); + if (ds->iInterface) + __entity(STRING, ds->iInterface); + } + break; + + case USB_DT_ENDPOINT: { + struct usb_endpoint_descriptor *ds = (void *)_ds; + FVDBG("endpoint descriptor"); + if (length != USB_DT_ENDPOINT_SIZE && + length != USB_DT_ENDPOINT_AUDIO_SIZE) + goto inv_length; + __entity(ENDPOINT, ds->bEndpointAddress); + } + break; + + case USB_DT_OTG: + if (length != sizeof(struct usb_otg_descriptor)) + goto inv_length; + break; + + case USB_DT_INTERFACE_ASSOCIATION: { + struct usb_interface_assoc_descriptor *ds = (void *)_ds; + FVDBG("interface association descriptor"); + if (length != sizeof *ds) + goto inv_length; + if (ds->iFunction) + __entity(STRING, ds->iFunction); + } + break; + + case USB_DT_OTHER_SPEED_CONFIG: + case USB_DT_INTERFACE_POWER: + case USB_DT_DEBUG: + case USB_DT_SECURITY: + case USB_DT_CS_RADIO_CONTROL: + /* TODO */ + FVDBG("unimplemented descriptor: %d", _ds->bDescriptorType); + return -EINVAL; + + default: + /* We should never be here */ + FVDBG("unknown descriptor: %d", _ds->bDescriptorType); + return -EINVAL; + + inv_length: + FVDBG("invalid length: %d (descriptor %d)", + _ds->bLength, _ds->bDescriptorType); + return -EINVAL; + } + +#undef __entity +#undef __entity_check_DESCRIPTOR +#undef __entity_check_INTERFACE +#undef __entity_check_STRING +#undef __entity_check_ENDPOINT + + return length; +} + + +static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, + ffs_entity_callback entity, void *priv) +{ + const unsigned _len = len; + unsigned long num = 0; + + ENTER(); + + for (;;) { + int ret; + + if (num == count) + data = NULL; + + /* Record "descriptor" entitny */ + ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv); + if (unlikely(ret < 0)) { + FDBG("entity DESCRIPTOR(%02lx); ret = %d", num, ret); + return ret; + } + + if (!data) + return _len - len; + + ret = ffs_do_desc(data, len, entity, priv); + if (unlikely(ret < 0)) { + FDBG("%s returns %d", __func__, ret); + return ret; + } + + len -= ret; + data += ret; + ++num; + } +} + + +static int __ffs_data_do_entity(enum ffs_entity_type type, + u8 *valuep, struct usb_descriptor_header *desc, + void *priv) +{ + struct ffs_data *ffs = priv; + + ENTER(); + + switch (type) { + case FFS_DESCRIPTOR: + break; + + case FFS_INTERFACE: + /* Interfaces are indexed from zero so if we + * encountered interface "n" then there are at least + * "n+1" interfaces. */ + if (*valuep >= ffs->interfaces_count) + ffs->interfaces_count = *valuep + 1; + break; + + case FFS_STRING: + /* Strings are indexed from 1 (0 is magic ;) reserved + * for languages list or some such) */ + if (*valuep > ffs->strings_count) + ffs->strings_count = *valuep; + break; + + case FFS_ENDPOINT: + /* Endpoints are indexed from 1 as well. */ + if ((*valuep & USB_ENDPOINT_NUMBER_MASK) > ffs->eps_count) + ffs->eps_count = (*valuep & USB_ENDPOINT_NUMBER_MASK); + break; + } + + return 0; +} + + +static int __ffs_data_got_descs(struct ffs_data *ffs, + char *const _data, size_t len) +{ + unsigned fs_count, hs_count; + int fs_len, ret = -EINVAL; + char *data = _data; + + ENTER(); + + if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC || + get_unaligned_le32(data + 4) != len)) + goto error; + fs_count = get_unaligned_le32(data + 8); + hs_count = get_unaligned_le32(data + 12); + + if (!fs_count && !hs_count) + goto einval; + + data += 16; + len -= 16; + + if (likely(fs_count)) { + fs_len = ffs_do_descs(fs_count, data, len, + __ffs_data_do_entity, ffs); + if (unlikely(fs_len < 0)) { + ret = fs_len; + goto error; + } + + data += fs_len; + len -= fs_len; + } else { + fs_len = 0; + } + + if (likely(hs_count)) { + ret = ffs_do_descs(hs_count, data, len, + __ffs_data_do_entity, ffs); + if (unlikely(ret < 0)) + goto error; + } else { + ret = 0; + } + + if (unlikely(len != ret)) + goto einval; + + ffs->raw_fs_descs_length = fs_len; + ffs->raw_descs_length = fs_len + ret; + ffs->raw_descs = _data; + ffs->fs_descs_count = fs_count; + ffs->hs_descs_count = hs_count; + + return 0; + +einval: + ret = -EINVAL; +error: + kfree(_data); + return ret; +} + + + +static int __ffs_data_got_strings(struct ffs_data *ffs, + char *const _data, size_t len) +{ + u32 str_count, needed_count, lang_count; + struct usb_gadget_strings **stringtabs, *t; + struct usb_string *strings, *s; + const char *data = _data; + + ENTER(); + + if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC || + get_unaligned_le32(data + 4) != len)) + goto error; + str_count = get_unaligned_le32(data + 8); + lang_count = get_unaligned_le32(data + 12); + + /* if one is zero the other must be zero */ + if (unlikely(!str_count != !lang_count)) + goto error; + + /* Do we have at least as many strings as descriptors need? */ + needed_count = ffs->strings_count; + if (unlikely(str_count < needed_count)) + goto error; + + /* If we don't need any strings just return and free all + * memory */ + if (!needed_count) { + kfree(_data); + return 0; + } + + /* Allocate */ + { + /* Allocate everything in one chunk so there's less + * maintanance. */ + struct { + struct usb_gadget_strings *stringtabs[lang_count + 1]; + struct usb_gadget_strings stringtab[lang_count]; + struct usb_string strings[lang_count*(needed_count+1)]; + } *d; + unsigned i = 0; + + d = kmalloc(sizeof *d, GFP_KERNEL); + if (unlikely(!d)) { + kfree(_data); + return -ENOMEM; + } + + stringtabs = d->stringtabs; + t = d->stringtab; + i = lang_count; + do { + *stringtabs++ = t++; + } while (--i); + *stringtabs = NULL; + + stringtabs = d->stringtabs; + t = d->stringtab; + s = d->strings; + strings = s; + } + + /* For each language */ + data += 16; + len -= 16; + + do { /* lang_count > 0 so we can use do-while */ + unsigned needed = needed_count; + + if (unlikely(len < 3)) + goto error_free; + t->language = get_unaligned_le16(data); + t->strings = s; + ++t; + + data += 2; + len -= 2; + + /* For each string */ + do { /* str_count > 0 so we can use do-while */ + size_t length = strnlen(data, len); + + if (unlikely(length == len)) + goto error_free; + + /* user may provide more strings then we need, + * if that's the case we simply ingore the + * rest */ + if (likely(needed)) { + /* s->id will be set while adding + * function to configuration so for + * now just leave garbage here. */ + s->s = data; + --needed; + ++s; + } + + data += length + 1; + len -= length + 1; + } while (--str_count); + + s->id = 0; /* terminator */ + s->s = NULL; + ++s; + + } while (--lang_count); + + /* Some garbage left? */ + if (unlikely(len)) + goto error_free; + + /* Done! */ + ffs->stringtabs = stringtabs; + ffs->raw_strings = _data; + + return 0; + +error_free: + kfree(stringtabs); +error: + kfree(_data); + return -EINVAL; +} + + + + +/* Events handling and management *******************************************/ + +static void __ffs_event_add(struct ffs_data *ffs, + enum usb_functionfs_event_type type) +{ + enum usb_functionfs_event_type rem_type1, rem_type2 = type; + int neg = 0; + + /* Abort any unhandled setup */ + /* We do not need to worry about some cmpxchg() changing value + * of ffs->setup_state without holding the lock because when + * state is FFS_SETUP_PENDING cmpxchg() in several places in + * the source does nothing. */ + if (ffs->setup_state == FFS_SETUP_PENDING) + ffs->setup_state = FFS_SETUP_CANCELED; + + switch (type) { + case FUNCTIONFS_RESUME: + rem_type2 = FUNCTIONFS_SUSPEND; + /* FALL THGOUTH */ + case FUNCTIONFS_SUSPEND: + case FUNCTIONFS_SETUP: + rem_type1 = type; + /* discard all similar events */ + break; + + case FUNCTIONFS_BIND: + case FUNCTIONFS_UNBIND: + case FUNCTIONFS_DISABLE: + case FUNCTIONFS_ENABLE: + /* discard everything other then power management. */ + rem_type1 = FUNCTIONFS_SUSPEND; + rem_type2 = FUNCTIONFS_RESUME; + neg = 1; + break; + + default: + BUG(); + } + + { + u8 *ev = ffs->ev.types, *out = ev; + unsigned n = ffs->ev.count; + for (; n; --n, ++ev) + if ((*ev == rem_type1 || *ev == rem_type2) == neg) + *out++ = *ev; + else + FVDBG("purging event %d", *ev); + ffs->ev.count = out - ffs->ev.types; + } + + FVDBG("adding event %d", type); + ffs->ev.types[ffs->ev.count++] = type; + wake_up_locked(&ffs->ev.waitq); +} + +static void ffs_event_add(struct ffs_data *ffs, + enum usb_functionfs_event_type type) +{ + unsigned long flags; + spin_lock_irqsave(&ffs->ev.waitq.lock, flags); + __ffs_event_add(ffs, type); + spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags); +} + + +/* Bind/unbind USB function hooks *******************************************/ + +static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, + struct usb_descriptor_header *desc, + void *priv) +{ + struct usb_endpoint_descriptor *ds = (void *)desc; + struct ffs_function *func = priv; + struct ffs_ep *ffs_ep; + + /* If hs_descriptors is not NULL then we are reading hs + * descriptors now */ + const int isHS = func->function.hs_descriptors != NULL; + unsigned idx; + + if (type != FFS_DESCRIPTOR) + return 0; + + if (isHS) + func->function.hs_descriptors[(long)valuep] = desc; + else + func->function.descriptors[(long)valuep] = desc; + + if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) + return 0; + + idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1; + ffs_ep = func->eps + idx; + + if (unlikely(ffs_ep->descs[isHS])) { + FVDBG("two %sspeed descriptors for EP %d", + isHS ? "high" : "full", + ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + return -EINVAL; + } + ffs_ep->descs[isHS] = ds; + + ffs_dump_mem(": Original ep desc", ds, ds->bLength); + if (ffs_ep->ep) { + ds->bEndpointAddress = ffs_ep->descs[0]->bEndpointAddress; + if (!ds->wMaxPacketSize) + ds->wMaxPacketSize = ffs_ep->descs[0]->wMaxPacketSize; + } else { + struct usb_request *req; + struct usb_ep *ep; + + FVDBG("autoconfig"); + ep = usb_ep_autoconfig(func->gadget, ds); + if (unlikely(!ep)) + return -ENOTSUPP; + ep->driver_data = func->eps + idx;; + + req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (unlikely(!req)) + return -ENOMEM; + + ffs_ep->ep = ep; + ffs_ep->req = req; + func->eps_revmap[ds->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK] = idx + 1; + } + ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength); + + return 0; +} + + +static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, + struct usb_descriptor_header *desc, + void *priv) +{ + struct ffs_function *func = priv; + unsigned idx; + u8 newValue; + + switch (type) { + default: + case FFS_DESCRIPTOR: + /* Handled in previous pass by __ffs_func_bind_do_descs() */ + return 0; + + case FFS_INTERFACE: + idx = *valuep; + if (func->interfaces_nums[idx] < 0) { + int id = usb_interface_id(func->conf, &func->function); + if (unlikely(id < 0)) + return id; + func->interfaces_nums[idx] = id; + } + newValue = func->interfaces_nums[idx]; + break; + + case FFS_STRING: + /* String' IDs are allocated when fsf_data is bound to cdev */ + newValue = func->ffs->stringtabs[0]->strings[*valuep - 1].id; + break; + + case FFS_ENDPOINT: + /* USB_DT_ENDPOINT are handled in + * __ffs_func_bind_do_descs(). */ + if (desc->bDescriptorType == USB_DT_ENDPOINT) + return 0; + + idx = (*valuep & USB_ENDPOINT_NUMBER_MASK) - 1; + if (unlikely(!func->eps[idx].ep)) + return -EINVAL; + + { + struct usb_endpoint_descriptor **descs; + descs = func->eps[idx].descs; + newValue = descs[descs[0] ? 0 : 1]->bEndpointAddress; + } + break; + } + + FVDBG("%02x -> %02x", *valuep, newValue); + *valuep = newValue; + return 0; +} + +static int ffs_func_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct ffs_data *ffs = func->ffs; + + const int full = !!func->ffs->fs_descs_count; + const int high = gadget_is_dualspeed(func->gadget) && + func->ffs->hs_descs_count; + + int ret; + + /* Make it a single chunk, less management later on */ + struct { + struct ffs_ep eps[ffs->eps_count]; + struct usb_descriptor_header + *fs_descs[full ? ffs->fs_descs_count + 1 : 0]; + struct usb_descriptor_header + *hs_descs[high ? ffs->hs_descs_count + 1 : 0]; + short inums[ffs->interfaces_count]; + char raw_descs[high ? ffs->raw_descs_length + : ffs->raw_fs_descs_length]; + } *data; + + ENTER(); + + /* Only high speed but not supported by gadget? */ + if (unlikely(!(full | high))) + return -ENOTSUPP; + + /* Allocate */ + data = kmalloc(sizeof *data, GFP_KERNEL); + if (unlikely(!data)) + return -ENOMEM; + + /* Zero */ + memset(data->eps, 0, sizeof data->eps); + memcpy(data->raw_descs, ffs->raw_descs + 16, sizeof data->raw_descs); + memset(data->inums, 0xff, sizeof data->inums); + for (ret = ffs->eps_count; ret; --ret) + data->eps[ret].num = -1; + + /* Save pointers */ + func->eps = data->eps; + func->interfaces_nums = data->inums; + + /* Go throught all the endpoint descriptors and allocate + * endpoints first, so that later we can rewrite the endpoint + * numbers without worying that it may be described later on. */ + if (likely(full)) { + func->function.descriptors = data->fs_descs; + ret = ffs_do_descs(ffs->fs_descs_count, + data->raw_descs, + sizeof data->raw_descs, + __ffs_func_bind_do_descs, func); + if (unlikely(ret < 0)) + goto error; + } else { + ret = 0; + } + + if (likely(high)) { + func->function.hs_descriptors = data->hs_descs; + ret = ffs_do_descs(ffs->hs_descs_count, + data->raw_descs + ret, + (sizeof data->raw_descs) - ret, + __ffs_func_bind_do_descs, func); + } + + /* Now handle interface numbers allocation and interface and + * enpoint numbers rewritting. We can do that in one go + * now. */ + ret = ffs_do_descs(ffs->fs_descs_count + + (high ? ffs->hs_descs_count : 0), + data->raw_descs, sizeof data->raw_descs, + __ffs_func_bind_do_nums, func); + if (unlikely(ret < 0)) + goto error; + + /* And we're done */ + ffs_event_add(ffs, FUNCTIONFS_BIND); + return 0; + +error: + /* XXX Do we need to release all claimed endpoints here? */ + return ret; +} + + +/* Other USB function hooks *************************************************/ + +static void ffs_func_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct ffs_data *ffs = func->ffs; + + ENTER(); + + if (ffs->func == func) { + ffs_func_eps_disable(func); + ffs->func = NULL; + } + + ffs_event_add(ffs, FUNCTIONFS_UNBIND); + + ffs_func_free(func); +} + + +static int ffs_func_set_alt(struct usb_function *f, + unsigned interface, unsigned alt) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct ffs_data *ffs = func->ffs; + int ret = 0, intf; + + if (alt != (unsigned)-1) { + intf = ffs_func_revmap_intf(func, interface); + if (unlikely(intf < 0)) + return intf; + } + + if (ffs->func) + ffs_func_eps_disable(ffs->func); + + if (ffs->state != FFS_ACTIVE) + return -ENODEV; + + if (alt == (unsigned)-1) { + ffs->func = NULL; + ffs_event_add(ffs, FUNCTIONFS_DISABLE); + return 0; + } + + ffs->func = func; + ret = ffs_func_eps_enable(func); + if (likely(ret >= 0)) + ffs_event_add(ffs, FUNCTIONFS_ENABLE); + return ret; +} + +static void ffs_func_disable(struct usb_function *f) +{ + ffs_func_set_alt(f, 0, (unsigned)-1); +} + +static int ffs_func_setup(struct usb_function *f, + const struct usb_ctrlrequest *creq) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct ffs_data *ffs = func->ffs; + unsigned long flags; + int ret; + + ENTER(); + + FVDBG("creq->bRequestType = %02x", creq->bRequestType); + FVDBG("creq->bRequest = %02x", creq->bRequest); + FVDBG("creq->wValue = %04x", le16_to_cpu(creq->wValue)); + FVDBG("creq->wIndex = %04x", le16_to_cpu(creq->wIndex)); + FVDBG("creq->wLength = %04x", le16_to_cpu(creq->wLength)); + + /* Most requests directed to interface go throught here + * (notable exceptions are set/get interface) so we need to + * handle them. All other either handled by composite or + * passed to usb_configuration->setup() (if one is set). No + * matter, we will handle requests directed to endpoint here + * as well (as it's straightforward) but what to do with any + * other request? */ + + if (ffs->state != FFS_ACTIVE) + return -ENODEV; + + switch (creq->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + ret = ffs_func_revmap_intf(func, le16_to_cpu(creq->wIndex)); + if (unlikely(ret < 0)) + return ret; + break; + + case USB_RECIP_ENDPOINT: + ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex)); + if (unlikely(ret < 0)) + return ret; + break; + + default: + return -EOPNOTSUPP; + } + + spin_lock_irqsave(&ffs->ev.waitq.lock, flags); + ffs->ev.setup = *creq; + ffs->ev.setup.wIndex = cpu_to_le16(ret); + __ffs_event_add(ffs, FUNCTIONFS_SETUP); + spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags); + + return 0; +} + +static void ffs_func_suspend(struct usb_function *f) +{ + ENTER(); + ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_SUSPEND); +} + +static void ffs_func_resume(struct usb_function *f) +{ + ENTER(); + ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_RESUME); +} + + + +/* Enpoint and interface numbers reverse mapping ****************************/ + +static int ffs_func_revmap_ep(struct ffs_function *func, u8 num) +{ + num = func->eps_revmap[num & USB_ENDPOINT_NUMBER_MASK]; + return num ? num : -EDOM; +} + +static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf) +{ + short *nums = func->interfaces_nums; + unsigned count = func->ffs->interfaces_count; + + for (; count; --count, ++nums) { + if (*nums >= 0 && *nums == intf) + return nums - func->interfaces_nums; + } + + return -EDOM; +} + + +/* Misc helper functions ****************************************************/ + +static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock) +{ + return nonblock + ? likely(mutex_trylock(mutex)) ? 0 : -EAGAIN + : mutex_lock_interruptible(mutex); +} + + +static char *ffs_prepare_buffer(const char * __user buf, size_t len) +{ + char *data; + + if (unlikely(!len)) + return NULL; + + data = kmalloc(len, GFP_KERNEL); + if (unlikely(!data)) + return ERR_PTR(-ENOMEM); + + if (unlikely(__copy_from_user(data, buf, len))) { + kfree(data); + return ERR_PTR(-EFAULT); + } + + FVDBG("Buffer from user space:"); + ffs_dump_mem("", data, len); + + return data; +} diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c new file mode 100644 index 0000000..1e00ff9 --- /dev/null +++ b/drivers/usb/gadget/f_hid.c @@ -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; +} diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index f4911c0..7d05a0b 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -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; + + /* 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); + } - 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); + { + 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; } diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 56b0221..882484a 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -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; diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c new file mode 100644 index 0000000..fc2611f --- /dev/null +++ b/drivers/usb/gadget/f_uvc.c @@ -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"); + diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h new file mode 100644 index 0000000..8a5db7c --- /dev/null +++ b/drivers/usb/gadget/f_uvc.h @@ -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_ */ + diff --git a/drivers/usb/gadget/fsl_mx3_udc.c b/drivers/usb/gadget/fsl_mxc_udc.c index 20a802e..d0b8bde 100644 --- a/drivers/usb/gadget/fsl_mx3_udc.c +++ b/drivers/usb/gadget/fsl_mxc_udc.c @@ -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); diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index fa3d142..08a9a62 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -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; diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c new file mode 100644 index 0000000..4b0e4a0 --- /dev/null +++ b/drivers/usb/gadget/g_ffs.c @@ -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 diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c new file mode 100644 index 0000000..7757226 --- /dev/null +++ b/drivers/usb/gadget/hid.c @@ -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); diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h index ff61e48..cd16231 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h @@ -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; diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 07f4178..1da755a 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -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; diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h new file mode 100644 index 0000000..0a705e6 --- /dev/null +++ b/drivers/usb/gadget/uvc.h @@ -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_ */ + diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c new file mode 100644 index 0000000..4389199 --- /dev/null +++ b/drivers/usb/gadget/uvc_queue.c @@ -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; +} + diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/uvc_queue.h new file mode 100644 index 0000000..7f5a33f --- /dev/null +++ b/drivers/usb/gadget/uvc_queue.h @@ -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_ */ + diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c new file mode 100644 index 0000000..a7989f2 --- /dev/null +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -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, +}; + diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c new file mode 100644 index 0000000..de8cbc4 --- /dev/null +++ b/drivers/usb/gadget/uvc_video.c @@ -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; +} + diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c new file mode 100644 index 0000000..417fd68 --- /dev/null +++ b/drivers/usb/gadget/webcam.c @@ -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"); + diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 8d3df03..f865be2 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -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) diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index e3a74e7..faa6174 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -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); diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 0e26aa1..5cd967d 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -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; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 13ead00..ef3e88f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -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> diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index c7178bc..e7d3d8d 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -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 */ diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 0cd6c77..5450e62 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -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: diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index ead5f4f..d43d176 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -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); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 8952177..11a79c4 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -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 diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 556c0b4..650a687 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -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 /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c index e799f86..6fe5500 100644 --- a/drivers/usb/host/fhci-dbg.c +++ b/drivers/usb/host/fhci-dbg.c @@ -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) diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 15379c6..9045337 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -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) diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c index 0cfaedc..348fe62 100644 --- a/drivers/usb/host/fhci-hub.c +++ b/drivers/usb/host/fhci-hub.c @@ -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 */ diff --git a/drivers/usb/host/fhci-mem.c b/drivers/usb/host/fhci-mem.c index 5591bfb..b0b88f5 100644 --- a/drivers/usb/host/fhci-mem.c +++ b/drivers/usb/host/fhci-mem.c @@ -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) diff --git a/drivers/usb/host/fhci-q.c b/drivers/usb/host/fhci-q.c index f73c923..03be749 100644 --- a/drivers/usb/host/fhci-q.c +++ b/drivers/usb/host/fhci-q.c @@ -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 */ diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index ff43747..4f2cbdc 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -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) diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c index 5701347..7be548c 100644 --- a/drivers/usb/host/fhci-tds.c +++ b/drivers/usb/host/fhci-tds.c @@ -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 diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h index 72dae1c..71c3caa 100644 --- a/drivers/usb/host/fhci.h +++ b/drivers/usb/host/fhci.h @@ -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; } diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index 8a12f29..ca0e98d 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -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 diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 92de71d..d9e8212 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -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" diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 217fb51..20a0dfe 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -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 diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 9f01293..dbcafa2 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -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; diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 4293cfd..8f0259e 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -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; } diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index afe59be..fc57655 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -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: diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c new file mode 100644 index 0000000..2cc8a50 --- /dev/null +++ b/drivers/usb/host/ohci-omap3.c @@ -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 <gadiyar@ti.com>"); diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index e62b30b..f608dfd 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -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 */ diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index d478ffa..6db57ab 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -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); diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 3b867a8..bcf9f0e 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -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; diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 228f2b0..5b31bae 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -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); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 0919706..6637e52 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -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" diff --git a/drivers/usb/host/whci/debug.c b/drivers/usb/host/whci/debug.c index c5305b5..767af26 100644 --- a/drivers/usb/host/whci/debug.c +++ b/drivers/usb/host/whci/debug.c @@ -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", }; diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index 141d049..ab5a14f 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c @@ -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); diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 105fa8b..fcbf4ab 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -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; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 208b805..a1a7a97 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -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; } } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index d64f572..fd9e03a 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -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; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 417d37a..edffd81 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -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); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 85d7e8f..36c858e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -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); } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7e42772..40e0a0c 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -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 diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ea389e9..dada2fb 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -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); diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index 094f91c..1fa6ce3 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -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); diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index 1edb6d3..82e1663 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -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; } diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index d3c8523..7dc9d3c 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -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: diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index aae95a0..30d9303 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -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; } diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c index b271b05..411e605 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_con.c +++ b/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -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 */ diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 90aede9..7828c76 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -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); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index a21cce6..16dffe9 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -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); diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index ddf7f9a..e7fa364 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -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); diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index e4af18b..812dc28 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -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); diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 4d0be13..a545d65 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -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 */ diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 07fe490..cfd38ed 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -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. diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 3a485da..9705f71 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -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) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index ec8d324..b611420 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -170,6 +170,13 @@ static irqreturn_t blackfin_interrupt(int irq, void *__hci) retval = musb_interrupt(musb); } + /* Start sampling ID pin, when plug is removed from MUSB */ + if (is_otg_enabled(musb) && (musb->xceiv->state == OTG_STATE_B_IDLE + || musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { + mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); + musb->a_wait_bcon = TIMER_DELAY; + } + spin_unlock_irqrestore(&musb->lock, flags); return retval; @@ -180,6 +187,7 @@ static void musb_conn_timer_handler(unsigned long _musb) struct musb *musb = (void *)_musb; unsigned long flags; u16 val; + static u8 toggle; spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -187,10 +195,44 @@ static void musb_conn_timer_handler(unsigned long _musb) case OTG_STATE_A_WAIT_BCON: /* Start a new session */ val = musb_readw(musb->mregs, MUSB_DEVCTL); + val &= ~MUSB_DEVCTL_SESSION; + musb_writew(musb->mregs, MUSB_DEVCTL, val); val |= MUSB_DEVCTL_SESSION; musb_writew(musb->mregs, MUSB_DEVCTL, val); + /* Check if musb is host or peripheral. */ + val = musb_readw(musb->mregs, MUSB_DEVCTL); + + if (!(val & MUSB_DEVCTL_BDEVICE)) { + gpio_set_value(musb->config->gpio_vrsel, 1); + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; + } else { + gpio_set_value(musb->config->gpio_vrsel, 0); + /* Ignore VBUSERROR and SUSPEND IRQ */ + val = musb_readb(musb->mregs, MUSB_INTRUSBE); + val &= ~MUSB_INTR_VBUSERROR; + musb_writeb(musb->mregs, MUSB_INTRUSBE, val); + val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR; + musb_writeb(musb->mregs, MUSB_INTRUSB, val); + if (is_otg_enabled(musb)) + musb->xceiv->state = OTG_STATE_B_IDLE; + else + musb_writeb(musb->mregs, MUSB_POWER, MUSB_POWER_HSENAB); + } + mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); + break; + case OTG_STATE_B_IDLE: + + if (!is_peripheral_enabled(musb)) + break; + /* Start a new session. It seems that MUSB needs taking + * some time to recognize the type of the plug inserted? + */ + val = musb_readw(musb->mregs, MUSB_DEVCTL); + val |= MUSB_DEVCTL_SESSION; + musb_writew(musb->mregs, MUSB_DEVCTL, val); val = musb_readw(musb->mregs, MUSB_DEVCTL); + if (!(val & MUSB_DEVCTL_BDEVICE)) { gpio_set_value(musb->config->gpio_vrsel, 1); musb->xceiv->state = OTG_STATE_A_WAIT_BCON; @@ -205,12 +247,27 @@ static void musb_conn_timer_handler(unsigned long _musb) val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR; musb_writeb(musb->mregs, MUSB_INTRUSB, val); - val = MUSB_POWER_HSENAB; - musb_writeb(musb->mregs, MUSB_POWER, val); + /* Toggle the Soft Conn bit, so that we can response to + * the inserting of either A-plug or B-plug. + */ + if (toggle) { + val = musb_readb(musb->mregs, MUSB_POWER); + val &= ~MUSB_POWER_SOFTCONN; + musb_writeb(musb->mregs, MUSB_POWER, val); + toggle = 0; + } else { + val = musb_readb(musb->mregs, MUSB_POWER); + val |= MUSB_POWER_SOFTCONN; + musb_writeb(musb->mregs, MUSB_POWER, val); + toggle = 1; + } + /* The delay time is set to 1/4 second by default, + * shortening it, if accelerating A-plug detection + * is needed in OTG mode. + */ + mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY / 4); } - mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); break; - default: DBG(1, "%s state not handled\n", otg_state_string(musb)); break; @@ -222,7 +279,7 @@ static void musb_conn_timer_handler(unsigned long _musb) void musb_platform_enable(struct musb *musb) { - if (is_host_enabled(musb)) { + if (!is_otg_enabled(musb) && is_host_enabled(musb)) { mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); musb->a_wait_bcon = TIMER_DELAY; } @@ -232,16 +289,12 @@ void musb_platform_disable(struct musb *musb) { } -static void bfin_vbus_power(struct musb *musb, int is_on, int sleeping) -{ -} - static void bfin_set_vbus(struct musb *musb, int is_on) { - if (is_on) - gpio_set_value(musb->config->gpio_vrsel, 1); - else - gpio_set_value(musb->config->gpio_vrsel, 0); + int value = musb->config->gpio_vrsel_active; + if (!is_on) + value = !value; + gpio_set_value(musb->config->gpio_vrsel, value); DBG(1, "VBUS %s, devctl %02x " /* otg %3x conf %08x prcm %08x */ "\n", @@ -256,7 +309,7 @@ static int bfin_set_power(struct otg_transceiver *x, unsigned mA) void musb_platform_try_idle(struct musb *musb, unsigned long timeout) { - if (is_host_enabled(musb)) + if (!is_otg_enabled(musb) && is_host_enabled(musb)) mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); } @@ -270,7 +323,7 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode) return -EIO; } -int __init musb_platform_init(struct musb *musb) +int __init musb_platform_init(struct musb *musb, void *board_data) { /* @@ -339,23 +392,10 @@ int __init musb_platform_init(struct musb *musb) return 0; } -int musb_platform_suspend(struct musb *musb) -{ - return 0; -} - -int musb_platform_resume(struct musb *musb) -{ - return 0; -} - - int musb_platform_exit(struct musb *musb) { - bfin_vbus_power(musb, 0 /*off*/, 1); gpio_free(musb->config->gpio_vrsel); - musb_platform_suspend(musb); return 0; } diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index ce2e16f..57624361 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -376,7 +376,7 @@ int musb_platform_set_mode(struct musb *musb, u8 mode) return -EIO; } -int __init musb_platform_init(struct musb *musb) +int __init musb_platform_init(struct musb *musb, void *board_data) { void __iomem *tibase = musb->ctrl_base; u32 revision; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 705cc4a..fad70bc 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -149,6 +149,87 @@ static inline struct musb *dev_to_musb(struct device *dev) /*-------------------------------------------------------------------------*/ +#ifndef CONFIG_BLACKFIN +static int musb_ulpi_read(struct otg_transceiver *otg, u32 offset) +{ + void __iomem *addr = otg->io_priv; + int i = 0; + u8 r; + u8 power; + + /* Make sure the transceiver is not in low power mode */ + power = musb_readb(addr, MUSB_POWER); + power &= ~MUSB_POWER_SUSPENDM; + musb_writeb(addr, MUSB_POWER, power); + + /* REVISIT: musbhdrc_ulpi_an.pdf recommends setting the + * ULPICarKitControlDisableUTMI after clearing POWER_SUSPENDM. + */ + + musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset); + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, + MUSB_ULPI_REG_REQ | MUSB_ULPI_RDN_WR); + + while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) + & MUSB_ULPI_REG_CMPLT)) { + i++; + if (i == 10000) { + DBG(3, "ULPI read timed out\n"); + return -ETIMEDOUT; + } + + } + r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); + r &= ~MUSB_ULPI_REG_CMPLT; + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r); + + return musb_readb(addr, MUSB_ULPI_REG_DATA); +} + +static int musb_ulpi_write(struct otg_transceiver *otg, + u32 offset, u32 data) +{ + void __iomem *addr = otg->io_priv; + int i = 0; + u8 r = 0; + u8 power; + + /* Make sure the transceiver is not in low power mode */ + power = musb_readb(addr, MUSB_POWER); + power &= ~MUSB_POWER_SUSPENDM; + musb_writeb(addr, MUSB_POWER, power); + + musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset); + musb_writeb(addr, MUSB_ULPI_REG_DATA, (u8)data); + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, MUSB_ULPI_REG_REQ); + + while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) + & MUSB_ULPI_REG_CMPLT)) { + i++; + if (i == 10000) { + DBG(3, "ULPI write timed out\n"); + return -ETIMEDOUT; + } + } + + r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); + r &= ~MUSB_ULPI_REG_CMPLT; + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r); + + return 0; +} +#else +#define musb_ulpi_read(a, b) NULL +#define musb_ulpi_write(a, b, c) NULL +#endif + +static struct otg_io_access_ops musb_ulpi_access = { + .read = musb_ulpi_read, + .write = musb_ulpi_write, +}; + +/*-------------------------------------------------------------------------*/ + #if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN) /* @@ -353,8 +434,7 @@ void musb_hnp_stop(struct musb *musb) * which cause occasional OPT A "Did not receive reset after connect" * errors. */ - musb->port1_status &= - ~(1 << USB_PORT_FEAT_C_CONNECTION); + musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16); } #endif @@ -530,8 +610,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, musb_writeb(mbase, MUSB_DEVCTL, devctl); } else { musb->port1_status |= - (1 << USB_PORT_FEAT_OVER_CURRENT) - | (1 << USB_PORT_FEAT_C_OVER_CURRENT); + USB_PORT_STAT_OVERCURRENT + | (USB_PORT_STAT_C_OVERCURRENT << 16); } break; default: @@ -986,7 +1066,8 @@ static void musb_shutdown(struct platform_device *pdev) * more than selecting one of a bunch of predefined configurations. */ #if defined(CONFIG_USB_TUSB6010) || \ - defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) + defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \ + || defined(CONFIG_ARCH_OMAP4) static ushort __initdata fifo_mode = 4; #else static ushort __initdata fifo_mode = 2; @@ -996,24 +1077,13 @@ static ushort __initdata fifo_mode = 2; module_param(fifo_mode, ushort, 0); MODULE_PARM_DESC(fifo_mode, "initial endpoint configuration"); - -enum fifo_style { FIFO_RXTX, FIFO_TX, FIFO_RX } __attribute__ ((packed)); -enum buf_mode { BUF_SINGLE, BUF_DOUBLE } __attribute__ ((packed)); - -struct fifo_cfg { - u8 hw_ep_num; - enum fifo_style style; - enum buf_mode mode; - u16 maxpacket; -}; - /* * tables defining fifo_mode values. define more if you like. * for host side, make sure both halves of ep1 are set up. */ /* mode 0 - fits in 2KB */ -static struct fifo_cfg __initdata mode_0_cfg[] = { +static struct musb_fifo_cfg __initdata mode_0_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, }, @@ -1022,7 +1092,7 @@ static struct fifo_cfg __initdata mode_0_cfg[] = { }; /* mode 1 - fits in 4KB */ -static struct fifo_cfg __initdata mode_1_cfg[] = { +static struct musb_fifo_cfg __initdata mode_1_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, .mode = BUF_DOUBLE, }, @@ -1031,7 +1101,7 @@ static struct fifo_cfg __initdata mode_1_cfg[] = { }; /* mode 2 - fits in 4KB */ -static struct fifo_cfg __initdata mode_2_cfg[] = { +static struct musb_fifo_cfg __initdata mode_2_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -1041,7 +1111,7 @@ static struct fifo_cfg __initdata mode_2_cfg[] = { }; /* mode 3 - fits in 4KB */ -static struct fifo_cfg __initdata mode_3_cfg[] = { +static struct musb_fifo_cfg __initdata mode_3_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -1051,7 +1121,7 @@ static struct fifo_cfg __initdata mode_3_cfg[] = { }; /* mode 4 - fits in 16KB */ -static struct fifo_cfg __initdata mode_4_cfg[] = { +static struct musb_fifo_cfg __initdata mode_4_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -1082,7 +1152,7 @@ static struct fifo_cfg __initdata mode_4_cfg[] = { }; /* mode 5 - fits in 8KB */ -static struct fifo_cfg __initdata mode_5_cfg[] = { +static struct musb_fifo_cfg __initdata mode_5_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -1120,7 +1190,7 @@ static struct fifo_cfg __initdata mode_5_cfg[] = { */ static int __init fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, - const struct fifo_cfg *cfg, u16 offset) + const struct musb_fifo_cfg *cfg, u16 offset) { void __iomem *mbase = musb->mregs; int size = 0; @@ -1191,17 +1261,23 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, return offset + (maxpacket << ((c_size & MUSB_FIFOSZ_DPB) ? 1 : 0)); } -static struct fifo_cfg __initdata ep0_cfg = { +static struct musb_fifo_cfg __initdata ep0_cfg = { .style = FIFO_RXTX, .maxpacket = 64, }; static int __init ep_config_from_table(struct musb *musb) { - const struct fifo_cfg *cfg; + const struct musb_fifo_cfg *cfg; unsigned i, n; int offset; struct musb_hw_ep *hw_ep = musb->endpoints; + if (musb->config->fifo_cfg) { + cfg = musb->config->fifo_cfg; + n = musb->config->fifo_cfg_size; + goto done; + } + switch (fifo_mode) { default: fifo_mode = 0; @@ -1236,6 +1312,7 @@ static int __init ep_config_from_table(struct musb *musb) musb_driver_name, fifo_mode); +done: offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0); /* assert(offset > 0) */ @@ -1461,7 +1538,8 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) || \ + defined(CONFIG_ARCH_OMAP4) static irqreturn_t generic_interrupt(int irq, void *__hci) { @@ -1948,7 +2026,7 @@ bad_config: * isp1504, non-OTG, etc) mostly hooking up through ULPI. */ musb->isr = generic_interrupt; - status = musb_platform_init(musb); + status = musb_platform_init(musb, plat->board_data); if (status < 0) goto fail2; @@ -1957,6 +2035,11 @@ bad_config: goto fail3; } + if (!musb->xceiv->io_ops) { + musb->xceiv->io_priv = musb->mregs; + musb->xceiv->io_ops = &musb_ulpi_access; + } + #ifndef CONFIG_MUSB_PIO_ONLY if (use_dma && dev->dma_mask) { struct dma_controller *c; @@ -2057,10 +2140,14 @@ bad_config: if (status < 0) goto fail3; + status = musb_init_debugfs(musb); + if (status < 0) + goto fail4; + #ifdef CONFIG_SYSFS status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group); if (status) - goto fail4; + goto fail5; #endif dev_info(dev, "USB %s mode controller at %p using %s, IRQ %d\n", @@ -2077,6 +2164,9 @@ bad_config: return 0; +fail5: + musb_exit_debugfs(musb); + fail4: if (!is_otg_enabled(musb) && is_host_enabled(musb)) usb_remove_hcd(musb_to_hcd(musb)); @@ -2153,6 +2243,7 @@ static int __exit musb_remove(struct platform_device *pdev) * - Peripheral mode: peripheral is deactivated (or never-activated) * - OTG mode: both roles are deactivated (or never-activated) */ + musb_exit_debugfs(musb); musb_shutdown(pdev); #ifdef CONFIG_USB_MUSB_HDRC_HCD if (musb->board_mode == MUSB_HOST) diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index ac17b00..b22d02d 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -69,7 +69,7 @@ struct musb_ep; #include "musb_regs.h" #include "musb_gadget.h" -#include "../core/hcd.h" +#include <linux/usb/hcd.h> #include "musb_host.h" @@ -213,7 +213,8 @@ enum musb_g_ep0_state { */ #if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_ARCH_OMAP2430) \ - || defined(CONFIG_ARCH_OMAP3430) || defined(CONFIG_BLACKFIN) + || defined(CONFIG_ARCH_OMAP3430) || defined(CONFIG_BLACKFIN) \ + || defined(CONFIG_ARCH_OMAP4) /* REVISIT indexed access seemed to * misbehave (on DaVinci) for at least peripheral IN ... */ @@ -596,7 +597,8 @@ extern void musb_hnp_stop(struct musb *musb); extern int musb_platform_set_mode(struct musb *musb, u8 musb_mode); #if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN) || \ - defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) + defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \ + defined(CONFIG_ARCH_OMAP4) extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout); #else #define musb_platform_try_idle(x, y) do {} while (0) @@ -608,7 +610,7 @@ extern int musb_platform_get_vbus_status(struct musb *musb); #define musb_platform_get_vbus_status(x) 0 #endif -extern int __init musb_platform_init(struct musb *musb); +extern int __init musb_platform_init(struct musb *musb, void *board_data); extern int musb_platform_exit(struct musb *musb); #endif /* __MUSB_CORE_H__ */ diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h index 9fc1db4..d73afdb 100644 --- a/drivers/usb/musb/musb_debug.h +++ b/drivers/usb/musb/musb_debug.h @@ -59,4 +59,17 @@ static inline int _dbg_level(unsigned l) extern const char *otg_state_string(struct musb *); +#ifdef CONFIG_DEBUG_FS +extern int musb_init_debugfs(struct musb *musb); +extern void musb_exit_debugfs(struct musb *musb); +#else +static inline int musb_init_debugfs(struct musb *musb) +{ + return 0; +} +static inline void musb_exit_debugfs(struct musb *musb) +{ +} +#endif + #endif /* __MUSB_LINUX_DEBUG_H__ */ diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c new file mode 100644 index 0000000..bba76af --- /dev/null +++ b/drivers/usb/musb/musb_debugfs.c @@ -0,0 +1,294 @@ +/* + * MUSB OTG driver debugfs support + * + * Copyright 2010 Nokia Corporation + * Contact: Felipe Balbi <felipe.balbi@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/kobject.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +#ifdef CONFIG_ARM +#include <mach/hardware.h> +#include <mach/memory.h> +#include <asm/mach-types.h> +#endif + +#include <asm/uaccess.h> + +#include "musb_core.h" +#include "musb_debug.h" + +#ifdef CONFIG_ARCH_DAVINCI +#include "davinci.h" +#endif + +struct musb_register_map { + char *name; + unsigned offset; + unsigned size; +}; + +static const struct musb_register_map musb_regmap[] = { + { "FAddr", 0x00, 8 }, + { "Power", 0x01, 8 }, + { "Frame", 0x0c, 16 }, + { "Index", 0x0e, 8 }, + { "Testmode", 0x0f, 8 }, + { "TxMaxPp", 0x10, 16 }, + { "TxCSRp", 0x12, 16 }, + { "RxMaxPp", 0x14, 16 }, + { "RxCSR", 0x16, 16 }, + { "RxCount", 0x18, 16 }, + { "ConfigData", 0x1f, 8 }, + { "DevCtl", 0x60, 8 }, + { "MISC", 0x61, 8 }, + { "TxFIFOsz", 0x62, 8 }, + { "RxFIFOsz", 0x63, 8 }, + { "TxFIFOadd", 0x64, 16 }, + { "RxFIFOadd", 0x66, 16 }, + { "VControl", 0x68, 32 }, + { "HWVers", 0x6C, 16 }, + { "EPInfo", 0x78, 8 }, + { "RAMInfo", 0x79, 8 }, + { "LinkInfo", 0x7A, 8 }, + { "VPLen", 0x7B, 8 }, + { "HS_EOF1", 0x7C, 8 }, + { "FS_EOF1", 0x7D, 8 }, + { "LS_EOF1", 0x7E, 8 }, + { "SOFT_RST", 0x7F, 8 }, + { "DMA_CNTLch0", 0x204, 16 }, + { "DMA_ADDRch0", 0x208, 16 }, + { "DMA_COUNTch0", 0x20C, 16 }, + { "DMA_CNTLch1", 0x214, 16 }, + { "DMA_ADDRch1", 0x218, 16 }, + { "DMA_COUNTch1", 0x21C, 16 }, + { "DMA_CNTLch2", 0x224, 16 }, + { "DMA_ADDRch2", 0x228, 16 }, + { "DMA_COUNTch2", 0x22C, 16 }, + { "DMA_CNTLch3", 0x234, 16 }, + { "DMA_ADDRch3", 0x238, 16 }, + { "DMA_COUNTch3", 0x23C, 16 }, + { "DMA_CNTLch4", 0x244, 16 }, + { "DMA_ADDRch4", 0x248, 16 }, + { "DMA_COUNTch4", 0x24C, 16 }, + { "DMA_CNTLch5", 0x254, 16 }, + { "DMA_ADDRch5", 0x258, 16 }, + { "DMA_COUNTch5", 0x25C, 16 }, + { "DMA_CNTLch6", 0x264, 16 }, + { "DMA_ADDRch6", 0x268, 16 }, + { "DMA_COUNTch6", 0x26C, 16 }, + { "DMA_CNTLch7", 0x274, 16 }, + { "DMA_ADDRch7", 0x278, 16 }, + { "DMA_COUNTch7", 0x27C, 16 }, + { } /* Terminating Entry */ +}; + +static struct dentry *musb_debugfs_root; + +static int musb_regdump_show(struct seq_file *s, void *unused) +{ + struct musb *musb = s->private; + unsigned i; + + seq_printf(s, "MUSB (M)HDRC Register Dump\n"); + + for (i = 0; i < ARRAY_SIZE(musb_regmap); i++) { + switch (musb_regmap[i].size) { + case 8: + seq_printf(s, "%-12s: %02x\n", musb_regmap[i].name, + musb_readb(musb->mregs, musb_regmap[i].offset)); + break; + case 16: + seq_printf(s, "%-12s: %04x\n", musb_regmap[i].name, + musb_readw(musb->mregs, musb_regmap[i].offset)); + break; + case 32: + seq_printf(s, "%-12s: %08x\n", musb_regmap[i].name, + musb_readl(musb->mregs, musb_regmap[i].offset)); + break; + } + } + + return 0; +} + +static int musb_regdump_open(struct inode *inode, struct file *file) +{ + return single_open(file, musb_regdump_show, inode->i_private); +} + +static int musb_test_mode_show(struct seq_file *s, void *unused) +{ + struct musb *musb = s->private; + unsigned test; + + test = musb_readb(musb->mregs, MUSB_TESTMODE); + + if (test & MUSB_TEST_FORCE_HOST) + seq_printf(s, "force host\n"); + + if (test & MUSB_TEST_FIFO_ACCESS) + seq_printf(s, "fifo access\n"); + + if (test & MUSB_TEST_FORCE_FS) + seq_printf(s, "force full-speed\n"); + + if (test & MUSB_TEST_FORCE_HS) + seq_printf(s, "force high-speed\n"); + + if (test & MUSB_TEST_PACKET) + seq_printf(s, "test packet\n"); + + if (test & MUSB_TEST_K) + seq_printf(s, "test K\n"); + + if (test & MUSB_TEST_J) + seq_printf(s, "test J\n"); + + if (test & MUSB_TEST_SE0_NAK) + seq_printf(s, "test SE0 NAK\n"); + + return 0; +} + +static const struct file_operations musb_regdump_fops = { + .open = musb_regdump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int musb_test_mode_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return single_open(file, musb_test_mode_show, inode->i_private); +} + +static ssize_t musb_test_mode_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct musb *musb = file->private_data; + u8 test = 0; + char buf[18]; + + memset(buf, 0x00, sizeof(buf)); + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "force host", 9)) + test = MUSB_TEST_FORCE_HOST; + + if (!strncmp(buf, "fifo access", 11)) + test = MUSB_TEST_FIFO_ACCESS; + + if (!strncmp(buf, "force full-speed", 15)) + test = MUSB_TEST_FORCE_FS; + + if (!strncmp(buf, "force high-speed", 15)) + test = MUSB_TEST_FORCE_HS; + + if (!strncmp(buf, "test packet", 10)) { + test = MUSB_TEST_PACKET; + musb_load_testpacket(musb); + } + + if (!strncmp(buf, "test K", 6)) + test = MUSB_TEST_K; + + if (!strncmp(buf, "test J", 6)) + test = MUSB_TEST_J; + + if (!strncmp(buf, "test SE0 NAK", 12)) + test = MUSB_TEST_SE0_NAK; + + musb_writeb(musb->mregs, MUSB_TESTMODE, test); + + return count; +} + +static const struct file_operations musb_test_mode_fops = { + .open = musb_test_mode_open, + .write = musb_test_mode_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +int __init musb_init_debugfs(struct musb *musb) +{ + struct dentry *root; + struct dentry *file; + int ret; + + root = debugfs_create_dir("musb", NULL); + if (IS_ERR(root)) { + ret = PTR_ERR(root); + goto err0; + } + + file = debugfs_create_file("regdump", S_IRUGO, root, musb, + &musb_regdump_fops); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto err1; + } + + file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, + root, musb, &musb_test_mode_fops); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto err1; + } + + musb_debugfs_root = root; + + return 0; + +err1: + debugfs_remove_recursive(root); + +err0: + return ret; +} + +void /* __init_or_exit */ musb_exit_debugfs(struct musb *musb) +{ + debugfs_remove_recursive(musb_debugfs_root); +} diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 53d0645..21b9788 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -351,6 +351,31 @@ __acquires(musb->lock) musb->test_mode_nr = MUSB_TEST_PACKET; break; + + case 0xc0: + /* TEST_FORCE_HS */ + pr_debug("TEST_FORCE_HS\n"); + musb->test_mode_nr = + MUSB_TEST_FORCE_HS; + break; + case 0xc1: + /* TEST_FORCE_FS */ + pr_debug("TEST_FORCE_FS\n"); + musb->test_mode_nr = + MUSB_TEST_FORCE_FS; + break; + case 0xc2: + /* TEST_FIFO_ACCESS */ + pr_debug("TEST_FIFO_ACCESS\n"); + musb->test_mode_nr = + MUSB_TEST_FIFO_ACCESS; + break; + case 0xc3: + /* TEST_FORCE_HOST */ + pr_debug("TEST_FORCE_HOST\n"); + musb->test_mode_nr = + MUSB_TEST_FORCE_HOST; + break; default: goto stall; } diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index fa55aac..2442675 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -75,6 +75,10 @@ /* MUSB ULPI VBUSCONTROL */ #define MUSB_ULPI_USE_EXTVBUS 0x01 #define MUSB_ULPI_USE_EXTVBUSIND 0x02 +/* ULPI_REG_CONTROL */ +#define MUSB_ULPI_REG_REQ (1 << 0) +#define MUSB_ULPI_REG_CMPLT (1 << 1) +#define MUSB_ULPI_RDN_WR (1 << 2) /* TESTMODE */ #define MUSB_TEST_FORCE_HOST 0x80 @@ -251,6 +255,12 @@ /* REVISIT: vctrl/vstatus: optional vendor utmi+phy register at 0x68 */ #define MUSB_HWVERS 0x6C /* 8 bit */ #define MUSB_ULPI_BUSCONTROL 0x70 /* 8 bit */ +#define MUSB_ULPI_INT_MASK 0x72 /* 8 bit */ +#define MUSB_ULPI_INT_SRC 0x73 /* 8 bit */ +#define MUSB_ULPI_REG_DATA 0x74 /* 8 bit */ +#define MUSB_ULPI_REG_ADDR 0x75 /* 8 bit */ +#define MUSB_ULPI_REG_CONTROL 0x76 /* 8 bit */ +#define MUSB_ULPI_RAW_DATA 0x77 /* 8 bit */ #define MUSB_EPINFO 0x78 /* 8 bit */ #define MUSB_RAMINFO 0x79 /* 8 bit */ diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 7775e1c..92e85e0 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -183,8 +183,8 @@ static void musb_port_reset(struct musb *musb, bool do_reset) void musb_root_disconnect(struct musb *musb) { - musb->port1_status = (1 << USB_PORT_FEAT_POWER) - | (1 << USB_PORT_FEAT_C_CONNECTION); + musb->port1_status = USB_PORT_STAT_POWER + | (USB_PORT_STAT_C_CONNECTION << 16); usb_hcd_poll_rh_status(musb_to_hcd(musb)); musb->is_active = 0; diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h index 613f95a..f763d62 100644 --- a/drivers/usb/musb/musbhsdma.h +++ b/drivers/usb/musb/musbhsdma.h @@ -102,26 +102,16 @@ static inline void musb_write_hsdma_addr(void __iomem *mbase, static inline u32 musb_read_hsdma_count(void __iomem *mbase, u8 bchannel) { - u32 count = musb_readw(mbase, + return musb_readl(mbase, MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_HIGH)); - - count = count << 16; - - count |= musb_readw(mbase, - MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_LOW)); - - return count; } static inline void musb_write_hsdma_count(void __iomem *mbase, u8 bchannel, u32 len) { - musb_writew(mbase, - MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_LOW), - ((u16)((u32) len & 0xFFFF))); - musb_writew(mbase, + musb_writel(mbase, MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_HIGH), - ((u16)(((u32) len >> 16) & 0xFFFF))); + len); } #endif /* CONFIG_BLACKFIN */ diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 8259263..e06d65e 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -32,17 +32,11 @@ #include <linux/clk.h> #include <linux/io.h> -#include <asm/mach-types.h> -#include <mach/hardware.h> #include <plat/mux.h> #include "musb_core.h" #include "omap2430.h" -#ifdef CONFIG_ARCH_OMAP3430 -#define get_cpu_rev() 2 -#endif - static struct timer_list musb_idle_timer; @@ -145,10 +139,6 @@ void musb_platform_enable(struct musb *musb) void musb_platform_disable(struct musb *musb) { } -static void omap_vbus_power(struct musb *musb, int is_on, int sleeping) -{ -} - static void omap_set_vbus(struct musb *musb, int is_on) { u8 devctl; @@ -199,9 +189,10 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode) return 0; } -int __init musb_platform_init(struct musb *musb) +int __init musb_platform_init(struct musb *musb, void *board_data) { u32 l; + struct omap_musb_board_data *data = board_data; #if defined(CONFIG_ARCH_OMAP2430) omap_cfg_reg(AE5_2430_USB0HS_STP); @@ -235,7 +226,15 @@ int __init musb_platform_init(struct musb *musb) musb_writel(musb->mregs, OTG_SYSCONFIG, l); l = musb_readl(musb->mregs, OTG_INTERFSEL); - l |= ULPI_12PIN; + + if (data->interface_type == MUSB_INTERFACE_UTMI) { + /* OMAP4 uses Internal PHY GS70 which uses UTMI interface */ + l &= ~ULPI_12PIN; /* Disable ULPI */ + l |= UTMI_8BIT; /* Enable UTMI */ + } else { + l |= ULPI_12PIN; + } + musb_writel(musb->mregs, OTG_INTERFSEL, l); pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, " @@ -246,8 +245,6 @@ int __init musb_platform_init(struct musb *musb) musb_readl(musb->mregs, OTG_INTERFSEL), musb_readl(musb->mregs, OTG_SIMENABLE)); - omap_vbus_power(musb, musb->board_mode == MUSB_HOST, 1); - if (is_host_enabled(musb)) musb->board_set_vbus = omap_set_vbus; @@ -272,7 +269,7 @@ void musb_platform_restore_context(struct musb *musb, } #endif -int musb_platform_suspend(struct musb *musb) +static int musb_platform_suspend(struct musb *musb) { u32 l; @@ -327,8 +324,6 @@ static int musb_platform_resume(struct musb *musb) int musb_platform_exit(struct musb *musb) { - omap_vbus_power(musb, 0 /*off*/, 1); - musb_platform_suspend(musb); return 0; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 60d3938..05c077f 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1104,7 +1104,7 @@ err: return -ENODEV; } -int __init musb_platform_init(struct musb *musb) +int __init musb_platform_init(struct musb *musb, void *board_data) { struct platform_device *pdev; struct resource *mem; diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index 78a2097..4569694 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1654,7 +1654,7 @@ static int __init isp_init(void) { return i2c_add_driver(&isp1301_driver); } -module_init(isp_init); +subsys_initcall(isp_init); static void __exit isp_exit(void) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 223cdf4..0e88885 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -33,6 +33,7 @@ #include <linux/io.h> #include <linux/delay.h> #include <linux/usb/otg.h> +#include <linux/usb/ulpi.h> #include <linux/i2c/twl.h> #include <linux/regulator/consumer.h> #include <linux/err.h> @@ -41,81 +42,7 @@ /* Register defines */ -#define VENDOR_ID_LO 0x00 -#define VENDOR_ID_HI 0x01 -#define PRODUCT_ID_LO 0x02 -#define PRODUCT_ID_HI 0x03 - -#define FUNC_CTRL 0x04 -#define FUNC_CTRL_SET 0x05 -#define FUNC_CTRL_CLR 0x06 -#define FUNC_CTRL_SUSPENDM (1 << 6) -#define FUNC_CTRL_RESET (1 << 5) -#define FUNC_CTRL_OPMODE_MASK (3 << 3) /* bits 3 and 4 */ -#define FUNC_CTRL_OPMODE_NORMAL (0 << 3) -#define FUNC_CTRL_OPMODE_NONDRIVING (1 << 3) -#define FUNC_CTRL_OPMODE_DISABLE_BIT_NRZI (2 << 3) -#define FUNC_CTRL_TERMSELECT (1 << 2) -#define FUNC_CTRL_XCVRSELECT_MASK (3 << 0) /* bits 0 and 1 */ -#define FUNC_CTRL_XCVRSELECT_HS (0 << 0) -#define FUNC_CTRL_XCVRSELECT_FS (1 << 0) -#define FUNC_CTRL_XCVRSELECT_LS (2 << 0) -#define FUNC_CTRL_XCVRSELECT_FS4LS (3 << 0) - -#define IFC_CTRL 0x07 -#define IFC_CTRL_SET 0x08 -#define IFC_CTRL_CLR 0x09 -#define IFC_CTRL_INTERFACE_PROTECT_DISABLE (1 << 7) -#define IFC_CTRL_AUTORESUME (1 << 4) -#define IFC_CTRL_CLOCKSUSPENDM (1 << 3) -#define IFC_CTRL_CARKITMODE (1 << 2) -#define IFC_CTRL_FSLSSERIALMODE_3PIN (1 << 1) - -#define TWL4030_OTG_CTRL 0x0A -#define TWL4030_OTG_CTRL_SET 0x0B -#define TWL4030_OTG_CTRL_CLR 0x0C -#define TWL4030_OTG_CTRL_DRVVBUS (1 << 5) -#define TWL4030_OTG_CTRL_CHRGVBUS (1 << 4) -#define TWL4030_OTG_CTRL_DISCHRGVBUS (1 << 3) -#define TWL4030_OTG_CTRL_DMPULLDOWN (1 << 2) -#define TWL4030_OTG_CTRL_DPPULLDOWN (1 << 1) -#define TWL4030_OTG_CTRL_IDPULLUP (1 << 0) - -#define USB_INT_EN_RISE 0x0D -#define USB_INT_EN_RISE_SET 0x0E -#define USB_INT_EN_RISE_CLR 0x0F -#define USB_INT_EN_FALL 0x10 -#define USB_INT_EN_FALL_SET 0x11 -#define USB_INT_EN_FALL_CLR 0x12 -#define USB_INT_STS 0x13 -#define USB_INT_LATCH 0x14 -#define USB_INT_IDGND (1 << 4) -#define USB_INT_SESSEND (1 << 3) -#define USB_INT_SESSVALID (1 << 2) -#define USB_INT_VBUSVALID (1 << 1) -#define USB_INT_HOSTDISCONNECT (1 << 0) - -#define CARKIT_CTRL 0x19 -#define CARKIT_CTRL_SET 0x1A -#define CARKIT_CTRL_CLR 0x1B -#define CARKIT_CTRL_MICEN (1 << 6) -#define CARKIT_CTRL_SPKRIGHTEN (1 << 5) -#define CARKIT_CTRL_SPKLEFTEN (1 << 4) -#define CARKIT_CTRL_RXDEN (1 << 3) -#define CARKIT_CTRL_TXDEN (1 << 2) -#define CARKIT_CTRL_IDGNDDRV (1 << 1) -#define CARKIT_CTRL_CARKITPWR (1 << 0) -#define CARKIT_PLS_CTRL 0x22 -#define CARKIT_PLS_CTRL_SET 0x23 -#define CARKIT_PLS_CTRL_CLR 0x24 -#define CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3) -#define CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2) -#define CARKIT_PLS_CTRL_RXPLSEN (1 << 1) -#define CARKIT_PLS_CTRL_TXPLSEN (1 << 0) - #define MCPC_CTRL 0x30 -#define MCPC_CTRL_SET 0x31 -#define MCPC_CTRL_CLR 0x32 #define MCPC_CTRL_RTSOL (1 << 7) #define MCPC_CTRL_EXTSWR (1 << 6) #define MCPC_CTRL_EXTSWC (1 << 5) @@ -125,8 +52,6 @@ #define MCPC_CTRL_HS_UART (1 << 0) #define MCPC_IO_CTRL 0x33 -#define MCPC_IO_CTRL_SET 0x34 -#define MCPC_IO_CTRL_CLR 0x35 #define MCPC_IO_CTRL_MICBIASEN (1 << 5) #define MCPC_IO_CTRL_CTS_NPU (1 << 4) #define MCPC_IO_CTRL_RXD_PU (1 << 3) @@ -135,19 +60,13 @@ #define MCPC_IO_CTRL_RTSTYP (1 << 0) #define MCPC_CTRL2 0x36 -#define MCPC_CTRL2_SET 0x37 -#define MCPC_CTRL2_CLR 0x38 #define MCPC_CTRL2_MCPC_CK_EN (1 << 0) #define OTHER_FUNC_CTRL 0x80 -#define OTHER_FUNC_CTRL_SET 0x81 -#define OTHER_FUNC_CTRL_CLR 0x82 #define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) #define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) #define OTHER_IFC_CTRL 0x83 -#define OTHER_IFC_CTRL_SET 0x84 -#define OTHER_IFC_CTRL_CLR 0x85 #define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) #define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) #define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) @@ -156,11 +75,7 @@ #define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) #define OTHER_INT_EN_RISE 0x86 -#define OTHER_INT_EN_RISE_SET 0x87 -#define OTHER_INT_EN_RISE_CLR 0x88 #define OTHER_INT_EN_FALL 0x89 -#define OTHER_INT_EN_FALL_SET 0x8A -#define OTHER_INT_EN_FALL_CLR 0x8B #define OTHER_INT_STS 0x8C #define OTHER_INT_LATCH 0x8D #define OTHER_INT_VB_SESS_VLD (1 << 7) @@ -178,13 +93,9 @@ #define ID_RES_GND (1 << 0) #define POWER_CTRL 0xAC -#define POWER_CTRL_SET 0xAD -#define POWER_CTRL_CLR 0xAE #define POWER_CTRL_OTG_ENAB (1 << 5) #define OTHER_IFC_CTRL2 0xAF -#define OTHER_IFC_CTRL2_SET 0xB0 -#define OTHER_IFC_CTRL2_CLR 0xB1 #define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) #define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) #define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) @@ -193,14 +104,10 @@ #define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) #define REG_CTRL_EN 0xB2 -#define REG_CTRL_EN_SET 0xB3 -#define REG_CTRL_EN_CLR 0xB4 #define REG_CTRL_ERROR 0xB5 #define ULPI_I2C_CONFLICT_INTEN (1 << 0) #define OTHER_FUNC_CTRL2 0xB8 -#define OTHER_FUNC_CTRL2_SET 0xB9 -#define OTHER_FUNC_CTRL2_CLR 0xBA #define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) /* following registers do not have separate _clr and _set registers */ @@ -328,13 +235,13 @@ static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) static inline int twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) { - return twl4030_usb_write(twl, reg + 1, bits); + return twl4030_usb_write(twl, ULPI_SET(reg), bits); } static inline int twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) { - return twl4030_usb_write(twl, reg + 2, bits); + return twl4030_usb_write(twl, ULPI_CLR(reg), bits); } /*-------------------------------------------------------------------------*/ @@ -393,11 +300,12 @@ static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) switch (mode) { case T2_USB_MODE_ULPI: - twl4030_usb_clear_bits(twl, IFC_CTRL, IFC_CTRL_CARKITMODE); + twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, + ULPI_IFC_CTRL_CARKITMODE); twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); - twl4030_usb_clear_bits(twl, FUNC_CTRL, - FUNC_CTRL_XCVRSELECT_MASK | - FUNC_CTRL_OPMODE_MASK); + twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, + ULPI_FUNC_CTRL_XCVRSEL_MASK | + ULPI_FUNC_CTRL_OPMODE_MASK); break; case -1: /* FIXME: power on defaults */ diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c index 9010225..b1b3469 100644 --- a/drivers/usb/otg/ulpi.c +++ b/drivers/usb/otg/ulpi.c @@ -29,28 +29,6 @@ #include <linux/usb/otg.h> #include <linux/usb/ulpi.h> -/* ULPI register addresses */ -#define ULPI_VID_LOW 0x00 /* Vendor ID low */ -#define ULPI_VID_HIGH 0x01 /* Vendor ID high */ -#define ULPI_PID_LOW 0x02 /* Product ID low */ -#define ULPI_PID_HIGH 0x03 /* Product ID high */ -#define ULPI_ITFCTL 0x07 /* Interface Control */ -#define ULPI_OTGCTL 0x0A /* OTG Control */ - -/* add to above register address to access Set/Clear functions */ -#define ULPI_REG_SET 0x01 -#define ULPI_REG_CLEAR 0x02 - -/* ULPI OTG Control Register bits */ -#define ID_PULL_UP (1 << 0) /* enable ID Pull Up */ -#define DP_PULL_DOWN (1 << 1) /* enable DP Pull Down */ -#define DM_PULL_DOWN (1 << 2) /* enable DM Pull Down */ -#define DISCHRG_VBUS (1 << 3) /* Discharge Vbus */ -#define CHRG_VBUS (1 << 4) /* Charge Vbus */ -#define DRV_VBUS (1 << 5) /* Drive Vbus */ -#define DRV_VBUS_EXT (1 << 6) /* Drive Vbus external */ -#define USE_EXT_VBUS_IND (1 << 7) /* Use ext. Vbus indicator */ - #define ULPI_ID(vendor, product) (((vendor) << 16) | (product)) #define TR_FLAG(flags, a, b) (((flags) & a) ? b : 0) @@ -65,28 +43,28 @@ static int ulpi_set_flags(struct otg_transceiver *otg) unsigned int flags = 0; if (otg->flags & USB_OTG_PULLUP_ID) - flags |= ID_PULL_UP; + flags |= ULPI_OTG_CTRL_ID_PULLUP; if (otg->flags & USB_OTG_PULLDOWN_DM) - flags |= DM_PULL_DOWN; + flags |= ULPI_OTG_CTRL_DM_PULLDOWN; if (otg->flags & USB_OTG_PULLDOWN_DP) - flags |= DP_PULL_DOWN; + flags |= ULPI_OTG_CTRL_DP_PULLDOWN; if (otg->flags & USB_OTG_EXT_VBUS_INDICATOR) - flags |= USE_EXT_VBUS_IND; + flags |= ULPI_OTG_CTRL_EXTVBUSIND; - return otg_io_write(otg, flags, ULPI_OTGCTL + ULPI_REG_SET); + return otg_io_write(otg, flags, ULPI_SET(ULPI_OTG_CTRL)); } static int ulpi_init(struct otg_transceiver *otg) { int i, vid, pid; - vid = (otg_io_read(otg, ULPI_VID_HIGH) << 8) | - otg_io_read(otg, ULPI_VID_LOW); - pid = (otg_io_read(otg, ULPI_PID_HIGH) << 8) | - otg_io_read(otg, ULPI_PID_LOW); + vid = (otg_io_read(otg, ULPI_VENDOR_ID_HIGH) << 8) | + otg_io_read(otg, ULPI_VENDOR_ID_LOW); + pid = (otg_io_read(otg, ULPI_PRODUCT_ID_HIGH) << 8) | + otg_io_read(otg, ULPI_PRODUCT_ID_LOW); pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid); @@ -100,19 +78,19 @@ static int ulpi_init(struct otg_transceiver *otg) static int ulpi_set_vbus(struct otg_transceiver *otg, bool on) { - unsigned int flags = otg_io_read(otg, ULPI_OTGCTL); + unsigned int flags = otg_io_read(otg, ULPI_OTG_CTRL); - flags &= ~(DRV_VBUS | DRV_VBUS_EXT); + flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT); if (on) { if (otg->flags & USB_OTG_DRV_VBUS) - flags |= DRV_VBUS; + flags |= ULPI_OTG_CTRL_DRVVBUS; if (otg->flags & USB_OTG_DRV_VBUS_EXT) - flags |= DRV_VBUS_EXT; + flags |= ULPI_OTG_CTRL_DRVVBUS_EXT; } - return otg_io_write(otg, flags, ULPI_OTGCTL + ULPI_REG_SET); + return otg_io_write(otg, flags, ULPI_SET(ULPI_OTG_CTRL)); } struct otg_transceiver * diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index a0ecb42..bd8aab0 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -425,6 +425,16 @@ config USB_SERIAL_MOS7720 To compile this driver as a module, choose M here: the module will be called mos7720. +config USB_SERIAL_MOS7715_PARPORT + bool "Support for parallel port on the Moschip 7715" + depends on USB_SERIAL_MOS7720 + depends on PARPORT=y || PARPORT=USB_SERIAL_MOS7720 + select PARPORT_NOT_PC + ---help--- + Say Y if you have a Moschip 7715 device and would like to use + the parallel port it provides. The port will register with + the parport subsystem as a low-level driver. + config USB_SERIAL_MOS7840 tristate "USB Moschip 7840/7820 USB Serial Driver" ---help--- @@ -485,6 +495,7 @@ config USB_SERIAL_QCAUX config USB_SERIAL_QUALCOMM tristate "USB Qualcomm Serial modem" + select USB_SERIAL_WWAN help Say Y here if you have a Qualcomm USB modem device. These are usually wireless cellular modems. @@ -576,8 +587,12 @@ config USB_SERIAL_XIRCOM To compile this driver as a module, choose M here: the module will be called keyspan_pda. +config USB_SERIAL_WWAN + tristate + config USB_SERIAL_OPTION tristate "USB driver for GSM and CDMA modems" + select USB_SERIAL_WWAN help Say Y here if you have a GSM or CDMA modem that's connected to USB. @@ -619,6 +634,14 @@ config USB_SERIAL_VIVOPAY_SERIAL To compile this driver as a module, choose M here: the module will be called vivopay-serial. +config USB_SERIAL_ZIO + tristate "ZIO Motherboard USB serial interface driver" + help + Say Y here if you want to use ZIO Motherboard. + + To compile this driver as a module, choose M here: the + module will be called zio. + config USB_SERIAL_DEBUG tristate "USB Debugging Device" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 83c9e43..e54c728 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -52,9 +52,11 @@ obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o +obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o obj-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda.o obj-$(CONFIG_USB_SERIAL_VIVOPAY_SERIAL) += vivopay-serial.o +obj-$(CONFIG_USB_SERIAL_ZIO) += zio.o diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 4fd7af9..0db6ace 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -1,7 +1,9 @@ /* * AIRcable USB Bluetooth Dongle Driver. * + * Copyright (C) 2010 Johan Hovold <jhovold@gmail.com> * Copyright (C) 2006 Manuel Francisco Naranjo (naranjo.manuel@gmail.com) + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 as published by the * Free Software Foundation. @@ -42,10 +44,10 @@ * */ +#include <asm/unaligned.h> #include <linux/tty.h> #include <linux/slab.h> #include <linux/tty_flip.h> -#include <linux/circ_buf.h> #include <linux/usb.h> #include <linux/usb/serial.h> @@ -55,16 +57,12 @@ static int debug; #define AIRCABLE_VID 0x16CA #define AIRCABLE_USB_PID 0x1502 -/* write buffer size defines */ -#define AIRCABLE_BUF_SIZE 2048 - /* Protocol Stuff */ #define HCI_HEADER_LENGTH 0x4 #define TX_HEADER_0 0x20 #define TX_HEADER_1 0x29 #define RX_HEADER_0 0x00 #define RX_HEADER_1 0x20 -#define MAX_HCI_FRAMESIZE 60 #define HCI_COMPLETE_FRAME 64 /* rx_flags */ @@ -74,8 +72,8 @@ static int debug; /* * Version Information */ -#define DRIVER_VERSION "v1.0b2" -#define DRIVER_AUTHOR "Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>" +#define DRIVER_VERSION "v2.0" +#define DRIVER_AUTHOR "Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>, Johan Hovold <jhovold@gmail.com>" #define DRIVER_DESC "AIRcable USB Driver" /* ID table that will be registered with USB core */ @@ -85,226 +83,21 @@ static const struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); - -/* Internal Structure */ -struct aircable_private { - spinlock_t rx_lock; /* spinlock for the receive lines */ - struct circ_buf *tx_buf; /* write buffer */ - struct circ_buf *rx_buf; /* read buffer */ - int rx_flags; /* for throttilng */ - struct work_struct rx_work; /* work cue for the receiving line */ - struct usb_serial_port *port; /* USB port with which associated */ -}; - -/* Private methods */ - -/* Circular Buffer Methods, code from ti_usb_3410_5052 used */ -/* - * serial_buf_clear - * - * Clear out all data in the circular buffer. - */ -static void serial_buf_clear(struct circ_buf *cb) -{ - cb->head = cb->tail = 0; -} - -/* - * serial_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ -static struct circ_buf *serial_buf_alloc(void) -{ - struct circ_buf *cb; - cb = kmalloc(sizeof(struct circ_buf), GFP_KERNEL); - if (cb == NULL) - return NULL; - cb->buf = kmalloc(AIRCABLE_BUF_SIZE, GFP_KERNEL); - if (cb->buf == NULL) { - kfree(cb); - return NULL; - } - serial_buf_clear(cb); - return cb; -} - -/* - * serial_buf_free - * - * Free the buffer and all associated memory. - */ -static void serial_buf_free(struct circ_buf *cb) -{ - kfree(cb->buf); - kfree(cb); -} - -/* - * serial_buf_data_avail - * - * Return the number of bytes of data available in the circular - * buffer. - */ -static int serial_buf_data_avail(struct circ_buf *cb) -{ - return CIRC_CNT(cb->head, cb->tail, AIRCABLE_BUF_SIZE); -} - -/* - * serial_buf_put - * - * Copy data data from a user buffer and put it into the circular buffer. - * Restrict to the amount of space available. - * - * Return the number of bytes copied. - */ -static int serial_buf_put(struct circ_buf *cb, const char *buf, int count) -{ - int c, ret = 0; - while (1) { - c = CIRC_SPACE_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - memcpy(cb->buf + cb->head, buf, c); - cb->head = (cb->head + c) & (AIRCABLE_BUF_SIZE-1); - buf += c; - count -= c; - ret = c; - } - return ret; -} - -/* - * serial_buf_get - * - * Get data from the circular buffer and copy to the given buffer. - * Restrict to the amount of data available. - * - * Return the number of bytes copied. - */ -static int serial_buf_get(struct circ_buf *cb, char *buf, int count) -{ - int c, ret = 0; - while (1) { - c = CIRC_CNT_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - memcpy(buf, cb->buf + cb->tail, c); - cb->tail = (cb->tail + c) & (AIRCABLE_BUF_SIZE-1); - buf += c; - count -= c; - ret = c; - } - return ret; -} - -/* End of circula buffer methods */ - -static void aircable_send(struct usb_serial_port *port) +static int aircable_prepare_write_buffer(struct usb_serial_port *port, + void *dest, size_t size) { - int count, result; - struct aircable_private *priv = usb_get_serial_port_data(port); - unsigned char *buf; - __le16 *dbuf; - dbg("%s - port %d", __func__, port->number); - if (port->write_urb_busy) - return; - - count = min(serial_buf_data_avail(priv->tx_buf), MAX_HCI_FRAMESIZE); - if (count == 0) - return; - - buf = kzalloc(count + HCI_HEADER_LENGTH, GFP_ATOMIC); - if (!buf) { - dev_err(&port->dev, "%s- kzalloc(%d) failed.\n", - __func__, count + HCI_HEADER_LENGTH); - return; - } + int count; + unsigned char *buf = dest; + count = kfifo_out_locked(&port->write_fifo, buf + HCI_HEADER_LENGTH, + size - HCI_HEADER_LENGTH, &port->lock); buf[0] = TX_HEADER_0; buf[1] = TX_HEADER_1; - dbuf = (__le16 *)&buf[2]; - *dbuf = cpu_to_le16((u16)count); - serial_buf_get(priv->tx_buf, buf + HCI_HEADER_LENGTH, - MAX_HCI_FRAMESIZE); - - memcpy(port->write_urb->transfer_buffer, buf, - count + HCI_HEADER_LENGTH); - - kfree(buf); - port->write_urb_busy = 1; - usb_serial_debug_data(debug, &port->dev, __func__, - count + HCI_HEADER_LENGTH, - port->write_urb->transfer_buffer); - port->write_urb->transfer_buffer_length = count + HCI_HEADER_LENGTH; - port->write_urb->dev = port->serial->dev; - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - - if (result) { - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", - __func__, result); - port->write_urb_busy = 0; - } + put_unaligned_le16(count, &buf[2]); - schedule_work(&port->work); + return count + HCI_HEADER_LENGTH; } -static void aircable_read(struct work_struct *work) -{ - struct aircable_private *priv = - container_of(work, struct aircable_private, rx_work); - struct usb_serial_port *port = priv->port; - struct tty_struct *tty; - unsigned char *data; - int count; - if (priv->rx_flags & THROTTLED) { - if (priv->rx_flags & ACTUALLY_THROTTLED) - schedule_work(&priv->rx_work); - return; - } - - /* By now I will flush data to the tty in packages of no more than - * 64 bytes, to ensure I do not get throttled. - * Ask USB mailing list for better aproach. - */ - tty = tty_port_tty_get(&port->port); - - if (!tty) { - schedule_work(&priv->rx_work); - dev_err(&port->dev, "%s - No tty available\n", __func__); - return ; - } - - count = min(64, serial_buf_data_avail(priv->rx_buf)); - - if (count <= 0) - goto out; /* We have finished sending everything. */ - - tty_prepare_flip_string(tty, &data, count); - if (!data) { - dev_err(&port->dev, "%s- kzalloc(%d) failed.", - __func__, count); - goto out; - } - - serial_buf_get(priv->rx_buf, data, count); - - tty_flip_buffer_push(tty); - - if (serial_buf_data_avail(priv->rx_buf)) - schedule_work(&priv->rx_work); -out: - tty_kref_put(tty); - return; -} -/* End of private methods */ - static int aircable_probe(struct usb_serial *serial, const struct usb_device_id *id) { @@ -330,247 +123,50 @@ static int aircable_probe(struct usb_serial *serial, return 0; } -static int aircable_attach(struct usb_serial *serial) -{ - struct usb_serial_port *port = serial->port[0]; - struct aircable_private *priv; - - priv = kzalloc(sizeof(struct aircable_private), GFP_KERNEL); - if (!priv) { - dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__, - sizeof(struct aircable_private)); - return -ENOMEM; - } - - /* Allocation of Circular Buffers */ - priv->tx_buf = serial_buf_alloc(); - if (priv->tx_buf == NULL) { - kfree(priv); - return -ENOMEM; - } - - priv->rx_buf = serial_buf_alloc(); - if (priv->rx_buf == NULL) { - kfree(priv->tx_buf); - kfree(priv); - return -ENOMEM; - } - - priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); - priv->port = port; - INIT_WORK(&priv->rx_work, aircable_read); - - usb_set_serial_port_data(serial->port[0], priv); - - return 0; -} - -static void aircable_release(struct usb_serial *serial) +static int aircable_process_packet(struct tty_struct *tty, + struct usb_serial_port *port, int has_headers, + char *packet, int len) { - - struct usb_serial_port *port = serial->port[0]; - struct aircable_private *priv = usb_get_serial_port_data(port); - - dbg("%s", __func__); - - if (priv) { - serial_buf_free(priv->tx_buf); - serial_buf_free(priv->rx_buf); - kfree(priv); + if (has_headers) { + len -= HCI_HEADER_LENGTH; + packet += HCI_HEADER_LENGTH; } -} - -static int aircable_write_room(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct aircable_private *priv = usb_get_serial_port_data(port); - return serial_buf_data_avail(priv->tx_buf); -} - -static int aircable_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *source, int count) -{ - struct aircable_private *priv = usb_get_serial_port_data(port); - int temp; - - dbg("%s - port %d, %d bytes", __func__, port->number, count); - - usb_serial_debug_data(debug, &port->dev, __func__, count, source); - - if (!count) { - dbg("%s - write request of 0 bytes", __func__); - return count; + if (len <= 0) { + dbg("%s - malformed packet", __func__); + return 0; } - temp = serial_buf_put(priv->tx_buf, source, count); - - aircable_send(port); - - if (count > AIRCABLE_BUF_SIZE) - count = AIRCABLE_BUF_SIZE; - - return count; + tty_insert_flip_string(tty, packet, len); + return len; } -static void aircable_write_bulk_callback(struct urb *urb) +static void aircable_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; - int status = urb->status; - int result; - - dbg("%s - urb status: %d", __func__ , status); - - /* This has been taken from cypress_m8.c cypress_write_int_callback */ - switch (status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", - __func__, status); - port->write_urb_busy = 0; - return; - default: - /* error in the urb, so we have to resubmit it */ - dbg("%s - Overflow in write", __func__); - dbg("%s - nonzero write bulk status received: %d", - __func__, status); - port->write_urb->transfer_buffer_length = 1; - port->write_urb->dev = port->serial->dev; - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) - dev_err(&urb->dev->dev, - "%s - failed resubmitting write urb, error %d\n", - __func__, result); - else - return; - } - - port->write_urb_busy = 0; - - aircable_send(port); -} - -static void aircable_read_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - struct aircable_private *priv = usb_get_serial_port_data(port); + char *data = (char *)urb->transfer_buffer; struct tty_struct *tty; - unsigned long no_packages, remaining, package_length, i; - int result, shift = 0; - unsigned char *temp; - int status = urb->status; - - dbg("%s - port %d", __func__, port->number); - - if (status) { - dbg("%s - urb status = %d", __func__, status); - if (status == -EPROTO) { - dbg("%s - caught -EPROTO, resubmitting the urb", - __func__); - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - aircable_read_bulk_callback, port); - - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result) - dev_err(&urb->dev->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); - return; - } - dbg("%s - unable to handle the error, exiting.", __func__); - return; - } - - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, urb->transfer_buffer); + int has_headers; + int count; + int len; + int i; tty = tty_port_tty_get(&port->port); - if (tty && urb->actual_length) { - if (urb->actual_length <= 2) { - /* This is an incomplete package */ - serial_buf_put(priv->rx_buf, urb->transfer_buffer, - urb->actual_length); - } else { - temp = urb->transfer_buffer; - if (temp[0] == RX_HEADER_0) - shift = HCI_HEADER_LENGTH; - - remaining = urb->actual_length; - no_packages = urb->actual_length / (HCI_COMPLETE_FRAME); - - if (urb->actual_length % HCI_COMPLETE_FRAME != 0) - no_packages++; + if (!tty) + return; - for (i = 0; i < no_packages; i++) { - if (remaining > (HCI_COMPLETE_FRAME)) - package_length = HCI_COMPLETE_FRAME; - else - package_length = remaining; - remaining -= package_length; + has_headers = (urb->actual_length > 2 && data[0] == RX_HEADER_0); - serial_buf_put(priv->rx_buf, - urb->transfer_buffer + shift + - (HCI_COMPLETE_FRAME) * (i), - package_length - shift); - } - } - aircable_read(&priv->rx_work); + count = 0; + for (i = 0; i < urb->actual_length; i += HCI_COMPLETE_FRAME) { + len = min_t(int, urb->actual_length - i, HCI_COMPLETE_FRAME); + count += aircable_process_packet(tty, port, has_headers, + &data[i], len); } - tty_kref_put(tty); - - /* Schedule the next read */ - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - aircable_read_bulk_callback, port); - - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result && result != -EPERM) - dev_err(&urb->dev->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); -} - -/* Based on ftdi_sio.c throttle */ -static void aircable_throttle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct aircable_private *priv = usb_get_serial_port_data(port); - dbg("%s - port %d", __func__, port->number); - - spin_lock_irq(&priv->rx_lock); - priv->rx_flags |= THROTTLED; - spin_unlock_irq(&priv->rx_lock); -} - -/* Based on ftdi_sio.c unthrottle */ -static void aircable_unthrottle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct aircable_private *priv = usb_get_serial_port_data(port); - int actually_throttled; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irq(&priv->rx_lock); - actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; - priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); - spin_unlock_irq(&priv->rx_lock); - - if (actually_throttled) - schedule_work(&priv->rx_work); + if (count) + tty_flip_buffer_push(tty); + tty_kref_put(tty); } static struct usb_driver aircable_driver = { @@ -589,15 +185,12 @@ static struct usb_serial_driver aircable_device = { .usb_driver = &aircable_driver, .id_table = id_table, .num_ports = 1, - .attach = aircable_attach, + .bulk_out_size = HCI_COMPLETE_FRAME, .probe = aircable_probe, - .release = aircable_release, - .write = aircable_write, - .write_room = aircable_write_room, - .write_bulk_callback = aircable_write_bulk_callback, - .read_bulk_callback = aircable_read_bulk_callback, - .throttle = aircable_throttle, - .unthrottle = aircable_unthrottle, + .process_read_urb = aircable_process_read_urb, + .prepare_write_buffer = aircable_prepare_write_buffer, + .throttle = usb_serial_generic_throttle, + .unthrottle = usb_serial_generic_unthrottle, }; static int __init aircable_init(void) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 9b66bf1..4e41a2a 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -42,7 +42,7 @@ static int debug; * Version information */ -#define DRIVER_VERSION "v0.5" +#define DRIVER_VERSION "v0.6" #define DRIVER_AUTHOR "Bart Hartgers <bart.hartgers+ark3116@gmail.com>" #define DRIVER_DESC "USB ARK3116 serial/IrDA driver" #define DRIVER_DEV_DESC "ARK3116 RS232/IrDA" @@ -355,14 +355,11 @@ static void ark3116_close(struct usb_serial_port *port) /* deactivate interrupts */ ark3116_write_reg(serial, UART_IER, 0); - /* shutdown any bulk reads that might be going on */ - if (serial->num_bulk_out) - usb_kill_urb(port->write_urb); - if (serial->num_bulk_in) - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); if (serial->num_interrupt_in) usb_kill_urb(port->interrupt_in_urb); } + } static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port) @@ -675,87 +672,45 @@ static void ark3116_read_int_callback(struct urb *urb) * error for the next block of data as well... * For now, let's pretend this can't happen. */ - -static void send_to_tty(struct tty_struct *tty, - const unsigned char *chars, - size_t size, char flag) +static void ark3116_process_read_urb(struct urb *urb) { - if (size == 0) - return; - if (flag == TTY_NORMAL) { - tty_insert_flip_string(tty, chars, size); - } else { - int i; - for (i = 0; i < size; ++i) - tty_insert_flip_char(tty, chars[i], flag); - } -} - -static void ark3116_read_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; + struct usb_serial_port *port = urb->context; struct ark3116_private *priv = usb_get_serial_port_data(port); - const __u8 *data = urb->transfer_buffer; - int status = urb->status; struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; + char tty_flag = TTY_NORMAL; unsigned long flags; - int result; - char flag; __u32 lsr; - switch (status) { - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", - __func__, status); + /* update line status */ + spin_lock_irqsave(&priv->status_lock, flags); + lsr = priv->lsr; + priv->lsr &= ~UART_LSR_BRK_ERROR_BITS; + spin_unlock_irqrestore(&priv->status_lock, flags); + + if (!urb->actual_length) return; - default: - dbg("%s - nonzero urb status received: %d", - __func__, status); - break; - case 0: /* success */ - spin_lock_irqsave(&priv->status_lock, flags); - lsr = priv->lsr; - /* clear error bits */ - priv->lsr &= ~UART_LSR_BRK_ERROR_BITS; - spin_unlock_irqrestore(&priv->status_lock, flags); - - if (unlikely(lsr & UART_LSR_BI)) - flag = TTY_BREAK; - else if (unlikely(lsr & UART_LSR_PE)) - flag = TTY_PARITY; - else if (unlikely(lsr & UART_LSR_FE)) - flag = TTY_FRAME; - else - flag = TTY_NORMAL; - - tty = tty_port_tty_get(&port->port); - if (tty) { - /* overrun is special, not associated with a char */ - if (unlikely(lsr & UART_LSR_OE)) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - send_to_tty(tty, data, urb->actual_length, flag); - tty_flip_buffer_push(tty); - tty_kref_put(tty); - } + tty = tty_port_tty_get(&port->port); + if (!tty) + return; + + if (lsr & UART_LSR_BRK_ERROR_BITS) { + if (lsr & UART_LSR_BI) + tty_flag = TTY_BREAK; + else if (lsr & UART_LSR_PE) + tty_flag = TTY_PARITY; + else if (lsr & UART_LSR_FE) + tty_flag = TTY_FRAME; - /* Throttle the device if requested by tty */ - spin_lock_irqsave(&port->lock, flags); - port->throttled = port->throttle_req; - if (port->throttled) { - spin_unlock_irqrestore(&port->lock, flags); - return; - } else - spin_unlock_irqrestore(&port->lock, flags); + /* overrun is special, not associated with a char */ + if (lsr & UART_LSR_OE) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); } - /* Continue reading from device */ - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result) - dev_err(&urb->dev->dev, "%s - failed resubmitting" - " read urb, error %d\n", __func__, result); + tty_insert_flip_string_fixed_flag(tty, data, tty_flag, + urb->actual_length); + tty_flip_buffer_push(tty); + tty_kref_put(tty); } static struct usb_driver ark3116_driver = { @@ -785,7 +740,7 @@ static struct usb_serial_driver ark3116_device = { .close = ark3116_close, .break_ctl = ark3116_break_ctl, .read_int_callback = ark3116_read_int_callback, - .read_bulk_callback = ark3116_read_bulk_callback, + .process_read_urb = ark3116_process_read_urb, }; static int __init ark3116_init(void) diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 1295e44..36df352 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -3,6 +3,7 @@ * * Copyright (C) 2000 William Greathouse (wgreathouse@smva.com) * Copyright (C) 2000-2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2010 Johan Hovold (jhovold@gmail.com) * * This program is largely derived from work by the linux-usb group * and associated source files. Please see the usb/serial files for @@ -84,7 +85,7 @@ static int debug; /* * Version Information */ -#define DRIVER_VERSION "v1.2" +#define DRIVER_VERSION "v1.3" #define DRIVER_AUTHOR "William Greathouse <wgreathouse@smva.com>" #define DRIVER_DESC "USB Belkin Serial converter driver" @@ -95,6 +96,7 @@ static int belkin_sa_open(struct tty_struct *tty, struct usb_serial_port *port); static void belkin_sa_close(struct usb_serial_port *port); static void belkin_sa_read_int_callback(struct urb *urb); +static void belkin_sa_process_read_urb(struct urb *urb); static void belkin_sa_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state); @@ -112,7 +114,6 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(BELKIN_DOCKSTATION_VID, BELKIN_DOCKSTATION_PID) }, { } /* Terminating entry */ }; - MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_driver belkin_driver = { @@ -120,7 +121,7 @@ static struct usb_driver belkin_driver = { .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table_combined, - .no_dynamic_id = 1, + .no_dynamic_id = 1, }; /* All of the device info needed for the serial converters */ @@ -136,7 +137,7 @@ static struct usb_serial_driver belkin_device = { .open = belkin_sa_open, .close = belkin_sa_close, .read_int_callback = belkin_sa_read_int_callback, - /* How we get the status info */ + .process_read_urb = belkin_sa_process_read_urb, .set_termios = belkin_sa_set_termios, .break_ctl = belkin_sa_break_ctl, .tiocmget = belkin_sa_tiocmget, @@ -145,7 +146,6 @@ static struct usb_serial_driver belkin_device = { .release = belkin_sa_release, }; - struct belkin_sa_private { spinlock_t lock; unsigned long control_state; @@ -196,62 +196,43 @@ static int belkin_sa_startup(struct usb_serial *serial) return 0; } - static void belkin_sa_release(struct usb_serial *serial) { - struct belkin_sa_private *priv; int i; dbg("%s", __func__); - for (i = 0; i < serial->num_ports; ++i) { - /* My special items, the standard routines free my urbs */ - priv = usb_get_serial_port_data(serial->port[i]); - kfree(priv); - } + for (i = 0; i < serial->num_ports; ++i) + kfree(usb_get_serial_port_data(serial->port[i])); } - -static int belkin_sa_open(struct tty_struct *tty, +static int belkin_sa_open(struct tty_struct *tty, struct usb_serial_port *port) { - int retval = 0; + int retval; dbg("%s port %d", __func__, port->number); - /*Start reading from the device*/ - /* TODO: Look at possibility of submitting multiple URBs to device to - * enhance buffering. Win trace shows 16 initial read URBs. - */ - port->read_urb->dev = port->serial->dev; - retval = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (retval) { - dev_err(&port->dev, "usb_submit_urb(read bulk) failed\n"); - goto exit; - } - - port->interrupt_in_urb->dev = port->serial->dev; retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (retval) { - usb_kill_urb(port->read_urb); dev_err(&port->dev, "usb_submit_urb(read int) failed\n"); + return retval; } -exit: - return retval; -} /* belkin_sa_open */ + retval = usb_serial_generic_open(tty, port); + if (retval) + usb_kill_urb(port->interrupt_in_urb); + return retval; +} static void belkin_sa_close(struct usb_serial_port *port) { dbg("%s port %d", __func__, port->number); - /* shutdown our bulk reads and writes */ - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); usb_kill_urb(port->interrupt_in_urb); -} /* belkin_sa_close */ - +} static void belkin_sa_read_int_callback(struct urb *urb) { @@ -310,31 +291,7 @@ static void belkin_sa_read_int_callback(struct urb *urb) else priv->control_state &= ~TIOCM_CD; - /* Now to report any errors */ priv->last_lsr = data[BELKIN_SA_LSR_INDEX]; -#if 0 - /* - * fill in the flip buffer here, but I do not know the relation - * to the current/next receive buffer or characters. I need - * to look in to this before committing any code. - */ - if (priv->last_lsr & BELKIN_SA_LSR_ERR) { - tty = tty_port_tty_get(&port->port); - /* Overrun Error */ - if (priv->last_lsr & BELKIN_SA_LSR_OE) { - } - /* Parity Error */ - if (priv->last_lsr & BELKIN_SA_LSR_PE) { - } - /* Framing Error */ - if (priv->last_lsr & BELKIN_SA_LSR_FE) { - } - /* Break Indicator */ - if (priv->last_lsr & BELKIN_SA_LSR_BI) { - } - tty_kref_put(tty); - } -#endif spin_unlock_irqrestore(&priv->lock, flags); exit: retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -343,6 +300,53 @@ exit: "result %d\n", __func__, retval); } +static void belkin_sa_process_read_urb(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + struct belkin_sa_private *priv = usb_get_serial_port_data(port); + struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; + unsigned long flags; + unsigned char status; + char tty_flag; + + /* Update line status */ + tty_flag = TTY_NORMAL; + + spin_lock_irqsave(&priv->lock, flags); + status = priv->last_lsr; + priv->last_lsr &= ~BELKIN_SA_LSR_ERR; + spin_unlock_irqrestore(&priv->lock, flags); + + if (!urb->actual_length) + return; + + tty = tty_port_tty_get(&port->port); + if (!tty) + return; + + if (status & BELKIN_SA_LSR_ERR) { + /* Break takes precedence over parity, which takes precedence + * over framing errors. */ + if (status & BELKIN_SA_LSR_BI) + tty_flag = TTY_BREAK; + else if (status & BELKIN_SA_LSR_PE) + tty_flag = TTY_PARITY; + else if (status & BELKIN_SA_LSR_FE) + tty_flag = TTY_FRAME; + dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); + + /* Overrun is special, not associated with a char. */ + if (status & BELKIN_SA_LSR_OE) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + + tty_insert_flip_string_fixed_flag(tty, data, tty_flag, + urb->actual_length); + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + static void belkin_sa_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { @@ -482,8 +486,7 @@ static void belkin_sa_set_termios(struct tty_struct *tty, spin_lock_irqsave(&priv->lock, flags); priv->control_state = control_state; spin_unlock_irqrestore(&priv->lock, flags); -} /* belkin_sa_set_termios */ - +} static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state) { @@ -494,7 +497,6 @@ static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state) dev_err(&port->dev, "Set break_ctl %d\n", break_state); } - static int belkin_sa_tiocmget(struct tty_struct *tty, struct file *file) { struct usb_serial_port *port = tty->driver_data; @@ -511,7 +513,6 @@ static int belkin_sa_tiocmget(struct tty_struct *tty, struct file *file) return control_state; } - static int belkin_sa_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { @@ -583,7 +584,6 @@ failed_usb_serial_register: return retval; } - static void __exit belkin_sa_exit (void) { usb_deregister(&belkin_driver); diff --git a/drivers/usb/serial/belkin_sa.h b/drivers/usb/serial/belkin_sa.h index c66a673..c74b58a 100644 --- a/drivers/usb/serial/belkin_sa.h +++ b/drivers/usb/serial/belkin_sa.h @@ -8,10 +8,10 @@ * and associated source files. Please see the usb/serial files for * individual credits and copyrights. * - * 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 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. * * See Documentation/usb/usb-serial.txt for more information on using this * driver @@ -66,7 +66,7 @@ #ifdef WHEN_I_LEARN_THIS #define BELKIN_SA_SET_MAGIC_REQUEST 17 /* I don't know, possibly flush */ /* (always in Wininit sequence before flow control) */ -#define BELKIN_SA_RESET xx /* Reset the port */ +#define BELKIN_SA_RESET xx /* Reset the port */ #define BELKIN_SA_GET_MODEM_STATUS xx /* Force return of modem status register */ #endif diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 7e8e398..63f7cc4 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -305,10 +305,7 @@ static void ch341_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); - /* shutdown our urbs */ - dbg("%s - shutting down urbs", __func__); - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); usb_kill_urb(port->interrupt_in_urb); } diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index f347da2..1ee6b2a 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -66,7 +66,7 @@ static int usb_console_setup(struct console *co, char *options) struct usb_serial_port *port; int retval; struct tty_struct *tty = NULL; - struct ktermios *termios = NULL, dummy; + struct ktermios dummy; dbg("%s", __func__); @@ -141,15 +141,14 @@ static int usb_console_setup(struct console *co, char *options) goto reset_open_count; } kref_init(&tty->kref); - termios = kzalloc(sizeof(*termios), GFP_KERNEL); - if (!termios) { + tty_port_tty_set(&port->port, tty); + tty->driver = usb_serial_tty_driver; + tty->index = co->index; + if (tty_init_termios(tty)) { retval = -ENOMEM; err("no more memory"); goto free_tty; } - memset(&dummy, 0, sizeof(struct ktermios)); - tty->termios = termios; - tty_port_tty_set(&port->port, tty); } /* only call the device specific open if this @@ -161,16 +160,16 @@ static int usb_console_setup(struct console *co, char *options) if (retval) { err("could not open USB console port"); - goto free_termios; + goto fail; } if (serial->type->set_termios) { - termios->c_cflag = cflag; - tty_termios_encode_baud_rate(termios, baud, baud); + tty->termios->c_cflag = cflag; + tty_termios_encode_baud_rate(tty->termios, baud, baud); + memset(&dummy, 0, sizeof(struct ktermios)); serial->type->set_termios(tty, port, &dummy); tty_port_tty_set(&port->port, NULL); - kfree(termios); kfree(tty); } set_bit(ASYNCB_INITIALIZED, &port->port.flags); @@ -180,14 +179,12 @@ static int usb_console_setup(struct console *co, char *options) --port->port.count; /* The console is special in terms of closing the device so * indicate this port is now acting as a system console. */ - port->console = 1; port->port.console = 1; mutex_unlock(&serial->disc_mutex); return retval; - free_termios: - kfree(termios); + fail: tty_port_tty_set(&port->port, NULL); free_tty: kfree(tty); @@ -217,7 +214,7 @@ static void usb_console_write(struct console *co, dbg("%s - port %d, %d byte(s)", __func__, port->number, count); - if (!port->console) { + if (!port->port.console) { dbg("%s - port not opened", __func__); return; } @@ -313,7 +310,7 @@ void usb_serial_console_exit(void) { if (usbcons_info.port) { unregister_console(&usbcons); - usbcons_info.port->console = 0; + usbcons_info.port->port.console = 0; usbcons_info.port = NULL; } } diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index ec9b044..8b8c797 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -34,7 +34,6 @@ * Function Prototypes */ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *); -static void cp210x_cleanup(struct usb_serial_port *); static void cp210x_close(struct usb_serial_port *); static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *port); @@ -49,7 +48,6 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *, unsigned int, unsigned int); static void cp210x_break_ctl(struct tty_struct *, int); static int cp210x_startup(struct usb_serial *); -static void cp210x_disconnect(struct usb_serial *); static void cp210x_dtr_rts(struct usb_serial_port *p, int on); static int cp210x_carrier_raised(struct usb_serial_port *p); @@ -61,6 +59,8 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x0745, 0x1000) }, /* CipherLab USB CCD Barcode Scanner 1000 */ { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */ { USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */ + { USB_DEVICE(0x0BED, 0x1100) }, /* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */ + { USB_DEVICE(0x0BED, 0x1101) }, /* MEI series 2000 Combo Acceptor */ { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ { USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */ { USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */ @@ -72,9 +72,12 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x1601) }, /* Arkham Technology DS101 Adapter */ { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ + { USB_DEVICE(0x10C4, 0x8044) }, /* Cygnal Debug Adapter */ + { USB_DEVICE(0x10C4, 0x804E) }, /* Software Bisque Paramount ME build-in converter */ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ { USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */ { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ + { USB_DEVICE(0x10C4, 0x806F) }, /* IMS USB to RS422 Converter Cable */ { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */ { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ { USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */ @@ -82,12 +85,15 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8115) }, /* Arygon NFC/Mifare Reader */ { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */ { USB_DEVICE(0x10C4, 0x813F) }, /* Tams Master Easy Control */ + { USB_DEVICE(0x10C4, 0x8149) }, /* West Mountain Radio Computerized Battery Analyzer */ { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */ { USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */ { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */ + { USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */ { USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */ { USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */ { USB_DEVICE(0x10C4, 0x81AC) }, /* MSD Dash Hawk */ + { USB_DEVICE(0x10C4, 0x81AD) }, /* INSYS USB Modem */ { USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */ { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */ { USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */ @@ -105,6 +111,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */ { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */ { USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */ { USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */ @@ -115,6 +122,8 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */ { USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */ { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ + { USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */ + { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ { } /* Terminating Entry */ @@ -138,6 +147,8 @@ static struct usb_serial_driver cp210x_device = { .usb_driver = &cp210x_driver, .id_table = id_table, .num_ports = 1, + .bulk_in_size = 256, + .bulk_out_size = 256, .open = cp210x_open, .close = cp210x_close, .break_ctl = cp210x_break_ctl, @@ -145,7 +156,6 @@ static struct usb_serial_driver cp210x_device = { .tiocmget = cp210x_tiocmget, .tiocmset = cp210x_tiocmset, .attach = cp210x_startup, - .disconnect = cp210x_disconnect, .dtr_rts = cp210x_dtr_rts, .carrier_raised = cp210x_carrier_raised }; @@ -370,7 +380,6 @@ static unsigned int cp210x_quantise_baudrate(unsigned int baud) { static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; int result; dbg("%s - port %d", __func__, port->number); @@ -381,49 +390,20 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) return -EPROTO; } - /* Start reading from the device */ - usb_fill_bulk_urb(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - serial->type->read_bulk_callback, - port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) { - dev_err(&port->dev, "%s - failed resubmitting read urb, " - "error %d\n", __func__, result); + result = usb_serial_generic_open(tty, port); + if (result) return result; - } /* Configure the termios structure */ cp210x_get_termios(tty, port); return 0; } -static void cp210x_cleanup(struct usb_serial_port *port) -{ - struct usb_serial *serial = port->serial; - - dbg("%s - port %d", __func__, port->number); - - if (serial->dev) { - /* shutdown any bulk reads that might be going on */ - if (serial->num_bulk_out) - usb_kill_urb(port->write_urb); - if (serial->num_bulk_in) - usb_kill_urb(port->read_urb); - } -} - static void cp210x_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); - /* shutdown our urbs */ - dbg("%s - shutting down urbs", __func__); - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); mutex_lock(&port->serial->disc_mutex); if (!port->serial->disconnected) @@ -807,17 +787,6 @@ static int cp210x_startup(struct usb_serial *serial) return 0; } -static void cp210x_disconnect(struct usb_serial *serial) -{ - int i; - - dbg("%s", __func__); - - /* Stop reads and writes on all ports */ - for (i = 0; i < serial->num_ports; ++i) - cp210x_cleanup(serial->port[i]); -} - static int __init cp210x_init(void) { int retval; diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index e23c779..f5d0674 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -64,6 +64,7 @@ #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/serial.h> +#include <linux/kfifo.h> #include <linux/delay.h> #include <linux/uaccess.h> #include <asm/unaligned.h> @@ -79,13 +80,12 @@ static int unstable_bauds; /* * Version Information */ -#define DRIVER_VERSION "v1.09" +#define DRIVER_VERSION "v1.10" #define DRIVER_AUTHOR "Lonnie Mendez <dignome@gmail.com>, Neil Whelchel <koyama@firstlight.net>" #define DRIVER_DESC "Cypress USB to Serial Driver" /* write buffer size defines */ #define CYPRESS_BUF_SIZE 1024 -#define CYPRESS_CLOSING_WAIT (30*HZ) static const struct usb_device_id id_table_earthmate[] = { { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) }, @@ -135,7 +135,7 @@ struct cypress_private { int bytes_out; /* used for statistics */ int cmd_count; /* used for statistics */ int cmd_ctrl; /* always set this to 1 before issuing a command */ - struct cypress_buf *buf; /* write buffer */ + struct kfifo write_fifo; /* write fifo */ int write_urb_in_use; /* write urb in use indicator */ int write_urb_interval; /* interval to use for write urb */ int read_urb_interval; /* interval to use for read urb */ @@ -157,14 +157,6 @@ struct cypress_private { struct ktermios tmp_termios; /* stores the old termios settings */ }; -/* write buffer structure */ -struct cypress_buf { - unsigned int buf_size; - char *buf_buf; - char *buf_get; - char *buf_put; -}; - /* function prototypes for the Cypress USB to serial device */ static int cypress_earthmate_startup(struct usb_serial *serial); static int cypress_hidcom_startup(struct usb_serial *serial); @@ -190,17 +182,6 @@ static void cypress_unthrottle(struct tty_struct *tty); static void cypress_set_dead(struct usb_serial_port *port); static void cypress_read_int_callback(struct urb *urb); static void cypress_write_int_callback(struct urb *urb); -/* write buffer functions */ -static struct cypress_buf *cypress_buf_alloc(unsigned int size); -static void cypress_buf_free(struct cypress_buf *cb); -static void cypress_buf_clear(struct cypress_buf *cb); -static unsigned int cypress_buf_data_avail(struct cypress_buf *cb); -static unsigned int cypress_buf_space_avail(struct cypress_buf *cb); -static unsigned int cypress_buf_put(struct cypress_buf *cb, - const char *buf, unsigned int count); -static unsigned int cypress_buf_get(struct cypress_buf *cb, - char *buf, unsigned int count); - static struct usb_serial_driver cypress_earthmate_device = { .driver = { @@ -503,8 +484,7 @@ static int generic_startup(struct usb_serial *serial) priv->comm_is_ok = !0; spin_lock_init(&priv->lock); - priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE); - if (priv->buf == NULL) { + if (kfifo_alloc(&priv->write_fifo, CYPRESS_BUF_SIZE, GFP_KERNEL)) { kfree(priv); return -ENOMEM; } @@ -627,7 +607,7 @@ static void cypress_release(struct usb_serial *serial) priv = usb_get_serial_port_data(serial->port[0]); if (priv) { - cypress_buf_free(priv->buf); + kfifo_free(&priv->write_fifo); kfree(priv); } } @@ -704,6 +684,7 @@ static void cypress_dtr_rts(struct usb_serial_port *port, int on) static void cypress_close(struct usb_serial_port *port) { struct cypress_private *priv = usb_get_serial_port_data(port); + unsigned long flags; dbg("%s - port %d", __func__, port->number); @@ -713,12 +694,14 @@ static void cypress_close(struct usb_serial_port *port) mutex_unlock(&port->serial->disc_mutex); return; } - cypress_buf_clear(priv->buf); + spin_lock_irqsave(&priv->lock, flags); + kfifo_reset_out(&priv->write_fifo); + spin_unlock_irqrestore(&priv->lock, flags); + dbg("%s - stopping urbs", __func__); usb_kill_urb(port->interrupt_in_urb); usb_kill_urb(port->interrupt_out_urb); - if (stats) dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n", priv->bytes_in, priv->bytes_out, priv->cmd_count); @@ -730,7 +713,6 @@ static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct cypress_private *priv = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d, %d bytes", __func__, port->number, count); @@ -745,9 +727,7 @@ static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, if (!count) return count; - spin_lock_irqsave(&priv->lock, flags); - count = cypress_buf_put(priv->buf, buf, count); - spin_unlock_irqrestore(&priv->lock, flags); + count = kfifo_in_locked(&priv->write_fifo, buf, count, &priv->lock); finish: cypress_send(port); @@ -807,9 +787,10 @@ static void cypress_send(struct usb_serial_port *port) } else spin_unlock_irqrestore(&priv->lock, flags); - count = cypress_buf_get(priv->buf, &port->interrupt_out_buffer[offset], - port->interrupt_out_size-offset); - + count = kfifo_out_locked(&priv->write_fifo, + &port->interrupt_out_buffer[offset], + port->interrupt_out_size - offset, + &priv->lock); if (count == 0) return; @@ -875,7 +856,7 @@ static int cypress_write_room(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); - room = cypress_buf_space_avail(priv->buf); + room = kfifo_avail(&priv->write_fifo); spin_unlock_irqrestore(&priv->lock, flags); dbg("%s - returns %d", __func__, room); @@ -1143,7 +1124,7 @@ static int cypress_chars_in_buffer(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); - chars = cypress_buf_data_avail(priv->buf); + chars = kfifo_len(&priv->write_fifo); spin_unlock_irqrestore(&priv->lock, flags); dbg("%s - returns %d", __func__, chars); @@ -1309,7 +1290,7 @@ static void cypress_read_int_callback(struct urb *urb) /* process read if there is data other than line status */ if (tty && bytes > i) { tty_insert_flip_string_fixed_flag(tty, data + i, - bytes - i, tty_flag); + tty_flag, bytes - i); tty_flip_buffer_push(tty); } @@ -1397,193 +1378,6 @@ static void cypress_write_int_callback(struct urb *urb) /***************************************************************************** - * Write buffer functions - buffering code from pl2303 used - *****************************************************************************/ - -/* - * cypress_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ - -static struct cypress_buf *cypress_buf_alloc(unsigned int size) -{ - - struct cypress_buf *cb; - - - if (size == 0) - return NULL; - - cb = kmalloc(sizeof(struct cypress_buf), GFP_KERNEL); - if (cb == NULL) - return NULL; - - cb->buf_buf = kmalloc(size, GFP_KERNEL); - if (cb->buf_buf == NULL) { - kfree(cb); - return NULL; - } - - cb->buf_size = size; - cb->buf_get = cb->buf_put = cb->buf_buf; - - return cb; - -} - - -/* - * cypress_buf_free - * - * Free the buffer and all associated memory. - */ - -static void cypress_buf_free(struct cypress_buf *cb) -{ - if (cb) { - kfree(cb->buf_buf); - kfree(cb); - } -} - - -/* - * cypress_buf_clear - * - * Clear out all data in the circular buffer. - */ - -static void cypress_buf_clear(struct cypress_buf *cb) -{ - if (cb != NULL) - cb->buf_get = cb->buf_put; - /* equivalent to a get of all data available */ -} - - -/* - * cypress_buf_data_avail - * - * Return the number of bytes of data available in the circular - * buffer. - */ - -static unsigned int cypress_buf_data_avail(struct cypress_buf *cb) -{ - if (cb != NULL) - return (cb->buf_size + cb->buf_put - cb->buf_get) - % cb->buf_size; - else - return 0; -} - - -/* - * cypress_buf_space_avail - * - * Return the number of bytes of space available in the circular - * buffer. - */ - -static unsigned int cypress_buf_space_avail(struct cypress_buf *cb) -{ - if (cb != NULL) - return (cb->buf_size + cb->buf_get - cb->buf_put - 1) - % cb->buf_size; - else - return 0; -} - - -/* - * cypress_buf_put - * - * Copy data data from a user buffer and put it into the circular buffer. - * Restrict to the amount of space available. - * - * Return the number of bytes copied. - */ - -static unsigned int cypress_buf_put(struct cypress_buf *cb, const char *buf, - unsigned int count) -{ - - unsigned int len; - - - if (cb == NULL) - return 0; - - len = cypress_buf_space_avail(cb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = cb->buf_buf + cb->buf_size - cb->buf_put; - if (count > len) { - memcpy(cb->buf_put, buf, len); - memcpy(cb->buf_buf, buf+len, count - len); - cb->buf_put = cb->buf_buf + count - len; - } else { - memcpy(cb->buf_put, buf, count); - if (count < len) - cb->buf_put += count; - else /* count == len */ - cb->buf_put = cb->buf_buf; - } - - return count; - -} - - -/* - * cypress_buf_get - * - * Get data from the circular buffer and copy to the given buffer. - * Restrict to the amount of data available. - * - * Return the number of bytes copied. - */ - -static unsigned int cypress_buf_get(struct cypress_buf *cb, char *buf, - unsigned int count) -{ - - unsigned int len; - - - if (cb == NULL) - return 0; - - len = cypress_buf_data_avail(cb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = cb->buf_buf + cb->buf_size - cb->buf_get; - if (count > len) { - memcpy(buf, cb->buf_get, len); - memcpy(buf+len, cb->buf_buf, count - len); - cb->buf_get = cb->buf_buf + count - len; - } else { - memcpy(buf, cb->buf_get, count); - if (count < len) - cb->buf_get += count; - else /* count == len */ - cb->buf_get = cb->buf_buf; - } - - return count; - -} - -/***************************************************************************** * Module functions *****************************************************************************/ diff --git a/drivers/usb/serial/cypress_m8.h b/drivers/usb/serial/cypress_m8.h index 1fd360e..67cf608 100644 --- a/drivers/usb/serial/cypress_m8.h +++ b/drivers/usb/serial/cypress_m8.h @@ -1,27 +1,32 @@ #ifndef CYPRESS_M8_H #define CYPRESS_M8_H -/* definitions and function prototypes used for the cypress USB to Serial controller */ +/* + * definitions and function prototypes used for the cypress USB to Serial + * controller + */ -/* For sending our feature buffer - controlling serial communication states */ -/* Linux HID has no support for serial devices so we do this through the driver */ -#define HID_REQ_GET_REPORT 0x01 -#define HID_REQ_SET_REPORT 0x09 +/* + * For sending our feature buffer - controlling serial communication states. + * Linux HID has no support for serial devices so we do this through the driver + */ +#define HID_REQ_GET_REPORT 0x01 +#define HID_REQ_SET_REPORT 0x09 /* List other cypress USB to Serial devices here, and add them to the id_table */ /* DeLorme Earthmate USB - a GPS device */ -#define VENDOR_ID_DELORME 0x1163 -#define PRODUCT_ID_EARTHMATEUSB 0x0100 -#define PRODUCT_ID_EARTHMATEUSB_LT20 0x0200 +#define VENDOR_ID_DELORME 0x1163 +#define PRODUCT_ID_EARTHMATEUSB 0x0100 +#define PRODUCT_ID_EARTHMATEUSB_LT20 0x0200 /* Cypress HID->COM RS232 Adapter */ -#define VENDOR_ID_CYPRESS 0x04b4 -#define PRODUCT_ID_CYPHIDCOM 0x5500 +#define VENDOR_ID_CYPRESS 0x04b4 +#define PRODUCT_ID_CYPHIDCOM 0x5500 /* Powercom UPS, chip CY7C63723 */ -#define VENDOR_ID_POWERCOM 0x0d9f -#define PRODUCT_ID_UPS 0x0002 +#define VENDOR_ID_POWERCOM 0x0d9f +#define PRODUCT_ID_UPS 0x0002 /* Nokia CA-42 USB to serial cable */ #define VENDOR_ID_DAZZLE 0x07d0 @@ -29,17 +34,17 @@ /* End of device listing */ /* Used for setting / requesting serial line settings */ -#define CYPRESS_SET_CONFIG 0x01 -#define CYPRESS_GET_CONFIG 0x02 +#define CYPRESS_SET_CONFIG 0x01 +#define CYPRESS_GET_CONFIG 0x02 /* Used for throttle control */ -#define THROTTLED 0x1 -#define ACTUALLY_THROTTLED 0x2 +#define THROTTLED 0x1 +#define ACTUALLY_THROTTLED 0x2 -/* chiptypes - used in case firmware differs from the generic form ... offering - * different baud speeds/etc. +/* + * chiptypes - used in case firmware differs from the generic form ... offering + * different baud speeds/etc. */ - #define CT_EARTHMATE 0x01 #define CT_CYPHIDCOM 0x02 #define CT_CA42V2 0x03 @@ -50,15 +55,15 @@ /* these are sent / read at byte 0 of the input/output hid reports */ /* You can find these values defined in the CY4601 USB to Serial design notes */ -#define CONTROL_DTR 0x20 /* data terminal ready - flow control - host to device */ +#define CONTROL_DTR 0x20 /* data terminal ready - flow control - host to device */ #define UART_DSR 0x20 /* data set ready - flow control - device to host */ -#define CONTROL_RTS 0x10 /* request to send - flow control - host to device */ +#define CONTROL_RTS 0x10 /* request to send - flow control - host to device */ #define UART_CTS 0x10 /* clear to send - flow control - device to host */ -#define UART_RI 0x10 /* ring indicator - modem - device to host */ +#define UART_RI 0x10 /* ring indicator - modem - device to host */ #define UART_CD 0x40 /* carrier detect - modem - device to host */ -#define CYP_ERROR 0x08 /* received from input report - device to host */ +#define CYP_ERROR 0x08 /* received from input report - device to host */ /* Note - the below has nothing to do with the "feature report" reset */ -#define CONTROL_RESET 0x08 /* sent with output report - host to device */ +#define CONTROL_RESET 0x08 /* sent with output report - host to device */ /* End of RS-232 protocol definitions */ diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 68b0aa5..3edda3e 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1703,8 +1703,8 @@ static int digi_read_inb_callback(struct urb *urb) /* data length is len-1 (one byte of len is port_status) */ --len; if (len > 0) { - tty_insert_flip_string_fixed_flag(tty, data, len, - flag); + tty_insert_flip_string_fixed_flag(tty, data, flag, + len); tty_flip_buffer_push(tty); } } diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 5f740a1..504b558 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -13,44 +13,6 @@ * * See Documentation/usb/usb-serial.txt for more information on using this * driver - * - * (07/16/2001) gb - * remove unused code in empeg_close() (thanks to Oliver Neukum for - * pointing this out) and rewrote empeg_set_termios(). - * - * (05/30/2001) gkh - * switched from using spinlock to a semaphore, which fixes lots of - * problems. - * - * (04/08/2001) gb - * Identify version on module load. - * - * (01/22/2001) gb - * Added write_room() and chars_in_buffer() support. - * - * (12/21/2000) gb - * Moved termio stuff inside the port->active check. - * Moved MOD_DEC_USE_COUNT to end of empeg_close(). - * - * (12/03/2000) gb - * Added tty->ldisc.set_termios(port, tty, NULL) to empeg_open(). - * This notifies the tty driver that the termios have changed. - * - * (11/13/2000) gb - * Moved tty->low_latency = 1 from empeg_read_bulk_callback() to - * empeg_open() (It only needs to be set once - Doh!) - * - * (11/11/2000) gb - * Updated to work with id_table structure. - * - * (11/04/2000) gb - * Forked this from visor.c, and hacked it up to work with an - * Empeg ltd. empeg-car player. Constructive criticism welcomed. - * I would like to say, 'Thank You' to Greg Kroah-Hartman for the - * use of his code, and for his guidance, advice and patience. :) - * A 'Thank You' is in order for John Ripley of Empeg ltd for his - * advice, and patience too. - * */ #include <linux/kernel.h> @@ -71,7 +33,7 @@ static int debug; /* * Version Information */ -#define DRIVER_VERSION "v1.2" +#define DRIVER_VERSION "v1.3" #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Gary Brubaker <xavyer@ix.netcom.com>" #define DRIVER_DESC "USB Empeg Mark I/II Driver" @@ -79,19 +41,8 @@ static int debug; #define EMPEG_PRODUCT_ID 0x0001 /* function prototypes for an empeg-car player */ -static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port); -static void empeg_close(struct usb_serial_port *port); -static int empeg_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, - int count); -static int empeg_write_room(struct tty_struct *tty); -static int empeg_chars_in_buffer(struct tty_struct *tty); -static void empeg_throttle(struct tty_struct *tty); -static void empeg_unthrottle(struct tty_struct *tty); static int empeg_startup(struct usb_serial *serial); static void empeg_init_termios(struct tty_struct *tty); -static void empeg_write_bulk_callback(struct urb *urb); -static void empeg_read_bulk_callback(struct urb *urb); static const struct usb_device_id id_table[] = { { USB_DEVICE(EMPEG_VENDOR_ID, EMPEG_PRODUCT_ID) }, @@ -105,7 +56,7 @@ static struct usb_driver empeg_driver = { .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, - .no_dynamic_id = 1, + .no_dynamic_id = 1, }; static struct usb_serial_driver empeg_device = { @@ -114,291 +65,16 @@ static struct usb_serial_driver empeg_device = { .name = "empeg", }, .id_table = id_table, - .usb_driver = &empeg_driver, + .usb_driver = &empeg_driver, .num_ports = 1, - .open = empeg_open, - .close = empeg_close, - .throttle = empeg_throttle, - .unthrottle = empeg_unthrottle, + .bulk_out_size = 256, + .throttle = usb_serial_generic_throttle, + .unthrottle = usb_serial_generic_unthrottle, .attach = empeg_startup, .init_termios = empeg_init_termios, - .write = empeg_write, - .write_room = empeg_write_room, - .chars_in_buffer = empeg_chars_in_buffer, - .write_bulk_callback = empeg_write_bulk_callback, - .read_bulk_callback = empeg_read_bulk_callback, }; -#define NUM_URBS 16 -#define URB_TRANSFER_BUFFER_SIZE 4096 - -static struct urb *write_urb_pool[NUM_URBS]; -static spinlock_t write_urb_pool_lock; -static int bytes_in; -static int bytes_out; - -/****************************************************************************** - * Empeg specific driver functions - ******************************************************************************/ -static int empeg_open(struct tty_struct *tty,struct usb_serial_port *port) -{ - struct usb_serial *serial = port->serial; - int result = 0; - - dbg("%s - port %d", __func__, port->number); - - bytes_in = 0; - bytes_out = 0; - - /* Start reading from the device */ - usb_fill_bulk_urb( - port->read_urb, - serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - empeg_read_bulk_callback, - port); - - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, result); - - return result; -} - - -static void empeg_close(struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); - - /* shutdown our bulk read */ - usb_kill_urb(port->read_urb); - /* Uncomment the following line if you want to see some statistics in your syslog */ - /* dev_info (&port->dev, "Bytes In = %d Bytes Out = %d\n", bytes_in, bytes_out); */ -} - - -static int empeg_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - struct usb_serial *serial = port->serial; - struct urb *urb; - const unsigned char *current_position = buf; - unsigned long flags; - int status; - int i; - int bytes_sent = 0; - int transfer_size; - - dbg("%s - port %d", __func__, port->number); - - while (count > 0) { - /* try to find a free urb in our list of them */ - urb = NULL; - - spin_lock_irqsave(&write_urb_pool_lock, flags); - - for (i = 0; i < NUM_URBS; ++i) { - if (write_urb_pool[i]->status != -EINPROGRESS) { - urb = write_urb_pool[i]; - break; - } - } - - spin_unlock_irqrestore(&write_urb_pool_lock, flags); - - if (urb == NULL) { - dbg("%s - no more free urbs", __func__); - goto exit; - } - - if (urb->transfer_buffer == NULL) { - urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC); - if (urb->transfer_buffer == NULL) { - dev_err(&port->dev, - "%s no more kernel memory...\n", - __func__); - goto exit; - } - } - - transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); - - memcpy(urb->transfer_buffer, current_position, transfer_size); - - usb_serial_debug_data(debug, &port->dev, __func__, transfer_size, urb->transfer_buffer); - - /* build up our urb */ - usb_fill_bulk_urb( - urb, - serial->dev, - usb_sndbulkpipe(serial->dev, - port->bulk_out_endpointAddress), - urb->transfer_buffer, - transfer_size, - empeg_write_bulk_callback, - port); - - /* send it down the pipe */ - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed with status = %d\n", __func__, status); - bytes_sent = status; - break; - } - - current_position += transfer_size; - bytes_sent += transfer_size; - count -= transfer_size; - bytes_out += transfer_size; - - } -exit: - return bytes_sent; -} - - -static int empeg_write_room(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - unsigned long flags; - int i; - int room = 0; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&write_urb_pool_lock, flags); - /* tally up the number of bytes available */ - for (i = 0; i < NUM_URBS; ++i) { - if (write_urb_pool[i]->status != -EINPROGRESS) - room += URB_TRANSFER_BUFFER_SIZE; - } - spin_unlock_irqrestore(&write_urb_pool_lock, flags); - dbg("%s - returns %d", __func__, room); - return room; - -} - - -static int empeg_chars_in_buffer(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - unsigned long flags; - int i; - int chars = 0; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&write_urb_pool_lock, flags); - - /* tally up the number of bytes waiting */ - for (i = 0; i < NUM_URBS; ++i) { - if (write_urb_pool[i]->status == -EINPROGRESS) - chars += URB_TRANSFER_BUFFER_SIZE; - } - - spin_unlock_irqrestore(&write_urb_pool_lock, flags); - dbg("%s - returns %d", __func__, chars); - return chars; -} - - -static void empeg_write_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - int status = urb->status; - - dbg("%s - port %d", __func__, port->number); - - if (status) { - dbg("%s - nonzero write bulk status received: %d", - __func__, status); - return; - } - - usb_serial_port_softint(port); -} - - -static void empeg_read_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - struct tty_struct *tty; - unsigned char *data = urb->transfer_buffer; - int result; - int status = urb->status; - - dbg("%s - port %d", __func__, port->number); - - if (status) { - dbg("%s - nonzero read bulk status received: %d", - __func__, status); - return; - } - - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - tty = tty_port_tty_get(&port->port); - - if (urb->actual_length) { - tty_insert_flip_string(tty, data, urb->actual_length); - tty_flip_buffer_push(tty); - bytes_in += urb->actual_length; - } - tty_kref_put(tty); - - /* Continue trying to always read */ - usb_fill_bulk_urb( - port->read_urb, - port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - empeg_read_bulk_callback, - port); - - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - - if (result) - dev_err(&urb->dev->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); - - return; - -} - - -static void empeg_throttle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - dbg("%s - port %d", __func__, port->number); - usb_kill_urb(port->read_urb); -} - - -static void empeg_unthrottle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - int result; - dbg("%s - port %d", __func__, port->number); - - port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, result); -} - - -static int empeg_startup(struct usb_serial *serial) +static int empeg_startup(struct usb_serial *serial) { int r; @@ -414,10 +90,8 @@ static int empeg_startup(struct usb_serial *serial) /* continue on with initialization */ return r; - } - static void empeg_init_termios(struct tty_struct *tty) { struct ktermios *termios = tty->termios; @@ -462,77 +136,28 @@ static void empeg_init_termios(struct tty_struct *tty) tty_encode_baud_rate(tty, 115200, 115200); } - static int __init empeg_init(void) { - struct urb *urb; - int i, retval; - - /* create our write urb pool and transfer buffers */ - spin_lock_init(&write_urb_pool_lock); - for (i = 0; i < NUM_URBS; ++i) { - urb = usb_alloc_urb(0, GFP_KERNEL); - write_urb_pool[i] = urb; - if (urb == NULL) { - printk(KERN_ERR "empeg: No more urbs???\n"); - continue; - } - - urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, - GFP_KERNEL); - if (!urb->transfer_buffer) { - printk(KERN_ERR "empeg: %s - out of memory for urb " - "buffers.", __func__); - continue; - } - } + int retval; retval = usb_serial_register(&empeg_device); if (retval) - goto failed_usb_serial_register; + return retval; retval = usb_register(&empeg_driver); - if (retval) - goto failed_usb_register; - + if (retval) { + usb_serial_deregister(&empeg_device); + return retval; + } printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" DRIVER_DESC "\n"); return 0; -failed_usb_register: - usb_serial_deregister(&empeg_device); -failed_usb_serial_register: - for (i = 0; i < NUM_URBS; ++i) { - if (write_urb_pool[i]) { - kfree(write_urb_pool[i]->transfer_buffer); - usb_free_urb(write_urb_pool[i]); - } - } - return retval; } - static void __exit empeg_exit(void) { - int i; - unsigned long flags; - usb_deregister(&empeg_driver); usb_serial_deregister(&empeg_device); - - spin_lock_irqsave(&write_urb_pool_lock, flags); - - for (i = 0; i < NUM_URBS; ++i) { - if (write_urb_pool[i]) { - /* FIXME - uncomment the following usb_kill_urb call - * when the host controllers get fixed to set urb->dev - * = NULL after the urb is finished. Otherwise this - * call oopses. */ - /* usb_kill_urb(write_urb_pool[i]); */ - kfree(write_urb_pool[i]->transfer_buffer); - usb_free_urb(write_urb_pool[i]); - } - } - spin_unlock_irqrestore(&write_urb_pool_lock, flags); } diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 1d7c4fa..050211a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1,6 +1,8 @@ /* * USB FTDI SIO driver * + * Copyright (C) 2009 - 2010 + * Johan Hovold (jhovold@gmail.com) * Copyright (C) 1999 - 2001 * Greg Kroah-Hartman (greg@kroah.com) * Bill Ryder (bryder@sgi.com) @@ -49,8 +51,8 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.5.0" -#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>, Andreas Mohr" +#define DRIVER_VERSION "v1.6.0" +#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>, Andreas Mohr, Johan Hovold <jhovold@gmail.com>" #define DRIVER_DESC "USB FTDI Serial Converters Driver" static int debug; @@ -59,7 +61,7 @@ static __u16 product; struct ftdi_private { struct kref kref; - ftdi_chip_type_t chip_type; + enum ftdi_chip_type chip_type; /* type of device, either SIO or FT8U232AM */ int baud_base; /* baud base clock for divisor setting */ int custom_divisor; /* custom_divisor kludge, this is for @@ -69,10 +71,6 @@ struct ftdi_private { /* the last data state set - needed for doing * a break */ - int write_offset; /* This is the offset in the usb data block to - * write the serial data - it varies between - * devices - */ int flags; /* some ASYNC_xxxx flags are supported */ unsigned long last_dtr_rts; /* saved modem control outputs */ wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ @@ -87,9 +85,6 @@ struct ftdi_private { be enabled */ unsigned int latency; /* latency setting in use */ - spinlock_t tx_lock; /* spinlock for transmit state */ - unsigned long tx_outstanding_bytes; - unsigned long tx_outstanding_urbs; unsigned short max_packet_size; struct mutex cfg_lock; /* Avoid mess by parallel calls of config ioctl() and change_speed() */ }; @@ -768,9 +763,6 @@ static const char *ftdi_chip_name[] = { }; -/* Constants for read urb and write urb */ -#define BUFSZ 512 - /* Used for TIOCMIWAIT */ #define FTDI_STATUS_B0_MASK (FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD) #define FTDI_STATUS_B1_MASK (FTDI_RS_BI) @@ -787,13 +779,9 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port); static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port); static void ftdi_close(struct usb_serial_port *port); static void ftdi_dtr_rts(struct usb_serial_port *port, int on); -static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count); -static int ftdi_write_room(struct tty_struct *tty); -static int ftdi_chars_in_buffer(struct tty_struct *tty); -static void ftdi_write_bulk_callback(struct urb *urb); -static void ftdi_read_bulk_callback(struct urb *urb); -static void ftdi_process_read(struct usb_serial_port *port); +static void ftdi_process_read_urb(struct urb *urb); +static int ftdi_prepare_write_buffer(struct usb_serial_port *port, + void *dest, size_t size); static void ftdi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int ftdi_tiocmget(struct tty_struct *tty, struct file *file); @@ -802,8 +790,6 @@ static int ftdi_tiocmset(struct tty_struct *tty, struct file *file, static int ftdi_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void ftdi_break_ctl(struct tty_struct *tty, int break_state); -static void ftdi_throttle(struct tty_struct *tty); -static void ftdi_unthrottle(struct tty_struct *tty); static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base); static unsigned short int ftdi_232am_baud_to_divisor(int baud); @@ -821,19 +807,18 @@ static struct usb_serial_driver ftdi_sio_device = { .usb_driver = &ftdi_driver, .id_table = id_table_combined, .num_ports = 1, + .bulk_in_size = 512, + .bulk_out_size = 256, .probe = ftdi_sio_probe, .port_probe = ftdi_sio_port_probe, .port_remove = ftdi_sio_port_remove, .open = ftdi_open, .close = ftdi_close, .dtr_rts = ftdi_dtr_rts, - .throttle = ftdi_throttle, - .unthrottle = ftdi_unthrottle, - .write = ftdi_write, - .write_room = ftdi_write_room, - .chars_in_buffer = ftdi_chars_in_buffer, - .read_bulk_callback = ftdi_read_bulk_callback, - .write_bulk_callback = ftdi_write_bulk_callback, + .throttle = usb_serial_generic_throttle, + .unthrottle = usb_serial_generic_unthrottle, + .process_read_urb = ftdi_process_read_urb, + .prepare_write_buffer = ftdi_prepare_write_buffer, .tiocmget = ftdi_tiocmget, .tiocmset = ftdi_tiocmset, .ioctl = ftdi_ioctl, @@ -849,9 +834,6 @@ static struct usb_serial_driver ftdi_sio_device = { #define HIGH 1 #define LOW 0 -/* number of outstanding urbs to prevent userspace DoS from happening */ -#define URB_UPPER_LIMIT 42 - /* * *************************************************************************** * Utility functions @@ -987,7 +969,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, static __u32 get_ftdi_divisor(struct tty_struct *tty, struct usb_serial_port *port) -{ /* get_ftdi_divisor */ +{ struct ftdi_private *priv = usb_get_serial_port_data(port); __u32 div_value = 0; int div_okay = 1; @@ -1211,12 +1193,11 @@ static int get_serial_info(struct usb_serial_port *port, if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0; -} /* get_serial_info */ - +} static int set_serial_info(struct tty_struct *tty, struct usb_serial_port *port, struct serial_struct __user *newinfo) -{ /* set_serial_info */ +{ struct ftdi_private *priv = usb_get_serial_port_data(port); struct serial_struct new_serial; struct ftdi_private old_priv; @@ -1279,8 +1260,7 @@ check_and_exit: else mutex_unlock(&priv->cfg_lock); return 0; - -} /* set_serial_info */ +} /* Determine type of FTDI chip based on USB config and descriptor. */ @@ -1294,7 +1274,6 @@ static void ftdi_determine_type(struct usb_serial_port *port) /* Assume it is not the original SIO device for now. */ priv->baud_base = 48000000 / 2; - priv->write_offset = 0; version = le16_to_cpu(udev->descriptor.bcdDevice); interfaces = udev->actconfig->desc.bNumInterfaces; @@ -1336,7 +1315,6 @@ static void ftdi_determine_type(struct usb_serial_port *port) /* Old device. Assume it's the original SIO. */ priv->chip_type = SIO; priv->baud_base = 12000000 / 16; - priv->write_offset = 1; } else if (version < 0x400) { /* Assume it's an FT8U232AM (or FT8U245AM) */ /* (It might be a BM because of the iSerialNumber bug, @@ -1543,7 +1521,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) } kref_init(&priv->kref); - spin_lock_init(&priv->tx_lock); mutex_init(&priv->cfg_lock); init_waitqueue_head(&priv->delta_msr_wait); @@ -1552,28 +1529,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) if (quirk && quirk->port_probe) quirk->port_probe(priv); - /* Increase the size of read buffers */ - kfree(port->bulk_in_buffer); - port->bulk_in_buffer = kmalloc(BUFSZ, GFP_KERNEL); - if (!port->bulk_in_buffer) { - kfree(priv); - return -ENOMEM; - } - if (port->read_urb) { - port->read_urb->transfer_buffer = port->bulk_in_buffer; - port->read_urb->transfer_buffer_length = BUFSZ; - } - priv->port = port; - - /* Free port's existing write urb and transfer buffer. */ - if (port->write_urb) { - usb_free_urb(port->write_urb); - port->write_urb = NULL; - } - kfree(port->bulk_out_buffer); - port->bulk_out_buffer = NULL; - usb_set_serial_port_data(port, priv); ftdi_determine_type(port); @@ -1594,7 +1550,7 @@ static void ftdi_USB_UIRT_setup(struct ftdi_private *priv) priv->flags |= ASYNC_SPD_CUST; priv->custom_divisor = 77; priv->force_baud = 38400; -} /* ftdi_USB_UIRT_setup */ +} /* Setup for the HE-TIRA1 device, which requires hardwired * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled. */ @@ -1607,7 +1563,7 @@ static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv) priv->custom_divisor = 240; priv->force_baud = 38400; priv->force_rtscts = 1; -} /* ftdi_HE_TIRA1_setup */ +} /* * Module parameter to control latency timer for NDI FTDI-based USB devices. @@ -1700,31 +1656,10 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) return 0; } -static int ftdi_submit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) -{ - struct urb *urb = port->read_urb; - struct usb_serial *serial = port->serial; - int result; - - usb_fill_bulk_urb(urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - urb->transfer_buffer, - urb->transfer_buffer_length, - ftdi_read_bulk_callback, port); - result = usb_submit_urb(urb, mem_flags); - if (result && result != -EPERM) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, result); - return result; -} - static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) -{ /* ftdi_open */ +{ struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); - unsigned long flags; int result; dbg("%s", __func__); @@ -1746,20 +1681,13 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) if (tty) ftdi_set_termios(tty, port, tty->termios); - /* Not throttled */ - spin_lock_irqsave(&port->lock, flags); - port->throttled = 0; - port->throttle_req = 0; - spin_unlock_irqrestore(&port->lock, flags); - /* Start reading from the device */ - result = ftdi_submit_read_urb(port, GFP_KERNEL); + result = usb_serial_generic_open(tty, port); if (!result) kref_get(&priv->kref); return result; -} /* ftdi_open */ - +} static void ftdi_dtr_rts(struct usb_serial_port *port, int on) { @@ -1789,22 +1717,16 @@ static void ftdi_dtr_rts(struct usb_serial_port *port, int on) * usbserial:__serial_close only calls ftdi_close if the point is open * * This only gets called when it is the last close - * - * */ - static void ftdi_close(struct usb_serial_port *port) -{ /* ftdi_close */ +{ struct ftdi_private *priv = usb_get_serial_port_data(port); dbg("%s", __func__); - /* shutdown our bulk read */ - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); kref_put(&priv->kref, ftdi_sio_priv_release); -} /* ftdi_close */ - - +} /* The SIO requires the first byte to have: * B0 1 @@ -1813,211 +1735,39 @@ static void ftdi_close(struct usb_serial_port *port) * * The new devices do not require this byte */ -static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count) -{ /* ftdi_write */ - struct ftdi_private *priv = usb_get_serial_port_data(port); - struct urb *urb; - unsigned char *buffer; - int data_offset ; /* will be 1 for the SIO and 0 otherwise */ - int status; - int transfer_size; +static int ftdi_prepare_write_buffer(struct usb_serial_port *port, + void *dest, size_t size) +{ + struct ftdi_private *priv; + int count; unsigned long flags; - dbg("%s port %d, %d bytes", __func__, port->number, count); - - if (count == 0) { - dbg("write request of 0 bytes"); - return 0; - } - spin_lock_irqsave(&priv->tx_lock, flags); - if (priv->tx_outstanding_urbs > URB_UPPER_LIMIT) { - spin_unlock_irqrestore(&priv->tx_lock, flags); - dbg("%s - write limit hit", __func__); - return 0; - } - priv->tx_outstanding_urbs++; - spin_unlock_irqrestore(&priv->tx_lock, flags); - - data_offset = priv->write_offset; - dbg("data_offset set to %d", data_offset); - - /* Determine total transfer size */ - transfer_size = count; - if (data_offset > 0) { - /* Original sio needs control bytes too... */ - transfer_size += (data_offset * - ((count + (priv->max_packet_size - 1 - data_offset)) / - (priv->max_packet_size - data_offset))); - } - - buffer = kmalloc(transfer_size, GFP_ATOMIC); - if (!buffer) { - dev_err(&port->dev, - "%s ran out of kernel memory for urb ...\n", __func__); - count = -ENOMEM; - goto error_no_buffer; - } - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - dev_err(&port->dev, "%s - no more free urbs\n", __func__); - count = -ENOMEM; - goto error_no_urb; - } + priv = usb_get_serial_port_data(port); - /* Copy data */ - if (data_offset > 0) { - /* Original sio requires control byte at start of - each packet. */ - int user_pktsz = priv->max_packet_size - data_offset; - int todo = count; - unsigned char *first_byte = buffer; - const unsigned char *current_position = buf; - - while (todo > 0) { - if (user_pktsz > todo) - user_pktsz = todo; - /* Write the control byte at the front of the packet*/ - *first_byte = 1 | ((user_pktsz) << 2); - /* Copy data for packet */ - memcpy(first_byte + data_offset, - current_position, user_pktsz); - first_byte += user_pktsz + data_offset; - current_position += user_pktsz; - todo -= user_pktsz; + if (priv->chip_type == SIO) { + unsigned char *buffer = dest; + int i, len, c; + + count = 0; + spin_lock_irqsave(&port->lock, flags); + for (i = 0; i < size - 1; i += priv->max_packet_size) { + len = min_t(int, size - i, priv->max_packet_size) - 1; + c = kfifo_out(&port->write_fifo, &buffer[i + 1], len); + if (!c) + break; + buffer[i] = (c << 2) + 1; + count += c + 1; } + spin_unlock_irqrestore(&port->lock, flags); } else { - /* No control byte required. */ - /* Copy in the data to send */ - memcpy(buffer, buf, count); - } - - usb_serial_debug_data(debug, &port->dev, __func__, - transfer_size, buffer); - - /* fill the buffer and send it */ - usb_fill_bulk_urb(urb, port->serial->dev, - usb_sndbulkpipe(port->serial->dev, - port->bulk_out_endpointAddress), - buffer, transfer_size, - ftdi_write_bulk_callback, port); - - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", - __func__, status); - count = status; - goto error; - } else { - spin_lock_irqsave(&priv->tx_lock, flags); - priv->tx_outstanding_bytes += count; - spin_unlock_irqrestore(&priv->tx_lock, flags); + count = kfifo_out_locked(&port->write_fifo, dest, size, + &port->lock); } - /* we are done with this urb, so let the host driver - * really free it when it is finished with it */ - usb_free_urb(urb); - - dbg("%s write returning: %d", __func__, count); - return count; -error: - usb_free_urb(urb); -error_no_urb: - kfree(buffer); -error_no_buffer: - spin_lock_irqsave(&priv->tx_lock, flags); - priv->tx_outstanding_urbs--; - spin_unlock_irqrestore(&priv->tx_lock, flags); return count; -} /* ftdi_write */ - - -/* This function may get called when the device is closed */ - -static void ftdi_write_bulk_callback(struct urb *urb) -{ - unsigned long flags; - struct usb_serial_port *port = urb->context; - struct ftdi_private *priv; - int data_offset; /* will be 1 for the SIO and 0 otherwise */ - unsigned long countback; - int status = urb->status; - - /* free up the transfer buffer, as usb_free_urb() does not do this */ - kfree(urb->transfer_buffer); - - dbg("%s - port %d", __func__, port->number); - - priv = usb_get_serial_port_data(port); - if (!priv) { - dbg("%s - bad port private data pointer - exiting", __func__); - return; - } - /* account for transferred data */ - countback = urb->transfer_buffer_length; - data_offset = priv->write_offset; - if (data_offset > 0) { - /* Subtract the control bytes */ - countback -= (data_offset * DIV_ROUND_UP(countback, priv->max_packet_size)); - } - spin_lock_irqsave(&priv->tx_lock, flags); - --priv->tx_outstanding_urbs; - priv->tx_outstanding_bytes -= countback; - spin_unlock_irqrestore(&priv->tx_lock, flags); - - if (status) { - dbg("nonzero write bulk status received: %d", status); - } - - usb_serial_port_softint(port); -} /* ftdi_write_bulk_callback */ - - -static int ftdi_write_room(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct ftdi_private *priv = usb_get_serial_port_data(port); - int room; - unsigned long flags; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&priv->tx_lock, flags); - if (priv->tx_outstanding_urbs < URB_UPPER_LIMIT) { - /* - * We really can take anything the user throws at us - * but let's pick a nice big number to tell the tty - * layer that we have lots of free space - */ - room = 2048; - } else { - room = 0; - } - spin_unlock_irqrestore(&priv->tx_lock, flags); - return room; } -static int ftdi_chars_in_buffer(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct ftdi_private *priv = usb_get_serial_port_data(port); - int buffered; - unsigned long flags; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&priv->tx_lock, flags); - buffered = (int)priv->tx_outstanding_bytes; - spin_unlock_irqrestore(&priv->tx_lock, flags); - if (buffered < 0) { - dev_err(&port->dev, "%s outstanding tx bytes is negative!\n", - __func__); - buffered = 0; - } - return buffered; -} +#define FTDI_RS_ERR_MASK (FTDI_RS_BI | FTDI_RS_PE | FTDI_RS_FE | FTDI_RS_OE) static int ftdi_process_packet(struct tty_struct *tty, struct usb_serial_port *port, struct ftdi_private *priv, @@ -2045,28 +1795,21 @@ static int ftdi_process_packet(struct tty_struct *tty, priv->prev_status = status; } - /* - * Although the device uses a bitmask and hence can have multiple - * errors on a packet - the order here sets the priority the error is - * returned to the tty layer. - */ flag = TTY_NORMAL; - if (packet[1] & FTDI_RS_OE) { - flag = TTY_OVERRUN; - dbg("OVERRRUN error"); - } - if (packet[1] & FTDI_RS_BI) { - flag = TTY_BREAK; - dbg("BREAK received"); - usb_serial_handle_break(port); - } - if (packet[1] & FTDI_RS_PE) { - flag = TTY_PARITY; - dbg("PARITY error"); - } - if (packet[1] & FTDI_RS_FE) { - flag = TTY_FRAME; - dbg("FRAMING error"); + if (packet[1] & FTDI_RS_ERR_MASK) { + /* Break takes precedence over parity, which takes precedence + * over framing errors */ + if (packet[1] & FTDI_RS_BI) { + flag = TTY_BREAK; + usb_serial_handle_break(port); + } else if (packet[1] & FTDI_RS_PE) { + flag = TTY_PARITY; + } else if (packet[1] & FTDI_RS_FE) { + flag = TTY_FRAME; + } + /* Overrun is special, not associated with a char */ + if (packet[1] & FTDI_RS_OE) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); } len -= 2; @@ -2074,20 +1817,21 @@ static int ftdi_process_packet(struct tty_struct *tty, return 0; /* status only */ ch = packet + 2; - if (!(port->console && port->sysrq) && flag == TTY_NORMAL) - tty_insert_flip_string(tty, ch, len); - else { + if (port->port.console && port->sysrq) { for (i = 0; i < len; i++, ch++) { if (!usb_serial_handle_sysrq_char(tty, port, *ch)) tty_insert_flip_char(tty, *ch, flag); } + } else { + tty_insert_flip_string_fixed_flag(tty, ch, flag, len); } + return len; } -static void ftdi_process_read(struct usb_serial_port *port) +static void ftdi_process_read_urb(struct urb *urb) { - struct urb *urb = port->read_urb; + struct usb_serial_port *port = urb->context; struct tty_struct *tty; struct ftdi_private *priv = usb_get_serial_port_data(port); char *data = (char *)urb->transfer_buffer; @@ -2109,32 +1853,6 @@ static void ftdi_process_read(struct usb_serial_port *port) tty_kref_put(tty); } -static void ftdi_read_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - unsigned long flags; - - dbg("%s - port %d", __func__, port->number); - - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", - __func__, urb->status); - return; - } - - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, urb->transfer_buffer); - ftdi_process_read(port); - - spin_lock_irqsave(&port->lock, flags); - port->throttled = port->throttle_req; - if (!port->throttled) { - spin_unlock_irqrestore(&port->lock, flags); - ftdi_submit_read_urb(port, GFP_ATOMIC); - } else - spin_unlock_irqrestore(&port->lock, flags); -} - static void ftdi_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; @@ -2165,15 +1883,13 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state) } - /* old_termios contains the original termios settings and tty->termios contains * the new setting to be used * WARNING: set_termios calls this with old_termios in kernel space */ - static void ftdi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) -{ /* ftdi_termios */ +{ struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); struct ktermios *termios = tty->termios; @@ -2401,7 +2117,6 @@ static int ftdi_tiocmset(struct tty_struct *tty, struct file *file, return update_mctrl(port, set, clear); } - static int ftdi_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { @@ -2470,35 +2185,6 @@ static int ftdi_ioctl(struct tty_struct *tty, struct file *file, return -ENOIOCTLCMD; } -static void ftdi_throttle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - unsigned long flags; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&port->lock, flags); - port->throttle_req = 1; - spin_unlock_irqrestore(&port->lock, flags); -} - -void ftdi_unthrottle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - int was_throttled; - unsigned long flags; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&port->lock, flags); - was_throttled = port->throttled; - port->throttled = port->throttle_req = 0; - spin_unlock_irqrestore(&port->lock, flags); - - if (was_throttled) - ftdi_submit_read_urb(port, GFP_KERNEL); -} - static int __init ftdi_init(void) { int retval; @@ -2529,15 +2215,12 @@ failed_sio_register: return retval; } - static void __exit ftdi_exit(void) { - dbg("%s", __func__); usb_deregister(&ftdi_driver); usb_serial_deregister(&ftdi_sio_device); - } diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index ff9bf80..213fe3d 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -23,14 +23,16 @@ */ /* Commands */ -#define FTDI_SIO_RESET 0 /* Reset the port */ -#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ -#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */ -#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */ -#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of the port */ -#define FTDI_SIO_GET_MODEM_STATUS 5 /* Retrieve current value of modem status register */ -#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */ -#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */ +#define FTDI_SIO_RESET 0 /* Reset the port */ +#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ +#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */ +#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */ +#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of + the port */ +#define FTDI_SIO_GET_MODEM_STATUS 5 /* Retrieve current value of modem + status register */ +#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */ +#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */ #define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */ #define FTDI_SIO_GET_LATENCY_TIMER 10 /* Get the latency timer */ @@ -52,7 +54,7 @@ */ /* Port Identifier Table */ -#define PIT_DEFAULT 0 /* SIOA */ +#define PIT_DEFAULT 0 /* SIOA */ #define PIT_SIOA 1 /* SIOA */ /* The device this driver is tested with one has only one port */ #define PIT_SIOB 2 /* SIOB */ @@ -103,20 +105,21 @@ * wLength: 0 * Data: None * The BaudDivisor values are calculated as follows: - * - BaseClock is either 12000000 or 48000000 depending on the device. FIXME: I wish - * I knew how to detect old chips to select proper base clock! + * - BaseClock is either 12000000 or 48000000 depending on the device. + * FIXME: I wish I knew how to detect old chips to select proper base clock! * - BaudDivisor is a fixed point number encoded in a funny way. * (--WRONG WAY OF THINKING--) * BaudDivisor is a fixed point number encoded with following bit weighs: * (-2)(-1)(13..0). It is a radical with a denominator of 4, so values * end with 0.0 (00...), 0.25 (10...), 0.5 (01...), and 0.75 (11...). * (--THE REALITY--) - * The both-bits-set has quite different meaning from 0.75 - the chip designers - * have decided it to mean 0.125 instead of 0.75. + * The both-bits-set has quite different meaning from 0.75 - the chip + * designers have decided it to mean 0.125 instead of 0.75. * This info looked up in FTDI application note "FT8U232 DEVICES \ Data Rates * and Flow Control Consideration for USB to RS232". * - BaudDivisor = (BaseClock / 16) / BaudRate, where the (=) operation should - * automagically re-encode the resulting value to take fractions into consideration. + * automagically re-encode the resulting value to take fractions into + * consideration. * As all values are integers, some bit twiddling is in order: * BaudDivisor = (BaseClock / 16 / BaudRate) | * (((BaseClock / 2 / BaudRate) & 4) ? 0x4000 // 0.5 @@ -146,7 +149,7 @@ * not supported by the FT8U232AM). */ -typedef enum { +enum ftdi_chip_type { SIO = 1, FT8U232AM = 2, FT232BM = 3, @@ -154,37 +157,36 @@ typedef enum { FT232RL = 5, FT2232H = 6, FT4232H = 7 -} ftdi_chip_type_t; - -typedef enum { - ftdi_sio_b300 = 0, - ftdi_sio_b600 = 1, - ftdi_sio_b1200 = 2, - ftdi_sio_b2400 = 3, - ftdi_sio_b4800 = 4, - ftdi_sio_b9600 = 5, - ftdi_sio_b19200 = 6, - ftdi_sio_b38400 = 7, - ftdi_sio_b57600 = 8, - ftdi_sio_b115200 = 9 -} FTDI_SIO_baudrate_t; +}; + +enum ftdi_sio_baudrate { + ftdi_sio_b300 = 0, + ftdi_sio_b600 = 1, + ftdi_sio_b1200 = 2, + ftdi_sio_b2400 = 3, + ftdi_sio_b4800 = 4, + ftdi_sio_b9600 = 5, + ftdi_sio_b19200 = 6, + ftdi_sio_b38400 = 7, + ftdi_sio_b57600 = 8, + ftdi_sio_b115200 = 9 +}; /* - * The ftdi_8U232AM_xxMHz_byyy constants have been removed. The encoded divisor values - * are calculated internally. + * The ftdi_8U232AM_xxMHz_byyy constants have been removed. The encoded divisor + * values are calculated internally. */ - -#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA -#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) -#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) -#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) -#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) -#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) -#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) -#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) -#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) -#define FTDI_SIO_SET_BREAK (0x1 << 14) +#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA +#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) +#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) +#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) +#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) +#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) +#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) +#define FTDI_SIO_SET_BREAK (0x1 << 14) /* FTDI_SIO_SET_DATA */ /* @@ -287,8 +289,8 @@ typedef enum { * * A value of zero in the hIndex field disables handshaking * - * If Xon/Xoff handshaking is specified, the hValue field should contain the XOFF character - * and the lValue field contains the XON character. + * If Xon/Xoff handshaking is specified, the hValue field should contain the + * XOFF character and the lValue field contains the XON character. */ /* @@ -373,7 +375,10 @@ typedef enum { /* FTDI_SIO_SET_ERROR_CHAR */ -/* Set the parity error replacement character for the specified communications port */ +/* + * Set the parity error replacement character for the specified communications + * port + */ /* * BmRequestType: 0100 0000b @@ -496,9 +501,10 @@ typedef enum { * * IN Endpoint * - * The device reserves the first two bytes of data on this endpoint to contain the current - * values of the modem and line status registers. In the absence of data, the device - * generates a message consisting of these two status bytes every 40 ms + * The device reserves the first two bytes of data on this endpoint to contain + * the current values of the modem and line status registers. In the absence of + * data, the device generates a message consisting of these two status bytes + * every 40 ms * * Byte 0: Modem Status * @@ -530,21 +536,21 @@ typedef enum { #define FTDI_RS0_RI (1 << 6) #define FTDI_RS0_RLSD (1 << 7) -#define FTDI_RS_DR 1 -#define FTDI_RS_OE (1<<1) -#define FTDI_RS_PE (1<<2) -#define FTDI_RS_FE (1<<3) -#define FTDI_RS_BI (1<<4) -#define FTDI_RS_THRE (1<<5) -#define FTDI_RS_TEMT (1<<6) -#define FTDI_RS_FIFO (1<<7) +#define FTDI_RS_DR 1 +#define FTDI_RS_OE (1<<1) +#define FTDI_RS_PE (1<<2) +#define FTDI_RS_FE (1<<3) +#define FTDI_RS_BI (1<<4) +#define FTDI_RS_THRE (1<<5) +#define FTDI_RS_TEMT (1<<6) +#define FTDI_RS_FIFO (1<<7) /* * OUT Endpoint * - * This device reserves the first bytes of data on this endpoint contain the length - * and port identifier of the message. For the FTDI USB Serial converter the port - * identifier is always 1. + * This device reserves the first bytes of data on this endpoint contain the + * length and port identifier of the message. For the FTDI USB Serial converter + * the port identifier is always 1. * * Byte 0: Line Status * diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 75482cb..94d86c3 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -275,8 +275,8 @@ /* * Hameg HO820 and HO870 interface (using VID 0x0403) */ -#define HAMEG_HO820_PID 0xed74 -#define HAMEG_HO870_PID 0xed71 +#define HAMEG_HO820_PID 0xed74 +#define HAMEG_HO870_PID 0xed71 /* * MaxStream devices www.maxstream.net @@ -289,14 +289,14 @@ * and Mike Studer (K6EEP) <k6eep@hamsoftware.org>. * Ian Abbott <abbotti@mev.co.uk> added a few more from the driver INF file. */ -#define FTDI_MHAM_KW_PID 0xEEE8 /* USB-KW interface */ -#define FTDI_MHAM_YS_PID 0xEEE9 /* USB-YS interface */ -#define FTDI_MHAM_Y6_PID 0xEEEA /* USB-Y6 interface */ -#define FTDI_MHAM_Y8_PID 0xEEEB /* USB-Y8 interface */ -#define FTDI_MHAM_IC_PID 0xEEEC /* USB-IC interface */ -#define FTDI_MHAM_DB9_PID 0xEEED /* USB-DB9 interface */ -#define FTDI_MHAM_RS232_PID 0xEEEE /* USB-RS232 interface */ -#define FTDI_MHAM_Y9_PID 0xEEEF /* USB-Y9 interface */ +#define FTDI_MHAM_KW_PID 0xEEE8 /* USB-KW interface */ +#define FTDI_MHAM_YS_PID 0xEEE9 /* USB-YS interface */ +#define FTDI_MHAM_Y6_PID 0xEEEA /* USB-Y6 interface */ +#define FTDI_MHAM_Y8_PID 0xEEEB /* USB-Y8 interface */ +#define FTDI_MHAM_IC_PID 0xEEEC /* USB-IC interface */ +#define FTDI_MHAM_DB9_PID 0xEEED /* USB-DB9 interface */ +#define FTDI_MHAM_RS232_PID 0xEEEE /* USB-RS232 interface */ +#define FTDI_MHAM_Y9_PID 0xEEEF /* USB-Y9 interface */ /* Domintell products http://www.domintell.com */ #define FTDI_DOMINTELL_DGQG_PID 0xEF50 /* Master */ @@ -483,9 +483,9 @@ * Blackfin gnICE JTAG * http://docs.blackfin.uclinux.org/doku.php?id=hw:jtag:gnice */ -#define ADI_VID 0x0456 -#define ADI_GNICE_PID 0xF000 -#define ADI_GNICEPLUS_PID 0xF001 +#define ADI_VID 0x0456 +#define ADI_GNICE_PID 0xF000 +#define ADI_GNICEPLUS_PID 0xF001 /* * RATOC REX-USB60F @@ -611,13 +611,13 @@ #define SEALEVEL_2802_7_PID 0X2872 /* SeaLINK+8/485 (2802) Port 7 */ #define SEALEVEL_2802_8_PID 0X2882 /* SeaLINK+8/485 (2802) Port 8 */ #define SEALEVEL_2803_1_PID 0X2813 /* SeaLINK+8 (2803) Port 1 */ -#define SEALEVEL_2803_2_PID 0X2823 /* SeaLINK+8 (2803) Port 2 */ -#define SEALEVEL_2803_3_PID 0X2833 /* SeaLINK+8 (2803) Port 3 */ -#define SEALEVEL_2803_4_PID 0X2843 /* SeaLINK+8 (2803) Port 4 */ -#define SEALEVEL_2803_5_PID 0X2853 /* SeaLINK+8 (2803) Port 5 */ -#define SEALEVEL_2803_6_PID 0X2863 /* SeaLINK+8 (2803) Port 6 */ -#define SEALEVEL_2803_7_PID 0X2873 /* SeaLINK+8 (2803) Port 7 */ -#define SEALEVEL_2803_8_PID 0X2883 /* SeaLINK+8 (2803) Port 8 */ +#define SEALEVEL_2803_2_PID 0X2823 /* SeaLINK+8 (2803) Port 2 */ +#define SEALEVEL_2803_3_PID 0X2833 /* SeaLINK+8 (2803) Port 3 */ +#define SEALEVEL_2803_4_PID 0X2843 /* SeaLINK+8 (2803) Port 4 */ +#define SEALEVEL_2803_5_PID 0X2853 /* SeaLINK+8 (2803) Port 5 */ +#define SEALEVEL_2803_6_PID 0X2863 /* SeaLINK+8 (2803) Port 6 */ +#define SEALEVEL_2803_7_PID 0X2873 /* SeaLINK+8 (2803) Port 7 */ +#define SEALEVEL_2803_8_PID 0X2883 /* SeaLINK+8 (2803) Port 8 */ /* * JETI SPECTROMETER SPECBOS 1201 @@ -1013,7 +1013,7 @@ */ #define EVOLUTION_VID 0xDEEE /* Vendor ID */ #define EVOLUTION_ER1_PID 0x0300 /* ER1 Control Module */ -#define EVO_8U232AM_PID 0x02FF /* Evolution robotics RCM2 (FT232AM)*/ +#define EVO_8U232AM_PID 0x02FF /* Evolution robotics RCM2 (FT232AM)*/ #define EVO_HYBRID_PID 0x0302 /* Evolution robotics RCM4 PID (FT232BM)*/ #define EVO_RCM4_PID 0x0303 /* Evolution robotics RCM4 PID */ diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index f804acb..a817ced 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -1,6 +1,7 @@ /* * USB Serial Converter Generic functions * + * Copyright (C) 2010 Johan Hovold (jhovold@gmail.com) * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or @@ -12,6 +13,7 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> +#include <linux/sysrq.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/module.h> @@ -117,7 +119,6 @@ void usb_serial_generic_deregister(void) int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; int result = 0; unsigned long flags; @@ -130,23 +131,8 @@ int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port spin_unlock_irqrestore(&port->lock, flags); /* if we have a bulk endpoint, start reading from it */ - if (port->bulk_in_size) { - /* Start reading from the device */ - usb_fill_bulk_urb(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ((serial->type->read_bulk_callback) ? - serial->type->read_bulk_callback : - usb_serial_generic_read_bulk_callback), - port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); - } + if (port->bulk_in_size) + result = usb_serial_generic_submit_read_urb(port, GFP_KERNEL); return result; } @@ -155,13 +141,22 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_open); static void generic_cleanup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; + unsigned long flags; + int i; dbg("%s - port %d", __func__, port->number); if (serial->dev) { /* shutdown any bulk transfers that might be going on */ - if (port->bulk_out_size) + if (port->bulk_out_size) { usb_kill_urb(port->write_urb); + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) + usb_kill_urb(port->write_urbs[i]); + + spin_lock_irqsave(&port->lock, flags); + kfifo_reset_out(&port->write_fifo); + spin_unlock_irqrestore(&port->lock, flags); + } if (port->bulk_in_size) usb_kill_urb(port->read_urb); } @@ -172,146 +167,68 @@ void usb_serial_generic_close(struct usb_serial_port *port) dbg("%s - port %d", __func__, port->number); generic_cleanup(port); } +EXPORT_SYMBOL_GPL(usb_serial_generic_close); -static int usb_serial_multi_urb_write(struct tty_struct *tty, - struct usb_serial_port *port, const unsigned char *buf, int count) +int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port, + void *dest, size_t size) { - unsigned long flags; - struct urb *urb; - unsigned char *buffer; - int status; - int towrite; - int bwrite = 0; - - dbg("%s - port %d", __func__, port->number); - - if (count == 0) - dbg("%s - write request of 0 bytes", __func__); - - while (count > 0) { - towrite = (count > port->bulk_out_size) ? - port->bulk_out_size : count; - spin_lock_irqsave(&port->lock, flags); - if (port->urbs_in_flight > - port->serial->type->max_in_flight_urbs) { - spin_unlock_irqrestore(&port->lock, flags); - dbg("%s - write limit hit", __func__); - return bwrite; - } - port->tx_bytes_flight += towrite; - port->urbs_in_flight++; - spin_unlock_irqrestore(&port->lock, flags); - - buffer = kmalloc(towrite, GFP_ATOMIC); - if (!buffer) { - dev_err(&port->dev, - "%s ran out of kernel memory for urb ...\n", __func__); - goto error_no_buffer; - } - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - dev_err(&port->dev, "%s - no more free urbs\n", - __func__); - goto error_no_urb; - } - - /* Copy data */ - memcpy(buffer, buf + bwrite, towrite); - usb_serial_debug_data(debug, &port->dev, __func__, - towrite, buffer); - /* fill the buffer and send it */ - usb_fill_bulk_urb(urb, port->serial->dev, - usb_sndbulkpipe(port->serial->dev, - port->bulk_out_endpointAddress), - buffer, towrite, - usb_serial_generic_write_bulk_callback, port); - - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", - __func__, status); - goto error; - } - - /* This urb is the responsibility of the host driver now */ - usb_free_urb(urb); - dbg("%s write: %d", __func__, towrite); - count -= towrite; - bwrite += towrite; - } - return bwrite; - -error: - usb_free_urb(urb); -error_no_urb: - kfree(buffer); -error_no_buffer: - spin_lock_irqsave(&port->lock, flags); - port->urbs_in_flight--; - port->tx_bytes_flight -= towrite; - spin_unlock_irqrestore(&port->lock, flags); - return bwrite; + return kfifo_out_locked(&port->write_fifo, dest, size, &port->lock); } /** * usb_serial_generic_write_start - kick off an URB write * @port: Pointer to the &struct usb_serial_port data * - * Returns the number of bytes queued on success. This will be zero if there - * was nothing to send. Otherwise, it returns a negative errno value + * Returns zero on success, or a negative errno value */ static int usb_serial_generic_write_start(struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; - unsigned char *data; - int result; - int count; + struct urb *urb; + int count, result; unsigned long flags; - bool start_io; + int i; - /* Atomically determine whether we can and need to start a USB - * operation. */ + if (test_and_set_bit_lock(USB_SERIAL_WRITE_BUSY, &port->flags)) + return 0; +retry: spin_lock_irqsave(&port->lock, flags); - if (port->write_urb_busy) - start_io = false; - else { - start_io = (kfifo_len(&port->write_fifo) != 0); - port->write_urb_busy = start_io; + if (!port->write_urbs_free || !kfifo_len(&port->write_fifo)) { + clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); + spin_unlock_irqrestore(&port->lock, flags); + return 0; } + i = (int)find_first_bit(&port->write_urbs_free, + ARRAY_SIZE(port->write_urbs)); spin_unlock_irqrestore(&port->lock, flags); - if (!start_io) - return 0; - - data = port->write_urb->transfer_buffer; - count = kfifo_out_locked(&port->write_fifo, data, port->bulk_out_size, &port->lock); - usb_serial_debug_data(debug, &port->dev, __func__, count, data); - - /* set up our urb */ - usb_fill_bulk_urb(port->write_urb, serial->dev, - usb_sndbulkpipe(serial->dev, - port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, count, - ((serial->type->write_bulk_callback) ? - serial->type->write_bulk_callback : - usb_serial_generic_write_bulk_callback), - port); - - /* send the data out the bulk port */ - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + urb = port->write_urbs[i]; + count = port->serial->type->prepare_write_buffer(port, + urb->transfer_buffer, + port->bulk_out_size); + urb->transfer_buffer_length = count; + usb_serial_debug_data(debug, &port->dev, __func__, count, + urb->transfer_buffer); + result = usb_submit_urb(urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", + dev_err(&port->dev, "%s - error submitting urb: %d\n", __func__, result); - /* don't have to grab the lock here, as we will - retry if != 0 */ - port->write_urb_busy = 0; - } else - result = count; + clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); + return result; + } + clear_bit(i, &port->write_urbs_free); - return result; + spin_lock_irqsave(&port->lock, flags); + port->tx_bytes += count; + spin_unlock_irqrestore(&port->lock, flags); + + /* Try sending off another urb, unless in irq context (in which case + * there will be no free urb). */ + if (!in_irq()) + goto retry; + + clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); + + return 0; } /** @@ -328,7 +245,6 @@ static int usb_serial_generic_write_start(struct usb_serial_port *port) int usb_serial_generic_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { - struct usb_serial *serial = port->serial; int result; dbg("%s - port %d", __func__, port->number); @@ -337,31 +253,23 @@ int usb_serial_generic_write(struct tty_struct *tty, if (!port->bulk_out_size) return -ENODEV; - if (count == 0) { - dbg("%s - write request of 0 bytes", __func__); + if (!count) return 0; - } - - if (serial->type->max_in_flight_urbs) - return usb_serial_multi_urb_write(tty, port, - buf, count); count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock); result = usb_serial_generic_write_start(port); + if (result) + return result; - if (result >= 0) - result = count; - - return result; + return count; } EXPORT_SYMBOL_GPL(usb_serial_generic_write); int usb_serial_generic_write_room(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct usb_serial *serial = port->serial; unsigned long flags; - int room = 0; + int room; dbg("%s - port %d", __func__, port->number); @@ -369,14 +277,7 @@ int usb_serial_generic_write_room(struct tty_struct *tty) return 0; spin_lock_irqsave(&port->lock, flags); - if (serial->type->max_in_flight_urbs) { - if (port->urbs_in_flight < serial->type->max_in_flight_urbs) - room = port->bulk_out_size * - (serial->type->max_in_flight_urbs - - port->urbs_in_flight); - } else { - room = kfifo_avail(&port->write_fifo); - } + room = kfifo_avail(&port->write_fifo); spin_unlock_irqrestore(&port->lock, flags); dbg("%s - returns %d", __func__, room); @@ -386,7 +287,6 @@ int usb_serial_generic_write_room(struct tty_struct *tty) int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct usb_serial *serial = port->serial; unsigned long flags; int chars; @@ -396,61 +296,47 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) return 0; spin_lock_irqsave(&port->lock, flags); - if (serial->type->max_in_flight_urbs) - chars = port->tx_bytes_flight; - else - chars = kfifo_len(&port->write_fifo); + chars = kfifo_len(&port->write_fifo) + port->tx_bytes; spin_unlock_irqrestore(&port->lock, flags); dbg("%s - returns %d", __func__, chars); return chars; } - -void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port, - gfp_t mem_flags) +int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, + gfp_t mem_flags) { - struct urb *urb = port->read_urb; - struct usb_serial *serial = port->serial; int result; - /* Continue reading from device */ - usb_fill_bulk_urb(urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - urb->transfer_buffer, - urb->transfer_buffer_length, - ((serial->type->read_bulk_callback) ? - serial->type->read_bulk_callback : - usb_serial_generic_read_bulk_callback), port); - - result = usb_submit_urb(urb, mem_flags); + result = usb_submit_urb(port->read_urb, mem_flags); if (result && result != -EPERM) { - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", + dev_err(&port->dev, "%s - error submitting urb: %d\n", __func__, result); } + return result; } -EXPORT_SYMBOL_GPL(usb_serial_generic_resubmit_read_urb); +EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urb); -/* Push data to tty layer and resubmit the bulk read URB */ -static void flush_and_resubmit_read_urb(struct usb_serial_port *port) +void usb_serial_generic_process_read_urb(struct urb *urb) { - struct urb *urb = port->read_urb; - struct tty_struct *tty = tty_port_tty_get(&port->port); + struct usb_serial_port *port = urb->context; + struct tty_struct *tty; char *ch = (char *)urb->transfer_buffer; int i; + if (!urb->actual_length) + return; + + tty = tty_port_tty_get(&port->port); if (!tty) - goto done; + return; /* The per character mucking around with sysrq path it too slow for stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases where the USB serial is not a console anyway */ - if (!port->console || !port->sysrq) + if (!port->port.console || !port->sysrq) tty_insert_flip_string(tty, ch, urb->actual_length); else { - /* Push data to tty */ for (i = 0; i < urb->actual_length; i++, ch++) { if (!usb_serial_handle_sysrq_char(tty, port, *ch)) tty_insert_flip_char(tty, *ch, TTY_NORMAL); @@ -458,9 +344,8 @@ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) } tty_flip_buffer_push(tty); tty_kref_put(tty); -done: - usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC); } +EXPORT_SYMBOL_GPL(usb_serial_generic_process_read_urb); void usb_serial_generic_read_bulk_callback(struct urb *urb) { @@ -479,13 +364,14 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); + port->serial->type->process_read_urb(urb); /* Throttle the device if requested by tty */ spin_lock_irqsave(&port->lock, flags); port->throttled = port->throttle_req; if (!port->throttled) { spin_unlock_irqrestore(&port->lock, flags); - flush_and_resubmit_read_urb(port); + usb_serial_generic_submit_read_urb(port, GFP_ATOMIC); } else spin_unlock_irqrestore(&port->lock, flags); } @@ -496,30 +382,29 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) unsigned long flags; struct usb_serial_port *port = urb->context; int status = urb->status; + int i; dbg("%s - port %d", __func__, port->number); - if (port->serial->type->max_in_flight_urbs) { - kfree(urb->transfer_buffer); + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) + if (port->write_urbs[i] == urb) + break; + + spin_lock_irqsave(&port->lock, flags); + port->tx_bytes -= urb->transfer_buffer_length; + set_bit(i, &port->write_urbs_free); + spin_unlock_irqrestore(&port->lock, flags); + + if (status) { + dbg("%s - non-zero urb status: %d", __func__, status); spin_lock_irqsave(&port->lock, flags); - --port->urbs_in_flight; - port->tx_bytes_flight -= urb->transfer_buffer_length; - if (port->urbs_in_flight < 0) - port->urbs_in_flight = 0; + kfifo_reset_out(&port->write_fifo); spin_unlock_irqrestore(&port->lock, flags); } else { - port->write_urb_busy = 0; - - if (status) - kfifo_reset_out(&port->write_fifo); - else - usb_serial_generic_write_start(port); + usb_serial_generic_write_start(port); } - if (status) - dbg("%s - non-zero urb status: %d", __func__, status); - usb_serial_port_softint(port); } EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); @@ -537,31 +422,31 @@ void usb_serial_generic_throttle(struct tty_struct *tty) port->throttle_req = 1; spin_unlock_irqrestore(&port->lock, flags); } +EXPORT_SYMBOL_GPL(usb_serial_generic_throttle); void usb_serial_generic_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; int was_throttled; - unsigned long flags; dbg("%s - port %d", __func__, port->number); /* Clear the throttle flags */ - spin_lock_irqsave(&port->lock, flags); + spin_lock_irq(&port->lock); was_throttled = port->throttled; port->throttled = port->throttle_req = 0; - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock_irq(&port->lock); - if (was_throttled) { - /* Resume reading from device */ - flush_and_resubmit_read_urb(port); - } + if (was_throttled) + usb_serial_generic_submit_read_urb(port, GFP_KERNEL); } +EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle); +#ifdef CONFIG_MAGIC_SYSRQ int usb_serial_handle_sysrq_char(struct tty_struct *tty, struct usb_serial_port *port, unsigned int ch) { - if (port->sysrq && port->console) { + if (port->sysrq && port->port.console) { if (ch && time_before(jiffies, port->sysrq)) { handle_sysrq(ch, tty); port->sysrq = 0; @@ -571,6 +456,13 @@ int usb_serial_handle_sysrq_char(struct tty_struct *tty, } return 0; } +#else +int usb_serial_handle_sysrq_char(struct tty_struct *tty, + struct usb_serial_port *port, unsigned int ch) +{ + return 0; +} +#endif EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char); int usb_serial_handle_break(struct usb_serial_port *port) @@ -600,7 +492,7 @@ int usb_serial_generic_resume(struct usb_serial *serial) c++; } - if (port->write_urb) { + if (port->bulk_out_size) { r = usb_serial_generic_write_start(port); if (r < 0) c++; diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 3ef8df0..76e6fb3 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -3020,7 +3020,7 @@ static int edge_startup(struct usb_serial *serial) /* set up our port private structures */ for (i = 0; i < serial->num_ports; ++i) { - edge_port = kmalloc(sizeof(struct edgeport_port), GFP_KERNEL); + edge_port = kzalloc(sizeof(struct edgeport_port), GFP_KERNEL); if (edge_port == NULL) { dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__); @@ -3033,7 +3033,6 @@ static int edge_startup(struct usb_serial *serial) kfree(edge_serial); return -ENOMEM; } - memset(edge_port, 0, sizeof(struct edgeport_port)); spin_lock_init(&edge_port->ep_lock); edge_port->port = serial->port[i]; usb_set_serial_port_data(serial->port[i], edge_port); diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h index cb201c1..dced7ec 100644 --- a/drivers/usb/serial/io_edgeport.h +++ b/drivers/usb/serial/io_edgeport.h @@ -34,15 +34,15 @@ -/* The following table is used to map the USBx port number to +/* The following table is used to map the USBx port number to * the device serial number (or physical USB path), */ #define MAX_EDGEPORTS 64 struct comMapper { char SerialNumber[MAX_SERIALNUMBER_LEN+1]; /* Serial number/usb path */ - int numPorts; /* Number of ports */ - int Original[MAX_RS232_PORTS]; /* Port numbers set by IOCTL */ - int Port[MAX_RS232_PORTS]; /* Actual used port numbers */ + int numPorts; /* Number of ports */ + int Original[MAX_RS232_PORTS]; /* Port numbers set by IOCTL */ + int Port[MAX_RS232_PORTS]; /* Actual used port numbers */ }; @@ -51,7 +51,7 @@ struct comMapper { /* /proc/edgeport Interface * This interface uses read/write/lseek interface to talk to the edgeport driver * the following read functions are supported: */ -#define PROC_GET_MAPPING_TO_PATH 1 +#define PROC_GET_MAPPING_TO_PATH 1 #define PROC_GET_COM_ENTRY 2 #define PROC_GET_EDGE_MANUF_DESCRIPTOR 3 #define PROC_GET_BOOT_DESCRIPTOR 4 @@ -64,7 +64,7 @@ struct comMapper { /* the following write functions are supported: */ -#define PROC_SET_COM_MAPPING 1 +#define PROC_SET_COM_MAPPING 1 #define PROC_SET_COM_ENTRY 2 @@ -97,8 +97,8 @@ struct edgeport_product_info { __u8 BoardRev; /* PCB revision level (chg only if s/w visible) */ __u8 BootMajorVersion; /* Boot Firmware version: xx. */ - __u8 BootMinorVersion; /* yy. */ - __le16 BootBuildNumber; /* zzzz (LE format) */ + __u8 BootMinorVersion; /* yy. */ + __le16 BootBuildNumber; /* zzzz (LE format) */ __u8 FirmwareMajorVersion; /* Operational Firmware version:xx. */ __u8 FirmwareMinorVersion; /* yy. */ diff --git a/drivers/usb/serial/io_ionsp.h b/drivers/usb/serial/io_ionsp.h index 092e03d..5cc591b 100644 --- a/drivers/usb/serial/io_ionsp.h +++ b/drivers/usb/serial/io_ionsp.h @@ -89,10 +89,10 @@ All 16-bit fields are sent in little-endian (Intel) format. // struct int_status_pkt { - __u16 RxBytesAvail; // Additional bytes available to - // be read from Bulk IN pipe - __u16 TxCredits[ MAX_RS232_PORTS ]; // Additional space available in - // given port's TxBuffer + __u16 RxBytesAvail; // Additional bytes available to + // be read from Bulk IN pipe + __u16 TxCredits[MAX_RS232_PORTS]; // Additional space available in + // given port's TxBuffer }; @@ -115,24 +115,24 @@ struct int_status_pkt { #define IOSP_CMD_STAT_BIT 0x80 // If set, this is command/status header #define IS_CMD_STAT_HDR(Byte1) ((Byte1) & IOSP_CMD_STAT_BIT) -#define IS_DATA_HDR(Byte1) (! IS_CMD_STAT_HDR(Byte1)) +#define IS_DATA_HDR(Byte1) (!IS_CMD_STAT_HDR(Byte1)) #define IOSP_GET_HDR_PORT(Byte1) ((__u8) ((Byte1) & IOSP_PORT_MASK)) -#define IOSP_GET_HDR_DATA_LEN(Byte1, Byte2) ((__u16) ( ((__u16)((Byte1) & 0x78)) << 5) | (Byte2)) +#define IOSP_GET_HDR_DATA_LEN(Byte1, Byte2) ((__u16) (((__u16)((Byte1) & 0x78)) << 5) | (Byte2)) #define IOSP_GET_STATUS_CODE(Byte1) ((__u8) (((Byte1) & 0x78) >> 3)) // // These macros build the 1st and 2nd bytes for a data header // -#define IOSP_BUILD_DATA_HDR1(Port, Len) ((__u8) (((Port) | ((__u8) (((__u16) (Len)) >> 5) & 0x78 )))) +#define IOSP_BUILD_DATA_HDR1(Port, Len) ((__u8) (((Port) | ((__u8) (((__u16) (Len)) >> 5) & 0x78)))) #define IOSP_BUILD_DATA_HDR2(Port, Len) ((__u8) (Len)) // // These macros build the 1st and 2nd bytes for a command header // -#define IOSP_BUILD_CMD_HDR1(Port, Cmd) ((__u8) ( IOSP_CMD_STAT_BIT | (Port) | ((__u8) ((Cmd) << 3)) )) +#define IOSP_BUILD_CMD_HDR1(Port, Cmd) ((__u8) (IOSP_CMD_STAT_BIT | (Port) | ((__u8) ((Cmd) << 3)))) //-------------------------------------------------------------- @@ -194,24 +194,25 @@ struct int_status_pkt { // Define macros to simplify building of IOSP cmds // -#define MAKE_CMD_WRITE_REG(ppBuf, pLen, Port, Reg, Val) \ - do { \ - (*(ppBuf))[0] = IOSP_BUILD_CMD_HDR1( (Port), IOSP_WRITE_UART_REG(Reg) ); \ - (*(ppBuf))[1] = (Val); \ - \ - *ppBuf += 2; \ - *pLen += 2; \ - } while (0) +#define MAKE_CMD_WRITE_REG(ppBuf, pLen, Port, Reg, Val) \ +do { \ + (*(ppBuf))[0] = IOSP_BUILD_CMD_HDR1((Port), \ + IOSP_WRITE_UART_REG(Reg)); \ + (*(ppBuf))[1] = (Val); \ + \ + *ppBuf += 2; \ + *pLen += 2; \ +} while (0) -#define MAKE_CMD_EXT_CMD(ppBuf, pLen, Port, ExtCmd, Param) \ - do { \ - (*(ppBuf))[0] = IOSP_BUILD_CMD_HDR1( (Port), IOSP_EXT_CMD ); \ - (*(ppBuf))[1] = (ExtCmd); \ - (*(ppBuf))[2] = (Param); \ - \ - *ppBuf += 3; \ - *pLen += 3; \ - } while (0) +#define MAKE_CMD_EXT_CMD(ppBuf, pLen, Port, ExtCmd, Param) \ +do { \ + (*(ppBuf))[0] = IOSP_BUILD_CMD_HDR1((Port), IOSP_EXT_CMD); \ + (*(ppBuf))[1] = (ExtCmd); \ + (*(ppBuf))[2] = (Param); \ + \ + *ppBuf += 3; \ + *pLen += 3; \ +} while (0) @@ -310,16 +311,16 @@ struct int_status_pkt { // // IOSP_CMD_RX_CHECK_REQ // -// This command is used to assist in the implementation of the -// IOCTL_SERIAL_PURGE Windows IOCTL. -// This IOSP command tries to place a marker at the end of the RX -// queue in the Edgeport. If the Edgeport RX queue is full then -// the Check will be discarded. -// It is up to the device driver to timeout waiting for the -// RX_CHECK_RSP. If a RX_CHECK_RSP is received, the driver is -// sure that all data has been received from the edgeport and +// This command is used to assist in the implementation of the +// IOCTL_SERIAL_PURGE Windows IOCTL. +// This IOSP command tries to place a marker at the end of the RX +// queue in the Edgeport. If the Edgeport RX queue is full then +// the Check will be discarded. +// It is up to the device driver to timeout waiting for the +// RX_CHECK_RSP. If a RX_CHECK_RSP is received, the driver is +// sure that all data has been received from the edgeport and // may now purge any internal RX buffers. -// Note tat the sequence numbers may be used to detect lost +// Note tat the sequence numbers may be used to detect lost // CHECK_REQs. // Example for Port 0 @@ -341,7 +342,7 @@ struct int_status_pkt { // // 1ssssPPP P1P1P1P1 [ P2P2P2P2P2 ]... // -// ssss: 00-07 2-byte status. ssss identifies which UART register +// ssss: 00-07 2-byte status. ssss identifies which UART register // has changed value, and the new value is in P1. // Note that the ssss values do not correspond to the // 16554 register numbers given in 16554.H. Instead, @@ -383,14 +384,14 @@ struct int_status_pkt { // returns this in order to report // changes in modem status lines // (CTS, DSR, RI, CD) -// +// // 0x02 // Available for future expansion -// 0x03 // -// 0x04 // -// 0x05 // -// 0x06 // -// 0x07 // +// 0x03 // +// 0x04 // +// 0x05 // +// 0x06 // +// 0x07 // /**************************************************** @@ -400,7 +401,7 @@ struct int_status_pkt { #define IOSP_STATUS_LSR_DATA 0x08 // P1 is new value of LSR register (same as STATUS_LSR) // P2 is errored character read from -// RxFIFO after LSR reported an error. +// RxFIFO after LSR reported an error. #define IOSP_EXT_STATUS 0x09 // P1 is status/response code, param in P2. @@ -408,7 +409,7 @@ struct int_status_pkt { // Response Codes (P1 values) for 3-byte status messages #define IOSP_EXT_STATUS_CHASE_RSP 0 // Reply to CHASE_PORT cmd. P2 is outcome: -#define IOSP_EXT_STATUS_CHASE_PASS 0 // P2 = 0: All Tx data drained successfully +#define IOSP_EXT_STATUS_CHASE_PASS 0 // P2 = 0: All Tx data drained successfully #define IOSP_EXT_STATUS_CHASE_FAIL 1 // P2 = 1: Timed out (stuck due to flow // control from remote device). @@ -446,9 +447,9 @@ struct int_status_pkt { // Macros to parse status messages // -#define IOSP_GET_STATUS_LEN(code) ( (code) < 8 ? 2 : ((code) < 0x0A ? 3 : 4) ) +#define IOSP_GET_STATUS_LEN(code) ((code) < 8 ? 2 : ((code) < 0x0A ? 3 : 4)) -#define IOSP_STATUS_IS_2BYTE(code) ( (code) < 0x08 ) -#define IOSP_STATUS_IS_3BYTE(code) ( ((code) >= 0x08) && ((code) <= 0x0B) ) -#define IOSP_STATUS_IS_4BYTE(code) ( ((code) >= 0x0C) && ((code) <= 0x0D) ) +#define IOSP_STATUS_IS_2BYTE(code) ((code) < 0x08) +#define IOSP_STATUS_IS_3BYTE(code) (((code) >= 0x08) && ((code) <= 0x0B)) +#define IOSP_STATUS_IS_4BYTE(code) (((code) >= 0x0C) && ((code) <= 0x0D)) diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index aa876f7..0fca265 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -36,6 +36,7 @@ #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/serial.h> +#include <linux/kfifo.h> #include <linux/ioctl.h> #include <linux/firmware.h> #include <linux/uaccess.h> @@ -56,10 +57,6 @@ #define EPROM_PAGE_SIZE 64 -struct edgeport_uart_buf_desc { - __u32 count; /* Number of bytes currently in buffer */ -}; - /* different hardware types */ #define HARDWARE_TYPE_930 0 #define HARDWARE_TYPE_TIUMP 1 @@ -87,14 +84,6 @@ struct product_info { __u8 hardware_type; /* Type of hardware */ } __attribute__((packed)); -/* circular buffer */ -struct edge_buf { - unsigned int buf_size; - char *buf_buf; - char *buf_get; - char *buf_put; -}; - struct edgeport_port { __u16 uart_base; __u16 dma_address; @@ -108,7 +97,6 @@ struct edgeport_port { int baud_rate; int close_pending; int lsr_event; - struct edgeport_uart_buf_desc tx; struct async_icount icount; wait_queue_head_t delta_msr_wait; /* for handling sleeping while waiting for msr change to @@ -119,7 +107,7 @@ struct edgeport_port { spinlock_t ep_lock; int ep_read_urb_state; int ep_write_urb_in_use; - struct edge_buf *ep_out_buf; + struct kfifo write_fifo; }; struct edgeport_serial { @@ -249,17 +237,6 @@ static void edge_send(struct tty_struct *tty); static int edge_create_sysfs_attrs(struct usb_serial_port *port); static int edge_remove_sysfs_attrs(struct usb_serial_port *port); -/* circular buffer */ -static struct edge_buf *edge_buf_alloc(unsigned int size); -static void edge_buf_free(struct edge_buf *eb); -static void edge_buf_clear(struct edge_buf *eb); -static unsigned int edge_buf_data_avail(struct edge_buf *eb); -static unsigned int edge_buf_space_avail(struct edge_buf *eb); -static unsigned int edge_buf_put(struct edge_buf *eb, const char *buf, - unsigned int count); -static unsigned int edge_buf_get(struct edge_buf *eb, char *buf, - unsigned int count); - static int ti_vread_sync(struct usb_device *dev, __u8 request, __u16 value, __u16 index, u8 *data, int size) @@ -590,7 +567,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, add_wait_queue(&tty->write_wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); - if (edge_buf_data_avail(port->ep_out_buf) == 0 + if (kfifo_len(&port->write_fifo) == 0 || timeout == 0 || signal_pending(current) || !usb_get_intfdata(port->port->serial->interface)) /* disconnect */ @@ -602,7 +579,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, set_current_state(TASK_RUNNING); remove_wait_queue(&tty->write_wait, &wait); if (flush) - edge_buf_clear(port->ep_out_buf); + kfifo_reset_out(&port->write_fifo); spin_unlock_irqrestore(&port->ep_lock, flags); tty_kref_put(tty); @@ -2089,7 +2066,6 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *data, int count) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); @@ -2103,10 +2079,8 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, if (edge_port->close_pending == 1) return -ENODEV; - spin_lock_irqsave(&edge_port->ep_lock, flags); - count = edge_buf_put(edge_port->ep_out_buf, data, count); - spin_unlock_irqrestore(&edge_port->ep_lock, flags); - + count = kfifo_in_locked(&edge_port->write_fifo, data, count, + &edge_port->ep_lock); edge_send(tty); return count; @@ -2129,7 +2103,7 @@ static void edge_send(struct tty_struct *tty) return; } - count = edge_buf_get(edge_port->ep_out_buf, + count = kfifo_out(&edge_port->write_fifo, port->write_urb->transfer_buffer, port->bulk_out_size); @@ -2185,7 +2159,7 @@ static int edge_write_room(struct tty_struct *tty) return 0; spin_lock_irqsave(&edge_port->ep_lock, flags); - room = edge_buf_space_avail(edge_port->ep_out_buf); + room = kfifo_avail(&edge_port->write_fifo); spin_unlock_irqrestore(&edge_port->ep_lock, flags); dbg("%s - returns %d", __func__, room); @@ -2207,7 +2181,7 @@ static int edge_chars_in_buffer(struct tty_struct *tty) return 0; spin_lock_irqsave(&edge_port->ep_lock, flags); - chars = edge_buf_data_avail(edge_port->ep_out_buf); + chars = kfifo_len(&edge_port->write_fifo); spin_unlock_irqrestore(&edge_port->ep_lock, flags); dbg("%s - returns %d", __func__, chars); @@ -2664,8 +2638,8 @@ static int edge_startup(struct usb_serial *serial) goto cleanup; } spin_lock_init(&edge_port->ep_lock); - edge_port->ep_out_buf = edge_buf_alloc(EDGE_OUT_BUF_SIZE); - if (edge_port->ep_out_buf == NULL) { + if (kfifo_alloc(&edge_port->write_fifo, EDGE_OUT_BUF_SIZE, + GFP_KERNEL)) { dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__); kfree(edge_port); @@ -2682,7 +2656,7 @@ static int edge_startup(struct usb_serial *serial) cleanup: for (--i; i >= 0; --i) { edge_port = usb_get_serial_port_data(serial->port[i]); - edge_buf_free(edge_port->ep_out_buf); + kfifo_free(&edge_port->write_fifo); kfree(edge_port); usb_set_serial_port_data(serial->port[i], NULL); } @@ -2713,7 +2687,7 @@ static void edge_release(struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { edge_port = usb_get_serial_port_data(serial->port[i]); - edge_buf_free(edge_port->ep_out_buf); + kfifo_free(&edge_port->write_fifo); kfree(edge_port); } kfree(usb_get_serial_data(serial)); @@ -2763,182 +2737,6 @@ static int edge_remove_sysfs_attrs(struct usb_serial_port *port) } -/* Circular Buffer */ - -/* - * edge_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ - -static struct edge_buf *edge_buf_alloc(unsigned int size) -{ - struct edge_buf *eb; - - - if (size == 0) - return NULL; - - eb = kmalloc(sizeof(struct edge_buf), GFP_KERNEL); - if (eb == NULL) - return NULL; - - eb->buf_buf = kmalloc(size, GFP_KERNEL); - if (eb->buf_buf == NULL) { - kfree(eb); - return NULL; - } - - eb->buf_size = size; - eb->buf_get = eb->buf_put = eb->buf_buf; - - return eb; -} - - -/* - * edge_buf_free - * - * Free the buffer and all associated memory. - */ - -static void edge_buf_free(struct edge_buf *eb) -{ - if (eb) { - kfree(eb->buf_buf); - kfree(eb); - } -} - - -/* - * edge_buf_clear - * - * Clear out all data in the circular buffer. - */ - -static void edge_buf_clear(struct edge_buf *eb) -{ - if (eb != NULL) - eb->buf_get = eb->buf_put; - /* equivalent to a get of all data available */ -} - - -/* - * edge_buf_data_avail - * - * Return the number of bytes of data available in the circular - * buffer. - */ - -static unsigned int edge_buf_data_avail(struct edge_buf *eb) -{ - if (eb == NULL) - return 0; - return ((eb->buf_size + eb->buf_put - eb->buf_get) % eb->buf_size); -} - - -/* - * edge_buf_space_avail - * - * Return the number of bytes of space available in the circular - * buffer. - */ - -static unsigned int edge_buf_space_avail(struct edge_buf *eb) -{ - if (eb == NULL) - return 0; - return ((eb->buf_size + eb->buf_get - eb->buf_put - 1) % eb->buf_size); -} - - -/* - * edge_buf_put - * - * Copy data data from a user buffer and put it into the circular buffer. - * Restrict to the amount of space available. - * - * Return the number of bytes copied. - */ - -static unsigned int edge_buf_put(struct edge_buf *eb, const char *buf, - unsigned int count) -{ - unsigned int len; - - - if (eb == NULL) - return 0; - - len = edge_buf_space_avail(eb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = eb->buf_buf + eb->buf_size - eb->buf_put; - if (count > len) { - memcpy(eb->buf_put, buf, len); - memcpy(eb->buf_buf, buf+len, count - len); - eb->buf_put = eb->buf_buf + count - len; - } else { - memcpy(eb->buf_put, buf, count); - if (count < len) - eb->buf_put += count; - else /* count == len */ - eb->buf_put = eb->buf_buf; - } - - return count; -} - - -/* - * edge_buf_get - * - * Get data from the circular buffer and copy to the given buffer. - * Restrict to the amount of data available. - * - * Return the number of bytes copied. - */ - -static unsigned int edge_buf_get(struct edge_buf *eb, char *buf, - unsigned int count) -{ - unsigned int len; - - - if (eb == NULL) - return 0; - - len = edge_buf_data_avail(eb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = eb->buf_buf + eb->buf_size - eb->buf_get; - if (count > len) { - memcpy(buf, eb->buf_get, len); - memcpy(buf+len, eb->buf_buf, count - len); - eb->buf_get = eb->buf_buf + count - len; - } else { - memcpy(buf, eb->buf_get, count); - if (count < len) - eb->buf_get += count; - else /* count == len */ - eb->buf_get = eb->buf_buf; - } - - return count; -} - - static struct usb_serial_driver edgeport_1port_device = { .driver = { .owner = THIS_MODULE, diff --git a/drivers/usb/serial/io_ti.h b/drivers/usb/serial/io_ti.h index cab84f2..1bd67b2 100644 --- a/drivers/usb/serial/io_ti.h +++ b/drivers/usb/serial/io_ti.h @@ -1,4 +1,4 @@ -/***************************************************************************** +/***************************************************************************** * * Copyright (C) 1997-2002 Inside Out Networks, Inc. * @@ -22,10 +22,10 @@ #define DTK_ADDR_SPACE_I2C_TYPE_II 0x82 /* Addr is placed in I2C area */ #define DTK_ADDR_SPACE_I2C_TYPE_III 0x83 /* Addr is placed in I2C area */ -// UART Defines -#define UMPMEM_BASE_UART1 0xFFA0 /* UMP UART1 base address */ -#define UMPMEM_BASE_UART2 0xFFB0 /* UMP UART2 base address */ -#define UMPMEM_OFFS_UART_LSR 0x05 /* UMP UART LSR register offset */ +/* UART Defines */ +#define UMPMEM_BASE_UART1 0xFFA0 /* UMP UART1 base address */ +#define UMPMEM_BASE_UART2 0xFFB0 /* UMP UART2 base address */ +#define UMPMEM_OFFS_UART_LSR 0x05 /* UMP UART LSR register offset */ /* Bits per character */ #define UMP_UART_CHAR5BITS 0x00 @@ -54,7 +54,7 @@ #define UMP_UART_LSR_RX_MASK 0x10 #define UMP_UART_LSR_TX_MASK 0x20 -#define UMP_UART_LSR_DATA_MASK ( LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK ) +#define UMP_UART_LSR_DATA_MASK (LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK) /* Port Settings Constants) */ #define UMP_MASK_UART_FLAGS_RTS_FLOW 0x0001 @@ -79,50 +79,57 @@ #define UMP_PORT_DIR_OUT 0x01 #define UMP_PORT_DIR_IN 0x02 -// Address of Port 0 -#define UMPM_UART1_PORT 0x03 - -// Commands -#define UMPC_SET_CONFIG 0x05 -#define UMPC_OPEN_PORT 0x06 -#define UMPC_CLOSE_PORT 0x07 -#define UMPC_START_PORT 0x08 -#define UMPC_STOP_PORT 0x09 -#define UMPC_TEST_PORT 0x0A -#define UMPC_PURGE_PORT 0x0B - -#define UMPC_COMPLETE_READ 0x80 // Force the Firmware to complete the current Read -#define UMPC_HARDWARE_RESET 0x81 // Force UMP back into BOOT Mode -#define UMPC_COPY_DNLD_TO_I2C 0x82 // Copy current download image to type 0xf2 record in 16k I2C - // firmware will change 0xff record to type 2 record when complete +/* Address of Port 0 */ +#define UMPM_UART1_PORT 0x03 + +/* Commands */ +#define UMPC_SET_CONFIG 0x05 +#define UMPC_OPEN_PORT 0x06 +#define UMPC_CLOSE_PORT 0x07 +#define UMPC_START_PORT 0x08 +#define UMPC_STOP_PORT 0x09 +#define UMPC_TEST_PORT 0x0A +#define UMPC_PURGE_PORT 0x0B + +/* Force the Firmware to complete the current Read */ +#define UMPC_COMPLETE_READ 0x80 +/* Force UMP back into BOOT Mode */ +#define UMPC_HARDWARE_RESET 0x81 +/* + * Copy current download image to type 0xf2 record in 16k I2C + * firmware will change 0xff record to type 2 record when complete + */ +#define UMPC_COPY_DNLD_TO_I2C 0x82 - // Special function register commands - // wIndex is register address - // wValue is MSB/LSB mask/data -#define UMPC_WRITE_SFR 0x83 // Write SFR Register +/* + * Special function register commands + * wIndex is register address + * wValue is MSB/LSB mask/data + */ +#define UMPC_WRITE_SFR 0x83 /* Write SFR Register */ - // wIndex is register address -#define UMPC_READ_SFR 0x84 // Read SRF Register +/* wIndex is register address */ +#define UMPC_READ_SFR 0x84 /* Read SRF Register */ - // Set or Clear DTR (wValue bit 0 Set/Clear) wIndex ModuleID (port) +/* Set or Clear DTR (wValue bit 0 Set/Clear) wIndex ModuleID (port) */ #define UMPC_SET_CLR_DTR 0x85 - // Set or Clear RTS (wValue bit 0 Set/Clear) wIndex ModuleID (port) +/* Set or Clear RTS (wValue bit 0 Set/Clear) wIndex ModuleID (port) */ #define UMPC_SET_CLR_RTS 0x86 - // Set or Clear LOOPBACK (wValue bit 0 Set/Clear) wIndex ModuleID (port) +/* Set or Clear LOOPBACK (wValue bit 0 Set/Clear) wIndex ModuleID (port) */ #define UMPC_SET_CLR_LOOPBACK 0x87 - // Set or Clear BREAK (wValue bit 0 Set/Clear) wIndex ModuleID (port) +/* Set or Clear BREAK (wValue bit 0 Set/Clear) wIndex ModuleID (port) */ #define UMPC_SET_CLR_BREAK 0x88 - // Read MSR wIndex ModuleID (port) +/* Read MSR wIndex ModuleID (port) */ #define UMPC_READ_MSR 0x89 - /* Toolkit commands */ - /* Read-write group */ -#define UMPC_MEMORY_READ 0x92 -#define UMPC_MEMORY_WRITE 0x93 +/* Toolkit commands */ +/* Read-write group */ +#define UMPC_MEMORY_READ 0x92 +#define UMPC_MEMORY_WRITE 0x93 /* * UMP DMA Definitions @@ -130,8 +137,7 @@ #define UMPD_OEDB1_ADDRESS 0xFF08 #define UMPD_OEDB2_ADDRESS 0xFF10 -struct out_endpoint_desc_block -{ +struct out_endpoint_desc_block { __u8 Configuration; __u8 XBufAddr; __u8 XByteCount; @@ -147,8 +153,8 @@ struct out_endpoint_desc_block * TYPE DEFINITIONS * Structures for Firmware commands */ -struct ump_uart_config /* UART settings */ -{ +/* UART settings */ +struct ump_uart_config { __u16 wBaudRate; /* Baud rate */ __u16 wFlags; /* Bitmap mask of flags */ __u8 bDataBits; /* 5..8 - data bits per character */ @@ -165,8 +171,8 @@ struct ump_uart_config /* UART settings */ * TYPE DEFINITIONS * Structures for USB interrupts */ -struct ump_interrupt /* Interrupt packet structure */ -{ +/* Interrupt packet structure */ +struct ump_interrupt { __u8 bICode; /* Interrupt code (interrupt num) */ __u8 bIInfo; /* Interrupt information */ } __attribute__((packed)); diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index 8e1a491..51f83fb 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h @@ -26,7 +26,7 @@ // // Definitions of USB product IDs -// +// #define USB_VENDOR_ID_ION 0x1608 // Our VID #define USB_VENDOR_ID_TI 0x0451 // TI VID @@ -54,7 +54,7 @@ // Product IDs - assigned to match middle digit of serial number (No longer true) #define ION_DEVICE_ID_80251_NETCHIP 0x020 // This bit is set in the PID if this edgeport hardware$ - // is based on the 80251+Netchip. + // is based on the 80251+Netchip. #define ION_DEVICE_ID_GENERATION_1 0x00 // Value for 930 based edgeports #define ION_DEVICE_ID_GENERATION_2 0x01 // Value for 80251+Netchip. @@ -134,7 +134,7 @@ #define ION_DEVICE_ID_TI_EDGEPORT_416 0x0212 // Edgeport/416 #define ION_DEVICE_ID_TI_EDGEPORT_1 0x0215 // Edgeport/1 RS232 #define ION_DEVICE_ID_TI_EDGEPORT_42 0x0217 // Edgeport/42 4 hub 2 RS232 -#define ION_DEVICE_ID_TI_EDGEPORT_22I 0x021A // Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_22I 0x021A // Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232 #define ION_DEVICE_ID_TI_EDGEPORT_2C 0x021B // Edgeport/2c RS232 #define ION_DEVICE_ID_TI_EDGEPORT_221C 0x021C // Edgeport/221c is a TI based Edgeport/2 with lucent chip and // 2 external hub ports - Large I2C @@ -142,7 +142,7 @@ // 2 external hub ports - Large I2C #define ION_DEVICE_ID_TI_EDGEPORT_21C 0x021E // Edgeport/21c is a TI based Edgeport/2 with lucent chip -// Generation 3 devices -- 3410 based edgport/1 (256 byte I2C) +// Generation 3 devices -- 3410 based edgport/1 (256 byte I2C) #define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1 0x0240 // Edgeport/1 RS232 #define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I 0x0241 // Edgeport/1i- RS422 model @@ -176,7 +176,7 @@ // Default to /P function #define ION_DEVICE_ID_PLUS_PWR_HP4CD 0x30C // 5052 Plus Power HubPort/4CD+ (for Dell) -#define ION_DEVICE_ID_PLUS_PWR_HP4C 0x30D // 5052 Plus Power HubPort/4C+ +#define ION_DEVICE_ID_PLUS_PWR_HP4C 0x30D // 5052 Plus Power HubPort/4C+ #define ION_DEVICE_ID_PLUS_PWR_PCI 0x30E // 3410 Plus Power PCI Host Controller 4 port @@ -217,17 +217,17 @@ #define ION_DEVICE_ID_MT4X56USB 0x1403 // OEM device -#define GENERATION_ID_FROM_USB_PRODUCT_ID( ProductId ) \ - ( (__u16) ((ProductId >> 8) & (ION_GENERATION_MASK)) ) +#define GENERATION_ID_FROM_USB_PRODUCT_ID(ProductId) \ + ((__u16) ((ProductId >> 8) & (ION_GENERATION_MASK))) -#define MAKE_USB_PRODUCT_ID( OemId, DeviceId ) \ - ( (__u16) (((OemId) << 10) || (DeviceId)) ) +#define MAKE_USB_PRODUCT_ID(OemId, DeviceId) \ + ((__u16) (((OemId) << 10) || (DeviceId))) -#define DEVICE_ID_FROM_USB_PRODUCT_ID( ProductId ) \ - ( (__u16) ((ProductId) & (EDGEPORT_DEVICE_ID_MASK)) ) +#define DEVICE_ID_FROM_USB_PRODUCT_ID(ProductId) \ + ((__u16) ((ProductId) & (EDGEPORT_DEVICE_ID_MASK))) -#define OEM_ID_FROM_USB_PRODUCT_ID( ProductId ) \ - ( (__u16) (((ProductId) >> 10) & 0x3F) ) +#define OEM_ID_FROM_USB_PRODUCT_ID(ProductId) \ + ((__u16) (((ProductId) >> 10) & 0x3F)) // // Definitions of parameters for download code. Note that these are @@ -237,7 +237,7 @@ // TxCredits value below which driver won't bother sending (to prevent too many small writes). // Send only if above 25% -#define EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(InitialCredit, MaxPacketSize) (max( ((InitialCredit) / 4), (MaxPacketSize) )) +#define EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(InitialCredit, MaxPacketSize) (max(((InitialCredit) / 4), (MaxPacketSize))) #define EDGE_FW_BULK_MAX_PACKET_SIZE 64 // Max Packet Size for Bulk In Endpoint (EP1) #define EDGE_FW_BULK_READ_BUFFER_SIZE 1024 // Size to use for Bulk reads @@ -263,7 +263,7 @@ // wValue = 16-bit address // wIndex = unused (though we could put segment 00: or FF: here) // wLength = # bytes to read/write (max 64) -// +// #define USB_REQUEST_ION_RESET_DEVICE 0 // Warm reboot Edgeport, retaining USB address #define USB_REQUEST_ION_GET_EPIC_DESC 1 // Get Edgeport Compatibility Descriptor @@ -278,7 +278,7 @@ #define USB_REQUEST_ION_ENABLE_SUSPEND 9 // Enable/Disable suspend feature // (wValue != 0: Enable; wValue = 0: Disable) -#define USB_REQUEST_ION_SEND_IOSP 10 // Send an IOSP command to the edgeport over the control pipe +#define USB_REQUEST_ION_SEND_IOSP 10 // Send an IOSP command to the edgeport over the control pipe #define USB_REQUEST_ION_RECV_IOSP 11 // Receive an IOSP command from the edgeport over the control pipe @@ -301,8 +301,7 @@ // this is a "real" Edgeport. // -struct edge_compatibility_bits -{ +struct edge_compatibility_bits { // This __u32 defines which Vendor-specific commands/functionality // the device supports on the default EP0 pipe. @@ -334,24 +333,22 @@ struct edge_compatibility_bits __u32 TrueEdgeport : 1; // 0001 Set if device is a 'real' Edgeport // (Used only by driver, NEVER set by an EPiC device) __u32 GenUnused : 31; // Available for future expansion, must be 0 - }; #define EDGE_COMPATIBILITY_MASK0 0x0001 #define EDGE_COMPATIBILITY_MASK1 0x3FFF #define EDGE_COMPATIBILITY_MASK2 0x0001 -struct edge_compatibility_descriptor -{ +struct edge_compatibility_descriptor { __u8 Length; // Descriptor Length (per USB spec) __u8 DescType; // Descriptor Type (per USB spec, =DEVICE type) __u8 EpicVer; // Version of EPiC spec supported - // (Currently must be 1) + // (Currently must be 1) __u8 NumPorts; // Number of serial ports supported __u8 iDownloadFile; // Index of string containing download code filename - // 0=no download, FF=download compiled into driver. - __u8 Unused[ 3 ]; // Available for future expansion, must be 0 - // (Currently must be 0). + // 0=no download, FF=download compiled into driver. + __u8 Unused[3]; // Available for future expansion, must be 0 + // (Currently must be 0). __u8 MajorVersion; // Firmware version: xx. __u8 MinorVersion; // yy. __le16 BuildNumber; // zzzz (LE format) @@ -359,9 +356,7 @@ struct edge_compatibility_descriptor // The following structure contains __u32s, with each bit // specifying whether the EPiC device supports the given // command or functionality. - struct edge_compatibility_bits Supports; - }; // Values for iDownloadFile @@ -391,8 +386,8 @@ struct edge_compatibility_descriptor // Define the max block size that may be read or written // in a read/write RAM/ROM command. -#define MAX_SIZE_REQ_ION_READ_MEM ( (__u16) 64 ) -#define MAX_SIZE_REQ_ION_WRITE_MEM ( (__u16) 64 ) +#define MAX_SIZE_REQ_ION_READ_MEM ((__u16)64) +#define MAX_SIZE_REQ_ION_WRITE_MEM ((__u16)64) // @@ -545,7 +540,7 @@ struct edge_boot_descriptor { __u8 MajorVersion; // C6 Firmware version: xx. __u8 MinorVersion; // C7 yy. __le16 BuildNumber; // C8 zzzz (LE format) - + __u16 EnumRootDescTable; // CA Root of ROM-based descriptor table __u8 NumDescTypes; // CC Number of supported descriptor types @@ -597,41 +592,36 @@ struct edge_boot_descriptor { #define I2C_DESC_TYPE_ION 0 // Not defined by TI -struct ti_i2c_desc -{ +struct ti_i2c_desc { __u8 Type; // Type of descriptor __u16 Size; // Size of data only not including header __u8 CheckSum; // Checksum (8 bit sum of data only) __u8 Data[0]; // Data starts here -}__attribute__((packed)); +} __attribute__((packed)); // for 5152 devices only (type 2 record) // for 3410 the version is stored in the WATCHPORT_FIRMWARE_VERSION descriptor -struct ti_i2c_firmware_rec -{ +struct ti_i2c_firmware_rec { __u8 Ver_Major; // Firmware Major version number __u8 Ver_Minor; // Firmware Minor version number __u8 Data[0]; // Download starts here -}__attribute__((packed)); +} __attribute__((packed)); -struct watchport_firmware_version -{ +struct watchport_firmware_version { // Added 2 bytes for version number __u8 Version_Major; // Download Version (for Watchport) __u8 Version_Minor; -}__attribute__((packed)); +} __attribute__((packed)); // Structure of header of download image in fw_down.h -struct ti_i2c_image_header -{ +struct ti_i2c_image_header { __le16 Length; __u8 CheckSum; -}__attribute__((packed)); +} __attribute__((packed)); -struct ti_basic_descriptor -{ +struct ti_basic_descriptor { __u8 Power; // Self powered // bit 7: 1 - power switching supported // 0 - power switching not supported @@ -663,9 +653,9 @@ struct ti_basic_descriptor #define TI_I2C_SIZE_MASK 0x1f // 5 bits #define TI_GET_I2C_SIZE(x) ((((x) & TI_I2C_SIZE_MASK)+1)*256) -#define TI_MAX_I2C_SIZE ( 16 * 1024 ) +#define TI_MAX_I2C_SIZE (16 * 1024) -#define TI_MANUF_VERSION_0 0 +#define TI_MANUF_VERSION_0 0 // IonConig2 flags #define TI_CONFIG2_RS232 0x01 @@ -676,8 +666,7 @@ struct ti_basic_descriptor #define TI_CONFIG2_WATCHPORT 0x10 -struct edge_ti_manuf_descriptor -{ +struct edge_ti_manuf_descriptor { __u8 IonConfig; // Config byte for ION manufacturing use __u8 IonConfig2; // Expansion __u8 Version; // Version @@ -688,7 +677,7 @@ struct edge_ti_manuf_descriptor __u8 HubConfig2; // Used to configure the Hub __u8 TotalPorts; // Total Number of Com Ports for the entire device (All UMPs) __u8 Reserved; // Reserved -}__attribute__((packed)); +} __attribute__((packed)); #endif // if !defined(_USBVEND_H) diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 3fea929..28913fa 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -56,7 +56,6 @@ #include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include "ipaq.h" #define KP_RETRIES 100 @@ -64,7 +63,7 @@ * Version Information */ -#define DRIVER_VERSION "v0.5" +#define DRIVER_VERSION "v1.0" #define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>" #define DRIVER_DESC "USB PocketPC PDA driver" @@ -76,20 +75,8 @@ static int initial_wait; /* Function prototypes for an ipaq */ static int ipaq_open(struct tty_struct *tty, struct usb_serial_port *port); -static void ipaq_close(struct usb_serial_port *port); static int ipaq_calc_num_ports(struct usb_serial *serial); static int ipaq_startup(struct usb_serial *serial); -static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count); -static int ipaq_write_bulk(struct usb_serial_port *port, - const unsigned char *buf, int count); -static void ipaq_write_gather(struct usb_serial_port *port); -static void ipaq_read_bulk_callback(struct urb *urb); -static void ipaq_write_bulk_callback(struct urb *urb); -static int ipaq_write_room(struct tty_struct *tty); -static int ipaq_chars_in_buffer(struct tty_struct *tty); -static void ipaq_destroy_lists(struct usb_serial_port *port); - static struct usb_device_id ipaq_id_table [] = { /* The first entry is a placeholder for the insmod-specified device */ @@ -558,7 +545,7 @@ static struct usb_driver ipaq_driver = { .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = ipaq_id_table, - .no_dynamic_id = 1, + .no_dynamic_id = 1, }; @@ -569,91 +556,24 @@ static struct usb_serial_driver ipaq_device = { .name = "ipaq", }, .description = "PocketPC PDA", - .usb_driver = &ipaq_driver, + .usb_driver = &ipaq_driver, .id_table = ipaq_id_table, + .bulk_in_size = 256, + .bulk_out_size = 256, .open = ipaq_open, - .close = ipaq_close, .attach = ipaq_startup, .calc_num_ports = ipaq_calc_num_ports, - .write = ipaq_write, - .write_room = ipaq_write_room, - .chars_in_buffer = ipaq_chars_in_buffer, - .read_bulk_callback = ipaq_read_bulk_callback, - .write_bulk_callback = ipaq_write_bulk_callback, }; -static spinlock_t write_list_lock; -static int bytes_in; -static int bytes_out; - static int ipaq_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; - struct ipaq_private *priv; - struct ipaq_packet *pkt; - int i, result = 0; + int result = 0; int retries = connect_retries; dbg("%s - port %d", __func__, port->number); - bytes_in = 0; - bytes_out = 0; - priv = kmalloc(sizeof(struct ipaq_private), GFP_KERNEL); - if (priv == NULL) { - dev_err(&port->dev, "%s - Out of memory\n", __func__); - return -ENOMEM; - } - usb_set_serial_port_data(port, priv); - priv->active = 0; - priv->queue_len = 0; - priv->free_len = 0; - INIT_LIST_HEAD(&priv->queue); - INIT_LIST_HEAD(&priv->freelist); - - for (i = 0; i < URBDATA_QUEUE_MAX / PACKET_SIZE; i++) { - pkt = kmalloc(sizeof(struct ipaq_packet), GFP_KERNEL); - if (pkt == NULL) - goto enomem; - - pkt->data = kmalloc(PACKET_SIZE, GFP_KERNEL); - if (pkt->data == NULL) { - kfree(pkt); - goto enomem; - } - pkt->len = 0; - pkt->written = 0; - INIT_LIST_HEAD(&pkt->list); - list_add(&pkt->list, &priv->freelist); - priv->free_len += PACKET_SIZE; - } - - /* - * Lose the small buffers usbserial provides. Make larger ones. - */ - - kfree(port->bulk_in_buffer); - kfree(port->bulk_out_buffer); - /* make sure the generic serial code knows */ - port->bulk_out_buffer = NULL; - - port->bulk_in_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL); - if (port->bulk_in_buffer == NULL) - goto enomem; - - port->bulk_out_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL); - if (port->bulk_out_buffer == NULL) { - /* the buffer is useless, free it */ - kfree(port->bulk_in_buffer); - port->bulk_in_buffer = NULL; - goto enomem; - } - port->read_urb->transfer_buffer = port->bulk_in_buffer; - port->write_urb->transfer_buffer = port->bulk_out_buffer; - port->read_urb->transfer_buffer_length = URBDATA_SIZE; - port->bulk_out_size = port->write_urb->transfer_buffer_length - = URBDATA_SIZE; - msleep(1000*initial_wait); /* @@ -663,7 +583,6 @@ static int ipaq_open(struct tty_struct *tty, * through. Since this has a reasonably high failure rate, we retry * several times. */ - while (retries--) { result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21, @@ -673,269 +592,15 @@ static int ipaq_open(struct tty_struct *tty, msleep(1000); } - if (!retries && result) { - dev_err(&port->dev, "%s - failed doing control urb, error %d\n", __func__, result); - goto error; - } - - /* Start reading from the device */ - usb_fill_bulk_urb(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ipaq_read_bulk_callback, port); - - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) { - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, result); - goto error; - } - - return 0; - -enomem: - result = -ENOMEM; - dev_err(&port->dev, "%s - Out of memory\n", __func__); -error: - ipaq_destroy_lists(port); - kfree(priv); - return result; -} - - -static void ipaq_close(struct usb_serial_port *port) -{ - struct ipaq_private *priv = usb_get_serial_port_data(port); - - dbg("%s - port %d", __func__, port->number); - - /* - * shut down bulk read and write - */ - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); - ipaq_destroy_lists(port); - kfree(priv); - usb_set_serial_port_data(port, NULL); - - /* Uncomment the following line if you want to see some statistics - * in your syslog */ - /* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */ -} - -static void ipaq_read_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - struct tty_struct *tty; - unsigned char *data = urb->transfer_buffer; - int result; - int status = urb->status; - - dbg("%s - port %d", __func__, port->number); - - if (status) { - dbg("%s - nonzero read bulk status received: %d", - __func__, status); - return; - } - - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - - tty = tty_port_tty_get(&port->port); - if (tty && urb->actual_length) { - tty_insert_flip_string(tty, data, urb->actual_length); - tty_flip_buffer_push(tty); - bytes_in += urb->actual_length; - } - tty_kref_put(tty); - - /* Continue trying to always read */ - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ipaq_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); - return; -} - -static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - const unsigned char *current_position = buf; - int bytes_sent = 0; - int transfer_size; - - dbg("%s - port %d", __func__, port->number); - - while (count > 0) { - transfer_size = min(count, PACKET_SIZE); - if (ipaq_write_bulk(port, current_position, transfer_size)) - break; - current_position += transfer_size; - bytes_sent += transfer_size; - count -= transfer_size; - bytes_out += transfer_size; + dev_err(&port->dev, "%s - failed doing control urb, error %d\n", + __func__, result); + return result; } - return bytes_sent; + return usb_serial_generic_open(tty, port); } -static int ipaq_write_bulk(struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - struct ipaq_private *priv = usb_get_serial_port_data(port); - struct ipaq_packet *pkt = NULL; - int result = 0; - unsigned long flags; - - if (priv->free_len <= 0) { - dbg("%s - we're stuffed", __func__); - return -EAGAIN; - } - - spin_lock_irqsave(&write_list_lock, flags); - if (!list_empty(&priv->freelist)) { - pkt = list_entry(priv->freelist.next, struct ipaq_packet, list); - list_del(&pkt->list); - priv->free_len -= PACKET_SIZE; - } - spin_unlock_irqrestore(&write_list_lock, flags); - if (pkt == NULL) { - dbg("%s - we're stuffed", __func__); - return -EAGAIN; - } - - memcpy(pkt->data, buf, count); - usb_serial_debug_data(debug, &port->dev, __func__, count, pkt->data); - - pkt->len = count; - pkt->written = 0; - spin_lock_irqsave(&write_list_lock, flags); - list_add_tail(&pkt->list, &priv->queue); - priv->queue_len += count; - if (priv->active == 0) { - priv->active = 1; - ipaq_write_gather(port); - spin_unlock_irqrestore(&write_list_lock, flags); - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", - __func__, result); - } else { - spin_unlock_irqrestore(&write_list_lock, flags); - } - return result; -} - -static void ipaq_write_gather(struct usb_serial_port *port) -{ - struct ipaq_private *priv = usb_get_serial_port_data(port); - struct usb_serial *serial = port->serial; - int count, room; - struct ipaq_packet *pkt, *tmp; - struct urb *urb = port->write_urb; - - room = URBDATA_SIZE; - list_for_each_entry_safe(pkt, tmp, &priv->queue, list) { - count = min(room, (int)(pkt->len - pkt->written)); - memcpy(urb->transfer_buffer + (URBDATA_SIZE - room), - pkt->data + pkt->written, count); - room -= count; - pkt->written += count; - priv->queue_len -= count; - if (pkt->written == pkt->len) { - list_move(&pkt->list, &priv->freelist); - priv->free_len += PACKET_SIZE; - } - if (room == 0) - break; - } - - count = URBDATA_SIZE - room; - usb_fill_bulk_urb(port->write_urb, serial->dev, - usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, count, - ipaq_write_bulk_callback, port); - return; -} - -static void ipaq_write_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - struct ipaq_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - int result; - int status = urb->status; - - dbg("%s - port %d", __func__, port->number); - - if (status) { - dbg("%s - nonzero write bulk status received: %d", - __func__, status); - return; - } - - spin_lock_irqsave(&write_list_lock, flags); - if (!list_empty(&priv->queue)) { - ipaq_write_gather(port); - spin_unlock_irqrestore(&write_list_lock, flags); - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", - __func__, result); - } else { - priv->active = 0; - spin_unlock_irqrestore(&write_list_lock, flags); - } - - usb_serial_port_softint(port); -} - -static int ipaq_write_room(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct ipaq_private *priv = usb_get_serial_port_data(port); - - dbg("%s - freelen %d", __func__, priv->free_len); - return priv->free_len; -} - -static int ipaq_chars_in_buffer(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct ipaq_private *priv = usb_get_serial_port_data(port); - - dbg("%s - queuelen %d", __func__, priv->queue_len); - return priv->queue_len; -} - -static void ipaq_destroy_lists(struct usb_serial_port *port) -{ - struct ipaq_private *priv = usb_get_serial_port_data(port); - struct ipaq_packet *pkt, *tmp; - - list_for_each_entry_safe(pkt, tmp, &priv->queue, list) { - kfree(pkt->data); - kfree(pkt); - } - list_for_each_entry_safe(pkt, tmp, &priv->freelist, list) { - kfree(pkt->data); - kfree(pkt); - } -} - - static int ipaq_calc_num_ports(struct usb_serial *serial) { /* @@ -994,7 +659,6 @@ static int ipaq_startup(struct usb_serial *serial) static int __init ipaq_init(void) { int retval; - spin_lock_init(&write_list_lock); retval = usb_serial_register(&ipaq_device); if (retval) goto failed_usb_serial_register; @@ -1015,7 +679,6 @@ failed_usb_serial_register: return retval; } - static void __exit ipaq_exit(void) { usb_deregister(&ipaq_driver); diff --git a/drivers/usb/serial/ipaq.h b/drivers/usb/serial/ipaq.h deleted file mode 100644 index 2b90359..0000000 --- a/drivers/usb/serial/ipaq.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * USB Compaq iPAQ driver - * - * Copyright (C) 2001 - 2002 - * Ganesh Varadarajan <ganesh@veritas.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 __LINUX_USB_SERIAL_IPAQ_H -#define __LINUX_USB_SERIAL_IPAQ_H - -/* - * Since we can't queue our bulk write urbs (don't know why - it just - * doesn't work), we can send down only one write urb at a time. The simplistic - * approach taken by the generic usbserial driver will work, but it's not good - * for performance. Therefore, we buffer upto URBDATA_QUEUE_MAX bytes of write - * requests coming from the line discipline. This is done by chaining them - * in lists of struct ipaq_packet, each packet holding a maximum of - * PACKET_SIZE bytes. - * - * ipaq_write() can be called from bottom half context; hence we can't - * allocate memory for packets there. So we initialize a pool of packets at - * the first open and maintain a freelist. - * - * The value of PACKET_SIZE was empirically determined by - * checking the maximum write sizes sent down by the ppp ldisc. - * URBDATA_QUEUE_MAX is set to 64K, which is the maximum TCP window size. - */ - -struct ipaq_packet { - char *data; - size_t len; - size_t written; - struct list_head list; -}; - -struct ipaq_private { - int active; - int queue_len; - int free_len; - struct list_head queue; - struct list_head freelist; -}; - -#define URBDATA_SIZE 4096 -#define URBDATA_QUEUE_MAX (64 * 1024) -#define PACKET_SIZE 256 - -#endif diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index e1d0784..ca77e88 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -34,7 +34,6 @@ * DCD, DTR, RTS, CTS which are currently faked. * It's good enough for PPP at this point. It's based off all kinds of * code found in usb/serial and usb/class - * */ #include <linux/kernel.h> @@ -52,7 +51,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.3" +#define DRIVER_VERSION "v0.4" #define DRIVER_AUTHOR "Roelf Diedericks" #define DRIVER_DESC "IPWireless tty driver" @@ -65,8 +64,6 @@ /* Message sizes */ #define EVENT_BUFFER_SIZE 0xFF #define CHAR2INT16(c1, c0) (((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff)) -#define NUM_BULK_URBS 24 -#define NUM_CONTROL_URBS 16 /* vendor/product pairs that are known work with this driver*/ #define IPW_VID 0x0bc3 @@ -151,47 +148,6 @@ static struct usb_driver usb_ipw_driver = { static int debug; -static void ipw_read_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - unsigned char *data = urb->transfer_buffer; - struct tty_struct *tty; - int result; - int status = urb->status; - - dbg("%s - port %d", __func__, port->number); - - if (status) { - dbg("%s - nonzero read bulk status received: %d", - __func__, status); - return; - } - - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - - tty = tty_port_tty_get(&port->port); - if (tty && urb->actual_length) { - tty_insert_flip_string(tty, data, urb->actual_length); - tty_flip_buffer_push(tty); - } - tty_kref_put(tty); - - /* Continue trying to always read */ - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ipw_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); - return; -} - static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_device *dev = port->serial->dev; @@ -229,15 +185,7 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port) /*--2: Start reading from the device */ dbg("%s: setting up bulk read callback", __func__); - usb_fill_bulk_urb(port->read_urb, dev, - usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress), - port->bulk_in_buffer, - port->bulk_in_size, - ipw_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result < 0) - dbg("%s - usb_submit_urb(read bulk) failed with status %d", - __func__, result); + usb_serial_generic_open(tty, port); /*--3: Tell the modem to open the floodgates on the rx bulk channel */ dbg("%s:asking modem for RxRead (RXBULK_ON)", __func__); @@ -267,35 +215,6 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port) dev_err(&port->dev, "initial flowcontrol failed (error = %d)\n", result); - - /*--5: raise the dtr */ - dbg("%s:raising dtr", __func__); - result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - IPW_SIO_SET_PIN, - USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - IPW_PIN_SETDTR, - 0, - NULL, - 0, - 200000); - if (result < 0) - dev_err(&port->dev, - "setting dtr failed (error = %d)\n", result); - - /*--6: raise the rts */ - dbg("%s:raising rts", __func__); - result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - IPW_SIO_SET_PIN, - USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - IPW_PIN_SETRTS, - 0, - NULL, - 0, - 200000); - if (result < 0) - dev_err(&port->dev, - "setting dtr failed (error = %d)\n", result); - kfree(buf_flow_init); return 0; } @@ -305,8 +224,8 @@ static void ipw_dtr_rts(struct usb_serial_port *port, int on) struct usb_device *dev = port->serial->dev; int result; - /*--1: drop the dtr */ - dbg("%s:dropping dtr", __func__); + dbg("%s: on = %d", __func__, on); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), IPW_SIO_SET_PIN, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, @@ -316,22 +235,20 @@ static void ipw_dtr_rts(struct usb_serial_port *port, int on) 0, 200000); if (result < 0) - dev_err(&port->dev, "dropping dtr failed (error = %d)\n", + dev_err(&port->dev, "setting dtr failed (error = %d)\n", result); - /*--2: drop the rts */ - dbg("%s:dropping rts", __func__); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), IPW_SIO_SET_PIN, USB_TYPE_VENDOR | - USB_RECIP_INTERFACE | USB_DIR_OUT, + USB_RECIP_INTERFACE | USB_DIR_OUT, on ? IPW_PIN_SETRTS : IPW_PIN_CLRRTS, 0, NULL, 0, 200000); if (result < 0) - dev_err(&port->dev, - "dropping rts failed (error = %d)\n", result); + dev_err(&port->dev, "setting rts failed (error = %d)\n", + result); } static void ipw_close(struct usb_serial_port *port) @@ -368,83 +285,7 @@ static void ipw_close(struct usb_serial_port *port) dev_err(&port->dev, "Disabling bulk RxRead failed (error = %d)\n", result); - /* shutdown any in-flight urbs that we know about */ - usb_kill_urb(port->read_urb); - usb_kill_urb(port->write_urb); -} - -static void ipw_write_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - int status = urb->status; - - dbg("%s", __func__); - - port->write_urb_busy = 0; - - if (status) - dbg("%s - nonzero write bulk status received: %d", - __func__, status); - - usb_serial_port_softint(port); -} - -static int ipw_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - struct usb_device *dev = port->serial->dev; - int ret; - - dbg("%s: TOP: count=%d, in_interrupt=%ld", __func__, - count, in_interrupt()); - - if (count == 0) { - dbg("%s - write request of 0 bytes", __func__); - return 0; - } - - spin_lock_bh(&port->lock); - if (port->write_urb_busy) { - spin_unlock_bh(&port->lock); - dbg("%s - already writing", __func__); - return 0; - } - port->write_urb_busy = 1; - spin_unlock_bh(&port->lock); - - count = min(count, port->bulk_out_size); - memcpy(port->bulk_out_buffer, buf, count); - - dbg("%s count now:%d", __func__, count); - - usb_fill_bulk_urb(port->write_urb, dev, - usb_sndbulkpipe(dev, port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, - count, - ipw_write_bulk_callback, - port); - - ret = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (ret != 0) { - port->write_urb_busy = 0; - dbg("%s - usb_submit_urb(write bulk) failed with error = %d", - __func__, ret); - return ret; - } - - dbg("%s returning %d", __func__, count); - return count; -} - -static int ipw_probe(struct usb_serial_port *port) -{ - return 0; -} - -static int ipw_disconnect(struct usb_serial_port *port) -{ - usb_set_serial_port_data(port, NULL); - return 0; + usb_serial_generic_close(port); } static struct usb_serial_driver ipw_device = { @@ -453,17 +294,12 @@ static struct usb_serial_driver ipw_device = { .name = "ipw", }, .description = "IPWireless converter", - .usb_driver = &usb_ipw_driver, + .usb_driver = &usb_ipw_driver, .id_table = usb_ipw_ids, .num_ports = 1, .open = ipw_open, .close = ipw_close, .dtr_rts = ipw_dtr_rts, - .port_probe = ipw_probe, - .port_remove = ipw_disconnect, - .write = ipw_write, - .write_bulk_callback = ipw_write_bulk_callback, - .read_bulk_callback = ipw_read_bulk_callback, }; diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 4a0f519..ccbce40 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -3,6 +3,7 @@ * * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2002 Gary Brubaker (xavyer@ix.netcom.com) + * Copyright (C) 2010 Johan Hovold (jhovold@gmail.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 @@ -72,8 +73,8 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.4" -#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>" +#define DRIVER_VERSION "v0.5" +#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Johan Hovold <jhovold@gmail.com>" #define DRIVER_DESC "USB IR Dongle driver" static int debug; @@ -87,11 +88,9 @@ static int xbof = -1; static int ir_startup (struct usb_serial *serial); static int ir_open(struct tty_struct *tty, struct usb_serial_port *port); -static void ir_close(struct usb_serial_port *port); -static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count); -static void ir_write_bulk_callback (struct urb *urb); -static void ir_read_bulk_callback (struct urb *urb); +static int ir_prepare_write_buffer(struct usb_serial_port *port, + void *dest, size_t size); +static void ir_process_read_urb(struct urb *urb); static void ir_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); @@ -130,10 +129,8 @@ static struct usb_serial_driver ir_device = { .set_termios = ir_set_termios, .attach = ir_startup, .open = ir_open, - .close = ir_close, - .write = ir_write, - .write_bulk_callback = ir_write_bulk_callback, - .read_bulk_callback = ir_read_bulk_callback, + .prepare_write_buffer = ir_prepare_write_buffer, + .process_read_urb = ir_process_read_urb, }; static inline void irda_usb_dump_class_desc(struct usb_irda_cs_descriptor *desc) @@ -198,7 +195,6 @@ error: return NULL; } - static u8 ir_xbof_change(u8 xbof) { u8 result; @@ -237,7 +233,6 @@ static u8 ir_xbof_change(u8 xbof) return(result); } - static int ir_startup(struct usb_serial *serial) { struct usb_irda_cs_descriptor *irda_desc; @@ -297,83 +292,22 @@ static int ir_startup(struct usb_serial *serial) static int ir_open(struct tty_struct *tty, struct usb_serial_port *port) { - char *buffer; - int result = 0; + int i; dbg("%s - port %d", __func__, port->number); - if (buffer_size) { - /* override the default buffer sizes */ - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) { - dev_err(&port->dev, "%s - out of memory.\n", __func__); - return -ENOMEM; - } - kfree(port->read_urb->transfer_buffer); - port->read_urb->transfer_buffer = buffer; - port->read_urb->transfer_buffer_length = buffer_size; - - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) { - dev_err(&port->dev, "%s - out of memory.\n", __func__); - return -ENOMEM; - } - kfree(port->write_urb->transfer_buffer); - port->write_urb->transfer_buffer = buffer; - port->write_urb->transfer_buffer_length = buffer_size; - port->bulk_out_size = buffer_size; - } + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) + port->write_urbs[i]->transfer_flags = URB_ZERO_PACKET; /* Start reading from the device */ - usb_fill_bulk_urb( - port->read_urb, - port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ir_read_bulk_callback, - port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, result); - - return result; + return usb_serial_generic_open(tty, port); } -static void ir_close(struct usb_serial_port *port) +static int ir_prepare_write_buffer(struct usb_serial_port *port, + void *dest, size_t size) { - dbg("%s - port %d", __func__, port->number); - - /* shutdown our bulk read */ - usb_kill_urb(port->read_urb); -} - -static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - unsigned char *transfer_buffer; - int result; - int transfer_size; - - dbg("%s - port = %d, count = %d", __func__, port->number, count); - - if (count == 0) - return 0; - - spin_lock_bh(&port->lock); - if (port->write_urb_busy) { - spin_unlock_bh(&port->lock); - dbg("%s - already writing", __func__); - return 0; - } - port->write_urb_busy = 1; - spin_unlock_bh(&port->lock); - - transfer_buffer = port->write_urb->transfer_buffer; - transfer_size = min(count, port->bulk_out_size - 1); + unsigned char *buf = dest; + int count; /* * The first byte of the packet we send to the device contains an @@ -382,119 +316,57 @@ static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, * * See section 5.4.2.2 of the USB IrDA spec. */ - *transfer_buffer = ir_xbof | ir_baud; - ++transfer_buffer; - - memcpy(transfer_buffer, buf, transfer_size); + *buf = ir_xbof | ir_baud; - usb_fill_bulk_urb( - port->write_urb, - port->serial->dev, - usb_sndbulkpipe(port->serial->dev, - port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, - transfer_size + 1, - ir_write_bulk_callback, - port); - - port->write_urb->transfer_flags = URB_ZERO_PACKET; - - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) { - port->write_urb_busy = 0; - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", - __func__, result); - } else - result = transfer_size; - - return result; + count = kfifo_out_locked(&port->write_fifo, buf + 1, size - 1, + &port->lock); + return count + 1; } -static void ir_write_bulk_callback(struct urb *urb) +static void ir_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; - int status = urb->status; - - dbg("%s - port %d", __func__, port->number); + unsigned char *data = urb->transfer_buffer; + struct tty_struct *tty; - port->write_urb_busy = 0; - if (status) { - dbg("%s - nonzero write bulk status received: %d", - __func__, status); + if (!urb->actual_length) return; - } + /* + * The first byte of the packet we get from the device + * contains a busy indicator and baud rate change. + * See section 5.4.1.2 of the USB IrDA spec. + */ + if (*data & 0x0f) + ir_baud = *data & 0x0f; - usb_serial_debug_data( - debug, - &port->dev, - __func__, - urb->actual_length, - urb->transfer_buffer); + if (urb->actual_length == 1) + return; - usb_serial_port_softint(port); + tty = tty_port_tty_get(&port->port); + if (!tty) + return; + tty_insert_flip_string(tty, data + 1, urb->actual_length - 1); + tty_flip_buffer_push(tty); + tty_kref_put(tty); } -static void ir_read_bulk_callback(struct urb *urb) +static void ir_set_termios_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty; - unsigned char *data = urb->transfer_buffer; - int result; int status = urb->status; dbg("%s - port %d", __func__, port->number); - switch (status) { - case 0: /* Successful */ - /* - * The first byte of the packet we get from the device - * contains a busy indicator and baud rate change. - * See section 5.4.1.2 of the USB IrDA spec. - */ - if ((*data & 0x0f) > 0) - ir_baud = *data & 0x0f; - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - tty = tty_port_tty_get(&port->port); - tty_insert_flip_string(tty, data+1, urb->actual_length - 1); - tty_flip_buffer_push(tty); - tty_kref_put(tty); - - /* - * No break here. - * We want to resubmit the urb so we can read - * again. - */ - - case -EPROTO: /* taking inspiration from pl2303.c */ - /* Continue trying to always read */ - usb_fill_bulk_urb( - port->read_urb, - port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ir_read_bulk_callback, - port); - - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", - __func__, result); - break ; - default: - dbg("%s - nonzero read bulk status received: %d", - __func__, status); - break ; - } - return; + kfree(urb->transfer_buffer); + + if (status) + dbg("%s - non-zero urb status: %d", __func__, status); } static void ir_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { + struct urb *urb; unsigned char *transfer_buffer; int result; speed_t baud; @@ -548,41 +420,63 @@ static void ir_set_termios(struct tty_struct *tty, else ir_xbof = ir_xbof_change(xbof) ; - /* FIXME need to check to see if our write urb is busy right - * now, or use a urb pool. - * + /* Only speed changes are supported */ + tty_termios_copy_hw(tty->termios, old_termios); + tty_encode_baud_rate(tty, baud, baud); + + /* * send the baud change out on an "empty" data packet */ - transfer_buffer = port->write_urb->transfer_buffer; + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dev_err(&port->dev, "%s - no more urbs\n", __func__); + return; + } + transfer_buffer = kmalloc(1, GFP_KERNEL); + if (!transfer_buffer) { + dev_err(&port->dev, "%s - out of memory\n", __func__); + goto err_buf; + } + *transfer_buffer = ir_xbof | ir_baud; usb_fill_bulk_urb( - port->write_urb, + urb, port->serial->dev, usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, + transfer_buffer, 1, - ir_write_bulk_callback, + ir_set_termios_callback, port); - port->write_urb->transfer_flags = URB_ZERO_PACKET; + urb->transfer_flags = URB_ZERO_PACKET; - result = usb_submit_urb(port->write_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", - __func__, result); + result = usb_submit_urb(urb, GFP_KERNEL); + if (result) { + dev_err(&port->dev, "%s - failed to submit urb: %d\n", + __func__, result); + goto err_subm; + } - /* Only speed changes are supported */ - tty_termios_copy_hw(tty->termios, old_termios); - tty_encode_baud_rate(tty, baud, baud); + usb_free_urb(urb); + + return; +err_subm: + kfree(transfer_buffer); +err_buf: + usb_free_urb(urb); } static int __init ir_init(void) { int retval; + if (buffer_size) { + ir_device.bulk_in_size = buffer_size; + ir_device.bulk_out_size = buffer_size; + } + retval = usb_serial_register(&ir_device); if (retval) goto failed_usb_serial_register; diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 43f13cf..74551cb 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -1044,34 +1044,6 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) if (buf == NULL) return -ENOMEM; - /* fixup the endpoint buffer size */ - kfree(port->bulk_out_buffer); - port->bulk_out_buffer = kmalloc(512, GFP_KERNEL); - port->bulk_out_size = 512; - kfree(port->bulk_in_buffer); - port->bulk_in_buffer = kmalloc(512, GFP_KERNEL); - port->bulk_in_size = 512; - - if (!port->bulk_out_buffer || !port->bulk_in_buffer) { - kfree(port->bulk_out_buffer); - kfree(port->bulk_in_buffer); - kfree(buf); - return -ENOMEM; - } - - usb_fill_bulk_urb(port->write_urb, port->serial->dev, - usb_sndbulkpipe(port->serial->dev, - port->bulk_out_endpointAddress), - port->bulk_out_buffer, 512, - NULL, NULL); - - - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->bulk_in_buffer, 512, - NULL, NULL); - priv->poll = 0; /* initialize writebuf */ @@ -1277,6 +1249,8 @@ static struct usb_serial_driver iuu_device = { }, .id_table = id_table, .num_ports = 1, + .bulk_in_size = 512, + .bulk_out_size = 512, .port_probe = iuu_create_sysfs_attrs, .port_remove = iuu_remove_sysfs_attrs, .open = iuu_open, diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 8eef91b..cdbe8bf 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -1,6 +1,7 @@ /* * KLSI KL5KUSB105 chip RS232 converter driver * + * Copyright (C) 2010 Johan Hovold <jhovold@gmail.com> * Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de> * * This program is free software; you can redistribute it and/or modify @@ -34,17 +35,6 @@ * implement handshaking or decide that we do not support it */ -/* History: - * 0.3a - implemented pools of write URBs - * 0.3 - alpha version for public testing - * 0.2 - TIOCMGET works, so autopilot(1) can be used! - * 0.1 - can be used to do pilot-xfer -p /dev/ttyUSB0 -l - * - * The driver skeleton is mainly based on mct_u232.c and various other - * pieces of code shamelessly copied from the drivers/usb/serial/ directory. - */ - - #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -64,8 +54,8 @@ static int debug; /* * Version Information */ -#define DRIVER_VERSION "v0.3a" -#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>" +#define DRIVER_VERSION "v0.4" +#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>, Johan Hovold <jhovold@gmail.com>" #define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver" @@ -73,23 +63,17 @@ static int debug; * Function prototypes */ static int klsi_105_startup(struct usb_serial *serial); -static void klsi_105_disconnect(struct usb_serial *serial); static void klsi_105_release(struct usb_serial *serial); static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port); static void klsi_105_close(struct usb_serial_port *port); -static int klsi_105_write(struct tty_struct *tty, - struct usb_serial_port *port, const unsigned char *buf, int count); -static void klsi_105_write_bulk_callback(struct urb *urb); -static int klsi_105_chars_in_buffer(struct tty_struct *tty); -static int klsi_105_write_room(struct tty_struct *tty); -static void klsi_105_read_bulk_callback(struct urb *urb); static void klsi_105_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); -static void klsi_105_throttle(struct tty_struct *tty); -static void klsi_105_unthrottle(struct tty_struct *tty); static int klsi_105_tiocmget(struct tty_struct *tty, struct file *file); static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); +static void klsi_105_process_read_urb(struct urb *urb); +static int klsi_105_prepare_write_buffer(struct usb_serial_port *port, + void *dest, size_t size); /* * All of the device info needed for the KLSI converters. @@ -107,7 +91,7 @@ static struct usb_driver kl5kusb105d_driver = { .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, - .no_dynamic_id = 1, + .no_dynamic_id = 1, }; static struct usb_serial_driver kl5kusb105d_device = { @@ -115,26 +99,23 @@ static struct usb_serial_driver kl5kusb105d_device = { .owner = THIS_MODULE, .name = "kl5kusb105d", }, - .description = "KL5KUSB105D / PalmConnect", - .usb_driver = &kl5kusb105d_driver, - .id_table = id_table, - .num_ports = 1, - .open = klsi_105_open, - .close = klsi_105_close, - .write = klsi_105_write, - .write_bulk_callback = klsi_105_write_bulk_callback, - .chars_in_buffer = klsi_105_chars_in_buffer, - .write_room = klsi_105_write_room, - .read_bulk_callback = klsi_105_read_bulk_callback, - .set_termios = klsi_105_set_termios, - /*.break_ctl = klsi_105_break_ctl,*/ - .tiocmget = klsi_105_tiocmget, - .tiocmset = klsi_105_tiocmset, - .attach = klsi_105_startup, - .disconnect = klsi_105_disconnect, - .release = klsi_105_release, - .throttle = klsi_105_throttle, - .unthrottle = klsi_105_unthrottle, + .description = "KL5KUSB105D / PalmConnect", + .usb_driver = &kl5kusb105d_driver, + .id_table = id_table, + .num_ports = 1, + .bulk_out_size = 64, + .open = klsi_105_open, + .close = klsi_105_close, + .set_termios = klsi_105_set_termios, + /*.break_ctl = klsi_105_break_ctl,*/ + .tiocmget = klsi_105_tiocmget, + .tiocmset = klsi_105_tiocmset, + .attach = klsi_105_startup, + .release = klsi_105_release, + .throttle = usb_serial_generic_throttle, + .unthrottle = usb_serial_generic_unthrottle, + .process_read_urb = klsi_105_process_read_urb, + .prepare_write_buffer = klsi_105_prepare_write_buffer, }; struct klsi_105_port_settings { @@ -145,18 +126,11 @@ struct klsi_105_port_settings { __u8 unknown2; } __attribute__ ((packed)); -/* we implement a pool of NUM_URBS urbs per usb_serial */ -#define NUM_URBS 1 -#define URB_TRANSFER_BUFFER_SIZE 64 struct klsi_105_private { struct klsi_105_port_settings cfg; struct ktermios termios; unsigned long line_state; /* modem line settings */ - /* write pool */ - struct urb *write_urb_pool[NUM_URBS]; spinlock_t lock; - unsigned long bytes_in; - unsigned long bytes_out; }; @@ -189,7 +163,7 @@ static int klsi_105_chg_port_settings(struct usb_serial_port *port, settings->pktlen, settings->baudrate, settings->databits, settings->unknown1, settings->unknown2); return rc; -} /* klsi_105_chg_port_settings */ +} /* translate a 16-bit status value from the device to linux's TIO bits */ static unsigned long klsi_105_status2linestate(const __u16 status) @@ -202,6 +176,7 @@ static unsigned long klsi_105_status2linestate(const __u16 status) return res; } + /* * Read line control via vendor command and return result through * *line_state_p @@ -258,7 +233,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, static int klsi_105_startup(struct usb_serial *serial) { struct klsi_105_private *priv; - int i, j; + int i; /* check if we support the product id (see keyspan.c) * FIXME @@ -282,29 +257,9 @@ static int klsi_105_startup(struct usb_serial *serial) priv->line_state = 0; - priv->bytes_in = 0; - priv->bytes_out = 0; usb_set_serial_port_data(serial->port[i], priv); spin_lock_init(&priv->lock); - for (j = 0; j < NUM_URBS; j++) { - struct urb *urb = usb_alloc_urb(0, GFP_KERNEL); - - priv->write_urb_pool[j] = urb; - if (urb == NULL) { - dev_err(&serial->dev->dev, "No more urbs???\n"); - goto err_cleanup; - } - - urb->transfer_buffer = - kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); - if (!urb->transfer_buffer) { - dev_err(&serial->dev->dev, - "%s - out of memory for urb buffers.\n", - __func__); - goto err_cleanup; - } - } /* priv->termios is left uninitalized until port opening */ init_waitqueue_head(&serial->port[i]->write_wait); @@ -315,44 +270,11 @@ static int klsi_105_startup(struct usb_serial *serial) err_cleanup: for (; i >= 0; i--) { priv = usb_get_serial_port_data(serial->port[i]); - for (j = 0; j < NUM_URBS; j++) { - if (priv->write_urb_pool[j]) { - kfree(priv->write_urb_pool[j]->transfer_buffer); - usb_free_urb(priv->write_urb_pool[j]); - } - } + kfree(priv); usb_set_serial_port_data(serial->port[i], NULL); } return -ENOMEM; -} /* klsi_105_startup */ - - -static void klsi_105_disconnect(struct usb_serial *serial) -{ - int i; - - dbg("%s", __func__); - - /* stop reads and writes on all ports */ - for (i = 0; i < serial->num_ports; ++i) { - struct klsi_105_private *priv = - usb_get_serial_port_data(serial->port[i]); - - if (priv) { - /* kill our write urb pool */ - int j; - struct urb **write_urbs = priv->write_urb_pool; - - for (j = 0; j < NUM_URBS; j++) { - if (write_urbs[j]) { - usb_kill_urb(write_urbs[j]); - usb_free_urb(write_urbs[j]); - } - } - } - } -} /* klsi_105_disconnect */ - +} static void klsi_105_release(struct usb_serial *serial) { @@ -360,13 +282,9 @@ static void klsi_105_release(struct usb_serial *serial) dbg("%s", __func__); - for (i = 0; i < serial->num_ports; ++i) { - struct klsi_105_private *priv = - usb_get_serial_port_data(serial->port[i]); - - kfree(priv); - } -} /* klsi_105_release */ + for (i = 0; i < serial->num_ports; ++i) + kfree(usb_get_serial_port_data(serial->port[i])); +} static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) { @@ -416,18 +334,8 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) spin_unlock_irqrestore(&priv->lock, flags); /* READ_ON and urb submission */ - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - klsi_105_read_bulk_callback, - port); - - rc = usb_submit_urb(port->read_urb, GFP_KERNEL); + rc = usb_serial_generic_open(tty, port); if (rc) { - dev_err(&port->dev, "%s - failed submitting read urb, " - "error %d\n", __func__, rc); retval = rc; goto exit; } @@ -460,12 +368,10 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) exit: kfree(cfg); return retval; -} /* klsi_105_open */ - +} static void klsi_105_close(struct usb_serial_port *port) { - struct klsi_105_private *priv = usb_get_serial_port_data(port); int rc; dbg("%s port %d", __func__, port->number); @@ -488,239 +394,62 @@ static void klsi_105_close(struct usb_serial_port *port) mutex_unlock(&port->serial->disc_mutex); /* shutdown our bulk reads and writes */ - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); - /* unlink our write pool */ - /* FIXME */ + usb_serial_generic_close(port); + /* wgg - do I need this? I think so. */ usb_kill_urb(port->interrupt_in_urb); - dev_info(&port->serial->dev->dev, - "port stats: %ld bytes in, %ld bytes out\n", - priv->bytes_in, priv->bytes_out); -} /* klsi_105_close */ - +} /* We need to write a complete 64-byte data block and encode the * number actually sent in the first double-byte, LSB-order. That * leaves at most 62 bytes of payload. */ -#define KLSI_105_DATA_OFFSET 2 /* in the bulk urb data block */ - - -static int klsi_105_write(struct tty_struct *tty, - struct usb_serial_port *port, const unsigned char *buf, int count) +#define KLSI_HDR_LEN 2 +static int klsi_105_prepare_write_buffer(struct usb_serial_port *port, + void *dest, size_t size) { - struct klsi_105_private *priv = usb_get_serial_port_data(port); - int result, size; - int bytes_sent = 0; - - dbg("%s - port %d", __func__, port->number); - - while (count > 0) { - /* try to find a free urb (write 0 bytes if none) */ - struct urb *urb = NULL; - unsigned long flags; - int i; - /* since the pool is per-port we might not need - the spin lock !? */ - spin_lock_irqsave(&priv->lock, flags); - for (i = 0; i < NUM_URBS; i++) { - if (priv->write_urb_pool[i]->status != -EINPROGRESS) { - urb = priv->write_urb_pool[i]; - dbg("%s - using pool URB %d", __func__, i); - break; - } - } - spin_unlock_irqrestore(&priv->lock, flags); - - if (urb == NULL) { - dbg("%s - no more free urbs", __func__); - goto exit; - } - - if (urb->transfer_buffer == NULL) { - urb->transfer_buffer = - kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC); - if (urb->transfer_buffer == NULL) { - dev_err(&port->dev, - "%s - no more kernel memory...\n", - __func__); - goto exit; - } - } - - size = min(count, port->bulk_out_size - KLSI_105_DATA_OFFSET); - size = min(size, URB_TRANSFER_BUFFER_SIZE - - KLSI_105_DATA_OFFSET); - - memcpy(urb->transfer_buffer + KLSI_105_DATA_OFFSET, buf, size); - - /* write payload size into transfer buffer */ - ((__u8 *)urb->transfer_buffer)[0] = (__u8) (size & 0xFF); - ((__u8 *)urb->transfer_buffer)[1] = (__u8) ((size & 0xFF00)>>8); - - /* set up our urb */ - usb_fill_bulk_urb(urb, port->serial->dev, - usb_sndbulkpipe(port->serial->dev, - port->bulk_out_endpointAddress), - urb->transfer_buffer, - URB_TRANSFER_BUFFER_SIZE, - klsi_105_write_bulk_callback, - port); - - /* send the data out the bulk port */ - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result) { - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", - __func__, result); - goto exit; - } - buf += size; - bytes_sent += size; - count -= size; - } -exit: - /* lockless, but it's for debug info only... */ - priv->bytes_out += bytes_sent; - - return bytes_sent; /* that's how much we wrote */ -} /* klsi_105_write */ - -static void klsi_105_write_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - int status = urb->status; - - dbg("%s - port %d", __func__, port->number); - - if (status) { - dbg("%s - nonzero write bulk status received: %d", __func__, - status); - return; - } + unsigned char *buf = dest; + int count; - usb_serial_port_softint(port); -} /* klsi_105_write_bulk_completion_callback */ + count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size, + &port->lock); + put_unaligned_le16(count, buf); - -/* return number of characters currently in the writing process */ -static int klsi_105_chars_in_buffer(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - int chars = 0; - int i; - unsigned long flags; - struct klsi_105_private *priv = usb_get_serial_port_data(port); - - spin_lock_irqsave(&priv->lock, flags); - - for (i = 0; i < NUM_URBS; ++i) { - if (priv->write_urb_pool[i]->status == -EINPROGRESS) - chars += URB_TRANSFER_BUFFER_SIZE; - } - - spin_unlock_irqrestore(&priv->lock, flags); - - dbg("%s - returns %d", __func__, chars); - return chars; + return count + KLSI_HDR_LEN; } -static int klsi_105_write_room(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - unsigned long flags; - int i; - int room = 0; - struct klsi_105_private *priv = usb_get_serial_port_data(port); - - spin_lock_irqsave(&priv->lock, flags); - for (i = 0; i < NUM_URBS; ++i) { - if (priv->write_urb_pool[i]->status != -EINPROGRESS) - room += URB_TRANSFER_BUFFER_SIZE; - } - - spin_unlock_irqrestore(&priv->lock, flags); - - dbg("%s - returns %d", __func__, room); - return room; -} - - - -static void klsi_105_read_bulk_callback(struct urb *urb) +/* The data received is preceded by a length double-byte in LSB-first order. + */ +static void klsi_105_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct klsi_105_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; - int rc; - int status = urb->status; + struct tty_struct *tty; + unsigned len; - dbg("%s - port %d", __func__, port->number); + /* empty urbs seem to happen, we ignore them */ + if (!urb->actual_length) + return; - /* The urb might have been killed. */ - if (status) { - dbg("%s - nonzero read bulk status received: %d", __func__, - status); + if (urb->actual_length <= KLSI_HDR_LEN) { + dbg("%s - malformed packet", __func__); return; } - /* The data received is again preceded by a length double-byte in LSB- - * first order (see klsi_105_write() ) - */ - if (urb->actual_length == 0) { - /* empty urbs seem to happen, we ignore them */ - /* dbg("%s - emtpy URB", __func__); */ - ; - } else if (urb->actual_length <= 2) { - dbg("%s - size %d URB not understood", __func__, - urb->actual_length); - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - } else { - int bytes_sent = ((__u8 *) data)[0] + - ((unsigned int) ((__u8 *) data)[1] << 8); - tty = tty_port_tty_get(&port->port); - /* we should immediately resubmit the URB, before attempting - * to pass the data on to the tty layer. But that needs locking - * against re-entry an then mixed-up data because of - * intermixed tty_flip_buffer_push()s - * FIXME - */ - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - - if (bytes_sent + 2 > urb->actual_length) { - dbg("%s - trying to read more data than available" - " (%d vs. %d)", __func__, - bytes_sent+2, urb->actual_length); - /* cap at implied limit */ - bytes_sent = urb->actual_length - 2; - } - - tty_insert_flip_string(tty, data + 2, bytes_sent); - tty_flip_buffer_push(tty); - tty_kref_put(tty); + tty = tty_port_tty_get(&port->port); + if (!tty) + return; - /* again lockless, but debug info only */ - priv->bytes_in += bytes_sent; + len = get_unaligned_le16(data); + if (len > urb->actual_length - KLSI_HDR_LEN) { + dbg("%s - packet length mismatch", __func__); + len = urb->actual_length - KLSI_HDR_LEN; } - /* Continue trying to always read */ - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - klsi_105_read_bulk_callback, - port); - rc = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (rc) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, rc); -} /* klsi_105_read_bulk_callback */ + tty_insert_flip_string(tty, data + KLSI_HDR_LEN, len); + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} static void klsi_105_set_termios(struct tty_struct *tty, struct usb_serial_port *port, @@ -887,8 +616,7 @@ static void klsi_105_set_termios(struct tty_struct *tty, klsi_105_chg_port_settings(port, cfg); err: kfree(cfg); -} /* klsi_105_set_termios */ - +} #if 0 static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) @@ -906,7 +634,7 @@ static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) lcr |= MCT_U232_SET_BREAK; mct_u232_set_line_ctrl(serial, lcr); -} /* mct_u232_break_ctl */ +} #endif static int klsi_105_tiocmget(struct tty_struct *tty, struct file *file) @@ -962,29 +690,6 @@ static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file, return retval; } -static void klsi_105_throttle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - dbg("%s - port %d", __func__, port->number); - usb_kill_urb(port->read_urb); -} - -static void klsi_105_unthrottle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - int result; - - dbg("%s - port %d", __func__, port->number); - - port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, result); -} - - static int __init klsi_105_init(void) { @@ -1005,7 +710,6 @@ failed_usb_serial_register: return retval; } - static void __exit klsi_105_exit(void) { usb_deregister(&kl5kusb105d_driver); @@ -1023,5 +727,3 @@ MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "enable extensive debugging messages"); - -/* vim: set sts=8 ts=8 sw=8: */ diff --git a/drivers/usb/serial/kl5kusb105.h b/drivers/usb/serial/kl5kusb105.h index 1231d9e..22a90ba 100644 --- a/drivers/usb/serial/kl5kusb105.h +++ b/drivers/usb/serial/kl5kusb105.h @@ -17,16 +17,16 @@ /* baud rates */ enum { - kl5kusb105a_sio_b115200 = 0, - kl5kusb105a_sio_b57600 = 1, - kl5kusb105a_sio_b38400 = 2, - kl5kusb105a_sio_b19200 = 4, - kl5kusb105a_sio_b14400 = 5, - kl5kusb105a_sio_b9600 = 6, - kl5kusb105a_sio_b4800 = 8, /* unchecked */ - kl5kusb105a_sio_b2400 = 9, /* unchecked */ - kl5kusb105a_sio_b1200 = 0xa, /* unchecked */ - kl5kusb105a_sio_b600 = 0xb /* unchecked */ + kl5kusb105a_sio_b115200 = 0, + kl5kusb105a_sio_b57600 = 1, + kl5kusb105a_sio_b38400 = 2, + kl5kusb105a_sio_b19200 = 4, + kl5kusb105a_sio_b14400 = 5, + kl5kusb105a_sio_b9600 = 6, + kl5kusb105a_sio_b4800 = 8, /* unchecked */ + kl5kusb105a_sio_b2400 = 9, /* unchecked */ + kl5kusb105a_sio_b1200 = 0xa, /* unchecked */ + kl5kusb105a_sio_b600 = 0xb /* unchecked */ }; /* data bits */ @@ -53,17 +53,16 @@ enum { #define KL5KUSB105A_CTS ((1<<5) | (1<<4)) #define KL5KUSB105A_WANTS_TO_SEND 0x30 -//#define KL5KUSB105A_DTR /* Data Terminal Ready */ -//#define KL5KUSB105A_CTS /* Clear To Send */ -//#define KL5KUSB105A_CD /* Carrier Detect */ -//#define KL5KUSB105A_DSR /* Data Set Ready */ -//#define KL5KUSB105A_RxD /* Receive pin */ - -//#define KL5KUSB105A_LE -//#define KL5KUSB105A_RTS -//#define KL5KUSB105A_ST -//#define KL5KUSB105A_SR -//#define KL5KUSB105A_RI /* Ring Indicator */ - -/* vim: set ts=8 sts=8: */ - +#if 0 +#define KL5KUSB105A_DTR /* Data Terminal Ready */ +#define KL5KUSB105A_CTS /* Clear To Send */ +#define KL5KUSB105A_CD /* Carrier Detect */ +#define KL5KUSB105A_DSR /* Data Set Ready */ +#define KL5KUSB105A_RxD /* Receive pin */ + +#define KL5KUSB105A_LE +#define KL5KUSB105A_RTS +#define KL5KUSB105A_ST +#define KL5KUSB105A_SR +#define KL5KUSB105A_RI /* Ring Indicator */ +#endif diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index c113a2a..bd5bd85 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -345,7 +345,8 @@ static void kobil_close(struct usb_serial_port *port) /* FIXME: Add rts/dtr methods */ if (port->write_urb) { - usb_kill_urb(port->write_urb); + usb_poison_urb(port->write_urb); + kfree(port->write_urb->transfer_buffer); usb_free_urb(port->write_urb); port->write_urb = NULL; } diff --git a/drivers/usb/serial/kobil_sct.h b/drivers/usb/serial/kobil_sct.h index a51fbb5..be207f7 100644 --- a/drivers/usb/serial/kobil_sct.h +++ b/drivers/usb/serial/kobil_sct.h @@ -23,38 +23,55 @@ #define SUSBCR_SSL_SETDTR 0x0004 #define SUSBCR_SSL_CLRDTR 0x0010 -#define SUSBCR_SSL_PURGE_TXABORT 0x0100 // Kill the pending/current writes to the comm port. -#define SUSBCR_SSL_PURGE_RXABORT 0x0200 // Kill the pending/current reads to the comm port. -#define SUSBCR_SSL_PURGE_TXCLEAR 0x0400 // Kill the transmit queue if there. -#define SUSBCR_SSL_PURGE_RXCLEAR 0x0800 // Kill the typeahead buffer if there. +/* Kill the pending/current writes to the comm port. */ +#define SUSBCR_SSL_PURGE_TXABORT 0x0100 +/* Kill the pending/current reads to the comm port. */ +#define SUSBCR_SSL_PURGE_RXABORT 0x0200 +/* Kill the transmit queue if there. */ +#define SUSBCR_SSL_PURGE_TXCLEAR 0x0400 +/* Kill the typeahead buffer if there. */ +#define SUSBCR_SSL_PURGE_RXCLEAR 0x0800 #define SUSBCRequest_GetStatusLineState 4 -#define SUSBCR_GSL_RXCHAR 0x0001 // Any Character received -#define SUSBCR_GSL_TXEMPTY 0x0004 // Transmitt Queue Empty -#define SUSBCR_GSL_CTS 0x0008 // CTS changed state -#define SUSBCR_GSL_DSR 0x0010 // DSR changed state -#define SUSBCR_GSL_RLSD 0x0020 // RLSD changed state -#define SUSBCR_GSL_BREAK 0x0040 // BREAK received -#define SUSBCR_GSL_ERR 0x0080 // Line status error occurred -#define SUSBCR_GSL_RING 0x0100 // Ring signal detected +/* Any Character received */ +#define SUSBCR_GSL_RXCHAR 0x0001 +/* Transmitt Queue Empty */ +#define SUSBCR_GSL_TXEMPTY 0x0004 +/* CTS changed state */ +#define SUSBCR_GSL_CTS 0x0008 +/* DSR changed state */ +#define SUSBCR_GSL_DSR 0x0010 +/* RLSD changed state */ +#define SUSBCR_GSL_RLSD 0x0020 +/* BREAK received */ +#define SUSBCR_GSL_BREAK 0x0040 +/* Line status error occurred */ +#define SUSBCR_GSL_ERR 0x0080 +/* Ring signal detected */ +#define SUSBCR_GSL_RING 0x0100 #define SUSBCRequest_Misc 8 -#define SUSBCR_MSC_ResetReader 0x0001 // use a predefined reset sequence -#define SUSBCR_MSC_ResetAllQueues 0x0002 // use a predefined sequence to reset the internal queues +/* use a predefined reset sequence */ +#define SUSBCR_MSC_ResetReader 0x0001 +/* use a predefined sequence to reset the internal queues */ +#define SUSBCR_MSC_ResetAllQueues 0x0002 #define SUSBCRequest_GetMisc 0x10 -#define SUSBCR_MSC_GetFWVersion 0x0001 /* get the firmware version from device, - coded like this 0xHHLLBBPP - with HH = Firmware Version High Byte - LL = Firmware Version Low Byte - BB = Build Number - PP = Further Attributes - */ - -#define SUSBCR_MSC_GetHWVersion 0x0002 /* get the hardware version from device - coded like this 0xHHLLPPRR - with HH = Software Version High Byte - LL = Software Version Low Byte - PP = Further Attributes - RR = Reserved for the hardware ID - */ + +/* + * get the firmware version from device, coded like this 0xHHLLBBPP with + * HH = Firmware Version High Byte + * LL = Firmware Version Low Byte + * BB = Build Number + * PP = Further Attributes + */ +#define SUSBCR_MSC_GetFWVersion 0x0001 + +/* + * get the hardware version from device coded like this 0xHHLLPPRR with + * HH = Software Version High Byte + * LL = Software Version Low Byte + * PP = Further Attributes + * RR = Reserved for the hardware ID + */ +#define SUSBCR_MSC_GetHWVersion 0x0002 diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 2849f8c..7aa01b9 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -549,12 +549,9 @@ static void mct_u232_close(struct usb_serial_port *port) { dbg("%s port %d", __func__, port->number); - if (port->serial->dev) { - /* shutdown our urbs */ - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); + if (port->serial->dev) usb_kill_urb(port->interrupt_in_urb); - } } /* mct_u232_close */ diff --git a/drivers/usb/serial/mct_u232.h b/drivers/usb/serial/mct_u232.h index 7417d5c..3a3f5e6 100644 --- a/drivers/usb/serial/mct_u232.h +++ b/drivers/usb/serial/mct_u232.h @@ -42,36 +42,44 @@ #define MCT_U232_SET_REQUEST_TYPE 0x40 #define MCT_U232_GET_REQUEST_TYPE 0xc0 -#define MCT_U232_GET_MODEM_STAT_REQUEST 2 /* Get Modem Status Register (MSR) */ -#define MCT_U232_GET_MODEM_STAT_SIZE 1 +/* Get Modem Status Register (MSR) */ +#define MCT_U232_GET_MODEM_STAT_REQUEST 2 +#define MCT_U232_GET_MODEM_STAT_SIZE 1 -#define MCT_U232_GET_LINE_CTRL_REQUEST 6 /* Get Line Control Register (LCR) */ -#define MCT_U232_GET_LINE_CTRL_SIZE 1 /* ... not used by this driver */ +/* Get Line Control Register (LCR) */ +/* ... not used by this driver */ +#define MCT_U232_GET_LINE_CTRL_REQUEST 6 +#define MCT_U232_GET_LINE_CTRL_SIZE 1 -#define MCT_U232_SET_BAUD_RATE_REQUEST 5 /* Set Baud Rate Divisor */ -#define MCT_U232_SET_BAUD_RATE_SIZE 4 +/* Set Baud Rate Divisor */ +#define MCT_U232_SET_BAUD_RATE_REQUEST 5 +#define MCT_U232_SET_BAUD_RATE_SIZE 4 -#define MCT_U232_SET_LINE_CTRL_REQUEST 7 /* Set Line Control Register (LCR) */ -#define MCT_U232_SET_LINE_CTRL_SIZE 1 +/* Set Line Control Register (LCR) */ +#define MCT_U232_SET_LINE_CTRL_REQUEST 7 +#define MCT_U232_SET_LINE_CTRL_SIZE 1 -#define MCT_U232_SET_MODEM_CTRL_REQUEST 10 /* Set Modem Control Register (MCR) */ -#define MCT_U232_SET_MODEM_CTRL_SIZE 1 +/* Set Modem Control Register (MCR) */ +#define MCT_U232_SET_MODEM_CTRL_REQUEST 10 +#define MCT_U232_SET_MODEM_CTRL_SIZE 1 -/* This USB device request code is not well understood. It is transmitted by - the MCT-supplied Windows driver whenever the baud rate changes. -*/ -#define MCT_U232_SET_UNKNOWN1_REQUEST 11 /* Unknown functionality */ -#define MCT_U232_SET_UNKNOWN1_SIZE 1 +/* + * This USB device request code is not well understood. It is transmitted by + * the MCT-supplied Windows driver whenever the baud rate changes. + */ +#define MCT_U232_SET_UNKNOWN1_REQUEST 11 /* Unknown functionality */ +#define MCT_U232_SET_UNKNOWN1_SIZE 1 -/* This USB device request code appears to control whether CTS is required - during transmission. - - Sending a zero byte allows data transmission to a device which is not - asserting CTS. Sending a '1' byte will cause transmission to be deferred - until the device asserts CTS. -*/ -#define MCT_U232_SET_CTS_REQUEST 12 -#define MCT_U232_SET_CTS_SIZE 1 +/* + * This USB device request code appears to control whether CTS is required + * during transmission. + * + * Sending a zero byte allows data transmission to a device which is not + * asserting CTS. Sending a '1' byte will cause transmission to be deferred + * until the device asserts CTS. + */ +#define MCT_U232_SET_CTS_REQUEST 12 +#define MCT_U232_SET_CTS_SIZE 1 #define MCT_U232_MAX_SIZE 4 /* of MCT_XXX_SIZE */ @@ -81,7 +89,8 @@ * and "Intel solution". They are the regular MCT and "Sitecom" for us. * This is pointless to document in the header, see the code for the bits. */ -static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value, speed_t *result); +static int mct_u232_calculate_baud_rate(struct usb_serial *serial, + speed_t value, speed_t *result); /* * Line Control Register (LCR) @@ -125,16 +134,16 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value /* * Line Status Register (LSR) */ -#define MCT_U232_LSR_INDEX 1 /* data[index] */ -#define MCT_U232_LSR_ERR 0x80 /* OE | PE | FE | BI */ -#define MCT_U232_LSR_TEMT 0x40 /* transmit register empty */ -#define MCT_U232_LSR_THRE 0x20 /* transmit holding register empty */ -#define MCT_U232_LSR_BI 0x10 /* break indicator */ -#define MCT_U232_LSR_FE 0x08 /* framing error */ -#define MCT_U232_LSR_OE 0x02 /* overrun error */ -#define MCT_U232_LSR_PE 0x04 /* parity error */ -#define MCT_U232_LSR_OE 0x02 /* overrun error */ -#define MCT_U232_LSR_DR 0x01 /* receive data ready */ +#define MCT_U232_LSR_INDEX 1 /* data[index] */ +#define MCT_U232_LSR_ERR 0x80 /* OE | PE | FE | BI */ +#define MCT_U232_LSR_TEMT 0x40 /* transmit register empty */ +#define MCT_U232_LSR_THRE 0x20 /* transmit holding register empty */ +#define MCT_U232_LSR_BI 0x10 /* break indicator */ +#define MCT_U232_LSR_FE 0x08 /* framing error */ +#define MCT_U232_LSR_OE 0x02 /* overrun error */ +#define MCT_U232_LSR_PE 0x04 /* parity error */ +#define MCT_U232_LSR_OE 0x02 /* overrun error */ +#define MCT_U232_LSR_DR 0x01 /* receive data ready */ /* ----------------------------------------------------------------------------- @@ -143,10 +152,10 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value * * The technical details of the device have been acquired be using "SniffUSB" * and the vendor-supplied device driver (version 2.3A) under Windows98. To - * identify the USB vendor-specific requests and to assign them to terminal + * identify the USB vendor-specific requests and to assign them to terminal * settings (flow control, baud rate, etc.) the program "SerialSettings" from * William G. Greathouse has been proven to be very useful. I also used the - * Win98 "HyperTerminal" and "usb-robot" on Linux for testing. The results and + * Win98 "HyperTerminal" and "usb-robot" on Linux for testing. The results and * observations are summarized below: * * The USB requests seem to be directly mapped to the registers of a 8250, @@ -186,33 +195,33 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value * Data: LCR (see below) * * Bit 7: Divisor Latch Access Bit (DLAB). When set, access to the data - * transmit/receive register (THR/RBR) and the Interrupt Enable Register - * (IER) is disabled. Any access to these ports is now redirected to the - * Divisor Latch Registers. Setting this bit, loading the Divisor - * Registers, and clearing DLAB should be done with interrupts disabled. + * transmit/receive register (THR/RBR) and the Interrupt Enable Register + * (IER) is disabled. Any access to these ports is now redirected to the + * Divisor Latch Registers. Setting this bit, loading the Divisor + * Registers, and clearing DLAB should be done with interrupts disabled. * Bit 6: Set Break. When set to "1", the transmitter begins to transmit - * continuous Spacing until this bit is set to "0". This overrides any - * bits of characters that are being transmitted. + * continuous Spacing until this bit is set to "0". This overrides any + * bits of characters that are being transmitted. * Bit 5: Stick Parity. When parity is enabled, setting this bit causes parity - * to always be "1" or "0", based on the value of Bit 4. + * to always be "1" or "0", based on the value of Bit 4. * Bit 4: Even Parity Select (EPS). When parity is enabled and Bit 5 is "0", - * setting this bit causes even parity to be transmitted and expected. - * Otherwise, odd parity is used. + * setting this bit causes even parity to be transmitted and expected. + * Otherwise, odd parity is used. * Bit 3: Parity Enable (PEN). When set to "1", a parity bit is inserted - * between the last bit of the data and the Stop Bit. The UART will also - * expect parity to be present in the received data. + * between the last bit of the data and the Stop Bit. The UART will also + * expect parity to be present in the received data. * Bit 2: Number of Stop Bits (STB). If set to "1" and using 5-bit data words, - * 1.5 Stop Bits are transmitted and expected in each data word. For - * 6, 7 and 8-bit data words, 2 Stop Bits are transmitted and expected. - * When this bit is set to "0", one Stop Bit is used on each data word. + * 1.5 Stop Bits are transmitted and expected in each data word. For + * 6, 7 and 8-bit data words, 2 Stop Bits are transmitted and expected. + * When this bit is set to "0", one Stop Bit is used on each data word. * Bit 1: Word Length Select Bit #1 (WLSB1) * Bit 0: Word Length Select Bit #0 (WLSB0) - * Together these bits specify the number of bits in each data word. - * 1 0 Word Length - * 0 0 5 Data Bits - * 0 1 6 Data Bits - * 1 0 7 Data Bits - * 1 1 8 Data Bits + * Together these bits specify the number of bits in each data word. + * 1 0 Word Length + * 0 0 5 Data Bits + * 0 1 6 Data Bits + * 1 0 7 Data Bits + * 1 1 8 Data Bits * * SniffUSB observations: Bit 7 seems not to be used. There seem to be two bugs * in the Win98 driver: the break does not work (bit 6 is not asserted) and the @@ -234,20 +243,20 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value * Bit 6: Reserved, always 0. * Bit 5: Reserved, always 0. * Bit 4: Loop-Back Enable. When set to "1", the UART transmitter and receiver - * are internally connected together to allow diagnostic operations. In - * addition, the UART modem control outputs are connected to the UART - * modem control inputs. CTS is connected to RTS, DTR is connected to - * DSR, OUT1 is connected to RI, and OUT 2 is connected to DCD. + * are internally connected together to allow diagnostic operations. In + * addition, the UART modem control outputs are connected to the UART + * modem control inputs. CTS is connected to RTS, DTR is connected to + * DSR, OUT1 is connected to RI, and OUT 2 is connected to DCD. * Bit 3: OUT 2. An auxiliary output that the host processor may set high or - * low. In the IBM PC serial adapter (and most clones), OUT 2 is used - * to tri-state (disable) the interrupt signal from the - * 8250/16450/16550 UART. + * low. In the IBM PC serial adapter (and most clones), OUT 2 is used + * to tri-state (disable) the interrupt signal from the + * 8250/16450/16550 UART. * Bit 2: OUT 1. An auxiliary output that the host processor may set high or - * low. This output is not used on the IBM PC serial adapter. + * low. This output is not used on the IBM PC serial adapter. * Bit 1: Request to Send (RTS). When set to "1", the output of the UART -RTS - * line is Low (Active). + * line is Low (Active). * Bit 0: Data Terminal Ready (DTR). When set to "1", the output of the UART - * -DTR line is Low (Active). + * -DTR line is Low (Active). * * SniffUSB observations: Bit 2 and 4 seem not to be used but bit 3 has been * seen _always_ set. @@ -264,22 +273,22 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value * Data: MSR (see below) * * Bit 7: Data Carrier Detect (CD). Reflects the state of the DCD line on the - * UART. + * UART. * Bit 6: Ring Indicator (RI). Reflects the state of the RI line on the UART. * Bit 5: Data Set Ready (DSR). Reflects the state of the DSR line on the UART. * Bit 4: Clear To Send (CTS). Reflects the state of the CTS line on the UART. * Bit 3: Delta Data Carrier Detect (DDCD). Set to "1" if the -DCD line has - * changed state one more more times since the last time the MSR was - * read by the host. + * changed state one more more times since the last time the MSR was + * read by the host. * Bit 2: Trailing Edge Ring Indicator (TERI). Set to "1" if the -RI line has - * had a low to high transition since the last time the MSR was read by - * the host. + * had a low to high transition since the last time the MSR was read by + * the host. * Bit 1: Delta Data Set Ready (DDSR). Set to "1" if the -DSR line has changed - * state one more more times since the last time the MSR was read by the - * host. + * state one more more times since the last time the MSR was read by the + * host. * Bit 0: Delta Clear To Send (DCTS). Set to "1" if the -CTS line has changed - * state one more times since the last time the MSR was read by the - * host. + * state one more times since the last time the MSR was read by the + * host. * * SniffUSB observations: the MSR is also returned as first byte on the * interrupt-in endpoint 0x83 to signal changes of modem status lines. The USB @@ -290,31 +299,34 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value * -------------------------- * * Bit 7 Error in Receiver FIFO. On the 8250/16450 UART, this bit is zero. - * This bit is set to "1" when any of the bytes in the FIFO have one or - * more of the following error conditions: PE, FE, or BI. + * This bit is set to "1" when any of the bytes in the FIFO have one + * or more of the following error conditions: PE, FE, or BI. * Bit 6 Transmitter Empty (TEMT). When set to "1", there are no words - * remaining in the transmit FIFO or the transmit shift register. The - * transmitter is completely idle. - * Bit 5 Transmitter Holding Register Empty (THRE). When set to "1", the FIFO - * (or holding register) now has room for at least one additional word - * to transmit. The transmitter may still be transmitting when this bit - * is set to "1". + * remaining in the transmit FIFO or the transmit shift register. The + * transmitter is completely idle. + * Bit 5 Transmitter Holding Register Empty (THRE). When set to "1", the + * FIFO (or holding register) now has room for at least one additional + * word to transmit. The transmitter may still be transmitting when + * this bit is set to "1". * Bit 4 Break Interrupt (BI). The receiver has detected a Break signal. - * Bit 3 Framing Error (FE). A Start Bit was detected but the Stop Bit did not - * appear at the expected time. The received word is probably garbled. - * Bit 2 Parity Error (PE). The parity bit was incorrect for the word received. - * Bit 1 Overrun Error (OE). A new word was received and there was no room in - * the receive buffer. The newly-arrived word in the shift register is - * discarded. On 8250/16450 UARTs, the word in the holding register is - * discarded and the newly- arrived word is put in the holding register. + * Bit 3 Framing Error (FE). A Start Bit was detected but the Stop Bit did + * not appear at the expected time. The received word is probably + * garbled. + * Bit 2 Parity Error (PE). The parity bit was incorrect for the word + * received. + * Bit 1 Overrun Error (OE). A new word was received and there was no room + * in the receive buffer. The newly-arrived word in the shift register + * is discarded. On 8250/16450 UARTs, the word in the holding register + * is discarded and the newly- arrived word is put in the holding + * register. * Bit 0 Data Ready (DR). One or more words are in the receive FIFO that the - * host may read. A word must be completely received and moved from the - * shift register into the FIFO (or holding register for 8250/16450 - * designs) before this bit is set. + * host may read. A word must be completely received and moved from + * the shift register into the FIFO (or holding register for + * 8250/16450 designs) before this bit is set. * - * SniffUSB observations: the LSR is returned as second byte on the interrupt-in - * endpoint 0x83 to signal error conditions. Such errors have been seen with - * minicom/zmodem transfers (CRC errors). + * SniffUSB observations: the LSR is returned as second byte on the + * interrupt-in endpoint 0x83 to signal error conditions. Such errors have + * been seen with minicom/zmodem transfers (CRC errors). * * * Unknown #1 @@ -364,16 +376,16 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value * -------------- * * SniffUSB observations: the bulk-out endpoint 0x1 and interrupt-in endpoint - * 0x81 is used to transmit and receive characters. The second interrupt-in - * endpoint 0x83 signals exceptional conditions like modem line changes and + * 0x81 is used to transmit and receive characters. The second interrupt-in + * endpoint 0x83 signals exceptional conditions like modem line changes and * errors. The first byte returned is the MSR and the second byte the LSR. * * * Other observations * ------------------ * - * Queued bulk transfers like used in visor.c did not work. - * + * Queued bulk transfers like used in visor.c did not work. + * * * Properties of the USB device used (as found in /var/log/messages) * ----------------------------------------------------------------- @@ -411,26 +423,26 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value * bInterface Class:SubClass:Protocol = 00:00:00 * iInterface = 00 * Endpoint: - * bLength = 7 - * bDescriptorType = 05 - * bEndpointAddress = 81 (in) - * bmAttributes = 03 (Interrupt) - * wMaxPacketSize = 0040 - * bInterval = 02 + * bLength = 7 + * bDescriptorType = 05 + * bEndpointAddress = 81 (in) + * bmAttributes = 03 (Interrupt) + * wMaxPacketSize = 0040 + * bInterval = 02 * Endpoint: - * bLength = 7 - * bDescriptorType = 05 - * bEndpointAddress = 01 (out) - * bmAttributes = 02 (Bulk) - * wMaxPacketSize = 0040 - * bInterval = 00 + * bLength = 7 + * bDescriptorType = 05 + * bEndpointAddress = 01 (out) + * bmAttributes = 02 (Bulk) + * wMaxPacketSize = 0040 + * bInterval = 00 * Endpoint: - * bLength = 7 - * bDescriptorType = 05 - * bEndpointAddress = 83 (in) - * bmAttributes = 03 (Interrupt) - * wMaxPacketSize = 0002 - * bInterval = 02 + * bLength = 7 + * bDescriptorType = 05 + * bEndpointAddress = 83 (in) + * bmAttributes = 03 (Interrupt) + * wMaxPacketSize = 0002 + * bInterval = 02 * * * Hardware details (added by Martin Hamilton, 2001/12/06) @@ -440,7 +452,7 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value * adaptor, which turns out to simply be a re-badged U232-P9. We * know this because there is a sticky label on the circuit board * which says "U232-P9" ;-) - * + * * The circuit board inside the adaptor contains a Philips PDIUSBD12 * USB endpoint chip and a Philips P87C52UBAA microcontroller with * embedded UART. Exhaustive documentation for these is available at: @@ -449,7 +461,7 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value * http://www.semiconductors.philips.com/pip/pdiusbd12 * * Thanks to Julian Highfield for the pointer to the Philips database. - * + * */ #endif /* __LINUX_USB_SERIAL_MCT_U232_H */ diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 0d47f2c..30922a7 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -34,21 +34,18 @@ #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/uaccess.h> - +#include <linux/parport.h> /* * Version Information */ -#define DRIVER_VERSION "1.0.0.4F" +#define DRIVER_VERSION "2.1" #define DRIVER_AUTHOR "Aspire Communications pvt Ltd." #define DRIVER_DESC "Moschip USB Serial Driver" /* default urb timeout */ #define MOS_WDR_TIMEOUT (HZ * 5) -#define MOS_PORT1 0x0200 -#define MOS_PORT2 0x0300 -#define MOS_VENREG 0x0000 #define MOS_MAX_PORT 0x02 #define MOS_WRITE 0x0E #define MOS_READ 0x0D @@ -63,7 +60,7 @@ #define NUM_URBS 16 /* URB Count */ #define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */ -/* This structure holds all of the local port information */ +/* This structure holds all of the local serial port information */ struct moschip_port { __u8 shadowLCR; /* last LCR value received */ __u8 shadowMCR; /* last MCR value received */ @@ -74,11 +71,6 @@ struct moschip_port { struct urb *write_urb_pool[NUM_URBS]; }; -/* This structure holds all of the individual serial device information */ -struct moschip_serial { - int interrupt_started; -}; - static int debug; static struct usb_serial_driver moschip7720_2port_driver; @@ -94,6 +86,658 @@ static const struct usb_device_id moschip_port_id_table[] = { }; MODULE_DEVICE_TABLE(usb, moschip_port_id_table); +#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT + +/* initial values for parport regs */ +#define DCR_INIT_VAL 0x0c /* SLCTIN, nINIT */ +#define ECR_INIT_VAL 0x00 /* SPP mode */ + +struct urbtracker { + struct mos7715_parport *mos_parport; + struct list_head urblist_entry; + struct kref ref_count; + struct urb *urb; +}; + +enum mos7715_pp_modes { + SPP = 0<<5, + PS2 = 1<<5, /* moschip calls this 'NIBBLE' mode */ + PPF = 2<<5, /* moschip calls this 'CB-FIFO mode */ +}; + +struct mos7715_parport { + struct parport *pp; /* back to containing struct */ + struct kref ref_count; /* to instance of this struct */ + struct list_head deferred_urbs; /* list deferred async urbs */ + struct list_head active_urbs; /* list async urbs in flight */ + spinlock_t listlock; /* protects list access */ + bool msg_pending; /* usb sync call pending */ + struct completion syncmsg_compl; /* usb sync call completed */ + struct tasklet_struct urb_tasklet; /* for sending deferred urbs */ + struct usb_serial *serial; /* back to containing struct */ + __u8 shadowECR; /* parallel port regs... */ + __u8 shadowDCR; + atomic_t shadowDSR; /* updated in int-in callback */ +}; + +/* lock guards against dereferencing NULL ptr in parport ops callbacks */ +static DEFINE_SPINLOCK(release_lock); + +#endif /* CONFIG_USB_SERIAL_MOS7715_PARPORT */ + +static const unsigned int dummy; /* for clarity in register access fns */ + +enum mos_regs { + THR, /* serial port regs */ + RHR, + IER, + FCR, + ISR, + LCR, + MCR, + LSR, + MSR, + SPR, + DLL, + DLM, + DPR, /* parallel port regs */ + DSR, + DCR, + ECR, + SP1_REG, /* device control regs */ + SP2_REG, /* serial port 2 (7720 only) */ + PP_REG, + SP_CONTROL_REG, +}; + +/* + * Return the correct value for the Windex field of the setup packet + * for a control endpoint message. See the 7715 datasheet. + */ +static inline __u16 get_reg_index(enum mos_regs reg) +{ + static const __u16 mos7715_index_lookup_table[] = { + 0x00, /* THR */ + 0x00, /* RHR */ + 0x01, /* IER */ + 0x02, /* FCR */ + 0x02, /* ISR */ + 0x03, /* LCR */ + 0x04, /* MCR */ + 0x05, /* LSR */ + 0x06, /* MSR */ + 0x07, /* SPR */ + 0x00, /* DLL */ + 0x01, /* DLM */ + 0x00, /* DPR */ + 0x01, /* DSR */ + 0x02, /* DCR */ + 0x0a, /* ECR */ + 0x01, /* SP1_REG */ + 0x02, /* SP2_REG (7720 only) */ + 0x04, /* PP_REG (7715 only) */ + 0x08, /* SP_CONTROL_REG */ + }; + return mos7715_index_lookup_table[reg]; +} + +/* + * Return the correct value for the upper byte of the Wvalue field of + * the setup packet for a control endpoint message. + */ +static inline __u16 get_reg_value(enum mos_regs reg, + unsigned int serial_portnum) +{ + if (reg >= SP1_REG) /* control reg */ + return 0x0000; + + else if (reg >= DPR) /* parallel port reg (7715 only) */ + return 0x0100; + + else /* serial port reg */ + return (serial_portnum + 2) << 8; +} + +/* + * Write data byte to the specified device register. The data is embedded in + * the value field of the setup packet. serial_portnum is ignored for registers + * not specific to a particular serial port. + */ +static int write_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, + enum mos_regs reg, __u8 data) +{ + struct usb_device *usbdev = serial->dev; + unsigned int pipe = usb_sndctrlpipe(usbdev, 0); + __u8 request = (__u8)0x0e; + __u8 requesttype = (__u8)0x40; + __u16 index = get_reg_index(reg); + __u16 value = get_reg_value(reg, serial_portnum) + data; + int status = usb_control_msg(usbdev, pipe, request, requesttype, value, + index, NULL, 0, MOS_WDR_TIMEOUT); + if (status < 0) + dev_err(&usbdev->dev, + "mos7720: usb_control_msg() failed: %d", status); + return status; +} + +/* + * Read data byte from the specified device register. The data returned by the + * device is embedded in the value field of the setup packet. serial_portnum is + * ignored for registers that are not specific to a particular serial port. + */ +static int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, + enum mos_regs reg, __u8 *data) +{ + struct usb_device *usbdev = serial->dev; + unsigned int pipe = usb_rcvctrlpipe(usbdev, 0); + __u8 request = (__u8)0x0d; + __u8 requesttype = (__u8)0xc0; + __u16 index = get_reg_index(reg); + __u16 value = get_reg_value(reg, serial_portnum); + int status = usb_control_msg(usbdev, pipe, request, requesttype, value, + index, data, 1, MOS_WDR_TIMEOUT); + if (status < 0) + dev_err(&usbdev->dev, + "mos7720: usb_control_msg() failed: %d", status); + return status; +} + +#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT + +static inline int mos7715_change_mode(struct mos7715_parport *mos_parport, + enum mos7715_pp_modes mode) +{ + mos_parport->shadowECR = mode; + write_mos_reg(mos_parport->serial, dummy, ECR, mos_parport->shadowECR); + return 0; +} + +static void destroy_mos_parport(struct kref *kref) +{ + struct mos7715_parport *mos_parport = + container_of(kref, struct mos7715_parport, ref_count); + + dbg("%s called", __func__); + kfree(mos_parport); +} + +static void destroy_urbtracker(struct kref *kref) +{ + struct urbtracker *urbtrack = + container_of(kref, struct urbtracker, ref_count); + struct mos7715_parport *mos_parport = urbtrack->mos_parport; + dbg("%s called", __func__); + usb_free_urb(urbtrack->urb); + kfree(urbtrack); + kref_put(&mos_parport->ref_count, destroy_mos_parport); +} + +/* + * This runs as a tasklet when sending an urb in a non-blocking parallel + * port callback had to be deferred because the disconnect mutex could not be + * obtained at the time. + */ +static void send_deferred_urbs(unsigned long _mos_parport) +{ + int ret_val; + unsigned long flags; + struct mos7715_parport *mos_parport = (void *)_mos_parport; + struct urbtracker *urbtrack; + struct list_head *cursor, *next; + + dbg("%s called", __func__); + + /* if release function ran, game over */ + if (unlikely(mos_parport->serial == NULL)) + return; + + /* try again to get the mutex */ + if (!mutex_trylock(&mos_parport->serial->disc_mutex)) { + dbg("%s: rescheduling tasklet", __func__); + tasklet_schedule(&mos_parport->urb_tasklet); + return; + } + + /* if device disconnected, game over */ + if (unlikely(mos_parport->serial->disconnected)) { + mutex_unlock(&mos_parport->serial->disc_mutex); + return; + } + + spin_lock_irqsave(&mos_parport->listlock, flags); + if (list_empty(&mos_parport->deferred_urbs)) { + spin_unlock_irqrestore(&mos_parport->listlock, flags); + mutex_unlock(&mos_parport->serial->disc_mutex); + dbg("%s: deferred_urbs list empty", __func__); + return; + } + + /* move contents of deferred_urbs list to active_urbs list and submit */ + list_for_each_safe(cursor, next, &mos_parport->deferred_urbs) + list_move_tail(cursor, &mos_parport->active_urbs); + list_for_each_entry(urbtrack, &mos_parport->active_urbs, + urblist_entry) { + ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC); + dbg("%s: urb submitted", __func__); + if (ret_val) { + dev_err(&mos_parport->serial->dev->dev, + "usb_submit_urb() failed: %d", ret_val); + list_del(&urbtrack->urblist_entry); + kref_put(&urbtrack->ref_count, destroy_urbtracker); + } + } + spin_unlock_irqrestore(&mos_parport->listlock, flags); + mutex_unlock(&mos_parport->serial->disc_mutex); +} + +/* callback for parallel port control urbs submitted asynchronously */ +static void async_complete(struct urb *urb) +{ + struct urbtracker *urbtrack = urb->context; + int status = urb->status; + dbg("%s called", __func__); + if (unlikely(status)) + dbg("%s - nonzero urb status received: %d", __func__, status); + + /* remove the urbtracker from the active_urbs list */ + spin_lock(&urbtrack->mos_parport->listlock); + list_del(&urbtrack->urblist_entry); + spin_unlock(&urbtrack->mos_parport->listlock); + kref_put(&urbtrack->ref_count, destroy_urbtracker); +} + +static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, + enum mos_regs reg, __u8 data) +{ + struct urbtracker *urbtrack; + int ret_val; + unsigned long flags; + struct usb_ctrlrequest setup; + struct usb_serial *serial = mos_parport->serial; + struct usb_device *usbdev = serial->dev; + dbg("%s called", __func__); + + /* create and initialize the control urb and containing urbtracker */ + urbtrack = kmalloc(sizeof(struct urbtracker), GFP_ATOMIC); + if (urbtrack == NULL) { + dev_err(&usbdev->dev, "out of memory"); + return -ENOMEM; + } + kref_get(&mos_parport->ref_count); + urbtrack->mos_parport = mos_parport; + urbtrack->urb = usb_alloc_urb(0, GFP_ATOMIC); + if (urbtrack->urb == NULL) { + dev_err(&usbdev->dev, "out of urbs"); + kfree(urbtrack); + return -ENOMEM; + } + setup.bRequestType = (__u8)0x40; + setup.bRequest = (__u8)0x0e; + setup.wValue = get_reg_value(reg, dummy); + setup.wIndex = get_reg_index(reg); + setup.wLength = 0; + usb_fill_control_urb(urbtrack->urb, usbdev, + usb_sndctrlpipe(usbdev, 0), + (unsigned char *)&setup, + NULL, 0, async_complete, urbtrack); + kref_init(&urbtrack->ref_count); + INIT_LIST_HEAD(&urbtrack->urblist_entry); + + /* + * get the disconnect mutex, or add tracker to the deferred_urbs list + * and schedule a tasklet to try again later + */ + if (!mutex_trylock(&serial->disc_mutex)) { + spin_lock_irqsave(&mos_parport->listlock, flags); + list_add_tail(&urbtrack->urblist_entry, + &mos_parport->deferred_urbs); + spin_unlock_irqrestore(&mos_parport->listlock, flags); + tasklet_schedule(&mos_parport->urb_tasklet); + dbg("tasklet scheduled"); + return 0; + } + + /* bail if device disconnected */ + if (serial->disconnected) { + kref_put(&urbtrack->ref_count, destroy_urbtracker); + mutex_unlock(&serial->disc_mutex); + return -ENODEV; + } + + /* add the tracker to the active_urbs list and submit */ + spin_lock_irqsave(&mos_parport->listlock, flags); + list_add_tail(&urbtrack->urblist_entry, &mos_parport->active_urbs); + spin_unlock_irqrestore(&mos_parport->listlock, flags); + ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC); + mutex_unlock(&serial->disc_mutex); + if (ret_val) { + dev_err(&usbdev->dev, + "%s: submit_urb() failed: %d", __func__, ret_val); + spin_lock_irqsave(&mos_parport->listlock, flags); + list_del(&urbtrack->urblist_entry); + spin_unlock_irqrestore(&mos_parport->listlock, flags); + kref_put(&urbtrack->ref_count, destroy_urbtracker); + return ret_val; + } + return 0; +} + +/* + * This is the the common top part of all parallel port callback operations that + * send synchronous messages to the device. This implements convoluted locking + * that avoids two scenarios: (1) a port operation is called after usbserial + * has called our release function, at which point struct mos7715_parport has + * been destroyed, and (2) the device has been disconnected, but usbserial has + * not called the release function yet because someone has a serial port open. + * The shared release_lock prevents the first, and the mutex and disconnected + * flag maintained by usbserial covers the second. We also use the msg_pending + * flag to ensure that all synchronous usb messgage calls have completed before + * our release function can return. + */ +static int parport_prologue(struct parport *pp) +{ + struct mos7715_parport *mos_parport; + + spin_lock(&release_lock); + mos_parport = pp->private_data; + if (unlikely(mos_parport == NULL)) { + /* release fn called, port struct destroyed */ + spin_unlock(&release_lock); + return -1; + } + mos_parport->msg_pending = true; /* synch usb call pending */ + INIT_COMPLETION(mos_parport->syncmsg_compl); + spin_unlock(&release_lock); + + mutex_lock(&mos_parport->serial->disc_mutex); + if (mos_parport->serial->disconnected) { + /* device disconnected */ + mutex_unlock(&mos_parport->serial->disc_mutex); + mos_parport->msg_pending = false; + complete(&mos_parport->syncmsg_compl); + return -1; + } + + return 0; +} + +/* + * This is the the common bottom part of all parallel port functions that send + * synchronous messages to the device. + */ +static inline void parport_epilogue(struct parport *pp) +{ + struct mos7715_parport *mos_parport = pp->private_data; + mutex_unlock(&mos_parport->serial->disc_mutex); + mos_parport->msg_pending = false; + complete(&mos_parport->syncmsg_compl); +} + +static void parport_mos7715_write_data(struct parport *pp, unsigned char d) +{ + struct mos7715_parport *mos_parport = pp->private_data; + dbg("%s called: %2.2x", __func__, d); + if (parport_prologue(pp) < 0) + return; + mos7715_change_mode(mos_parport, SPP); + write_mos_reg(mos_parport->serial, dummy, DPR, (__u8)d); + parport_epilogue(pp); +} + +static unsigned char parport_mos7715_read_data(struct parport *pp) +{ + struct mos7715_parport *mos_parport = pp->private_data; + unsigned char d; + dbg("%s called", __func__); + if (parport_prologue(pp) < 0) + return 0; + read_mos_reg(mos_parport->serial, dummy, DPR, &d); + parport_epilogue(pp); + return d; +} + +static void parport_mos7715_write_control(struct parport *pp, unsigned char d) +{ + struct mos7715_parport *mos_parport = pp->private_data; + __u8 data; + dbg("%s called: %2.2x", __func__, d); + if (parport_prologue(pp) < 0) + return; + data = ((__u8)d & 0x0f) | (mos_parport->shadowDCR & 0xf0); + write_mos_reg(mos_parport->serial, dummy, DCR, data); + mos_parport->shadowDCR = data; + parport_epilogue(pp); +} + +static unsigned char parport_mos7715_read_control(struct parport *pp) +{ + struct mos7715_parport *mos_parport = pp->private_data; + __u8 dcr; + dbg("%s called", __func__); + spin_lock(&release_lock); + mos_parport = pp->private_data; + if (unlikely(mos_parport == NULL)) { + spin_unlock(&release_lock); + return 0; + } + dcr = mos_parport->shadowDCR & 0x0f; + spin_unlock(&release_lock); + return dcr; +} + +static unsigned char parport_mos7715_frob_control(struct parport *pp, + unsigned char mask, + unsigned char val) +{ + struct mos7715_parport *mos_parport = pp->private_data; + __u8 dcr; + dbg("%s called", __func__); + mask &= 0x0f; + val &= 0x0f; + if (parport_prologue(pp) < 0) + return 0; + mos_parport->shadowDCR = (mos_parport->shadowDCR & (~mask)) ^ val; + write_mos_reg(mos_parport->serial, dummy, DCR, mos_parport->shadowDCR); + dcr = mos_parport->shadowDCR & 0x0f; + parport_epilogue(pp); + return dcr; +} + +static unsigned char parport_mos7715_read_status(struct parport *pp) +{ + unsigned char status; + struct mos7715_parport *mos_parport = pp->private_data; + dbg("%s called", __func__); + spin_lock(&release_lock); + mos_parport = pp->private_data; + if (unlikely(mos_parport == NULL)) { /* release called */ + spin_unlock(&release_lock); + return 0; + } + status = atomic_read(&mos_parport->shadowDSR) & 0xf8; + spin_unlock(&release_lock); + return status; +} + +static void parport_mos7715_enable_irq(struct parport *pp) +{ + dbg("%s called", __func__); +} +static void parport_mos7715_disable_irq(struct parport *pp) +{ + dbg("%s called", __func__); +} + +static void parport_mos7715_data_forward(struct parport *pp) +{ + struct mos7715_parport *mos_parport = pp->private_data; + dbg("%s called", __func__); + if (parport_prologue(pp) < 0) + return; + mos7715_change_mode(mos_parport, PS2); + mos_parport->shadowDCR &= ~0x20; + write_mos_reg(mos_parport->serial, dummy, DCR, mos_parport->shadowDCR); + parport_epilogue(pp); +} + +static void parport_mos7715_data_reverse(struct parport *pp) +{ + struct mos7715_parport *mos_parport = pp->private_data; + dbg("%s called", __func__); + if (parport_prologue(pp) < 0) + return; + mos7715_change_mode(mos_parport, PS2); + mos_parport->shadowDCR |= 0x20; + write_mos_reg(mos_parport->serial, dummy, DCR, mos_parport->shadowDCR); + parport_epilogue(pp); +} + +static void parport_mos7715_init_state(struct pardevice *dev, + struct parport_state *s) +{ + dbg("%s called", __func__); + s->u.pc.ctr = DCR_INIT_VAL; + s->u.pc.ecr = ECR_INIT_VAL; +} + +/* N.B. Parport core code requires that this function not block */ +static void parport_mos7715_save_state(struct parport *pp, + struct parport_state *s) +{ + struct mos7715_parport *mos_parport; + dbg("%s called", __func__); + spin_lock(&release_lock); + mos_parport = pp->private_data; + if (unlikely(mos_parport == NULL)) { /* release called */ + spin_unlock(&release_lock); + return; + } + s->u.pc.ctr = mos_parport->shadowDCR; + s->u.pc.ecr = mos_parport->shadowECR; + spin_unlock(&release_lock); +} + +/* N.B. Parport core code requires that this function not block */ +static void parport_mos7715_restore_state(struct parport *pp, + struct parport_state *s) +{ + struct mos7715_parport *mos_parport; + dbg("%s called", __func__); + spin_lock(&release_lock); + mos_parport = pp->private_data; + if (unlikely(mos_parport == NULL)) { /* release called */ + spin_unlock(&release_lock); + return; + } + write_parport_reg_nonblock(mos_parport, DCR, mos_parport->shadowDCR); + write_parport_reg_nonblock(mos_parport, ECR, mos_parport->shadowECR); + spin_unlock(&release_lock); +} + +static size_t parport_mos7715_write_compat(struct parport *pp, + const void *buffer, + size_t len, int flags) +{ + int retval; + struct mos7715_parport *mos_parport = pp->private_data; + int actual_len; + dbg("%s called: %u chars", __func__, (unsigned int)len); + if (parport_prologue(pp) < 0) + return 0; + mos7715_change_mode(mos_parport, PPF); + retval = usb_bulk_msg(mos_parport->serial->dev, + usb_sndbulkpipe(mos_parport->serial->dev, 2), + (void *)buffer, len, &actual_len, + MOS_WDR_TIMEOUT); + parport_epilogue(pp); + if (retval) { + dev_err(&mos_parport->serial->dev->dev, + "mos7720: usb_bulk_msg() failed: %d", retval); + return 0; + } + return actual_len; +} + +static struct parport_operations parport_mos7715_ops = { + .owner = THIS_MODULE, + .write_data = parport_mos7715_write_data, + .read_data = parport_mos7715_read_data, + + .write_control = parport_mos7715_write_control, + .read_control = parport_mos7715_read_control, + .frob_control = parport_mos7715_frob_control, + + .read_status = parport_mos7715_read_status, + + .enable_irq = parport_mos7715_enable_irq, + .disable_irq = parport_mos7715_disable_irq, + + .data_forward = parport_mos7715_data_forward, + .data_reverse = parport_mos7715_data_reverse, + + .init_state = parport_mos7715_init_state, + .save_state = parport_mos7715_save_state, + .restore_state = parport_mos7715_restore_state, + + .compat_write_data = parport_mos7715_write_compat, + + .nibble_read_data = parport_ieee1284_read_nibble, + .byte_read_data = parport_ieee1284_read_byte, +}; + +/* + * Allocate and initialize parallel port control struct, initialize + * the parallel port hardware device, and register with the parport subsystem. + */ +static int mos7715_parport_init(struct usb_serial *serial) +{ + struct mos7715_parport *mos_parport; + + /* allocate and initialize parallel port control struct */ + mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL); + if (mos_parport == NULL) { + dbg("mos7715_parport_init: kzalloc failed"); + return -ENOMEM; + } + mos_parport->msg_pending = false; + kref_init(&mos_parport->ref_count); + spin_lock_init(&mos_parport->listlock); + INIT_LIST_HEAD(&mos_parport->active_urbs); + INIT_LIST_HEAD(&mos_parport->deferred_urbs); + usb_set_serial_data(serial, mos_parport); /* hijack private pointer */ + mos_parport->serial = serial; + tasklet_init(&mos_parport->urb_tasklet, send_deferred_urbs, + (unsigned long) mos_parport); + init_completion(&mos_parport->syncmsg_compl); + + /* cycle parallel port reset bit */ + write_mos_reg(mos_parport->serial, dummy, PP_REG, (__u8)0x80); + write_mos_reg(mos_parport->serial, dummy, PP_REG, (__u8)0x00); + + /* initialize device registers */ + mos_parport->shadowDCR = DCR_INIT_VAL; + write_mos_reg(mos_parport->serial, dummy, DCR, mos_parport->shadowDCR); + mos_parport->shadowECR = ECR_INIT_VAL; + write_mos_reg(mos_parport->serial, dummy, ECR, mos_parport->shadowECR); + + /* register with parport core */ + mos_parport->pp = parport_register_port(0, PARPORT_IRQ_NONE, + PARPORT_DMA_NONE, + &parport_mos7715_ops); + if (mos_parport->pp == NULL) { + dev_err(&serial->interface->dev, + "Could not register parport\n"); + kref_put(&mos_parport->ref_count, destroy_mos_parport); + return -EIO; + } + mos_parport->pp->private_data = mos_parport; + mos_parport->pp->modes = PARPORT_MODE_COMPAT | PARPORT_MODE_PCSPP; + mos_parport->pp->dev = &serial->interface->dev; + parport_announce_port(mos_parport->pp); + + return 0; +} +#endif /* CONFIG_USB_SERIAL_MOS7715_PARPORT */ /* * mos7720_interrupt_callback @@ -109,8 +753,6 @@ static void mos7720_interrupt_callback(struct urb *urb) __u8 sp1; __u8 sp2; - dbg(" : Entering"); - switch (status) { case 0: /* success */ @@ -161,7 +803,7 @@ static void mos7720_interrupt_callback(struct urb *urb) dbg("Serial Port 1: Receiver time out"); break; case SERIAL_IIR_MS: - dbg("Serial Port 1: Modem status change"); + /* dbg("Serial Port 1: Modem status change"); */ break; } @@ -174,7 +816,7 @@ static void mos7720_interrupt_callback(struct urb *urb) dbg("Serial Port 2: Receiver time out"); break; case SERIAL_IIR_MS: - dbg("Serial Port 2: Modem status change"); + /* dbg("Serial Port 2: Modem status change"); */ break; } } @@ -208,6 +850,7 @@ static void mos7715_interrupt_callback(struct urb *urb) case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: + case -ENODEV: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", __func__, status); @@ -243,11 +886,21 @@ static void mos7715_interrupt_callback(struct urb *urb) dbg("Serial Port: Receiver time out"); break; case SERIAL_IIR_MS: - dbg("Serial Port: Modem status change"); + /* dbg("Serial Port: Modem status change"); */ break; } } +#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT + { /* update local copy of DSR reg */ + struct usb_serial_port *port = urb->context; + struct mos7715_parport *mos_parport = port->serial->private; + if (unlikely(mos_parport == NULL)) + return; + atomic_set(&mos_parport->shadowDSR, data[2]); + } +#endif + exit: result = usb_submit_urb(urb, GFP_ATOMIC); if (result) @@ -267,7 +920,6 @@ static void mos7720_bulk_in_callback(struct urb *urb) int retval; unsigned char *data ; struct usb_serial_port *port; - struct moschip_port *mos7720_port; struct tty_struct *tty; int status = urb->status; @@ -276,13 +928,7 @@ static void mos7720_bulk_in_callback(struct urb *urb) return; } - mos7720_port = urb->context; - if (!mos7720_port) { - dbg("NULL mos7720_port pointer"); - return ; - } - - port = mos7720_port->port; + port = urb->context; dbg("Entering...%s", __func__); @@ -332,8 +978,6 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) return ; } - dbg("Entering ........."); - tty = tty_port_tty_get(&mos7720_port->port->port); if (tty && mos7720_port->open) @@ -342,56 +986,6 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) } /* - * send_mos_cmd - * this function will be used for sending command to device - */ -static int send_mos_cmd(struct usb_serial *serial, __u8 request, __u16 value, - __u16 index, u8 *data) -{ - int status; - u8 *buf; - u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); - - if (value < MOS_MAX_PORT) { - if (product == MOSCHIP_DEVICE_ID_7715) - value = 0x0200; /* identifies the 7715's serial port */ - else - value = value*0x100+0x200; - } else { - value = 0x0000; - if ((product == MOSCHIP_DEVICE_ID_7715) && - (index != 0x08)) { - dbg("serial->product== MOSCHIP_DEVICE_ID_7715"); - /* index = 0x01 ; */ - } - } - - if (request == MOS_WRITE) { - value = value + *data; - status = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), MOS_WRITE, - 0x40, value, index, NULL, 0, MOS_WDR_TIMEOUT); - } else { - buf = kmalloc(1, GFP_KERNEL); - if (!buf) { - status = -ENOMEM; - goto out; - } - status = usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), MOS_READ, - 0xc0, value, index, buf, 1, MOS_WDR_TIMEOUT); - *data = *buf; - kfree(buf); - } -out: - if (status < 0) - dbg("Command Write failed Value %x index %x", value, index); - - return status; -} - - -/* * mos77xx_probe * this function installs the appropriate read interrupt endpoint callback * depending on whether the device is a 7720 or 7715, thus avoiding costly @@ -424,11 +1018,10 @@ static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port) struct usb_serial *serial; struct usb_serial_port *port0; struct urb *urb; - struct moschip_serial *mos7720_serial; struct moschip_port *mos7720_port; int response; int port_number; - char data; + __u8 data; int allocated_urbs = 0; int j; @@ -440,11 +1033,6 @@ static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port) port0 = serial->port[0]; - mos7720_serial = usb_get_serial_data(serial); - - if (mos7720_serial == NULL || port0 == NULL) - return -ENODEV; - usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); @@ -489,103 +1077,36 @@ static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port) * 0x08 : SP1/2 Control Reg */ port_number = port->number - port->serial->minor; - send_mos_cmd(port->serial, MOS_READ, port_number, UART_LSR, &data); + read_mos_reg(serial, port_number, LSR, &data); + dbg("SS::%p LSR:%x", mos7720_port, data); dbg("Check:Sending Command .........."); - data = 0x02; - send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x01, &data); - data = 0x02; - send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x02, &data); + write_mos_reg(serial, dummy, SP1_REG, 0x02); + write_mos_reg(serial, dummy, SP2_REG, 0x02); - data = 0x00; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); - data = 0x00; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x02, &data); + write_mos_reg(serial, port_number, IER, 0x00); + write_mos_reg(serial, port_number, FCR, 0x00); - data = 0xCF; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x02, &data); - data = 0x03; - mos7720_port->shadowLCR = data; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); - data = 0x0b; - mos7720_port->shadowMCR = data; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); - data = 0x0b; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); - - data = 0x00; - send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data); - data = 0x00; - send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data); - -/* data = 0x00; - send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, port_number + 1, &data); - data = 0x03; - send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1, &data); - data = 0x00; - send_mos_cmd(port->serial, MOS_WRITE, MOS_MAX_PORT, - port_number + 1, &data); -*/ - data = 0x00; - send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data); + write_mos_reg(serial, port_number, FCR, 0xcf); + mos7720_port->shadowLCR = 0x03; + write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR); + mos7720_port->shadowMCR = 0x0b; + write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR); + write_mos_reg(serial, port_number, SP_CONTROL_REG, 0x00); + read_mos_reg(serial, dummy, SP_CONTROL_REG, &data); data = data | (port->number - port->serial->minor + 1); - send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data); + write_mos_reg(serial, dummy, SP_CONTROL_REG, data); + mos7720_port->shadowLCR = 0x83; + write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR); + write_mos_reg(serial, port_number, THR, 0x0c); + write_mos_reg(serial, port_number, IER, 0x00); + mos7720_port->shadowLCR = 0x03; + write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR); + write_mos_reg(serial, port_number, IER, 0x0c); - data = 0x83; - mos7720_port->shadowLCR = data; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); - data = 0x0c; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x00, &data); - data = 0x00; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); - data = 0x03; - mos7720_port->shadowLCR = data; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); - data = 0x0c; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); - data = 0x0c; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); - - /* see if we've set up our endpoint info yet * - * (can't set it up in mos7720_startup as the * - * structures were not set up at that time.) */ - if (!mos7720_serial->interrupt_started) { - dbg("Interrupt buffer NULL !!!"); - - /* not set up yet, so do it now */ - mos7720_serial->interrupt_started = 1; - - dbg("To Submit URB !!!"); - - /* set up our interrupt urb */ - usb_fill_int_urb(port0->interrupt_in_urb, serial->dev, - usb_rcvintpipe(serial->dev, - port->interrupt_in_endpointAddress), - port0->interrupt_in_buffer, - port0->interrupt_in_urb->transfer_buffer_length, - mos7720_interrupt_callback, mos7720_port, - port0->interrupt_in_urb->interval); - - /* start interrupt read for this mos7720 this interrupt * - * will continue as long as the mos7720 is connected */ - dbg("Submit URB over !!!"); - response = usb_submit_urb(port0->interrupt_in_urb, GFP_KERNEL); - if (response) - dev_err(&port->dev, - "%s - Error %d submitting control urb\n", - __func__, response); - } - - /* set up our bulk in urb */ - usb_fill_bulk_urb(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - port->bulk_in_buffer, - port->read_urb->transfer_buffer_length, - mos7720_bulk_in_callback, mos7720_port); response = usb_submit_urb(port->read_urb, GFP_KERNEL); if (response) dev_err(&port->dev, "%s - Error %d submitting read urb\n", @@ -640,7 +1161,6 @@ static void mos7720_close(struct usb_serial_port *port) { struct usb_serial *serial; struct moschip_port *mos7720_port; - char data; int j; dbg("mos7720_close:entering..."); @@ -673,13 +1193,10 @@ static void mos7720_close(struct usb_serial_port *port) /* these commands must not be issued if the device has * been disconnected */ if (!serial->disconnected) { - data = 0x00; - send_mos_cmd(serial, MOS_WRITE, - port->number - port->serial->minor, 0x04, &data); - - data = 0x00; - send_mos_cmd(serial, MOS_WRITE, - port->number - port->serial->minor, 0x01, &data); + write_mos_reg(serial, port->number - port->serial->minor, + MCR, 0x00); + write_mos_reg(serial, port->number - port->serial->minor, + IER, 0x00); } mutex_unlock(&serial->disc_mutex); mos7720_port->open = 0; @@ -708,8 +1225,8 @@ static void mos7720_break(struct tty_struct *tty, int break_state) data = mos7720_port->shadowLCR & ~UART_LCR_SBC; mos7720_port->shadowLCR = data; - send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor, - 0x03, &data); + write_mos_reg(serial, port->number - port->serial->minor, + LCR, mos7720_port->shadowLCR); return; } @@ -854,9 +1371,8 @@ static void mos7720_throttle(struct tty_struct *tty) /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { mos7720_port->shadowMCR &= ~UART_MCR_RTS; - status = send_mos_cmd(port->serial, MOS_WRITE, - port->number - port->serial->minor, - UART_MCR, &mos7720_port->shadowMCR); + write_mos_reg(port->serial, port->number - port->serial->minor, + MCR, mos7720_port->shadowMCR); if (status != 0) return; } @@ -889,22 +1405,21 @@ static void mos7720_unthrottle(struct tty_struct *tty) /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { mos7720_port->shadowMCR |= UART_MCR_RTS; - status = send_mos_cmd(port->serial, MOS_WRITE, - port->number - port->serial->minor, - UART_MCR, &mos7720_port->shadowMCR); + write_mos_reg(port->serial, port->number - port->serial->minor, + MCR, mos7720_port->shadowMCR); if (status != 0) return; } } +/* FIXME: this function does not work */ static int set_higher_rates(struct moschip_port *mos7720_port, unsigned int baud) { - unsigned char data; struct usb_serial_port *port; struct usb_serial *serial; int port_number; - + enum mos_regs sp_reg; if (mos7720_port == NULL) return -EINVAL; @@ -917,58 +1432,35 @@ static int set_higher_rates(struct moschip_port *mos7720_port, dbg("Sending Setting Commands .........."); port_number = port->number - port->serial->minor; - data = 0x000; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); - data = 0x000; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x02, &data); - data = 0x0CF; - send_mos_cmd(serial, MOS_WRITE, port->number, 0x02, &data); - data = 0x00b; - mos7720_port->shadowMCR = data; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); - data = 0x00b; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); - - data = 0x000; - send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data); - data = 0x000; - send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data); - + write_mos_reg(serial, port_number, IER, 0x00); + write_mos_reg(serial, port_number, FCR, 0x00); + write_mos_reg(serial, port_number, FCR, 0xcf); + mos7720_port->shadowMCR = 0x0b; + write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR); + write_mos_reg(serial, dummy, SP_CONTROL_REG, 0x00); /*********************************************** * Set for higher rates * ***********************************************/ - - data = baud * 0x10; - send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1, &data); - - data = 0x003; - send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data); - data = 0x003; - send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data); - - data = 0x02b; - mos7720_port->shadowMCR = data; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); - data = 0x02b; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); + /* writing baud rate verbatum into uart clock field clearly not right */ + if (port_number == 0) + sp_reg = SP1_REG; + else + sp_reg = SP2_REG; + write_mos_reg(serial, dummy, sp_reg, baud * 0x10); + write_mos_reg(serial, dummy, SP_CONTROL_REG, 0x03); + mos7720_port->shadowMCR = 0x2b; + write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR); /*********************************************** * Set DLL/DLM ***********************************************/ - - data = mos7720_port->shadowLCR | UART_LCR_DLAB; - mos7720_port->shadowLCR = data; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); - - data = 0x001; /* DLL */ - send_mos_cmd(serial, MOS_WRITE, port_number, 0x00, &data); - data = 0x000; /* DLM */ - send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); - - data = mos7720_port->shadowLCR & ~UART_LCR_DLAB; - mos7720_port->shadowLCR = data; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); + mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB; + write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR); + write_mos_reg(serial, port_number, DLL, 0x01); + write_mos_reg(serial, port_number, DLM, 0x00); + mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB; + write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR); return 0; } @@ -1056,7 +1548,6 @@ static int send_cmd_write_baud_rate(struct moschip_port *mos7720_port, struct usb_serial *serial; int divisor; int status; - unsigned char data; unsigned char number; if (mos7720_port == NULL) @@ -1078,21 +1569,16 @@ static int send_cmd_write_baud_rate(struct moschip_port *mos7720_port, } /* Enable access to divisor latch */ - data = mos7720_port->shadowLCR | UART_LCR_DLAB; - mos7720_port->shadowLCR = data; - send_mos_cmd(serial, MOS_WRITE, number, UART_LCR, &data); + mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB; + write_mos_reg(serial, number, LCR, mos7720_port->shadowLCR); /* Write the divisor */ - data = ((unsigned char)(divisor & 0xff)); - send_mos_cmd(serial, MOS_WRITE, number, 0x00, &data); - - data = ((unsigned char)((divisor & 0xff00) >> 8)); - send_mos_cmd(serial, MOS_WRITE, number, 0x01, &data); + write_mos_reg(serial, number, DLL, (__u8)(divisor & 0xff)); + write_mos_reg(serial, number, DLM, (__u8)((divisor & 0xff00) >> 8)); /* Disable access to divisor latch */ - data = mos7720_port->shadowLCR & ~UART_LCR_DLAB; - mos7720_port->shadowLCR = data; - send_mos_cmd(serial, MOS_WRITE, number, 0x03, &data); + mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB; + write_mos_reg(serial, number, LCR, mos7720_port->shadowLCR); return status; } @@ -1117,7 +1603,6 @@ static void change_port_settings(struct tty_struct *tty, __u8 lStop; int status; int port_number; - char data; if (mos7720_port == NULL) return ; @@ -1196,30 +1681,19 @@ static void change_port_settings(struct tty_struct *tty, /* Update the LCR with the correct value */ mos7720_port->shadowLCR &= - ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); + ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); mos7720_port->shadowLCR |= (lData | lParity | lStop); /* Disable Interrupts */ - data = 0x00; - send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor, - UART_IER, &data); - - data = 0x00; - send_mos_cmd(serial, MOS_WRITE, port_number, UART_FCR, &data); - - data = 0xcf; - send_mos_cmd(serial, MOS_WRITE, port_number, UART_FCR, &data); + write_mos_reg(serial, port_number, IER, 0x00); + write_mos_reg(serial, port_number, FCR, 0x00); + write_mos_reg(serial, port_number, FCR, 0xcf); /* Send the updated LCR value to the mos7720 */ - data = mos7720_port->shadowLCR; - send_mos_cmd(serial, MOS_WRITE, port_number, UART_LCR, &data); - - data = 0x00b; - mos7720_port->shadowMCR = data; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); - data = 0x00b; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); + write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR); + mos7720_port->shadowMCR = 0x0b; + write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR); /* set up the MCR register and send it to the mos7720 */ mos7720_port->shadowMCR = UART_MCR_OUT2; @@ -1230,21 +1704,15 @@ static void change_port_settings(struct tty_struct *tty, mos7720_port->shadowMCR |= (UART_MCR_XONANY); /* To set hardware flow control to the specified * * serial port, in SP1/2_CONTROL_REG */ - if (port->number) { - data = 0x001; - send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, - 0x08, &data); - } else { - data = 0x002; - send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, - 0x08, &data); - } - } else { + if (port->number) + write_mos_reg(serial, dummy, SP_CONTROL_REG, 0x01); + else + write_mos_reg(serial, dummy, SP_CONTROL_REG, 0x02); + + } else mos7720_port->shadowMCR &= ~(UART_MCR_XONANY); - } - data = mos7720_port->shadowMCR; - send_mos_cmd(serial, MOS_WRITE, port_number, UART_MCR, &data); + write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR); /* Determine divisor based on baud rate */ baud = tty_get_baud_rate(tty); @@ -1257,8 +1725,7 @@ static void change_port_settings(struct tty_struct *tty, if (baud >= 230400) { set_higher_rates(mos7720_port, baud); /* Enable Interrupts */ - data = 0x0c; - send_mos_cmd(serial, MOS_WRITE, port_number, UART_IER, &data); + write_mos_reg(serial, port_number, IER, 0x0c); return; } @@ -1269,8 +1736,7 @@ static void change_port_settings(struct tty_struct *tty, if (cflag & CBAUD) tty_encode_baud_rate(tty, baud, baud); /* Enable Interrupts */ - data = 0x0c; - send_mos_cmd(serial, MOS_WRITE, port_number, UART_IER, &data); + write_mos_reg(serial, port_number, IER, 0x0c); if (port->read_urb->status != -EINPROGRESS) { port->read_urb->dev = serial->dev; @@ -1308,7 +1774,7 @@ static void mos7720_set_termios(struct tty_struct *tty, return; } - dbg("setting termios - ASPIRE"); + dbg("%s\n", "setting termios - ASPIRE"); cflag = tty->termios->c_cflag; @@ -1326,7 +1792,7 @@ static void mos7720_set_termios(struct tty_struct *tty, change_port_settings(tty, mos7720_port, old_termios); if (!port->read_urb) { - dbg("URB KILLED !!!!!"); + dbg("%s", "URB KILLED !!!!!"); return; } @@ -1361,8 +1827,7 @@ static int get_lsr_info(struct tty_struct *tty, count = mos7720_chars_in_buffer(tty); if (count == 0) { - send_mos_cmd(port->serial, MOS_READ, port_number, - UART_LSR, &data); + read_mos_reg(port->serial, port_number, LSR, &data); if ((data & (UART_LSR_TEMT | UART_LSR_THRE)) == (UART_LSR_TEMT | UART_LSR_THRE)) { dbg("%s -- Empty", __func__); @@ -1400,13 +1865,11 @@ static int mos7720_tiocmget(struct tty_struct *tty, struct file *file) } static int mos7720_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) + unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port = usb_get_serial_port_data(port); unsigned int mcr ; - unsigned char lmcr; - dbg("%s - port %d", __func__, port->number); dbg("he was at tiocmget"); @@ -1427,10 +1890,8 @@ static int mos7720_tiocmset(struct tty_struct *tty, struct file *file, mcr &= ~UART_MCR_LOOP; mos7720_port->shadowMCR = mcr; - lmcr = mos7720_port->shadowMCR; - - send_mos_cmd(port->serial, MOS_WRITE, - port->number - port->serial->minor, UART_MCR, &lmcr); + write_mos_reg(port->serial, port->number - port->serial->minor, + MCR, mos7720_port->shadowMCR); return 0; } @@ -1440,7 +1901,6 @@ static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd, { unsigned int mcr ; unsigned int arg; - unsigned char data; struct usb_serial_port *port; @@ -1475,10 +1935,8 @@ static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd, } mos7720_port->shadowMCR = mcr; - - data = mos7720_port->shadowMCR; - send_mos_cmd(port->serial, MOS_WRITE, - port->number - port->serial->minor, UART_MCR, &data); + write_mos_reg(port->serial, port->number - port->serial->minor, + MCR, mos7720_port->shadowMCR); return 0; } @@ -1590,12 +2048,12 @@ static int mos7720_ioctl(struct tty_struct *tty, struct file *file, static int mos7720_startup(struct usb_serial *serial) { - struct moschip_serial *mos7720_serial; struct moschip_port *mos7720_port; struct usb_device *dev; int i; char data; u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); + int ret_val; dbg("%s: Entering ..........", __func__); @@ -1606,15 +2064,6 @@ static int mos7720_startup(struct usb_serial *serial) dev = serial->dev; - /* create our private serial structure */ - mos7720_serial = kzalloc(sizeof(struct moschip_serial), GFP_KERNEL); - if (mos7720_serial == NULL) { - dev_err(&dev->dev, "%s - Out of memory\n", __func__); - return -ENOMEM; - } - - usb_set_serial_data(serial, mos7720_serial); - /* * The 7715 uses the first bulk in/out endpoint pair for the parallel * port, and the second for the serial port. Because the usbserial core @@ -1638,16 +2087,12 @@ static int mos7720_startup(struct usb_serial *serial) serial->port[1]->interrupt_in_buffer = NULL; } - /* we set up the pointers to the endpoints in the mos7720_open * - * function, as the structures aren't created yet. */ - /* set up port private structures */ + /* set up serial port private structures */ for (i = 0; i < serial->num_ports; ++i) { mos7720_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL); if (mos7720_port == NULL) { dev_err(&dev->dev, "%s - Out of memory\n", __func__); - usb_set_serial_data(serial, NULL); - kfree(mos7720_serial); return -ENOMEM; } @@ -1669,12 +2114,22 @@ static int mos7720_startup(struct usb_serial *serial) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), (__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5*HZ); - /* LSR For Port 1 */ - send_mos_cmd(serial, MOS_READ, 0x00, UART_LSR, &data); - dbg("LSR:%x", data); + /* start the interrupt urb */ + ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); + if (ret_val) + dev_err(&dev->dev, + "%s - Error %d submitting control urb\n", + __func__, ret_val); - /* LSR For Port 2 */ - send_mos_cmd(serial, MOS_READ, 0x01, UART_LSR, &data); +#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT + if (product == MOSCHIP_DEVICE_ID_7715) { + ret_val = mos7715_parport_init(serial); + if (ret_val < 0) + return ret_val; + } +#endif + /* LSR For Port 1 */ + read_mos_reg(serial, 0, LSR, &data); dbg("LSR:%x", data); return 0; @@ -1684,12 +2139,47 @@ static void mos7720_release(struct usb_serial *serial) { int i; +#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT + /* close the parallel port */ + + if (le16_to_cpu(serial->dev->descriptor.idProduct) + == MOSCHIP_DEVICE_ID_7715) { + struct urbtracker *urbtrack; + unsigned long flags; + struct mos7715_parport *mos_parport = + usb_get_serial_data(serial); + + /* prevent NULL ptr dereference in port callbacks */ + spin_lock(&release_lock); + mos_parport->pp->private_data = NULL; + spin_unlock(&release_lock); + + /* wait for synchronous usb calls to return */ + if (mos_parport->msg_pending) + wait_for_completion_timeout(&mos_parport->syncmsg_compl, + MOS_WDR_TIMEOUT); + + parport_remove_port(mos_parport->pp); + usb_set_serial_data(serial, NULL); + mos_parport->serial = NULL; + + /* if tasklet currently scheduled, wait for it to complete */ + tasklet_kill(&mos_parport->urb_tasklet); + + /* unlink any urbs sent by the tasklet */ + spin_lock_irqsave(&mos_parport->listlock, flags); + list_for_each_entry(urbtrack, + &mos_parport->active_urbs, + urblist_entry) + usb_unlink_urb(urbtrack->urb); + spin_unlock_irqrestore(&mos_parport->listlock, flags); + + kref_put(&mos_parport->ref_count, destroy_mos_parport); + } +#endif /* free private structure allocated for serial port */ for (i = 0; i < serial->num_ports; ++i) kfree(usb_get_serial_port_data(serial->port[i])); - - /* free private structure allocated for serial device */ - kfree(usb_get_serial_data(serial)); } static struct usb_driver usb_driver = { diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 2fda1c0..f8424d1 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -26,7 +26,6 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 84d0eda..e280ad8 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -42,35 +42,14 @@ #include <linux/bitops.h> #include <linux/usb.h> #include <linux/usb/serial.h> +#include "usb-wwan.h" /* Function prototypes */ static int option_probe(struct usb_serial *serial, const struct usb_device_id *id); -static int option_open(struct tty_struct *tty, struct usb_serial_port *port); -static void option_close(struct usb_serial_port *port); -static void option_dtr_rts(struct usb_serial_port *port, int on); - -static int option_startup(struct usb_serial *serial); -static void option_disconnect(struct usb_serial *serial); -static void option_release(struct usb_serial *serial); -static int option_write_room(struct tty_struct *tty); - +static int option_send_setup(struct usb_serial_port *port); static void option_instat_callback(struct urb *urb); -static int option_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count); -static int option_chars_in_buffer(struct tty_struct *tty); -static void option_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, struct ktermios *old); -static int option_tiocmget(struct tty_struct *tty, struct file *file); -static int option_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear); -static int option_send_setup(struct usb_serial_port *port); -#ifdef CONFIG_PM -static int option_suspend(struct usb_serial *serial, pm_message_t message); -static int option_resume(struct usb_serial *serial); -#endif - /* Vendor and product IDs */ #define OPTION_VENDOR_ID 0x0AF0 #define OPTION_PRODUCT_COLT 0x5000 @@ -380,6 +359,10 @@ static int option_resume(struct usb_serial *serial); #define CINTERION_VENDOR_ID 0x0681 +/* Olivetti products */ +#define OLIVETTI_VENDOR_ID 0x0b3c +#define OLIVETTI_PRODUCT_OLICARD100 0xc000 + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -675,6 +658,180 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0160, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1059, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1060, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1061, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1062, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1063, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1064, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1065, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1066, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1067, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1068, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1069, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1070, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1071, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1072, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1073, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1074, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1075, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1076, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1077, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1078, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1079, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1080, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1081, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1082, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1083, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1084, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1085, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1086, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1087, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1088, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1089, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1090, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1091, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1092, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1093, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1094, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1095, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1096, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1097, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1098, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1099, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1100, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1101, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1102, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1103, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1104, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1105, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1106, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1107, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1108, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1109, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1110, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1111, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1112, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1113, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1114, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1115, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1116, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1117, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1118, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1119, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1120, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1121, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1122, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1123, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1124, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1125, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1126, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1127, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1128, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1129, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1130, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1131, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1132, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1133, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1134, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1135, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1136, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1137, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1138, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1139, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1140, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1141, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1142, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1143, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1144, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1145, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1146, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1147, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1148, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1149, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1150, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1151, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1152, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1153, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1154, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1155, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1156, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1157, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1158, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1159, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1160, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1161, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1162, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1163, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1164, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1165, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1166, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1167, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1168, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1169, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1170, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1244, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1245, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1246, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1247, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1248, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1249, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1250, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1251, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1252, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1253, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1254, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1255, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1256, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1257, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1258, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1259, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1260, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1261, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1262, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1263, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1264, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1265, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1266, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1267, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1268, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1269, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1270, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1271, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1272, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1273, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1274, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1275, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1276, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1277, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1278, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1279, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1280, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1281, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1282, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1283, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1284, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1285, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1286, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1287, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1288, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1289, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1290, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1291, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1292, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1293, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1294, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1295, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1296, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1297, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1298, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1299, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1300, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0027, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, @@ -726,6 +883,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1012)}, { USB_DEVICE(CINTERION_VENDOR_ID, 0x0047) }, + + { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -757,22 +916,22 @@ static struct usb_serial_driver option_1port_device = { .id_table = option_ids, .num_ports = 1, .probe = option_probe, - .open = option_open, - .close = option_close, - .dtr_rts = option_dtr_rts, - .write = option_write, - .write_room = option_write_room, - .chars_in_buffer = option_chars_in_buffer, - .set_termios = option_set_termios, - .tiocmget = option_tiocmget, - .tiocmset = option_tiocmset, - .attach = option_startup, - .disconnect = option_disconnect, - .release = option_release, + .open = usb_wwan_open, + .close = usb_wwan_close, + .dtr_rts = usb_wwan_dtr_rts, + .write = usb_wwan_write, + .write_room = usb_wwan_write_room, + .chars_in_buffer = usb_wwan_chars_in_buffer, + .set_termios = usb_wwan_set_termios, + .tiocmget = usb_wwan_tiocmget, + .tiocmset = usb_wwan_tiocmset, + .attach = usb_wwan_startup, + .disconnect = usb_wwan_disconnect, + .release = usb_wwan_release, .read_int_callback = option_instat_callback, #ifdef CONFIG_PM - .suspend = option_suspend, - .resume = option_resume, + .suspend = usb_wwan_suspend, + .resume = usb_wwan_resume, #endif }; @@ -785,13 +944,6 @@ static int debug; #define IN_BUFLEN 4096 #define OUT_BUFLEN 4096 -struct option_intf_private { - spinlock_t susp_lock; - unsigned int suspended:1; - int in_flight; - struct option_blacklist_info *blacklist_info; -}; - struct option_port_private { /* Input endpoints and buffer for this port */ struct urb *in_urbs[N_IN_URB]; @@ -848,8 +1000,7 @@ module_exit(option_exit); static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { - struct option_intf_private *data; - + struct usb_wwan_intf_private *data; /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */ if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID && serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 && @@ -862,11 +1013,13 @@ static int option_probe(struct usb_serial *serial, serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff) return -ENODEV; - data = serial->private = kzalloc(sizeof(struct option_intf_private), GFP_KERNEL); + data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); + if (!data) return -ENOMEM; + data->send_setup = option_send_setup; spin_lock_init(&data->susp_lock); - data->blacklist_info = (struct option_blacklist_info*) id->driver_info; + data->private = (void *)id->driver_info; return 0; } @@ -887,194 +1040,6 @@ static enum option_blacklist_reason is_blacklisted(const u8 ifnum, return OPTION_BLACKLIST_NONE; } -static void option_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, struct ktermios *old_termios) -{ - dbg("%s", __func__); - /* Doesn't support option setting */ - tty_termios_copy_hw(tty->termios, old_termios); - option_send_setup(port); -} - -static int option_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct usb_serial_port *port = tty->driver_data; - unsigned int value; - struct option_port_private *portdata; - - portdata = usb_get_serial_port_data(port); - - value = ((portdata->rts_state) ? TIOCM_RTS : 0) | - ((portdata->dtr_state) ? TIOCM_DTR : 0) | - ((portdata->cts_state) ? TIOCM_CTS : 0) | - ((portdata->dsr_state) ? TIOCM_DSR : 0) | - ((portdata->dcd_state) ? TIOCM_CAR : 0) | - ((portdata->ri_state) ? TIOCM_RNG : 0); - - return value; -} - -static int option_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct usb_serial_port *port = tty->driver_data; - struct option_port_private *portdata; - - portdata = usb_get_serial_port_data(port); - - /* FIXME: what locks portdata fields ? */ - if (set & TIOCM_RTS) - portdata->rts_state = 1; - if (set & TIOCM_DTR) - portdata->dtr_state = 1; - - if (clear & TIOCM_RTS) - portdata->rts_state = 0; - if (clear & TIOCM_DTR) - portdata->dtr_state = 0; - return option_send_setup(port); -} - -/* Write */ -static int option_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - struct option_port_private *portdata; - struct option_intf_private *intfdata; - int i; - int left, todo; - struct urb *this_urb = NULL; /* spurious */ - int err; - unsigned long flags; - - portdata = usb_get_serial_port_data(port); - intfdata = port->serial->private; - - dbg("%s: write (%d chars)", __func__, count); - - i = 0; - left = count; - for (i = 0; left > 0 && i < N_OUT_URB; i++) { - todo = left; - if (todo > OUT_BUFLEN) - todo = OUT_BUFLEN; - - this_urb = portdata->out_urbs[i]; - if (test_and_set_bit(i, &portdata->out_busy)) { - if (time_before(jiffies, - portdata->tx_start_time[i] + 10 * HZ)) - continue; - usb_unlink_urb(this_urb); - continue; - } - dbg("%s: endpoint %d buf %d", __func__, - usb_pipeendpoint(this_urb->pipe), i); - - err = usb_autopm_get_interface_async(port->serial->interface); - if (err < 0) - break; - - /* send the data */ - memcpy(this_urb->transfer_buffer, buf, todo); - this_urb->transfer_buffer_length = todo; - - spin_lock_irqsave(&intfdata->susp_lock, flags); - if (intfdata->suspended) { - usb_anchor_urb(this_urb, &portdata->delayed); - spin_unlock_irqrestore(&intfdata->susp_lock, flags); - } else { - intfdata->in_flight++; - spin_unlock_irqrestore(&intfdata->susp_lock, flags); - err = usb_submit_urb(this_urb, GFP_ATOMIC); - if (err) { - dbg("usb_submit_urb %p (write bulk) failed " - "(%d)", this_urb, err); - clear_bit(i, &portdata->out_busy); - spin_lock_irqsave(&intfdata->susp_lock, flags); - intfdata->in_flight--; - spin_unlock_irqrestore(&intfdata->susp_lock, flags); - continue; - } - } - - portdata->tx_start_time[i] = jiffies; - buf += todo; - left -= todo; - } - - count -= left; - dbg("%s: wrote (did %d)", __func__, count); - return count; -} - -static void option_indat_callback(struct urb *urb) -{ - int err; - int endpoint; - struct usb_serial_port *port; - struct tty_struct *tty; - unsigned char *data = urb->transfer_buffer; - int status = urb->status; - - dbg("%s: %p", __func__, urb); - - endpoint = usb_pipeendpoint(urb->pipe); - port = urb->context; - - if (status) { - dbg("%s: nonzero status: %d on endpoint %02x.", - __func__, status, endpoint); - } else { - tty = tty_port_tty_get(&port->port); - if (urb->actual_length) { - tty_insert_flip_string(tty, data, urb->actual_length); - tty_flip_buffer_push(tty); - } else - dbg("%s: empty read urb received", __func__); - tty_kref_put(tty); - - /* Resubmit urb so we continue receiving */ - if (status != -ESHUTDOWN) { - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err && err != -EPERM) - printk(KERN_ERR "%s: resubmit read urb failed. " - "(%d)", __func__, err); - else - usb_mark_last_busy(port->serial->dev); - } - - } - return; -} - -static void option_outdat_callback(struct urb *urb) -{ - struct usb_serial_port *port; - struct option_port_private *portdata; - struct option_intf_private *intfdata; - int i; - - dbg("%s", __func__); - - port = urb->context; - intfdata = port->serial->private; - - usb_serial_port_softint(port); - usb_autopm_put_interface_async(port->serial->interface); - portdata = usb_get_serial_port_data(port); - spin_lock(&intfdata->susp_lock); - intfdata->in_flight--; - spin_unlock(&intfdata->susp_lock); - - for (i = 0; i < N_OUT_URB; ++i) { - if (portdata->out_urbs[i] == urb) { - smp_mb__before_clear_bit(); - clear_bit(i, &portdata->out_busy); - break; - } - } -} - static void option_instat_callback(struct urb *urb) { int err; @@ -1131,183 +1096,6 @@ static void option_instat_callback(struct urb *urb) } } -static int option_write_room(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct option_port_private *portdata; - int i; - int data_len = 0; - struct urb *this_urb; - - portdata = usb_get_serial_port_data(port); - - for (i = 0; i < N_OUT_URB; i++) { - this_urb = portdata->out_urbs[i]; - if (this_urb && !test_bit(i, &portdata->out_busy)) - data_len += OUT_BUFLEN; - } - - dbg("%s: %d", __func__, data_len); - return data_len; -} - -static int option_chars_in_buffer(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct option_port_private *portdata; - int i; - int data_len = 0; - struct urb *this_urb; - - portdata = usb_get_serial_port_data(port); - - for (i = 0; i < N_OUT_URB; i++) { - this_urb = portdata->out_urbs[i]; - /* FIXME: This locking is insufficient as this_urb may - go unused during the test */ - if (this_urb && test_bit(i, &portdata->out_busy)) - data_len += this_urb->transfer_buffer_length; - } - dbg("%s: %d", __func__, data_len); - return data_len; -} - -static int option_open(struct tty_struct *tty, struct usb_serial_port *port) -{ - struct option_port_private *portdata; - struct option_intf_private *intfdata; - struct usb_serial *serial = port->serial; - int i, err; - struct urb *urb; - - portdata = usb_get_serial_port_data(port); - intfdata = serial->private; - - dbg("%s", __func__); - - /* Start reading from the IN endpoint */ - for (i = 0; i < N_IN_URB; i++) { - urb = portdata->in_urbs[i]; - if (!urb) - continue; - err = usb_submit_urb(urb, GFP_KERNEL); - if (err) { - dbg("%s: submit urb %d failed (%d) %d", - __func__, i, err, - urb->transfer_buffer_length); - } - } - - option_send_setup(port); - - serial->interface->needs_remote_wakeup = 1; - spin_lock_irq(&intfdata->susp_lock); - portdata->opened = 1; - spin_unlock_irq(&intfdata->susp_lock); - usb_autopm_put_interface(serial->interface); - - return 0; -} - -static void option_dtr_rts(struct usb_serial_port *port, int on) -{ - struct usb_serial *serial = port->serial; - struct option_port_private *portdata; - - dbg("%s", __func__); - portdata = usb_get_serial_port_data(port); - mutex_lock(&serial->disc_mutex); - portdata->rts_state = on; - portdata->dtr_state = on; - if (serial->dev) - option_send_setup(port); - mutex_unlock(&serial->disc_mutex); -} - - -static void option_close(struct usb_serial_port *port) -{ - int i; - struct usb_serial *serial = port->serial; - struct option_port_private *portdata; - struct option_intf_private *intfdata = port->serial->private; - - dbg("%s", __func__); - portdata = usb_get_serial_port_data(port); - - if (serial->dev) { - /* Stop reading/writing urbs */ - spin_lock_irq(&intfdata->susp_lock); - portdata->opened = 0; - spin_unlock_irq(&intfdata->susp_lock); - - for (i = 0; i < N_IN_URB; i++) - usb_kill_urb(portdata->in_urbs[i]); - for (i = 0; i < N_OUT_URB; i++) - usb_kill_urb(portdata->out_urbs[i]); - usb_autopm_get_interface(serial->interface); - serial->interface->needs_remote_wakeup = 0; - } -} - -/* Helper functions used by option_setup_urbs */ -static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint, - int dir, void *ctx, char *buf, int len, - void (*callback)(struct urb *)) -{ - struct urb *urb; - - if (endpoint == -1) - return NULL; /* endpoint not needed */ - - urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ - if (urb == NULL) { - dbg("%s: alloc for endpoint %d failed.", __func__, endpoint); - return NULL; - } - - /* Fill URB using supplied data. */ - usb_fill_bulk_urb(urb, serial->dev, - usb_sndbulkpipe(serial->dev, endpoint) | dir, - buf, len, callback, ctx); - - return urb; -} - -/* Setup urbs */ -static void option_setup_urbs(struct usb_serial *serial) -{ - int i, j; - struct usb_serial_port *port; - struct option_port_private *portdata; - - dbg("%s", __func__); - - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - portdata = usb_get_serial_port_data(port); - - /* Do indat endpoints first */ - for (j = 0; j < N_IN_URB; ++j) { - portdata->in_urbs[j] = option_setup_urb(serial, - port->bulk_in_endpointAddress, - USB_DIR_IN, port, - portdata->in_buffer[j], - IN_BUFLEN, option_indat_callback); - } - - /* outdat endpoints */ - for (j = 0; j < N_OUT_URB; ++j) { - portdata->out_urbs[j] = option_setup_urb(serial, - port->bulk_out_endpointAddress, - USB_DIR_OUT, port, - portdata->out_buffer[j], - OUT_BUFLEN, option_outdat_callback); - } - } -} - - /** send RTS/DTR state to the port. * * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN @@ -1316,15 +1104,16 @@ static void option_setup_urbs(struct usb_serial *serial) static int option_send_setup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; - struct option_intf_private *intfdata = - (struct option_intf_private *) serial->private; + struct usb_wwan_intf_private *intfdata = + (struct usb_wwan_intf_private *) serial->private; struct option_port_private *portdata; int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber; int val = 0; dbg("%s", __func__); - if (is_blacklisted(ifNum, intfdata->blacklist_info) == - OPTION_BLACKLIST_SENDSETUP) { + if (is_blacklisted(ifNum, + (struct option_blacklist_info *) intfdata->private) + == OPTION_BLACKLIST_SENDSETUP) { dbg("No send_setup on blacklisted interface #%d\n", ifNum); return -EIO; } @@ -1341,224 +1130,6 @@ static int option_send_setup(struct usb_serial_port *port) 0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT); } -static int option_startup(struct usb_serial *serial) -{ - int i, j, err; - struct usb_serial_port *port; - struct option_port_private *portdata; - u8 *buffer; - - dbg("%s", __func__); - - /* Now setup per port private data */ - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); - if (!portdata) { - dbg("%s: kmalloc for option_port_private (%d) failed!.", - __func__, i); - return 1; - } - init_usb_anchor(&portdata->delayed); - - for (j = 0; j < N_IN_URB; j++) { - buffer = (u8 *)__get_free_page(GFP_KERNEL); - if (!buffer) - goto bail_out_error; - portdata->in_buffer[j] = buffer; - } - - for (j = 0; j < N_OUT_URB; j++) { - buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); - if (!buffer) - goto bail_out_error2; - portdata->out_buffer[j] = buffer; - } - - usb_set_serial_port_data(port, portdata); - - if (!port->interrupt_in_urb) - continue; - err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (err) - dbg("%s: submit irq_in urb failed %d", - __func__, err); - } - option_setup_urbs(serial); - return 0; - -bail_out_error2: - for (j = 0; j < N_OUT_URB; j++) - kfree(portdata->out_buffer[j]); -bail_out_error: - for (j = 0; j < N_IN_URB; j++) - if (portdata->in_buffer[j]) - free_page((unsigned long)portdata->in_buffer[j]); - kfree(portdata); - return 1; -} - -static void stop_read_write_urbs(struct usb_serial *serial) -{ - int i, j; - struct usb_serial_port *port; - struct option_port_private *portdata; - - /* Stop reading/writing urbs */ - for (i = 0; i < serial->num_ports; ++i) { - port = serial->port[i]; - portdata = usb_get_serial_port_data(port); - for (j = 0; j < N_IN_URB; j++) - usb_kill_urb(portdata->in_urbs[j]); - for (j = 0; j < N_OUT_URB; j++) - usb_kill_urb(portdata->out_urbs[j]); - } -} - -static void option_disconnect(struct usb_serial *serial) -{ - dbg("%s", __func__); - - stop_read_write_urbs(serial); -} - -static void option_release(struct usb_serial *serial) -{ - int i, j; - struct usb_serial_port *port; - struct option_port_private *portdata; - - dbg("%s", __func__); - - /* Now free them */ - for (i = 0; i < serial->num_ports; ++i) { - port = serial->port[i]; - portdata = usb_get_serial_port_data(port); - - for (j = 0; j < N_IN_URB; j++) { - if (portdata->in_urbs[j]) { - usb_free_urb(portdata->in_urbs[j]); - free_page((unsigned long) - portdata->in_buffer[j]); - portdata->in_urbs[j] = NULL; - } - } - for (j = 0; j < N_OUT_URB; j++) { - if (portdata->out_urbs[j]) { - usb_free_urb(portdata->out_urbs[j]); - kfree(portdata->out_buffer[j]); - portdata->out_urbs[j] = NULL; - } - } - } - - /* Now free per port private data */ - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - kfree(usb_get_serial_port_data(port)); - } -} - -#ifdef CONFIG_PM -static int option_suspend(struct usb_serial *serial, pm_message_t message) -{ - struct option_intf_private *intfdata = serial->private; - int b; - - dbg("%s entered", __func__); - - if (message.event & PM_EVENT_AUTO) { - spin_lock_irq(&intfdata->susp_lock); - b = intfdata->in_flight; - spin_unlock_irq(&intfdata->susp_lock); - - if (b) - return -EBUSY; - } - - spin_lock_irq(&intfdata->susp_lock); - intfdata->suspended = 1; - spin_unlock_irq(&intfdata->susp_lock); - stop_read_write_urbs(serial); - - return 0; -} - -static void play_delayed(struct usb_serial_port *port) -{ - struct option_intf_private *data; - struct option_port_private *portdata; - struct urb *urb; - int err; - - portdata = usb_get_serial_port_data(port); - data = port->serial->private; - while ((urb = usb_get_from_anchor(&portdata->delayed))) { - err = usb_submit_urb(urb, GFP_ATOMIC); - if (!err) - data->in_flight++; - } -} - -static int option_resume(struct usb_serial *serial) -{ - int i, j; - struct usb_serial_port *port; - struct option_intf_private *intfdata = serial->private; - struct option_port_private *portdata; - struct urb *urb; - int err = 0; - - dbg("%s entered", __func__); - /* get the interrupt URBs resubmitted unconditionally */ - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - if (!port->interrupt_in_urb) { - dbg("%s: No interrupt URB for port %d", __func__, i); - continue; - } - err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); - dbg("Submitted interrupt URB for port %d (result %d)", i, err); - if (err < 0) { - err("%s: Error %d for interrupt URB of port%d", - __func__, err, i); - goto err_out; - } - } - - for (i = 0; i < serial->num_ports; i++) { - /* walk all ports */ - port = serial->port[i]; - portdata = usb_get_serial_port_data(port); - - /* skip closed ports */ - spin_lock_irq(&intfdata->susp_lock); - if (!portdata->opened) { - spin_unlock_irq(&intfdata->susp_lock); - continue; - } - - for (j = 0; j < N_IN_URB; j++) { - urb = portdata->in_urbs[j]; - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0) { - err("%s: Error %d for bulk URB %d", - __func__, err, i); - spin_unlock_irq(&intfdata->susp_lock); - goto err_out; - } - } - play_delayed(port); - spin_unlock_irq(&intfdata->susp_lock); - } - spin_lock_irq(&intfdata->susp_lock); - intfdata->suspended = 0; - spin_unlock_irq(&intfdata->susp_lock); -err_out: - return err; -} -#endif - MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index deeacde..e199b0f 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -51,12 +51,13 @@ #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/uaccess.h> +#include <linux/kfifo.h> #include "oti6858.h" #define OTI6858_DESCRIPTION \ "Ours Technology Inc. OTi-6858 USB to serial adapter driver" #define OTI6858_AUTHOR "Tomasz Michal Lukaszewski <FIXME@FIXME>" -#define OTI6858_VERSION "0.1" +#define OTI6858_VERSION "0.2" static const struct usb_device_id id_table[] = { { USB_DEVICE(OTI6858_VENDOR_ID, OTI6858_PRODUCT_ID) }, @@ -75,18 +76,6 @@ static struct usb_driver oti6858_driver = { static int debug; - -/* buffering code, copied from pl2303 driver */ -#define PL2303_BUF_SIZE 1024 -#define PL2303_TMP_BUF_SIZE 1024 - -struct oti6858_buf { - unsigned int buf_size; - char *buf_buf; - char *buf_get; - char *buf_put; -}; - /* requests */ #define OTI6858_REQ_GET_STATUS (USB_DIR_IN | USB_TYPE_VENDOR | 0x00) #define OTI6858_REQ_T_GET_STATUS 0x01 @@ -161,18 +150,6 @@ static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, static int oti6858_startup(struct usb_serial *serial); static void oti6858_release(struct usb_serial *serial); -/* functions operating on buffers */ -static struct oti6858_buf *oti6858_buf_alloc(unsigned int size); -static void oti6858_buf_free(struct oti6858_buf *pb); -static void oti6858_buf_clear(struct oti6858_buf *pb); -static unsigned int oti6858_buf_data_avail(struct oti6858_buf *pb); -static unsigned int oti6858_buf_space_avail(struct oti6858_buf *pb); -static unsigned int oti6858_buf_put(struct oti6858_buf *pb, const char *buf, - unsigned int count); -static unsigned int oti6858_buf_get(struct oti6858_buf *pb, char *buf, - unsigned int count); - - /* device info */ static struct usb_serial_driver oti6858_device = { .driver = { @@ -201,7 +178,6 @@ static struct usb_serial_driver oti6858_device = { struct oti6858_private { spinlock_t lock; - struct oti6858_buf *buf; struct oti6858_control_pkt status; struct { @@ -295,7 +271,7 @@ static void setup_line(struct work_struct *work) } } -void send_data(struct work_struct *work) +static void send_data(struct work_struct *work) { struct oti6858_private *priv = container_of(work, struct oti6858_private, delayed_write_work.work); @@ -314,9 +290,12 @@ void send_data(struct work_struct *work) return; } priv->flags.write_urb_in_use = 1; - - count = oti6858_buf_data_avail(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); + + spin_lock_irqsave(&port->lock, flags); + count = kfifo_len(&port->write_fifo); + spin_unlock_irqrestore(&port->lock, flags); + if (count > port->bulk_out_size) count = port->bulk_out_size; @@ -350,10 +329,9 @@ void send_data(struct work_struct *work) return; } - spin_lock_irqsave(&priv->lock, flags); - oti6858_buf_get(priv->buf, port->write_urb->transfer_buffer, count); - spin_unlock_irqrestore(&priv->lock, flags); - + count = kfifo_out_locked(&port->write_fifo, + port->write_urb->transfer_buffer, + count, &port->lock); port->write_urb->transfer_buffer_length = count; port->write_urb->dev = port->serial->dev; result = usb_submit_urb(port->write_urb, GFP_NOIO); @@ -376,11 +354,6 @@ static int oti6858_startup(struct usb_serial *serial) priv = kzalloc(sizeof(struct oti6858_private), GFP_KERNEL); if (!priv) break; - priv->buf = oti6858_buf_alloc(PL2303_BUF_SIZE); - if (priv->buf == NULL) { - kfree(priv); - break; - } spin_lock_init(&priv->lock); init_waitqueue_head(&priv->intr_wait); @@ -397,7 +370,6 @@ static int oti6858_startup(struct usb_serial *serial) for (--i; i >= 0; --i) { priv = usb_get_serial_port_data(serial->port[i]); - oti6858_buf_free(priv->buf); kfree(priv); usb_set_serial_port_data(serial->port[i], NULL); } @@ -407,17 +379,12 @@ static int oti6858_startup(struct usb_serial *serial) static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { - struct oti6858_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - dbg("%s(port = %d, count = %d)", __func__, port->number, count); if (!count) return count; - spin_lock_irqsave(&priv->lock, flags); - count = oti6858_buf_put(priv->buf, buf, count); - spin_unlock_irqrestore(&priv->lock, flags); + count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock); return count; } @@ -425,15 +392,14 @@ static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, static int oti6858_write_room(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct oti6858_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; dbg("%s(port = %d)", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); - room = oti6858_buf_space_avail(priv->buf); - spin_unlock_irqrestore(&priv->lock, flags); + spin_lock_irqsave(&port->lock, flags); + room = kfifo_avail(&port->write_fifo); + spin_unlock_irqrestore(&port->lock, flags); return room; } @@ -441,15 +407,14 @@ static int oti6858_write_room(struct tty_struct *tty) static int oti6858_chars_in_buffer(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct oti6858_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; dbg("%s(port = %d)", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); - chars = oti6858_buf_data_avail(priv->buf); - spin_unlock_irqrestore(&priv->lock, flags); + spin_lock_irqsave(&port->lock, flags); + chars = kfifo_len(&port->write_fifo); + spin_unlock_irqrestore(&port->lock, flags); return chars; } @@ -640,10 +605,10 @@ static void oti6858_close(struct usb_serial_port *port) dbg("%s(port = %d)", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&port->lock, flags); /* clear out any remaining data in the buffer */ - oti6858_buf_clear(priv->buf); - spin_unlock_irqrestore(&priv->lock, flags); + kfifo_reset_out(&port->write_fifo); + spin_unlock_irqrestore(&port->lock, flags); dbg("%s(): after buf_clear()", __func__); @@ -785,18 +750,12 @@ static int oti6858_ioctl(struct tty_struct *tty, struct file *file, static void oti6858_release(struct usb_serial *serial) { - struct oti6858_private *priv; int i; dbg("%s()", __func__); - for (i = 0; i < serial->num_ports; ++i) { - priv = usb_get_serial_port_data(serial->port[i]); - if (priv) { - oti6858_buf_free(priv->buf); - kfree(priv); - } - } + for (i = 0; i < serial->num_ports; ++i) + kfree(usb_get_serial_port_data(serial->port[i])); } static void oti6858_read_int_callback(struct urb *urb) @@ -889,10 +848,14 @@ static void oti6858_read_int_callback(struct urb *urb) } } else if (!transient) { unsigned long flags; + int count; + + spin_lock_irqsave(&port->lock, flags); + count = kfifo_len(&port->write_fifo); + spin_unlock_irqrestore(&port->lock, flags); spin_lock_irqsave(&priv->lock, flags); - if (priv->flags.write_urb_in_use == 0 - && oti6858_buf_data_avail(priv->buf) != 0) { + if (priv->flags.write_urb_in_use == 0 && count != 0) { schedule_delayed_work(&priv->delayed_write_work, 0); resubmit = 0; } @@ -1014,165 +977,6 @@ static void oti6858_write_bulk_callback(struct urb *urb) } } - -/* - * oti6858_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ -static struct oti6858_buf *oti6858_buf_alloc(unsigned int size) -{ - struct oti6858_buf *pb; - - if (size == 0) - return NULL; - - pb = kmalloc(sizeof(struct oti6858_buf), GFP_KERNEL); - if (pb == NULL) - return NULL; - - pb->buf_buf = kmalloc(size, GFP_KERNEL); - if (pb->buf_buf == NULL) { - kfree(pb); - return NULL; - } - - pb->buf_size = size; - pb->buf_get = pb->buf_put = pb->buf_buf; - - return pb; -} - -/* - * oti6858_buf_free - * - * Free the buffer and all associated memory. - */ -static void oti6858_buf_free(struct oti6858_buf *pb) -{ - if (pb) { - kfree(pb->buf_buf); - kfree(pb); - } -} - -/* - * oti6858_buf_clear - * - * Clear out all data in the circular buffer. - */ -static void oti6858_buf_clear(struct oti6858_buf *pb) -{ - if (pb != NULL) { - /* equivalent to a get of all data available */ - pb->buf_get = pb->buf_put; - } -} - -/* - * oti6858_buf_data_avail - * - * Return the number of bytes of data available in the circular - * buffer. - */ -static unsigned int oti6858_buf_data_avail(struct oti6858_buf *pb) -{ - if (pb == NULL) - return 0; - return (pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size; -} - -/* - * oti6858_buf_space_avail - * - * Return the number of bytes of space available in the circular - * buffer. - */ -static unsigned int oti6858_buf_space_avail(struct oti6858_buf *pb) -{ - if (pb == NULL) - return 0; - return (pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size; -} - -/* - * oti6858_buf_put - * - * Copy data data from a user buffer and put it into the circular buffer. - * Restrict to the amount of space available. - * - * Return the number of bytes copied. - */ -static unsigned int oti6858_buf_put(struct oti6858_buf *pb, const char *buf, - unsigned int count) -{ - unsigned int len; - - if (pb == NULL) - return 0; - - len = oti6858_buf_space_avail(pb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = pb->buf_buf + pb->buf_size - pb->buf_put; - if (count > len) { - memcpy(pb->buf_put, buf, len); - memcpy(pb->buf_buf, buf+len, count - len); - pb->buf_put = pb->buf_buf + count - len; - } else { - memcpy(pb->buf_put, buf, count); - if (count < len) - pb->buf_put += count; - else /* count == len */ - pb->buf_put = pb->buf_buf; - } - - return count; -} - -/* - * oti6858_buf_get - * - * Get data from the circular buffer and copy to the given buffer. - * Restrict to the amount of data available. - * - * Return the number of bytes copied. - */ -static unsigned int oti6858_buf_get(struct oti6858_buf *pb, char *buf, - unsigned int count) -{ - unsigned int len; - - if (pb == NULL) - return 0; - - len = oti6858_buf_data_avail(pb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = pb->buf_buf + pb->buf_size - pb->buf_get; - if (count > len) { - memcpy(buf, pb->buf_get, len); - memcpy(buf+len, pb->buf_buf, count - len); - pb->buf_get = pb->buf_buf + count - len; - } else { - memcpy(buf, pb->buf_get, count); - if (count < len) - pb->buf_get += count; - else /* count == len */ - pb->buf_get = pb->buf_buf; - } - - return count; -} - /* module description and (de)initialization */ static int __init oti6858_init(void) diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index c28b160..6b60018 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -40,16 +40,6 @@ static int debug; #define PL2303_CLOSING_WAIT (30*HZ) -#define PL2303_BUF_SIZE 1024 -#define PL2303_TMP_BUF_SIZE 1024 - -struct pl2303_buf { - unsigned int buf_size; - char *buf_buf; - char *buf_get; - char *buf_put; -}; - static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, @@ -157,173 +147,12 @@ enum pl2303_type { struct pl2303_private { spinlock_t lock; - struct pl2303_buf *buf; - int write_urb_in_use; wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; enum pl2303_type type; }; -/* - * pl2303_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ -static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) -{ - struct pl2303_buf *pb; - - if (size == 0) - return NULL; - - pb = kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL); - if (pb == NULL) - return NULL; - - pb->buf_buf = kmalloc(size, GFP_KERNEL); - if (pb->buf_buf == NULL) { - kfree(pb); - return NULL; - } - - pb->buf_size = size; - pb->buf_get = pb->buf_put = pb->buf_buf; - - return pb; -} - -/* - * pl2303_buf_free - * - * Free the buffer and all associated memory. - */ -static void pl2303_buf_free(struct pl2303_buf *pb) -{ - if (pb) { - kfree(pb->buf_buf); - kfree(pb); - } -} - -/* - * pl2303_buf_clear - * - * Clear out all data in the circular buffer. - */ -static void pl2303_buf_clear(struct pl2303_buf *pb) -{ - if (pb != NULL) - pb->buf_get = pb->buf_put; - /* equivalent to a get of all data available */ -} - -/* - * pl2303_buf_data_avail - * - * Return the number of bytes of data available in the circular - * buffer. - */ -static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb) -{ - if (pb == NULL) - return 0; - - return (pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size; -} - -/* - * pl2303_buf_space_avail - * - * Return the number of bytes of space available in the circular - * buffer. - */ -static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb) -{ - if (pb == NULL) - return 0; - - return (pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size; -} - -/* - * pl2303_buf_put - * - * Copy data data from a user buffer and put it into the circular buffer. - * Restrict to the amount of space available. - * - * Return the number of bytes copied. - */ -static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, - unsigned int count) -{ - unsigned int len; - - if (pb == NULL) - return 0; - - len = pl2303_buf_space_avail(pb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = pb->buf_buf + pb->buf_size - pb->buf_put; - if (count > len) { - memcpy(pb->buf_put, buf, len); - memcpy(pb->buf_buf, buf+len, count - len); - pb->buf_put = pb->buf_buf + count - len; - } else { - memcpy(pb->buf_put, buf, count); - if (count < len) - pb->buf_put += count; - else /* count == len */ - pb->buf_put = pb->buf_buf; - } - - return count; -} - -/* - * pl2303_buf_get - * - * Get data from the circular buffer and copy to the given buffer. - * Restrict to the amount of data available. - * - * Return the number of bytes copied. - */ -static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, - unsigned int count) -{ - unsigned int len; - - if (pb == NULL) - return 0; - - len = pl2303_buf_data_avail(pb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = pb->buf_buf + pb->buf_size - pb->buf_get; - if (count > len) { - memcpy(buf, pb->buf_get, len); - memcpy(buf+len, pb->buf_buf, count - len); - pb->buf_get = pb->buf_buf + count - len; - } else { - memcpy(buf, pb->buf_get, count); - if (count < len) - pb->buf_get += count; - else /* count == len */ - pb->buf_get = pb->buf_buf; - } - - return count; -} - static int pl2303_vendor_read(__u16 value, __u16 index, struct usb_serial *serial, unsigned char *buf) { @@ -372,11 +201,6 @@ static int pl2303_startup(struct usb_serial *serial) if (!priv) goto cleanup; spin_lock_init(&priv->lock); - priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE); - if (priv->buf == NULL) { - kfree(priv); - goto cleanup; - } init_waitqueue_head(&priv->delta_msr_wait); priv->type = type; usb_set_serial_port_data(serial->port[i], priv); @@ -404,7 +228,6 @@ cleanup: kfree(buf); for (--i; i >= 0; --i) { priv = usb_get_serial_port_data(serial->port[i]); - pl2303_buf_free(priv->buf); kfree(priv); usb_set_serial_port_data(serial->port[i], NULL); } @@ -422,102 +245,6 @@ static int set_control_lines(struct usb_device *dev, u8 value) return retval; } -static void pl2303_send(struct usb_serial_port *port) -{ - int count, result; - struct pl2303_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&priv->lock, flags); - - if (priv->write_urb_in_use) { - spin_unlock_irqrestore(&priv->lock, flags); - return; - } - - count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, - port->bulk_out_size); - - if (count == 0) { - spin_unlock_irqrestore(&priv->lock, flags); - return; - } - - priv->write_urb_in_use = 1; - - spin_unlock_irqrestore(&priv->lock, flags); - - usb_serial_debug_data(debug, &port->dev, __func__, count, - port->write_urb->transfer_buffer); - - port->write_urb->transfer_buffer_length = count; - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) { - dev_err(&port->dev, "%s - failed submitting write urb," - " error %d\n", __func__, result); - priv->write_urb_in_use = 0; - /* TODO: reschedule pl2303_send */ - } - - usb_serial_port_softint(port); -} - -static int pl2303_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - struct pl2303_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - - dbg("%s - port %d, %d bytes", __func__, port->number, count); - - if (!count) - return count; - - spin_lock_irqsave(&priv->lock, flags); - count = pl2303_buf_put(priv->buf, buf, count); - spin_unlock_irqrestore(&priv->lock, flags); - - pl2303_send(port); - - return count; -} - -static int pl2303_write_room(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct pl2303_private *priv = usb_get_serial_port_data(port); - int room = 0; - unsigned long flags; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&priv->lock, flags); - room = pl2303_buf_space_avail(priv->buf); - spin_unlock_irqrestore(&priv->lock, flags); - - dbg("%s - returns %d", __func__, room); - return room; -} - -static int pl2303_chars_in_buffer(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct pl2303_private *priv = usb_get_serial_port_data(port); - int chars = 0; - unsigned long flags; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&priv->lock, flags); - chars = pl2303_buf_data_avail(priv->buf); - spin_unlock_irqrestore(&priv->lock, flags); - - dbg("%s - returns %d", __func__, chars); - return chars; -} - static void pl2303_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { @@ -729,22 +456,10 @@ static void pl2303_dtr_rts(struct usb_serial_port *port, int on) static void pl2303_close(struct usb_serial_port *port) { - struct pl2303_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); - /* clear out any remaining data in the buffer */ - pl2303_buf_clear(priv->buf); - spin_unlock_irqrestore(&priv->lock, flags); - - /* shutdown our urbs */ - dbg("%s - shutting down urbs", __func__); - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); usb_kill_urb(port->interrupt_in_urb); - } static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) @@ -770,10 +485,8 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) pl2303_set_termios(tty, port, &tmp_termios); dbg("%s - submitting read urb", __func__); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); + result = usb_serial_generic_submit_read_urb(port, GFP_KERNEL); if (result) { - dev_err(&port->dev, "%s - failed submitting read urb," - " error %d\n", __func__, result); pl2303_close(port); return -EPROTO; } @@ -953,10 +666,7 @@ static void pl2303_release(struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { priv = usb_get_serial_port_data(serial->port[i]); - if (priv) { - pl2303_buf_free(priv->buf); - kfree(priv); - } + kfree(priv); } } @@ -1037,13 +747,31 @@ exit: __func__, retval); } -static void pl2303_push_data(struct tty_struct *tty, - struct usb_serial_port *port, struct urb *urb, - u8 line_status) +static void pl2303_process_read_urb(struct urb *urb) { + struct usb_serial_port *port = urb->context; + struct pl2303_private *priv = usb_get_serial_port_data(port); + struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; - /* get tty_flag from status */ char tty_flag = TTY_NORMAL; + unsigned long flags; + u8 line_status; + int i; + + /* update line status */ + spin_lock_irqsave(&priv->lock, flags); + line_status = priv->line_status; + priv->line_status &= ~UART_STATE_TRANSIENT_MASK; + spin_unlock_irqrestore(&priv->lock, flags); + wake_up_interruptible(&priv->delta_msr_wait); + + if (!urb->actual_length) + return; + + tty = tty_port_tty_get(&port->port); + if (!tty) + return; + /* break takes precedence over parity, */ /* which takes precedence over framing errors */ if (line_status & UART_BREAK_ERROR) @@ -1058,107 +786,17 @@ static void pl2303_push_data(struct tty_struct *tty, if (line_status & UART_OVERRUN_ERROR) tty_insert_flip_char(tty, 0, TTY_OVERRUN); - if (tty_flag == TTY_NORMAL && !(port->console && port->sysrq)) - tty_insert_flip_string(tty, data, urb->actual_length); - else { - int i; + if (port->port.console && port->sysrq) { for (i = 0; i < urb->actual_length; ++i) if (!usb_serial_handle_sysrq_char(tty, port, data[i])) tty_insert_flip_char(tty, data[i], tty_flag); + } else { + tty_insert_flip_string_fixed_flag(tty, data, tty_flag, + urb->actual_length); } - tty_flip_buffer_push(tty); -} - -static void pl2303_read_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - struct pl2303_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty; - unsigned long flags; - int result; - int status = urb->status; - u8 line_status; - - dbg("%s - port %d", __func__, port->number); - - if (status) { - dbg("%s - urb status = %d", __func__, status); - if (status == -EPROTO) { - /* PL2303 mysteriously fails with -EPROTO reschedule - * the read */ - dbg("%s - caught -EPROTO, resubmitting the urb", - __func__); - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result) - dev_err(&urb->dev->dev, "%s - failed" - " resubmitting read urb, error %d\n", - __func__, result); - return; - } - dbg("%s - unable to handle the error, exiting.", __func__); - return; - } - - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, urb->transfer_buffer); - - spin_lock_irqsave(&priv->lock, flags); - line_status = priv->line_status; - priv->line_status &= ~UART_STATE_TRANSIENT_MASK; - spin_unlock_irqrestore(&priv->lock, flags); - wake_up_interruptible(&priv->delta_msr_wait); - tty = tty_port_tty_get(&port->port); - if (tty && urb->actual_length) { - pl2303_push_data(tty, port, urb, line_status); - } + tty_flip_buffer_push(tty); tty_kref_put(tty); - /* Schedule the next read _if_ we are still open */ - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result && result != -EPERM) - dev_err(&urb->dev->dev, "%s - failed resubmitting" - " read urb, error %d\n", __func__, result); -} - -static void pl2303_write_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - struct pl2303_private *priv = usb_get_serial_port_data(port); - int result; - int status = urb->status; - - dbg("%s - port %d", __func__, port->number); - - switch (status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, - status); - priv->write_urb_in_use = 0; - return; - default: - /* error in the urb, so we have to resubmit it */ - dbg("%s - Overflow in write", __func__); - dbg("%s - nonzero write bulk status received: %d", __func__, - status); - port->write_urb->transfer_buffer_length = 1; - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) - dev_err(&urb->dev->dev, "%s - failed resubmitting write" - " urb, error %d\n", __func__, result); - else - return; - } - - priv->write_urb_in_use = 0; - - /* send any buffered data */ - pl2303_send(port); } /* All of the device info needed for the PL2303 SIO serial converter */ @@ -1170,21 +808,19 @@ static struct usb_serial_driver pl2303_device = { .id_table = id_table, .usb_driver = &pl2303_driver, .num_ports = 1, + .bulk_in_size = 256, + .bulk_out_size = 256, .open = pl2303_open, .close = pl2303_close, .dtr_rts = pl2303_dtr_rts, .carrier_raised = pl2303_carrier_raised, - .write = pl2303_write, .ioctl = pl2303_ioctl, .break_ctl = pl2303_break_ctl, .set_termios = pl2303_set_termios, .tiocmget = pl2303_tiocmget, .tiocmset = pl2303_tiocmset, - .read_bulk_callback = pl2303_read_bulk_callback, + .process_read_urb = pl2303_process_read_urb, .read_int_callback = pl2303_read_int_callback, - .write_bulk_callback = pl2303_write_bulk_callback, - .write_room = pl2303_write_room, - .chars_in_buffer = pl2303_chars_in_buffer, .attach = pl2303_startup, .release = pl2303_release, }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 23c09b3..a871645 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -5,7 +5,7 @@ * 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. - * + * */ #define BENQ_VENDOR_ID 0x04a5 @@ -137,5 +137,5 @@ #define SANWA_PRODUCT_ID 0x0001 /* ADLINK ND-6530 RS232,RS485 and RS422 adapter */ -#define ADLINK_VENDOR_ID 0x0b63 -#define ADLINK_ND6530_PRODUCT_ID 0x6530 +#define ADLINK_VENDOR_ID 0x0b63 +#define ADLINK_ND6530_PRODUCT_ID 0x6530 diff --git a/drivers/usb/serial/qcaux.c b/drivers/usb/serial/qcaux.c index 7e3bea2..214a3e5 100644 --- a/drivers/usb/serial/qcaux.c +++ b/drivers/usb/serial/qcaux.c @@ -50,6 +50,10 @@ #define SANYO_VENDOR_ID 0x0474 #define SANYO_PRODUCT_KATANA_LX 0x0754 /* SCP-3800 (Katana LX) */ +/* Samsung devices */ +#define SAMSUNG_VENDOR_ID 0x04e8 +#define SAMSUNG_PRODUCT_U520 0x6640 /* SCH-U520 */ + static struct usb_device_id id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_PC5740, 0xff, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_PC5750, 0xff, 0x00, 0x00) }, @@ -61,6 +65,7 @@ static struct usb_device_id id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDX650, 0xff, 0xff, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(LG_VENDOR_ID, LG_PRODUCT_VX4400_6000, 0xff, 0xff, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(SANYO_VENDOR_ID, SANYO_PRODUCT_KATANA_LX, 0xff, 0xff, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_U520, 0xff, 0x00, 0x00) }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 53a2d5a..04bb759 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -15,6 +15,8 @@ #include <linux/tty_flip.h> #include <linux/usb.h> #include <linux/usb/serial.h> +#include <linux/slab.h> +#include "usb-wwan.h" #define DRIVER_AUTHOR "Qualcomm Inc" #define DRIVER_DESC "Qualcomm USB Serial driver" @@ -76,6 +78,8 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ {USB_DEVICE(0x16d8, 0x8001)}, /* CMDTech Gobi 2000 QDL device (VU922) */ {USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */ + {USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */ + {USB_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); @@ -92,6 +96,8 @@ static struct usb_driver qcdriver = { static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) { + struct usb_wwan_intf_private *data; + struct usb_host_interface *intf = serial->interface->cur_altsetting; int retval = -ENODEV; __u8 nintf; __u8 ifnum; @@ -100,33 +106,45 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) nintf = serial->dev->actconfig->desc.bNumInterfaces; dbg("Num Interfaces = %d", nintf); - ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; + ifnum = intf->desc.bInterfaceNumber; dbg("This Interface = %d", ifnum); + data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&data->susp_lock); + switch (nintf) { case 1: /* QDL mode */ - if (serial->interface->num_altsetting == 2) { - struct usb_host_interface *intf; - + /* Gobi 2000 has a single altsetting, older ones have two */ + if (serial->interface->num_altsetting == 2) intf = &serial->interface->altsetting[1]; - if (intf->desc.bNumEndpoints == 2) { - if (usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) && - usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) { - dbg("QDL port found"); - retval = usb_set_interface(serial->dev, ifnum, 1); - if (retval < 0) { - dev_err(&serial->dev->dev, - "Could not set interface, error %d\n", - retval); - retval = -ENODEV; - } - return retval; - } + else if (serial->interface->num_altsetting > 2) + break; + + if (intf->desc.bNumEndpoints == 2 && + usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) && + usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) { + dbg("QDL port found"); + + if (serial->interface->num_altsetting == 1) + return 0; + + retval = usb_set_interface(serial->dev, ifnum, 1); + if (retval < 0) { + dev_err(&serial->dev->dev, + "Could not set interface, error %d\n", + retval); + retval = -ENODEV; } + return retval; } break; + case 3: case 4: /* Composite mode */ if (ifnum == 2) { @@ -161,6 +179,18 @@ static struct usb_serial_driver qcdevice = { .usb_driver = &qcdriver, .num_ports = 1, .probe = qcprobe, + .open = usb_wwan_open, + .close = usb_wwan_close, + .write = usb_wwan_write, + .write_room = usb_wwan_write_room, + .chars_in_buffer = usb_wwan_chars_in_buffer, + .attach = usb_wwan_startup, + .disconnect = usb_wwan_disconnect, + .release = usb_wwan_release, +#ifdef CONFIG_PM + .suspend = usb_wwan_suspend, + .resume = usb_wwan_resume, +#endif }; static int __init qcinit(void) diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 43a0cad..a36e231 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -1,6 +1,7 @@ /* * Safe Encapsulated USB Serial Driver * + * Copyright (C) 2010 Johan Hovold <jhovold@gmail.com> * Copyright (C) 2001 Lineo * Copyright (C) 2001 Hewlett-Packard * @@ -84,8 +85,8 @@ static int debug; static int safe = 1; static int padded = CONFIG_USB_SERIAL_SAFE_PADDED; -#define DRIVER_VERSION "v0.0b" -#define DRIVER_AUTHOR "sl@lineo.com, tbr@lineo.com" +#define DRIVER_VERSION "v0.1" +#define DRIVER_AUTHOR "sl@lineo.com, tbr@lineo.com, Johan Hovold <jhovold@gmail.com>" #define DRIVER_DESC "USB Safe Encapsulated Serial" MODULE_AUTHOR(DRIVER_AUTHOR); @@ -212,191 +213,80 @@ static __u16 __inline__ fcs_compute10(unsigned char *sp, int len, __u16 fcs) return fcs; } -static void safe_read_bulk_callback(struct urb *urb) +static void safe_process_read_urb(struct urb *urb) { - struct usb_serial_port *port = urb->context; + struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; unsigned char length = urb->actual_length; + int actual_length; struct tty_struct *tty; - int result; - int status = urb->status; + __u16 fcs; - dbg("%s", __func__); - - if (status) { - dbg("%s - nonzero read bulk status received: %d", - __func__, status); + if (!length) return; - } - dbg("safe_read_bulk_callback length: %d", - port->read_urb->actual_length); -#ifdef ECHO_RCV - { - int i; - unsigned char *cp = port->read_urb->transfer_buffer; - for (i = 0; i < port->read_urb->actual_length; i++) { - if ((i % 32) == 0) - printk("\nru[%02x] ", i); - printk("%02x ", *cp++); - } - printk("\n"); - } -#endif tty = tty_port_tty_get(&port->port); - if (safe) { - __u16 fcs; - fcs = fcs_compute10(data, length, CRC10_INITFCS); - if (!fcs) { - int actual_length = data[length - 2] >> 2; - if (actual_length <= (length - 2)) { - dev_info(&urb->dev->dev, "%s - actual: %d\n", - __func__, actual_length); - tty_insert_flip_string(tty, - data, actual_length); - tty_flip_buffer_push(tty); - } else { - dev_err(&port->dev, - "%s - inconsistent lengths %d:%d\n", - __func__, actual_length, length); - } - } else { - dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs); - } - } else { - tty_insert_flip_string(tty, data, length); - tty_flip_buffer_push(tty); - } - tty_kref_put(tty); - - /* Continue trying to always read */ - usb_fill_bulk_urb(urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - urb->transfer_buffer, urb->transfer_buffer_length, - safe_read_bulk_callback, port); - - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); - /* FIXME: Need a mechanism to retry later if this happens */ -} - -static int safe_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - unsigned char *data; - int result; - int i; - int packet_length; - - dbg("safe_write port: %p %d urb: %p count: %d", - port, port->number, port->write_urb, count); - - if (!port->write_urb) { - dbg("%s - write urb NULL", __func__); - return 0; - } - - dbg("safe_write write_urb: %d transfer_buffer_length", - port->write_urb->transfer_buffer_length); - - if (!port->write_urb->transfer_buffer_length) { - dbg("%s - write urb transfer_buffer_length zero", __func__); - return 0; - } - if (count == 0) { - dbg("%s - write request of 0 bytes", __func__); - return 0; - } - spin_lock_bh(&port->lock); - if (port->write_urb_busy) { - spin_unlock_bh(&port->lock); - dbg("%s - already writing", __func__); - return 0; - } - port->write_urb_busy = 1; - spin_unlock_bh(&port->lock); - - packet_length = port->bulk_out_size; /* get max packetsize */ - - i = packet_length - (safe ? 2 : 0); /* get bytes to send */ - count = (count > i) ? i : count; - - - /* get the data into the transfer buffer */ - data = port->write_urb->transfer_buffer; - memset(data, '0', packet_length); - - memcpy(data, buf, count); - - if (safe) { - __u16 fcs; - - /* pad if necessary */ - if (!padded) - packet_length = count + 2; - /* set count */ - data[packet_length - 2] = count << 2; - data[packet_length - 1] = 0; + if (!tty) + return; - /* compute fcs and insert into trailer */ - fcs = fcs_compute10(data, packet_length, CRC10_INITFCS); - data[packet_length - 2] |= fcs >> 8; - data[packet_length - 1] |= fcs & 0xff; + if (!safe) + goto out; - /* set length to send */ - port->write_urb->transfer_buffer_length = packet_length; - } else { - port->write_urb->transfer_buffer_length = count; + fcs = fcs_compute10(data, length, CRC10_INITFCS); + if (fcs) { + dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs); + goto err; } - usb_serial_debug_data(debug, &port->dev, __func__, count, - port->write_urb->transfer_buffer); -#ifdef ECHO_TX - { - int i; - unsigned char *cp = port->write_urb->transfer_buffer; - for (i = 0; i < port->write_urb->transfer_buffer_length; i++) { - if ((i % 32) == 0) - printk("\nsu[%02x] ", i); - printk("%02x ", *cp++); - } - printk("\n"); - } -#endif - port->write_urb->dev = port->serial->dev; - result = usb_submit_urb(port->write_urb, GFP_KERNEL); - if (result) { - port->write_urb_busy = 0; - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", - __func__, result); - return 0; + actual_length = data[length - 2] >> 2; + if (actual_length > (length - 2)) { + dev_err(&port->dev, "%s - inconsistent lengths %d:%d\n", + __func__, actual_length, length); + goto err; } - dbg("%s urb: %p submitted", __func__, port->write_urb); - - return count; + dev_info(&urb->dev->dev, "%s - actual: %d\n", __func__, actual_length); + length = actual_length; +out: + tty_insert_flip_string(tty, data, length); + tty_flip_buffer_push(tty); +err: + tty_kref_put(tty); } -static int safe_write_room(struct tty_struct *tty) +static int safe_prepare_write_buffer(struct usb_serial_port *port, + void *dest, size_t size) { - struct usb_serial_port *port = tty->driver_data; - int room = 0; /* Default: no room */ - unsigned long flags; + unsigned char *buf = dest; + int count; + int trailer_len; + int pkt_len; + __u16 fcs; + + trailer_len = safe ? 2 : 0; + + count = kfifo_out_locked(&port->write_fifo, buf, size - trailer_len, + &port->lock); + if (!safe) + return count; + + /* pad if necessary */ + if (padded) { + pkt_len = size; + memset(buf + count, '0', pkt_len - count - trailer_len); + } else { + pkt_len = count + trailer_len; + } - dbg("%s", __func__); + /* set count */ + buf[pkt_len - 2] = count << 2; + buf[pkt_len - 1] = 0; - spin_lock_irqsave(&port->lock, flags); - if (port->write_urb_busy) - room = port->bulk_out_size - (safe ? 2 : 0); - spin_unlock_irqrestore(&port->lock, flags); + /* compute fcs and insert into trailer */ + fcs = fcs_compute10(buf, pkt_len, CRC10_INITFCS); + buf[pkt_len - 2] |= fcs >> 8; + buf[pkt_len - 1] |= fcs & 0xff; - if (room) - dbg("safe_write_room returns %d", room); - return room; + return pkt_len; } static int safe_startup(struct usb_serial *serial) @@ -421,9 +311,8 @@ static struct usb_serial_driver safe_device = { .id_table = id_table, .usb_driver = &safe_driver, .num_ports = 1, - .write = safe_write, - .write_room = safe_write_room, - .read_bulk_callback = safe_read_bulk_callback, + .process_read_urb = safe_process_read_urb, + .prepare_write_buffer = safe_prepare_write_buffer, .attach = safe_startup, }; diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 5d39191..329d311 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -1,6 +1,7 @@ /* * spcp8x5 USB to serial adaptor driver * + * Copyright (C) 2010 Johan Hovold (jhovold@gmail.com) * Copyright (C) 2006 Linxb (xubin.lin@worldplus.com.cn) * Copyright (C) 2006 S1 Corp. * @@ -29,7 +30,7 @@ /* Version Information */ -#define DRIVER_VERSION "v0.04" +#define DRIVER_VERSION "v0.10" #define DRIVER_DESC "SPCP8x5 USB to serial adaptor driver" static int debug; @@ -64,11 +65,6 @@ struct spcp8x5_usb_ctrl_arg { u16 length; }; -/* wait 30s before close */ -#define SPCP8x5_CLOSING_WAIT (30*HZ) - -#define SPCP8x5_BUF_SIZE 1024 - /* spcp8x5 spec register define */ #define MCR_CONTROL_LINE_RTS 0x02 @@ -155,133 +151,6 @@ enum spcp8x5_type { SPCP835_TYPE, }; -/* 1st in 1st out buffer 4 driver */ -struct ringbuf { - unsigned int buf_size; - char *buf_buf; - char *buf_get; - char *buf_put; -}; - -/* alloc the ring buf and alloc the buffer itself */ -static inline struct ringbuf *alloc_ringbuf(unsigned int size) -{ - struct ringbuf *pb; - - if (size == 0) - return NULL; - - pb = kmalloc(sizeof(*pb), GFP_KERNEL); - if (pb == NULL) - return NULL; - - pb->buf_buf = kmalloc(size, GFP_KERNEL); - if (pb->buf_buf == NULL) { - kfree(pb); - return NULL; - } - - pb->buf_size = size; - pb->buf_get = pb->buf_put = pb->buf_buf; - - return pb; -} - -/* free the ring buf and the buffer itself */ -static inline void free_ringbuf(struct ringbuf *pb) -{ - if (pb != NULL) { - kfree(pb->buf_buf); - kfree(pb); - } -} - -/* clear pipo , juest repoint the pointer here */ -static inline void clear_ringbuf(struct ringbuf *pb) -{ - if (pb != NULL) - pb->buf_get = pb->buf_put; -} - -/* get the number of data in the pipo */ -static inline unsigned int ringbuf_avail_data(struct ringbuf *pb) -{ - if (pb == NULL) - return 0; - return (pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size; -} - -/* get the number of space in the pipo */ -static inline unsigned int ringbuf_avail_space(struct ringbuf *pb) -{ - if (pb == NULL) - return 0; - return (pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size; -} - -/* put count data into pipo */ -static unsigned int put_ringbuf(struct ringbuf *pb, const char *buf, - unsigned int count) -{ - unsigned int len; - - if (pb == NULL) - return 0; - - len = ringbuf_avail_space(pb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = pb->buf_buf + pb->buf_size - pb->buf_put; - if (count > len) { - memcpy(pb->buf_put, buf, len); - memcpy(pb->buf_buf, buf+len, count - len); - pb->buf_put = pb->buf_buf + count - len; - } else { - memcpy(pb->buf_put, buf, count); - if (count < len) - pb->buf_put += count; - else /* count == len */ - pb->buf_put = pb->buf_buf; - } - return count; -} - -/* get count data from pipo */ -static unsigned int get_ringbuf(struct ringbuf *pb, char *buf, - unsigned int count) -{ - unsigned int len; - - if (pb == NULL || buf == NULL) - return 0; - - len = ringbuf_avail_data(pb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = pb->buf_buf + pb->buf_size - pb->buf_get; - if (count > len) { - memcpy(buf, pb->buf_get, len); - memcpy(buf+len, pb->buf_buf, count - len); - pb->buf_get = pb->buf_buf + count - len; - } else { - memcpy(buf, pb->buf_get, count); - if (count < len) - pb->buf_get += count; - else /* count == len */ - pb->buf_get = pb->buf_buf; - } - - return count; -} - static struct usb_driver spcp8x5_driver = { .name = "spcp8x5", .probe = usb_serial_probe, @@ -293,8 +162,6 @@ static struct usb_driver spcp8x5_driver = { struct spcp8x5_private { spinlock_t lock; - struct ringbuf *buf; - int write_urb_in_use; enum spcp8x5_type type; wait_queue_head_t delta_msr_wait; u8 line_control; @@ -330,24 +197,15 @@ static int spcp8x5_startup(struct usb_serial *serial) goto cleanup; spin_lock_init(&priv->lock); - priv->buf = alloc_ringbuf(SPCP8x5_BUF_SIZE); - if (priv->buf == NULL) - goto cleanup2; - init_waitqueue_head(&priv->delta_msr_wait); priv->type = type; usb_set_serial_port_data(serial->port[i] , priv); - } return 0; - -cleanup2: - kfree(priv); cleanup: for (--i; i >= 0; --i) { priv = usb_get_serial_port_data(serial->port[i]); - free_ringbuf(priv->buf); kfree(priv); usb_set_serial_port_data(serial->port[i] , NULL); } @@ -358,15 +216,9 @@ cleanup: static void spcp8x5_release(struct usb_serial *serial) { int i; - struct spcp8x5_private *priv; - for (i = 0; i < serial->num_ports; i++) { - priv = usb_get_serial_port_data(serial->port[i]); - if (priv) { - free_ringbuf(priv->buf); - kfree(priv); - } - } + for (i = 0; i < serial->num_ports; i++) + kfree(usb_get_serial_port_data(serial->port[i])); } /* set the modem control line of the device. @@ -470,33 +322,6 @@ static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on) spcp8x5_set_ctrlLine(port->serial->dev, control , priv->type); } -/* close the serial port. We should wait for data sending to device 1st and - * then kill all urb. */ -static void spcp8x5_close(struct usb_serial_port *port) -{ - struct spcp8x5_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - int result; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&priv->lock, flags); - /* clear out any remaining data in the buffer */ - clear_ringbuf(priv->buf); - spin_unlock_irqrestore(&priv->lock, flags); - - /* kill urb */ - if (port->write_urb != NULL) { - result = usb_unlink_urb(port->write_urb); - if (result) - dev_dbg(&port->dev, - "usb_unlink_urb(write_urb) = %d\n", result); - } - result = usb_unlink_urb(port->read_urb); - if (result) - dev_dbg(&port->dev, "usb_unlink_urb(read_urb) = %d\n", result); -} - static void spcp8x5_init_termios(struct tty_struct *tty) { /* for the 1st time call this function */ @@ -620,7 +445,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty, } /* open the serial port. do some usb system call. set termios and get the line - * status of the device. then submit the read urb */ + * status of the device. */ static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) { struct ktermios tmp_termios; @@ -655,52 +480,21 @@ static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) priv->line_status = status & 0xf0 ; spin_unlock_irqrestore(&priv->lock, flags); - dbg("%s - submitting read urb", __func__); - port->read_urb->dev = serial->dev; - ret = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (ret) { - spcp8x5_close(port); - return -EPROTO; - } port->port.drain_delay = 256; - return 0; + + return usb_serial_generic_open(tty, port); } -/* bulk read call back function. check the status of the urb. if transfer - * failed return. then update the status and the tty send data to tty subsys. - * submit urb again. - */ -static void spcp8x5_read_bulk_callback(struct urb *urb) +static void spcp8x5_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; struct spcp8x5_private *priv = usb_get_serial_port_data(port); struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; unsigned long flags; - int result = urb->status; u8 status; char tty_flag; - dev_dbg(&port->dev, "start, result = %d, urb->actual_length = %d\n,", - result, urb->actual_length); - - /* check the urb status */ - if (result) { - if (result == -EPROTO) { - /* spcp8x5 mysteriously fails with -EPROTO */ - /* reschedule the read */ - urb->dev = port->serial->dev; - result = usb_submit_urb(urb , GFP_ATOMIC); - if (result) - dev_dbg(&port->dev, - "failed submitting read urb %d\n", - result); - return; - } - dev_dbg(&port->dev, "unable to handle the error, exiting.\n"); - return; - } - /* get tty_flag from status */ tty_flag = TTY_NORMAL; @@ -711,141 +505,33 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) /* wake up the wait for termios */ wake_up_interruptible(&priv->delta_msr_wait); - /* break takes precedence over parity, which takes precedence over - * framing errors */ - if (status & UART_BREAK_ERROR) - tty_flag = TTY_BREAK; - else if (status & UART_PARITY_ERROR) - tty_flag = TTY_PARITY; - else if (status & UART_FRAME_ERROR) - tty_flag = TTY_FRAME; - dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); - - tty = tty_port_tty_get(&port->port); - if (tty && urb->actual_length) { - /* overrun is special, not associated with a char */ - if (status & UART_OVERRUN_ERROR) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - tty_insert_flip_string_fixed_flag(tty, data, - urb->actual_length, tty_flag); - tty_flip_buffer_push(tty); - } - tty_kref_put(tty); - - /* Schedule the next read */ - urb->dev = port->serial->dev; - result = usb_submit_urb(urb , GFP_ATOMIC); - if (result) - dev_dbg(&port->dev, "failed submitting read urb %d\n", result); -} - -/* get data from ring buffer and then write to usb bus */ -static void spcp8x5_send(struct usb_serial_port *port) -{ - int count, result; - struct spcp8x5_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - if (priv->write_urb_in_use) { - dev_dbg(&port->dev, "write urb still used\n"); - spin_unlock_irqrestore(&priv->lock, flags); + if (!urb->actual_length) return; - } - - /* send the 1st urb for writting */ - memset(port->write_urb->transfer_buffer , 0x00 , port->bulk_out_size); - count = get_ringbuf(priv->buf, port->write_urb->transfer_buffer, - port->bulk_out_size); - if (count == 0) { - spin_unlock_irqrestore(&priv->lock, flags); + tty = tty_port_tty_get(&port->port); + if (!tty) return; - } - - /* update the urb status */ - priv->write_urb_in_use = 1; - - spin_unlock_irqrestore(&priv->lock, flags); - - port->write_urb->transfer_buffer_length = count; - port->write_urb->dev = port->serial->dev; - - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) { - dev_dbg(&port->dev, "failed submitting write urb, error %d\n", - result); - priv->write_urb_in_use = 0; - /* TODO: reschedule spcp8x5_send */ - } - - - schedule_work(&port->work); -} -/* this is the call back function for write urb. NOTE we should not sleep in - * this routine. check the urb return code and then submit the write urb again - * to hold the write loop */ -static void spcp8x5_write_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - struct spcp8x5_private *priv = usb_get_serial_port_data(port); - int result; - int status = urb->status; + if (status & UART_STATE_TRANSIENT_MASK) { + /* break takes precedence over parity, which takes precedence + * over framing errors */ + if (status & UART_BREAK_ERROR) + tty_flag = TTY_BREAK; + else if (status & UART_PARITY_ERROR) + tty_flag = TTY_PARITY; + else if (status & UART_FRAME_ERROR) + tty_flag = TTY_FRAME; + dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); - switch (status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dev_dbg(&port->dev, "urb shutting down with status: %d\n", - status); - priv->write_urb_in_use = 0; - return; - default: - /* error in the urb, so we have to resubmit it */ - dbg("%s - Overflow in write", __func__); - dbg("%s - nonzero write bulk status received: %d", - __func__, status); - port->write_urb->transfer_buffer_length = 1; - port->write_urb->dev = port->serial->dev; - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) - dev_dbg(&port->dev, - "failed resubmitting write urb %d\n", result); - else - return; + /* overrun is special, not associated with a char */ + if (status & UART_OVERRUN_ERROR) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); } - priv->write_urb_in_use = 0; - - /* send any buffered data */ - spcp8x5_send(port); -} - -/* write data to ring buffer. and then start the write transfer */ -static int spcp8x5_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - struct spcp8x5_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - - dev_dbg(&port->dev, "%d bytes\n", count); - - if (!count) - return count; - - spin_lock_irqsave(&priv->lock, flags); - count = put_ringbuf(priv->buf, buf, count); - spin_unlock_irqrestore(&priv->lock, flags); - - spcp8x5_send(port); - - return count; + tty_insert_flip_string_fixed_flag(tty, data, tty_flag, + urb->actual_length); + tty_flip_buffer_push(tty); + tty_kref_put(tty); } static int spcp8x5_wait_modem_info(struct usb_serial_port *port, @@ -953,36 +639,6 @@ static int spcp8x5_tiocmget(struct tty_struct *tty, struct file *file) return result; } -/* get the avail space room in ring buffer */ -static int spcp8x5_write_room(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct spcp8x5_private *priv = usb_get_serial_port_data(port); - int room = 0; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - room = ringbuf_avail_space(priv->buf); - spin_unlock_irqrestore(&priv->lock, flags); - - return room; -} - -/* get the number of avail data in write ring buffer */ -static int spcp8x5_chars_in_buffer(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct spcp8x5_private *priv = usb_get_serial_port_data(port); - int chars = 0; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - chars = ringbuf_avail_data(priv->buf); - spin_unlock_irqrestore(&priv->lock, flags); - - return chars; -} - /* All of the device info needed for the spcp8x5 SIO serial converter */ static struct usb_serial_driver spcp8x5_device = { .driver = { @@ -992,21 +648,16 @@ static struct usb_serial_driver spcp8x5_device = { .id_table = id_table, .num_ports = 1, .open = spcp8x5_open, - .close = spcp8x5_close, .dtr_rts = spcp8x5_dtr_rts, .carrier_raised = spcp8x5_carrier_raised, - .write = spcp8x5_write, .set_termios = spcp8x5_set_termios, .init_termios = spcp8x5_init_termios, .ioctl = spcp8x5_ioctl, .tiocmget = spcp8x5_tiocmget, .tiocmset = spcp8x5_tiocmset, - .write_room = spcp8x5_write_room, - .read_bulk_callback = spcp8x5_read_bulk_callback, - .write_bulk_callback = spcp8x5_write_bulk_callback, - .chars_in_buffer = spcp8x5_chars_in_buffer, .attach = spcp8x5_startup, .release = spcp8x5_release, + .process_read_urb = spcp8x5_process_read_urb, }; static int __init spcp8x5_init(void) diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index e1bfda3..90979a1 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -30,7 +30,7 @@ #include <linux/spinlock.h> #include <linux/ioctl.h> #include <linux/serial.h> -#include <linux/circ_buf.h> +#include <linux/kfifo.h> #include <linux/mutex.h> #include <linux/uaccess.h> #include <linux/usb.h> @@ -40,7 +40,7 @@ /* Defines */ -#define TI_DRIVER_VERSION "v0.9" +#define TI_DRIVER_VERSION "v0.10" #define TI_DRIVER_AUTHOR "Al Borchers <alborchers@steinerpoint.com>" #define TI_DRIVER_DESC "TI USB 3410/5052 Serial Driver" @@ -82,7 +82,7 @@ struct ti_port { spinlock_t tp_lock; int tp_read_urb_state; int tp_write_urb_in_use; - struct circ_buf *tp_write_buf; + struct kfifo write_fifo; }; struct ti_device { @@ -144,15 +144,6 @@ static int ti_write_byte(struct ti_device *tdev, unsigned long addr, static int ti_download_firmware(struct ti_device *tdev); -/* circular buffer */ -static struct circ_buf *ti_buf_alloc(void); -static void ti_buf_free(struct circ_buf *cb); -static void ti_buf_clear(struct circ_buf *cb); -static int ti_buf_data_avail(struct circ_buf *cb); -static int ti_buf_space_avail(struct circ_buf *cb); -static int ti_buf_put(struct circ_buf *cb, const char *buf, int count); -static int ti_buf_get(struct circ_buf *cb, char *buf, int count); - /* Data */ @@ -450,8 +441,8 @@ static int ti_startup(struct usb_serial *serial) tport->tp_closing_wait = closing_wait; init_waitqueue_head(&tport->tp_msr_wait); init_waitqueue_head(&tport->tp_write_wait); - tport->tp_write_buf = ti_buf_alloc(); - if (tport->tp_write_buf == NULL) { + if (kfifo_alloc(&tport->write_fifo, TI_WRITE_BUF_SIZE, + GFP_KERNEL)) { dev_err(&dev->dev, "%s - out of memory\n", __func__); kfree(tport); status = -ENOMEM; @@ -468,7 +459,7 @@ static int ti_startup(struct usb_serial *serial) free_tports: for (--i; i >= 0; --i) { tport = usb_get_serial_port_data(serial->port[i]); - ti_buf_free(tport->tp_write_buf); + kfifo_free(&tport->write_fifo); kfree(tport); usb_set_serial_port_data(serial->port[i], NULL); } @@ -490,7 +481,7 @@ static void ti_release(struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { tport = usb_get_serial_port_data(serial->port[i]); if (tport) { - ti_buf_free(tport->tp_write_buf); + kfifo_free(&tport->write_fifo); kfree(tport); } } @@ -701,7 +692,6 @@ static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *data, int count) { struct ti_port *tport = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); @@ -713,10 +703,8 @@ static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, if (tport == NULL || !tport->tp_is_open) return -ENODEV; - spin_lock_irqsave(&tport->tp_lock, flags); - count = ti_buf_put(tport->tp_write_buf, data, count); - spin_unlock_irqrestore(&tport->tp_lock, flags); - + count = kfifo_in_locked(&tport->write_fifo, data, count, + &tport->tp_lock); ti_send(tport); return count; @@ -736,7 +724,7 @@ static int ti_write_room(struct tty_struct *tty) return 0; spin_lock_irqsave(&tport->tp_lock, flags); - room = ti_buf_space_avail(tport->tp_write_buf); + room = kfifo_avail(&tport->write_fifo); spin_unlock_irqrestore(&tport->tp_lock, flags); dbg("%s - returns %d", __func__, room); @@ -757,7 +745,7 @@ static int ti_chars_in_buffer(struct tty_struct *tty) return 0; spin_lock_irqsave(&tport->tp_lock, flags); - chars = ti_buf_data_avail(tport->tp_write_buf); + chars = kfifo_len(&tport->write_fifo); spin_unlock_irqrestore(&tport->tp_lock, flags); dbg("%s - returns %d", __func__, chars); @@ -1309,7 +1297,7 @@ static void ti_send(struct ti_port *tport) if (tport->tp_write_urb_in_use) goto unlock; - count = ti_buf_get(tport->tp_write_buf, + count = kfifo_out(&tport->write_fifo, port->write_urb->transfer_buffer, port->bulk_out_size); @@ -1504,7 +1492,7 @@ static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush) add_wait_queue(&tport->tp_write_wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); - if (ti_buf_data_avail(tport->tp_write_buf) == 0 + if (kfifo_len(&tport->write_fifo) == 0 || timeout == 0 || signal_pending(current) || tdev->td_urb_error || port->serial->disconnected) /* disconnect */ @@ -1518,7 +1506,7 @@ static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush) /* flush any remaining data in the buffer */ if (flush) - ti_buf_clear(tport->tp_write_buf); + kfifo_reset_out(&tport->write_fifo); spin_unlock_irq(&tport->tp_lock); @@ -1761,142 +1749,3 @@ static int ti_download_firmware(struct ti_device *tdev) return 0; } - - -/* Circular Buffer Functions */ - -/* - * ti_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ - -static struct circ_buf *ti_buf_alloc(void) -{ - struct circ_buf *cb; - - cb = kmalloc(sizeof(struct circ_buf), GFP_KERNEL); - if (cb == NULL) - return NULL; - - cb->buf = kmalloc(TI_WRITE_BUF_SIZE, GFP_KERNEL); - if (cb->buf == NULL) { - kfree(cb); - return NULL; - } - - ti_buf_clear(cb); - - return cb; -} - - -/* - * ti_buf_free - * - * Free the buffer and all associated memory. - */ - -static void ti_buf_free(struct circ_buf *cb) -{ - kfree(cb->buf); - kfree(cb); -} - - -/* - * ti_buf_clear - * - * Clear out all data in the circular buffer. - */ - -static void ti_buf_clear(struct circ_buf *cb) -{ - cb->head = cb->tail = 0; -} - - -/* - * ti_buf_data_avail - * - * Return the number of bytes of data available in the circular - * buffer. - */ - -static int ti_buf_data_avail(struct circ_buf *cb) -{ - return CIRC_CNT(cb->head, cb->tail, TI_WRITE_BUF_SIZE); -} - - -/* - * ti_buf_space_avail - * - * Return the number of bytes of space available in the circular - * buffer. - */ - -static int ti_buf_space_avail(struct circ_buf *cb) -{ - return CIRC_SPACE(cb->head, cb->tail, TI_WRITE_BUF_SIZE); -} - - -/* - * ti_buf_put - * - * Copy data data from a user buffer and put it into the circular buffer. - * Restrict to the amount of space available. - * - * Return the number of bytes copied. - */ - -static int ti_buf_put(struct circ_buf *cb, const char *buf, int count) -{ - int c, ret = 0; - - while (1) { - c = CIRC_SPACE_TO_END(cb->head, cb->tail, TI_WRITE_BUF_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - memcpy(cb->buf + cb->head, buf, c); - cb->head = (cb->head + c) & (TI_WRITE_BUF_SIZE-1); - buf += c; - count -= c; - ret += c; - } - - return ret; -} - - -/* - * ti_buf_get - * - * Get data from the circular buffer and copy to the given buffer. - * Restrict to the amount of data available. - * - * Return the number of bytes copied. - */ - -static int ti_buf_get(struct circ_buf *cb, char *buf, int count) -{ - int c, ret = 0; - - while (1) { - c = CIRC_CNT_TO_END(cb->head, cb->tail, TI_WRITE_BUF_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - memcpy(buf, cb->buf + cb->tail, c); - cb->tail = (cb->tail + c) & (TI_WRITE_BUF_SIZE-1); - buf += c; - count -= c; - ret += c; - } - - return ret; -} diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 3873660..941c2d4 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -289,7 +289,7 @@ static void serial_down(struct tty_port *tport) * The console is magical. Do not hang up the console hardware * or there will be tears. */ - if (port->console) + if (port->port.console) return; if (drv->close) drv->close(port); @@ -328,7 +328,7 @@ static void serial_cleanup(struct tty_struct *tty) /* The console is magical. Do not hang up the console hardware * or there will be tears. */ - if (port->console) + if (port->port.console) return; dbg("%s - port %d", __func__, port->number); @@ -548,8 +548,12 @@ static void usb_serial_port_work(struct work_struct *work) static void kill_traffic(struct usb_serial_port *port) { + int i; + usb_kill_urb(port->read_urb); usb_kill_urb(port->write_urb); + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) + usb_kill_urb(port->write_urbs[i]); /* * This is tricky. * Some drivers submit the read_urb in the @@ -568,6 +572,7 @@ static void kill_traffic(struct usb_serial_port *port) static void port_release(struct device *dev) { struct usb_serial_port *port = to_usb_serial_port(dev); + int i; dbg ("%s - %s", __func__, dev_name(dev)); @@ -582,6 +587,10 @@ static void port_release(struct device *dev) usb_free_urb(port->write_urb); usb_free_urb(port->interrupt_in_urb); usb_free_urb(port->interrupt_out_urb); + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) { + usb_free_urb(port->write_urbs[i]); + kfree(port->bulk_out_buffers[i]); + } kfifo_free(&port->write_fifo); kfree(port->bulk_in_buffer); kfree(port->bulk_out_buffer); @@ -901,7 +910,9 @@ int usb_serial_probe(struct usb_interface *interface, dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = serial->type->bulk_in_size; + if (!buffer_size) + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); @@ -918,6 +929,8 @@ int usb_serial_probe(struct usb_interface *interface, } for (i = 0; i < num_bulk_out; ++i) { + int j; + endpoint = bulk_out_endpoint[i]; port = serial->port[i]; port->write_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -927,7 +940,9 @@ int usb_serial_probe(struct usb_interface *interface, } if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL)) goto probe_error; - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = serial->type->bulk_out_size; + if (!buffer_size) + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); @@ -941,6 +956,28 @@ int usb_serial_probe(struct usb_interface *interface, endpoint->bEndpointAddress), port->bulk_out_buffer, buffer_size, serial->type->write_bulk_callback, port); + for (j = 0; j < ARRAY_SIZE(port->write_urbs); ++j) { + set_bit(j, &port->write_urbs_free); + port->write_urbs[j] = usb_alloc_urb(0, GFP_KERNEL); + if (!port->write_urbs[j]) { + dev_err(&interface->dev, + "No free urbs available\n"); + goto probe_error; + } + port->bulk_out_buffers[j] = kmalloc(buffer_size, + GFP_KERNEL); + if (!port->bulk_out_buffers[j]) { + dev_err(&interface->dev, + "Couldn't allocate bulk_out_buffer\n"); + goto probe_error; + } + usb_fill_bulk_urb(port->write_urbs[j], dev, + usb_sndbulkpipe(dev, + endpoint->bEndpointAddress), + port->bulk_out_buffers[j], buffer_size, + serial->type->write_bulk_callback, + port); + } } if (serial->type->read_int_callback) { @@ -1294,6 +1331,8 @@ static void fixup_generic(struct usb_serial_driver *device) set_to_generic_if_null(device, write_bulk_callback); set_to_generic_if_null(device, disconnect); set_to_generic_if_null(device, release); + set_to_generic_if_null(device, process_read_urb); + set_to_generic_if_null(device, prepare_write_buffer); } int usb_serial_register(struct usb_serial_driver *driver) diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h new file mode 100644 index 0000000..2be298a --- /dev/null +++ b/drivers/usb/serial/usb-wwan.h @@ -0,0 +1,67 @@ +/* + * Definitions for USB serial mobile broadband cards + */ + +#ifndef __LINUX_USB_USB_WWAN +#define __LINUX_USB_USB_WWAN + +extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on); +extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port); +extern void usb_wwan_close(struct usb_serial_port *port); +extern int usb_wwan_startup(struct usb_serial *serial); +extern void usb_wwan_disconnect(struct usb_serial *serial); +extern void usb_wwan_release(struct usb_serial *serial); +extern int usb_wwan_write_room(struct tty_struct *tty); +extern void usb_wwan_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old); +extern int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file); +extern int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); +extern int usb_wwan_send_setup(struct usb_serial_port *port); +extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +extern int usb_wwan_chars_in_buffer(struct tty_struct *tty); +#ifdef CONFIG_PM +extern int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message); +extern int usb_wwan_resume(struct usb_serial *serial); +#endif + +/* per port private data */ + +#define N_IN_URB 4 +#define N_OUT_URB 4 +#define IN_BUFLEN 4096 +#define OUT_BUFLEN 4096 + +struct usb_wwan_intf_private { + spinlock_t susp_lock; + unsigned int suspended:1; + int in_flight; + int (*send_setup) (struct usb_serial_port *port); + void *private; +}; + +struct usb_wwan_port_private { + /* Input endpoints and buffer for this port */ + struct urb *in_urbs[N_IN_URB]; + u8 *in_buffer[N_IN_URB]; + /* Output endpoints and buffer for this port */ + struct urb *out_urbs[N_OUT_URB]; + u8 *out_buffer[N_OUT_URB]; + unsigned long out_busy; /* Bit vector of URBs in use */ + int opened; + struct usb_anchor delayed; + + /* Settings for the port */ + int rts_state; /* Handshaking pins (outputs) */ + int dtr_state; + int cts_state; /* Handshaking pins (inputs) */ + int dsr_state; + int dcd_state; + int ri_state; + + unsigned long tx_start_time[N_OUT_URB]; +}; + +#endif /* __LINUX_USB_USB_WWAN */ diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index 28026b4..f2ed6a3 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -16,7 +16,6 @@ #include <linux/usb.h> #include <linux/usb/serial.h> -#define URB_DEBUG_MAX_IN_FLIGHT_URBS 4000 #define USB_DEBUG_MAX_PACKET_SIZE 8 #define USB_DEBUG_BRK_SIZE 8 static char USB_DEBUG_BRK[USB_DEBUG_BRK_SIZE] = { @@ -44,12 +43,6 @@ static struct usb_driver debug_driver = { .no_dynamic_id = 1, }; -static int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port) -{ - port->bulk_out_size = USB_DEBUG_MAX_PACKET_SIZE; - return usb_serial_generic_open(tty, port); -} - /* This HW really does not support a serial break, so one will be * emulated when ever the break state is set to true. */ @@ -69,7 +62,7 @@ static void usb_debug_read_bulk_callback(struct urb *urb) memcmp(urb->transfer_buffer, USB_DEBUG_BRK, USB_DEBUG_BRK_SIZE) == 0) { usb_serial_handle_break(port); - usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC); + usb_serial_generic_submit_read_urb(port, GFP_ATOMIC); return; } @@ -83,8 +76,7 @@ static struct usb_serial_driver debug_device = { }, .id_table = id_table, .num_ports = 1, - .open = usb_debug_open, - .max_in_flight_urbs = URB_DEBUG_MAX_IN_FLIGHT_URBS, + .bulk_out_size = USB_DEBUG_MAX_PACKET_SIZE, .break_ctl = usb_debug_break_ctl, .read_bulk_callback = usb_debug_read_bulk_callback, }; diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c new file mode 100644 index 0000000..0c70b4a --- /dev/null +++ b/drivers/usb/serial/usb_wwan.c @@ -0,0 +1,665 @@ +/* + USB Driver layer for GSM modems + + Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de> + + This driver is free software; you can redistribute it and/or modify + it under the terms of Version 2 of the GNU General Public License as + published by the Free Software Foundation. + + Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org> + + History: see the git log. + + Work sponsored by: Sigos GmbH, Germany <info@sigos.de> + + This driver exists because the "normal" serial driver doesn't work too well + with GSM modems. Issues: + - data loss -- one single Receive URB is not nearly enough + - controlling the baud rate doesn't make sense +*/ + +#define DRIVER_VERSION "v0.7.2" +#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>" +#define DRIVER_DESC "USB Driver for GSM modems" + +#include <linux/kernel.h> +#include <linux/jiffies.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/module.h> +#include <linux/bitops.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> +#include "usb-wwan.h" + +static int debug; + +void usb_wwan_dtr_rts(struct usb_serial_port *port, int on) +{ + struct usb_serial *serial = port->serial; + struct usb_wwan_port_private *portdata; + + struct usb_wwan_intf_private *intfdata; + + dbg("%s", __func__); + + intfdata = port->serial->private; + + if (!intfdata->send_setup) + return; + + portdata = usb_get_serial_port_data(port); + mutex_lock(&serial->disc_mutex); + portdata->rts_state = on; + portdata->dtr_state = on; + if (serial->dev) + intfdata->send_setup(port); + mutex_unlock(&serial->disc_mutex); +} +EXPORT_SYMBOL(usb_wwan_dtr_rts); + +void usb_wwan_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios) +{ + struct usb_wwan_intf_private *intfdata = port->serial->private; + + dbg("%s", __func__); + + /* Doesn't support option setting */ + tty_termios_copy_hw(tty->termios, old_termios); + + if (intfdata->send_setup) + intfdata->send_setup(port); +} +EXPORT_SYMBOL(usb_wwan_set_termios); + +int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct usb_serial_port *port = tty->driver_data; + unsigned int value; + struct usb_wwan_port_private *portdata; + + portdata = usb_get_serial_port_data(port); + + value = ((portdata->rts_state) ? TIOCM_RTS : 0) | + ((portdata->dtr_state) ? TIOCM_DTR : 0) | + ((portdata->cts_state) ? TIOCM_CTS : 0) | + ((portdata->dsr_state) ? TIOCM_DSR : 0) | + ((portdata->dcd_state) ? TIOCM_CAR : 0) | + ((portdata->ri_state) ? TIOCM_RNG : 0); + + return value; +} +EXPORT_SYMBOL(usb_wwan_tiocmget); + +int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct usb_serial_port *port = tty->driver_data; + struct usb_wwan_port_private *portdata; + struct usb_wwan_intf_private *intfdata; + + portdata = usb_get_serial_port_data(port); + intfdata = port->serial->private; + + if (!intfdata->send_setup) + return -EINVAL; + + /* FIXME: what locks portdata fields ? */ + if (set & TIOCM_RTS) + portdata->rts_state = 1; + if (set & TIOCM_DTR) + portdata->dtr_state = 1; + + if (clear & TIOCM_RTS) + portdata->rts_state = 0; + if (clear & TIOCM_DTR) + portdata->dtr_state = 0; + return intfdata->send_setup(port); +} +EXPORT_SYMBOL(usb_wwan_tiocmset); + +/* Write */ +int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) +{ + struct usb_wwan_port_private *portdata; + struct usb_wwan_intf_private *intfdata; + int i; + int left, todo; + struct urb *this_urb = NULL; /* spurious */ + int err; + unsigned long flags; + + portdata = usb_get_serial_port_data(port); + intfdata = port->serial->private; + + dbg("%s: write (%d chars)", __func__, count); + + i = 0; + left = count; + for (i = 0; left > 0 && i < N_OUT_URB; i++) { + todo = left; + if (todo > OUT_BUFLEN) + todo = OUT_BUFLEN; + + this_urb = portdata->out_urbs[i]; + if (test_and_set_bit(i, &portdata->out_busy)) { + if (time_before(jiffies, + portdata->tx_start_time[i] + 10 * HZ)) + continue; + usb_unlink_urb(this_urb); + continue; + } + dbg("%s: endpoint %d buf %d", __func__, + usb_pipeendpoint(this_urb->pipe), i); + + err = usb_autopm_get_interface_async(port->serial->interface); + if (err < 0) + break; + + /* send the data */ + memcpy(this_urb->transfer_buffer, buf, todo); + this_urb->transfer_buffer_length = todo; + + spin_lock_irqsave(&intfdata->susp_lock, flags); + if (intfdata->suspended) { + usb_anchor_urb(this_urb, &portdata->delayed); + spin_unlock_irqrestore(&intfdata->susp_lock, flags); + } else { + intfdata->in_flight++; + spin_unlock_irqrestore(&intfdata->susp_lock, flags); + err = usb_submit_urb(this_urb, GFP_ATOMIC); + if (err) { + dbg("usb_submit_urb %p (write bulk) failed " + "(%d)", this_urb, err); + clear_bit(i, &portdata->out_busy); + spin_lock_irqsave(&intfdata->susp_lock, flags); + intfdata->in_flight--; + spin_unlock_irqrestore(&intfdata->susp_lock, + flags); + continue; + } + } + + portdata->tx_start_time[i] = jiffies; + buf += todo; + left -= todo; + } + + count -= left; + dbg("%s: wrote (did %d)", __func__, count); + return count; +} +EXPORT_SYMBOL(usb_wwan_write); + +static void usb_wwan_indat_callback(struct urb *urb) +{ + int err; + int endpoint; + struct usb_serial_port *port; + struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; + int status = urb->status; + + dbg("%s: %p", __func__, urb); + + endpoint = usb_pipeendpoint(urb->pipe); + port = urb->context; + + if (status) { + dbg("%s: nonzero status: %d on endpoint %02x.", + __func__, status, endpoint); + } else { + tty = tty_port_tty_get(&port->port); + if (urb->actual_length) { + tty_insert_flip_string(tty, data, urb->actual_length); + tty_flip_buffer_push(tty); + } else + dbg("%s: empty read urb received", __func__); + tty_kref_put(tty); + + /* Resubmit urb so we continue receiving */ + if (status != -ESHUTDOWN) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err && err != -EPERM) + printk(KERN_ERR "%s: resubmit read urb failed. " + "(%d)", __func__, err); + else + usb_mark_last_busy(port->serial->dev); + } + + } + return; +} + +static void usb_wwan_outdat_callback(struct urb *urb) +{ + struct usb_serial_port *port; + struct usb_wwan_port_private *portdata; + struct usb_wwan_intf_private *intfdata; + int i; + + dbg("%s", __func__); + + port = urb->context; + intfdata = port->serial->private; + + usb_serial_port_softint(port); + usb_autopm_put_interface_async(port->serial->interface); + portdata = usb_get_serial_port_data(port); + spin_lock(&intfdata->susp_lock); + intfdata->in_flight--; + spin_unlock(&intfdata->susp_lock); + + for (i = 0; i < N_OUT_URB; ++i) { + if (portdata->out_urbs[i] == urb) { + smp_mb__before_clear_bit(); + clear_bit(i, &portdata->out_busy); + break; + } + } +} + +int usb_wwan_write_room(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + struct usb_wwan_port_private *portdata; + int i; + int data_len = 0; + struct urb *this_urb; + + portdata = usb_get_serial_port_data(port); + + for (i = 0; i < N_OUT_URB; i++) { + this_urb = portdata->out_urbs[i]; + if (this_urb && !test_bit(i, &portdata->out_busy)) + data_len += OUT_BUFLEN; + } + + dbg("%s: %d", __func__, data_len); + return data_len; +} +EXPORT_SYMBOL(usb_wwan_write_room); + +int usb_wwan_chars_in_buffer(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + struct usb_wwan_port_private *portdata; + int i; + int data_len = 0; + struct urb *this_urb; + + portdata = usb_get_serial_port_data(port); + + for (i = 0; i < N_OUT_URB; i++) { + this_urb = portdata->out_urbs[i]; + /* FIXME: This locking is insufficient as this_urb may + go unused during the test */ + if (this_urb && test_bit(i, &portdata->out_busy)) + data_len += this_urb->transfer_buffer_length; + } + dbg("%s: %d", __func__, data_len); + return data_len; +} +EXPORT_SYMBOL(usb_wwan_chars_in_buffer); + +int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port) +{ + struct usb_wwan_port_private *portdata; + struct usb_wwan_intf_private *intfdata; + struct usb_serial *serial = port->serial; + int i, err; + struct urb *urb; + + portdata = usb_get_serial_port_data(port); + intfdata = serial->private; + + dbg("%s", __func__); + + /* Start reading from the IN endpoint */ + for (i = 0; i < N_IN_URB; i++) { + urb = portdata->in_urbs[i]; + if (!urb) + continue; + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + dbg("%s: submit urb %d failed (%d) %d", + __func__, i, err, urb->transfer_buffer_length); + } + } + + if (intfdata->send_setup) + intfdata->send_setup(port); + + serial->interface->needs_remote_wakeup = 1; + spin_lock_irq(&intfdata->susp_lock); + portdata->opened = 1; + spin_unlock_irq(&intfdata->susp_lock); + usb_autopm_put_interface(serial->interface); + + return 0; +} +EXPORT_SYMBOL(usb_wwan_open); + +void usb_wwan_close(struct usb_serial_port *port) +{ + int i; + struct usb_serial *serial = port->serial; + struct usb_wwan_port_private *portdata; + struct usb_wwan_intf_private *intfdata = port->serial->private; + + dbg("%s", __func__); + portdata = usb_get_serial_port_data(port); + + if (serial->dev) { + /* Stop reading/writing urbs */ + spin_lock_irq(&intfdata->susp_lock); + portdata->opened = 0; + spin_unlock_irq(&intfdata->susp_lock); + + for (i = 0; i < N_IN_URB; i++) + usb_kill_urb(portdata->in_urbs[i]); + for (i = 0; i < N_OUT_URB; i++) + usb_kill_urb(portdata->out_urbs[i]); + usb_autopm_get_interface(serial->interface); + serial->interface->needs_remote_wakeup = 0; + } +} +EXPORT_SYMBOL(usb_wwan_close); + +/* Helper functions used by usb_wwan_setup_urbs */ +static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint, + int dir, void *ctx, char *buf, int len, + void (*callback) (struct urb *)) +{ + struct urb *urb; + + if (endpoint == -1) + return NULL; /* endpoint not needed */ + + urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ + if (urb == NULL) { + dbg("%s: alloc for endpoint %d failed.", __func__, endpoint); + return NULL; + } + + /* Fill URB using supplied data. */ + usb_fill_bulk_urb(urb, serial->dev, + usb_sndbulkpipe(serial->dev, endpoint) | dir, + buf, len, callback, ctx); + + return urb; +} + +/* Setup urbs */ +static void usb_wwan_setup_urbs(struct usb_serial *serial) +{ + int i, j; + struct usb_serial_port *port; + struct usb_wwan_port_private *portdata; + + dbg("%s", __func__); + + for (i = 0; i < serial->num_ports; i++) { + port = serial->port[i]; + portdata = usb_get_serial_port_data(port); + + /* Do indat endpoints first */ + for (j = 0; j < N_IN_URB; ++j) { + portdata->in_urbs[j] = usb_wwan_setup_urb(serial, + port-> + bulk_in_endpointAddress, + USB_DIR_IN, + port, + portdata-> + in_buffer[j], + IN_BUFLEN, + usb_wwan_indat_callback); + } + + /* outdat endpoints */ + for (j = 0; j < N_OUT_URB; ++j) { + portdata->out_urbs[j] = usb_wwan_setup_urb(serial, + port-> + bulk_out_endpointAddress, + USB_DIR_OUT, + port, + portdata-> + out_buffer + [j], + OUT_BUFLEN, + usb_wwan_outdat_callback); + } + } +} + +int usb_wwan_startup(struct usb_serial *serial) +{ + int i, j, err; + struct usb_serial_port *port; + struct usb_wwan_port_private *portdata; + u8 *buffer; + + dbg("%s", __func__); + + /* Now setup per port private data */ + for (i = 0; i < serial->num_ports; i++) { + port = serial->port[i]; + portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); + if (!portdata) { + dbg("%s: kmalloc for usb_wwan_port_private (%d) failed!.", + __func__, i); + return 1; + } + init_usb_anchor(&portdata->delayed); + + for (j = 0; j < N_IN_URB; j++) { + buffer = (u8 *) __get_free_page(GFP_KERNEL); + if (!buffer) + goto bail_out_error; + portdata->in_buffer[j] = buffer; + } + + for (j = 0; j < N_OUT_URB; j++) { + buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); + if (!buffer) + goto bail_out_error2; + portdata->out_buffer[j] = buffer; + } + + usb_set_serial_port_data(port, portdata); + + if (!port->interrupt_in_urb) + continue; + err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (err) + dbg("%s: submit irq_in urb failed %d", __func__, err); + } + usb_wwan_setup_urbs(serial); + return 0; + +bail_out_error2: + for (j = 0; j < N_OUT_URB; j++) + kfree(portdata->out_buffer[j]); +bail_out_error: + for (j = 0; j < N_IN_URB; j++) + if (portdata->in_buffer[j]) + free_page((unsigned long)portdata->in_buffer[j]); + kfree(portdata); + return 1; +} +EXPORT_SYMBOL(usb_wwan_startup); + +static void stop_read_write_urbs(struct usb_serial *serial) +{ + int i, j; + struct usb_serial_port *port; + struct usb_wwan_port_private *portdata; + + /* Stop reading/writing urbs */ + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; + portdata = usb_get_serial_port_data(port); + for (j = 0; j < N_IN_URB; j++) + usb_kill_urb(portdata->in_urbs[j]); + for (j = 0; j < N_OUT_URB; j++) + usb_kill_urb(portdata->out_urbs[j]); + } +} + +void usb_wwan_disconnect(struct usb_serial *serial) +{ + dbg("%s", __func__); + + stop_read_write_urbs(serial); +} +EXPORT_SYMBOL(usb_wwan_disconnect); + +void usb_wwan_release(struct usb_serial *serial) +{ + int i, j; + struct usb_serial_port *port; + struct usb_wwan_port_private *portdata; + + dbg("%s", __func__); + + /* Now free them */ + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; + portdata = usb_get_serial_port_data(port); + + for (j = 0; j < N_IN_URB; j++) { + usb_free_urb(portdata->in_urbs[j]); + free_page((unsigned long) + portdata->in_buffer[j]); + portdata->in_urbs[j] = NULL; + } + for (j = 0; j < N_OUT_URB; j++) { + usb_free_urb(portdata->out_urbs[j]); + kfree(portdata->out_buffer[j]); + portdata->out_urbs[j] = NULL; + } + } + + /* Now free per port private data */ + for (i = 0; i < serial->num_ports; i++) { + port = serial->port[i]; + kfree(usb_get_serial_port_data(port)); + } +} +EXPORT_SYMBOL(usb_wwan_release); + +#ifdef CONFIG_PM +int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message) +{ + struct usb_wwan_intf_private *intfdata = serial->private; + int b; + + dbg("%s entered", __func__); + + if (message.event & PM_EVENT_AUTO) { + spin_lock_irq(&intfdata->susp_lock); + b = intfdata->in_flight; + spin_unlock_irq(&intfdata->susp_lock); + + if (b) + return -EBUSY; + } + + spin_lock_irq(&intfdata->susp_lock); + intfdata->suspended = 1; + spin_unlock_irq(&intfdata->susp_lock); + stop_read_write_urbs(serial); + + return 0; +} +EXPORT_SYMBOL(usb_wwan_suspend); + +static void play_delayed(struct usb_serial_port *port) +{ + struct usb_wwan_intf_private *data; + struct usb_wwan_port_private *portdata; + struct urb *urb; + int err; + + portdata = usb_get_serial_port_data(port); + data = port->serial->private; + while ((urb = usb_get_from_anchor(&portdata->delayed))) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (!err) + data->in_flight++; + } +} + +int usb_wwan_resume(struct usb_serial *serial) +{ + int i, j; + struct usb_serial_port *port; + struct usb_wwan_intf_private *intfdata = serial->private; + struct usb_wwan_port_private *portdata; + struct urb *urb; + int err = 0; + + dbg("%s entered", __func__); + /* get the interrupt URBs resubmitted unconditionally */ + for (i = 0; i < serial->num_ports; i++) { + port = serial->port[i]; + if (!port->interrupt_in_urb) { + dbg("%s: No interrupt URB for port %d", __func__, i); + continue; + } + err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); + dbg("Submitted interrupt URB for port %d (result %d)", i, err); + if (err < 0) { + err("%s: Error %d for interrupt URB of port%d", + __func__, err, i); + goto err_out; + } + } + + for (i = 0; i < serial->num_ports; i++) { + /* walk all ports */ + port = serial->port[i]; + portdata = usb_get_serial_port_data(port); + + /* skip closed ports */ + spin_lock_irq(&intfdata->susp_lock); + if (!portdata->opened) { + spin_unlock_irq(&intfdata->susp_lock); + continue; + } + + for (j = 0; j < N_IN_URB; j++) { + urb = portdata->in_urbs[j]; + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + err("%s: Error %d for bulk URB %d", + __func__, err, i); + spin_unlock_irq(&intfdata->susp_lock); + goto err_out; + } + } + play_delayed(port); + spin_unlock_irq(&intfdata->susp_lock); + } + spin_lock_irq(&intfdata->susp_lock); + intfdata->suspended = 0; + spin_unlock_irq(&intfdata->susp_lock); +err_out: + return err; +} +EXPORT_SYMBOL(usb_wwan_resume); +#endif + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug messages"); diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 0949427..eb76aae 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -38,17 +38,9 @@ /* function prototypes for a handspring visor */ static int visor_open(struct tty_struct *tty, struct usb_serial_port *port); static void visor_close(struct usb_serial_port *port); -static int visor_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count); -static int visor_write_room(struct tty_struct *tty); -static void visor_throttle(struct tty_struct *tty); -static void visor_unthrottle(struct tty_struct *tty); static int visor_probe(struct usb_serial *serial, const struct usb_device_id *id); static int visor_calc_num_ports(struct usb_serial *serial); -static void visor_release(struct usb_serial *serial); -static void visor_write_bulk_callback(struct urb *urb); -static void visor_read_bulk_callback(struct urb *urb); static void visor_read_int_callback(struct urb *urb); static int clie_3_5_startup(struct usb_serial *serial); static int treo_attach(struct usb_serial *serial); @@ -194,18 +186,14 @@ static struct usb_serial_driver handspring_device = { .usb_driver = &visor_driver, .id_table = id_table, .num_ports = 2, + .bulk_out_size = 256, .open = visor_open, .close = visor_close, - .throttle = visor_throttle, - .unthrottle = visor_unthrottle, + .throttle = usb_serial_generic_throttle, + .unthrottle = usb_serial_generic_unthrottle, .attach = treo_attach, .probe = visor_probe, .calc_num_ports = visor_calc_num_ports, - .release = visor_release, - .write = visor_write, - .write_room = visor_write_room, - .write_bulk_callback = visor_write_bulk_callback, - .read_bulk_callback = visor_read_bulk_callback, .read_int_callback = visor_read_int_callback, }; @@ -219,18 +207,14 @@ static struct usb_serial_driver clie_5_device = { .usb_driver = &visor_driver, .id_table = clie_id_5_table, .num_ports = 2, + .bulk_out_size = 256, .open = visor_open, .close = visor_close, - .throttle = visor_throttle, - .unthrottle = visor_unthrottle, + .throttle = usb_serial_generic_throttle, + .unthrottle = usb_serial_generic_unthrottle, .attach = clie_5_attach, .probe = visor_probe, .calc_num_ports = visor_calc_num_ports, - .release = visor_release, - .write = visor_write, - .write_room = visor_write_room, - .write_bulk_callback = visor_write_bulk_callback, - .read_bulk_callback = visor_read_bulk_callback, .read_int_callback = visor_read_int_callback, }; @@ -244,39 +228,19 @@ static struct usb_serial_driver clie_3_5_device = { .usb_driver = &visor_driver, .id_table = clie_id_3_5_table, .num_ports = 1, + .bulk_out_size = 256, .open = visor_open, .close = visor_close, - .throttle = visor_throttle, - .unthrottle = visor_unthrottle, + .throttle = usb_serial_generic_throttle, + .unthrottle = usb_serial_generic_unthrottle, .attach = clie_3_5_startup, - .write = visor_write, - .write_room = visor_write_room, - .write_bulk_callback = visor_write_bulk_callback, - .read_bulk_callback = visor_read_bulk_callback, }; -struct visor_private { - spinlock_t lock; - int bytes_in; - int bytes_out; - int outstanding_urbs; - unsigned char throttled; - unsigned char actually_throttled; -}; - -/* number of outstanding urbs to prevent userspace DoS from happening */ -#define URB_UPPER_LIMIT 42 - -static int stats; - /****************************************************************************** * Handspring Visor specific driver functions ******************************************************************************/ static int visor_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; - struct visor_private *priv = usb_get_serial_port_data(port); - unsigned long flags; int result = 0; dbg("%s - port %d", __func__, port->number); @@ -287,26 +251,10 @@ static int visor_open(struct tty_struct *tty, struct usb_serial_port *port) return -ENODEV; } - spin_lock_irqsave(&priv->lock, flags); - priv->bytes_in = 0; - priv->bytes_out = 0; - priv->throttled = 0; - spin_unlock_irqrestore(&priv->lock, flags); - /* Start reading from the device */ - usb_fill_bulk_urb(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - visor_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) { - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, result); + result = usb_serial_generic_open(tty, port); + if (result) goto exit; - } if (port->interrupt_in_urb) { dbg("%s - adding interrupt input for treo", __func__); @@ -323,13 +271,12 @@ exit: static void visor_close(struct usb_serial_port *port) { - struct visor_private *priv = usb_get_serial_port_data(port); unsigned char *transfer_buffer; dbg("%s - port %d", __func__, port->number); /* shutdown our urbs */ - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); usb_kill_urb(port->interrupt_in_urb); mutex_lock(&port->serial->disc_mutex); @@ -346,192 +293,6 @@ static void visor_close(struct usb_serial_port *port) } } mutex_unlock(&port->serial->disc_mutex); - - if (stats) - dev_info(&port->dev, "Bytes In = %d Bytes Out = %d\n", - priv->bytes_in, priv->bytes_out); -} - - -static int visor_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - struct visor_private *priv = usb_get_serial_port_data(port); - struct usb_serial *serial = port->serial; - struct urb *urb; - unsigned char *buffer; - unsigned long flags; - int status; - - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&priv->lock, flags); - if (priv->outstanding_urbs > URB_UPPER_LIMIT) { - spin_unlock_irqrestore(&priv->lock, flags); - dbg("%s - write limit hit", __func__); - return 0; - } - priv->outstanding_urbs++; - spin_unlock_irqrestore(&priv->lock, flags); - - buffer = kmalloc(count, GFP_ATOMIC); - if (!buffer) { - dev_err(&port->dev, "out of memory\n"); - count = -ENOMEM; - goto error_no_buffer; - } - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - dev_err(&port->dev, "no more free urbs\n"); - count = -ENOMEM; - goto error_no_urb; - } - - memcpy(buffer, buf, count); - - usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); - - usb_fill_bulk_urb(urb, serial->dev, - usb_sndbulkpipe(serial->dev, - port->bulk_out_endpointAddress), - buffer, count, - visor_write_bulk_callback, port); - - /* send it down the pipe */ - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - dev_err(&port->dev, - "%s - usb_submit_urb(write bulk) failed with status = %d\n", - __func__, status); - count = status; - goto error; - } else { - spin_lock_irqsave(&priv->lock, flags); - priv->bytes_out += count; - spin_unlock_irqrestore(&priv->lock, flags); - } - - /* we are done with this urb, so let the host driver - * really free it when it is finished with it */ - usb_free_urb(urb); - - return count; -error: - usb_free_urb(urb); -error_no_urb: - kfree(buffer); -error_no_buffer: - spin_lock_irqsave(&priv->lock, flags); - --priv->outstanding_urbs; - spin_unlock_irqrestore(&priv->lock, flags); - return count; -} - - -static int visor_write_room(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct visor_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - - dbg("%s - port %d", __func__, port->number); - - /* - * We really can take anything the user throws at us - * but let's pick a nice big number to tell the tty - * layer that we have lots of free space, unless we don't. - */ - - spin_lock_irqsave(&priv->lock, flags); - if (priv->outstanding_urbs > URB_UPPER_LIMIT * 2 / 3) { - spin_unlock_irqrestore(&priv->lock, flags); - dbg("%s - write limit hit", __func__); - return 0; - } - spin_unlock_irqrestore(&priv->lock, flags); - - return 2048; -} - - -static void visor_write_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - struct visor_private *priv = usb_get_serial_port_data(port); - int status = urb->status; - unsigned long flags; - - /* free up the transfer buffer, as usb_free_urb() does not do this */ - kfree(urb->transfer_buffer); - - dbg("%s - port %d", __func__, port->number); - - if (status) - dbg("%s - nonzero write bulk status received: %d", - __func__, status); - - spin_lock_irqsave(&priv->lock, flags); - --priv->outstanding_urbs; - spin_unlock_irqrestore(&priv->lock, flags); - - usb_serial_port_softint(port); -} - - -static void visor_read_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - struct visor_private *priv = usb_get_serial_port_data(port); - unsigned char *data = urb->transfer_buffer; - int status = urb->status; - struct tty_struct *tty; - int result; - int available_room = 0; - - dbg("%s - port %d", __func__, port->number); - - if (status) { - dbg("%s - nonzero read bulk status received: %d", - __func__, status); - return; - } - - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - - if (urb->actual_length) { - tty = tty_port_tty_get(&port->port); - if (tty) { - tty_insert_flip_string(tty, data, - urb->actual_length); - tty_flip_buffer_push(tty); - tty_kref_put(tty); - } - spin_lock(&priv->lock); - if (tty) - priv->bytes_in += available_room; - - } else { - spin_lock(&priv->lock); - } - - /* Continue trying to always read if we should */ - if (!priv->throttled) { - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - visor_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); - } else - priv->actually_throttled = 1; - spin_unlock(&priv->lock); } static void visor_read_int_callback(struct urb *urb) @@ -575,41 +336,6 @@ exit: __func__, result); } -static void visor_throttle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct visor_private *priv = usb_get_serial_port_data(port); - - dbg("%s - port %d", __func__, port->number); - spin_lock_irq(&priv->lock); - priv->throttled = 1; - spin_unlock_irq(&priv->lock); -} - - -static void visor_unthrottle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct visor_private *priv = usb_get_serial_port_data(port); - int result, was_throttled; - - dbg("%s - port %d", __func__, port->number); - spin_lock_irq(&priv->lock); - priv->throttled = 0; - was_throttled = priv->actually_throttled; - priv->actually_throttled = 0; - spin_unlock_irq(&priv->lock); - - if (was_throttled) { - port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, result); - } -} - static int palm_os_3_probe(struct usb_serial *serial, const struct usb_device_id *id) { @@ -777,28 +503,6 @@ static int visor_calc_num_ports(struct usb_serial *serial) return num_ports; } -static int generic_startup(struct usb_serial *serial) -{ - struct usb_serial_port **ports = serial->port; - struct visor_private *priv; - int i; - - for (i = 0; i < serial->num_ports; ++i) { - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - while (i-- != 0) { - priv = usb_get_serial_port_data(ports[i]); - usb_set_serial_port_data(ports[i], NULL); - kfree(priv); - } - return -ENOMEM; - } - spin_lock_init(&priv->lock); - usb_set_serial_port_data(ports[i], priv); - } - return 0; -} - static int clie_3_5_startup(struct usb_serial *serial) { struct device *dev = &serial->dev->dev; @@ -849,7 +553,7 @@ static int clie_3_5_startup(struct usb_serial *serial) goto out; } - result = generic_startup(serial); + result = 0; out: kfree(data); @@ -867,7 +571,7 @@ static int treo_attach(struct usb_serial *serial) (le16_to_cpu(serial->dev->descriptor.idVendor) == KYOCERA_VENDOR_ID)) || (serial->num_interrupt_in == 0)) - goto generic_startup; + return 0; dbg("%s", __func__); @@ -897,8 +601,7 @@ static int treo_attach(struct usb_serial *serial) COPY_PORT(serial->port[1], swap_port); kfree(swap_port); -generic_startup: - return generic_startup(serial); + return 0; } static int clie_5_attach(struct usb_serial *serial) @@ -921,20 +624,7 @@ static int clie_5_attach(struct usb_serial *serial) serial->port[0]->bulk_out_endpointAddress = serial->port[1]->bulk_out_endpointAddress; - return generic_startup(serial); -} - -static void visor_release(struct usb_serial *serial) -{ - struct visor_private *priv; - int i; - - dbg("%s", __func__); - - for (i = 0; i < serial->num_ports; i++) { - priv = usb_get_serial_port_data(serial->port[i]); - kfree(priv); - } + return 0; } static int __init visor_init(void) @@ -1018,8 +708,6 @@ MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug enabled or not"); -module_param(stats, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(stats, "Enables statistics or not"); module_param(vendor, ushort, 0); MODULE_PARM_DESC(vendor, "User specified vendor ID"); diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h index 57229cf..88db4d0 100644 --- a/drivers/usb/serial/visor.h +++ b/drivers/usb/serial/visor.h @@ -9,8 +9,9 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * See Documentation/usb/usb-serial.txt for more information on using this driver - * + * See Documentation/usb/usb-serial.txt for more information on using this + * driver. + * */ #ifndef __LINUX_USB_SERIAL_VISOR_H @@ -65,7 +66,7 @@ #define ACEECA_MEZ1000_ID 0x0001 #define KYOCERA_VENDOR_ID 0x0C88 -#define KYOCERA_7135_ID 0x0021 +#define KYOCERA_7135_ID 0x0021 #define FOSSIL_VENDOR_ID 0x0E67 #define FOSSIL_ABACUS_ID 0x0002 @@ -145,7 +146,7 @@ struct visor_connection_info { * The maximum number of connections currently supported is 2 */ struct palm_ext_connection_info { - __u8 num_ports; + __u8 num_ports; __u8 endpoint_numbers_different; __le16 reserved1; struct { diff --git a/drivers/usb/serial/zio.c b/drivers/usb/serial/zio.c new file mode 100644 index 0000000..f579672 --- /dev/null +++ b/drivers/usb/serial/zio.c @@ -0,0 +1,64 @@ +/* + * ZIO Motherboard USB driver + * + * Copyright (C) 2010 Zilogic Systems <code@zilogic.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> +#include <linux/uaccess.h> + +static const struct usb_device_id id_table[] = { + { USB_DEVICE(0x1CBE, 0x0103) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static struct usb_driver zio_driver = { + .name = "zio", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, + .no_dynamic_id = 1, +}; + +static struct usb_serial_driver zio_device = { + .driver = { + .owner = THIS_MODULE, + .name = "zio", + }, + .id_table = id_table, + .usb_driver = &zio_driver, + .num_ports = 1, +}; + +static int __init zio_init(void) +{ + int retval; + + retval = usb_serial_register(&zio_device); + if (retval) + return retval; + retval = usb_register(&zio_driver); + if (retval) + usb_serial_deregister(&zio_device); + return retval; +} + +static void __exit zio_exit(void) +{ + usb_deregister(&zio_driver); + usb_serial_deregister(&zio_device); +} + +module_init(zio_init); +module_exit(zio_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index fdba2f6..e9cbc14 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -490,13 +490,13 @@ static int isd200_action( struct us_data *us, int action, void* pointer, int value ) { union ata_cdb ata; - struct scsi_device srb_dev; + /* static to prevent this large struct being placed on the valuable stack */ + static struct scsi_device srb_dev; struct isd200_info *info = (struct isd200_info *)us->extra; struct scsi_cmnd *srb = &info->srb; int status; memset(&ata, 0, sizeof(ata)); - memset(&srb_dev, 0, sizeof(srb_dev)); srb->cmnd = info->cmnd; srb->device = &srb_dev; ++srb->serial_number; diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index 198bb3e..1943be5 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -201,8 +201,8 @@ static int onetouch_connect_input(struct us_data *ss) if (!onetouch || !input_dev) goto fail1; - onetouch->data = usb_buffer_alloc(udev, ONETOUCH_PKT_LEN, - GFP_KERNEL, &onetouch->data_dma); + onetouch->data = usb_alloc_coherent(udev, ONETOUCH_PKT_LEN, + GFP_KERNEL, &onetouch->data_dma); if (!onetouch->data) goto fail1; @@ -264,8 +264,8 @@ static int onetouch_connect_input(struct us_data *ss) return 0; fail3: usb_free_urb(onetouch->irq); - fail2: usb_buffer_free(udev, ONETOUCH_PKT_LEN, - onetouch->data, onetouch->data_dma); + fail2: usb_free_coherent(udev, ONETOUCH_PKT_LEN, + onetouch->data, onetouch->data_dma); fail1: kfree(onetouch); input_free_device(input_dev); return error; @@ -279,8 +279,8 @@ static void onetouch_release_input(void *onetouch_) usb_kill_urb(onetouch->irq); input_unregister_device(onetouch->dev); usb_free_urb(onetouch->irq); - usb_buffer_free(onetouch->udev, ONETOUCH_PKT_LEN, - onetouch->data, onetouch->data_dma); + usb_free_coherent(onetouch->udev, ONETOUCH_PKT_LEN, + onetouch->data, onetouch->data_dma); } } diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index f253ede..4471642 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -147,11 +147,9 @@ static int usb_stor_msg_common(struct us_data *us, int timeout) * hasn't been mapped for DMA. Yes, this is clunky, but it's * easier than always having the caller tell us whether the * transfer buffer has already been mapped. */ - us->current_urb->transfer_flags = URB_NO_SETUP_DMA_MAP; if (us->current_urb->transfer_buffer == us->iobuf) us->current_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; us->current_urb->transfer_dma = us->iobuf_dma; - us->current_urb->setup_dma = us->cr_dma; /* submit the URB */ status = usb_submit_urb(us->current_urb, GFP_NOIO); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index ccf1dbb..2c897ee 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -365,15 +365,6 @@ UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x2210, "FinePix 1400Zoom", US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY | US_FL_SINGLE_LUN), -/* Reported by Peter Wächtler <pwaechtler@loewe-komp.de> - * The device needs the flags only. - */ -UNUSUAL_DEV( 0x04ce, 0x0002, 0x0074, 0x0074, - "ScanLogic", - "SL11R-IDE", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY), - /* Reported by Ondrej Zary <linux@rainbow-software.org> * The device reports one sector more and breaks when that sector is accessed */ @@ -1853,6 +1844,21 @@ UNUSUAL_DEV( 0x1652, 0x6600, 0x0201, 0x0201, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), +/* Reported by Hans de Goede <hdegoede@redhat.com> + * These Appotech controllers are found in Picture Frames, they provide a + * (buggy) emulation of a cdrom drive which contains the windows software + * Uploading of pictures happens over the corresponding /dev/sg device. */ +UNUSUAL_DEV( 0x1908, 0x1315, 0x0000, 0x0000, + "BUILDWIN", + "Photo Frame", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_BAD_SENSE ), +UNUSUAL_DEV( 0x1908, 0x1320, 0x0000, 0x0000, + "BUILDWIN", + "Photo Frame", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_BAD_SENSE ), + UNUSUAL_DEV( 0x2116, 0x0320, 0x0001, 0x0001, "ST", "2A", diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index bbeeb92..a7d0bf9 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -407,15 +407,14 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf) /* Store our private data in the interface */ usb_set_intfdata(intf, us); - /* Allocate the device-related DMA-mapped buffers */ - us->cr = usb_buffer_alloc(us->pusb_dev, sizeof(*us->cr), - GFP_KERNEL, &us->cr_dma); + /* Allocate the control/setup and DMA-mapped buffers */ + us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL); if (!us->cr) { US_DEBUGP("usb_ctrlrequest allocation failed\n"); return -ENOMEM; } - us->iobuf = usb_buffer_alloc(us->pusb_dev, US_IOBUF_SIZE, + us->iobuf = usb_alloc_coherent(us->pusb_dev, US_IOBUF_SIZE, GFP_KERNEL, &us->iobuf_dma); if (!us->iobuf) { US_DEBUGP("I/O buffer allocation failed\n"); @@ -499,9 +498,6 @@ static void adjust_quirks(struct us_data *us) } } us->fflags = (us->fflags & ~mask) | f; - dev_info(&us->pusb_intf->dev, "Quirks match for " - "vid %04x pid %04x: %x\n", - vid, pid, f); } /* Get the unusual_devs entries and the string descriptors */ @@ -511,6 +507,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id, struct usb_device *dev = us->pusb_dev; struct usb_interface_descriptor *idesc = &us->pusb_intf->cur_altsetting->desc; + struct device *pdev = &us->pusb_intf->dev; /* Store the entries */ us->unusual_dev = unusual_dev; @@ -524,7 +521,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id, adjust_quirks(us); if (us->fflags & US_FL_IGNORE_DEVICE) { - printk(KERN_INFO USB_STORAGE "device ignored\n"); + dev_info(pdev, "device ignored\n"); return -ENODEV; } @@ -535,6 +532,12 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id, if (dev->speed != USB_SPEED_HIGH) us->fflags &= ~US_FL_GO_SLOW; + if (us->fflags) + dev_info(pdev, "Quirks match for vid %04x pid %04x: %lx\n", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct), + us->fflags); + /* Log a message if a non-generic unusual_dev entry contains an * unnecessary subclass or protocol override. This may stimulate * reports from users that will help us remove unneeded entries @@ -555,20 +558,20 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id, us->protocol == idesc->bInterfaceProtocol) msg += 2; if (msg >= 0 && !(us->fflags & US_FL_NEED_OVERRIDE)) - printk(KERN_NOTICE USB_STORAGE "This device " - "(%04x,%04x,%04x S %02x P %02x)" - " has %s in unusual_devs.h (kernel" - " %s)\n" - " Please send a copy of this message to " - "<linux-usb@vger.kernel.org> and " - "<usb-storage@lists.one-eyed-alien.net>\n", - le16_to_cpu(ddesc->idVendor), - le16_to_cpu(ddesc->idProduct), - le16_to_cpu(ddesc->bcdDevice), - idesc->bInterfaceSubClass, - idesc->bInterfaceProtocol, - msgs[msg], - utsname()->release); + dev_notice(pdev, "This device " + "(%04x,%04x,%04x S %02x P %02x)" + " has %s in unusual_devs.h (kernel" + " %s)\n" + " Please send a copy of this message to " + "<linux-usb@vger.kernel.org> and " + "<usb-storage@lists.one-eyed-alien.net>\n", + le16_to_cpu(ddesc->idVendor), + le16_to_cpu(ddesc->idProduct), + le16_to_cpu(ddesc->bcdDevice), + idesc->bInterfaceSubClass, + idesc->bInterfaceProtocol, + msgs[msg], + utsname()->release); } return 0; @@ -718,8 +721,8 @@ static int usb_stor_acquire_resources(struct us_data *us) /* Start up our control thread */ th = kthread_run(usb_stor_control_thread, us, "usb-storage"); if (IS_ERR(th)) { - printk(KERN_WARNING USB_STORAGE - "Unable to start control thread\n"); + dev_warn(&us->pusb_intf->dev, + "Unable to start control thread\n"); return PTR_ERR(th); } us->ctl_thread = th; @@ -757,13 +760,9 @@ static void dissociate_dev(struct us_data *us) { US_DEBUGP("-- %s\n", __func__); - /* Free the device-related DMA-mapped buffers */ - if (us->cr) - usb_buffer_free(us->pusb_dev, sizeof(*us->cr), us->cr, - us->cr_dma); - if (us->iobuf) - usb_buffer_free(us->pusb_dev, US_IOBUF_SIZE, us->iobuf, - us->iobuf_dma); + /* Free the buffers */ + kfree(us->cr); + usb_free_coherent(us->pusb_dev, US_IOBUF_SIZE, us->iobuf, us->iobuf_dma); /* Remove our private data from the interface */ usb_set_intfdata(us->pusb_intf, NULL); @@ -816,13 +815,14 @@ static void release_everything(struct us_data *us) static int usb_stor_scan_thread(void * __us) { struct us_data *us = (struct us_data *)__us; + struct device *dev = &us->pusb_intf->dev; - dev_dbg(&us->pusb_intf->dev, "device found\n"); + dev_dbg(dev, "device found\n"); set_freezable(); /* Wait for the timeout to expire or for a disconnect */ if (delay_use > 0) { - dev_dbg(&us->pusb_intf->dev, "waiting for device to settle " + dev_dbg(dev, "waiting for device to settle " "before scanning\n"); wait_event_freezable_timeout(us->delay_wait, test_bit(US_FLIDX_DONT_SCAN, &us->dflags), @@ -840,7 +840,7 @@ static int usb_stor_scan_thread(void * __us) mutex_unlock(&us->dev_mutex); } scsi_scan_host(us_to_host(us)); - dev_dbg(&us->pusb_intf->dev, "scan complete\n"); + dev_dbg(dev, "scan complete\n"); /* Should we unbind if no devices were detected? */ } @@ -876,8 +876,8 @@ int usb_stor_probe1(struct us_data **pus, */ host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us)); if (!host) { - printk(KERN_WARNING USB_STORAGE - "Unable to allocate the scsi host\n"); + dev_warn(&intf->dev, + "Unable to allocate the scsi host\n"); return -ENOMEM; } @@ -925,6 +925,7 @@ int usb_stor_probe2(struct us_data *us) { struct task_struct *th; int result; + struct device *dev = &us->pusb_intf->dev; /* Make sure the transport and protocol have both been set */ if (!us->transport || !us->proto_handler) { @@ -949,18 +950,18 @@ int usb_stor_probe2(struct us_data *us) goto BadDevice; snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s", dev_name(&us->pusb_intf->dev)); - result = scsi_add_host(us_to_host(us), &us->pusb_intf->dev); + result = scsi_add_host(us_to_host(us), dev); if (result) { - printk(KERN_WARNING USB_STORAGE - "Unable to add the scsi host\n"); + dev_warn(dev, + "Unable to add the scsi host\n"); goto BadDevice; } /* Start up the thread for delayed SCSI-device scanning */ th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan"); if (IS_ERR(th)) { - printk(KERN_WARNING USB_STORAGE - "Unable to start the device-scanning thread\n"); + dev_warn(dev, + "Unable to start the device-scanning thread\n"); complete(&us->scanning_done); quiesce_and_remove_host(us); result = PTR_ERR(th); @@ -1046,12 +1047,12 @@ static int __init usb_stor_init(void) { int retval; - printk(KERN_INFO "Initializing USB Mass Storage driver...\n"); + pr_info("Initializing USB Mass Storage driver...\n"); /* register the driver, return usb_register return code if error */ retval = usb_register(&usb_storage_driver); if (retval == 0) { - printk(KERN_INFO "USB Mass Storage support registered.\n"); + pr_info("USB Mass Storage support registered.\n"); usb_usual_set_present(USB_US_TYPE_STOR); } return retval; diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 6971713..89d3bff 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -139,8 +139,7 @@ struct us_data { struct usb_ctrlrequest *cr; /* control requests */ struct usb_sg_request current_sg; /* scatter-gather req. */ unsigned char *iobuf; /* I/O buffer */ - dma_addr_t cr_dma; /* buffer DMA addresses */ - dma_addr_t iobuf_dma; + dma_addr_t iobuf_dma; /* buffer DMA addresses */ struct task_struct *ctl_thread; /* the control thread */ /* mutual exclusion and synchronization structures */ diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 6152278..d110588 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -387,8 +387,8 @@ static void skel_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); } @@ -442,8 +442,8 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, goto error; } - buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, - &urb->transfer_dma); + buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL, + &urb->transfer_dma); if (!buf) { retval = -ENOMEM; goto error; @@ -491,7 +491,7 @@ error_unanchor: usb_unanchor_urb(urb); error: if (urb) { - usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); + usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma); usb_free_urb(urb); } up(&dev->limit_sem); diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 608d61a..84b744c 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -474,8 +474,6 @@ static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer, struct wa_xfer_ctl *xfer_ctl = container_of(xfer_hdr0, struct wa_xfer_ctl, hdr); xfer_ctl->bmAttribute = xfer->is_inbound ? 1 : 0; - BUG_ON(xfer->urb->transfer_flags & URB_NO_SETUP_DMA_MAP - && xfer->urb->setup_packet == NULL); memcpy(&xfer_ctl->baSetupData, xfer->urb->setup_packet, sizeof(xfer_ctl->baSetupData)); break; diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h index 759cda5..3d94c42 100644 --- a/drivers/usb/wusbcore/wusbhc.h +++ b/drivers/usb/wusbcore/wusbhc.h @@ -58,9 +58,7 @@ #include <linux/mutex.h> #include <linux/kref.h> #include <linux/workqueue.h> -/* FIXME: Yes, I know: BAD--it's not my fault the USB HC iface is not - * public */ -#include <linux/../../drivers/usb/core/hcd.h> +#include <linux/usb/hcd.h> #include <linux/uwb.h> #include <linux/usb/wusb.h> |