免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5828 | 回复: 15
打印 上一主题 下一主题

请教, INT_TC和INT_ADC没有响应中断? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-01 10:02 |只看该作者 |倒序浏览
在s3c2410板上移植一个触摸屏的驱动程序。在加载驱动,建立相关的设备节点后,cat /dev/tsc 进行简单测试时,点击触摸屏没有输出,驱动停在 read中。经过简单调试,发现用writel来设置arm的寄存器,readl读出寄存器值,没有发生变化,寄存器的值一直是初始值。(上面部分以解决)。不能响应INT_TC和INT_ADC中断。请各位帮忙看看。
cat /dev/tsc部分信息:
touchscreen s3c2410_ts_read
ADCCON = 0x4c40
ADCTSC = 0xc7
ADCDLY = 0xff
ADCDAT0 = 0xf000
ADCDAT1 = 0xf000
CLKCON = 0x8fff0
SUBSRCPND = 0x92
INTSUBMSK = 0x1fe
INTMSK = 0x6b4e3dcf


附上驱动源代码。
#define PEN_UP            0   
#define PEN_DOWN    1
#define PEN_FLEETING    2
#define MAX_TS_BUF    8    /* how many do we want to buffer */

#define DRIVER_NAME    "touchscreen"
#define TSRAW_MAJOR    224
#define TSRAW_MINOR    1

#define PDEBUG
#ifdef PDEBUG
#define pr_debug(fmt,arg...) \
    printk(KERN_INFO fmt,##arg)
#else
#define pr_debug(fmt,arg...)
#endif

typedef struct {
  unsigned short pressure;
  unsigned short x;
  unsigned short y;
  unsigned short pad;
} tsret;

typedef struct {
    struct cdev cdev;
    unsigned int penStatus;        /* PEN_UP, PEN_DOWN, PEN_SAMPLE */
    tsret buf[MAX_TS_BUF];        /* protect against overrun */
    unsigned int head, tail;    /* head and tail for queued events */
    wait_queue_head_t wq;
    spinlock_t lock;
    int xp;
    int yp;
}tsdev;

static tsdev *ts;
static dev_t devno;
static int adc_state = 0;
static void __iomem *base_addr;
static struct clk *adc_clock;

#define BUF_HEAD    (ts->buf[ts->head])
#define BUF_TAIL    (ts->buf[ts->tail])
#define INCBUF(x,mod)     ((++(x)) & ((mod) - 1))

static void (*tsEvent)(void);

#define WAIT4INT      (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | \
            S3C2410_ADCTSC_AUTO_PST    | S3C2410_ADCTSC_XY_PST(3) )

#define MODE_X        (S3C2410_ADCTSC_XM_SEN | S3C2410_ADCTSC_YP_SEN | \
            S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_PULL_UP_DISABLE | \
            S3C2410_ADCTSC_XY_PST(1))

#define MODE_Y        (S3C2410_ADCTSC_XP_SEN | S3C2410_ADCTSC_YM_SEN | \
            S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_PULL_UP_DISABLE | \
            S3C2410_ADCTSC_XY_PST(2))

#define ADC_X        (S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(49) | \
            S3C2410_ADCCON_SELMUX(7)| S3C2410_ADCCON_ENABLE_START)

#define ADC_Y        (S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(49) | \
            S3C2410_ADCCON_SELMUX(5)| S3C2410_ADCCON_ENABLE_START)

static inline void user_print(void)
{
    pr_debug("ADCCON = 0x%x\n",readl(base_addr + S3C2410_ADCCON));
    pr_debug("ADCTSC = 0x%x\n",readl(base_addr + S3C2410_ADCTSC));
    pr_debug("ADCDLY = 0x%x\n",readl(base_addr + S3C2410_ADCDLY));
    pr_debug("ADCDAT0 = 0x%x\n",readl(base_addr + S3C2410_ADCDAT0));
    pr_debug("ADCDAT1 = 0x%x\n",readl(base_addr + S3C2410_ADCDAT1));

    pr_debug("CLKCON = 0x%x\n",readl(S3C2410_CLKCON));

    pr_debug("SUBSRCPND = 0x%x\n",readl(S3C2410_SUBSRCPND));
    pr_debug("INTSUBMSK = 0x%x\n",readl(S3C2410_INTSUBMSK));
    pr_debug("INTMSK = 0x%x\n",readl(S3C2410_INTMSK));
}

