?? main.c
字號:
* 上面為這步準備了具體在哪個鏈表項的指針數(shù)組的第幾行的第幾列(即dptr->data[s_pos] + q_pos) * 從這個位置的內(nèi)核態(tài)的buf中拷給用戶態(tài) */ //關(guān)鍵一步,將數(shù)據(jù)拷給用戶空間 if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) { retval = -EFAULT; goto out; } *f_pos += count; //更新文件指針 retval = count; out: up(&dev->sem); return retval;}//與read的實現(xiàn)類似ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){ struct scull_dev *dev = filp->private_data; struct scull_qset *dptr; int quantum = dev->quantum, qset = dev->qset; int itemsize = quantum * qset; int item, s_pos, q_pos, rest; ssize_t retval = -ENOMEM; /* value used in "goto out" statements */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; /* find listitem, qset index and offset in the quantum */ item = (long)*f_pos / itemsize; rest = (long)*f_pos % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; /* follow the list up to the right position */ dptr = scull_follow(dev, item); if (dptr == NULL) goto out; if (!dptr->data) { dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL); if (!dptr->data) goto out; memset(dptr->data, 0, qset * sizeof(char *)); } if (!dptr->data[s_pos]) { dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL); if (!dptr->data[s_pos]) goto out; } /* write only up to the end of this quantum */ if (count > quantum - q_pos) count = quantum - q_pos; if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) { retval = -EFAULT; goto out; } *f_pos += count; retval = count; /* update the size */ if (dev->size < *f_pos) dev->size = *f_pos; out: up(&dev->sem); return retval;}/* * The ioctl() implementation */int scull_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ int err = 0, tmp; int retval = 0; /* * extract the type and number bitfields, and don't decode * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() */ if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY; /* * the direction is a bitmask, and VERIFY_WRITE catches R/W * transfers. `Type' is user-oriented, while * access_ok is kernel-oriented, so the concept of "read" and * "write" is reversed */ if (_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); if (err) return -EFAULT; switch(cmd) { case SCULL_IOCRESET: scull_quantum = SCULL_QUANTUM; scull_qset = SCULL_QSET; break; case SCULL_IOCSQUANTUM: /* Set: arg points to the value */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; retval = __get_user(scull_quantum, (int __user *)arg); break; case SCULL_IOCTQUANTUM: /* Tell: arg is the value */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; scull_quantum = arg; break; case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */ retval = __put_user(scull_quantum, (int __user *)arg); break; case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */ return scull_quantum; case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_quantum; retval = __get_user(scull_quantum, (int __user *)arg); if (retval == 0) retval = __put_user(tmp, (int __user *)arg); break; case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_quantum; scull_quantum = arg; return tmp; case SCULL_IOCSQSET: if (! capable (CAP_SYS_ADMIN)) return -EPERM; retval = __get_user(scull_qset, (int __user *)arg); break; case SCULL_IOCTQSET: if (! capable (CAP_SYS_ADMIN)) return -EPERM; scull_qset = arg; break; case SCULL_IOCGQSET: retval = __put_user(scull_qset, (int __user *)arg); break; case SCULL_IOCQQSET: return scull_qset; case SCULL_IOCXQSET: if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_qset; retval = __get_user(scull_qset, (int __user *)arg); if (retval == 0) retval = put_user(tmp, (int __user *)arg); break; case SCULL_IOCHQSET: if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_qset; scull_qset = arg; return tmp; /* * The following two change the buffer size for scullpipe. * The scullpipe device uses this same ioctl method, just to * write less code. Actually, it's the same driver, isn't it? */ case SCULL_P_IOCTSIZE: scull_p_buffer = arg; break; case SCULL_P_IOCQSIZE: return scull_p_buffer; default: /* redundant, as cmd was checked against MAXNR */ return -ENOTTY; } return retval;}/* * The "extended" operations -- only seek */loff_t scull_llseek(struct file *filp, loff_t off, int whence){ struct scull_dev *dev = filp->private_data; loff_t newpos; switch(whence) { case 0: /* SEEK_SET */ newpos = off; break; case 1: /* SEEK_CUR */ newpos = filp->f_pos + off; break; case 2: /* SEEK_END */ newpos = dev->size + off; break; default: /* can't happen */ return -EINVAL; } if (newpos < 0) return -EINVAL; filp->f_pos = newpos; return newpos;}//[Tag007]將這組操作打包為一個對象;struct file_operations scull_fops = { .owner = THIS_MODULE, .llseek = scull_llseek, .read = scull_read, .write = scull_write, .ioctl = scull_ioctl, .open = scull_open, .release = scull_release,};/* * Finally, the module stuff *///[Tag008]模塊卸載或goto fail時;/* * The cleanup function is used to handle initialization failures as well. * Thefore, it must be careful to work correctly even if some of the items * have not been initialized */void scull_cleanup_module(void){ int i; dev_t devno = MKDEV(scull_major, scull_minor); /* Get rid of our char dev entries */ if (scull_devices) { for (i = 0; i < scull_nr_devs; i++) { scull_trim(scull_devices + i); cdev_del(&scull_devices[i].cdev); //[???]是一個內(nèi)核函數(shù)么? } kfree(scull_devices); }#ifdef SCULL_DEBUG /* use proc only if debugging */ scull_remove_proc();#endif /* cleanup_module is never called if registering failed */ unregister_chrdev_region(devno, scull_nr_devs); /* and call the cleanup functions for friend devices */ scull_p_cleanup(); scull_access_cleanup();}/* [Tag002] 這里主要干了2件事; 在內(nèi)核內(nèi)部使用struct cdev結(jié)構(gòu)來表示字符設(shè)備; [1]在這里因為我們將cdev結(jié)構(gòu)嵌入到自己的scull_dev設(shè)備下了,所以我們用下面這個方法來 初始化已分配的結(jié)構(gòu); cdev_init(&dev->cdev, &scull_fops); [2]告訴內(nèi)核我們新結(jié)構(gòu)的信息;*//* * Set up the char_dev structure for this device. */static void scull_setup_cdev(struct scull_dev *dev, int index){ int err, devno = MKDEV(scull_major, scull_minor + index); // [1] cdev_init(&dev->cdev, &scull_fops); /* 初始化, 字符設(shè)備和給它一組在它上面操作的方法集 */ /* 填充基本字符設(shè)備的成員 */ dev->cdev.owner = THIS_MODULE; //模塊計數(shù) dev->cdev.ops = &scull_fops; //附上一組操作自己的方法集 // [2] err = cdev_add (&dev->cdev, devno, 1); /* 函數(shù)說明: cdev -- 字符設(shè)備的結(jié)構(gòu)指針,我們就是要把他告訴給內(nèi)核; devno -- 設(shè)備編號,用MKDEV利用全局的主設(shè)備號和次設(shè)備號生成的; 1 -- 是應(yīng)該和該設(shè)備關(guān)聯(lián)的設(shè)備編號的數(shù)量, 一般情況下都為1; 一般我們都是一個設(shè)備編號對應(yīng)一個設(shè)備; */ /* 注意: 在調(diào)用cdev_add后,我們的設(shè)備就被添加到系統(tǒng)了,他"活"了. 附加的操作集也就可以被內(nèi)核調(diào)用了 ,因此,在驅(qū)動程序還沒有完全準備好處理設(shè)備上的操作時,就不能調(diào)用cdev_add! */ /* Fail gracefully if need be */ if (err) printk(KERN_NOTICE "Error %d adding scull%d", err, index);}/*[Tag000] * 當模塊加載時,調(diào)用;但是為什么要放在最后來實現(xiàn)他呢,看到Tag002時,你應(yīng)該就明白了;*/int scull_init_module(void){ int result, i; dev_t dev = 0;/* [Tag001] *//* [1]分配設(shè)備編號 *//* * Get a range of minor numbers to work with, asking for a dynamic * major unless directed otherwise at load time. */ if (scull_major) { /* 預(yù)先自己指定了主設(shè)備號 */ dev = MKDEV(scull_major, scull_minor); /* 利用主設(shè)備號,找到設(shè)備編號給方法1用 */ result = register_chrdev_region(dev, scull_nr_devs, "scull"); } else { /* 動態(tài)自己生成設(shè)備編號,然后再利用設(shè)備編號得到主設(shè)備號; 記住如果用這個方法那么就要后建設(shè)備文件了,因為不能提前知道主號 當然也可以利用ldd3書中提供的腳本,巨方便&&通用 */ result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull"); scull_major = MAJOR(dev); } if (result < 0) { printk(KERN_WARNING "scull: can't get major %d\n", scull_major); return result; } /*[2]設(shè)備對象實例化*/ /* * allocate the devices -- we can't have them static, as the number * can be specified at load time */ scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL); if (!scull_devices) { result = -ENOMEM; goto fail; /* Make this more graceful */ } memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));/* [3]在這里初始化設(shè)備用了2.6的新方法,在scull_setup_cdev里完成 */ /* Initialize each device. */ for (i = 0; i < scull_nr_devs; i++) { scull_devices[i].quantum = scull_quantum; /* 可以根據(jù)自己insmod時傳參 來自己改變量子和量子集(指針數(shù)組)的大小 */ scull_devices[i].qset = scull_qset; init_MUTEX(&scull_devices[i].sem); scull_setup_cdev(&scull_devices[i], i); /* 在分別完主設(shè)備編號后goto Tag002 設(shè)備注冊 */ } /* At this point call the init function for any friend device */ dev = MKDEV(scull_major, scull_minor + scull_nr_devs); dev += scull_p_init(dev); dev += scull_access_init(dev);#ifdef SCULL_DEBUG /* only when debugging */ scull_create_proc();#endif return 0; /* succeed */ fail: scull_cleanup_module(); return result;}module_init(scull_init_module); //insmod module_exit(scull_cleanup_module); //rmmod
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -