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 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 63 static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data, u16 *size); | 63 static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data, u16 *size); |
| 64 static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid, | 64 static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid, |
| 65 void (*hook)(struct qcusbnet *, u16 cid, void *), | 65 void (*hook)(struct qcusbnet *, u16 cid, void *), |
| 66 void *data); | 66 void *data); |
| 67 static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid); | 67 static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid); |
| 68 static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb); | 68 static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb); |
| 69 static struct urb *client_delurb(struct qcusbnet *dev, u16 cid); | 69 static struct urb *client_delurb(struct qcusbnet *dev, u16 cid); |
| 70 | 70 |
| 71 static int devqmi_open(struct inode *inode, struct file *file); | 71 static int devqmi_open(struct inode *inode, struct file *file); |
| 72 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd , unsigned long arg); | 72 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd , unsigned long arg); |
| 73 static int devqmi_close(struct file *file, fl_owner_t ftable); | 73 static int devqmi_release(struct inode *inode, struct file *file); |
| 74 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, | 74 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, |
| 75 loff_t *pos); | 75 loff_t *pos); |
| 76 static ssize_t devqmi_write(struct file *file, const char __user *buf, | 76 static ssize_t devqmi_write(struct file *file, const char __user *buf, |
| 77 size_t size, loff_t *pos); | 77 size_t size, loff_t *pos); |
| 78 | 78 |
| 79 static bool qmi_ready(struct qcusbnet *dev, u16 timeout); | 79 static bool qmi_ready(struct qcusbnet *dev, u16 timeout); |
| 80 static void wds_callback(struct qcusbnet *dev, u16 cid, void *data); | 80 static void wds_callback(struct qcusbnet *dev, u16 cid, void *data); |
| 81 static int setup_wds_callback(struct qcusbnet *dev); | 81 static int setup_wds_callback(struct qcusbnet *dev); |
| 82 static int qmidms_getmeid(struct qcusbnet *dev); | 82 static int qmidms_getmeid(struct qcusbnet *dev); |
| 83 | 83 |
| 84 #define IOCTL_QMI_GET_SERVICE_FILE» (0x8BE0 + 1) | 84 #define IOCTL_QMI_GET_SERVICE_FILE (0x8BE0 + 1) |
| 85 #define IOCTL_QMI_GET_DEVICE_VIDPID» (0x8BE0 + 2) | 85 #define IOCTL_QMI_GET_DEVICE_VIDPID (0x8BE0 + 2) |
| 86 #define IOCTL_QMI_GET_DEVICE_MEID» (0x8BE0 + 3) | 86 #define IOCTL_QMI_GET_DEVICE_MEID (0x8BE0 + 3) |
| 87 #define IOCTL_QMI_CLOSE (0x8BE0 + 4) | |
| 87 #define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll | 88 #define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll |
| 88 #define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll | 89 #define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll |
| 89 | 90 |
| 90 static const struct file_operations devqmi_fops = { | 91 static const struct file_operations devqmi_fops = { |
| 91 » .owner = THIS_MODULE, | 92 » .owner = THIS_MODULE, |
| 92 » .read = devqmi_read, | 93 » .read = devqmi_read, |
| 93 » .write = devqmi_write, | 94 » .write = devqmi_write, |
| 94 » .ioctl = devqmi_ioctl, | 95 » .ioctl = devqmi_ioctl, |
| 95 » .open = devqmi_open, | 96 » .open = devqmi_open, |
| 96 » .flush = devqmi_close, | 97 » .release = devqmi_release, |
| 97 }; | 98 }; |
| 98 | 99 |
| 99 #ifdef CONFIG_SMP | 100 #ifdef CONFIG_SMP |
| 100 static inline void assert_locked(struct qcusbnet *dev) | 101 static inline void assert_locked(struct qcusbnet *dev) |
| 101 { | 102 { |
| 102 BUG_ON(!spin_is_locked(&dev->qmi.clients_lock)); | 103 BUG_ON(!spin_is_locked(&dev->qmi.clients_lock)); |
| 103 } | 104 } |
| 104 #else | 105 #else |
| 105 static inline void assert_locked(struct qcusbnet *dev) | 106 static inline void assert_locked(struct qcusbnet *dev) |
| 106 { | 107 { |
| (...skipping 565 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 672 struct urb *urb; | 673 struct urb *urb; |
| 673 void *data; | 674 void *data; |
| 674 u16 size; | 675 u16 size; |
| 675 void *wbuf; | 676 void *wbuf; |
| 676 size_t wbufsize; | 677 size_t wbufsize; |
| 677 void *rbuf; | 678 void *rbuf; |
| 678 u16 rbufsize; | 679 u16 rbufsize; |
| 679 unsigned long flags; | 680 unsigned long flags; |
| 680 u8 tid; | 681 u8 tid; |
| 681 | 682 |
| 682 if (!device_valid(dev)) { | |
| 683 DBG("invalid device\n"); | |
| 684 return; | |
| 685 } | |
| 686 | |
| 687 DBG("releasing 0x%04X\n", cid); | 683 DBG("releasing 0x%04X\n", cid); |
| 688 | 684 |
| 689 if (cid != QMICTL) { | 685 if (cid != QMICTL) { |
| 690 tid = atomic_add_return(1, &dev->qmi.qmitid); | 686 tid = atomic_add_return(1, &dev->qmi.qmitid); |
| 691 if (!tid) | 687 if (!tid) |
| 692 tid = atomic_add_return(1, &dev->qmi.qmitid); | 688 tid = atomic_add_return(1, &dev->qmi.qmitid); |
| 693 wbuf = qmictl_new_releasecid(tid, cid, &wbufsize); | 689 wbuf = qmictl_new_releasecid(tid, cid, &wbufsize); |
| 694 if (!wbuf) { | 690 if (!wbuf) { |
| 695 DBG("memory error\n"); | 691 DBG("memory error\n"); |
| 696 } else { | 692 } else { |
| (...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 978 struct qmihandle *handle = (struct qmihandle *)file->private_data; | 974 struct qmihandle *handle = (struct qmihandle *)file->private_data; |
| 979 | 975 |
| 980 DBG("%p %04x %08x", handle, handle->cid, cmd); | 976 DBG("%p %04x %08x", handle, handle->cid, cmd); |
| 981 | 977 |
| 982 if (!handle) { | 978 if (!handle) { |
| 983 DBG("Bad file data\n"); | 979 DBG("Bad file data\n"); |
| 984 return -EBADF; | 980 return -EBADF; |
| 985 } | 981 } |
| 986 | 982 |
| 987 if (!device_valid(handle->dev)) { | 983 if (!device_valid(handle->dev)) { |
| 988 » » DBG("Invalid device! Updating f_ops\n"); | 984 » » DBG("Invalid device!\n"); |
| 989 » » file->f_op = file->f_dentry->d_inode->i_fop; | |
| 990 return -ENXIO; | 985 return -ENXIO; |
| 991 } | 986 } |
| 992 | 987 |
| 993 switch (cmd) { | 988 switch (cmd) { |
| 994 case IOCTL_QMI_GET_SERVICE_FILE: | 989 case IOCTL_QMI_GET_SERVICE_FILE: |
| 995 | 990 |
| 996 DBG("Setting up QMI for service %lu\n", arg); | 991 DBG("Setting up QMI for service %lu\n", arg); |
| 997 if (!(u8)arg) { | 992 if (!(u8)arg) { |
| 998 DBG("Cannot use QMICTL from userspace\n"); | 993 DBG("Cannot use QMICTL from userspace\n"); |
| 999 return -EINVAL; | 994 return -EINVAL; |
| 1000 } | 995 } |
| 1001 | 996 |
| 1002 if (handle->cid != (u16)-1) { | 997 if (handle->cid != (u16)-1) { |
| 1003 DBG("Close the current connection before opening a new o ne\n"); | 998 DBG("Close the current connection before opening a new o ne\n"); |
| 1004 return -EBADR; | 999 return -EBADR; |
| 1005 } | 1000 } |
| 1006 | 1001 |
| 1007 result = client_alloc(handle->dev, (u8)arg); | 1002 result = client_alloc(handle->dev, (u8)arg); |
| 1008 if (result < 0) | 1003 if (result < 0) |
| 1009 return result; | 1004 return result; |
| 1010 handle->cid = result; | 1005 handle->cid = result; |
| 1011 | 1006 |
| 1012 return 0; | 1007 return 0; |
| 1013 break; | 1008 break; |
| 1014 | 1009 |
| 1010 /* Okay, all aboard the nasty hack express. If we don't have this | |
| 1011 * ioctl() (and we just rely on userspace to close() the file | |
| 1012 * descriptors), if userspace has any refs left to this fd (like, say, a | |
| 1013 * pending read()), then the read might hang around forever. Userspace | |
| 1014 * needs a way to cause us to kick people off those waitqueues before | |
| 1015 * closing the fd for good. | |
| 1016 * | |
| 1017 * If this driver used workqueues, the correct approach here would | |
| 1018 * instead be to make the file descriptor select()able, and then just | |
| 1019 * use select() instead of aio in userspace (thus allowing us to get | |
| 1020 * away with one thread total and avoiding the recounting mess | |
| 1021 * altogether). | |
| 1022 */ | |
|
Mandeep Singh Baines
2011/03/15 20:56:31
I've read the comment but I'm still not getting th
| |
| 1023 case IOCTL_QMI_CLOSE: | |
| 1024 DBG("Tearing down QMI for service %lu", arg); | |
| 1025 if (handle->cid == (u16)-1) { | |
| 1026 DBG("no qmi cid"); | |
| 1027 return -EBADR; | |
| 1028 } | |
| 1029 | |
| 1030 file->private_data = NULL; | |
| 1031 client_free(handle->dev, handle->cid); | |
| 1032 kfree(handle); | |
| 1033 return 0; | |
| 1034 break; | |
| 1015 | 1035 |
| 1016 case IOCTL_QMI_GET_DEVICE_VIDPID: | 1036 case IOCTL_QMI_GET_DEVICE_VIDPID: |
| 1017 if (!arg) { | 1037 if (!arg) { |
| 1018 DBG("Bad VIDPID buffer\n"); | 1038 DBG("Bad VIDPID buffer\n"); |
| 1019 return -EINVAL; | 1039 return -EINVAL; |
| 1020 } | 1040 } |
| 1021 | 1041 |
| 1022 if (!handle->dev->usbnet) { | 1042 if (!handle->dev->usbnet) { |
| 1023 DBG("Bad usbnet\n"); | 1043 DBG("Bad usbnet\n"); |
| 1024 return -ENOMEM; | 1044 return -ENOMEM; |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 1049 if (result) | 1069 if (result) |
| 1050 DBG("copy to userspace failure\n"); | 1070 DBG("copy to userspace failure\n"); |
| 1051 | 1071 |
| 1052 return result; | 1072 return result; |
| 1053 break; | 1073 break; |
| 1054 default: | 1074 default: |
| 1055 return -EBADRQC; | 1075 return -EBADRQC; |
| 1056 } | 1076 } |
| 1057 } | 1077 } |
| 1058 | 1078 |
| 1059 static int devqmi_close(struct file *file, fl_owner_t ftable) | 1079 static int devqmi_release(struct inode *inode, struct file *file) |
| 1060 { | 1080 { |
| 1061 struct qmihandle *handle = (struct qmihandle *)file->private_data; | 1081 struct qmihandle *handle = (struct qmihandle *)file->private_data; |
| 1062 » struct task_struct *task; | 1082 » if (!handle) |
| 1063 » struct fdtable *fdtable; | 1083 » » return 0; |
| 1064 » int count = 0; | |
| 1065 » int used = 0; | |
| 1066 » unsigned long flags; | |
| 1067 | |
| 1068 » if (!handle) { | |
| 1069 » » DBG("bad file data\n"); | |
| 1070 » » return -EBADF; | |
| 1071 » } | |
| 1072 | |
| 1073 » if (file_count(file) != 1) { | |
| 1074 » » rcu_read_lock(); | |
| 1075 » » for_each_process(task) { | |
| 1076 » » » if (!task || !task->files) | |
| 1077 » » » » continue; | |
| 1078 » » » spin_lock_irqsave(&task->files->file_lock, flags); | |
| 1079 » » » fdtable = files_fdtable(task->files); | |
| 1080 » » » for (count = 0; count < fdtable->max_fds; count++) { | |
| 1081 » » » » /* Before this function was called, this file wa s removed | |
| 1082 » » » » * from our task's file table so if we find it i n a file | |
| 1083 » » » » * table then it is being used by another task | |
| 1084 » » » » */ | |
| 1085 » » » » if (fdtable->fd[count] == file) { | |
| 1086 » » » » » used++; | |
| 1087 » » » » » break; | |
| 1088 » » » » } | |
| 1089 » » » } | |
| 1090 » » » spin_unlock_irqrestore(&task->files->file_lock, flags); | |
| 1091 » » } | |
| 1092 » » rcu_read_unlock(); | |
| 1093 | |
| 1094 » » if (used > 0) { | |
| 1095 » » » DBG("not closing, as this FD is open by %d other process \n", used); | |
| 1096 » » » return 0; | |
| 1097 » » } | |
| 1098 » } | |
| 1099 | |
| 1100 » if (!device_valid(handle->dev)) { | |
| 1101 » » DBG("Invalid device! Updating f_ops\n"); | |
| 1102 » » file->f_op = file->f_dentry->d_inode->i_fop; | |
| 1103 » » return -ENXIO; | |
| 1104 » } | |
| 1105 | |
| 1106 » DBG("0x%04X\n", handle->cid); | |
| 1107 | |
| 1108 file->private_data = NULL; | 1084 file->private_data = NULL; |
| 1109 | |
| 1110 if (handle->cid != (u16)-1) | 1085 if (handle->cid != (u16)-1) |
| 1111 client_free(handle->dev, handle->cid); | 1086 client_free(handle->dev, handle->cid); |
| 1112 | |
| 1113 kfree(handle); | 1087 kfree(handle); |
| 1114 return 0; | 1088 return 0; |
| 1115 } | 1089 } |
| 1116 | 1090 |
| 1117 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, | 1091 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, |
| 1118 loff_t *pos) | 1092 loff_t *pos) |
| 1119 { | 1093 { |
| 1120 int result; | 1094 int result; |
| 1121 void *data = NULL; | 1095 void *data = NULL; |
| 1122 void *smalldata; | 1096 void *smalldata; |
| 1123 struct qmihandle *handle = (struct qmihandle *)file->private_data; | 1097 struct qmihandle *handle = (struct qmihandle *)file->private_data; |
| 1124 | 1098 |
| 1125 if (!handle) { | 1099 if (!handle) { |
| 1126 DBG("Bad file data\n"); | 1100 DBG("Bad file data\n"); |
| 1127 return -EBADF; | 1101 return -EBADF; |
| 1128 } | 1102 } |
| 1129 | 1103 |
| 1130 if (!device_valid(handle->dev)) { | 1104 if (!device_valid(handle->dev)) { |
| 1131 » » DBG("Invalid device! Updating f_ops\n"); | 1105 » » DBG("Invalid device!\n"); |
| 1132 » » file->f_op = file->f_dentry->d_inode->i_fop; | |
| 1133 return -ENXIO; | 1106 return -ENXIO; |
| 1134 } | 1107 } |
| 1135 | 1108 |
| 1136 if (handle->cid == (u16)-1) { | 1109 if (handle->cid == (u16)-1) { |
| 1137 DBG("Client ID must be set before reading 0x%04X\n", | 1110 DBG("Client ID must be set before reading 0x%04X\n", |
| 1138 handle->cid); | 1111 handle->cid); |
| 1139 return -EBADR; | 1112 return -EBADR; |
| 1140 } | 1113 } |
| 1141 | 1114 |
| 1142 result = read_sync(handle->dev, &data, handle->cid, 0); | 1115 result = read_sync(handle->dev, &data, handle->cid, 0); |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1280 struct list_head *node, *tmp; | 1253 struct list_head *node, *tmp; |
| 1281 struct client *client; | 1254 struct client *client; |
| 1282 struct inode *inode; | 1255 struct inode *inode; |
| 1283 struct list_head *inodes; | 1256 struct list_head *inodes; |
| 1284 struct task_struct *task; | 1257 struct task_struct *task; |
| 1285 struct fdtable *fdtable; | 1258 struct fdtable *fdtable; |
| 1286 struct file *file; | 1259 struct file *file; |
| 1287 unsigned long flags; | 1260 unsigned long flags; |
| 1288 int count = 0; | 1261 int count = 0; |
| 1289 | 1262 |
| 1290 if (!device_valid(dev)) { | |
| 1291 DBG("wrong device\n"); | |
| 1292 return; | |
| 1293 } | |
| 1294 | |
| 1295 list_for_each_safe(node, tmp, &dev->qmi.clients) { | 1263 list_for_each_safe(node, tmp, &dev->qmi.clients) { |
| 1296 client = list_entry(node, struct client, node); | 1264 client = list_entry(node, struct client, node); |
| 1297 DBG("release 0x%04X\n", client->cid); | 1265 DBG("release 0x%04X\n", client->cid); |
| 1298 client_free(dev, client->cid); | 1266 client_free(dev, client->cid); |
| 1299 } | 1267 } |
| 1300 | 1268 |
| 1301 qc_stopread(dev); | 1269 qc_stopread(dev); |
| 1302 dev->valid = false; | 1270 dev->valid = false; |
| 1303 list_for_each(inodes, &dev->qmi.cdev.list) { | 1271 list_for_each(inodes, &dev->qmi.cdev.list) { |
| 1304 inode = container_of(inodes, struct inode, i_devices); | 1272 inode = container_of(inodes, struct inode, i_devices); |
| (...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1583 DBG("bad get MEID resp\n"); | 1551 DBG("bad get MEID resp\n"); |
| 1584 memset(&dev->meid[0], '0', 14); | 1552 memset(&dev->meid[0], '0', 14); |
| 1585 } | 1553 } |
| 1586 | 1554 |
| 1587 client_free(dev, cid); | 1555 client_free(dev, cid); |
| 1588 return 0; | 1556 return 0; |
| 1589 } | 1557 } |
| 1590 | 1558 |
| 1591 module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR); | 1559 module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR); |
| 1592 MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware"); | 1560 MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware"); |
| OLD | NEW |