Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* qmidevice.c - gobi QMI device | 1 /* qmidevice.c - gobi QMI device |
| 2 * Copyright (c) 2010, Code Aurora Forum. All rights reserved. | 2 * Copyright (c) 2010, Code Aurora Forum. All rights reserved. |
| 3 | 3 |
| 4 * This program is free software; you can redistribute it and/or modify | 4 * This program is free software; you can redistribute it and/or modify |
| 5 * it under the terms of the GNU General Public License version 2 and | 5 * it under the terms of the GNU General Public License version 2 and |
| 6 * only version 2 as published by the Free Software Foundation. | 6 * only version 2 as published by the Free Software Foundation. |
| 7 | 7 |
| 8 * This program is distributed in the hope that it will be useful, | 8 * This program is distributed in the hope that it will be useful, |
| 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| (...skipping 718 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 729 urb = client_delurb(dev, cid); | 729 urb = client_delurb(dev, cid); |
| 730 } | 730 } |
| 731 | 731 |
| 732 while (client_delread(dev, cid, 0, &data, &size)) | 732 while (client_delread(dev, cid, 0, &data, &size)) |
| 733 kfree(data); | 733 kfree(data); |
| 734 | 734 |
| 735 list_del(&client->node); | 735 list_del(&client->node); |
| 736 kfree(client); | 736 kfree(client); |
| 737 } | 737 } |
| 738 } | 738 } |
| 739 | |
| 740 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); | 739 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 741 } | 740 } |
| 742 | 741 |
| 743 struct client *client_bycid(struct qcusbnet *dev, u16 cid) | 742 struct client *client_bycid(struct qcusbnet *dev, u16 cid) |
| 744 { | 743 { |
| 745 struct list_head *node; | 744 struct list_head *node; |
| 746 struct client *client; | 745 struct client *client; |
| 747 | 746 |
| 748 if (!device_valid(dev)) { | 747 if (!device_valid(dev)) { |
| 749 DBG("Invalid device\n"); | 748 DBG("Invalid device\n"); |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 943 kfree(req); | 942 kfree(req); |
| 944 return urb; | 943 return urb; |
| 945 } | 944 } |
| 946 | 945 |
| 947 static int devqmi_open(struct inode *inode, struct file *file) | 946 static int devqmi_open(struct inode *inode, struct file *file) |
| 948 { | 947 { |
| 949 struct qmihandle *handle; | 948 struct qmihandle *handle; |
| 950 struct qmidev *qmidev = container_of(inode->i_cdev, struct qmidev, cdev) ; | 949 struct qmidev *qmidev = container_of(inode->i_cdev, struct qmidev, cdev) ; |
| 951 struct qcusbnet *dev = container_of(qmidev, struct qcusbnet, qmi); | 950 struct qcusbnet *dev = container_of(qmidev, struct qcusbnet, qmi); |
| 952 | 951 |
| 953 » if (!device_valid(dev)) { | 952 » /* We need an extra ref on the device per fd, since we stash a ref |
| 954 » » DBG("Invalid device\n"); | 953 » * inside the handle. If qcusbnet_get() returns NULL, that means the |
| 954 » * device has been removed from the list - no new refs for us. */ | |
| 955 » struct qcusbnet *ref = qcusbnet_get(dev); | |
| 956 | |
| 957 » if (!ref) | |
| 955 return -ENXIO; | 958 return -ENXIO; |
| 956 } | |
| 957 | 959 |
| 958 file->private_data = kmalloc(sizeof(struct qmihandle), GFP_KERNEL); | 960 file->private_data = kmalloc(sizeof(struct qmihandle), GFP_KERNEL); |
| 959 if (!file->private_data) { | 961 if (!file->private_data) { |
| 960 DBG("Mem error\n"); | 962 DBG("Mem error\n"); |
| 961 return -ENOMEM; | 963 return -ENOMEM; |
| 962 } | 964 } |
| 963 | 965 |
| 964 handle = (struct qmihandle *)file->private_data; | 966 handle = (struct qmihandle *)file->private_data; |
| 965 handle->cid = (u16)-1; | 967 handle->cid = (u16)-1; |
| 966 » handle->dev = dev; | 968 » handle->dev = ref; |
| 967 | 969 |
| 968 DBG("%p %04x", handle, handle->cid); | 970 DBG("%p %04x", handle, handle->cid); |
| 969 | 971 |
| 970 return 0; | 972 return 0; |
| 971 } | 973 } |
| 972 | 974 |
| 973 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd , unsigned long arg) | 975 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd , unsigned long arg) |
| 974 { | 976 { |
| 975 int result; | 977 int result; |
| 976 u32 vidpid; | 978 u32 vidpid; |
| 977 | 979 |
| 978 struct qmihandle *handle = (struct qmihandle *)file->private_data; | 980 struct qmihandle *handle = (struct qmihandle *)file->private_data; |
| 979 | 981 |
| 980 DBG("%p %04x %08x", handle, handle->cid, cmd); | 982 DBG("%p %04x %08x", handle, handle->cid, cmd); |
| 981 | 983 |
| 982 if (!handle) { | 984 if (!handle) { |
| 983 DBG("Bad file data\n"); | 985 DBG("Bad file data\n"); |
| 984 return -EBADF; | 986 return -EBADF; |
| 985 } | 987 } |
| 986 | 988 |
| 989 if (handle->dev->dying) { | |
|
Jason Glasgow
2011/03/10 18:36:28
Dying never set to true.
Elly Fong-Jones
2011/03/10 19:15:46
Oops. Supposed to be done early in qc_deregister()
| |
| 990 DBG("Dying device"); | |
| 991 return -ENXIO; | |
| 992 } | |
| 993 | |
| 987 if (!device_valid(handle->dev)) { | 994 if (!device_valid(handle->dev)) { |
| 988 DBG("Invalid device! Updating f_ops\n"); | 995 DBG("Invalid device! Updating f_ops\n"); |
| 989 file->f_op = file->f_dentry->d_inode->i_fop; | 996 file->f_op = file->f_dentry->d_inode->i_fop; |
| 990 return -ENXIO; | 997 return -ENXIO; |
| 991 } | 998 } |
| 992 | 999 |
| 993 switch (cmd) { | 1000 switch (cmd) { |
| 994 case IOCTL_QMI_GET_SERVICE_FILE: | 1001 case IOCTL_QMI_GET_SERVICE_FILE: |
| 995 | 1002 |
| 996 DBG("Setting up QMI for service %lu\n", arg); | 1003 DBG("Setting up QMI for service %lu\n", arg); |
| 997 if (!(u8)arg) { | 1004 if (!(u8)arg) { |
| 998 DBG("Cannot use QMICTL from userspace\n"); | 1005 DBG("Cannot use QMICTL from userspace\n"); |
| 999 return -EINVAL; | 1006 return -EINVAL; |
| 1000 } | 1007 } |
| 1001 | 1008 |
| 1002 if (handle->cid != (u16)-1) { | 1009 if (handle->cid != (u16)-1) { |
| 1003 DBG("Close the current connection before opening a new o ne\n"); | 1010 DBG("Close the current connection before opening a new o ne\n"); |
| 1004 return -EBADR; | 1011 return -EBADR; |
| 1005 } | 1012 } |
| 1006 | 1013 |
| 1007 result = client_alloc(handle->dev, (u8)arg); | 1014 result = client_alloc(handle->dev, (u8)arg); |
| 1008 if (result < 0) | 1015 if (result < 0) |
| 1009 return result; | 1016 return result; |
| 1010 handle->cid = result; | 1017 handle->cid = result; |
| 1011 | 1018 |
| 1012 return 0; | 1019 return 0; |
| 1013 break; | 1020 break; |
| 1014 | 1021 |
| 1015 | |
| 1016 case IOCTL_QMI_GET_DEVICE_VIDPID: | 1022 case IOCTL_QMI_GET_DEVICE_VIDPID: |
| 1017 if (!arg) { | 1023 if (!arg) { |
| 1018 DBG("Bad VIDPID buffer\n"); | 1024 DBG("Bad VIDPID buffer\n"); |
| 1019 return -EINVAL; | 1025 return -EINVAL; |
| 1020 } | 1026 } |
| 1021 | 1027 |
| 1022 if (!handle->dev->usbnet) { | 1028 if (!handle->dev->usbnet) { |
| 1023 DBG("Bad usbnet\n"); | 1029 DBG("Bad usbnet\n"); |
| 1024 return -ENOMEM; | 1030 return -ENOMEM; |
| 1025 } | 1031 } |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1102 file->f_op = file->f_dentry->d_inode->i_fop; | 1108 file->f_op = file->f_dentry->d_inode->i_fop; |
| 1103 return -ENXIO; | 1109 return -ENXIO; |
| 1104 } | 1110 } |
| 1105 | 1111 |
| 1106 DBG("0x%04X\n", handle->cid); | 1112 DBG("0x%04X\n", handle->cid); |
| 1107 | 1113 |
| 1108 file->private_data = NULL; | 1114 file->private_data = NULL; |
| 1109 | 1115 |
| 1110 if (handle->cid != (u16)-1) | 1116 if (handle->cid != (u16)-1) |
| 1111 client_free(handle->dev, handle->cid); | 1117 client_free(handle->dev, handle->cid); |
| 1112 | 1118 » qcusbnet_put(handle->dev); |
| 1113 kfree(handle); | 1119 kfree(handle); |
| 1114 return 0; | 1120 return 0; |
| 1115 } | 1121 } |
| 1116 | 1122 |
| 1117 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, | 1123 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, |
| 1118 loff_t *pos) | 1124 loff_t *pos) |
| 1119 { | 1125 { |
| 1120 int result; | 1126 int result; |
| 1121 void *data = NULL; | 1127 void *data = NULL; |
| 1122 void *smalldata; | 1128 void *smalldata; |
| 1123 struct qmihandle *handle = (struct qmihandle *)file->private_data; | 1129 struct qmihandle *handle = (struct qmihandle *)file->private_data; |
| 1124 | 1130 |
| 1125 if (!handle) { | 1131 if (!handle) { |
| 1126 DBG("Bad file data\n"); | 1132 DBG("Bad file data\n"); |
| 1127 return -EBADF; | 1133 return -EBADF; |
| 1128 } | 1134 } |
| 1129 | 1135 |
| 1136 if (handle->dev->dying) { | |
| 1137 DBG("Dying device"); | |
| 1138 return -ENXIO; | |
| 1139 } | |
| 1140 | |
| 1130 if (!device_valid(handle->dev)) { | 1141 if (!device_valid(handle->dev)) { |
| 1131 DBG("Invalid device! Updating f_ops\n"); | 1142 DBG("Invalid device! Updating f_ops\n"); |
| 1132 file->f_op = file->f_dentry->d_inode->i_fop; | 1143 file->f_op = file->f_dentry->d_inode->i_fop; |
| 1133 return -ENXIO; | 1144 return -ENXIO; |
| 1134 } | 1145 } |
| 1135 | 1146 |
| 1136 if (handle->cid == (u16)-1) { | 1147 if (handle->cid == (u16)-1) { |
| 1137 DBG("Client ID must be set before reading 0x%04X\n", | 1148 DBG("Client ID must be set before reading 0x%04X\n", |
| 1138 handle->cid); | 1149 handle->cid); |
| 1139 return -EBADR; | 1150 return -EBADR; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1205 } | 1216 } |
| 1206 | 1217 |
| 1207 int qc_register(struct qcusbnet *dev) | 1218 int qc_register(struct qcusbnet *dev) |
| 1208 { | 1219 { |
| 1209 int result; | 1220 int result; |
| 1210 int qmiidx = 0; | 1221 int qmiidx = 0; |
| 1211 dev_t devno; | 1222 dev_t devno; |
| 1212 char *name; | 1223 char *name; |
| 1213 | 1224 |
| 1214 dev->valid = true; | 1225 dev->valid = true; |
| 1226 dev->dying = false; | |
| 1215 result = client_alloc(dev, QMICTL); | 1227 result = client_alloc(dev, QMICTL); |
| 1216 if (result) { | 1228 if (result) { |
| 1217 dev->valid = false; | 1229 dev->valid = false; |
| 1218 return result; | 1230 return result; |
| 1219 } | 1231 } |
| 1220 atomic_set(&dev->qmi.qmitid, 1); | 1232 atomic_set(&dev->qmi.qmitid, 1); |
| 1221 | 1233 |
| 1222 result = qc_startread(dev); | 1234 result = qc_startread(dev); |
| 1223 if (result) { | 1235 if (result) { |
| 1224 dev->valid = false; | 1236 dev->valid = false; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1272 device_create(dev->qmi.devclass, NULL, devno, NULL, "qcqmi%d", qmiidx); | 1284 device_create(dev->qmi.devclass, NULL, devno, NULL, "qcqmi%d", qmiidx); |
| 1273 | 1285 |
| 1274 dev->qmi.devnum = devno; | 1286 dev->qmi.devnum = devno; |
| 1275 return 0; | 1287 return 0; |
| 1276 } | 1288 } |
| 1277 | 1289 |
| 1278 void qc_deregister(struct qcusbnet *dev) | 1290 void qc_deregister(struct qcusbnet *dev) |
| 1279 { | 1291 { |
| 1280 struct list_head *node, *tmp; | 1292 struct list_head *node, *tmp; |
| 1281 struct client *client; | 1293 struct client *client; |
| 1282 struct inode *inode; | |
| 1283 struct list_head *inodes; | |
| 1284 struct task_struct *task; | |
| 1285 struct fdtable *fdtable; | |
| 1286 struct file *file; | |
| 1287 unsigned long flags; | |
| 1288 int count = 0; | |
| 1289 | 1294 |
| 1290 if (!device_valid(dev)) { | 1295 if (!device_valid(dev)) { |
| 1291 DBG("wrong device\n"); | 1296 DBG("wrong device\n"); |
| 1292 return; | 1297 return; |
| 1293 } | 1298 } |
| 1294 | 1299 |
| 1295 list_for_each_safe(node, tmp, &dev->qmi.clients) { | 1300 list_for_each_safe(node, tmp, &dev->qmi.clients) { |
| 1296 client = list_entry(node, struct client, node); | 1301 client = list_entry(node, struct client, node); |
| 1297 DBG("release 0x%04X\n", client->cid); | 1302 DBG("release 0x%04X\n", client->cid); |
| 1298 client_free(dev, client->cid); | 1303 client_free(dev, client->cid); |
| 1299 } | 1304 } |
| 1300 | 1305 |
| 1301 qc_stopread(dev); | 1306 qc_stopread(dev); |
| 1302 dev->valid = false; | 1307 dev->valid = false; |
| 1303 list_for_each(inodes, &dev->qmi.cdev.list) { | |
| 1304 inode = container_of(inodes, struct inode, i_devices); | |
| 1305 if (inode != NULL && !IS_ERR(inode)) { | |
| 1306 rcu_read_lock(); | |
| 1307 for_each_process(task) { | |
| 1308 if (!task || !task->files) | |
| 1309 continue; | |
| 1310 spin_lock_irqsave(&task->files->file_lock, flags ); | |
| 1311 fdtable = files_fdtable(task->files); | |
| 1312 for (count = 0; count < fdtable->max_fds; count+ +) { | |
| 1313 file = fdtable->fd[count]; | |
| 1314 if (file != NULL && file->f_dentry != N ULL) { | |
| 1315 if (file->f_dentry->d_inode == i node) { | |
| 1316 rcu_assign_pointer(fdtab le->fd[count], NULL); | |
| 1317 spin_unlock_irqrestore(& task->files->file_lock, flags); | |
| 1318 DBG("forcing close of op en file handle\n"); | |
| 1319 filp_close(file, task->f iles); | |
| 1320 spin_lock_irqsave(&task- >files->file_lock, flags); | |
| 1321 } | |
| 1322 } | |
| 1323 } | |
| 1324 spin_unlock_irqrestore(&task->files->file_lock, flags); | |
| 1325 } | |
| 1326 rcu_read_unlock(); | |
| 1327 } | |
| 1328 } | |
| 1329 | |
| 1330 if (!IS_ERR(dev->qmi.devclass)) | 1308 if (!IS_ERR(dev->qmi.devclass)) |
| 1331 device_destroy(dev->qmi.devclass, dev->qmi.devnum); | 1309 device_destroy(dev->qmi.devclass, dev->qmi.devnum); |
| 1332 cdev_del(&dev->qmi.cdev); | 1310 cdev_del(&dev->qmi.cdev); |
| 1333 unregister_chrdev_region(dev->qmi.devnum, 1); | 1311 unregister_chrdev_region(dev->qmi.devnum, 1); |
| 1334 } | 1312 } |
| 1335 | 1313 |
| 1336 static bool qmi_ready(struct qcusbnet *dev, u16 timeout) | 1314 static bool qmi_ready(struct qcusbnet *dev, u16 timeout) |
| 1337 { | 1315 { |
| 1338 int result; | 1316 int result; |
| 1339 void *wbuf; | 1317 void *wbuf; |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1583 DBG("bad get MEID resp\n"); | 1561 DBG("bad get MEID resp\n"); |
| 1584 memset(&dev->meid[0], '0', 14); | 1562 memset(&dev->meid[0], '0', 14); |
| 1585 } | 1563 } |
| 1586 | 1564 |
| 1587 client_free(dev, cid); | 1565 client_free(dev, cid); |
| 1588 return 0; | 1566 return 0; |
| 1589 } | 1567 } |
| 1590 | 1568 |
| 1591 module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR); | 1569 module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR); |
| 1592 MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware"); | 1570 MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware"); |
| OLD | NEW |