一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Linux i2c子系統(tǒng)應(yīng)用之Linux ARM嵌入式i2c通信(設(shè)備驅(qū)動(dòng)完成i2c從設(shè)備寄存器的配...

 西北望msm66g9f 2019-03-30

一、前言

        本文主要分為三個(gè)部分,第一部分,介紹i2c字符設(shè)備驅(qū)動(dòng)應(yīng)用的背景以及本文測(cè)試需要的開發(fā)環(huán)境;第二部分,介紹主要的字符驅(qū)動(dòng)源碼及測(cè)試程序;第三部分,測(cè)試方法以及測(cè)試結(jié)果,i2c從設(shè)備的器件地址可以在該器件的datasheet查找。文章的最后會(huì)給大家分享本文的所有源碼。

二、開發(fā)背景和環(huán)境 

        我已經(jīng)講解過利用i2c總線的去配置i2c從設(shè)備的方法,本文采用i2c設(shè)備驅(qū)動(dòng)的方式完成同樣的功能,在此完善工作記錄。

優(yōu)點(diǎn):(1)當(dāng)從設(shè)備需要多種功能操作時(shí)(比如修改攝像頭的亮度、放大、曝光、分辨率等配置),把每個(gè)功能包裝成子模塊,相對(duì)總線方式的配置層次清晰,而且方便管理維護(hù),而且在擴(kuò)展時(shí)還能夠在設(shè)備驅(qū)動(dòng)中添加對(duì)系統(tǒng)內(nèi)核資源的訪問(操作時(shí)請(qǐng)注意安全);

            (2)設(shè)備初始化順序可以隨意控制,想i2c從設(shè)備啟動(dòng)快點(diǎn)就把設(shè)備初始化添加到內(nèi)核啟動(dòng),想它啟動(dòng)慢一點(diǎn),就以.ko的方式加載,等文件系統(tǒng)加載完畢了再初始化;

缺點(diǎn) :(1)相比總線操作的方式編寫代碼較復(fù)雜,因?yàn)槭紫纫煜ぷ址?qū)動(dòng)架構(gòu),而且還需要編寫一個(gè)操作設(shè)備驅(qū)動(dòng)的應(yīng)用程序;

運(yùn)行環(huán)境:ARM S3C6410平臺(tái)

交叉編譯器:ARM-LINUX-GCC

內(nèi)核版本:2.6.28.6

三、源碼的講解

        源碼的講解分為兩個(gè)部分,第一個(gè)部分初略地介紹下i2c字符設(shè)備初始化過程,具體的字符驅(qū)動(dòng)架構(gòu)不再本文講解的范圍內(nèi),第二部分,講解利用i2c設(shè)備驅(qū)動(dòng)對(duì)i2c從設(shè)備的寄存器進(jìn)行讀寫操作;

驅(qū)動(dòng)源碼初始化執(zhí)行步驟,

module_init(ch7026_init)

首先執(zhí)行ch7026_init函數(shù)

  1. static __init int ch7026_init(void)
  2. {
  3. int ret;
  4. dev_t devno;
  5. printk(DEVICE_NAME ' start init...\n');
  6. p_bank = kmalloc(sizeof(struct ch7026_bank), GFP_KERNEL);
  7. if (!p_bank)
  8. return -ENOMEM;
  9. memset(p_bank, 0, sizeof(struct ch7026_bank));
  10. devno = MKDEV(CH7026_MAJOR,0);
  11. ret = register_chrdev_region(devno,1,DEVICE_NAME);
  12. if(ret<0)
  13. {
  14. printk(KERN_NOTICE 'can not register ch7026 device');
  15. return ret;
  16. }
  17. cdev_init(&cdev_ch7026,&ch7026_fops);
  18. cdev_ch7026.owner = THIS_MODULE;
  19. ret =cdev_add(&cdev_ch7026,devno,1);
  20. if(ret)
  21. {
  22. printk(KERN_NOTICE 'can not add ch7026 device');
  23. return ret;
  24. }
  25. /*在/sys/class/下創(chuàng)建相對(duì)應(yīng)的類目錄*/
  26. my_class = class_create(THIS_MODULE,'ch7026');
  27. if(IS_ERR(my_class))
  28. {
  29. printk('Err: Failed in creating class\n');
  30. return -1;
  31. }
  32. /*完成設(shè)備節(jié)點(diǎn)的自動(dòng)創(chuàng)建,當(dāng)加載模塊時(shí),就會(huì)在/dev下自動(dòng)創(chuàng)建設(shè)備文件*/
  33. device_create(my_class,NULL,MKDEV(CH7026_MAJOR,0),NULL,DEVICE_NAME);
  34. printk(DEVICE_NAME ' initialized\n');
  35. i2c_add_driver(&ch7026_driver);
  36. return 0;
  37. }

執(zhí)行回調(diào)函數(shù) ch7026_probe()函數(shù)

  1. static struct i2c_driver ch7026_driver = {
  2. .driver = {
  3. .name = 'ch7026',
  4. .owner = THIS_MODULE,
  5. },
  6. .id = I2C_DRIVERID_CH7026,
  7. .attach_adapter = ch7026_probe,
  8. .detach_client = ch7026_detach,
  9. };

在執(zhí)行ch7026_attach()函數(shù)

  1. static int ch7026_probe(struct i2c_adapter *adap)
  2. {
  3. int ret = 0;
  4. ret = i2c_probe(adap, &addr_data, ch7026_attach);
  5. if (ret) {
  6. printk('failed to attach ch7026 driver\n');
  7. ret = -ENODEV;
  8. }
  9. return ret;
  10. }

執(zhí)行ch7026_attach函數(shù),執(zhí)行用戶配置i2c從設(shè)備ch7026_config()函數(shù)

  1. static int ch7026_attach(struct i2c_adapter *adap, int addr, int flags )
  2. {
  3. int ret = 0;
  4. strcpy(p_bank->c.name, 'ch7026');
  5. p_bank->c.addr = addr;
  6. p_bank->c.adapter = adap;
  7. p_bank->c.driver = &ch7026_driver;
  8. ret = i2c_attach_client(&p_bank->c);
  9. ch7026_config(&p_bank->c);
  10. printk('CH7026 attached successfully\n');
  11. return ret;
  12. }

第二部分下面講解下應(yīng)用層write、read、ioctl系統(tǒng)調(diào)用到設(shè)備驅(qū)動(dòng)的讀寫函數(shù),即i2c設(shè)備驅(qū)動(dòng)的讀寫操作,熟悉設(shè)備驅(qū)動(dòng)的人就知道,分別實(shí)現(xiàn)對(duì)應(yīng)的ch7026_write、 ch7026_read、 ch7026_ioctl函數(shù)即可,

  1. static struct file_operations ch7026_fops = {
  2. .owner = THIS_MODULE,
  3. .open = ch7026_open,
  4. .write = ch7026_write,
  5. .read = ch7026_read,
  6. .ioctl = ch7026_ioctl,
  7. };

ch7026_write函數(shù)的作用是實(shí)現(xiàn)用戶空間的數(shù)據(jù)到內(nèi)核空間的拷貝,然后再調(diào)用ch7026_i2c_write函數(shù)

  1. static ssize_t ch7026_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
  2. {
  3. int ret, err = 0;
  4. unsigned char addr = *ppos;
  5. unsigned char buffer;
  6. if (copy_from_user(&buffer, buf, count)) {
  7. err = -EFAULT;
  8. return err;
  9. }
  10. ret = ch7026_i2c_write(&p_bank->c, addr, buffer);
  11. if (ret == -EIO) {
  12. printk('i2c transfer error\n');
  13. return -EIO;
  14. }
  15. return ret;
  16. }

ch7026_read函數(shù)讀取地址的數(shù)據(jù),再從內(nèi)核空間拷貝該數(shù)據(jù)到用戶空間,本文所講的i2從設(shè)備寄存器地址是8bit(一個(gè)字節(jié)),如果遇到i2從設(shè)備寄存器地址是16bit(兩個(gè)字節(jié)),對(duì)應(yīng)修改下面buffer為unsigned short型,i2c_master_send(&p_bank->c, &buffer, 2),讀操作也同理,改下數(shù)據(jù)接口就得,根據(jù)手冊(cè)多測(cè)試就知道怎么改了。

  1. static ssize_t ch7026_read(struct file *file, char *buf, size_t count, loff_t *ppos)
  2. {
  3. int ret = 0;
  4. u8 p = *ppos;
  5. u8 buffer;
  6. u8 Rdval = 0;
  7. buffer = p&0xff;
  8. if (i2c_master_send(&p_bank->c, &buffer, 1) != 1) {
  9. return -1;
  10. }
  11. if (i2c_master_recv(&p_bank->c, &Rdval, 1) != 1) {
  12. return -1;
  13. }
  14. if (copy_to_user(buf, &Rdval, sizeof(Rdval))) {
  15. ret = -EFAULT;
  16. }
  17. return ret;
  18. }

要在ioctl實(shí)現(xiàn)從設(shè)備各種子功能的實(shí)現(xiàn),就自定義實(shí)現(xiàn)一個(gè)設(shè)備驅(qū)動(dòng)里能讀寫的函數(shù)接口,開機(jī)配置i2c從設(shè)備多個(gè)寄存器的接口函數(shù)作用如下,就用到了ch7026_i2c_write函數(shù),

  1. void inline ch7026_config(struct i2c_client *client)
  2. {
  3. int i;
  4. int ret = 0;
  5. for (i = 0; i < CH7026_INIT_REGS; i++) {
  6. delay(50);
  7. ret = ch7026_i2c_write(client,
  8. ch7026_reg[i].subaddr, ch7026_reg[i].value);
  9. if(ret != 0) printk('ch7026:write faild!\n');
  10. }
  11. }

以下為i2c設(shè)備驅(qū)動(dòng)讀寫i2c從設(shè)備寄存器的接口,

  1. static unsigned char ch7026_i2c_read(struct i2c_client *client, u8 subaddr)
  2. {
  3. u8 Regbuf = subaddr;
  4. u8 Rdval = 0;
  5. if (i2c_master_send(client, Regbuf, 1) != 1) {//發(fā)送要讀取從設(shè)備的寄存器地址
  6. return -1;
  7. }
  8. if (i2c_master_recv(client, &Rdval, 1) != 1) {//把讀取到寄存器的值保存在Rdval并返回
  9. return -1;
  10. }
  11. return Rdval;
  12. }
  13. static int ch7026_i2c_write(struct i2c_client *client, unsigned char subaddr, unsigned char val)
  14. {
  15. int ret;
  16. unsigned char buf[2];
  17. struct i2c_msg msg = { client->addr, 0, 2, buf };
  18. buf[0] = subaddr;//所寫寄存器的地址
  19. buf[1] = val;//將要向寄存器寫的值
  20. printk('Kernel Reg: 0x%x Value: 0x%x\n',buf[0], buf[1]);
  21. ret = i2c_transfer(client->adapter, &msg, 1) == 1 ? 0 : -EIO;
  22. if(ret < 0)
  23. printk('ch7026_i2c_write error!\n');
  24. return ret;
  25. }

以下為i2c讀寫接口函數(shù)的延展,展示了i2c_master_send、i2c_master_recv、i2c_transfer的函數(shù)關(guān)系,其中i2c_msg 數(shù)據(jù)結(jié)構(gòu)非常關(guān)鍵

/usr/local/arm/4.2.2-eabi/usr/include/linux/i2c.h //i2c_msg 數(shù)據(jù)定義的頭文件
  1. /*
  2. * I2C Message - used for pure i2c transaction, also from /dev interface
  3. */
  4. struct i2c_msg {
  5. __u16 addr; /* slave address---從設(shè)備地址*/
  6. __u16 flags; /*以下的宏定義為可以對(duì)flags操作的位運(yùn)算*/
  7. #define I2C_M_TEN 0x10 /* we have a ten bit chip address */
  8. #define I2C_M_RD 0x01
  9. #define I2C_M_NOSTART 0x4000
  10. #define I2C_M_REV_DIR_ADDR 0x2000
  11. #define I2C_M_IGNORE_NAK 0x1000
  12. #define I2C_M_NO_RD_ACK 0x0800
  13. #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
  14. __u16 len; /* msg length---數(shù)據(jù)長度,字節(jié)為單位*/
  15. __u8 *buf; /* pointer to msg data---存在數(shù)據(jù)的指針*/
  16. };
  1. /opt/htx-linux-2.6.28-d300-20170531/drivers/i2c/i2c-core.c //i2c_master_send、
  2. i2c_master_recv、i2c_transfer函數(shù)定義的文件
  1. /**
  2. * i2c_master_send - issue a single I2C message in master transmit mode
  3. * @client: Handle to slave device
  4. * @buf: Data that will be written to the slave
  5. * @count: How many bytes to write
  6. *
  7. * Returns negative errno, or else the number of bytes written.
  8. */
  9. int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
  10. {
  11. int ret;
  12. struct i2c_adapter *adap=client->adapter;
  13. struct i2c_msg msg;
  14. msg.addr = client->addr;
  15. msg.flags = client->flags & I2C_M_TEN;
  16. msg.len = count;
  17. msg.buf = (char *)buf;
  18. ret = i2c_transfer(adap, &msg, 1);
  19. /* If everything went ok (i.e. 1 msg transmitted), return #bytes
  20. transmitted, else error code. */
  21. return (ret == 1) ? count : ret;
  22. }
  23. EXPORT_SYMBOL(i2c_master_send);
  24. /**
  25. * i2c_master_recv - issue a single I2C message in master receive mode
  26. * @client: Handle to slave device
  27. * @buf: Where to store data read from slave
  28. * @count: How many bytes to read
  29. *
  30. * Returns negative errno, or else the number of bytes read.
  31. */
  32. int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
  33. {
  34. struct i2c_adapter *adap=client->adapter;
  35. struct i2c_msg msg;
  36. int ret;
  37. msg.addr = client->addr;
  38. msg.flags = client->flags & I2C_M_TEN;
  39. msg.flags |= I2C_M_RD;
  40. msg.len = count;
  41. msg.buf = buf;
  42. ret = i2c_transfer(adap, &msg, 1);
  43. /* If everything went ok (i.e. 1 msg transmitted), return #bytes
  44. transmitted, else error code. */
  45. return (ret == 1) ? count : ret;
  46. }
  47. EXPORT_SYMBOL(i2c_master_recv);