static inline void mode_x_axis(void)
{
    writel(MODE_X, base_addr+S3C2410_ADCTSC);
}

static inline void mode_y_axis(void)
{
    writel(MODE_Y, base_addr+S3C2410_ADCTSC);
}

static inline void start_adc_x(void)
{
    writel(ADC_X, base_addr+S3C2410_ADCCON);
}

static inline void start_adc_y(void)
{
    writel(ADC_Y, base_addr+S3C2410_ADCCON);
}

static inline void  disable_ts_adc(void)
{
    writel(readl(base_addr+S3C2410_ADCCON) & (~(S3C2410_ADCCON_READ_START)),
            base_addr+S3C2410_ADCCON);
}

static void tsEvent_raw(void)
{
    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);

    if (ts->penStatus == PEN_DOWN) {
        BUF_HEAD.x = ts->xp;
        BUF_HEAD.y = ts->yp;
        BUF_HEAD.pressure = PEN_DOWN;

    } else {
        
        BUF_HEAD.x = 0;
        BUF_HEAD.y = 0;
        BUF_HEAD.pressure = PEN_UP;
    }

    ts->head = INCBUF(ts->head, MAX_TS_BUF);
    wake_up_interruptible(&(ts->wq));
}

static int tsRead(tsret * ts_ret)
{
    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);

    spin_lock_irq(&(ts->lock));
    ts_ret->x = BUF_TAIL.x;
    ts_ret->y = BUF_TAIL.y;
    ts_ret->pressure = BUF_TAIL.pressure;
    ts->tail = INCBUF(ts->tail, MAX_TS_BUF);
    spin_unlock_irq(&(ts->lock));

    return sizeof(tsret);
}


static ssize_t s3c2410_ts_read(struct file *filp, __user char *buffer, size_t count, loff_t *ppos)
{
    tsret ts_ret;

    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
    user_print();

retry:
    if (ts->head != ts->tail) {
        int count;
        count = tsRead(&ts_ret);
        if(count){
            copy_to_user(buffer, (char *)&ts_ret, count);
        }
        pr_debug( "copy to user\n" );
        return count;
    } else {
        if (filp->f_flags & O_NONBLOCK){
            pr_debug( "error: nonblock\n" );
            return -EAGAIN;
        }
        pr_debug( "sleep\n" );
        interruptible_sleep_on(&(ts->wq));

        if (signal_pending(current)){
            pr_debug( "error pending\n" );
            return -ERESTARTSYS;
        }

        pr_debug( "%s %s retry.\n", DRIVER_NAME, __FUNCTION__);

        goto retry;
    }

    return sizeof(tsret);
}

static unsigned int s3c2410_ts_poll(struct file *filp, struct poll_table_struct *wait)
{
    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);

    poll_wait(filp, &(ts->wq), wait);
    return (ts->head == ts->tail) ? 0 : (POLLIN | POLLRDNORM);
}

static inline void start_ts_adc(void)
{
    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);

    adc_state = 0;
    mode_x_axis();

    start_adc_x();

}

