diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-26 08:09:27 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-26 08:09:27 +0200 |
commit | d5ef642355bdd9b383ff5c18cbc6102a06eecbaf (patch) | |
tree | fcf78d33c1790c6c24efbfd0c3695f7874f053d7 /drivers | |
parent | Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/greg... (diff) | |
parent | amba pl011: workaround for uart registers lockup (diff) | |
download | linux-d5ef642355bdd9b383ff5c18cbc6102a06eecbaf.tar.xz linux-d5ef642355bdd9b383ff5c18cbc6102a06eecbaf.zip |
Merge branch 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6
* 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6: (26 commits)
amba pl011: workaround for uart registers lockup
n_gsm: fix the wrong FCS handling
pch_uart: add missing comment about OKI ML7223
pch_uart: Add MSI support
tty: fix "IRQ45: nobody cared"
PTI feature to allow user to name and mark masterchannel request.
0 for o PTI Makefile bug.
tty: serial: samsung.c remove legacy PM code.
SERIAL: SC26xx: Fix link error.
serial: mrst_max3110: initialize waitqueue earlier
mrst_max3110: Change max missing message priority.
tty: s5pv210: Add delay loop on fifo reset function for UART
tty/serial: Fix XSCALE serial ports, e.g. ce4100
serial: bfin_5xx: fix off-by-one with resource size
drivers/tty: use printk_ratelimited() instead of printk_ratelimit()
tty: n_gsm: Added refcount usage to gsm_mux and gsm_dlci structs
tty: n_gsm: Add raw-ip support
tty: n_gsm: expose gsmtty device nodes at ldisc open time
pch_phub: Fix register miss-setting issue
serial: 8250, increase PASS_LIMIT
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/misc/Makefile | 2 | ||||
-rw-r--r-- | drivers/misc/pch_phub.c | 4 | ||||
-rw-r--r-- | drivers/misc/pti.c | 99 | ||||
-rw-r--r-- | drivers/parport/parport_pc.c | 54 | ||||
-rw-r--r-- | drivers/parport/parport_serial.c | 229 | ||||
-rw-r--r-- | drivers/tty/moxa.c | 5 | ||||
-rw-r--r-- | drivers/tty/mxser.c | 4 | ||||
-rw-r--r-- | drivers/tty/n_gsm.c | 401 | ||||
-rw-r--r-- | drivers/tty/n_tty.c | 2 | ||||
-rw-r--r-- | drivers/tty/serial/8250.c | 4 | ||||
-rw-r--r-- | drivers/tty/serial/8250_pci.c | 183 | ||||
-rw-r--r-- | drivers/tty/serial/Kconfig | 2 | ||||
-rw-r--r-- | drivers/tty/serial/pch_uart.c | 11 | ||||
-rw-r--r-- | drivers/tty/serial/s5pv210.c | 4 | ||||
-rw-r--r-- | drivers/tty/serial/samsung.c | 27 | ||||
-rw-r--r-- | drivers/tty/tty_io.c | 4 |
16 files changed, 867 insertions, 168 deletions
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 33282157bc3c..8f3efb68a141 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o -0bj-$(CONFIG_INTEL_MID_PTI) += pti.o +obj-$(CONFIG_INTEL_MID_PTI) += pti.o obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 01eb67b4871a..0fd7e77bee29 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -735,6 +735,8 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev, * Device8(GbE) */ iowrite32(0x000a0000, chip->pch_phub_base_address + 0x14); + /* set the interrupt delay value */ + iowrite32(0x25, chip->pch_phub_base_address + 0x140); chip->pch_opt_rom_start_address =\ PCH_PHUB_ROM_START_ADDR_ML7223; chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_ML7223; @@ -752,8 +754,6 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev, * Device6(SATA 2):f */ iowrite32(0x0000ffa0, chip->pch_phub_base_address + 0x14); - /* set the interrupt delay value */ - iowrite32(0x25, chip->pch_phub_base_address + 0x140); chip->pch_opt_rom_start_address =\ PCH_PHUB_ROM_START_ADDR_ML7223; chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_ML7223; diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c index e5f295a38a34..8653bd0b1a33 100644 --- a/drivers/misc/pti.c +++ b/drivers/misc/pti.c @@ -146,45 +146,54 @@ static void pti_write_to_aperture(struct pti_masterchannel *mc, /** * pti_control_frame_built_and_sent()- control frame build and send function. * - * @mc: The master / channel structure on which the function - * built a control frame. + * @mc: The master / channel structure on which the function + * built a control frame. + * @thread_name: The thread name associated with the master / channel or + * 'NULL' if using the 'current' global variable. * * To be able to post process the PTI contents on host side, a control frame * is added before sending any PTI content. So the host side knows on * each PTI frame the name of the thread using a dedicated master / channel. - * The thread name is retrieved from the 'current' global variable. + * The thread name is retrieved from 'current' global variable if 'thread_name' + * is 'NULL', else it is retrieved from 'thread_name' parameter. * This function builds this frame and sends it to a master ID CONTROL_ID. * The overhead is only 32 bytes since the driver only writes to HW * in 32 byte chunks. */ - -static void pti_control_frame_built_and_sent(struct pti_masterchannel *mc) +static void pti_control_frame_built_and_sent(struct pti_masterchannel *mc, + const char *thread_name) { struct pti_masterchannel mccontrol = {.master = CONTROL_ID, .channel = 0}; + const char *thread_name_p; const char *control_format = "%3d %3d %s"; u8 control_frame[CONTROL_FRAME_LEN]; - /* - * Since we access the comm member in current's task_struct, - * we only need to be as large as what 'comm' in that - * structure is. - */ - char comm[TASK_COMM_LEN]; + if (!thread_name) { + /* + * Since we access the comm member in current's task_struct, + * we only need to be as large as what 'comm' in that + * structure is. + */ + char comm[TASK_COMM_LEN]; - if (!in_interrupt()) - get_task_comm(comm, current); - else - strncpy(comm, "Interrupt", TASK_COMM_LEN); + if (!in_interrupt()) + get_task_comm(comm, current); + else + strncpy(comm, "Interrupt", TASK_COMM_LEN); - /* Absolutely ensure our buffer is zero terminated. */ - comm[TASK_COMM_LEN-1] = 0; + /* Absolutely ensure our buffer is zero terminated. */ + comm[TASK_COMM_LEN-1] = 0; + thread_name_p = comm; + } else { + thread_name_p = thread_name; + } mccontrol.channel = pti_control_channel; pti_control_channel = (pti_control_channel + 1) & 0x7f; snprintf(control_frame, CONTROL_FRAME_LEN, control_format, mc->master, - mc->channel, comm); + mc->channel, thread_name_p); pti_write_to_aperture(&mccontrol, control_frame, strlen(control_frame)); } @@ -206,18 +215,20 @@ static void pti_write_full_frame_to_aperture(struct pti_masterchannel *mc, const unsigned char *buf, int len) { - pti_control_frame_built_and_sent(mc); + pti_control_frame_built_and_sent(mc, NULL); pti_write_to_aperture(mc, (u8 *)buf, len); } /** * get_id()- Allocate a master and channel ID. * - * @id_array: an array of bits representing what channel - * id's are allocated for writing. - * @max_ids: The max amount of available write IDs to use. - * @base_id: The starting SW channel ID, based on the Intel - * PTI arch. + * @id_array: an array of bits representing what channel + * id's are allocated for writing. + * @max_ids: The max amount of available write IDs to use. + * @base_id: The starting SW channel ID, based on the Intel + * PTI arch. + * @thread_name: The thread name associated with the master / channel or + * 'NULL' if using the 'current' global variable. * * Returns: * pti_masterchannel struct with master, channel ID address @@ -227,7 +238,10 @@ static void pti_write_full_frame_to_aperture(struct pti_masterchannel *mc, * channel id. The bit is one if the id is taken and 0 if free. For * every master there are 128 channel id's. */ -static struct pti_masterchannel *get_id(u8 *id_array, int max_ids, int base_id) +static struct pti_masterchannel *get_id(u8 *id_array, + int max_ids, + int base_id, + const char *thread_name) { struct pti_masterchannel *mc; int i, j, mask; @@ -257,7 +271,7 @@ static struct pti_masterchannel *get_id(u8 *id_array, int max_ids, int base_id) mc->master = base_id; mc->channel = ((i & 0xf)<<3) + j; /* write new master Id / channel Id allocation to channel control */ - pti_control_frame_built_and_sent(mc); + pti_control_frame_built_and_sent(mc, thread_name); return mc; } @@ -273,18 +287,22 @@ static struct pti_masterchannel *get_id(u8 *id_array, int max_ids, int base_id) * a master, channel ID address * to write to PTI HW. * - * @type: 0- request Application master, channel aperture ID write address. - * 1- request OS master, channel aperture ID write - * address. - * 2- request Modem master, channel aperture ID - * write address. - * Other values, error. + * @type: 0- request Application master, channel aperture ID + * write address. + * 1- request OS master, channel aperture ID write + * address. + * 2- request Modem master, channel aperture ID + * write address. + * Other values, error. + * @thread_name: The thread name associated with the master / channel or + * 'NULL' if using the 'current' global variable. * * Returns: * pti_masterchannel struct * 0 for error */ -struct pti_masterchannel *pti_request_masterchannel(u8 type) +struct pti_masterchannel *pti_request_masterchannel(u8 type, + const char *thread_name) { struct pti_masterchannel *mc; @@ -293,15 +311,18 @@ struct pti_masterchannel *pti_request_masterchannel(u8 type) switch (type) { case 0: - mc = get_id(drv_data->ia_app, MAX_APP_IDS, APP_BASE_ID); + mc = get_id(drv_data->ia_app, MAX_APP_IDS, + APP_BASE_ID, thread_name); break; case 1: - mc = get_id(drv_data->ia_os, MAX_OS_IDS, OS_BASE_ID); + mc = get_id(drv_data->ia_os, MAX_OS_IDS, + OS_BASE_ID, thread_name); break; case 2: - mc = get_id(drv_data->ia_modem, MAX_MODEM_IDS, MODEM_BASE_ID); + mc = get_id(drv_data->ia_modem, MAX_MODEM_IDS, + MODEM_BASE_ID, thread_name); break; default: mc = NULL; @@ -472,9 +493,9 @@ static int pti_tty_install(struct tty_driver *driver, struct tty_struct *tty) return -ENOMEM; if (idx == PTITTY_MINOR_START) - pti_tty_data->mc = pti_request_masterchannel(0); + pti_tty_data->mc = pti_request_masterchannel(0, NULL); else - pti_tty_data->mc = pti_request_masterchannel(2); + pti_tty_data->mc = pti_request_masterchannel(2, NULL); if (pti_tty_data->mc == NULL) { kfree(pti_tty_data); @@ -563,7 +584,7 @@ static int pti_char_open(struct inode *inode, struct file *filp) * before assigning the value to filp->private_data. * Slightly easier to debug if this driver needs debugging. */ - mc = pti_request_masterchannel(0); + mc = pti_request_masterchannel(0, NULL); if (mc == NULL) return -ENOMEM; filp->private_data = mc; diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index f330338c2f22..d1cdb9449f84 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -2864,24 +2864,6 @@ enum parport_pc_pci_cards { lava_parallel_dual_b, boca_ioppar, plx_9050, - timedia_4078a, - timedia_4079h, - timedia_4085h, - timedia_4088a, - timedia_4089a, - timedia_4095a, - timedia_4096a, - timedia_4078u, - timedia_4079a, - timedia_4085u, - timedia_4079r, - timedia_4079s, - timedia_4079d, - timedia_4079e, - timedia_4079f, - timedia_9079a, - timedia_9079b, - timedia_9079c, timedia_4006a, timedia_4014, timedia_4008a, @@ -2940,24 +2922,6 @@ static struct parport_pc_pci { /* lava_parallel_dual_b */ { 1, { { 0, -1 }, } }, /* boca_ioppar */ { 1, { { 0, -1 }, } }, /* plx_9050 */ { 2, { { 4, -1 }, { 5, -1 }, } }, - /* timedia_4078a */ { 1, { { 2, -1 }, } }, - /* timedia_4079h */ { 1, { { 2, 3 }, } }, - /* timedia_4085h */ { 2, { { 2, -1 }, { 4, -1 }, } }, - /* timedia_4088a */ { 2, { { 2, 3 }, { 4, 5 }, } }, - /* timedia_4089a */ { 2, { { 2, 3 }, { 4, 5 }, } }, - /* timedia_4095a */ { 2, { { 2, 3 }, { 4, 5 }, } }, - /* timedia_4096a */ { 2, { { 2, 3 }, { 4, 5 }, } }, - /* timedia_4078u */ { 1, { { 2, -1 }, } }, - /* timedia_4079a */ { 1, { { 2, 3 }, } }, - /* timedia_4085u */ { 2, { { 2, -1 }, { 4, -1 }, } }, - /* timedia_4079r */ { 1, { { 2, 3 }, } }, - /* timedia_4079s */ { 1, { { 2, 3 }, } }, - /* timedia_4079d */ { 1, { { 2, 3 }, } }, - /* timedia_4079e */ { 1, { { 2, 3 }, } }, - /* timedia_4079f */ { 1, { { 2, 3 }, } }, - /* timedia_9079a */ { 1, { { 2, 3 }, } }, - /* timedia_9079b */ { 1, { { 2, 3 }, } }, - /* timedia_9079c */ { 1, { { 2, 3 }, } }, /* timedia_4006a */ { 1, { { 0, -1 }, } }, /* timedia_4014 */ { 2, { { 0, -1 }, { 2, -1 }, } }, /* timedia_4008a */ { 1, { { 0, 1 }, } }, @@ -3019,24 +2983,6 @@ static const struct pci_device_id parport_pc_pci_tbl[] = { { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014, 0, 0, plx_9050 }, /* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/ - { 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a }, - { 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h }, - { 0x1409, 0x7168, 0x1409, 0x4085, 0, 0, timedia_4085h }, - { 0x1409, 0x7168, 0x1409, 0x4088, 0, 0, timedia_4088a }, - { 0x1409, 0x7168, 0x1409, 0x4089, 0, 0, timedia_4089a }, - { 0x1409, 0x7168, 0x1409, 0x4095, 0, 0, timedia_4095a }, - { 0x1409, 0x7168, 0x1409, 0x4096, 0, 0, timedia_4096a }, - { 0x1409, 0x7168, 0x1409, 0x5078, 0, 0, timedia_4078u }, - { 0x1409, 0x7168, 0x1409, 0x5079, 0, 0, timedia_4079a }, - { 0x1409, 0x7168, 0x1409, 0x5085, 0, 0, timedia_4085u }, - { 0x1409, 0x7168, 0x1409, 0x6079, 0, 0, timedia_4079r }, - { 0x1409, 0x7168, 0x1409, 0x7079, 0, 0, timedia_4079s }, - { 0x1409, 0x7168, 0x1409, 0x8079, 0, 0, timedia_4079d }, - { 0x1409, 0x7168, 0x1409, 0x9079, 0, 0, timedia_4079e }, - { 0x1409, 0x7168, 0x1409, 0xa079, 0, 0, timedia_4079f }, - { 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a }, - { 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b }, - { 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c }, { 0x1409, 0x7268, 0x1409, 0x0101, 0, 0, timedia_4006a }, { 0x1409, 0x7268, 0x1409, 0x0102, 0, 0, timedia_4014 }, { 0x1409, 0x7268, 0x1409, 0x0103, 0, 0, timedia_4008a }, diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index f01e26440f11..e9c32274df3f 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -33,6 +33,9 @@ enum parport_pc_pci_cards { netmos_9xx5_combo, netmos_9855, netmos_9855_2p, + netmos_9900, + netmos_9900_2p, + netmos_99xx_1p, avlab_1s1p, avlab_1s2p, avlab_2s1p, @@ -41,6 +44,24 @@ enum parport_pc_pci_cards { siig_2p1s_20x, siig_1s1p_20x, siig_2s1p_20x, + timedia_4078a, + timedia_4079h, + timedia_4085h, + timedia_4088a, + timedia_4089a, + timedia_4095a, + timedia_4096a, + timedia_4078u, + timedia_4079a, + timedia_4085u, + timedia_4079r, + timedia_4079s, + timedia_4079d, + timedia_4079e, + timedia_4079f, + timedia_9079a, + timedia_9079b, + timedia_9079c, }; /* each element directly indexed from enum list, above */ @@ -72,22 +93,20 @@ static int __devinit netmos_parallel_init(struct pci_dev *dev, struct parport_pc dev->subsystem_vendor == PCI_VENDOR_ID_IBM && dev->subsystem_device == 0x0299) return -ENODEV; - /* - * Netmos uses the subdevice ID to indicate the number of parallel - * and serial ports. The form is 0x00PS, where <P> is the number of - * parallel ports and <S> is the number of serial ports. - */ - par->numports = (dev->subsystem_device & 0xf0) >> 4; - if (par->numports > ARRAY_SIZE(par->addr)) - par->numports = ARRAY_SIZE(par->addr); - /* - * This function is currently only called for cards with up to - * one parallel port. - * Parallel port BAR is either before or after serial ports BARS; - * hence, lo should be either 0 or equal to the number of serial ports. - */ - if (par->addr[0].lo != 0) - par->addr[0].lo = dev->subsystem_device & 0xf; + + if (dev->device == PCI_DEVICE_ID_NETMOS_9912) { + par->numports = 1; + } else { + /* + * Netmos uses the subdevice ID to indicate the number of parallel + * and serial ports. The form is 0x00PS, where <P> is the number of + * parallel ports and <S> is the number of serial ports. + */ + par->numports = (dev->subsystem_device & 0xf0) >> 4; + if (par->numports > ARRAY_SIZE(par->addr)) + par->numports = ARRAY_SIZE(par->addr); + } + return 0; } @@ -97,6 +116,9 @@ static struct parport_pc_pci cards[] __devinitdata = { /* netmos_9xx5_combo */ { 1, { { 2, -1 }, }, netmos_parallel_init }, /* netmos_9855 */ { 1, { { 0, -1 }, }, netmos_parallel_init }, /* netmos_9855_2p */ { 2, { { 0, -1 }, { 2, -1 }, } }, + /* netmos_9900 */ {1, { { 3, 4 }, }, netmos_parallel_init }, + /* netmos_9900_2p */ {2, { { 0, 1 }, { 3, 4 }, } }, + /* netmos_99xx_1p */ {1, { { 0, 1 }, } }, /* avlab_1s1p */ { 1, { { 1, 2}, } }, /* avlab_1s2p */ { 2, { { 1, 2}, { 3, 4 },} }, /* avlab_2s1p */ { 1, { { 2, 3}, } }, @@ -105,6 +127,24 @@ static struct parport_pc_pci cards[] __devinitdata = { /* siig_2p1s_20x */ { 2, { { 1, 2 }, { 3, 4 }, } }, /* siig_1s1p_20x */ { 1, { { 1, 2 }, } }, /* siig_2s1p_20x */ { 1, { { 2, 3 }, } }, + /* timedia_4078a */ { 1, { { 2, -1 }, } }, + /* timedia_4079h */ { 1, { { 2, 3 }, } }, + /* timedia_4085h */ { 2, { { 2, -1 }, { 4, -1 }, } }, + /* timedia_4088a */ { 2, { { 2, 3 }, { 4, 5 }, } }, + /* timedia_4089a */ { 2, { { 2, 3 }, { 4, 5 }, } }, + /* timedia_4095a */ { 2, { { 2, 3 }, { 4, 5 }, } }, + /* timedia_4096a */ { 2, { { 2, 3 }, { 4, 5 }, } }, + /* timedia_4078u */ { 1, { { 2, -1 }, } }, + /* timedia_4079a */ { 1, { { 2, 3 }, } }, + /* timedia_4085u */ { 2, { { 2, -1 }, { 4, -1 }, } }, + /* timedia_4079r */ { 1, { { 2, 3 }, } }, + /* timedia_4079s */ { 1, { { 2, 3 }, } }, + /* timedia_4079d */ { 1, { { 2, 3 }, } }, + /* timedia_4079e */ { 1, { { 2, 3 }, } }, + /* timedia_4079f */ { 1, { { 2, 3 }, } }, + /* timedia_9079a */ { 1, { { 2, 3 }, } }, + /* timedia_9079b */ { 1, { { 2, 3 }, } }, + /* timedia_9079c */ { 1, { { 2, 3 }, } }, }; static struct pci_device_id parport_serial_pci_tbl[] = { @@ -127,6 +167,14 @@ static struct pci_device_id parport_serial_pci_tbl[] = { 0x1000, 0x0022, 0, 0, netmos_9855_2p }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3011, 0, 0, netmos_9900 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3012, 0, 0, netmos_9900 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3020, 0, 0, netmos_9900_2p }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, + 0xA000, 0x2000, 0, 0, netmos_99xx_1p }, /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ { PCI_VENDOR_ID_AFAVLAB, 0x2110, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p }, @@ -176,6 +224,25 @@ static struct pci_device_id parport_serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, + /* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/ + { 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a }, + { 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h }, + { 0x1409, 0x7168, 0x1409, 0x4085, 0, 0, timedia_4085h }, + { 0x1409, 0x7168, 0x1409, 0x4088, 0, 0, timedia_4088a }, + { 0x1409, 0x7168, 0x1409, 0x4089, 0, 0, timedia_4089a }, + { 0x1409, 0x7168, 0x1409, 0x4095, 0, 0, timedia_4095a }, + { 0x1409, 0x7168, 0x1409, 0x4096, 0, 0, timedia_4096a }, + { 0x1409, 0x7168, 0x1409, 0x5078, 0, 0, timedia_4078u }, + { 0x1409, 0x7168, 0x1409, 0x5079, 0, 0, timedia_4079a }, + { 0x1409, 0x7168, 0x1409, 0x5085, 0, 0, timedia_4085u }, + { 0x1409, 0x7168, 0x1409, 0x6079, 0, 0, timedia_4079r }, + { 0x1409, 0x7168, 0x1409, 0x7079, 0, 0, timedia_4079s }, + { 0x1409, 0x7168, 0x1409, 0x8079, 0, 0, timedia_4079d }, + { 0x1409, 0x7168, 0x1409, 0x9079, 0, 0, timedia_4079e }, + { 0x1409, 0x7168, 0x1409, 0xa079, 0, 0, timedia_4079f }, + { 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a }, + { 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b }, + { 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c }, { 0, } /* terminate list */ }; @@ -219,6 +286,24 @@ static struct pciserial_board pci_parport_serial_boards[] __devinitdata = { .base_baud = 115200, .uart_offset = 8, }, + [netmos_9900] = { /* n/t */ + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [netmos_9900_2p] = { /* parallel only */ /* n/t */ + .flags = FL_BASE0, + .num_ports = 0, + .base_baud = 115200, + .uart_offset = 8, + }, + [netmos_99xx_1p] = { /* parallel only */ /* n/t */ + .flags = FL_BASE0, + .num_ports = 0, + .base_baud = 115200, + .uart_offset = 8, + }, [avlab_1s1p] = { /* n/t */ .flags = FL_BASE0 | FL_BASE_BARS, .num_ports = 1, @@ -267,6 +352,114 @@ static struct pciserial_board pci_parport_serial_boards[] __devinitdata = { .base_baud = 921600, .uart_offset = 8, }, + [timedia_4078a] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4079h] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4085h] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4088a] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4089a] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4095a] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4096a] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4078u] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4079a] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4085u] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4079r] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4079s] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4079d] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4079e] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_4079f] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_9079a] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_9079b] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [timedia_9079c] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, }; struct parport_serial_private { @@ -285,6 +478,10 @@ static int __devinit serial_register (struct pci_dev *dev, struct serial_private *serial; board = &pci_parport_serial_boards[id->driver_data]; + + if (board->num_ports == 0) + return 0; + serial = pciserial_init_ports(dev, board); if (IS_ERR(serial)) diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index ba679ce0a774..d15a071b1a54 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -44,6 +44,7 @@ #include <linux/init.h> #include <linux/bitops.h> #include <linux/slab.h> +#include <linux/ratelimit.h> #include <asm/system.h> #include <asm/io.h> @@ -242,8 +243,8 @@ static void moxa_wait_finish(void __iomem *ofsAddr) while (readw(ofsAddr + FuncCode) != 0) if (time_after(jiffies, end)) return; - if (readw(ofsAddr + FuncCode) != 0 && printk_ratelimit()) - printk(KERN_WARNING "moxa function expired\n"); + if (readw(ofsAddr + FuncCode) != 0) + printk_ratelimited(KERN_WARNING "moxa function expired\n"); } static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg) diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index d188f378684d..7fc8c02fea6c 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -39,6 +39,7 @@ #include <linux/pci.h> #include <linux/bitops.h> #include <linux/slab.h> +#include <linux/ratelimit.h> #include <asm/system.h> #include <asm/io.h> @@ -1490,8 +1491,7 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) switch (cmd) { case MOXA_GET_MAJOR: - if (printk_ratelimit()) - printk(KERN_WARNING "mxser: '%s' uses deprecated ioctl " + printk_ratelimited(KERN_WARNING "mxser: '%s' uses deprecated ioctl " "%x (GET_MAJOR), fix your userspace\n", current->comm, cmd); return put_user(ttymajor, (int __user *)argp); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 19b4ae052af8..8a50e4eebf18 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -58,6 +58,10 @@ #include <linux/serial.h> #include <linux/kfifo.h> #include <linux/skbuff.h> +#include <net/arp.h> +#include <linux/ip.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> #include <linux/gsmmux.h> static int debug; @@ -77,8 +81,24 @@ module_param(debug, int, 0600); * Semi-arbitrary buffer size limits. 0710 is normally run with 32-64 byte * limits so this is plenty */ -#define MAX_MRU 512 -#define MAX_MTU 512 +#define MAX_MRU 1500 +#define MAX_MTU 1500 +#define GSM_NET_TX_TIMEOUT (HZ*10) + +/** + * struct gsm_mux_net - network interface + * @struct gsm_dlci* dlci + * @struct net_device_stats stats; + * + * Created when net interface is initialized. + **/ +struct gsm_mux_net { + struct kref ref; + struct gsm_dlci *dlci; + struct net_device_stats stats; +}; + +#define STATS(net) (((struct gsm_mux_net *)netdev_priv(net))->stats) /* * Each block of data we have queued to go out is in the form of @@ -113,6 +133,8 @@ struct gsm_dlci { #define DLCI_OPENING 1 /* Sending SABM not seen UA */ #define DLCI_OPEN 2 /* SABM/UA complete */ #define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */ + struct kref ref; /* freed from port or mux close */ + struct mutex mutex; /* Link layer */ spinlock_t lock; /* Protects the internal state */ @@ -123,6 +145,7 @@ struct gsm_dlci { struct kfifo *fifo; /* Queue fifo for the DLCI */ struct kfifo _fifo; /* For new fifo API porting only */ int adaption; /* Adaption layer in use */ + int prev_adaption; u32 modem_rx; /* Our incoming virtual modem lines */ u32 modem_tx; /* Our outgoing modem lines */ int dead; /* Refuse re-open */ @@ -134,6 +157,8 @@ struct gsm_dlci { struct sk_buff_head skb_list; /* Queued frames */ /* Data handling callback */ void (*data)(struct gsm_dlci *dlci, u8 *data, int len); + void (*prev_data)(struct gsm_dlci *dlci, u8 *data, int len); + struct net_device *net; /* network interface, if created */ }; /* DLCI 0, 62/63 are special or reseved see gsmtty_open */ @@ -169,6 +194,8 @@ struct gsm_control { struct gsm_mux { struct tty_struct *tty; /* The tty our ldisc is bound to */ spinlock_t lock; + unsigned int num; + struct kref ref; /* Events on the GSM channel */ wait_queue_head_t event; @@ -250,6 +277,8 @@ struct gsm_mux { static struct gsm_mux *gsm_mux[MAX_MUX]; /* GSM muxes */ static spinlock_t gsm_mux_lock; +static struct tty_driver *gsm_tty_driver; + /* * This section of the driver logic implements the GSM encodings * both the basic and the 'advanced'. Reliable transport is not @@ -878,8 +907,10 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm, memcpy(dp, dlci->skb->data, len); skb_pull(dlci->skb, len); __gsm_data_queue(dlci, msg); - if (last) + if (last) { + kfree_skb(dlci->skb); dlci->skb = NULL; + } return size; } @@ -912,7 +943,7 @@ static void gsm_dlci_data_sweep(struct gsm_mux *gsm) i++; continue; } - if (dlci->adaption < 3) + if (dlci->adaption < 3 && !dlci->net) len = gsm_dlci_data_output(gsm, dlci); else len = gsm_dlci_data_output_framed(gsm, dlci); @@ -939,9 +970,12 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci) spin_lock_irqsave(&dlci->gsm->tx_lock, flags); /* If we have nothing running then we need to fire up */ - if (dlci->gsm->tx_bytes == 0) - gsm_dlci_data_output(dlci->gsm, dlci); - else if (dlci->gsm->tx_bytes < TX_THRESH_LO) + if (dlci->gsm->tx_bytes == 0) { + if (dlci->net) + gsm_dlci_data_output_framed(dlci->gsm, dlci); + else + gsm_dlci_data_output(dlci->gsm, dlci); + } else if (dlci->gsm->tx_bytes < TX_THRESH_LO) gsm_dlci_data_sweep(dlci->gsm); spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); } @@ -1588,6 +1622,8 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) if (dlci == NULL) return NULL; spin_lock_init(&dlci->lock); + kref_init(&dlci->ref); + mutex_init(&dlci->mutex); dlci->fifo = &dlci->_fifo; if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) { kfree(dlci); @@ -1613,26 +1649,52 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) } /** - * gsm_dlci_free - release DLCI + * gsm_dlci_free - free DLCI + * @dlci: DLCI to free + * + * Free up a DLCI. + * + * Can sleep. + */ +static void gsm_dlci_free(struct kref *ref) +{ + struct gsm_dlci *dlci = container_of(ref, struct gsm_dlci, ref); + + del_timer_sync(&dlci->t1); + dlci->gsm->dlci[dlci->addr] = NULL; + kfifo_free(dlci->fifo); + while ((dlci->skb = skb_dequeue(&dlci->skb_list))) + kfree_skb(dlci->skb); + kfree(dlci); +} + +static inline void dlci_get(struct gsm_dlci *dlci) +{ + kref_get(&dlci->ref); +} + +static inline void dlci_put(struct gsm_dlci *dlci) +{ + kref_put(&dlci->ref, gsm_dlci_free); +} + +/** + * gsm_dlci_release - release DLCI * @dlci: DLCI to destroy * - * Free up a DLCI. Currently to keep the lifetime rules sane we only - * clean up DLCI objects when the MUX closes rather than as the port - * is closed down on both the tty and mux levels. + * Release a DLCI. Actual free is deferred until either + * mux is closed or tty is closed - whichever is last. * * Can sleep. */ -static void gsm_dlci_free(struct gsm_dlci *dlci) +static void gsm_dlci_release(struct gsm_dlci *dlci) { struct tty_struct *tty = tty_port_tty_get(&dlci->port); if (tty) { tty_vhangup(tty); tty_kref_put(tty); } - del_timer_sync(&dlci->t1); - dlci->gsm->dlci[dlci->addr] = NULL; - kfifo_free(dlci->fifo); - kfree(dlci); + dlci_put(dlci); } /* @@ -1823,10 +1885,6 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c) break; case GSM_FCS: /* FCS follows the packet */ gsm->received_fcs = c; - if (c == GSM0_SOF) { - gsm->state = GSM_SEARCH; - break; - } gsm_queue(gsm); gsm->state = GSM_SSOF; break; @@ -1970,7 +2028,7 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) /* Free up any link layer users */ for (i = 0; i < NUM_DLCI; i++) if (gsm->dlci[i]) - gsm_dlci_free(gsm->dlci[i]); + gsm_dlci_release(gsm->dlci[i]); /* Now wipe the queues */ for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) { gsm->tx_head = txq->next; @@ -2010,6 +2068,7 @@ int gsm_activate_mux(struct gsm_mux *gsm) spin_lock(&gsm_mux_lock); for (i = 0; i < MAX_MUX; i++) { if (gsm_mux[i] == NULL) { + gsm->num = i; gsm_mux[i] = gsm; break; } @@ -2030,8 +2089,7 @@ EXPORT_SYMBOL_GPL(gsm_activate_mux); * gsm_free_mux - free up a mux * @mux: mux to free * - * Dispose of allocated resources for a dead mux. No refcounting - * at present so the mux must be truly dead. + * Dispose of allocated resources for a dead mux */ void gsm_free_mux(struct gsm_mux *gsm) { @@ -2042,6 +2100,28 @@ void gsm_free_mux(struct gsm_mux *gsm) EXPORT_SYMBOL_GPL(gsm_free_mux); /** + * gsm_free_muxr - free up a mux + * @mux: mux to free + * + * Dispose of allocated resources for a dead mux + */ +static void gsm_free_muxr(struct kref *ref) +{ + struct gsm_mux *gsm = container_of(ref, struct gsm_mux, ref); + gsm_free_mux(gsm); +} + +static inline void mux_get(struct gsm_mux *gsm) +{ + kref_get(&gsm->ref); +} + +static inline void mux_put(struct gsm_mux *gsm) +{ + kref_put(&gsm->ref, gsm_free_muxr); +} + +/** * gsm_alloc_mux - allocate a mux * * Creates a new mux ready for activation. @@ -2064,12 +2144,12 @@ struct gsm_mux *gsm_alloc_mux(void) return NULL; } spin_lock_init(&gsm->lock); + kref_init(&gsm->ref); gsm->t1 = T1; gsm->t2 = T2; gsm->n2 = N2; gsm->ftype = UIH; - gsm->initiator = 0; gsm->adaption = 1; gsm->encoding = 1; gsm->mru = 64; /* Default to encoding 1 so these should be 64 */ @@ -2115,13 +2195,20 @@ static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len) static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) { - int ret; + int ret, i; + int base = gsm->num << 6; /* Base for this MUX */ gsm->tty = tty_kref_get(tty); gsm->output = gsmld_output; ret = gsm_activate_mux(gsm); if (ret != 0) tty_kref_put(gsm->tty); + else { + /* Don't register device 0 - this is the control channel and not + a usable tty interface */ + for (i = 1; i < NUM_DLCI; i++) + tty_register_device(gsm_tty_driver, base + i, NULL); + } return ret; } @@ -2136,7 +2223,12 @@ static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) { + int i; + int base = gsm->num << 6; /* Base for this MUX */ + WARN_ON(tty != gsm->tty); + for (i = 1; i < NUM_DLCI; i++) + tty_unregister_device(gsm_tty_driver, base + i); gsm_cleanup_mux(gsm); tty_kref_put(gsm->tty); gsm->tty = NULL; @@ -2224,7 +2316,7 @@ static void gsmld_close(struct tty_struct *tty) gsmld_flush_buffer(tty); /* Do other clean up here */ - gsm_free_mux(gsm); + mux_put(gsm); } /** @@ -2476,6 +2568,220 @@ static int gsmld_ioctl(struct tty_struct *tty, struct file *file, } } +/* + * Network interface + * + */ + +static int gsm_mux_net_open(struct net_device *net) +{ + pr_debug("%s called\n", __func__); + netif_start_queue(net); + return 0; +} + +static int gsm_mux_net_close(struct net_device *net) +{ + netif_stop_queue(net); + return 0; +} + +static struct net_device_stats *gsm_mux_net_get_stats(struct net_device *net) +{ + return &((struct gsm_mux_net *)netdev_priv(net))->stats; +} +static void dlci_net_free(struct gsm_dlci *dlci) +{ + if (!dlci->net) { + WARN_ON(1); + return; + } + dlci->adaption = dlci->prev_adaption; + dlci->data = dlci->prev_data; + free_netdev(dlci->net); + dlci->net = NULL; +} +static void net_free(struct kref *ref) +{ + struct gsm_mux_net *mux_net; + struct gsm_dlci *dlci; + + mux_net = container_of(ref, struct gsm_mux_net, ref); + dlci = mux_net->dlci; + + if (dlci->net) { + unregister_netdev(dlci->net); + dlci_net_free(dlci); + } +} + +static inline void muxnet_get(struct gsm_mux_net *mux_net) +{ + kref_get(&mux_net->ref); +} + +static inline void muxnet_put(struct gsm_mux_net *mux_net) +{ + kref_put(&mux_net->ref, net_free); +} + +static int gsm_mux_net_start_xmit(struct sk_buff *skb, + struct net_device *net) +{ + struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); + struct gsm_dlci *dlci = mux_net->dlci; + muxnet_get(mux_net); + + skb_queue_head(&dlci->skb_list, skb); + STATS(net).tx_packets++; + STATS(net).tx_bytes += skb->len; + gsm_dlci_data_kick(dlci); + /* And tell the kernel when the last transmit started. */ + net->trans_start = jiffies; + muxnet_put(mux_net); + return NETDEV_TX_OK; +} + +/* called when a packet did not ack after watchdogtimeout */ +static void gsm_mux_net_tx_timeout(struct net_device *net) +{ + /* Tell syslog we are hosed. */ + dev_dbg(&net->dev, "Tx timed out.\n"); + + /* Update statistics */ + STATS(net).tx_errors++; +} + +static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, + unsigned char *in_buf, int size) +{ + struct net_device *net = dlci->net; + struct sk_buff *skb; + struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); + muxnet_get(mux_net); + + /* Allocate an sk_buff */ + skb = dev_alloc_skb(size + NET_IP_ALIGN); + if (!skb) { + /* We got no receive buffer. */ + STATS(net).rx_dropped++; + muxnet_put(mux_net); + return; + } + skb_reserve(skb, NET_IP_ALIGN); + memcpy(skb_put(skb, size), in_buf, size); + + skb->dev = net; + skb->protocol = __constant_htons(ETH_P_IP); + + /* Ship it off to the kernel */ + netif_rx(skb); + + /* update out statistics */ + STATS(net).rx_packets++; + STATS(net).rx_bytes += size; + muxnet_put(mux_net); + return; +} + +int gsm_change_mtu(struct net_device *net, int new_mtu) +{ + struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); + if ((new_mtu < 8) || (new_mtu > mux_net->dlci->gsm->mtu)) + return -EINVAL; + net->mtu = new_mtu; + return 0; +} + +static void gsm_mux_net_init(struct net_device *net) +{ + static const struct net_device_ops gsm_netdev_ops = { + .ndo_open = gsm_mux_net_open, + .ndo_stop = gsm_mux_net_close, + .ndo_start_xmit = gsm_mux_net_start_xmit, + .ndo_tx_timeout = gsm_mux_net_tx_timeout, + .ndo_get_stats = gsm_mux_net_get_stats, + .ndo_change_mtu = gsm_change_mtu, + }; + + net->netdev_ops = &gsm_netdev_ops; + + /* fill in the other fields */ + net->watchdog_timeo = GSM_NET_TX_TIMEOUT; + net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + net->type = ARPHRD_NONE; + net->tx_queue_len = 10; +} + + +/* caller holds the dlci mutex */ +static void gsm_destroy_network(struct gsm_dlci *dlci) +{ + struct gsm_mux_net *mux_net; + + pr_debug("destroy network interface"); + if (!dlci->net) + return; + mux_net = (struct gsm_mux_net *)netdev_priv(dlci->net); + muxnet_put(mux_net); +} + + +/* caller holds the dlci mutex */ +static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc) +{ + char *netname; + int retval = 0; + struct net_device *net; + struct gsm_mux_net *mux_net; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Already in a non tty mode */ + if (dlci->adaption > 2) + return -EBUSY; + + if (nc->protocol != htons(ETH_P_IP)) + return -EPROTONOSUPPORT; + + if (nc->adaption != 3 && nc->adaption != 4) + return -EPROTONOSUPPORT; + + pr_debug("create network interface"); + + netname = "gsm%d"; + if (nc->if_name[0] != '\0') + netname = nc->if_name; + net = alloc_netdev(sizeof(struct gsm_mux_net), + netname, + gsm_mux_net_init); + if (!net) { + pr_err("alloc_netdev failed"); + return -ENOMEM; + } + net->mtu = dlci->gsm->mtu; + mux_net = (struct gsm_mux_net *)netdev_priv(net); + mux_net->dlci = dlci; + kref_init(&mux_net->ref); + strncpy(nc->if_name, net->name, IFNAMSIZ); /* return net name */ + + /* reconfigure dlci for network */ + dlci->prev_adaption = dlci->adaption; + dlci->prev_data = dlci->data; + dlci->adaption = nc->adaption; + dlci->data = gsm_mux_rx_netchar; + dlci->net = net; + + pr_debug("register netdev"); + retval = register_netdev(net); + if (retval) { + pr_err("network register fail %d\n", retval); + dlci_net_free(dlci); + return retval; + } + return net->ifindex; /* return network index */ +} /* Line discipline for real tty */ struct tty_ldisc_ops tty_ldisc_packet = { @@ -2579,6 +2885,9 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) port = &dlci->port; port->count++; tty->driver_data = dlci; + dlci_get(dlci); + dlci_get(dlci->gsm->dlci[0]); + mux_get(dlci->gsm); tty_port_tty_set(port, tty); dlci->modem_rx = 0; @@ -2594,13 +2903,23 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) static void gsmtty_close(struct tty_struct *tty, struct file *filp) { struct gsm_dlci *dlci = tty->driver_data; + struct gsm_mux *gsm; + if (dlci == NULL) return; + mutex_lock(&dlci->mutex); + gsm_destroy_network(dlci); + mutex_unlock(&dlci->mutex); + gsm = dlci->gsm; if (tty_port_close_start(&dlci->port, tty, filp) == 0) - return; + goto out; gsm_dlci_begin_close(dlci); tty_port_close_end(&dlci->port, tty); tty_port_tty_set(&dlci->port, NULL); +out: + dlci_put(dlci); + dlci_put(gsm->dlci[0]); + mux_put(gsm); } static void gsmtty_hangup(struct tty_struct *tty) @@ -2677,7 +2996,32 @@ static int gsmtty_tiocmset(struct tty_struct *tty, static int gsmtty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { - return -ENOIOCTLCMD; + struct gsm_dlci *dlci = tty->driver_data; + struct gsm_netconfig nc; + int index; + + switch (cmd) { + case GSMIOC_ENABLE_NET: + if (copy_from_user(&nc, (void __user *)arg, sizeof(nc))) + return -EFAULT; + nc.if_name[IFNAMSIZ-1] = '\0'; + /* return net interface index or error code */ + mutex_lock(&dlci->mutex); + index = gsm_create_network(dlci, &nc); + mutex_unlock(&dlci->mutex); + if (copy_to_user((void __user *)arg, &nc, sizeof(nc))) + return -EFAULT; + return index; + case GSMIOC_DISABLE_NET: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + mutex_lock(&dlci->mutex); + gsm_destroy_network(dlci); + mutex_unlock(&dlci->mutex); + return 0; + default: + return -ENOIOCTLCMD; + } } static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) @@ -2726,7 +3070,6 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state) return gsmtty_modem_update(dlci, encode); } -static struct tty_driver *gsm_tty_driver; /* Virtual ttys for the demux */ static const struct tty_operations gsmtty_ops = { diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index c3954fbf6ac4..39d6ab6551e0 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -185,7 +185,6 @@ static void reset_buffer_flags(struct tty_struct *tty) tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); n_tty_set_room(tty); - check_unthrottle(tty); } /** @@ -1587,6 +1586,7 @@ static int n_tty_open(struct tty_struct *tty) return -ENOMEM; } reset_buffer_flags(tty); + tty_unthrottle(tty); tty->column = 0; n_tty_set_termios(tty, NULL); tty->minimum_to_wake = 1; diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index b4129f53fb1b..f2dfec82faf8 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -81,7 +81,7 @@ static unsigned int skip_txen_test; /* force skip of txen test at init time */ #define DEBUG_INTR(fmt...) do { } while (0) #endif -#define PASS_LIMIT 256 +#define PASS_LIMIT 512 #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) @@ -1107,7 +1107,7 @@ static void autoconfig_16550a(struct uart_8250_port *up) */ DEBUG_AUTOCONF("Xscale "); up->port.type = PORT_XSCALE; - up->capabilities |= UART_CAP_UUE; + up->capabilities |= UART_CAP_UUE | UART_CAP_RTOIE; return; } } else { diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index cf35e0dc5085..6b887d90a205 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -39,6 +39,7 @@ struct pci_serial_quirk { u32 device; u32 subvendor; u32 subdevice; + int (*probe)(struct pci_dev *dev); int (*init)(struct pci_dev *dev); int (*setup)(struct serial_private *, const struct pciserial_board *, @@ -56,6 +57,9 @@ struct serial_private { int line[0]; }; +static int pci_default_setup(struct serial_private*, + const struct pciserial_board*, struct uart_port*, int); + static void moan_device(const char *str, struct pci_dev *dev) { printk(KERN_WARNING @@ -571,6 +575,28 @@ static const struct timedia_struct { { 8, timedia_eight_port } }; +/* + * There are nearly 70 different Timedia/SUNIX PCI serial devices. Instead of + * listing them individually, this driver merely grabs them all with + * PCI_ANY_ID. Some of these devices, however, also feature a parallel port, + * and should be left free to be claimed by parport_serial instead. + */ +static int pci_timedia_probe(struct pci_dev *dev) +{ + /* + * Check the third digit of the subdevice ID + * (0,2,3,5,6: serial only -- 7,8,9: serial + parallel) + */ + if ((dev->subsystem_device & 0x00f0) >= 0x70) { + dev_info(&dev->dev, + "ignoring Timedia subdevice %04x for parport_serial\n", + dev->subsystem_device); + return -ENODEV; + } + + return 0; +} + static int pci_timedia_init(struct pci_dev *dev) { const unsigned short *ids; @@ -752,6 +778,62 @@ pci_ni8430_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, board->reg_shift); } +static int pci_netmos_9900_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar; + + if ((priv->dev->subsystem_device & 0xff00) == 0x3000) { + /* netmos apparently orders BARs by datasheet layout, so serial + * ports get BARs 0 and 3 (or 1 and 4 for memmapped) + */ + bar = 3 * idx; + + return setup_port(priv, port, bar, 0, board->reg_shift); + } else { + return pci_default_setup(priv, board, port, idx); + } +} + +/* the 99xx series comes with a range of device IDs and a variety + * of capabilities: + * + * 9900 has varying capabilities and can cascade to sub-controllers + * (cascading should be purely internal) + * 9904 is hardwired with 4 serial ports + * 9912 and 9922 are hardwired with 2 serial ports + */ +static int pci_netmos_9900_numports(struct pci_dev *dev) +{ + unsigned int c = dev->class; + unsigned int pi; + unsigned short sub_serports; + + pi = (c & 0xff); + + if (pi == 2) { + return 1; + } else if ((pi == 0) && + (dev->device == PCI_DEVICE_ID_NETMOS_9900)) { + /* two possibilities: 0x30ps encodes number of parallel and + * serial ports, or 0x1000 indicates *something*. This is not + * immediately obvious, since the 2s1p+4s configuration seems + * to offer all functionality on functions 0..2, while still + * advertising the same function 3 as the 4s+2s1p config. + */ + sub_serports = dev->subsystem_device & 0xf; + if (sub_serports > 0) { + return sub_serports; + } else { + printk(KERN_NOTICE "NetMos/Mostech serial driver ignoring port on ambiguous config.\n"); + return 0; + } + } + + moan_device("unknown NetMos/Mostech program interface", dev); + return 0; +} static int pci_netmos_init(struct pci_dev *dev) { @@ -761,12 +843,28 @@ static int pci_netmos_init(struct pci_dev *dev) if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) || (dev->device == PCI_DEVICE_ID_NETMOS_9865)) return 0; + if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && dev->subsystem_device == 0x0299) return 0; + switch (dev->device) { /* FALLTHROUGH on all */ + case PCI_DEVICE_ID_NETMOS_9904: + case PCI_DEVICE_ID_NETMOS_9912: + case PCI_DEVICE_ID_NETMOS_9922: + case PCI_DEVICE_ID_NETMOS_9900: + num_serial = pci_netmos_9900_numports(dev); + break; + + default: + if (num_serial == 0 ) { + moan_device("unknown NetMos/Mostech device", dev); + } + } + if (num_serial == 0) return -ENODEV; + return num_serial; } @@ -1396,6 +1494,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .device = PCI_DEVICE_ID_TIMEDIA_1889, .subvendor = PCI_VENDOR_ID_TIMEDIA, .subdevice = PCI_ANY_ID, + .probe = pci_timedia_probe, .init = pci_timedia_init, .setup = pci_timedia_setup, }, @@ -1426,7 +1525,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .init = pci_netmos_init, - .setup = pci_default_setup, + .setup = pci_netmos_9900_setup, }, /* * For Oxford Semiconductor Tornado based devices @@ -1703,6 +1802,7 @@ enum pci_board_num_t { pbn_ADDIDATA_PCIe_8_3906250, pbn_ce4100_1_115200, pbn_omegapci, + pbn_NETMOS9900_2s_115200, }; /* @@ -2404,6 +2504,11 @@ static struct pciserial_board pci_boards[] __devinitdata = { .base_baud = 115200, .uart_offset = 0x200, }, + [pbn_NETMOS9900_2s_115200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + }, }; static const struct pci_device_id softmodem_blacklist[] = { @@ -2640,11 +2745,19 @@ EXPORT_SYMBOL_GPL(pciserial_resume_ports); static int __devinit pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { + struct pci_serial_quirk *quirk; struct serial_private *priv; const struct pciserial_board *board; struct pciserial_board tmp; int rc; + quirk = find_quirk(dev); + if (quirk->probe) { + rc = quirk->probe(dev); + if (rc) + return rc; + } + if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n", ent->driver_data); @@ -2654,6 +2767,7 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) board = &pci_boards[ent->driver_data]; rc = pci_enable_device(dev); + pci_save_state(dev); if (rc) return rc; @@ -3885,6 +3999,27 @@ static struct pci_device_id serial_pci_tbl[] = { 0xA000, 0x1000, 0, 0, pbn_b0_1_115200 }, + /* the 9901 is a rebranded 9912 */ + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9904, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3002, + 0, 0, pbn_NETMOS9900_2s_115200 }, + /* * Best Connectivity PCI Multi I/O cards */ @@ -3927,6 +4062,51 @@ static struct pci_device_id serial_pci_tbl[] = { { 0, } }; +static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev, + pci_channel_state_t state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + + if (priv) + pciserial_suspend_ports(priv); + + pci_disable_device(dev); + + return PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev) +{ + int rc; + + rc = pci_enable_device(dev); + + if (rc) + return PCI_ERS_RESULT_DISCONNECT; + + pci_restore_state(dev); + pci_save_state(dev); + + return PCI_ERS_RESULT_RECOVERED; +} + +static void serial8250_io_resume(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) + pciserial_resume_ports(priv); +} + +static struct pci_error_handlers serial8250_err_handler = { + .error_detected = serial8250_io_error_detected, + .slot_reset = serial8250_io_slot_reset, + .resume = serial8250_io_resume, +}; + static struct pci_driver serial_pci_driver = { .name = "serial", .probe = pciserial_init_one, @@ -3936,6 +4116,7 @@ static struct pci_driver serial_pci_driver = { .resume = pciserial_resume_one, #endif .id_table = serial_pci_tbl, + .err_handler = &serial8250_err_handler, }; static int __init serial8250_pci_init(void) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 8f41e1123461..cb40b82daf36 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1404,7 +1404,7 @@ config SERIAL_SC26XX config SERIAL_SC26XX_CONSOLE bool "Console on SC2681/SC2692 serial port" - depends on SERIAL_SC26XX + depends on SERIAL_SC26XX=y select SERIAL_CORE_CONSOLE help Support for Console on SC2681/SC2692 serial ports. diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 465210930890..846dfcd3ce0d 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -14,6 +14,7 @@ *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/serial_reg.h> #include <linux/slab.h> #include <linux/module.h> @@ -44,6 +45,7 @@ enum { /* Set the max number of UART port * Intel EG20T PCH: 4 port * OKI SEMICONDUCTOR ML7213 IOH: 3 port + * OKI SEMICONDUCTOR ML7223 IOH: 2 port */ #define PCH_UART_NR 4 @@ -137,8 +139,6 @@ enum { #define PCH_UART_DLL 0x00 #define PCH_UART_DLM 0x01 -#define DIV_ROUND(a, b) (((a) + ((b)/2)) / (b)) - #define PCH_UART_IID_RLS (PCH_UART_IIR_REI) #define PCH_UART_IID_RDR (PCH_UART_IIR_RRI) #define PCH_UART_IID_RDR_TO (PCH_UART_IIR_RRI | PCH_UART_IIR_TOI) @@ -316,7 +316,7 @@ static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud, unsigned int dll, dlm, lcr; int div; - div = DIV_ROUND(priv->base_baud / 16, baud); + div = DIV_ROUND_CLOSEST(priv->base_baud / 16, baud); if (div < 0 || USHRT_MAX <= div) { dev_err(priv->port.dev, "Invalid Baud(div=0x%x)\n", div); return -EINVAL; @@ -1429,6 +1429,8 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, goto init_port_hal_free; } + pci_enable_msi(pdev); + iobase = pci_resource_start(pdev, 0); mapbase = pci_resource_start(pdev, 1); priv->mapbase = mapbase; @@ -1485,6 +1487,8 @@ static void pch_uart_pci_remove(struct pci_dev *pdev) struct eg20t_port *priv; priv = (struct eg20t_port *)pci_get_drvdata(pdev); + + pci_disable_msi(pdev); pch_uart_exit_port(priv); pci_disable_device(pdev); kfree(priv); @@ -1568,6 +1572,7 @@ static int __devinit pch_uart_pci_probe(struct pci_dev *pdev, return ret; probe_disable_device: + pci_disable_msi(pdev); pci_disable_device(pdev); probe_error: return ret; diff --git a/drivers/tty/serial/s5pv210.c b/drivers/tty/serial/s5pv210.c index 8dd160c96e87..8b0b888a1b76 100644 --- a/drivers/tty/serial/s5pv210.c +++ b/drivers/tty/serial/s5pv210.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/serial_core.h> #include <linux/serial.h> +#include <linux/delay.h> #include <asm/irq.h> #include <mach/hardware.h> @@ -83,6 +84,9 @@ static int s5pv210_serial_resetport(struct uart_port *port, wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); wr_regl(port, S3C2410_UFCON, cfg->ufcon); + /* It is need to delay When reset FIFO register */ + udelay(1); + return 0; } diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 7ead42104c67..afc629423152 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1194,12 +1194,10 @@ int __devexit s3c24xx_serial_remove(struct platform_device *dev) EXPORT_SYMBOL_GPL(s3c24xx_serial_remove); /* UART power management code */ - -#ifdef CONFIG_PM - -static int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int s3c24xx_serial_suspend(struct device *dev) { - struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); + struct uart_port *port = s3c24xx_dev_to_port(dev); if (port) uart_suspend_port(&s3c24xx_uart_drv, port); @@ -1207,9 +1205,9 @@ static int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t stat return 0; } -static int s3c24xx_serial_resume(struct platform_device *dev) +static int s3c24xx_serial_resume(struct device *dev) { - struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); + struct uart_port *port = s3c24xx_dev_to_port(dev); struct s3c24xx_uart_port *ourport = to_ourport(port); if (port) { @@ -1222,17 +1220,20 @@ static int s3c24xx_serial_resume(struct platform_device *dev) return 0; } -#endif + +static const struct dev_pm_ops s3c24xx_serial_pm_ops = { + .suspend = s3c24xx_serial_suspend, + .resume = s3c24xx_serial_resume, +}; +#else /* !CONFIG_PM_SLEEP */ +#define s3c24xx_serial_pm_ops NULL +#endif /* CONFIG_PM_SLEEP */ int s3c24xx_serial_init(struct platform_driver *drv, struct s3c24xx_uart_info *info) { dbg("s3c24xx_serial_init(%p,%p)\n", drv, info); - -#ifdef CONFIG_PM - drv->suspend = s3c24xx_serial_suspend; - drv->resume = s3c24xx_serial_resume; -#endif + drv->driver.pm = &s3c24xx_serial_pm_ops; return platform_driver_register(drv); } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 6556f7452ba6..150e4f747c7d 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -94,6 +94,7 @@ #include <linux/delay.h> #include <linux/seq_file.h> #include <linux/serial.h> +#include <linux/ratelimit.h> #include <linux/uaccess.h> #include <asm/system.h> @@ -1420,8 +1421,7 @@ err_module_put: /* call the tty release_tty routine to clean out this slot */ err_release_tty: - if (printk_ratelimit()) - printk(KERN_INFO "tty_init_dev: ldisc open failed, " + printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); return ERR_PTR(retval); |