從上可以看到,i2c_master_send和i2c_master_recv傳輸數(shù)據(jù),最終調(diào)用的函數(shù)接口都是i2c_transfer,唯一的區(qū)別就在數(shù)據(jù)包中的msg.flags標(biāo)志位,接收消息時(shí)多了個(gè)I2C_M_RD標(biāo)志的或運(yùn)算。

  1. /* ----------------------------------------------------
  2. * the functional interface to the i2c busses.
  3. * ----------------------------------------------------
  4. */
  5. /**
  6. * i2c_transfer - execute a single or combined I2C message
  7. * @adap: Handle to I2C bus
  8. * @msgs: One or more messages to execute before STOP is issued to
  9. * terminate the operation; each message begins with a START.
  10. * @num: Number of messages to be executed.
  11. *
  12. * Returns negative errno, else the number of messages executed.
  13. *
  14. * Note that there is no requirement that each message be sent to
  15. * the same slave address, although that is the most common model.
  16. */
  17. int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
  18. {
  19. int ret;
  20. /* REVISIT the fault reporting model here is weak:
  21. *
  22. * - When we get an error after receiving N bytes from a slave,
  23. * there is no way to report 'N'.
  24. *
  25. * - When we get a NAK after transmitting N bytes to a slave,
  26. * there is no way to report 'N' ... or to let the master
  27. * continue executing the rest of this combined message, if
  28. * that's the appropriate response.
  29. *
  30. * - When for example 'num' is two and we successfully complete
  31. * the first message but get an error part way through the
  32. * second, it's unclear whether that should be reported as
  33. * one (discarding status on the second message) or errno
  34. * (discarding status on the first one).
  35. */
  36. if (adap->algo->master_xfer) {
  37. #ifdef DEBUG
  38. for (ret = 0; ret < num; ret++) {
  39. dev_dbg(&adap->dev, 'master_xfer[%d] %c, addr=0x%02x, '
  40. 'len=%d%s\n', ret, (msgs[ret].flags & I2C_M_RD)
  41. ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
  42. (msgs[ret].flags & I2C_M_RECV_LEN) ? '+' : '');
  43. }
  44. #endif
  45. if (in_atomic() || irqs_disabled()) {
  46. ret = mutex_trylock(&adap->bus_lock);
  47. if (!ret)
  48. /* I2C activity is ongoing. */
  49. return -EAGAIN;
  50. } else {
  51. mutex_lock_nested(&adap->bus_lock, adap->level);
  52. }
  53. ret = adap->algo->master_xfer(adap,msgs,num);
  54. mutex_unlock(&adap->bus_lock);
  55. return ret;
  56. } else {
  57. dev_dbg(&adap->dev, 'I2C level transfers not supported\n');
  58. return -EOPNOTSUPP;
  59. }
  60. }
  61. EXPORT_SYMBOL(i2c_transfer);