static  void s3c2410_get_XY(void)
{
    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);

    if (adc_state == 0) {
        adc_state = 1;
        disable_ts_adc();

        ts->yp = readl(base_addr + S3C2410_ADCDAT0) & 0x3ff;
        mode_y_axis();

        start_adc_y();

    } else if (adc_state == 1) {
        adc_state = 0;
        disable_ts_adc();

        ts->xp = readl(base_addr + S3C2410_ADCDAT1) & 0x3ff;
        ts->penStatus = PEN_DOWN;
        pr_debug( "PEN DOWN: x: %08d, y: %08d\n", ts->xp, ts->yp);
//        wait_up_int();
        writel(WAIT4INT, base_addr + S3C2410_ADCTSC);

        tsEvent();
    }
}

static irqreturn_t s3c2410_isr_adc(int irq, void *dev_id)
{
    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);

    spin_lock_irq(&(ts->lock));
    if (ts->penStatus == PEN_UP)
      s3c2410_get_XY();

    spin_unlock_irq(&(ts->lock));

    return IRQ_HANDLED;
}

static irqreturn_t s3c2410_isr_tc(int irq, void *dev_id)
{
    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);

    spin_lock_irq(&(ts->lock));
    if (ts->penStatus == PEN_UP) {
      start_ts_adc();
    }else{
      ts->penStatus = PEN_UP;
      pr_debug( "PEN UP: x: %08d, y: %08d\n", ts->xp, ts->yp);
      writel(WAIT4INT, base_addr + S3C2410_ADCTSC);
      tsEvent();
    }
    spin_unlock_irq(&(ts->lock));

    return IRQ_HANDLED;
}

static int s3c2410_ts_open(struct inode *inode, struct file *filp)
{
    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);

    filp->private_data = ts;

    ts->head = ts->tail = 0;
    ts->penStatus = PEN_UP;

    tsEvent = tsEvent_raw;
    init_waitqueue_head(&(ts->wq));

    return 0;
}

static int s3c2410_ts_release(struct inode *inode, struct file *filp)
{
    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);

    return 0;
}

static struct file_operations s3c2410ts_fops = {
    .owner        = THIS_MODULE,
    .open        = s3c2410_ts_open,
    .read        = s3c2410_ts_read,   
    .release    = s3c2410_ts_release,
    .poll        = s3c2410_ts_poll,
};

void tsEvent_dummy(void) {}

static int __init s3c2410_ts_init(void)
{
    int err;

    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);

    tsEvent = tsEvent_dummy;

    adc_clock = clk_get(NULL, "adc");
    if (!adc_clock) {
        printk(KERN_WARNING "failed to get adc clock source\n");
        return -ENOENT;
    }
    clk_enable(adc_clock);

    base_addr = ioremap(S3C2410_PA_ADC,0x20);
    if(base_addr == NULL){
        printk(KERN_WARNING "%s : Can't remap register block!\n",DRIVER_NAME);
        return -ENOMEM;
    }
    pr_debug( "ioremap base_addr = %x\n",(unsigned int)base_addr);

    /* set gpio to XP, YM, YP and  YM */
    s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
    s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
    s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
    s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
   
    writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(49), base_addr + S3C2410_ADCCON);
    writel(WAIT4INT, base_addr + S3C2410_ADCTSC);
    writel(readl(S3C2410_INTMSK)&0x7fffffff, S3C2410_INTMSK);
    pr_debug( "%s : initial pin connection.\n",DRIVER_NAME);

    if(TSRAW_MAJOR){
        devno = MKDEV(TSRAW_MAJOR,TSRAW_MINOR);
        err = register_chrdev_region(devno,1,DRIVER_NAME);
    }else{
        err = alloc_chrdev_region(&devno,0,1,DRIVER_NAME);
    }
    if(err < 0){
        printk(KERN_WARNING "%s :Can't get major!\n",DRIVER_NAME);
        goto major_failed;
    }else{
        pr_debug( "%s : get major.\n",DRIVER_NAME);
    }

    ts = kmalloc(sizeof(tsdev),GFP_KERNEL);
    if(!ts){
        printk(KERN_WARNING "%s : Can't malloc memory!\n",DRIVER_NAME);
        err =  -ENOMEM;
        goto allc_err;
    }
    memset(ts, 0, sizeof(tsdev));
    cdev_init(&(ts->cdev), &s3c2410ts_fops);
    ts->cdev.owner = THIS_MODULE;
    ts->cdev.ops = &s3c2410ts_fops;
    pr_debug( "%s : get memory.\n",DRIVER_NAME);

    /* Enable touch interrupt */
    err = request_irq(IRQ_ADC, s3c2410_isr_adc, IRQF_SHARED, DRIVER_NAME, &(ts->cdev));
    if(err){
        printk(KERN_WARNING "Could not allocate ts IRQ_ADC!\n");
        err = -EIO;
        goto adc_failed;
    }
    pr_debug( "%s : register IRQ_ADC.\n",DRIVER_NAME);

    err = request_irq(IRQ_TC, s3c2410_isr_tc, IRQF_SHARED, DRIVER_NAME, &(ts->cdev));
    if(err){
        printk(KERN_WARNING "Could not allocate ts IRQ_TC!\n");
        err = -EIO;
        goto tc_failed;
    }
    pr_debug( "%s : register IRQ_TC.\n",DRIVER_NAME);

    err = cdev_add(&(ts->cdev),devno,1);
    if(err < 0){
        printk(KERN_WARNING "%s : add cdev error!\n",DRIVER_NAME);
        goto failed;
    }

    pr_debug( "char device %s loaded successfully!\n",DRIVER_NAME);

    return 0;

