1212#include <linux/skbuff.h>
1313#include <linux/slab.h>
1414#include <linux/types.h>
15+ #include <linux/termios.h>
1516#include <linux/wwan.h>
1617
17- #define WWAN_MAX_MINORS 256 /* 256 minors allowed with register_chrdev() */
18+ /* Maximum number of minors in use */
19+ #define WWAN_MAX_MINORS (1 << MINORBITS)
1820
1921static DEFINE_MUTEX (wwan_register_lock ); /* WWAN device create|remove lock */
2022static DEFINE_IDA (minors ); /* minors for WWAN port chardevs */
@@ -33,12 +35,10 @@ static int wwan_major;
3335 *
3436 * @id: WWAN device unique ID.
3537 * @dev: Underlying device.
36- * @port_id: Current available port ID to pick.
3738 */
3839struct wwan_device {
3940 unsigned int id ;
4041 struct device dev ;
41- atomic_t port_id ;
4242};
4343
4444/**
@@ -51,6 +51,8 @@ struct wwan_device {
5151 * @dev: Underlying device
5252 * @rxq: Buffer inbound queue
5353 * @waitqueue: The waitqueue for port fops (read/write/poll)
54+ * @data_lock: Port specific data access serialization
55+ * @at_data: AT port specific data
5456 */
5557struct wwan_port {
5658 enum wwan_port_type type ;
@@ -61,6 +63,13 @@ struct wwan_port {
6163 struct device dev ;
6264 struct sk_buff_head rxq ;
6365 wait_queue_head_t waitqueue ;
66+ struct mutex data_lock ; /* Port specific data access serialization */
67+ union {
68+ struct {
69+ struct ktermios termios ;
70+ int mdmbits ;
71+ } at_data ;
72+ };
6473};
6574
6675static ssize_t index_show (struct device * dev , struct device_attribute * attr , char * buf )
@@ -184,21 +193,38 @@ static void wwan_remove_dev(struct wwan_device *wwandev)
184193
185194/* ------- WWAN port management ------- */
186195
187- /* Keep aligned with wwan_port_type enum */
188- static const char * const wwan_port_type_str [] = {
189- "AT" ,
190- "MBIM" ,
191- "QMI" ,
192- "QCDM" ,
193- "FIREHOSE"
196+ static const struct {
197+ const char * const name ; /* Port type name */
198+ const char * const devsuf ; /* Port devce name suffix */
199+ } wwan_port_types [WWAN_PORT_MAX + 1 ] = {
200+ [WWAN_PORT_AT ] = {
201+ .name = "AT" ,
202+ .devsuf = "at" ,
203+ },
204+ [WWAN_PORT_MBIM ] = {
205+ .name = "MBIM" ,
206+ .devsuf = "mbim" ,
207+ },
208+ [WWAN_PORT_QMI ] = {
209+ .name = "QMI" ,
210+ .devsuf = "qmi" ,
211+ },
212+ [WWAN_PORT_QCDM ] = {
213+ .name = "QCDM" ,
214+ .devsuf = "qcdm" ,
215+ },
216+ [WWAN_PORT_FIREHOSE ] = {
217+ .name = "FIREHOSE" ,
218+ .devsuf = "firehose" ,
219+ },
194220};
195221
196222static ssize_t type_show (struct device * dev , struct device_attribute * attr ,
197223 char * buf )
198224{
199225 struct wwan_port * port = to_wwan_port (dev );
200226
201- return sprintf (buf , "%s\n" , wwan_port_type_str [port -> type ]);
227+ return sprintf (buf , "%s\n" , wwan_port_types [port -> type ]. name );
202228}
203229static DEVICE_ATTR_RO (type );
204230
@@ -213,7 +239,7 @@ static void wwan_port_destroy(struct device *dev)
213239 struct wwan_port * port = to_wwan_port (dev );
214240
215241 ida_free (& minors , MINOR (port -> dev .devt ));
216- skb_queue_purge (& port -> rxq );
242+ mutex_destroy (& port -> data_lock );
217243 mutex_destroy (& port -> ops_lock );
218244 kfree (port );
219245}
@@ -241,6 +267,56 @@ static struct wwan_port *wwan_port_get_by_minor(unsigned int minor)
241267 return to_wwan_port (dev );
242268}
243269
270+ /* Allocate and set unique name based on passed format
271+ *
272+ * Name allocation approach is highly inspired by the __dev_alloc_name()
273+ * function.
274+ *
275+ * To avoid names collision, the caller must prevent the new port device
276+ * registration as well as concurrent invocation of this function.
277+ */
278+ static int __wwan_port_dev_assign_name (struct wwan_port * port , const char * fmt )
279+ {
280+ struct wwan_device * wwandev = to_wwan_dev (port -> dev .parent );
281+ const unsigned int max_ports = PAGE_SIZE * 8 ;
282+ struct class_dev_iter iter ;
283+ unsigned long * idmap ;
284+ struct device * dev ;
285+ char buf [0x20 ];
286+ int id ;
287+
288+ idmap = (unsigned long * )get_zeroed_page (GFP_KERNEL );
289+ if (!idmap )
290+ return - ENOMEM ;
291+
292+ /* Collect ids of same name format ports */
293+ class_dev_iter_init (& iter , wwan_class , NULL , & wwan_port_dev_type );
294+ while ((dev = class_dev_iter_next (& iter ))) {
295+ if (dev -> parent != & wwandev -> dev )
296+ continue ;
297+ if (sscanf (dev_name (dev ), fmt , & id ) != 1 )
298+ continue ;
299+ if (id < 0 || id >= max_ports )
300+ continue ;
301+ set_bit (id , idmap );
302+ }
303+ class_dev_iter_exit (& iter );
304+
305+ /* Allocate unique id */
306+ id = find_first_zero_bit (idmap , max_ports );
307+ free_page ((unsigned long )idmap );
308+
309+ snprintf (buf , sizeof (buf ), fmt , id ); /* Name generation */
310+
311+ dev = device_find_child_by_name (& wwandev -> dev , buf );
312+ if (dev ) {
313+ put_device (dev );
314+ return - ENFILE ;
315+ }
316+
317+ return dev_set_name (& port -> dev , buf );
318+ }
319+
244320struct wwan_port * wwan_create_port (struct device * parent ,
245321 enum wwan_port_type type ,
246322 const struct wwan_port_ops * ops ,
@@ -249,8 +325,9 @@ struct wwan_port *wwan_create_port(struct device *parent,
249325 struct wwan_device * wwandev ;
250326 struct wwan_port * port ;
251327 int minor , err = - ENOMEM ;
328+ char namefmt [0x20 ];
252329
253- if (type >= WWAN_PORT_MAX || !ops )
330+ if (type > WWAN_PORT_MAX || !ops )
254331 return ERR_PTR (- EINVAL );
255332
256333 /* A port is always a child of a WWAN device, retrieve (allocate or
@@ -276,19 +353,26 @@ struct wwan_port *wwan_create_port(struct device *parent,
276353 mutex_init (& port -> ops_lock );
277354 skb_queue_head_init (& port -> rxq );
278355 init_waitqueue_head (& port -> waitqueue );
356+ mutex_init (& port -> data_lock );
279357
280358 port -> dev .parent = & wwandev -> dev ;
281359 port -> dev .class = wwan_class ;
282360 port -> dev .type = & wwan_port_dev_type ;
283361 port -> dev .devt = MKDEV (wwan_major , minor );
284362 dev_set_drvdata (& port -> dev , drvdata );
285363
286- /* create unique name based on wwan device id, port index and type */
287- dev_set_name (& port -> dev , "wwan%up%u%s" , wwandev -> id ,
288- atomic_inc_return (& wwandev -> port_id ),
289- wwan_port_type_str [port -> type ]);
364+ /* allocate unique name based on wwan device id, port type and number */
365+ snprintf (namefmt , sizeof (namefmt ), "wwan%u%s%%d" , wwandev -> id ,
366+ wwan_port_types [port -> type ].devsuf );
290367
368+ /* Serialize ports registration */
369+ mutex_lock (& wwan_register_lock );
370+
371+ __wwan_port_dev_assign_name (port , namefmt );
291372 err = device_register (& port -> dev );
373+
374+ mutex_unlock (& wwan_register_lock );
375+
292376 if (err )
293377 goto error_put_device ;
294378
@@ -377,8 +461,11 @@ static void wwan_port_op_stop(struct wwan_port *port)
377461{
378462 mutex_lock (& port -> ops_lock );
379463 port -> start_count -- ;
380- if (port -> ops && !port -> start_count )
381- port -> ops -> stop (port );
464+ if (!port -> start_count ) {
465+ if (port -> ops )
466+ port -> ops -> stop (port );
467+ skb_queue_purge (& port -> rxq );
468+ }
382469 mutex_unlock (& port -> ops_lock );
383470}
384471
@@ -545,13 +632,121 @@ static __poll_t wwan_port_fops_poll(struct file *filp, poll_table *wait)
545632 return mask ;
546633}
547634
635+ /* Implements minimalistic stub terminal IOCTLs support */
636+ static long wwan_port_fops_at_ioctl (struct wwan_port * port , unsigned int cmd ,
637+ unsigned long arg )
638+ {
639+ int ret = 0 ;
640+
641+ mutex_lock (& port -> data_lock );
642+
643+ switch (cmd ) {
644+ case TCFLSH :
645+ break ;
646+
647+ case TCGETS :
648+ if (copy_to_user ((void __user * )arg , & port -> at_data .termios ,
649+ sizeof (struct termios )))
650+ ret = - EFAULT ;
651+ break ;
652+
653+ case TCSETS :
654+ case TCSETSW :
655+ case TCSETSF :
656+ if (copy_from_user (& port -> at_data .termios , (void __user * )arg ,
657+ sizeof (struct termios )))
658+ ret = - EFAULT ;
659+ break ;
660+
661+ #ifdef TCGETS2
662+ case TCGETS2 :
663+ if (copy_to_user ((void __user * )arg , & port -> at_data .termios ,
664+ sizeof (struct termios2 )))
665+ ret = - EFAULT ;
666+ break ;
667+
668+ case TCSETS2 :
669+ case TCSETSW2 :
670+ case TCSETSF2 :
671+ if (copy_from_user (& port -> at_data .termios , (void __user * )arg ,
672+ sizeof (struct termios2 )))
673+ ret = - EFAULT ;
674+ break ;
675+ #endif
676+
677+ case TIOCMGET :
678+ ret = put_user (port -> at_data .mdmbits , (int __user * )arg );
679+ break ;
680+
681+ case TIOCMSET :
682+ case TIOCMBIC :
683+ case TIOCMBIS : {
684+ int mdmbits ;
685+
686+ if (copy_from_user (& mdmbits , (int __user * )arg , sizeof (int ))) {
687+ ret = - EFAULT ;
688+ break ;
689+ }
690+ if (cmd == TIOCMBIC )
691+ port -> at_data .mdmbits &= ~mdmbits ;
692+ else if (cmd == TIOCMBIS )
693+ port -> at_data .mdmbits |= mdmbits ;
694+ else
695+ port -> at_data .mdmbits = mdmbits ;
696+ break ;
697+ }
698+
699+ default :
700+ ret = - ENOIOCTLCMD ;
701+ }
702+
703+ mutex_unlock (& port -> data_lock );
704+
705+ return ret ;
706+ }
707+
708+ static long wwan_port_fops_ioctl (struct file * filp , unsigned int cmd ,
709+ unsigned long arg )
710+ {
711+ struct wwan_port * port = filp -> private_data ;
712+ int res ;
713+
714+ if (port -> type == WWAN_PORT_AT ) { /* AT port specific IOCTLs */
715+ res = wwan_port_fops_at_ioctl (port , cmd , arg );
716+ if (res != - ENOIOCTLCMD )
717+ return res ;
718+ }
719+
720+ switch (cmd ) {
721+ case TIOCINQ : { /* aka SIOCINQ aka FIONREAD */
722+ unsigned long flags ;
723+ struct sk_buff * skb ;
724+ int amount = 0 ;
725+
726+ spin_lock_irqsave (& port -> rxq .lock , flags );
727+ skb_queue_walk (& port -> rxq , skb )
728+ amount += skb -> len ;
729+ spin_unlock_irqrestore (& port -> rxq .lock , flags );
730+
731+ return put_user (amount , (int __user * )arg );
732+ }
733+
734+ default :
735+ return - ENOIOCTLCMD ;
736+ }
737+ }
738+
548739static const struct file_operations wwan_port_fops = {
549740 .owner = THIS_MODULE ,
550741 .open = wwan_port_fops_open ,
551742 .release = wwan_port_fops_release ,
552743 .read = wwan_port_fops_read ,
553744 .write = wwan_port_fops_write ,
554745 .poll = wwan_port_fops_poll ,
746+ .unlocked_ioctl = wwan_port_fops_ioctl ,
747+ #ifdef CONFIG_COMPAT
748+ .compat_ioctl = compat_ptr_ioctl ,
749+ #endif
555750 .llseek = noop_llseek ,
556751};
557752
@@ -562,7 +757,8 @@ static int __init wwan_init(void)
562757 return PTR_ERR (wwan_class );
563758
564759 /* chrdev used for wwan ports */
565- wwan_major = register_chrdev (0 , "wwan_port" , & wwan_port_fops );
760+ wwan_major = __register_chrdev (0 , 0 , WWAN_MAX_MINORS , "wwan_port" ,
761+ & wwan_port_fops );
566762 if (wwan_major < 0 ) {
567763 class_destroy (wwan_class );
568764 return wwan_major ;
@@ -573,7 +769,7 @@ static int __init wwan_init(void)
573769
574770static void __exit wwan_exit (void )
575771{
576- unregister_chrdev (wwan_major , "wwan_port" );
772+ __unregister_chrdev (wwan_major , 0 , WWAN_MAX_MINORS , "wwan_port" );
577773 class_destroy (wwan_class );
578774}
579775
0 commit comments