它們同i2c總線操作一樣,都要回歸于i2c標(biāo)準(zhǔn)的子系統(tǒng)中去,最終接口為i2c_transfer()。ioctl的應(yīng)用也是一樣,應(yīng)用層調(diào)用的參考代碼如下,

  1. enum imagerStatus {
  2. SET_CONTRAT = 0x1,
  3. SET_BRIGHTNESS,
  4. POWER_ON,
  5. POWER_OFF,
  6. };
  7. int ret;
  8. if(m_imagerFd<=0) {
  9. m_imagerFd = open('/dev/ch7026', O_RDWR);
  10. if(m_imagerFd<0) {
  11. perror('open device ch7026:');
  12. return false;
  13. }
  14. }
  15. if(var){
  16. ret = ioctl(m_imagerFd, POWER_ON, NULL);
  17. }else{
  18. ret = ioctl(m_imagerFd, POWER_OFF, NULL);
  19. }
  20. if(ret < 0) {
  21. perror('ioctl:device ch7026:');
  22. return false;
  23. }

設(shè)備驅(qū)動(dòng)ioctl部分實(shí)現(xiàn)的部分,每個(gè)宏定義都可以配置成一個(gè)子功能塊,每個(gè)子模塊可以配置多個(gè)寄存器;

  1. #define SET_CONTRAT 0x01
  2. #define SET_BRIGHTNESS 0x02
  3. #define POWER_ON 0x03
  4. #define POWER_OFF 0x04
  5. typedef struct samsung_t{
  6. unsigned char subaddr;
  7. unsigned char value;
  8. } ch7026_t;
  9. static int ch7026_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
  10. {
  11. int ret = 0;
  12. int i;
  13. unsigned char contrat = 0x30;
  14. unsigned char brightness = 0x31;
  15. unsigned char value;
  16. value = arg;
  17. switch (cmd) {
  18. case SET_CONTRAT:
  19. ret = ch7026_i2c_write(&p_bank->c, contrat, value);
  20. if (ret == -EIO) {
  21. printk('i2c transfer error\n');
  22. return -EIO;
  23. }
  24. printk('vga_contrat = 0x:%x\n', value);
  25. break;
  26. case SET_BRIGHTNESS:
  27. ret = ch7026_i2c_write(&p_bank->c, brightness, value);
  28. if (ret == -EIO) {
  29. printk('i2c transfer error\n');
  30. return -EIO;
  31. }
  32. printk('vga_brightness = 0x:%x\n', value);
  33. break;
  34. case POWER_ON:
  35. ch7026_config(&p_bank->c);
  36. printk('ch7026 power up!\n');
  37. break;
  38. case POWER_OFF:
  39. for (i = 0; i < CH7026_POW_OFF_REGS; i++) {
  40. delay(50);
  41. ret = ch7026_i2c_write(&p_bank->c,
  42. ch7026_reg_pow_off[i].subaddr, ch7026_reg_pow_off[i].value);
  43. if(ret != 0) printk('ch7026:write faild!\n');
  44. }
  45. printk('ch7026 power down!\n');
  46. break;
  47. default:
  48. printk('unexpect command\n');
  49. break;
  50. }
  51. return ret;
  52. }

四、程序測(cè)試