failed:
    free_irq(IRQ_TC, &(ts->cdev));

tc_failed:
    free_irq(IRQ_ADC, &(ts->cdev));

adc_failed:
    kfree(ts);

allc_err:
    unregister_chrdev_region(devno,1);

major_failed:
    iounmap(base_addr);
    clk_disable(adc_clock);
    clk_put(adc_clock);
    adc_clock = NULL;

    return err;
}

static void __exit s3c2410_ts_exit(void)
{
    pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);

    cdev_del(&(ts->cdev));
    free_irq(IRQ_TC, &(ts->cdev));
    free_irq(IRQ_ADC, &(ts->cdev));
    kfree(ts);
    unregister_chrdev_region(devno,1);
    iounmap(base_addr);
    if(adc_clock){
        clk_disable(adc_clock);
        clk_put(adc_clock);
        adc_clock = NULL;
    }
}

module_init(s3c2410_ts_init);
module_exit(s3c2410_ts_exit);

MODULE_LICENSE("GPL");

[ 本帖最后由 yawsh 于 2009-4-3 13:48 编辑 ]

论坛徽章:
5
摩羯座
日期:2014-07-22 09:03:552015元宵节徽章
日期:2015-03-06 15:50:392015亚冠之大阪钢巴
日期:2015-06-12 16:01:352015年中国系统架构师大会
日期:2015-06-29 16:11:2815-16赛季CBA联赛之四川
日期:2018-12-17 14:10:21
2 [报告]
发表于 2009-04-01 10:24 |只看该作者
给你转篇我google到底帖子吧


  1. 内核里面writel是如何实现的 在邮件列表里讨论了一下writel是如何实现的,这个函数实现在操作系统层,有内存保护的情况下,往一个寄存器或者内存地址写一个数据。

  2. 在arch/alpha/kernel/io.c中有
  3. 188 void writel(u32 b, volatile void __iomem *addr)
  4. 189 {
  5. 190     __raw_writel(b, addr);
  6. 191     mb();
  7. 192 }


  8. 这样一个writel函数的作用应该是向一个地址上写一个值,我想知道这个函数底下具体实现的细节,于是往下继续跟踪代码:__raw_writel(b, addr);

  9. 129 void __raw_writel(u32 b, volatile void __iomem *addr)
  10. 130 {
  11. 131     IO_CONCAT(__IO_PREFIX,writel)(b, addr);
  12. 132 }

  13. 再往下跟踪 IO_CONCAT,在对应的io.h中的定义如下:
  14. 134 #define IO_CONCAT(a,b)  _IO_CONCAT(a,b)
  15. 135 #define _IO_CONCAT(a,b) a ## _ ## b
  16. 这段代码前几天问过了,是标示将两边的字符串连接起来的意思。

  17. 跟踪__IO_PREFIX 定义如下
  18. 501 #undef __IO_PREFIX
  19. 502 #define __IO_PREFIX     apecs

  20. 到这里就结束了,再往下我就晕了,有问题如下:
  21. 1、到底是怎么将数据写入地址的?我把这些单独提取出来,进行预编译,宏展开后,发现是这样的:
  22. void __raw_writel(                                )
  23. {
  24.     apecs_writel(b, addr);
  25. }
  26. 但是在内核里根本就没找到apecs_writel函数,请帮忙解释下。


  27. For the first question,
  28. you should refer to the file "arch\alpha\kernle\Machvec_impl.h"
  29. "~\Machve.h" "~\io.c" "~\io.h" "~\core_**.h".

  30. as you have analysized before, in the file Machvec_impl.h and Machve.h,
  31. DO_CIA_IO,IO,IO_LITE, these three macros implement the symbole
  32. connection between ** arch and writel function, and the function
  33. pointer initializations.
  34. so, the details implementation to writel is to init the
  35. alpha_machine_vector structure and the definition to the relevant
  36. function pointer invoked to complete the low-level write operation.

  37. .mv_writel =CAT(low,_writel),<---IO(CIA,cia)<-->cia_writel(b, addr); <---

  38.                                 |
  39. writel(b, addr)-->__raw_writel(b, addr);--->cia_writel(b,addr)---------------


  40. For the second quesiton,
  41. mb()--->__asm__ __volatile__("mb": : :"memory");
  42. so, it is a memory barrier for alpha architecture to ensure some
  43. operations before some actions could be occured.
  44. and, it is similiar with the barrier() in x86 platform/arm platform.


  45. 继续阅读代码,看看定义__IO_PREFIX之后紧接着包含了哪个头文件。在哪个头文
  46. 件里面寻找答案。对于你的apsec,看看以下代码段(linux-2.6.28-rc4)

  47. arch/alpha/include/asm/core_apecs.h
  48. ------------------------------------------
  49. #undef __IO_PREFIX
  50. #define __IO_PREFIX             apecs
  51. #define apecs_trivial_io_bw     0
  52. #define apecs_trivial_io_lq     0
  53. #define apecs_trivial_rw_bw     2
  54. #define apecs_trivial_rw_lq     1
  55. #define apecs_trivial_iounmap   1
  56. #include <asm/io_trivial.h>
  57. ------------------------------------------

  58. arch/alpha/include/asm/io_trivial.h
  59. ------------------------------------------
  60. __EXTERN_INLINE void
  61. IO_CONCAT(__IO_PREFIX,writel)(u32 b, volatile void __iomem *a)
  62. {
  63.        *(volatile u32 __force *)a = b;
  64. }

  65. 就是最终通过*(volatile u32 __force *)a = b;
  66. 来写入数据的。


  67. 如果在没有os,没有mmu的情况下,当开发板裸跑的时候,我们只需要一句话就一切ok:
  68. *(unsigned long *)addr = value;



复制代码

论坛徽章:
0
3 [报告]
发表于 2009-04-01 13:08 |只看该作者
原帖由 yawsh 于 2009-4-1 10:02 发表
在s3c2410板上移植一个触摸屏的驱动程序。在加载驱动,建立相关的设备节点后,cat /dev/tsc 进行简单测试时,点击触摸屏没有输出,驱动停在 read中。经过简单调试,发现用writel来设置arm的寄存器,readl读出寄 ...

遇到过类似的情况,跟writel和readl的使用没关,应该是有个寄存器设置时钟的你没有设置

论坛徽章:
0
4 [报告]
发表于 2009-04-01 15:27 |只看该作者

回复 #2 T-bagwell 的帖子

多谢您的回复。
我试过在
......
base_addr = iorempa(S3C2410_PA_ADC,0x20);
......
writel(xxx,base_addr + S3C2410_ADCCON);
mb();
readl(base_addr + S3C2410_ADCCON);
似乎没有起作用。

论坛徽章:
0
5 [报告]
发表于 2009-04-01 16:15 |只看该作者

回复 #3 zhongfrank 的帖子

在下依样画葫芦,在ioremap之前,用了clk函数,发现可以设置相应的寄存器,不过还是不明白原理。谢谢!
static void __iomem *base_addr;
static struct clk *adc_clock;
......
        adc_clock = clk_get(NULL, "adc");
        if (!adc_clock) {
                printk(KERN_ERR "failed to get adc clock source\n");
                return -ENOENT;
        }
        clk_enable(adc_clock);

        base_addr = ioremap(S3C2410_PA_ADC,0x20);
        if(base_addr == NULL){
                printk(KERN_WARNING "%s : Can't remap register block!\n",DRIVER_NAME);
                return -ENOMEM;
        }