應(yīng)用層測(cè)試main函數(shù):

  1. #include <unistd.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <sys/ioctl.h>
  7. #include <termios.h>
  8. static int fd;
  9. static void print_usage(void);
  10. /* Main function */
  11. int main(int argc, char *argv[])
  12. {
  13. int ret;
  14. int num;
  15. int i;
  16. int buf;//char buf;
  17. unsigned int tmp;
  18. unsigned int addr;
  19. unsigned char val;
  20. int buffer;
  21. float multiple;
  22. fd = open('/dev/ch7026', O_RDWR);
  23. if(fd<0) {
  24. perror('open device ch7026');
  25. exit(1);
  26. }
  27. if( (argc == 4)&& (strcmp(argv[1], 'write') == 0) ) {
  28. if((argv[2][0]=='0') && (argv[2][1]=='x'))
  29. sscanf(argv[2], '0x%x', &addr);
  30. else
  31. addr = atoi(argv[2]);
  32. if((argv[3][0]=='0') && (argv[3][1]=='x'))
  33. {
  34. sscanf(argv[3], '0x%x', &tmp);
  35. val = tmp&0xFF;
  36. }
  37. else
  38. val = atoi(argv[3]);
  39. printf('Write: addr[0x%02x] [0x%02x] \n', addr, val);
  40. lseek(fd, addr, SEEK_SET);
  41. write(fd, &val, sizeof(unsigned char));
  42. } else if( (argc == 3) && (strcmp(argv[1], 'read') == 0)) {
  43. if((argv[2][0]=='0') && (argv[2][1]=='x'))
  44. sscanf(argv[2], '0x%x', &addr);
  45. else
  46. addr = atoi(argv[2]);
  47. lseek(fd, addr, SEEK_SET);
  48. if(ret < 0) {
  49. perror('lseek');
  50. exit(1);
  51. }
  52. read(fd, &val, sizeof(val));
  53. printf('Read: addr[0x%02x] [0x%02x] \n', addr, val);
  54. } else {
  55. print_usage();
  56. exit(1);
  57. }
  58. close(fd);
  59. return 0;
  60. }
  61. static void print_usage(void)
  62. {
  63. printf('usage:./ch7026_test [commad]\n');
  64. printf('./ch7026_test write [address] [value]\n');
  65. printf('./ch7026_test read [address]\n');
  66. printf('For example:\n');
  67. printf('./ch7026_test write 0x03 0x01\n');
  68. printf('./ch7026_test read 0x03\n');
  69. }

    在PC Linux上用交叉編譯器編譯設(shè)備驅(qū)動(dòng)模塊(make)和應(yīng)用程序(make test),

     測(cè)試結(jié)果如下,可以看到應(yīng)用層讀寫i2c從設(shè)備的0x06寄存器成功:

        在編譯設(shè)備驅(qū)動(dòng)模塊的時(shí)候,首先要先編譯內(nèi)核,因?yàn)樵O(shè)備驅(qū)動(dòng)中的許多函數(shù)定義都來自內(nèi)核,如i2c子系統(tǒng),'.o'后綴的就是內(nèi)核已經(jīng)編譯好的。當(dāng)然,Makefile還要加入編譯好的內(nèi)核路徑。

   Makefile的內(nèi)容,更換自己的內(nèi)核路徑和交叉編譯器

  1. obj-m += ch7026.o
  2. KERNEL=/opt/kernel-s3c6410/htx-linux-2.6.28-g96p-***** #更換成自己的內(nèi)核路徑
  3. CC=/usr/local/arm/4.2.2-eabi/usr/bin/arm-linux-gcc #更換成自己平臺(tái)的交叉編譯器
  4. default:
  5. @make -C $(KERNEL) M=`pwd` modules
  6. install:
  7. @make -C /lib/modules/`uname -r`/build M=`pwd` modules_install
  8. clean:
  9. @make -C /lib/modules/`uname -r`/build M=`pwd` clean
  10. rm ch7026_test -rf
  11. test:
  12. arm-linux-gcc ch7026_test.c -o ch7026_test
  13. cp ch7026_test /mnt/hgfs/upload/

最后我把i2c總線方式和i2c設(shè)備驅(qū)動(dòng)方式配置從設(shè)備的源碼整理在一起上傳到資源區(qū)(https://download.csdn.net/download/psy6653/11014339),

        寫了這么多本想設(shè)置2個(gè)下載積分安慰下自己得,但不知道怎么的系統(tǒng)默認(rèn)設(shè)置為5分(但修改不了),實(shí)在抱歉。不過沒有關(guān)系,有多的積分的朋友就贊助下哈,沒積分的朋友可以給我留言,我會(huì)用wan盤單獨(dú)分享給你。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    欧美成人黄色一区二区三区| 在线观看欧美视频一区| 国产精品美女午夜视频| 亚洲欧美天堂精品在线| 国产美女精品午夜福利视频| 欧美黑人精品一区二区在线| 中文字幕人妻综合一区二区| 精品国模一区二区三区欧美| 国产亚洲精品香蕉视频播放| 欧美一区二区三区性视频| 久久99午夜福利视频| 欧美日韩国产午夜福利| 99久热只有精品视频免费看| 日本加勒比系列在线播放| 欧美大黄片在线免费观看| 亚洲高清亚洲欧美一区二区| 欧美大胆女人的大胆人体| 亚洲欧美国产中文色妇| 亚洲最新一区二区三区| 加勒比东京热拍拍一区二区| 黄片美女在线免费观看| 人妻一区二区三区在线| 99免费人成看国产片| 亚洲欧美日韩在线看片| 国产又粗又猛又爽色噜噜| 国产亚洲精品一二三区| 亚洲国产精品av在线观看| 欧美精品久久男人的天堂| 日本精品中文字幕在线视频| 东京热电东京热一区二区三区| 色婷婷久久五月中文字幕| 在线观看视频成人午夜| 国产在线观看不卡一区二区| 丝袜破了有美女肉体免费观看 | 国产精品免费自拍视频| 蜜桃av人妻精品一区二区三区| 色欧美一区二区三区在线| 高清免费在线不卡视频| 99热九九热这里只有精品| 日韩精品中文在线观看| 成人欧美一区二区三区视频|