......
        writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(49), base_addr + S3C2410_ADCCON);
        pr_debug("ADCCON = 0x%x\n",readl(base_addr + S3C2410_ADCCON));
......
        iounmap(base_addr);
        clk_disable(adc_clock);
        clk_put(adc_clock);
        adc_clock = NULL;

论坛徽章:
0
6 [报告]
发表于 2009-04-03 00:02 |只看该作者
但是写之前不需要进行地址映射吗?

论坛徽章:
0
7 [报告]
发表于 2009-04-03 07:58 |只看该作者

回复 #6 emmoblin 的帖子

有映射地址

论坛徽章:
0
8 [报告]
发表于 2009-04-03 08:16 |只看该作者
折磨了几天,触摸屏的INT_TC和INT_ADC中断服务程序一直没有响应,我检查了CLKCON、INTSUBMSK、INTMSK似乎都没有问题,不知道是哪里还需要设置。请各位帮忙看看。
板上的部分信息
#cat /proc/interrupts
......
79:          0     s3c-adc  touchscreen
80:          0     s3c-adc  touchscreen
Err:          0

#cat /proc/devices
.......
224 touchscreen
......

# ls -l /dev/tsc
crw-r--r--    1 0        0        224,   1 Jan  1 00:01 /dev/tsc

#cat /dev/tsc
touchscreen s3c2410_ts_read
ADCCON = 0x4c40
ADCTSC = 0xc7
ADCDLY = 0xff
ADCDAT0 = 0xf000
ADCDAT1 = 0xf000
CLKCON = 0x8fff0
SUBSRCPND = 0x92
INTSUBMSK = 0x1fe
INTMSK = 0x6b4e3dcf

论坛徽章:
0
9 [报告]
发表于 2009-04-03 08:38 |只看该作者
忘记说明,内核版本是2.6.22.6

在下看了其他人的源代码,发现一个地方非常不解,就是在设置ADCTSC这个寄存器的时候。
别人的源代码有这样的宏定义
#define WAIT4INT(x)  (((x)<< | \
                     S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
                     S3C2410_ADCTSC_XY_PST(3))
但是在S3C2410A的文档中,说明
  ADCTSC   |  Bit   |  Description               |    Initial State
Reserved    |  [8]    | This bit should be zero.   |         0

而代码中,却将WAIT4INT(1) 写到ADCTSC寄存器。

论坛徽章:
5
10 [报告]
发表于 2009-04-03 09:03 |只看该作者
从下面来看,

原帖由 yawsh 于 2009/4/3 08:38 发表 [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=6968522&ptid=1095535][img]#cat /proc/interrupts
......
79:          0     s3c-adc  touchscreen
80:          0     s3c-adc  touchscreen


中断并没有产生
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP